Skip to content

Commit 1fff028

Browse files
committed
Add guard against pending transaction for multi-use snapshots.
1 parent ec38b8b commit 1fff028

2 files changed

Lines changed: 38 additions & 22 deletions

File tree

spanner/google/cloud/spanner/snapshot.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,14 @@ def read(self, table, columns, keyset, index='', limit=0,
7373
7474
:rtype: :class:`~google.cloud.spanner.streamed.StreamedResultSet`
7575
:returns: a result set instance which can be used to consume rows.
76-
:raises: ValueError for reuse of single-use snapshots.
76+
:raises: ValueError for reuse of single-use snapshots, or if a
77+
transaction ID is pending for multiple-use snapshots.
7778
"""
78-
if not self._multi_use and self._read_request_count > 0:
79-
raise ValueError("Cannot re-use single-use snapshot.")
79+
if self._read_request_count > 0:
80+
if not self._multi_use:
81+
raise ValueError("Cannot re-use single-use snapshot.")
82+
if self._transaction_id is None:
83+
raise ValueError("Transaction ID pending.")
8084

8185
database = self._session._database
8286
api = database.spanner_api
@@ -121,10 +125,14 @@ def execute_sql(self, sql, params=None, param_types=None, query_mode=None,
121125
122126
:rtype: :class:`~google.cloud.spanner.streamed.StreamedResultSet`
123127
:returns: a result set instance which can be used to consume rows.
124-
:raises: ValueError for reuse of single-use snapshots.
128+
:raises: ValueError for reuse of single-use snapshots, or if a
129+
transaction ID is pending for multiple-use snapshots.
125130
"""
126-
if not self._multi_use and self._read_request_count > 0:
127-
raise ValueError("Cannot re-use single-use snapshot.")
131+
if self._read_request_count > 0:
132+
if not self._multi_use:
133+
raise ValueError("Cannot re-use single-use snapshot.")
134+
if self._transaction_id is None:
135+
raise ValueError("Transaction ID pending.")
128136

129137
if params is not None:
130138
if param_types is None:

spanner/tests/unit/test_snapshot.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def test_read_grpc_error(self):
112112
self.assertEqual(options.kwargs['metadata'],
113113
[('google-cloud-resource-prefix', database.name)])
114114

115-
def _read_helper(self, multi_use, first=True):
115+
def _read_helper(self, multi_use, first=True, count=0):
116116
from google.protobuf.struct_pb2 import Struct
117117
from google.cloud.proto.spanner.v1.result_set_pb2 import (
118118
PartialResultSet, ResultSetMetadata, ResultSetStats)
@@ -156,18 +156,15 @@ def _read_helper(self, multi_use, first=True):
156156
session = _Session(database)
157157
derived = self._makeDerived(session)
158158
derived._multi_use = multi_use
159+
derived._read_request_count = count
159160
if not first:
160161
derived._transaction_id = TXN_ID
161-
derived._read_request_count = 1
162162

163163
result_set = derived.read(
164164
TABLE_NAME, COLUMNS, KEYSET,
165165
index=INDEX, limit=LIMIT, resume_token=TOKEN)
166166

167-
if first:
168-
self.assertEqual(derived._read_request_count, 1)
169-
else:
170-
self.assertEqual(derived._read_request_count, 2)
167+
self.assertEqual(derived._read_request_count, count + 1)
171168

172169
if multi_use:
173170
self.assertIs(result_set._source, derived)
@@ -205,14 +202,21 @@ def test_read_wo_multi_use(self):
205202

206203
def test_read_wo_multi_use_w_read_request_count_gt_0(self):
207204
with self.assertRaises(ValueError):
208-
self._read_helper(multi_use=False, first=False)
205+
self._read_helper(multi_use=False, count=1)
209206

210207
def test_read_w_multi_use_wo_first(self):
211208
self._read_helper(multi_use=True, first=False)
212209

210+
def test_read_w_multi_use_wo_first_w_count_gt_0(self):
211+
self._read_helper(multi_use=True, first=False, count=1)
212+
213213
def test_read_w_multi_use_w_first(self):
214214
self._read_helper(multi_use=True, first=True)
215215

216+
def test_read_w_multi_use_w_first_w_count_gt_0(self):
217+
with self.assertRaises(ValueError):
218+
self._read_helper(multi_use=True, first=True, count=1)
219+
216220
def test_execute_sql_grpc_error(self):
217221
from google.cloud.proto.spanner.v1.transaction_pb2 import (
218222
TransactionSelector)
@@ -249,7 +253,7 @@ def test_execute_sql_w_params_wo_param_types(self):
249253
with self.assertRaises(ValueError):
250254
derived.execute_sql(SQL_QUERY_WITH_PARAM, PARAMS)
251255

252-
def _execute_sql_helper(self, multi_use, first=True):
256+
def _execute_sql_helper(self, multi_use, first=True, count=0):
253257
from google.protobuf.struct_pb2 import Struct
254258
from google.cloud.proto.spanner.v1.result_set_pb2 import (
255259
PartialResultSet, ResultSetMetadata, ResultSetStats)
@@ -291,18 +295,15 @@ def _execute_sql_helper(self, multi_use, first=True):
291295
session = _Session(database)
292296
derived = self._makeDerived(session)
293297
derived._multi_use = multi_use
298+
derived._read_request_count = count
294299
if not first:
295300
derived._transaction_id = TXN_ID
296-
derived._read_request_count = 1
297301

298302
result_set = derived.execute_sql(
299303
SQL_QUERY_WITH_PARAM, PARAMS, PARAM_TYPES,
300304
query_mode=MODE, resume_token=TOKEN)
301305

302-
if first:
303-
self.assertEqual(derived._read_request_count, 1)
304-
else:
305-
self.assertEqual(derived._read_request_count, 2)
306+
self.assertEqual(derived._read_request_count, count + 1)
306307

307308
if multi_use:
308309
self.assertIs(result_set._source, derived)
@@ -341,13 +342,20 @@ def test_execute_sql_wo_multi_use(self):
341342

342343
def test_execute_sql_wo_multi_use_w_read_request_count_gt_0(self):
343344
with self.assertRaises(ValueError):
344-
self._execute_sql_helper(multi_use=False, first=False)
345+
self._execute_sql_helper(multi_use=False, count=1)
346+
347+
def test_execute_sql_w_multi_use_wo_first(self):
348+
self._execute_sql_helper(multi_use=True, first=False)
349+
350+
def test_execute_sql_w_multi_use_wo_first_w_count_gt_0(self):
351+
self._execute_sql_helper(multi_use=True, first=False, count=1)
345352

346353
def test_execute_sql_w_multi_use_w_first(self):
347354
self._execute_sql_helper(multi_use=True, first=True)
348355

349-
def test_execute_sql_w_multi_use_wo_first(self):
350-
self._execute_sql_helper(multi_use=True, first=False)
356+
def test_execute_sql_w_multi_use_w_first_w_count_gt_0(self):
357+
with self.assertRaises(ValueError):
358+
self._execute_sql_helper(multi_use=True, first=True, count=1)
351359

352360

353361
class _MockCancellableIterator(object):

0 commit comments

Comments
 (0)