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 |
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(): |
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.") |
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, |