better support for google datastore

This commit is contained in:
mdipierro
2014-03-30 23:36:51 -05:00
parent 74b0bd9c3a
commit 0caae1f8d4
3 changed files with 46 additions and 40 deletions
+3
View File
@@ -11,6 +11,7 @@ env:
- DB=mysql://root:@localhost/test_w2p
- DB=postgres://postgres:@localhost/test_w2p
- DB=google:datastore
- DB=google:datastore+ndb
- DB=mongodb://mongodb:mongodb@localhost/test_w2p
- DB=imap://imap:imap@localhost:993
before_script:
@@ -43,6 +44,8 @@ matrix:
env: DB=google:datastore
- python: '2.6'
env: DB=google:datastore
- python: '2.6'
env: DB=google:datastore+ndb
script: export COVERAGE_PROCESS_START=gluon/tests/coverage.ini; ./web2py.py --run_system_tests --with_coverage
+1 -1
View File
@@ -1 +1 @@
Version 2.9.5-trunk+timestamp.2014.03.29.17.42.51
Version 2.9.5-trunk+timestamp.2014.03.30.23.35.50
+42 -39
View File
@@ -122,6 +122,7 @@ Supported DAL URI strings::
'informixu://user:password@server:3050/database' # unicode informix
'ingres://database' # or use an ODBC connection string, e.g. 'ingres://dsn=dsn_name'
'google:datastore' # for google app engine datastore
'google:datastore+ndb' # for google app engine datastore + ndb
'google:sql' # for google app engine with sql (mysql compatible)
'teradata://DSN=dsn;UID=user;PWD=pass; DATABASE=database' # experimental
'imap://user:password@server:port' # experimental
@@ -4832,6 +4833,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
See: https://developers.google.com/appengine/docs/python/ndb/cache
"""
MAX_FETCH_LIMIT = 1000000
uploads_in_blob = True
types = {}
# reconnect is not required for Datastore dbs
@@ -4846,7 +4848,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
def __init__(self,db,uri,pool_size=0,folder=None,db_codec ='UTF-8',
credential_decoder=IDENTITY, driver_args={},
adapter_args={}, do_connect=True, after_connection=None):
self.use_ndb = ('use_ndb' in adapter_args) and adapter_args['use_ndb']
self.use_ndb = adapter_args.get('use_ndb',uri.startswith('google:datastore+ndb'))
if self.use_ndb is True:
self.types.update({
'boolean': ndb.BooleanProperty,
@@ -5095,11 +5097,13 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
attributes = attributes or {}
args_get = attributes.get
new_fields = []
for item in fields:
if isinstance(item,SQLALL):
new_fields += item._table
else:
new_fields.append(item)
fields = new_fields
if query:
tablename = self.get_table(query)
@@ -5129,6 +5133,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
"text and blob field types not allowed in projection queries")
else:
projection.append(f.name)
elif args_get('filterfields') == True:
projection = []
for f in fields:
@@ -5142,22 +5147,19 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
args_get('projection') == True\
else None
cursor = None
if isinstance(args_get('reusecursor'), str):
cursor = args_get('reusecursor')
cursor = args_get('reusecursor')
cursor = cursor if isinstance(cursor, str) else None
if self.use_ndb:
qo = ndb.QueryOptions(projection=query_projection, cursor=cursor)
items = tableobj.query(default_options=qo)
else:
items = gae.Query(tableobj, projection=query_projection,
cursor=cursor)
items = gae.Query(tableobj, projection=query_projection, cursor=cursor)
for filter in filters:
if args_get('projection') == True and \
filter.name in query_projection and \
filter.op in ['=', '<=', '>=']:
raise SyntaxError(
"projection fields cannot have equality filters")
if (args_get('projection') == True and
filter.name in query_projection and
filter.op in ('=', '<=', '>=')):
raise SyntaxError("projection fields cannot have equality filters")
if filter.name=='__key__' and filter.op=='>' and filter.value==0:
continue
elif filter.name=='__key__' and filter.op=='=':
@@ -5168,29 +5170,26 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
# can't use projection
# extra values will be ignored in post-processing later
item = filter.value.get() if self.use_ndb else tableobj.get(filter.value)
items = (item and [item]) or []
items = [item] if item else []
else:
# key qeuries return a class instance,
# can't use projection
# extra values will be ignored in post-processing later
item = tableobj.get_by_id(filter.value)
items = (item and [item]) or []
items = [item] if item else []
elif isinstance(items,list): # i.e. there is a single record!
items = [i for i in items if filter.apply(
getattr(item,filter.name),filter.value)]
else:
else:
if filter.name=='__key__' and filter.op != 'in':
if self.use_ndb:
items.order(tableobj._key)
else:
items.order('__key__')
items = self.filter(items, tableobj, filter.name,
filter.op, filter.value) \
if self.use_ndb else \
items.filter('%s %s' % (filter.name,filter.op),
filter.value)
items.order(tableobj._key) if self.use_ndb else items.order('__key__')
if self.use_ndb:
items = self.filter(items, tableobj, filter.name, filter.op, filter.value)
else:
items = items.filter('%s %s' % (filter.name,filter.op), filter.value)
if not isinstance(items,list):
query = items
if args_get('left', None):
raise SyntaxError('Set: no left join in appengine')
if args_get('groupby', None):
@@ -5214,26 +5213,29 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
_order = {'-id':-tableobj._key,'id':tableobj._key}.get(order)
if _order is None:
_order = make_order(order)
items = items.order(_order)
query = query.order(_order)
else:
order={'-id':'-__key__','id':'__key__'}.get(order,order)
items = items.order(order)
query = query.order(order)
if args_get('limitby', None):
(lmin, lmax) = attributes['limitby']
(limit, offset) = (lmax - lmin, lmin)
if self.use_ndb:
rows, cursor, more = items.fetch_page(limit,offset=offset,keys_only=True)
else:
rows = items.fetch(limit,offset=offset,keys_only=True)
rows = ndb.get_multi(rows) if self.use_ndb else gae.get(rows)
#cursor is only useful if there was a limit and we didn't return
# all results
if args_get('reusecursor'):
db['_lastcursor'] = cursor if self.use_ndb else items.cursor()
items = rows
limit, fetch_args = lmax-lmin, {'offset':lmin,'keys_only':True}
else:
limit, fetch_args = self.MAX_FETCH_LIMIT, {'offset':0,'keys_only':True}
if self.use_ndb:
keys, cursor, more = query.fetch_page(limit,**fetch_args)
items = ndb.get_multi(keys)
else:
keys = query.fetch(limit, **fetch_args)
items = gae.get(keys)
cursor = query.cursor()
#cursor is only useful if there was a limit and we didn't return
# all results
if args_get('reusecursor'):
db['_lastcursor'] = cursor
return (items, tablename, projection or db[tablename].fields)
def select(self,query,fields,attributes):
@@ -7177,6 +7179,7 @@ ADAPTERS = {
'jdbc:postgres': JDBCPostgreSQLAdapter,
'gae': GoogleDatastoreAdapter, # discouraged, for backward compatibility
'google:datastore': GoogleDatastoreAdapter,
'google:datastore+ndb': GoogleDatastoreAdapter,
'google:sql': GoogleSQLAdapter,
'couchdb': CouchDBAdapter,
'mongodb': MongoDBAdapter,