thirdparty/google_appengine/google/appengine/api/datastore_file_stub.py
changeset 2273 e4cb9c53db3e
parent 1278 a7766286a7be
child 2309 be1b94099f2d
equal deleted inserted replaced
2272:26491ee91e33 2273:e4cb9c53db3e
    35 
    35 
    36 
    36 
    37 
    37 
    38 import datetime
    38 import datetime
    39 import logging
    39 import logging
       
    40 import md5
    40 import os
    41 import os
    41 import struct
    42 import struct
    42 import sys
    43 import sys
    43 import tempfile
    44 import tempfile
    44 import threading
    45 import threading
   143   def __init__(self,
   144   def __init__(self,
   144                app_id,
   145                app_id,
   145                datastore_file,
   146                datastore_file,
   146                history_file,
   147                history_file,
   147                require_indexes=False,
   148                require_indexes=False,
   148                service_name='datastore_v3'):
   149                service_name='datastore_v3',
       
   150                trusted=False):
   149     """Constructor.
   151     """Constructor.
   150 
   152 
   151     Initializes and loads the datastore from the backing files, if they exist.
   153     Initializes and loads the datastore from the backing files, if they exist.
   152 
   154 
   153     Args:
   155     Args:
   157       history_file: string, stores query history.  Use None as with
   159       history_file: string, stores query history.  Use None as with
   158           datastore_file.
   160           datastore_file.
   159       require_indexes: bool, default False.  If True, composite indexes must
   161       require_indexes: bool, default False.  If True, composite indexes must
   160           exist in index.yaml for queries that need them.
   162           exist in index.yaml for queries that need them.
   161       service_name: Service name expected for all calls.
   163       service_name: Service name expected for all calls.
       
   164       trusted: bool, default False.  If True, this stub allows an app to
       
   165         access the data of another app.
   162     """
   166     """
   163     super(DatastoreFileStub, self).__init__(service_name)
   167     super(DatastoreFileStub, self).__init__(service_name)
   164 
   168 
   165 
   169 
   166     assert isinstance(app_id, basestring) and app_id != ''
   170     assert isinstance(app_id, basestring) and app_id != ''
   167     self.__app_id = app_id
   171     self.__app_id = app_id
   168     self.__datastore_file = datastore_file
   172     self.__datastore_file = datastore_file
   169     self.__history_file = history_file
   173     self.__history_file = history_file
       
   174     self.SetTrusted(trusted)
   170 
   175 
   171     self.__entities = {}
   176     self.__entities = {}
   172 
   177 
   173     self.__schema_cache = {}
   178     self.__schema_cache = {}
   174 
   179 
   204     self.__entities = {}
   209     self.__entities = {}
   205     self.__queries = {}
   210     self.__queries = {}
   206     self.__transactions = {}
   211     self.__transactions = {}
   207     self.__query_history = {}
   212     self.__query_history = {}
   208     self.__schema_cache = {}
   213     self.__schema_cache = {}
       
   214 
       
   215   def SetTrusted(self, trusted):
       
   216     """Set/clear the trusted bit in the stub.
       
   217 
       
   218     This bit indicates that the app calling the stub is trusted. A
       
   219     trusted app can write to datastores of other apps.
       
   220 
       
   221     Args:
       
   222       trusted: boolean.
       
   223     """
       
   224     self.__trusted = trusted
       
   225 
       
   226   def __ValidateAppId(self, app_id):
       
   227     """Verify that this is the stub for app_id.
       
   228 
       
   229     Args:
       
   230       app_id: An application ID.
       
   231 
       
   232     Raises:
       
   233       datastore_errors.BadRequestError: if this is not the stub for app_id.
       
   234     """
       
   235     if not self.__trusted and app_id != self.__app_id:
       
   236       raise datastore_errors.BadRequestError(
       
   237           'app %s cannot access app %s\'s data' % (self.__app_id, app_id))
       
   238 
   209 
   239 
   210   def _AppKindForKey(self, key):
   240   def _AppKindForKey(self, key):
   211     """ Get (app, kind) tuple from given key.
   241     """ Get (app, kind) tuple from given key.
   212 
   242 
   213     The (app, kind) tuple is used as an index into several internal
   243     The (app, kind) tuple is used as an index into several internal
   334       try:
   364       try:
   335         if filename and filename != '/dev/null' and os.path.isfile(filename):
   365         if filename and filename != '/dev/null' and os.path.isfile(filename):
   336           return pickle.load(open(filename, 'rb'))
   366           return pickle.load(open(filename, 'rb'))
   337         else:
   367         else:
   338           logging.warning('Could not read datastore data from %s', filename)
   368           logging.warning('Could not read datastore data from %s', filename)
   339       except (AttributeError, LookupError, NameError, TypeError,
   369       except (AttributeError, LookupError, ImportError, NameError, TypeError,
   340               ValueError, struct.error, pickle.PickleError), e:
   370               ValueError, struct.error, pickle.PickleError), e:
   341         raise datastore_errors.InternalError(
   371         raise datastore_errors.InternalError(
   342           'Could not read data from %s. Try running with the '
   372           'Could not read data from %s. Try running with the '
   343           '--clear_datastore flag. Cause:\n%r' % (filename, e))
   373           '--clear_datastore flag. Cause:\n%r' % (filename, e))
   344     finally:
   374     finally:
   392                 if pb.app() == self.__app_id)
   422                 if pb.app() == self.__app_id)
   393 
   423 
   394   def _Dynamic_Put(self, put_request, put_response):
   424   def _Dynamic_Put(self, put_request, put_response):
   395     clones = []
   425     clones = []
   396     for entity in put_request.entity_list():
   426     for entity in put_request.entity_list():
       
   427       self.__ValidateAppId(entity.key().app())
       
   428 
   397       clone = entity_pb.EntityProto()
   429       clone = entity_pb.EntityProto()
   398       clone.CopyFrom(entity)
   430       clone.CopyFrom(entity)
       
   431 
       
   432       for property in clone.property_list():
       
   433         if property.value().has_uservalue():
       
   434           uid = md5.new(property.value().uservalue().email().lower()).digest()
       
   435           uid = '1' + ''.join(['%02d' % ord(x) for x in uid])[:20]
       
   436           property.mutable_value().mutable_uservalue().set_obfuscated_gaiaid(
       
   437               uid)
       
   438 
   399       clones.append(clone)
   439       clones.append(clone)
   400 
   440 
   401       assert clone.has_key()
   441       assert clone.has_key()
   402       assert clone.key().path().element_size() > 0
   442       assert clone.key().path().element_size() > 0
   403 
   443 
   431     put_response.key_list().extend([c.key() for c in clones])
   471     put_response.key_list().extend([c.key() for c in clones])
   432 
   472 
   433 
   473 
   434   def _Dynamic_Get(self, get_request, get_response):
   474   def _Dynamic_Get(self, get_request, get_response):
   435     for key in get_request.key_list():
   475     for key in get_request.key_list():
   436         app_kind = self._AppKindForKey(key)
   476       self.__ValidateAppId(key.app())
   437 
   477       app_kind = self._AppKindForKey(key)
   438         group = get_response.add_entity()
   478 
   439         try:
   479       group = get_response.add_entity()
   440           entity = self.__entities[app_kind][key].protobuf
   480       try:
   441         except KeyError:
   481         entity = self.__entities[app_kind][key].protobuf
   442           entity = None
   482       except KeyError:
   443 
   483         entity = None
   444         if entity:
   484 
   445           group.mutable_entity().CopyFrom(entity)
   485       if entity:
       
   486         group.mutable_entity().CopyFrom(entity)
   446 
   487 
   447 
   488 
   448   def _Dynamic_Delete(self, delete_request, delete_response):
   489   def _Dynamic_Delete(self, delete_request, delete_response):
   449     self.__entities_lock.acquire()
   490     self.__entities_lock.acquire()
   450     try:
   491     try:
   451       for key in delete_request.key_list():
   492       for key in delete_request.key_list():
       
   493         self.__ValidateAppId(key.app())
   452         app_kind = self._AppKindForKey(key)
   494         app_kind = self._AppKindForKey(key)
   453         try:
   495         try:
   454           del self.__entities[app_kind][key]
   496           del self.__entities[app_kind][key]
   455           if not self.__entities[app_kind]:
   497           if not self.__entities[app_kind]:
   456             del self.__entities[app_kind]
   498             del self.__entities[app_kind]
   469     if not self.__tx_lock.acquire(False):
   511     if not self.__tx_lock.acquire(False):
   470       raise apiproxy_errors.ApplicationError(
   512       raise apiproxy_errors.ApplicationError(
   471           datastore_pb.Error.BAD_REQUEST, 'Can\'t query inside a transaction.')
   513           datastore_pb.Error.BAD_REQUEST, 'Can\'t query inside a transaction.')
   472     else:
   514     else:
   473       self.__tx_lock.release()
   515       self.__tx_lock.release()
       
   516 
       
   517     app = query.app()
       
   518     self.__ValidateAppId(app)
   474 
   519 
   475     if query.has_offset() and query.offset() > _MAX_QUERY_OFFSET:
   520     if query.has_offset() and query.offset() > _MAX_QUERY_OFFSET:
   476       raise apiproxy_errors.ApplicationError(
   521       raise apiproxy_errors.ApplicationError(
   477           datastore_pb.Error.BAD_REQUEST, 'Too big query offset.')
   522           datastore_pb.Error.BAD_REQUEST, 'Too big query offset.')
   478 
   523 
   482     if num_components > _MAX_QUERY_COMPONENTS:
   527     if num_components > _MAX_QUERY_COMPONENTS:
   483       raise apiproxy_errors.ApplicationError(
   528       raise apiproxy_errors.ApplicationError(
   484           datastore_pb.Error.BAD_REQUEST,
   529           datastore_pb.Error.BAD_REQUEST,
   485           ('query is too large. may not have more than %s filters'
   530           ('query is too large. may not have more than %s filters'
   486            ' + sort orders ancestor total' % _MAX_QUERY_COMPONENTS))
   531            ' + sort orders ancestor total' % _MAX_QUERY_COMPONENTS))
   487 
       
   488     app = query.app()
       
   489 
   532 
   490     if self.__require_indexes:
   533     if self.__require_indexes:
   491       required, kind, ancestor, props, num_eq_filters = datastore_index.CompositeIndexForQuery(query)
   534       required, kind, ancestor, props, num_eq_filters = datastore_index.CompositeIndexForQuery(query)
   492       if required:
   535       if required:
   493         required_key = kind, ancestor, props
   536         required_key = kind, ancestor, props
   699     except KeyError:
   742     except KeyError:
   700       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   743       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   701                                              'Cursor %d not found' % cursor)
   744                                              'Cursor %d not found' % cursor)
   702 
   745 
   703     count = next_request.count()
   746     count = next_request.count()
       
   747 
   704     results_pb = [r._ToPb() for r in results[:count]]
   748     results_pb = [r._ToPb() for r in results[:count]]
   705     query_result.result_list().extend(results_pb)
   749     query_result.result_list().extend(results_pb)
   706     del results[:count]
   750     del results[:count]
   707 
   751 
   708     query_result.set_more_results(len(results) > 0)
   752     query_result.set_more_results(len(results) > 0)
   709 
   753 
   710   def _Dynamic_Count(self, query, integer64proto):
   754   def _Dynamic_Count(self, query, integer64proto):
       
   755     self.__ValidateAppId(query.app())
   711     query_result = datastore_pb.QueryResult()
   756     query_result = datastore_pb.QueryResult()
   712     self._Dynamic_RunQuery(query, query_result)
   757     self._Dynamic_RunQuery(query, query_result)
   713     cursor = query_result.cursor().cursor()
   758     cursor = query_result.cursor().cursor()
   714     results, count = self.__queries[cursor]
   759     results, count = self.__queries[cursor]
   715     integer64proto.set_value(count)
   760     integer64proto.set_value(count)
   757       minfloat = float('-inf')
   802       minfloat = float('-inf')
   758     except ValueError:
   803     except ValueError:
   759       minfloat = -1e300000
   804       minfloat = -1e300000
   760 
   805 
   761     app_str = app_str.value()
   806     app_str = app_str.value()
       
   807     self.__ValidateAppId(app_str)
   762 
   808 
   763     kinds = []
   809     kinds = []
   764 
   810 
   765     for app, kind in self.__entities:
   811     for app, kind in self.__entities:
   766       if app == app_str:
   812       if app == app_str:
   814 
   860 
   815     for kind_pb in kinds:
   861     for kind_pb in kinds:
   816       schema.add_kind().CopyFrom(kind_pb)
   862       schema.add_kind().CopyFrom(kind_pb)
   817 
   863 
   818   def _Dynamic_CreateIndex(self, index, id_response):
   864   def _Dynamic_CreateIndex(self, index, id_response):
       
   865     self.__ValidateAppId(index.app_id())
   819     if index.id() != 0:
   866     if index.id() != 0:
   820       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   867       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   821                                              'New index id must be 0.')
   868                                              'New index id must be 0.')
   822     elif self.__FindIndex(index):
   869     elif self.__FindIndex(index):
   823       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   870       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   841       self.__indexes[app].append(clone)
   888       self.__indexes[app].append(clone)
   842     finally:
   889     finally:
   843       self.__indexes_lock.release()
   890       self.__indexes_lock.release()
   844 
   891 
   845   def _Dynamic_GetIndices(self, app_str, composite_indices):
   892   def _Dynamic_GetIndices(self, app_str, composite_indices):
       
   893     self.__ValidateAppId(app_str.value())
   846     composite_indices.index_list().extend(
   894     composite_indices.index_list().extend(
   847       self.__indexes.get(app_str.value(), []))
   895       self.__indexes.get(app_str.value(), []))
   848 
   896 
   849   def _Dynamic_UpdateIndex(self, index, void):
   897   def _Dynamic_UpdateIndex(self, index, void):
       
   898     self.__ValidateAppId(index.app_id())
   850     stored_index = self.__FindIndex(index)
   899     stored_index = self.__FindIndex(index)
   851     if not stored_index:
   900     if not stored_index:
   852       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   901       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   853                                              "Index doesn't exist.")
   902                                              "Index doesn't exist.")
   854     elif (index.state() != stored_index.state() and
   903     elif (index.state() != stored_index.state() and
   864       stored_index.set_state(index.state())
   913       stored_index.set_state(index.state())
   865     finally:
   914     finally:
   866       self.__indexes_lock.release()
   915       self.__indexes_lock.release()
   867 
   916 
   868   def _Dynamic_DeleteIndex(self, index, void):
   917   def _Dynamic_DeleteIndex(self, index, void):
       
   918     self.__ValidateAppId(index.app_id())
   869     stored_index = self.__FindIndex(index)
   919     stored_index = self.__FindIndex(index)
   870     if not stored_index:
   920     if not stored_index:
   871       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   921       raise apiproxy_errors.ApplicationError(datastore_pb.Error.BAD_REQUEST,
   872                                              "Index doesn't exist.")
   922                                              "Index doesn't exist.")
   873 
   923 
   886 
   936 
   887     Returns:
   937     Returns:
   888       entity_pb.CompositeIndex, if it exists; otherwise None
   938       entity_pb.CompositeIndex, if it exists; otherwise None
   889     """
   939     """
   890     app = index.app_id()
   940     app = index.app_id()
       
   941     self.__ValidateAppId(app)
   891     if app in self.__indexes:
   942     if app in self.__indexes:
   892       for stored_index in self.__indexes[app]:
   943       for stored_index in self.__indexes[app]:
   893         if index.definition() == stored_index.definition():
   944         if index.definition() == stored_index.definition():
   894           return stored_index
   945           return stored_index
   895 
   946