From 4cdb25bf5d70e6e8a789c75c59a2a908433674ce Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 15 Mar 2017 17:10:28 -0400 Subject: [PATCH] Apply DDLCompiler name rules to Index for autogenerate The autogenerate compare scheme now takes into account the name truncation rules applied by SQLAlchemy's DDL compiler to the names of the :class:`.Index` object, when these names are dynamically truncated due to a too-long identifier name. As the identifier truncation is deterministic, applying the same rule to the metadata name allows correct comparison to the database-derived name. Change-Id: I270fbde4430a41f4bcc7857f1932347d86f07675 Fixes: #421 --- alembic/autogenerate/compare.py | 8 +++++++- alembic/util/sqla_compat.py | 7 +++++++ docs/build/changelog.rst | 11 +++++++++++ tests/test_autogen_indexes.py | 24 ++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/alembic/autogenerate/compare.py b/alembic/autogenerate/compare.py index 0d4f4d2..f393c40 100644 --- a/alembic/autogenerate/compare.py +++ b/alembic/autogenerate/compare.py @@ -277,6 +277,9 @@ def _compare_columns(schema, tname, conn_table, metadata_table, class _constraint_sig(object): + def md_name_to_sql_name(self, context): + return self.name + def __eq__(self, other): return self.const == other.const @@ -310,6 +313,9 @@ class _ix_constraint_sig(_constraint_sig): self.sig = tuple(sorted([col.name for col in const.columns])) self.is_unique = bool(const.unique) + def md_name_to_sql_name(self, context): + return sqla_compat._get_index_final_name(context.dialect, self.const) + @property def column_names(self): return sqla_compat._get_index_column_names(self.const) @@ -433,7 +439,7 @@ def _compare_indexes_and_uniques( # 5. index things by name, for those objects that have names metadata_names = dict( - (c.name, c) for c in + (c.md_name_to_sql_name(autogen_context), c) for c in metadata_unique_constraints.union(metadata_indexes) if c.name is not None) diff --git a/alembic/util/sqla_compat.py b/alembic/util/sqla_compat.py index 2d56d3e..28a6548 100644 --- a/alembic/util/sqla_compat.py +++ b/alembic/util/sqla_compat.py @@ -171,3 +171,10 @@ def _get_index_expressions(idx): def _get_index_column_names(idx): return [getattr(exp, "name", None) for exp in _get_index_expressions(idx)] + + +def _get_index_final_name(dialect, idx): + if sqla_08: + return dialect.ddl_compiler(dialect, None)._prepared_index_name(idx) + else: + return idx.name diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index 32a9a2c..7835883 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -7,6 +7,17 @@ Changelog :version: 0.9.2 :released: + .. change:: 421 + :tags: bug, autogenerate + :tickets: 421 + + The autogenerate compare scheme now takes into account the name truncation + rules applied by SQLAlchemy's DDL compiler to the names of the + :class:`.Index` object, when these names are dynamically truncated + due to a too-long identifier name. As the identifier truncation is + deterministic, applying the same rule to the metadata name allows + correct comparison to the database-derived name. + .. change:: 419 :tags: bug environment :tickets: 419 diff --git a/tests/test_autogen_indexes.py b/tests/test_autogen_indexes.py index bec90a7..85e0731 100644 --- a/tests/test_autogen_indexes.py +++ b/tests/test_autogen_indexes.py @@ -979,3 +979,27 @@ class IncludeHooksTest(AutogenFixtureTest, TestBase): eq_(diffs[1][0], 'add_constraint') eq_(diffs[1][1].name, 'uq2') eq_(len(diffs), 2) + + +class TruncatedIdxTest(AutogenFixtureTest, TestBase): + __requires__ = ('sqlalchemy_09', ) + + def setUp(self): + self.bind = engines.testing_engine() + self.bind.dialect.max_identifier_length = 30 + + def test_idx_matches_long(self): + from alembic.operations.base import conv + + m1 = MetaData() + Table( + 'q', m1, + Column('id', Integer, primary_key=True), + Column('data', Integer), + Index( + conv("idx_q_table_this_is_more_than_thirty_characters"), + "data") + ) + + diffs = self._fixture(m1, m1) + eq_(diffs, [])