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) |
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 |