1414
1515import collections
1616import datetime
17+ import decimal
1718import math
1819import operator
1920import os
3839from google .cloud .spanner_v1 .proto .type_pb2 import INT64
3940from google .cloud .spanner_v1 .proto .type_pb2 import STRING
4041from google .cloud .spanner_v1 .proto .type_pb2 import TIMESTAMP
42+ from google .cloud .spanner_v1 .proto .type_pb2 import NUMERIC
4143from google .cloud .spanner_v1 .proto .type_pb2 import Type
4244
4345from google .cloud ._helpers import UTC
5254from test_utils .retry import RetryResult
5355from test_utils .system import unique_resource_id
5456from tests ._fixtures import DDL_STATEMENTS
57+ from tests ._fixtures import EMULATOR_DDL_STATEMENTS
5558from tests ._helpers import OpenTelemetryBase , HAS_OPENTELEMETRY_INSTALLED
5659
5760
5861CREATE_INSTANCE = os .getenv ("GOOGLE_CLOUD_TESTS_CREATE_SPANNER_INSTANCE" ) is not None
5962USE_EMULATOR = os .getenv ("SPANNER_EMULATOR_HOST" ) is not None
63+ SKIP_BACKUP_TESTS = os .getenv ("SKIP_BACKUP_TESTS" ) is not None
6064
6165if CREATE_INSTANCE :
6266 INSTANCE_ID = "google-cloud" + unique_resource_id ("-" )
@@ -92,7 +96,8 @@ class Config(object):
9296
9397
9498def _has_all_ddl (database ):
95- return len (database .ddl_statements ) == len (DDL_STATEMENTS )
99+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
100+ return len (database .ddl_statements ) == len (ddl_statements )
96101
97102
98103def _list_instances ():
@@ -284,8 +289,9 @@ class TestDatabaseAPI(unittest.TestCase, _TestData):
284289 @classmethod
285290 def setUpClass (cls ):
286291 pool = BurstyPool (labels = {"testcase" : "database_api" })
292+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
287293 cls ._db = Config .INSTANCE .database (
288- cls .DATABASE_NAME , ddl_statements = DDL_STATEMENTS , pool = pool
294+ cls .DATABASE_NAME , ddl_statements = ddl_statements , pool = pool
289295 )
290296 operation = cls ._db .create ()
291297 operation .result (30 ) # raises on failure / timeout.
@@ -359,12 +365,13 @@ def test_update_database_ddl_with_operation_id(self):
359365 temp_db = Config .INSTANCE .database (temp_db_id , pool = pool )
360366 create_op = temp_db .create ()
361367 self .to_delete .append (temp_db )
368+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
362369
363370 # We want to make sure the operation completes.
364371 create_op .result (240 ) # raises on failure / timeout.
365372 # random but shortish always start with letter
366373 operation_id = "a" + str (uuid .uuid4 ())[:8 ]
367- operation = temp_db .update_ddl (DDL_STATEMENTS , operation_id = operation_id )
374+ operation = temp_db .update_ddl (ddl_statements , operation_id = operation_id )
368375
369376 self .assertEqual (operation_id , operation .operation .name .split ("/" )[- 1 ])
370377
@@ -373,7 +380,7 @@ def test_update_database_ddl_with_operation_id(self):
373380
374381 temp_db .reload ()
375382
376- self .assertEqual (len (temp_db .ddl_statements ), len (DDL_STATEMENTS ))
383+ self .assertEqual (len (temp_db .ddl_statements ), len (ddl_statements ))
377384
378385 def test_db_batch_insert_then_db_snapshot_read (self ):
379386 retry = RetryInstanceState (_has_all_ddl )
@@ -447,15 +454,17 @@ def _unit_of_work(transaction, name):
447454
448455
449456@unittest .skipIf (USE_EMULATOR , "Skipping backup tests" )
457+ @unittest .skipIf (SKIP_BACKUP_TESTS , "Skipping backup tests" )
450458class TestBackupAPI (unittest .TestCase , _TestData ):
451459 DATABASE_NAME = "test_database" + unique_resource_id ("_" )
452460 DATABASE_NAME_2 = "test_database2" + unique_resource_id ("_" )
453461
454462 @classmethod
455463 def setUpClass (cls ):
456464 pool = BurstyPool (labels = {"testcase" : "database_api" })
465+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
457466 db1 = Config .INSTANCE .database (
458- cls .DATABASE_NAME , ddl_statements = DDL_STATEMENTS , pool = pool
467+ cls .DATABASE_NAME , ddl_statements = ddl_statements , pool = pool
459468 )
460469 db2 = Config .INSTANCE .database (cls .DATABASE_NAME_2 , pool = pool )
461470 cls ._db = db1
@@ -736,6 +745,8 @@ def test_list_backups(self):
736745(OTHER_NAN ,) = struct .unpack ("<d" , b"\x01 \x00 \x01 \x00 \x00 \x00 \xf8 \xff " )
737746BYTES_1 = b"Ymlu"
738747BYTES_2 = b"Ym9vdHM="
748+ NUMERIC_1 = decimal .Decimal ("0.123456789" )
749+ NUMERIC_2 = decimal .Decimal ("1234567890" )
739750ALL_TYPES_TABLE = "all_types"
740751ALL_TYPES_COLUMNS = (
741752 "pkey" ,
@@ -753,9 +764,18 @@ def test_list_backups(self):
753764 "string_array" ,
754765 "timestamp_value" ,
755766 "timestamp_array" ,
767+ "numeric_value" ,
768+ "numeric_array" ,
756769)
770+ EMULATOR_ALL_TYPES_COLUMNS = ALL_TYPES_COLUMNS [:- 2 ]
757771AllTypesRowData = collections .namedtuple ("AllTypesRowData" , ALL_TYPES_COLUMNS )
758772AllTypesRowData .__new__ .__defaults__ = tuple ([None for colum in ALL_TYPES_COLUMNS ])
773+ EmulatorAllTypesRowData = collections .namedtuple (
774+ "EmulatorAllTypesRowData" , EMULATOR_ALL_TYPES_COLUMNS
775+ )
776+ EmulatorAllTypesRowData .__new__ .__defaults__ = tuple (
777+ [None for colum in EMULATOR_ALL_TYPES_COLUMNS ]
778+ )
759779
760780ALL_TYPES_ROWDATA = (
761781 # all nulls
@@ -769,6 +789,7 @@ def test_list_backups(self):
769789 AllTypesRowData (pkey = 106 , string_value = u"VALUE" ),
770790 AllTypesRowData (pkey = 107 , timestamp_value = SOME_TIME ),
771791 AllTypesRowData (pkey = 108 , timestamp_value = NANO_TIME ),
792+ AllTypesRowData (pkey = 109 , numeric_value = NUMERIC_1 ),
772793 # empty array values
773794 AllTypesRowData (pkey = 201 , int_array = []),
774795 AllTypesRowData (pkey = 202 , bool_array = []),
@@ -777,6 +798,7 @@ def test_list_backups(self):
777798 AllTypesRowData (pkey = 205 , float_array = []),
778799 AllTypesRowData (pkey = 206 , string_array = []),
779800 AllTypesRowData (pkey = 207 , timestamp_array = []),
801+ AllTypesRowData (pkey = 208 , numeric_array = []),
780802 # non-empty array values, including nulls
781803 AllTypesRowData (pkey = 301 , int_array = [123 , 456 , None ]),
782804 AllTypesRowData (pkey = 302 , bool_array = [True , False , None ]),
@@ -785,6 +807,36 @@ def test_list_backups(self):
785807 AllTypesRowData (pkey = 305 , float_array = [3.1415926 , 2.71828 , None ]),
786808 AllTypesRowData (pkey = 306 , string_array = [u"One" , u"Two" , None ]),
787809 AllTypesRowData (pkey = 307 , timestamp_array = [SOME_TIME , NANO_TIME , None ]),
810+ AllTypesRowData (pkey = 308 , numeric_array = [NUMERIC_1 , NUMERIC_2 , None ]),
811+ )
812+ EMULATOR_ALL_TYPES_ROWDATA = (
813+ # all nulls
814+ EmulatorAllTypesRowData (pkey = 0 ),
815+ # Non-null values
816+ EmulatorAllTypesRowData (pkey = 101 , int_value = 123 ),
817+ EmulatorAllTypesRowData (pkey = 102 , bool_value = False ),
818+ EmulatorAllTypesRowData (pkey = 103 , bytes_value = BYTES_1 ),
819+ EmulatorAllTypesRowData (pkey = 104 , date_value = SOME_DATE ),
820+ EmulatorAllTypesRowData (pkey = 105 , float_value = 1.4142136 ),
821+ EmulatorAllTypesRowData (pkey = 106 , string_value = u"VALUE" ),
822+ EmulatorAllTypesRowData (pkey = 107 , timestamp_value = SOME_TIME ),
823+ EmulatorAllTypesRowData (pkey = 108 , timestamp_value = NANO_TIME ),
824+ # empty array values
825+ EmulatorAllTypesRowData (pkey = 201 , int_array = []),
826+ EmulatorAllTypesRowData (pkey = 202 , bool_array = []),
827+ EmulatorAllTypesRowData (pkey = 203 , bytes_array = []),
828+ EmulatorAllTypesRowData (pkey = 204 , date_array = []),
829+ EmulatorAllTypesRowData (pkey = 205 , float_array = []),
830+ EmulatorAllTypesRowData (pkey = 206 , string_array = []),
831+ EmulatorAllTypesRowData (pkey = 207 , timestamp_array = []),
832+ # non-empty array values, including nulls
833+ EmulatorAllTypesRowData (pkey = 301 , int_array = [123 , 456 , None ]),
834+ EmulatorAllTypesRowData (pkey = 302 , bool_array = [True , False , None ]),
835+ EmulatorAllTypesRowData (pkey = 303 , bytes_array = [BYTES_1 , BYTES_2 , None ]),
836+ EmulatorAllTypesRowData (pkey = 304 , date_array = [SOME_DATE , None ]),
837+ EmulatorAllTypesRowData (pkey = 305 , float_array = [3.1415926 , 2.71828 , None ]),
838+ EmulatorAllTypesRowData (pkey = 306 , string_array = [u"One" , u"Two" , None ]),
839+ EmulatorAllTypesRowData (pkey = 307 , timestamp_array = [SOME_TIME , NANO_TIME , None ]),
788840)
789841
790842
@@ -794,8 +846,9 @@ class TestSessionAPI(OpenTelemetryBase, _TestData):
794846 @classmethod
795847 def setUpClass (cls ):
796848 pool = BurstyPool (labels = {"testcase" : "session_api" })
849+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
797850 cls ._db = Config .INSTANCE .database (
798- cls .DATABASE_NAME , ddl_statements = DDL_STATEMENTS , pool = pool
851+ cls .DATABASE_NAME , ddl_statements = ddl_statements , pool = pool
799852 )
800853 operation = cls ._db .create ()
801854 operation .result (30 ) # raises on failure / timeout.
@@ -899,13 +952,19 @@ def test_batch_insert_then_read_all_datatypes(self):
899952 retry = RetryInstanceState (_has_all_ddl )
900953 retry (self ._db .reload )()
901954
955+ if USE_EMULATOR :
956+ all_types_columns = EMULATOR_ALL_TYPES_COLUMNS
957+ all_types_rowdata = EMULATOR_ALL_TYPES_ROWDATA
958+ else :
959+ all_types_columns = ALL_TYPES_COLUMNS
960+ all_types_rowdata = ALL_TYPES_ROWDATA
902961 with self ._db .batch () as batch :
903962 batch .delete (ALL_TYPES_TABLE , self .ALL )
904- batch .insert (ALL_TYPES_TABLE , ALL_TYPES_COLUMNS , ALL_TYPES_ROWDATA )
963+ batch .insert (ALL_TYPES_TABLE , all_types_columns , all_types_rowdata )
905964
906965 with self ._db .snapshot (read_timestamp = batch .committed ) as snapshot :
907- rows = list (snapshot .read (ALL_TYPES_TABLE , ALL_TYPES_COLUMNS , self .ALL ))
908- self ._check_rows_data (rows , expected = ALL_TYPES_ROWDATA )
966+ rows = list (snapshot .read (ALL_TYPES_TABLE , all_types_columns , self .ALL ))
967+ self ._check_rows_data (rows , expected = all_types_rowdata )
909968
910969 def test_batch_insert_or_update_then_query (self ):
911970 retry = RetryInstanceState (_has_all_ddl )
@@ -1704,9 +1763,10 @@ def test_read_w_index(self):
17041763 MY_COLUMNS = self .COLUMNS [0 ], self .COLUMNS [2 ]
17051764 EXTRA_DDL = ["CREATE INDEX contacts_by_last_name ON contacts(last_name)" ]
17061765 pool = BurstyPool (labels = {"testcase" : "read_w_index" })
1766+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
17071767 temp_db = Config .INSTANCE .database (
17081768 "test_read" + unique_resource_id ("_" ),
1709- ddl_statements = DDL_STATEMENTS + EXTRA_DDL ,
1769+ ddl_statements = ddl_statements + EXTRA_DDL ,
17101770 pool = pool ,
17111771 )
17121772 operation = temp_db .create ()
@@ -2282,6 +2342,10 @@ def test_execute_sql_w_date_bindings(self):
22822342 dates = [SOME_DATE , SOME_DATE + datetime .timedelta (days = 1 )]
22832343 self ._bind_test_helper (DATE , SOME_DATE , dates )
22842344
2345+ @unittest .skipIf (USE_EMULATOR , "Skipping NUMERIC" )
2346+ def test_execute_sql_w_numeric_bindings (self ):
2347+ self ._bind_test_helper (NUMERIC , NUMERIC_1 , [NUMERIC_1 , NUMERIC_2 ])
2348+
22852349 def test_execute_sql_w_query_param_struct (self ):
22862350 NAME = "Phred"
22872351 COUNT = 123
0 commit comments