thirdparty/google_appengine/google/appengine/api/datastore.py
changeset 2413 d0b7dac5325c
parent 2309 be1b94099f2d
child 2864 2e0b0af889be
equal deleted inserted replaced
2412:c61d96e72e6f 2413:d0b7dac5325c
   176   for entity, key in zip(entities, keys):
   176   for entity, key in zip(entities, keys):
   177     entity._Entity__key._Key__reference.CopyFrom(key)
   177     entity._Entity__key._Key__reference.CopyFrom(key)
   178 
   178 
   179   if tx:
   179   if tx:
   180     tx.RecordModifiedKeys([e.key() for e in entities], error_on_repeat=False)
   180     tx.RecordModifiedKeys([e.key() for e in entities], error_on_repeat=False)
       
   181     tx.entity_group = entities[0].entity_group()
   181 
   182 
   182   if multiple:
   183   if multiple:
   183     return [Key._FromPb(k) for k in keys]
   184     return [Key._FromPb(k) for k in keys]
   184   else:
   185   else:
   185     return Key._FromPb(resp.key(0))
   186     return Key._FromPb(resp.key(0))
   356     """
   357     """
   357     return self.key().entity_group()
   358     return self.key().entity_group()
   358 
   359 
   359   def unindexed_properties(self):
   360   def unindexed_properties(self):
   360     """Returns this entity's unindexed properties, as a frozenset of strings."""
   361     """Returns this entity's unindexed properties, as a frozenset of strings."""
   361     return self.__unindexed_properties
   362     return getattr(self, '_Entity__unindexed_properties', [])
   362 
   363 
   363   def __setitem__(self, name, value):
   364   def __setitem__(self, name, value):
   364     """Implements the [] operator. Used to set property value(s).
   365     """Implements the [] operator. Used to set property value(s).
   365 
   366 
   366     If the property name is the empty string or not a string, raises
   367     If the property name is the empty string or not a string, raises
   508       sample = values
   509       sample = values
   509       if isinstance(sample, list):
   510       if isinstance(sample, list):
   510         sample = values[0]
   511         sample = values[0]
   511 
   512 
   512       if (isinstance(sample, datastore_types._RAW_PROPERTY_TYPES) or
   513       if (isinstance(sample, datastore_types._RAW_PROPERTY_TYPES) or
   513           name in self.__unindexed_properties):
   514           name in self.unindexed_properties()):
   514         pb.raw_property_list().extend(properties)
   515         pb.raw_property_list().extend(properties)
   515       else:
   516       else:
   516         pb.property_list().extend(properties)
   517         pb.property_list().extend(properties)
   517 
   518 
   518     if pb.property_size() > _MAX_INDEXED_PROPERTIES:
   519     if pb.property_size() > _MAX_INDEXED_PROPERTIES:
   858 
   859 
   859     Returns:
   860     Returns:
   860       # this query
   861       # this query
   861       Query
   862       Query
   862     """
   863     """
   863     key = _GetCompleteKeyOrError(ancestor)
   864     self.__ancestor = _GetCompleteKeyOrError(ancestor)
   864     self.__ancestor = datastore_pb.Reference()
       
   865     self.__ancestor.CopyFrom(key._Key__reference)
       
   866     return self
   865     return self
   867 
   866 
   868   def IsKeysOnly(self):
   867   def IsKeysOnly(self):
   869     """Returns True if this query is keys only, false otherwise."""
   868     """Returns True if this query is keys only, false otherwise."""
   870     return self.__keys_only
   869     return self.__keys_only
   892     limit and offset must both be integers >= 0.
   891     limit and offset must both be integers >= 0.
   893 
   892 
   894     This is not intended to be used by application developers. Use Get()
   893     This is not intended to be used by application developers. Use Get()
   895     instead!
   894     instead!
   896     """
   895     """
   897     if _CurrentTransactionKey():
       
   898       raise datastore_errors.BadRequestError(
       
   899         "Can't query inside a transaction.")
       
   900 
       
   901     pb = self._ToPb(limit, offset)
   896     pb = self._ToPb(limit, offset)
   902     result = datastore_pb.QueryResult()
   897     result = datastore_pb.QueryResult()
   903 
   898 
   904     try:
   899     try:
   905       apiproxy_stub_map.MakeSyncCall('datastore_v3', 'RunQuery', pb, result)
   900       apiproxy_stub_map.MakeSyncCall('datastore_v3', 'RunQuery', pb, result)
   910         yaml = datastore_index.IndexYamlForQuery(
   905         yaml = datastore_index.IndexYamlForQuery(
   911           *datastore_index.CompositeIndexForQuery(pb)[1:-1])
   906           *datastore_index.CompositeIndexForQuery(pb)[1:-1])
   912         raise datastore_errors.NeedIndexError(
   907         raise datastore_errors.NeedIndexError(
   913           str(exc) + '\nThis query needs this index:\n' + yaml)
   908           str(exc) + '\nThis query needs this index:\n' + yaml)
   914 
   909 
   915     return Iterator._FromPb(result)
   910     return Iterator(result)
   916 
   911 
   917   def Get(self, limit, offset=0):
   912   def Get(self, limit, offset=0):
   918     """Fetches and returns a maximum number of results from the query.
   913     """Fetches and returns a maximum number of results from the query.
   919 
   914 
   920     This method fetches and returns a list of resulting entities that matched
   915     This method fetches and returns a list of resulting entities that matched
   959     if not isinstance(offset, (int, long)) or offset < 0:
   954     if not isinstance(offset, (int, long)) or offset < 0:
   960       raise datastore_errors.BadArgumentError(
   955       raise datastore_errors.BadArgumentError(
   961           'Argument to Get named \'offset\' must be an int greater than or '
   956           'Argument to Get named \'offset\' must be an int greater than or '
   962           'equal to 0; received %s (a %s)' % (offset, typename(offset)))
   957           'equal to 0; received %s (a %s)' % (offset, typename(offset)))
   963 
   958 
   964     return self._Run(limit, offset)._Next(limit)
   959     return self._Run(limit, offset)._Get(limit)
   965 
   960 
   966   def Count(self, limit=None):
   961   def Count(self, limit=None):
   967     """Returns the number of entities that this query matches. The returned
   962     """Returns the number of entities that this query matches. The returned
   968     count is cached; successive Count() calls will not re-scan the datastore
   963     count is cached; successive Count() calls will not re-scan the datastore
   969     unless the query is changed.
   964     unless the query is changed.
  1136       offset: int
  1131       offset: int
  1137 
  1132 
  1138     Returns:
  1133     Returns:
  1139       # the PB representation of this Query
  1134       # the PB representation of this Query
  1140       datastore_pb.Query
  1135       datastore_pb.Query
  1141     """
  1136 
       
  1137     Raises:
       
  1138       BadRequestError if called inside a transaction and the query does not
       
  1139       include an ancestor.
       
  1140     """
       
  1141     if not self.__ancestor and _CurrentTransactionKey():
       
  1142       raise datastore_errors.BadRequestError(
       
  1143         'Only ancestor queries are allowed inside transactions.')
       
  1144 
  1142     pb = datastore_pb.Query()
  1145     pb = datastore_pb.Query()
       
  1146     _MaybeSetupTransaction(pb, [self.__ancestor])
  1143 
  1147 
  1144     pb.set_kind(self.__kind.encode('utf-8'))
  1148     pb.set_kind(self.__kind.encode('utf-8'))
  1145     pb.set_keys_only(bool(self.__keys_only))
  1149     pb.set_keys_only(bool(self.__keys_only))
  1146     if self.__app:
  1150     if self.__app:
  1147       pb.set_app(self.__app.encode('utf-8'))
  1151       pb.set_app(self.__app.encode('utf-8'))
  1148     if limit is not None:
  1152     if limit is not None:
  1149       pb.set_limit(limit)
  1153       pb.set_limit(limit)
  1150     if offset is not None:
  1154     if offset is not None:
  1151       pb.set_offset(offset)
  1155       pb.set_offset(offset)
  1152     if self.__ancestor:
  1156     if self.__ancestor:
  1153       pb.mutable_ancestor().CopyFrom(self.__ancestor)
  1157       pb.mutable_ancestor().CopyFrom(self.__ancestor._Key__reference)
  1154 
  1158 
  1155     if ((self.__hint == self.ORDER_FIRST and self.__orderings) or
  1159     if ((self.__hint == self.ORDER_FIRST and self.__orderings) or
  1156         (self.__hint == self.ANCESTOR_FIRST and self.__ancestor) or
  1160         (self.__hint == self.ANCESTOR_FIRST and self.__ancestor) or
  1157         (self.__hint == self.FILTER_FIRST and len(self) > 0)):
  1161         (self.__hint == self.FILTER_FIRST and len(self) > 0)):
  1158       pb.set_hint(self.__hint)
  1162       pb.set_hint(self.__hint)
  1511 
  1515 
  1512   > it = Query('Person').Run()
  1516   > it = Query('Person').Run()
  1513   > for person in it:
  1517   > for person in it:
  1514   >   print 'Hi, %s!' % person['name']
  1518   >   print 'Hi, %s!' % person['name']
  1515   """
  1519   """
  1516   def __init__(self, cursor, keys_only=False):
  1520   def __init__(self, query_result_pb):
  1517     self.__cursor = cursor
  1521     self.__cursor = query_result_pb.cursor()
  1518     self.__buffer = []
  1522     self.__keys_only = query_result_pb.keys_only()
  1519     self.__more_results = True
  1523     self.__buffer = self._ProcessQueryResult(query_result_pb)
  1520     self.__keys_only = keys_only
  1524 
       
  1525   def _Get(self, count):
       
  1526     """Gets the next count result(s) of the query.
       
  1527 
       
  1528     Not intended to be used by application developers. Use the python
       
  1529     iterator protocol instead.
       
  1530 
       
  1531     This method uses _Next to returns the next entities or keys from the list of
       
  1532     matching results. If the query specified a sort order, results are returned
       
  1533     in that order. Otherwise, the order is undefined.
       
  1534 
       
  1535     The argument, count, specifies the number of results to return. However, the
       
  1536     length of the returned list may be smaller than count. This is the case only
       
  1537     if count is greater than the number of remaining results.
       
  1538 
       
  1539     The results are always returned as a list. If there are no results left,
       
  1540     an empty list is returned.
       
  1541 
       
  1542     Args:
       
  1543       # the number of results to return; must be >= 1
       
  1544       count: int or long
       
  1545 
       
  1546     Returns:
       
  1547       # a list of entities or keys
       
  1548       [Entity or Key, ...]
       
  1549     """
       
  1550     entityList = self._Next(count)
       
  1551     while len(entityList) < count and self.__more_results:
       
  1552       next_results = self._Next(count - len(entityList))
       
  1553       if not next_results:
       
  1554         break
       
  1555       entityList += next_results
       
  1556     return entityList;
  1521 
  1557 
  1522   def _Next(self, count):
  1558   def _Next(self, count):
  1523     """Returns the next result(s) of the query.
  1559     """Returns the next result(s) of the query.
  1524 
  1560 
  1525     Not intended to be used by application developers. Use the python
  1561     Not intended to be used by application developers. Use the python
  1527 
  1563 
  1528     This method returns the next entities or keys from the list of matching
  1564     This method returns the next entities or keys from the list of matching
  1529     results. If the query specified a sort order, results are returned in that
  1565     results. If the query specified a sort order, results are returned in that
  1530     order. Otherwise, the order is undefined.
  1566     order. Otherwise, the order is undefined.
  1531 
  1567 
  1532     The argument specifies the number of results to return. If it's greater
  1568     The argument, count, specifies the number of results to return. However, the
  1533     than the number of remaining results, all of the remaining results are
  1569     length of the returned list may be smaller than count. This is the case if
  1534     returned. In that case, the length of the returned list will be smaller
  1570     count is greater than the number of remaining results or the size of the
  1535     than count.
  1571     remaining results exciteds the RPC buffer limit. Use _Get to insure all
       
  1572     possible entities are retrieved.
  1536 
  1573 
  1537     There is an internal buffer for use with the next() method. If this buffer
  1574     There is an internal buffer for use with the next() method. If this buffer
  1538     is not empty, up to 'count' values are removed from this buffer and
  1575     is not empty, up to 'count' values are removed from this buffer and
  1539     returned. It's best not to mix _Next() and next().
  1576     returned. It's best not to mix _Next() and next().
  1540 
  1577 
  1553       raise datastore_errors.BadArgumentError(
  1590       raise datastore_errors.BadArgumentError(
  1554         'Argument to _Next must be an int greater than 0; received %s (a %s)' %
  1591         'Argument to _Next must be an int greater than 0; received %s (a %s)' %
  1555         (count, typename(count)))
  1592         (count, typename(count)))
  1556 
  1593 
  1557     if self.__buffer:
  1594     if self.__buffer:
  1558       raise datastore_errors.BadRequestError(
  1595       if count <= len(self.__buffer):
  1559           'You can\'t mix next() and _Next()')
  1596         entity_list = self.__buffer[:count]
       
  1597         del self.__buffer[:count]
       
  1598         return entity_list
       
  1599       else:
       
  1600         entity_list = self.__buffer
       
  1601         self.__buffer = []
       
  1602         count -= len(entity_list)
       
  1603     else:
       
  1604         entity_list=[]
  1560 
  1605 
  1561     if not self.__more_results:
  1606     if not self.__more_results:
  1562       return []
  1607       return entity_list
  1563 
  1608 
  1564     req = datastore_pb.NextRequest()
  1609     req = datastore_pb.NextRequest()
  1565     req.set_count(count)
  1610     req.set_count(count)
  1566     req.mutable_cursor().CopyFrom(self._ToPb())
  1611     req.mutable_cursor().CopyFrom(self.__cursor)
  1567     result = datastore_pb.QueryResult()
  1612     result = datastore_pb.QueryResult()
  1568     try:
  1613     try:
  1569       apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Next', req, result)
  1614       apiproxy_stub_map.MakeSyncCall('datastore_v3', 'Next', req, result)
  1570     except apiproxy_errors.ApplicationError, err:
  1615     except apiproxy_errors.ApplicationError, err:
  1571       raise _ToDatastoreError(err)
  1616       raise _ToDatastoreError(err)
  1572 
  1617 
       
  1618     return entity_list + self._ProcessQueryResult(result)
       
  1619 
       
  1620   def _ProcessQueryResult(self, result):
       
  1621     """Returns all results from datastore_pb.QueryResult and updates
       
  1622     self.__more_results
       
  1623 
       
  1624     Not intended to be used by application developers. Use the python
       
  1625     iterator protocol instead.
       
  1626 
       
  1627     The results are always returned as a list. If there are no results left,
       
  1628     an empty list is returned.
       
  1629 
       
  1630     Args:
       
  1631       # the instance of datastore_pb.QueryResult to be stored
       
  1632       result: datastore_pb.QueryResult
       
  1633 
       
  1634     Returns:
       
  1635       # a list of entities or keys
       
  1636       [Entity or Key, ...]
       
  1637     """
  1573     self.__more_results = result.more_results()
  1638     self.__more_results = result.more_results()
  1574 
  1639 
  1575     if self.__keys_only:
  1640     if self.__keys_only:
  1576       return [Key._FromPb(e.key()) for e in result.result_list()]
  1641       return [Key._FromPb(e.key()) for e in result.result_list()]
  1577     else:
  1642     else:
  1586       return self.__buffer.pop(0)
  1651       return self.__buffer.pop(0)
  1587     except IndexError:
  1652     except IndexError:
  1588       raise StopIteration
  1653       raise StopIteration
  1589 
  1654 
  1590   def __iter__(self): return self
  1655   def __iter__(self): return self
  1591 
       
  1592   def _ToPb(self):
       
  1593     """Converts this Iterator to its protocol buffer representation. Not
       
  1594     intended to be used by application developers. Enforced by hiding the
       
  1595     datastore_pb classes.
       
  1596 
       
  1597     Returns:
       
  1598       # the PB representation of this Iterator
       
  1599       datastore_pb.Cursor
       
  1600     """
       
  1601     pb = datastore_pb.Cursor()
       
  1602     pb.set_cursor(self.__cursor)
       
  1603     return pb
       
  1604 
       
  1605   @staticmethod
       
  1606   def _FromPb(pb):
       
  1607     """Static factory method. Returns the Iterator representation of the given
       
  1608     protocol buffer (datastore_pb.QueryResult). Not intended to be used by
       
  1609     application developers. Enforced by hiding the datastore_pb classes.
       
  1610 
       
  1611     Args:
       
  1612       pb: datastore_pb.QueryResult
       
  1613 
       
  1614     Returns:
       
  1615       Iterator
       
  1616     """
       
  1617     return Iterator(pb.cursor().cursor(), keys_only=pb.keys_only())
       
  1618 
       
  1619 
  1656 
  1620 class _Transaction(object):
  1657 class _Transaction(object):
  1621   """Encapsulates a transaction currently in progress.
  1658   """Encapsulates a transaction currently in progress.
  1622 
  1659 
  1623   If we've sent a BeginTransaction call, then handle will be a
  1660   If we've sent a BeginTransaction call, then handle will be a
  1638     self.modified_keys = set()
  1675     self.modified_keys = set()
  1639 
  1676 
  1640   def RecordModifiedKeys(self, keys, error_on_repeat=True):
  1677   def RecordModifiedKeys(self, keys, error_on_repeat=True):
  1641     """Updates the modified keys seen so far.
  1678     """Updates the modified keys seen so far.
  1642 
  1679 
  1643     Also sets entity_group if it hasn't yet been set.
       
  1644 
       
  1645     If error_on_repeat is True and any of the given keys have already been
  1680     If error_on_repeat is True and any of the given keys have already been
  1646     modified, raises BadRequestError.
  1681     modified, raises BadRequestError.
  1647 
  1682 
  1648     Args:
  1683     Args:
  1649       keys: sequence of Keys
  1684       keys: sequence of Keys
  1650     """
  1685     """
  1651     keys, _ = NormalizeAndTypeCheckKeys(keys)
  1686     keys, _ = NormalizeAndTypeCheckKeys(keys)
  1652 
       
  1653     if keys and not self.entity_group:
       
  1654       self.entity_group = keys[0].entity_group()
       
  1655 
       
  1656     keys = set(keys)
  1687     keys = set(keys)
  1657 
  1688 
  1658     if error_on_repeat:
  1689     if error_on_repeat:
  1659       already_modified = self.modified_keys.intersection(keys)
  1690       already_modified = self.modified_keys.intersection(keys)
  1660       if already_modified:
  1691       if already_modified:
  1832 
  1863 
  1833   Raises BadRequestError if the entity has a different entity group than the
  1864   Raises BadRequestError if the entity has a different entity group than the
  1834   current transaction.
  1865   current transaction.
  1835 
  1866 
  1836   Args:
  1867   Args:
  1837     request: GetRequest, PutRequest, or DeleteRequest
  1868     request: GetRequest, PutRequest, DeleteRequest, or Query
  1838     keys: sequence of Keys
  1869     keys: sequence of Keys
  1839 
  1870 
  1840   Returns:
  1871   Returns:
  1841     _Transaction if we're inside a transaction, otherwise None
  1872     _Transaction if we're inside a transaction, otherwise None
  1842   """
  1873   """
  1843   assert isinstance(request, (datastore_pb.GetRequest, datastore_pb.PutRequest,
  1874   assert isinstance(request, (datastore_pb.GetRequest, datastore_pb.PutRequest,
  1844                               datastore_pb.DeleteRequest))
  1875                               datastore_pb.DeleteRequest, datastore_pb.Query))
  1845   tx_key = None
  1876   tx_key = None
  1846 
  1877 
  1847   try:
  1878   try:
  1848     tx_key = _CurrentTransactionKey()
  1879     tx_key = _CurrentTransactionKey()
  1849     if tx_key:
  1880     if tx_key:
  1852       groups = [k.entity_group() for k in keys]
  1883       groups = [k.entity_group() for k in keys]
  1853       if tx.entity_group:
  1884       if tx.entity_group:
  1854         expected_group = tx.entity_group
  1885         expected_group = tx.entity_group
  1855       else:
  1886       else:
  1856         expected_group = groups[0]
  1887         expected_group = groups[0]
       
  1888 
  1857       for group in groups:
  1889       for group in groups:
  1858         if (group != expected_group or
  1890         if (group != expected_group or
  1859 
  1891 
  1860 
  1892 
  1861 
  1893 
  1863 
  1895 
  1864 
  1896 
  1865 
  1897 
  1866             (not group.has_id_or_name() and group is not expected_group)):
  1898             (not group.has_id_or_name() and group is not expected_group)):
  1867           raise _DifferentEntityGroupError(expected_group, group)
  1899           raise _DifferentEntityGroupError(expected_group, group)
       
  1900 
       
  1901         if not tx.entity_group and group.has_id_or_name():
       
  1902           tx.entity_group = group
  1868 
  1903 
  1869       if not tx.handle:
  1904       if not tx.handle:
  1870         tx.handle = datastore_pb.Transaction()
  1905         tx.handle = datastore_pb.Transaction()
  1871         req = api_base_pb.VoidProto()
  1906         req = api_base_pb.VoidProto()
  1872         apiproxy_stub_map.MakeSyncCall('datastore_v3', 'BeginTransaction', req,
  1907         apiproxy_stub_map.MakeSyncCall('datastore_v3', 'BeginTransaction', req,