14
14
15
15
import collections
16
16
import datetime
17
+ import decimal
17
18
import math
18
19
import operator
19
20
import os
38
39
from google .cloud .spanner_v1 .proto .type_pb2 import INT64
39
40
from google .cloud .spanner_v1 .proto .type_pb2 import STRING
40
41
from google .cloud .spanner_v1 .proto .type_pb2 import TIMESTAMP
42
+ from google .cloud .spanner_v1 .proto .type_pb2 import NUMERIC
41
43
from google .cloud .spanner_v1 .proto .type_pb2 import Type
42
44
43
45
from google .cloud ._helpers import UTC
52
54
from test_utils .retry import RetryResult
53
55
from test_utils .system import unique_resource_id
54
56
from tests ._fixtures import DDL_STATEMENTS
57
+ from tests ._fixtures import EMULATOR_DDL_STATEMENTS
55
58
from tests ._helpers import OpenTelemetryBase , HAS_OPENTELEMETRY_INSTALLED
56
59
57
60
58
61
CREATE_INSTANCE = os .getenv ("GOOGLE_CLOUD_TESTS_CREATE_SPANNER_INSTANCE" ) is not None
59
62
USE_EMULATOR = os .getenv ("SPANNER_EMULATOR_HOST" ) is not None
63
+ SKIP_BACKUP_TESTS = os .getenv ("SKIP_BACKUP_TESTS" ) is not None
60
64
61
65
if CREATE_INSTANCE :
62
66
INSTANCE_ID = "google-cloud" + unique_resource_id ("-" )
@@ -92,7 +96,8 @@ class Config(object):
92
96
93
97
94
98
def _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 )
96
101
97
102
98
103
def _list_instances ():
@@ -284,8 +289,9 @@ class TestDatabaseAPI(unittest.TestCase, _TestData):
284
289
@classmethod
285
290
def setUpClass (cls ):
286
291
pool = BurstyPool (labels = {"testcase" : "database_api" })
292
+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
287
293
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
289
295
)
290
296
operation = cls ._db .create ()
291
297
operation .result (30 ) # raises on failure / timeout.
@@ -359,12 +365,13 @@ def test_update_database_ddl_with_operation_id(self):
359
365
temp_db = Config .INSTANCE .database (temp_db_id , pool = pool )
360
366
create_op = temp_db .create ()
361
367
self .to_delete .append (temp_db )
368
+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
362
369
363
370
# We want to make sure the operation completes.
364
371
create_op .result (240 ) # raises on failure / timeout.
365
372
# random but shortish always start with letter
366
373
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 )
368
375
369
376
self .assertEqual (operation_id , operation .operation .name .split ("/" )[- 1 ])
370
377
@@ -373,7 +380,7 @@ def test_update_database_ddl_with_operation_id(self):
373
380
374
381
temp_db .reload ()
375
382
376
- self .assertEqual (len (temp_db .ddl_statements ), len (DDL_STATEMENTS ))
383
+ self .assertEqual (len (temp_db .ddl_statements ), len (ddl_statements ))
377
384
378
385
def test_db_batch_insert_then_db_snapshot_read (self ):
379
386
retry = RetryInstanceState (_has_all_ddl )
@@ -447,15 +454,17 @@ def _unit_of_work(transaction, name):
447
454
448
455
449
456
@unittest .skipIf (USE_EMULATOR , "Skipping backup tests" )
457
+ @unittest .skipIf (SKIP_BACKUP_TESTS , "Skipping backup tests" )
450
458
class TestBackupAPI (unittest .TestCase , _TestData ):
451
459
DATABASE_NAME = "test_database" + unique_resource_id ("_" )
452
460
DATABASE_NAME_2 = "test_database2" + unique_resource_id ("_" )
453
461
454
462
@classmethod
455
463
def setUpClass (cls ):
456
464
pool = BurstyPool (labels = {"testcase" : "database_api" })
465
+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
457
466
db1 = Config .INSTANCE .database (
458
- cls .DATABASE_NAME , ddl_statements = DDL_STATEMENTS , pool = pool
467
+ cls .DATABASE_NAME , ddl_statements = ddl_statements , pool = pool
459
468
)
460
469
db2 = Config .INSTANCE .database (cls .DATABASE_NAME_2 , pool = pool )
461
470
cls ._db = db1
@@ -736,6 +745,8 @@ def test_list_backups(self):
736
745
(OTHER_NAN ,) = struct .unpack ("<d" , b"\x01 \x00 \x01 \x00 \x00 \x00 \xf8 \xff " )
737
746
BYTES_1 = b"Ymlu"
738
747
BYTES_2 = b"Ym9vdHM="
748
+ NUMERIC_1 = decimal .Decimal ("0.123456789" )
749
+ NUMERIC_2 = decimal .Decimal ("1234567890" )
739
750
ALL_TYPES_TABLE = "all_types"
740
751
ALL_TYPES_COLUMNS = (
741
752
"pkey" ,
@@ -753,9 +764,18 @@ def test_list_backups(self):
753
764
"string_array" ,
754
765
"timestamp_value" ,
755
766
"timestamp_array" ,
767
+ "numeric_value" ,
768
+ "numeric_array" ,
756
769
)
770
+ EMULATOR_ALL_TYPES_COLUMNS = ALL_TYPES_COLUMNS [:- 2 ]
757
771
AllTypesRowData = collections .namedtuple ("AllTypesRowData" , ALL_TYPES_COLUMNS )
758
772
AllTypesRowData .__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
+ )
759
779
760
780
ALL_TYPES_ROWDATA = (
761
781
# all nulls
@@ -769,6 +789,7 @@ def test_list_backups(self):
769
789
AllTypesRowData (pkey = 106 , string_value = u"VALUE" ),
770
790
AllTypesRowData (pkey = 107 , timestamp_value = SOME_TIME ),
771
791
AllTypesRowData (pkey = 108 , timestamp_value = NANO_TIME ),
792
+ AllTypesRowData (pkey = 109 , numeric_value = NUMERIC_1 ),
772
793
# empty array values
773
794
AllTypesRowData (pkey = 201 , int_array = []),
774
795
AllTypesRowData (pkey = 202 , bool_array = []),
@@ -777,6 +798,7 @@ def test_list_backups(self):
777
798
AllTypesRowData (pkey = 205 , float_array = []),
778
799
AllTypesRowData (pkey = 206 , string_array = []),
779
800
AllTypesRowData (pkey = 207 , timestamp_array = []),
801
+ AllTypesRowData (pkey = 208 , numeric_array = []),
780
802
# non-empty array values, including nulls
781
803
AllTypesRowData (pkey = 301 , int_array = [123 , 456 , None ]),
782
804
AllTypesRowData (pkey = 302 , bool_array = [True , False , None ]),
@@ -785,6 +807,36 @@ def test_list_backups(self):
785
807
AllTypesRowData (pkey = 305 , float_array = [3.1415926 , 2.71828 , None ]),
786
808
AllTypesRowData (pkey = 306 , string_array = [u"One" , u"Two" , None ]),
787
809
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 ]),
788
840
)
789
841
790
842
@@ -794,8 +846,9 @@ class TestSessionAPI(OpenTelemetryBase, _TestData):
794
846
@classmethod
795
847
def setUpClass (cls ):
796
848
pool = BurstyPool (labels = {"testcase" : "session_api" })
849
+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
797
850
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
799
852
)
800
853
operation = cls ._db .create ()
801
854
operation .result (30 ) # raises on failure / timeout.
@@ -899,13 +952,19 @@ def test_batch_insert_then_read_all_datatypes(self):
899
952
retry = RetryInstanceState (_has_all_ddl )
900
953
retry (self ._db .reload )()
901
954
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
902
961
with self ._db .batch () as batch :
903
962
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 )
905
964
906
965
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 )
909
968
910
969
def test_batch_insert_or_update_then_query (self ):
911
970
retry = RetryInstanceState (_has_all_ddl )
@@ -1704,9 +1763,10 @@ def test_read_w_index(self):
1704
1763
MY_COLUMNS = self .COLUMNS [0 ], self .COLUMNS [2 ]
1705
1764
EXTRA_DDL = ["CREATE INDEX contacts_by_last_name ON contacts(last_name)" ]
1706
1765
pool = BurstyPool (labels = {"testcase" : "read_w_index" })
1766
+ ddl_statements = EMULATOR_DDL_STATEMENTS if USE_EMULATOR else DDL_STATEMENTS
1707
1767
temp_db = Config .INSTANCE .database (
1708
1768
"test_read" + unique_resource_id ("_" ),
1709
- ddl_statements = DDL_STATEMENTS + EXTRA_DDL ,
1769
+ ddl_statements = ddl_statements + EXTRA_DDL ,
1710
1770
pool = pool ,
1711
1771
)
1712
1772
operation = temp_db .create ()
@@ -2282,6 +2342,10 @@ def test_execute_sql_w_date_bindings(self):
2282
2342
dates = [SOME_DATE , SOME_DATE + datetime .timedelta (days = 1 )]
2283
2343
self ._bind_test_helper (DATE , SOME_DATE , dates )
2284
2344
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
+
2285
2349
def test_execute_sql_w_query_param_struct (self ):
2286
2350
NAME = "Phred"
2287
2351
COUNT = 123
0 commit comments