Skip to content

Commit 9b0f87a

Browse files
committed
Adding one-time RPC to find unaliased / true dataset ID.
Also removing unnecessary functions that are no longer needed since there is no need to muck with the dataset ID.
1 parent 9518db7 commit 9b0f87a

File tree

11 files changed

+179
-234
lines changed

11 files changed

+179
-234
lines changed

gcloud/datastore/__init__.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,48 @@
6969
_DATASET_ENV_VAR_NAME = 'GCLOUD_DATASET_ID'
7070

7171

72+
def _find_true_dataset_id(dataset_id, connection=None):
73+
"""Find the true (unaliased) dataset ID.
74+
75+
If the given ID already has a 's~' or 'e~' prefix, does nothing.
76+
Otherwise, looks up a bogus Key('__MissingLookupKind', 1) and reads the
77+
true prefixed dataset ID from the response (either from found or from
78+
missing).
79+
80+
For some context, see:
81+
github.com/GoogleCloudPlatform/gcloud-python/pull/528
82+
github.com/GoogleCloudPlatform/google-cloud-datastore/issues/59
83+
84+
:type dataset_id: string
85+
:param dataset_id: The dataset ID to un-alias / prefix.
86+
87+
:type connection: :class:`gcloud.datastore.connection.Connection`
88+
:param connection: Optional. A connection provided to be the default.
89+
90+
:rtype: string
91+
:returns: The true / prefixed / un-aliased dataset ID.
92+
"""
93+
if dataset_id.startswith('s~') or dataset_id.startswith('e~'):
94+
return dataset_id
95+
96+
connection = connection or _implicit_environ.CONNECTION
97+
98+
# Create the bogus Key protobuf to be looked up and remove
99+
# the dataset ID so the backend won't complain.
100+
bogus_key_pb = Key('__MissingLookupKind', 1,
101+
dataset_id=dataset_id).to_protobuf()
102+
bogus_key_pb.partition_id.ClearField('dataset_id')
103+
104+
found_pbs, missing_pbs, _ = connection.lookup(dataset_id, [bogus_key_pb])
105+
# By not passing in `deferred`, lookup will continue until
106+
# all results are `found` or `missing`.
107+
all_pbs = missing_pbs + found_pbs
108+
# We only asked for one, so should only receive one.
109+
returned_pb, = all_pbs
110+
111+
return returned_pb.key.partition_id.dataset_id
112+
113+
72114
def set_default_dataset_id(dataset_id=None):
73115
"""Set default dataset ID either explicitly or implicitly as fall-back.
74116
@@ -91,6 +133,7 @@ def set_default_dataset_id(dataset_id=None):
91133
dataset_id = _implicit_environ.compute_engine_id()
92134

93135
if dataset_id is not None:
136+
dataset_id = _find_true_dataset_id(dataset_id)
94137
_implicit_environ.DATASET_ID = dataset_id
95138

96139

@@ -120,8 +163,9 @@ def set_defaults(dataset_id=None, connection=None):
120163
:type connection: :class:`gcloud.datastore.connection.Connection`
121164
:param connection: A connection provided to be the default.
122165
"""
123-
set_default_dataset_id(dataset_id=dataset_id)
166+
# Set CONNECTION first in case _find_true_dataset_id needs it.
124167
set_default_connection(connection=connection)
168+
set_default_dataset_id(dataset_id=dataset_id)
125169

126170

127171
def get_connection():

gcloud/datastore/batch.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,7 @@ def put(self, entity):
216216
if entity.key is None:
217217
raise ValueError("Entity must have a key")
218218

219-
if not helpers._dataset_ids_equal(self._dataset_id,
220-
entity.key.dataset_id):
219+
if self._dataset_id != entity.key.dataset_id:
221220
raise ValueError("Key must be from same dataset as batch")
222221

223222
_assign_entity_to_mutation(
@@ -235,8 +234,7 @@ def delete(self, key):
235234
if key.is_partial:
236235
raise ValueError("Key must be complete")
237236

238-
if not helpers._dataset_ids_equal(self._dataset_id,
239-
key.dataset_id):
237+
if self._dataset_id != key.dataset_id:
240238
raise ValueError("Key must be from same dataset as batch")
241239

242240
key_pb = key.to_protobuf()
@@ -309,7 +307,6 @@ def _assign_entity_to_mutation(mutation_pb, entity, auto_id_entities):
309307
auto_id = entity.key.is_partial
310308

311309
key_pb = entity.key.to_protobuf()
312-
key_pb = helpers._prepare_key_for_request(key_pb)
313310

314311
if auto_id:
315312
insert = mutation_pb.insert_auto_id.add()

gcloud/datastore/helpers.py

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import pytz
2727
import six
2828

29-
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb
3029
from gcloud.datastore.entity import Entity
3130
from gcloud.datastore.key import Key
3231

@@ -280,33 +279,6 @@ def _set_protobuf_value(value_pb, val):
280279
setattr(value_pb, attr, val)
281280

282281

283-
def _prepare_key_for_request(key_pb):
284-
"""Add protobuf keys to a request object.
285-
286-
:type key_pb: :class:`gcloud.datastore._datastore_v1_pb2.Key`
287-
:param key_pb: A key to be added to a request.
288-
289-
:rtype: :class:`gcloud.datastore._datastore_v1_pb2.Key`
290-
:returns: A key which will be added to a request. It will be the
291-
original if nothing needs to be changed.
292-
"""
293-
if key_pb.partition_id.HasField('dataset_id'):
294-
# We remove the dataset_id from the protobuf. This is because
295-
# the backend fails a request if the key contains un-prefixed
296-
# dataset ID. The backend fails because requests to
297-
# /datastore/.../datasets/foo/...
298-
# and
299-
# /datastore/.../datasets/s~foo/...
300-
# both go to the datastore given by 's~foo'. So if the key
301-
# protobuf in the request body has dataset_id='foo', the
302-
# backend will reject since 'foo' != 's~foo'.
303-
new_key_pb = datastore_pb.Key()
304-
new_key_pb.CopyFrom(key_pb)
305-
new_key_pb.partition_id.ClearField('dataset_id')
306-
key_pb = new_key_pb
307-
return key_pb
308-
309-
310282
def _add_keys_to_request(request_field_pb, key_pbs):
311283
"""Add protobuf keys to a request object.
312284
@@ -317,57 +289,4 @@ def _add_keys_to_request(request_field_pb, key_pbs):
317289
:param key_pbs: The keys to add to a request.
318290
"""
319291
for key_pb in key_pbs:
320-
key_pb = _prepare_key_for_request(key_pb)
321292
request_field_pb.add().CopyFrom(key_pb)
322-
323-
324-
def _dataset_ids_equal(dataset_id1, dataset_id2):
325-
"""Compares two dataset IDs for fuzzy equality.
326-
327-
Each may be prefixed or unprefixed (but not null, since dataset ID
328-
is required on a key). The only allowed prefixes are 's~' and 'e~'.
329-
330-
Two identical prefixed match
331-
332-
>>> 's~foo' == 's~foo'
333-
>>> 'e~bar' == 'e~bar'
334-
335-
while non-identical prefixed don't
336-
337-
>>> 's~foo' != 's~bar'
338-
>>> 's~foo' != 'e~foo'
339-
340-
As for non-prefixed, they can match other non-prefixed or
341-
prefixed:
342-
343-
>>> 'foo' == 'foo'
344-
>>> 'foo' == 's~foo'
345-
>>> 'foo' == 'e~foo'
346-
>>> 'foo' != 'bar'
347-
>>> 'foo' != 's~bar'
348-
349-
(Ties are resolved since 'foo' can only be an alias for one of
350-
s~foo or e~foo in the backend.)
351-
352-
:type dataset_id1: string
353-
:param dataset_id1: A dataset ID.
354-
355-
:type dataset_id2: string
356-
:param dataset_id2: A dataset ID.
357-
358-
:rtype: boolean
359-
:returns: Boolean indicating if the IDs are the same.
360-
"""
361-
if dataset_id1 == dataset_id2:
362-
return True
363-
364-
if dataset_id1.startswith('s~') or dataset_id1.startswith('e~'):
365-
# If `dataset_id1` is prefixed and not matching, then the only way
366-
# they can match is if `dataset_id2` is unprefixed.
367-
return dataset_id1[2:] == dataset_id2
368-
elif dataset_id2.startswith('s~') or dataset_id2.startswith('e~'):
369-
# Here we know `dataset_id1` is unprefixed and `dataset_id2`
370-
# is prefixed.
371-
return dataset_id1 == dataset_id2[2:]
372-
373-
return False

gcloud/datastore/query.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,7 @@ def _pb_from_query(query):
449449
composite_filter.operator = datastore_pb.CompositeFilter.AND
450450

451451
if query.ancestor:
452-
ancestor_pb = helpers._prepare_key_for_request(
453-
query.ancestor.to_protobuf())
452+
ancestor_pb = query.ancestor.to_protobuf()
454453

455454
# Filter on __key__ HAS_ANCESTOR == ancestor.
456455
ancestor_filter = composite_filter.filter.add().property_filter
@@ -469,8 +468,7 @@ def _pb_from_query(query):
469468
# Set the value to filter on based on the type.
470469
if property_name == '__key__':
471470
key_pb = value.to_protobuf()
472-
property_filter.value.key_value.CopyFrom(
473-
helpers._prepare_key_for_request(key_pb))
471+
property_filter.value.key_value.CopyFrom(key_pb)
474472
else:
475473
helpers._set_protobuf_value(property_filter.value, value)
476474

0 commit comments

Comments
 (0)