thirdparty/google_appengine/google/appengine/api/datastore_file_stub.py
changeset 2864 2e0b0af889be
parent 2413 d0b7dac5325c
child 3031 7678f72140e6
equal deleted inserted replaced
2862:27971a13089f 2864:2e0b0af889be
    73 _MAX_QUERY_OFFSET = 1000
    73 _MAX_QUERY_OFFSET = 1000
    74 
    74 
    75 
    75 
    76 _MAX_QUERY_COMPONENTS = 100
    76 _MAX_QUERY_COMPONENTS = 100
    77 
    77 
       
    78 _BATCH_SIZE = 20
    78 
    79 
    79 class _StoredEntity(object):
    80 class _StoredEntity(object):
    80   """Simple wrapper around an entity stored by the stub.
    81   """Simple wrapper around an entity stored by the stub.
    81 
    82 
    82   Public properties:
    83   Public properties:
   103 
   104 
   104   Public properties:
   105   Public properties:
   105     cursor: the integer cursor
   106     cursor: the integer cursor
   106     count: the original total number of results
   107     count: the original total number of results
   107     keys_only: whether the query is keys_only
   108     keys_only: whether the query is keys_only
       
   109 
       
   110   Class attributes:
       
   111     _next_cursor: the next cursor to allocate
       
   112     _next_cursor_lock: protects _next_cursor
   108   """
   113   """
       
   114   _next_cursor = 1
       
   115   _next_cursor_lock = threading.Lock()
       
   116 
   109   def __init__(self, results, keys_only):
   117   def __init__(self, results, keys_only):
   110     """Constructor.
   118     """Constructor.
   111 
   119 
   112     Args:
   120     Args:
   113       # the query results, in order, such that pop(0) is the next result
   121       # the query results, in order, such that pop(0) is the next result
   115       keys_only: integer
   123       keys_only: integer
   116     """
   124     """
   117     self.__results = results
   125     self.__results = results
   118     self.count = len(results)
   126     self.count = len(results)
   119     self.keys_only = keys_only
   127     self.keys_only = keys_only
   120     self.cursor = id(self)
   128 
       
   129     self._next_cursor_lock.acquire()
       
   130     try:
       
   131       self.cursor = _Cursor._next_cursor
       
   132       _Cursor._next_cursor += 1
       
   133     finally:
       
   134       self._next_cursor_lock.release()
   121 
   135 
   122   def PopulateQueryResult(self, result, count):
   136   def PopulateQueryResult(self, result, count):
   123     """Populates a QueryResult with this cursor and the given number of results.
   137     """Populates a QueryResult with this cursor and the given number of results.
   124 
   138 
   125     Args:
   139     Args:
   270     """
   284     """
   271     if not self.__trusted and app_id != self.__app_id:
   285     if not self.__trusted and app_id != self.__app_id:
   272       raise datastore_errors.BadRequestError(
   286       raise datastore_errors.BadRequestError(
   273           'app %s cannot access app %s\'s data' % (self.__app_id, app_id))
   287           'app %s cannot access app %s\'s data' % (self.__app_id, app_id))
   274 
   288 
   275 
   289   def __ValidateKey(self, key):
   276   def _AppKindForKey(self, key):
   290     """Validate this key.
       
   291 
       
   292     Args:
       
   293       key: entity_pb.Reference
       
   294 
       
   295     Raises:
       
   296       datastore_errors.BadRequestError: if the key is invalid
       
   297     """
       
   298     assert isinstance(key, entity_pb.Reference)
       
   299 
       
   300     self.__ValidateAppId(key.app())
       
   301 
       
   302     for elem in key.path().element_list():
       
   303       if elem.has_id() == elem.has_name():
       
   304         raise datastore_errors.BadRequestError(
       
   305           'each key path element should have id or name but not both: %r' % key)
       
   306 
       
   307   def _AppIdNamespaceKindForKey(self, key):
   277     """ Get (app, kind) tuple from given key.
   308     """ Get (app, kind) tuple from given key.
   278 
   309 
   279     The (app, kind) tuple is used as an index into several internal
   310     The (app, kind) tuple is used as an index into several internal
   280     dictionaries, e.g. __entities.
   311     dictionaries, e.g. __entities.
   281 
   312 
   293 
   324 
   294     Args:
   325     Args:
   295       entity: entity_pb.EntityProto
   326       entity: entity_pb.EntityProto
   296     """
   327     """
   297     key = entity.key()
   328     key = entity.key()
   298     app_kind = self._AppKindForKey(key)
   329     app_kind = self._AppIdNamespaceKindForKey(key)
   299     if app_kind not in self.__entities:
   330     if app_kind not in self.__entities:
   300       self.__entities[app_kind] = {}
   331       self.__entities[app_kind] = {}
   301     self.__entities[app_kind][key] = _StoredEntity(entity)
   332     self.__entities[app_kind][key] = _StoredEntity(entity)
   302 
   333 
   303     if app_kind in self.__schema_cache:
   334     if app_kind in self.__schema_cache:
   438         os.rename(tmpfile.name, filename)
   469         os.rename(tmpfile.name, filename)
   439     finally:
   470     finally:
   440       self.__file_lock.release()
   471       self.__file_lock.release()
   441 
   472 
   442   def MakeSyncCall(self, service, call, request, response):
   473   def MakeSyncCall(self, service, call, request, response):
   443     """ The main RPC entry point. service must be 'datastore_v3'. So far, the
   474     """ The main RPC entry point. service must be 'datastore_v3'.
   444     supported calls are 'Get', 'Put', 'RunQuery', 'Next', and 'Count'.
   475     """
   445     """
   476     self.assertPbIsInitialized(request)
   446     super(DatastoreFileStub, self).MakeSyncCall(service,
   477     super(DatastoreFileStub, self).MakeSyncCall(service,
   447                                                 call,
   478                                                 call,
   448                                                 request,
   479                                                 request,
   449                                                 response)
   480                                                 response)
   450 
   481     self.assertPbIsInitialized(response)
       
   482 
       
   483   def assertPbIsInitialized(self, pb):
       
   484     """Raises an exception if the given PB is not initialized and valid."""
   451     explanation = []
   485     explanation = []
   452     assert response.IsInitialized(explanation), explanation
   486     assert pb.IsInitialized(explanation), explanation
       
   487     pb.Encode()
   453 
   488 
   454   def QueryHistory(self):
   489   def QueryHistory(self):
   455     """Returns a dict that maps Query PBs to times they've been run.
   490     """Returns a dict that maps Query PBs to times they've been run.
   456     """
   491     """
   457     return dict((pb, times) for pb, times in self.__query_history.items()
   492     return dict((pb, times) for pb, times in self.__query_history.items()
   458                 if pb.app() == self.__app_id)
   493                 if pb.app() == self.__app_id)
   459 
   494 
   460   def _Dynamic_Put(self, put_request, put_response):
   495   def _Dynamic_Put(self, put_request, put_response):
   461     clones = []
   496     clones = []
   462     for entity in put_request.entity_list():
   497     for entity in put_request.entity_list():
   463       self.__ValidateAppId(entity.key().app())
   498       self.__ValidateKey(entity.key())
   464 
   499 
   465       clone = entity_pb.EntityProto()
   500       clone = entity_pb.EntityProto()
   466       clone.CopyFrom(entity)
   501       clone.CopyFrom(entity)
   467 
   502 
   468       for property in clone.property_list():
   503       for property in clone.property_list():
   513     else:
   548     else:
   514       entities = self.__entities
   549       entities = self.__entities
   515 
   550 
   516     for key in get_request.key_list():
   551     for key in get_request.key_list():
   517       self.__ValidateAppId(key.app())
   552       self.__ValidateAppId(key.app())
   518       app_kind = self._AppKindForKey(key)
   553       app_kind = self._AppIdNamespaceKindForKey(key)
   519 
   554 
   520       group = get_response.add_entity()
   555       group = get_response.add_entity()
   521       try:
   556       try:
   522         entity = entities[app_kind][key].protobuf
   557         entity = entities[app_kind][key].protobuf
   523       except KeyError:
   558       except KeyError:
   530   def _Dynamic_Delete(self, delete_request, delete_response):
   565   def _Dynamic_Delete(self, delete_request, delete_response):
   531     self.__entities_lock.acquire()
   566     self.__entities_lock.acquire()
   532     try:
   567     try:
   533       for key in delete_request.key_list():
   568       for key in delete_request.key_list():
   534         self.__ValidateAppId(key.app())
   569         self.__ValidateAppId(key.app())
   535         app_kind = self._AppKindForKey(key)
   570         app_kind = self._AppIdNamespaceKindForKey(key)
   536         try:
   571         try:
   537           del self.__entities[app_kind][key]
   572           del self.__entities[app_kind][key]
   538           if not self.__entities[app_kind]:
   573           if not self.__entities[app_kind]:
   539             del self.__entities[app_kind]
   574             del self.__entities[app_kind]
   540 
   575 
   557       entities = self.__tx_snapshot
   592       entities = self.__tx_snapshot
   558     else:
   593     else:
   559       entities = self.__entities
   594       entities = self.__entities
   560       self.__tx_lock.release()
   595       self.__tx_lock.release()
   561 
   596 
   562     app = query.app()
   597     app_id_namespace = datastore_types.parse_app_id_namespace(query.app())
   563     self.__ValidateAppId(app)
   598     app_id = app_id_namespace.app_id()
       
   599     self.__ValidateAppId(app_id)
   564 
   600 
   565     if query.has_offset() and query.offset() > _MAX_QUERY_OFFSET:
   601     if query.has_offset() and query.offset() > _MAX_QUERY_OFFSET:
   566       raise apiproxy_errors.ApplicationError(
   602       raise apiproxy_errors.ApplicationError(
   567           datastore_pb.Error.BAD_REQUEST, 'Too big query offset.')
   603           datastore_pb.Error.BAD_REQUEST, 'Too big query offset.')
   568 
   604 
   573       raise apiproxy_errors.ApplicationError(
   609       raise apiproxy_errors.ApplicationError(
   574           datastore_pb.Error.BAD_REQUEST,
   610           datastore_pb.Error.BAD_REQUEST,
   575           ('query is too large. may not have more than %s filters'
   611           ('query is too large. may not have more than %s filters'
   576            ' + sort orders ancestor total' % _MAX_QUERY_COMPONENTS))
   612            ' + sort orders ancestor total' % _MAX_QUERY_COMPONENTS))
   577 
   613 
       
   614     (filters, orders) = datastore_index.Normalize(query.filter_list(),
       
   615                                                   query.order_list())
       
   616 
   578     if self.__require_indexes:
   617     if self.__require_indexes:
   579       required, kind, ancestor, props, num_eq_filters = datastore_index.CompositeIndexForQuery(query)
   618       required, kind, ancestor, props, num_eq_filters = datastore_index.CompositeIndexForQuery(query)
   580       if required:
   619       if required:
   581         required_key = kind, ancestor, props
   620         required_key = kind, ancestor, props
   582         indexes = self.__indexes.get(app)
   621         indexes = self.__indexes.get(app_id)
   583         if not indexes:
   622         if not indexes:
   584           raise apiproxy_errors.ApplicationError(
   623           raise apiproxy_errors.ApplicationError(
   585               datastore_pb.Error.NEED_INDEX,
   624               datastore_pb.Error.NEED_INDEX,
   586               "This query requires a composite index, but none are defined. "
   625               "This query requires a composite index, but none are defined. "
   587               "You must create an index.yaml file in your application root.")
   626               "You must create an index.yaml file in your application root.")
   604               datastore_pb.Error.NEED_INDEX,
   643               datastore_pb.Error.NEED_INDEX,
   605               "This query requires a composite index that is not defined. "
   644               "This query requires a composite index that is not defined. "
   606               "You must update the index.yaml file in your application root.")
   645               "You must update the index.yaml file in your application root.")
   607 
   646 
   608     try:
   647     try:
   609       query.set_app(app)
   648       query.set_app(app_id_namespace.to_encoded())
   610       results = entities[app, query.kind()].values()
   649       if query.has_kind():
   611       results = [entity.native for entity in results]
   650         results = entities[app_id_namespace.to_encoded(), query.kind()].values()
       
   651         results = [entity.native for entity in results]
       
   652       else:
       
   653         results = []
       
   654         for key in entities:
       
   655           if key[0] == app_id_namespace.to_encoded():
       
   656             results += [entity.native for entity in entities[key].values()]
   612     except KeyError:
   657     except KeyError:
   613       results = []
   658       results = []
   614 
   659 
   615     if query.has_ancestor():
   660     if query.has_ancestor():
   616       ancestor_path = query.ancestor().path().element_list()
   661       ancestor_path = query.ancestor().path().element_list()
   640       for value in values:
   685       for value in values:
   641         if type(value) not in datastore_types._RAW_PROPERTY_TYPES:
   686         if type(value) not in datastore_types._RAW_PROPERTY_TYPES:
   642           return True
   687           return True
   643       return False
   688       return False
   644 
   689 
   645     for filt in query.filter_list():
   690     for filt in filters:
   646       assert filt.op() != datastore_pb.Query_Filter.IN
   691       assert filt.op() != datastore_pb.Query_Filter.IN
   647 
   692 
   648       prop = filt.property(0).name().decode('utf-8')
   693       prop = filt.property(0).name().decode('utf-8')
   649       op = operators[filt.op()]
   694       op = operators[filt.op()]
   650 
   695 
   692 
   737 
   693         return False
   738         return False
   694 
   739 
   695       results = filter(passes_filter, results)
   740       results = filter(passes_filter, results)
   696 
   741 
   697     for order in query.order_list():
   742     for order in orders:
   698       prop = order.property().decode('utf-8')
   743       prop = order.property().decode('utf-8')
   699       results = [entity for entity in results if has_prop_indexed(entity, prop)]
   744       results = [entity for entity in results if has_prop_indexed(entity, prop)]
   700 
   745 
   701     def order_compare_entities(a, b):
   746     def order_compare_entities(a, b):
   702       """ Return a negative, zero or positive number depending on whether
   747       """ Return a negative, zero or positive number depending on whether
   703       entity a is considered smaller than, equal to, or larger than b,
   748       entity a is considered smaller than, equal to, or larger than b,
   704       according to the query's orderings. """
   749       according to the query's orderings. """
   705       cmped = 0
   750       cmped = 0
   706       for o in query.order_list():
   751       for o in orders:
   707         prop = o.property().decode('utf-8')
   752         prop = o.property().decode('utf-8')
   708 
   753 
   709         reverse = (o.direction() is datastore_pb.Query_Order.DESCENDING)
   754         reverse = (o.direction() is datastore_pb.Query_Order.DESCENDING)
   710 
   755 
   711         a_val = datastore._GetPropertyValue(a, prop)
   756         a_val = datastore._GetPropertyValue(a, prop)
   771       self.__query_history[clone] = 1
   816       self.__query_history[clone] = 1
   772     self.__WriteHistory()
   817     self.__WriteHistory()
   773 
   818 
   774     cursor = _Cursor(results, query.keys_only())
   819     cursor = _Cursor(results, query.keys_only())
   775     self.__queries[cursor.cursor] = cursor
   820     self.__queries[cursor.cursor] = cursor
   776     cursor.PopulateQueryResult(query_result, 0)
   821 
       
   822     if query.has_count():
       
   823       count = query.count()
       
   824     elif query.has_limit():
       
   825       count = query.limit()
       
   826     else:
       
   827       count = _BATCH_SIZE
       
   828 
       
   829     cursor.PopulateQueryResult(query_result, count)
   777 
   830 
   778   def _Dynamic_Next(self, next_request, query_result):
   831   def _Dynamic_Next(self, next_request, query_result):
   779     cursor_handle = next_request.cursor().cursor()
   832     cursor_handle = next_request.cursor().cursor()
   780 
   833 
   781     try:
   834     try:
   782       cursor = self.__queries[cursor_handle]
   835       cursor = self.__queries[cursor_handle]
   783     except KeyError:
   836     except KeyError:
   784       raise apiproxy_errors.ApplicationError(
   837       raise apiproxy_errors.ApplicationError(
   785           datastore_pb.Error.BAD_REQUEST, 'Cursor %d not found' % cursor_handle)
   838           datastore_pb.Error.BAD_REQUEST, 'Cursor %d not found' % cursor_handle)
   786 
   839 
   787     cursor.PopulateQueryResult(query_result, next_request.count())
   840     count = _BATCH_SIZE
       
   841     if next_request.has_count():
       
   842       count = next_request.count()
       
   843     cursor.PopulateQueryResult(query_result, count)
   788 
   844 
   789   def _Dynamic_Count(self, query, integer64proto):
   845   def _Dynamic_Count(self, query, integer64proto):
   790     self.__ValidateAppId(query.app())
   846     self.__ValidateAppId(query.app())
   791     query_result = datastore_pb.QueryResult()
   847     query_result = datastore_pb.QueryResult()
   792     self._Dynamic_RunQuery(query, query_result)
   848     self._Dynamic_RunQuery(query, query_result)
   828 
   884 
   829     self.__entities = self.__tx_snapshot
   885     self.__entities = self.__tx_snapshot
   830     self.__tx_snapshot = {}
   886     self.__tx_snapshot = {}
   831     self.__tx_lock.release()
   887     self.__tx_lock.release()
   832 
   888 
   833   def _Dynamic_GetSchema(self, app_str, schema):
   889   def _Dynamic_GetSchema(self, req, schema):
   834     minint = -sys.maxint - 1
   890     app_str = req.app()
   835     try:
       
   836       minfloat = float('-inf')
       
   837     except ValueError:
       
   838       minfloat = -1e300000
       
   839 
       
   840     app_str = app_str.value()
       
   841     self.__ValidateAppId(app_str)
   891     self.__ValidateAppId(app_str)
   842 
   892 
   843     kinds = []
   893     kinds = []
   844 
   894 
   845     for app, kind in self.__entities:
   895     for app, kind in self.__entities:
   846       if app == app_str:
   896       if (app != app_str or
   847         app_kind = (app, kind)
   897           (req.has_start_kind() and kind < req.start_kind()) or
   848         if app_kind in self.__schema_cache:
   898           (req.has_end_kind() and kind > req.end_kind())):
   849           kinds.append(self.__schema_cache[app_kind])
   899         continue
   850           continue
   900 
   851 
   901       app_kind = (app, kind)
   852         kind_pb = entity_pb.EntityProto()
   902       if app_kind in self.__schema_cache:
   853         kind_pb.mutable_key().set_app('')
   903         kinds.append(self.__schema_cache[app_kind])
   854         kind_pb.mutable_key().mutable_path().add_element().set_type(kind)
   904         continue
   855         kind_pb.mutable_entity_group()
   905 
   856 
   906       kind_pb = entity_pb.EntityProto()
   857         props = {}
   907       kind_pb.mutable_key().set_app('')
   858 
   908       kind_pb.mutable_key().mutable_path().add_element().set_type(kind)
   859         for entity in self.__entities[app_kind].values():
   909       kind_pb.mutable_entity_group()
   860           for prop in entity.protobuf.property_list():
   910 
   861             if prop.name() not in props:
   911       props = {}
   862               props[prop.name()] = entity_pb.PropertyValue()
   912 
   863             props[prop.name()].MergeFrom(prop.value())
   913       for entity in self.__entities[app_kind].values():
   864 
   914         for prop in entity.protobuf.property_list():
   865         for value_pb in props.values():
   915           if prop.name() not in props:
   866           if value_pb.has_int64value():
   916             props[prop.name()] = entity_pb.PropertyValue()
   867             value_pb.set_int64value(minint)
   917           props[prop.name()].MergeFrom(prop.value())
   868           if value_pb.has_booleanvalue():
   918 
   869             value_pb.set_booleanvalue(False)
   919       for value_pb in props.values():
   870           if value_pb.has_stringvalue():
   920         if value_pb.has_int64value():
   871             value_pb.set_stringvalue('')
   921           value_pb.set_int64value(0)
   872           if value_pb.has_doublevalue():
   922         if value_pb.has_booleanvalue():
   873             value_pb.set_doublevalue(minfloat)
   923           value_pb.set_booleanvalue(False)
   874           if value_pb.has_pointvalue():
   924         if value_pb.has_stringvalue():
   875             value_pb.mutable_pointvalue().set_x(minfloat)
   925           value_pb.set_stringvalue('none')
   876             value_pb.mutable_pointvalue().set_y(minfloat)
   926         if value_pb.has_doublevalue():
   877           if value_pb.has_uservalue():
   927           value_pb.set_doublevalue(0.0)
   878             value_pb.mutable_uservalue().set_gaiaid(minint)
   928         if value_pb.has_pointvalue():
   879             value_pb.mutable_uservalue().set_email('')
   929           value_pb.mutable_pointvalue().set_x(0.0)
   880             value_pb.mutable_uservalue().set_auth_domain('')
   930           value_pb.mutable_pointvalue().set_y(0.0)
   881             value_pb.mutable_uservalue().clear_nickname()
   931         if value_pb.has_uservalue():
   882           elif value_pb.has_referencevalue():
   932           value_pb.mutable_uservalue().set_gaiaid(0)
   883             value_pb.clear_referencevalue()
   933           value_pb.mutable_uservalue().set_email('none')
   884             value_pb.mutable_referencevalue().set_app('')
   934           value_pb.mutable_uservalue().set_auth_domain('none')
   885 
   935           value_pb.mutable_uservalue().clear_nickname()
   886         for name, value_pb in props.items():
   936           value_pb.mutable_uservalue().clear_obfuscated_gaiaid()
   887           prop_pb = kind_pb.add_property()
   937         if value_pb.has_referencevalue():
   888           prop_pb.set_name(name)
   938           value_pb.clear_referencevalue()
   889           prop_pb.set_multiple(False)
   939           value_pb.mutable_referencevalue().set_app('none')
   890           prop_pb.mutable_value().CopyFrom(value_pb)
   940           pathelem = value_pb.mutable_referencevalue().add_pathelement()
   891 
   941           pathelem.set_type('none')
   892         kinds.append(kind_pb)
   942           pathelem.set_name('none')
   893         self.__schema_cache[app_kind] = kind_pb
   943 
       
   944       for name, value_pb in props.items():
       
   945         prop_pb = kind_pb.add_property()
       
   946         prop_pb.set_name(name)
       
   947         prop_pb.set_multiple(False)
       
   948         prop_pb.mutable_value().CopyFrom(value_pb)
       
   949 
       
   950       kinds.append(kind_pb)
       
   951       self.__schema_cache[app_kind] = kind_pb
   894 
   952 
   895     for kind_pb in kinds:
   953     for kind_pb in kinds:
   896       schema.add_kind().CopyFrom(kind_pb)
   954       kind = schema.add_kind()
       
   955       kind.CopyFrom(kind_pb)
       
   956       if not req.properties():
       
   957         kind.clear_property()
       
   958 
       
   959     schema.set_more_results(False)
       
   960 
       
   961   def _Dynamic_AllocateIds(self, allocate_ids_request, allocate_ids_response):
       
   962     model_key = allocate_ids_request.model_key()
       
   963     size = allocate_ids_request.size()
       
   964 
       
   965     self.__ValidateAppId(model_key.app())
       
   966 
       
   967     try:
       
   968       self.__id_lock.acquire()
       
   969       start = self.__next_id
       
   970       self.__next_id += size
       
   971       end = self.__next_id - 1
       
   972     finally:
       
   973      self.__id_lock.release()
       
   974 
       
   975     allocate_ids_response.set_start(start)
       
   976     allocate_ids_response.set_end(end)
   897 
   977 
   898   def _Dynamic_CreateIndex(self, index, id_response):
   978   def _Dynamic_CreateIndex(self, index, id_response):
   899     self.__ValidateAppId(index.app_id())
   979     self.__ValidateAppId(index.app_id())
   900     if index.id() != 0:
   980     if index.id() != 0:
   901       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   981       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,