]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
Accommodate for SQLAlchemy 1.4 deferral of index/unique names
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 14 Oct 2019 04:07:55 +0000 (00:07 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 14 Oct 2019 15:15:09 +0000 (11:15 -0400)
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

alembic/autogenerate/compare.py
alembic/util/sqla_compat.py
docs/build/unreleased/sqla14_4911.rst [new file with mode: 0644]
tests/test_autogen_indexes.py

index 49ceb21ecb24ed3e0a5c2ec4044ed9ad6352fcb0..cacedee7005d5c1e07765abf9a3e7b725076515b 100644 (file)
@@ -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:
index 373314256bd7b82a539f0320e74a75a5bdebecd2..c3b10dc191ac94d3920051524b70a6695568c6cf 100644 (file)
@@ -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 (file)
index 0000000..71463cb
--- /dev/null
@@ -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.
index c8eaffb5684808de24c4faa30f3fe95ac8b66e77..416444c95258a44bd3ca357d2a071026855e67b2 100644 (file)
@@ -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()