From: Mike Bayer Date: Mon, 14 Oct 2019 04:07:55 +0000 (-0400) Subject: Accommodate for SQLAlchemy 1.4 deferral of index/unique names X-Git-Tag: rel_1_3_0~1^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e90002d7fd325218c18f556daab94ab13b46877d;p=thirdparty%2Fsqlalchemy%2Falembic.git Accommodate for SQLAlchemy 1.4 deferral of index/unique names Some internal modifications have been made to how the names of indexes and unique constraints work to make use of new functions added in SQLAlchemy 1.4, so that SQLAlchemy has more flexibility over how naming conventions may be applied to these objects. Change-Id: I7dc4f6c1c3cf13c503f2ab070afbf157f8f0e0d5 --- diff --git a/alembic/autogenerate/compare.py b/alembic/autogenerate/compare.py index 49ceb21e..cacedee7 100644 --- a/alembic/autogenerate/compare.py +++ b/alembic/autogenerate/compare.py @@ -338,7 +338,9 @@ def _compare_columns( class _constraint_sig(object): def md_name_to_sql_name(self, context): - return self.name + return sqla_compat._get_constraint_final_name( + self.const, context.dialect + ) def __eq__(self, other): return self.const == other.const @@ -529,7 +531,8 @@ def _compare_indexes_and_uniques( metadata_names = dict( (c.md_name_to_sql_name(autogen_context), c) for c in metadata_unique_constraints.union(metadata_indexes) - if c.name is not None + if isinstance(c, _ix_constraint_sig) + or sqla_compat._constraint_is_named(c.const, autogen_context.dialect) ) conn_uniques_by_name = dict((c.name, c) for c in conn_unique_constraints) @@ -556,7 +559,11 @@ def _compare_indexes_and_uniques( ) metadata_indexes_by_sig = dict((ix.sig, ix) for ix in metadata_indexes) unnamed_metadata_uniques = dict( - (uq.sig, uq) for uq in metadata_unique_constraints if uq.name is None + (uq.sig, uq) + for uq in metadata_unique_constraints + if not sqla_compat._constraint_is_named( + uq.const, autogen_context.dialect + ) ) # assumptions: diff --git a/alembic/util/sqla_compat.py b/alembic/util/sqla_compat.py index 37331425..c3b10dc1 100644 --- a/alembic/util/sqla_compat.py +++ b/alembic/util/sqla_compat.py @@ -183,15 +183,11 @@ def _column_kwargs(col): def _get_index_final_name(dialect, idx): - # trying to keep the truncation rules totally localized on the - # SQLA side while also stepping around the quoting issue. Ideally - # the _prepared_index_name() method on the SQLA side would have - # a quoting option or the truncation routine would be broken out. - # - # test for SQLA quoted_name construct, introduced in - # 0.9 or thereabouts. - # this doesn't work in 0.8 and the "quote" option on Index doesn't - # seem to work in 0.8 either. + if sqla_14: + return _get_constraint_final_name(idx, dialect) + + # prior to SQLAlchemy 1.4, work around quoting logic to get at the + # final compiled name without quotes. if hasattr(idx.name, "quote"): # might be quoted_name, might be truncated_name, keep it the # same @@ -201,6 +197,35 @@ def _get_index_final_name(dialect, idx): return dialect.ddl_compiler(dialect, None)._prepared_index_name(idx) +def _get_constraint_final_name(constraint, dialect): + if sqla_14: + if constraint.name is None: + return None + + # for SQLAlchemy 1.4 we would like to have the option to expand + # the use of "deferred" names for constraints as well as to have + # some flexibility with "None" name and similar; make use of new + # SQLAlchemy API to return what would be the final compiled form of + # the name for this dialect. + return dialect.identifier_preparer.format_constraint( + constraint, _alembic_quote=False + ) + else: + return constraint.name + + +def _constraint_is_named(constraint, dialect): + if sqla_14: + if constraint.name is None: + return False + name = dialect.identifier_preparer.format_constraint( + constraint, _alembic_quote=False + ) + return name is not None + else: + return constraint.name is not None + + def _dialect_supports_comments(dialect): if sqla_120: return dialect.supports_comments diff --git a/docs/build/unreleased/sqla14_4911.rst b/docs/build/unreleased/sqla14_4911.rst new file mode 100644 index 00000000..71463cb7 --- /dev/null +++ b/docs/build/unreleased/sqla14_4911.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: change, compatibility + + Some internal modifications have been made to how the names of indexes and + unique constraints work to make use of new functions added in SQLAlchemy + 1.4, so that SQLAlchemy has more flexibility over how naming conventions + may be applied to these objects. diff --git a/tests/test_autogen_indexes.py b/tests/test_autogen_indexes.py index c8eaffb5..416444c9 100644 --- a/tests/test_autogen_indexes.py +++ b/tests/test_autogen_indexes.py @@ -18,6 +18,7 @@ from alembic.testing import engines from alembic.testing import eq_ from alembic.testing import TestBase from alembic.testing.env import staging_env +from alembic.util import sqla_compat from ._autogen_fixtures import AutogenFixtureTest py3k = sys.version_info >= (3,) @@ -204,7 +205,12 @@ class AutogenerateUniqueIndexTest(AutogenFixtureTest, TestBase): eq_(diffs[0][0], "add_table") eq_(diffs[1][0], "add_index") - eq_(diffs[1][1].name, "ix_extra_foo") + eq_( + sqla_compat._get_constraint_final_name( + diffs[1][1], config.db.dialect + ), + "ix_extra_foo", + ) eq_(diffs[2][0], "add_index") eq_(diffs[2][1].name, "newtable_idx") @@ -257,6 +263,40 @@ class AutogenerateUniqueIndexTest(AutogenFixtureTest, TestBase): diffs = self._fixture(m1, m2) eq_(diffs, []) + def test_nothing_changed_implicit_uq_w_naming_conv(self): + m1 = MetaData( + naming_convention={ + "ix": "ix_%(column_0_label)s", + "uq": "uq_%(column_0_label)s", + } + ) + m2 = MetaData( + naming_convention={ + "ix": "ix_%(column_0_label)s", + "uq": "uq_%(column_0_label)s", + } + ) + + Table( + "nothing_changed", + m1, + Column("id1", Integer, primary_key=True), + Column("id2", Integer, primary_key=True), + Column("x", String(20), unique=True), + mysql_engine="InnoDB", + ) + + Table( + "nothing_changed", + m2, + Column("id1", Integer, primary_key=True), + Column("id2", Integer, primary_key=True), + Column("x", String(20), unique=True), + mysql_engine="InnoDB", + ) + diffs = self._fixture(m1, m2) + eq_(diffs, []) + def test_nothing_changed_two(self): m1 = MetaData() m2 = MetaData()