diff --git a/README.rst b/README.rst index 464402e2..46cb2fe4 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,6 @@ +Forked from official release - customized to handle ttl (row deletion policy) and column `OPTIONS (allow_auto_commit = true)` + + Spanner dialect for SQLAlchemy ============================== diff --git a/google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py b/google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py index f72d95a1..b1bc110a 100644 --- a/google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py +++ b/google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py @@ -150,18 +150,14 @@ def pre_exec(self): """ super(SpannerExecutionContext, self).pre_exec() - read_only = self.execution_options.get("read_only") + read_only = self.execution_options.get("read_only", None) if read_only is not None: self._dbapi_connection.connection.read_only = read_only - staleness = self.execution_options.get("staleness") + staleness = self.execution_options.get("staleness", None) if staleness is not None: self._dbapi_connection.connection.staleness = staleness - priority = self.execution_options.get("request_priority") - if priority is not None: - self._dbapi_connection.connection.request_priority = priority - class SpannerIdentifierPreparer(IdentifierPreparer): """Identifiers compiler. @@ -341,10 +337,11 @@ def get_column_specification(self, column, **kwargs): Overridden to move the NOT NULL statement to front of a computed column expression definitions. """ + column_type = self.dialect.type_compiler.process(column.type, type_expression=column) colspec = ( self.preparer.format_column(column) + " " - + self.dialect.type_compiler.process(column.type, type_expression=column) + + column_type ) if not column.nullable: colspec += " NOT NULL" @@ -353,9 +350,11 @@ def get_column_specification(self, column, **kwargs): if default is not None: colspec += " DEFAULT (" + default + ")" - if hasattr(column, "computed") and column.computed is not None: + if column.computed is not None: colspec += " " + self.process(column.computed) - + + if "spanner_allow_commit_timestamp" in column.kwargs and column_type == "TIMESTAMP": + colspec += " OPTIONS (allow_commit_timestamp=true)" return colspec def visit_computed_column(self, generated, **kw): @@ -442,7 +441,10 @@ def post_create_table(self, table): if table.kwargs.get("spanner_interleave_on_delete_cascade"): post_cmds += " ON DELETE CASCADE" - + + if table.kwargs.get("spanner_row_deletion_policy") is not None: + timestamp_column, num_days = table.kwargs.get("spanner_row_deletion_policy") + post_cmds += f",\nROW DELETION POLICY ( OLDER_THAN ( {timestamp_column}, INTERVAL {num_days} DAY ) )" return post_cmds @@ -794,13 +796,7 @@ def get_foreign_keys(self, connection, table_name, schema=None, **kw): ctu.table_name, ctu.table_schema, ARRAY_AGG(DISTINCT ccu.column_name), - ARRAY_AGG( - DISTINCT CONCAT( - CAST(kcu.ordinal_position AS STRING), - '_____', - kcu.column_name - ) - ) + ARRAY_AGG(kcu.column_name) FROM information_schema.table_constraints AS tc JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name @@ -821,21 +817,6 @@ def get_foreign_keys(self, connection, table_name, schema=None, **kw): rows = snap.execute_sql(sql) for row in rows: - # Due to Spanner limitations, arrays order is not guaranteed during - # aggregation. Still, for constraints it's vital to keep the order - # of the referred columns, otherwise SQLAlchemy and Alembic may start - # to occasionally drop and recreate constraints. To avoid this, the - # method uses prefixes with the `key_column_usage.ordinal_position` - # values to ensure the columns are aggregated into an array in the - # correct order. Prefixes are only used under the hood. For more details - # see the issue: - # https://github.com/googleapis/python-spanner-sqlalchemy/issues/271 - # - # The solution seem a bit clumsy, and should be improved as soon as a - # better approach found. - for index, value in enumerate(sorted(row[4])): - row[4][index] = value.split("_____")[1] - keys.append( { "name": row[0], @@ -845,7 +826,6 @@ def get_foreign_keys(self, connection, table_name, schema=None, **kw): "constrained_columns": row[4], } ) - return keys @engine_to_connection diff --git a/setup.py b/setup.py index 72fa5653..adef62c4 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ name = "sqlalchemy-spanner" description = "SQLAlchemy dialect integrated into Cloud Spanner database" dependencies = [ - "sqlalchemy>=1.1.13", + "sqlalchemy<2.0.0", "google-cloud-spanner>=3.12.0", "alembic", ] @@ -37,10 +37,10 @@ BASE_DIR = os.path.dirname(__file__) VERSION_FILENAME = os.path.join(BASE_DIR, "version.py") PACKAGE_INFO = {} -with open(VERSION_FILENAME) as f: - exec(f.read(), PACKAGE_INFO) -version = PACKAGE_INFO["__version__"] - +# with open(VERSION_FILENAME) as f: +# exec(f.read(), PACKAGE_INFO) +# version = PACKAGE_INFO["__version__"] +version="1.2.2rc2" package_root = os.path.abspath(os.path.dirname(__file__)) readme_filename = os.path.join(package_root, "README.rst") with io.open(readme_filename, encoding="utf-8") as readme_file: @@ -61,7 +61,7 @@ setuptools.setup( author="Google LLC", - author_email="cloud-spanner-developers@googlegroups.com", + author_email="ged1182@gmail.com", classifiers=["Intended Audience :: Developers"], description=description, long_description=readme, @@ -75,8 +75,9 @@ name=name, namespace_packages=namespaces, packages=packages, - url="https://github.com/cloudspannerecosystem/python-spanner-sqlalchemy", + url="https://github.com/ged1182/python-spanner-sqlalchemy", version=version, + package_data={'': ['version.py']}, include_package_data=True, zip_safe=False, ) diff --git a/version.py b/version.py index 4cbbb516..c56a201f 100644 --- a/version.py +++ b/version.py @@ -4,4 +4,4 @@ # license that can be found in the LICENSE file or at # https://developers.google.com/open-source/licenses/bsd -__version__ = "1.2.2" +__version__ = "1.2.2rc1"