From 474cb18cc537dc78a7b111d8dd385956634b92e1 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 11 Jul 2017 15:15:44 -0400 Subject: [PATCH 1/5] Defend against back-end returning instance configs for disallowed regions. --- spanner/tests/system/test_system.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spanner/tests/system/test_system.py b/spanner/tests/system/test_system.py index b1ce6c892e3e..eef16145867b 100644 --- a/spanner/tests/system/test_system.py +++ b/spanner/tests/system/test_system.py @@ -87,6 +87,10 @@ def setUpModule(): configs = list(retry(Config.CLIENT.list_instance_configs)()) + # Defend against back-end returning configs for regions we aren't + # actually allowed to use. + configs = [config for config in configs if '-us-' in config.name] + if len(configs) < 1: raise ValueError('List instance configs failed in module set up.') From 707e44efc9ddfc2bfeba489ea47171a7a3d80c03 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 10 Jul 2017 16:50:01 -0400 Subject: [PATCH 2/5] Additional systests for 'Snapshot.read': - Read single key. - Read multiple keys. - Read open-closed ranges. - Read open-open ranges. - Read closed-open ranges. - Read closed-closed ranges. --- spanner/tests/system/test_system.py | 64 +++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/spanner/tests/system/test_system.py b/spanner/tests/system/test_system.py index eef16145867b..298e1e5cfc86 100644 --- a/spanner/tests/system/test_system.py +++ b/spanner/tests/system/test_system.py @@ -33,6 +33,7 @@ from google.cloud.exceptions import GrpcRendezvous from google.cloud.spanner._helpers import TimestampWithNanoseconds from google.cloud.spanner.client import Client +from google.cloud.spanner.keyset import KeyRange from google.cloud.spanner.keyset import KeySet from google.cloud.spanner.pool import BurstyPool @@ -584,6 +585,32 @@ def test_read_w_index(self): [(row[0], row[2]) for row in self._row_data(ROW_COUNT)])) self._check_row_data(rows, expected) + def test_read_w_single_key(self): + ROW_COUNT = 40 + session, committed = self._set_up_table(ROW_COUNT) + + snapshot = session.snapshot(read_timestamp=committed) + rows = list(snapshot.read( + self.TABLE, self.COLUMNS, KeySet(keys=[(0,)]))) + + all_data_rows = list(self._row_data(ROW_COUNT)) + expected = [all_data_rows[0]] + self._check_row_data(rows, expected) + + def test_read_w_multiple_keys(self): + ROW_COUNT = 40 + indices = [0, 5, 17] + session, committed = self._set_up_table(ROW_COUNT) + + snapshot = session.snapshot(read_timestamp=committed) + rows = list(snapshot.read( + self.TABLE, self.COLUMNS, + KeySet(keys=[(index,) for index in indices]))) + + all_data_rows = list(self._row_data(ROW_COUNT)) + expected = [row for row in all_data_rows if row[0] in indices] + self._check_row_data(rows, expected) + def test_read_w_limit(self): ROW_COUNT = 4000 LIMIT = 100 @@ -597,21 +624,40 @@ def test_read_w_limit(self): expected = all_data_rows[:LIMIT] self._check_row_data(rows, expected) - def test_read_w_range(self): - from google.cloud.spanner.keyset import KeyRange + def test_read_w_ranges(self): ROW_COUNT = 4000 - START_CLOSED = 1000 - END_OPEN = 2000 + START = 1000 + END = 2000 session, committed = self._set_up_table(ROW_COUNT) - key_range = KeyRange(start_closed=[START_CLOSED], end_open=[END_OPEN]) - keyset = KeySet(ranges=(key_range,)) - snapshot = session.snapshot(read_timestamp=committed) + all_data_rows = list(self._row_data(ROW_COUNT)) + + closed_closed = KeyRange(start_closed=[START], end_closed=[END]) + keyset = KeySet(ranges=(closed_closed,)) rows = list(snapshot.read( self.TABLE, self.COLUMNS, keyset)) + expected = all_data_rows[START:END+1] + self._check_row_data(rows, expected) - all_data_rows = list(self._row_data(ROW_COUNT)) - expected = all_data_rows[START_CLOSED:END_OPEN] + closed_open = KeyRange(start_closed=[START], end_open=[END]) + keyset = KeySet(ranges=(closed_open,)) + rows = list(snapshot.read( + self.TABLE, self.COLUMNS, keyset)) + expected = all_data_rows[START:END] + self._check_row_data(rows, expected) + + open_open = KeyRange(start_open=[START], end_open=[END]) + keyset = KeySet(ranges=(open_open,)) + rows = list(snapshot.read( + self.TABLE, self.COLUMNS, keyset)) + expected = all_data_rows[START+1:END] + self._check_row_data(rows, expected) + + open_closed = KeyRange(start_open=[START], end_closed=[END]) + keyset = KeySet(ranges=(open_closed,)) + rows = list(snapshot.read( + self.TABLE, self.COLUMNS, keyset)) + expected = all_data_rows[START+1:END+1] self._check_row_data(rows, expected) def test_execute_sql_w_manual_consume(self): From 41c521d50b64b48299eb43a3bbfb874f6910d457 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 11 Jul 2017 13:03:29 -0400 Subject: [PATCH 3/5] Additional system test for 'Snapshot.execute_sql': - Query returning 'ARRAY'. --- spanner/tests/system/test_system.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spanner/tests/system/test_system.py b/spanner/tests/system/test_system.py index 298e1e5cfc86..24caccac92bc 100644 --- a/spanner/tests/system/test_system.py +++ b/spanner/tests/system/test_system.py @@ -687,6 +687,26 @@ def _check_sql_results(self, snapshot, sql, params, param_types, expected): sql, params=params, param_types=param_types)) self._check_row_data(rows, expected=expected) + def test_execute_sql_returning_array_of_struct(self): + SQL = ( + "SELECT ARRAY(SELECT AS STRUCT C1, C2 " + "FROM (SELECT 'a' AS C1, 1 AS C2 " + "UNION ALL SELECT 'b' AS C1, 2 AS C2) " + "ORDER BY C1 ASC)" + ) + session = self._db.session() + session.create() + self.to_delete.append(session) + snapshot = session.snapshot() + self._check_sql_results( + snapshot, + sql=SQL, + params=None, + param_types=None, + expected=[ + [[['a', 1], ['b', 2]]], + ]) + def test_execute_sql_w_query_param(self): session = self._db.session() session.create() From 9aefd29ca99ffe3127e9c343b172c107f5a6a551 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 11 Jul 2017 13:34:29 -0400 Subject: [PATCH 4/5] Additional system test for 'Snapshot.execute_sql': - Bind INT64 parameter to null. --- spanner/tests/system/test_system.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spanner/tests/system/test_system.py b/spanner/tests/system/test_system.py index 24caccac92bc..c4065d199d83 100644 --- a/spanner/tests/system/test_system.py +++ b/spanner/tests/system/test_system.py @@ -784,6 +784,14 @@ def test_execute_sql_w_query_param(self): expected=[(u'dog',)], ) + self._check_sql_results( + snapshot, + sql='SELECT description FROM all_types WHERE eye_d = @my_id', + params={'my_id': None}, + param_types={'my_id': Type(code=INT64)}, + expected=[], + ) + self._check_sql_results( snapshot, sql='SELECT eye_d FROM all_types WHERE description = @description', From b176bef2e493040682e667cc014c0c65352000e1 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 11 Jul 2017 15:21:29 -0400 Subject: [PATCH 5/5] Additional system test for 'Snapshot.read': - Read timestamp. - Min read timestamp. - Max staleness. - Exact staleness. - Strong. --- spanner/tests/system/test_system.py | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/spanner/tests/system/test_system.py b/spanner/tests/system/test_system.py index c4065d199d83..b4ac62194bb1 100644 --- a/spanner/tests/system/test_system.py +++ b/spanner/tests/system/test_system.py @@ -538,6 +538,42 @@ def _unit_of_work(transaction, test): return session, committed + def test_snapshot_read_w_various_staleness(self): + from datetime import datetime + from google.cloud._helpers import UTC + ROW_COUNT = 400 + session, committed = self._set_up_table(ROW_COUNT) + all_data_rows = list(self._row_data(ROW_COUNT)) + + before_reads = datetime.utcnow().replace(tzinfo=UTC) + + # Test w/ read timestamp + read_tx = session.snapshot(read_timestamp=committed) + rows = list(read_tx.read(self.TABLE, self.COLUMNS, self.ALL)) + self._check_row_data(rows, all_data_rows) + + # Test w/ min read timestamp + min_read_ts = session.snapshot(min_read_timestamp=committed) + rows = list(min_read_ts.read(self.TABLE, self.COLUMNS, self.ALL)) + self._check_row_data(rows, all_data_rows) + + staleness = datetime.utcnow().replace(tzinfo=UTC) - before_reads + + # Test w/ max staleness + max_staleness = session.snapshot(max_staleness=staleness) + rows = list(max_staleness.read(self.TABLE, self.COLUMNS, self.ALL)) + self._check_row_data(rows, all_data_rows) + + # Test w/ exact staleness + exact_staleness = session.snapshot(exact_staleness=staleness) + rows = list(exact_staleness.read(self.TABLE, self.COLUMNS, self.ALL)) + self._check_row_data(rows, all_data_rows) + + # Test w/ strong + strong = session.snapshot() + rows = list(strong.read(self.TABLE, self.COLUMNS, self.ALL)) + self._check_row_data(rows, all_data_rows) + def test_read_w_manual_consume(self): ROW_COUNT = 4000 session, committed = self._set_up_table(ROW_COUNT)