Skip to content

Commit d2551b0

Browse files
ansh0llarkee
andauthored
feat: Add support for Postgresql dialect (#741)
* chore: regen (via synth) : fix conflicts * feat: add NUMERIC support: conflicts resolved * feat: add dialect support: fix conflicts * fix: update table queries to support PG dialect * feat: add database dialect support for database factory * test: add dialect support to system tests: resolve conflict, correct POSTGRES_ALL_TYPES_COLUMNS * feat: postgres dialect - review fixes * feat: postgres dialect - review fixes * feat: postgres dialect - review fixes * feat: postgres dialect - review fixes * feat: postgres dialect - review fixes * feat: postgres dialect - review fixes * feat: postgres dialect - review fixes * feat: postgres dialect - review fixes * feat: postgres dialect - docstring fixes * feat: fix linting * feat: add opentelemetry version in noxfile to remove failures * feat: add opentelemetry version and constraints.txt * Revert "feat: add opentelemetry version and constraints.txt" This reverts commit 8525bf5. * Revert "feat: add opentelemetry version in noxfile to remove failures" This reverts commit 666285b. * feat: removing duplicate imports * feat: correcting imports * feat: correcting imports * feat: skip backup tests * feat: correct the import * feat: fix linting Co-authored-by: larkee <[email protected]>
1 parent fa5ba0a commit d2551b0

File tree

18 files changed

+486
-189
lines changed

18 files changed

+486
-189
lines changed

google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ class CreateDatabaseRequest(proto.Message):
294294
Cloud Spanner will encrypt/decrypt all data at
295295
rest using Google default encryption.
296296
database_dialect (google.cloud.spanner_admin_database_v1.types.DatabaseDialect):
297-
Optional. The dialect of the Cloud Spanner
297+
Output only. The dialect of the Cloud Spanner
298298
Database.
299299
"""
300300

google/cloud/spanner_v1/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
from .types.transaction import TransactionSelector
5858
from .types.type import StructType
5959
from .types.type import Type
60+
from .types.type import TypeAnnotationCode
6061
from .types.type import TypeCode
6162
from .data_types import JsonObject
6263

@@ -132,6 +133,7 @@
132133
"TransactionOptions",
133134
"TransactionSelector",
134135
"Type",
136+
"TypeAnnotationCode",
135137
"TypeCode",
136138
# Custom spanner related data types
137139
"JsonObject",

google/cloud/spanner_v1/backup.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def __init__(
9494
self._encryption_info = None
9595
self._max_expire_time = None
9696
self._referencing_backups = None
97+
self._database_dialect = None
9798
if type(encryption_config) == dict:
9899
if source_backup:
99100
self._encryption_config = CopyBackupEncryptionConfig(
@@ -193,7 +194,7 @@ def referencing_databases(self):
193194
@property
194195
def encryption_info(self):
195196
"""Encryption info for this backup.
196-
:rtype: :class:`~google.clod.spanner_admin_database_v1.types.EncryptionInfo`
197+
:rtype: :class:`~google.cloud.spanner_admin_database_v1.types.EncryptionInfo`
197198
:returns: a class representing the encryption info
198199
"""
199200
return self._encryption_info
@@ -216,6 +217,13 @@ def referencing_backups(self):
216217
"""
217218
return self._referencing_backups
218219

220+
def database_dialect(self):
221+
"""Database Dialect for this backup.
222+
:rtype: :class:`~google.cloud.spanner_admin_database_v1.types.DatabaseDialect`
223+
:returns: a class representing the dialect of this backup's database
224+
"""
225+
return self._database_dialect
226+
219227
@classmethod
220228
def from_pb(cls, backup_pb, instance):
221229
"""Create an instance of this class from a protobuf message.

google/cloud/spanner_v1/database.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from google.cloud.spanner_admin_database_v1 import RestoreDatabaseEncryptionConfig
3535
from google.cloud.spanner_admin_database_v1 import RestoreDatabaseRequest
3636
from google.cloud.spanner_admin_database_v1 import UpdateDatabaseDdlRequest
37+
from google.cloud.spanner_admin_database_v1.types import DatabaseDialect
3738
from google.cloud.spanner_v1 import ExecuteSqlRequest
3839
from google.cloud.spanner_v1 import TransactionSelector
3940
from google.cloud.spanner_v1 import TransactionOptions
@@ -68,7 +69,7 @@
6869

6970
_LIST_TABLES_QUERY = """SELECT TABLE_NAME
7071
FROM INFORMATION_SCHEMA.TABLES
71-
WHERE SPANNER_STATE = 'COMMITTED'
72+
{}
7273
"""
7374

7475
DEFAULT_RETRY_BACKOFF = Retry(initial=0.02, maximum=32, multiplier=1.3)
@@ -114,6 +115,11 @@ class Database(object):
114115
If a dict is provided, it must be of the same form as either of the protobuf
115116
messages :class:`~google.cloud.spanner_admin_database_v1.types.EncryptionConfig`
116117
or :class:`~google.cloud.spanner_admin_database_v1.types.RestoreDatabaseEncryptionConfig`
118+
:type database_dialect:
119+
:class:`~google.cloud.spanner_admin_database_v1.types.DatabaseDialect`
120+
:param database_dialect:
121+
(Optional) database dialect for the database
122+
117123
"""
118124

119125
_spanner_api = None
@@ -126,6 +132,7 @@ def __init__(
126132
pool=None,
127133
logger=None,
128134
encryption_config=None,
135+
database_dialect=DatabaseDialect.DATABASE_DIALECT_UNSPECIFIED,
129136
):
130137
self.database_id = database_id
131138
self._instance = instance
@@ -141,6 +148,7 @@ def __init__(
141148
self.log_commit_stats = False
142149
self._logger = logger
143150
self._encryption_config = encryption_config
151+
self._database_dialect = database_dialect
144152

145153
if pool is None:
146154
pool = BurstyPool()
@@ -294,6 +302,18 @@ def ddl_statements(self):
294302
"""
295303
return self._ddl_statements
296304

305+
@property
306+
def database_dialect(self):
307+
"""DDL Statements used to define database schema.
308+
309+
See
310+
cloud.google.com/spanner/docs/data-definition-language
311+
312+
:rtype: :class:`google.cloud.spanner_admin_database_v1.types.DatabaseDialect`
313+
:returns: the dialect of the database
314+
"""
315+
return self._database_dialect
316+
297317
@property
298318
def logger(self):
299319
"""Logger used by the database.
@@ -364,7 +384,10 @@ def create(self):
364384
metadata = _metadata_with_prefix(self.name)
365385
db_name = self.database_id
366386
if "-" in db_name:
367-
db_name = "`%s`" % (db_name,)
387+
if self._database_dialect == DatabaseDialect.POSTGRESQL:
388+
db_name = f'"{db_name}"'
389+
else:
390+
db_name = f"`{db_name}`"
368391
if type(self._encryption_config) == dict:
369392
self._encryption_config = EncryptionConfig(**self._encryption_config)
370393

@@ -373,6 +396,7 @@ def create(self):
373396
create_statement="CREATE DATABASE %s" % (db_name,),
374397
extra_statements=list(self._ddl_statements),
375398
encryption_config=self._encryption_config,
399+
database_dialect=self._database_dialect,
376400
)
377401
future = api.create_database(request=request, metadata=metadata)
378402
return future
@@ -418,6 +442,7 @@ def reload(self):
418442
self._encryption_config = response.encryption_config
419443
self._encryption_info = response.encryption_info
420444
self._default_leader = response.default_leader
445+
self._database_dialect = response.database_dialect
421446

422447
def update_ddl(self, ddl_statements, operation_id=""):
423448
"""Update DDL for this database.
@@ -778,7 +803,11 @@ def list_tables(self):
778803
resources within the current database.
779804
"""
780805
with self.snapshot() as snapshot:
781-
results = snapshot.execute_sql(_LIST_TABLES_QUERY)
806+
if self._database_dialect == DatabaseDialect.POSTGRESQL:
807+
where_clause = "WHERE TABLE_SCHEMA = 'public'"
808+
else:
809+
where_clause = "WHERE SPANNER_STATE = 'COMMITTED'"
810+
results = snapshot.execute_sql(_LIST_TABLES_QUERY.format(where_clause))
782811
for row in results:
783812
yield self.table(row[0])
784813

google/cloud/spanner_v1/instance.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from google.cloud.spanner_admin_instance_v1 import Instance as InstancePB
2626
from google.cloud.spanner_admin_database_v1.types import backup
2727
from google.cloud.spanner_admin_database_v1.types import spanner_database_admin
28+
from google.cloud.spanner_admin_database_v1 import DatabaseDialect
2829
from google.cloud.spanner_admin_database_v1 import ListBackupsRequest
2930
from google.cloud.spanner_admin_database_v1 import ListBackupOperationsRequest
3031
from google.cloud.spanner_admin_database_v1 import ListDatabasesRequest
@@ -428,6 +429,7 @@ def database(
428429
pool=None,
429430
logger=None,
430431
encryption_config=None,
432+
database_dialect=DatabaseDialect.DATABASE_DIALECT_UNSPECIFIED,
431433
):
432434
"""Factory to create a database within this instance.
433435
@@ -458,6 +460,11 @@ def database(
458460
messages :class:`~google.cloud.spanner_admin_database_v1.types.EncryptionConfig`
459461
or :class:`~google.cloud.spanner_admin_database_v1.types.RestoreDatabaseEncryptionConfig`
460462
463+
:type database_dialect:
464+
:class:`~google.cloud.spanner_admin_database_v1.types.DatabaseDialect`
465+
:param database_dialect:
466+
(Optional) database dialect for the database
467+
461468
:rtype: :class:`~google.cloud.spanner_v1.database.Database`
462469
:returns: a database owned by this instance.
463470
"""
@@ -468,6 +475,7 @@ def database(
468475
pool=pool,
469476
logger=logger,
470477
encryption_config=encryption_config,
478+
database_dialect=database_dialect,
471479
)
472480

473481
def list_databases(self, page_size=None):

google/cloud/spanner_v1/param_types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Types exported from this package."""
1616

1717
from google.cloud.spanner_v1 import Type
18+
from google.cloud.spanner_v1 import TypeAnnotationCode
1819
from google.cloud.spanner_v1 import TypeCode
1920
from google.cloud.spanner_v1 import StructType
2021

@@ -29,6 +30,7 @@
2930
TIMESTAMP = Type(code=TypeCode.TIMESTAMP)
3031
NUMERIC = Type(code=TypeCode.NUMERIC)
3132
JSON = Type(code=TypeCode.JSON)
33+
PG_NUMERIC = Type(code=TypeCode.NUMERIC, type_annotation=TypeAnnotationCode.PG_NUMERIC)
3234

3335

3436
def Array(element_type):

google/cloud/spanner_v1/table.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from google.cloud.exceptions import NotFound
1818

19+
from google.cloud.spanner_admin_database_v1 import DatabaseDialect
1920
from google.cloud.spanner_v1.types import (
2021
Type,
2122
TypeCode,
@@ -26,7 +27,7 @@
2627
SELECT EXISTS(
2728
SELECT TABLE_NAME
2829
FROM INFORMATION_SCHEMA.TABLES
29-
WHERE TABLE_NAME = @table_id
30+
{}
3031
)
3132
"""
3233
_GET_SCHEMA_TEMPLATE = "SELECT * FROM {} LIMIT 0"
@@ -76,11 +77,18 @@ def _exists(self, snapshot):
7677
:rtype: bool
7778
:returns: True if the table exists, else false.
7879
"""
79-
results = snapshot.execute_sql(
80-
_EXISTS_TEMPLATE,
81-
params={"table_id": self.table_id},
82-
param_types={"table_id": Type(code=TypeCode.STRING)},
83-
)
80+
if self._database.database_dialect == DatabaseDialect.POSTGRESQL:
81+
results = snapshot.execute_sql(
82+
_EXISTS_TEMPLATE.format("WHERE TABLE_NAME = $1"),
83+
params={"p1": self.table_id},
84+
param_types={"p1": Type(code=TypeCode.STRING)},
85+
)
86+
else:
87+
results = snapshot.execute_sql(
88+
_EXISTS_TEMPLATE.format("WHERE TABLE_NAME = @table_id"),
89+
params={"table_id": self.table_id},
90+
param_types={"table_id": Type(code=TypeCode.STRING)},
91+
)
8492
return next(iter(results))[0]
8593

8694
@property

0 commit comments

Comments
 (0)