From eeee44eaffae75b9a3d0496ccf478cc74bacef0c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 6 Feb 2020 10:38:57 -0500 Subject: [PATCH] Repair de-quoting logic used for pre-1.4 SQLAlchemy versions Fixed regression in 1.4.0 due to :ticket:`647` where unique constraint comparison with mixed case constraint names while not using a naming convention would produce false positives during autogenerate. Change-Id: Ic3d60f7d44377cdb4937ac0bb2bc11d27d03b8bd Fixes: #654 --- alembic/util/sqla_compat.py | 10 ++- docs/build/unreleased/654.rst | 7 ++ tests/test_autogen_indexes.py | 116 ++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 docs/build/unreleased/654.rst diff --git a/alembic/util/sqla_compat.py b/alembic/util/sqla_compat.py index c7b43d0a..82b65f1e 100644 --- a/alembic/util/sqla_compat.py +++ b/alembic/util/sqla_compat.py @@ -8,6 +8,7 @@ from sqlalchemy.ext.compiler import compiles from sqlalchemy.schema import CheckConstraint from sqlalchemy.schema import Column from sqlalchemy.schema import ForeignKeyConstraint +from sqlalchemy.sql.elements import quoted_name from sqlalchemy.sql.expression import _BindParamClause from sqlalchemy.sql.expression import _TextClause as TextClause from sqlalchemy.sql.visitors import traverse @@ -214,14 +215,19 @@ def _get_constraint_final_name(constraint, dialect): # might be quoted_name, might be truncated_name, keep it the # same quoted_name_cls = type(constraint.name) - new_name = quoted_name_cls(str(constraint.name), quote=False) - constraint = constraint.__class__(name=new_name) + else: + quoted_name_cls = quoted_name + + new_name = quoted_name_cls(str(constraint.name), quote=False) + constraint = constraint.__class__(name=new_name) if isinstance(constraint, schema.Index): + # name should not be quoted. return dialect.ddl_compiler(dialect, None)._prepared_index_name( constraint ) else: + # name should not be quoted. return dialect.identifier_preparer.format_constraint(constraint) diff --git a/docs/build/unreleased/654.rst b/docs/build/unreleased/654.rst new file mode 100644 index 00000000..e21bbd90 --- /dev/null +++ b/docs/build/unreleased/654.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: bug, autogenerate + :tickets: 654 + + Fixed regression in 1.4.0 due to :ticket:`647` where unique constraint + comparison with mixed case constraint names while not using a naming + convention would produce false positives during autogenerate. diff --git a/tests/test_autogen_indexes.py b/tests/test_autogen_indexes.py index 6a8800b2..fb5b3501 100644 --- a/tests/test_autogen_indexes.py +++ b/tests/test_autogen_indexes.py @@ -365,6 +365,122 @@ class AutogenerateUniqueIndexTest(AutogenFixtureTest, TestBase): diffs = self._fixture(m1, m2, max_identifier_length=30) eq_(diffs, []) + def test_nothing_changed_uq_w_mixed_case_nconv_name(self): + m1 = MetaData( + naming_convention={ + "ix": "index_%(table_name)s_%(column_0_label)s", + "uq": "unique_%(table_name)s_%(column_0_label)s", + } + ) + m2 = MetaData( + naming_convention={ + "ix": "index_%(table_name)s_%(column_0_label)s", + "uq": "unique_%(table_name)s_%(column_0_label)s", + } + ) + + Table( + "NothingChanged", + m1, + Column("id", Integer, primary_key=True), + Column("XCol", Integer), + UniqueConstraint("XCol"), + mysql_engine="InnoDB", + ) + + Table( + "NothingChanged", + m2, + Column("id", Integer, primary_key=True), + Column("XCol", Integer), + UniqueConstraint("XCol"), + mysql_engine="InnoDB", + ) + + diffs = self._fixture(m1, m2) + eq_(diffs, []) + + def test_nothing_changed_uq_w_mixed_case_plain_name(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "nothing_changed", + m1, + Column("id", Integer, primary_key=True), + Column("x", Integer), + UniqueConstraint("x", name="SomeConstraint"), + mysql_engine="InnoDB", + ) + + Table( + "nothing_changed", + m2, + Column("id", Integer, primary_key=True), + Column("x", Integer), + UniqueConstraint("x", name="SomeConstraint"), + mysql_engine="InnoDB", + ) + diffs = self._fixture(m1, m2) + eq_(diffs, []) + + def test_nothing_changed_ix_w_mixed_case_plain_name(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "nothing_changed", + m1, + Column("id", Integer, primary_key=True), + Column("x", Integer), + Index("SomeIndex", "x"), + mysql_engine="InnoDB", + ) + + Table( + "nothing_changed", + m2, + Column("id", Integer, primary_key=True), + Column("x", Integer), + Index("SomeIndex", "x"), + mysql_engine="InnoDB", + ) + diffs = self._fixture(m1, m2) + eq_(diffs, []) + + def test_nothing_changed_ix_w_mixed_case_nconv_name(self): + m1 = MetaData( + naming_convention={ + "ix": "index_%(table_name)s_%(column_0_label)s", + "uq": "unique_%(table_name)s_%(column_0_label)s", + } + ) + m2 = MetaData( + naming_convention={ + "ix": "index_%(table_name)s_%(column_0_label)s", + "uq": "unique_%(table_name)s_%(column_0_label)s", + } + ) + + Table( + "NothingChanged", + m1, + Column("id", Integer, primary_key=True), + Column("XCol", Integer, index=True), + mysql_engine="InnoDB", + ) + + Table( + "NothingChanged", + m2, + Column("id", Integer, primary_key=True), + Column("XCol", Integer, index=True), + mysql_engine="InnoDB", + ) + + diffs = self._fixture(m1, m2) + eq_(diffs, []) + def test_nothing_changed_two(self): m1 = MetaData() m2 = MetaData() -- 2.47.2