From: Mike Bayer Date: Fri, 2 May 2014 20:04:43 +0000 (-0400) Subject: - This releases' "autogenerate index detection" bug, when a MySQL table X-Git-Tag: rel_0_6_5~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=55d97db8c3edc12ee15eb6e7ca6afb173a93e770;p=thirdparty%2Fsqlalchemy%2Falembic.git - This releases' "autogenerate index detection" bug, when a MySQL table includes an Index with the same name as a column, autogenerate reported it as an "add" even though its not; this is because we ignore reflected indexes of this nature due to MySQL creating them implicitly. Indexes that are named the same as a column are now ignored on MySQL if we see that the backend is reporting that it already exists; this indicates that we can still detect additions of these indexes but not drops, as we cannot distinguish a backend index same-named as the column as one that is user generated or mysql-generated. fixes #202 --- diff --git a/alembic/ddl/mysql.py b/alembic/ddl/mysql.py index 088ba59b..96f42f38 100644 --- a/alembic/ddl/mysql.py +++ b/alembic/ddl/mysql.py @@ -71,15 +71,24 @@ class MySQLImpl(DefaultImpl): ) ) - def correct_for_autogen_constraints(self, conn_unique_constraints, conn_indexes, + def correct_for_autogen_constraints(self, conn_unique_constraints, + conn_indexes, metadata_unique_constraints, metadata_indexes): + removed = set() for idx in list(conn_indexes): # MySQL puts implicit indexes on FK columns, even if # composite and even if MyISAM, so can't check this too easily if idx.name == idx.columns.keys()[0]: conn_indexes.remove(idx) - + removed.add(idx.name) + + # then remove indexes from the "metadata_indexes" + # that we've removed from reflected, otherwise they come out + # as adds (see #202) + for idx in list(metadata_indexes): + if idx.name in removed: + metadata_indexes.remove(idx) class MySQLAlterDefault(AlterColumn): def __init__(self, name, column_name, default, schema=None): diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index e312603e..356c506f 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -5,6 +5,20 @@ Changelog .. changelog:: :version: 0.6.5 + .. change:: + :tags: bug, autogenerate, mysql + :tickets: 202 + + This releases' "autogenerate index detection" bug, when a MySQL table + includes an Index with the same name as a column, autogenerate reported + it as an "add" even though its not; this is because we ignore reflected + indexes of this nature due to MySQL creating them implicitly. Indexes + that are named the same as a column are now ignored on + MySQL if we see that the backend is reporting that it already exists; + this indicates that we can still detect additions of these indexes + but not drops, as we cannot distinguish a backend index same-named + as the column as one that is user generated or mysql-generated. + .. change:: :tags: feature, environment :tickets: 201 diff --git a/tests/test_autogen_indexes.py b/tests/test_autogen_indexes.py index 51436709..e9d93211 100644 --- a/tests/test_autogen_indexes.py +++ b/tests/test_autogen_indexes.py @@ -7,8 +7,7 @@ from sqlalchemy import MetaData, Column, Table, Integer, String, Text, \ UniqueConstraint, Boolean, \ PrimaryKeyConstraint, Index, func, ForeignKeyConstraint -from . import staging_env, sqlite_db, clear_staging_env, eq_, \ - eq_ignore_whitespace, db_for_dialect +from . import sqlite_db, eq_, db_for_dialect py3k = sys.version_info >= (3, ) @@ -216,6 +215,68 @@ class AutogenerateUniqueIndexTest(AutogenFixtureTest, TestCase): diffs = self._fixture(m1, m2) eq_(diffs, []) + def test_nothing_changed_index_named_as_column(self): + m1 = MetaData() + m2 = MetaData() + + Table('nothing_changed', m1, + Column('id1', Integer, primary_key=True), + Column('id2', Integer, primary_key=True), + Column('x', String(20)), + Index('x', 'x') + ) + + Table('nothing_changed', m2, + Column('id1', Integer, primary_key=True), + Column('id2', Integer, primary_key=True), + Column('x', String(20)), + Index('x', 'x') + ) + + diffs = self._fixture(m1, m2) + eq_(diffs, []) + + def test_new_idx_index_named_as_column(self): + m1 = MetaData() + m2 = MetaData() + + Table('new_idx', m1, + Column('id1', Integer, primary_key=True), + Column('id2', Integer, primary_key=True), + Column('x', String(20)), + ) + + idx = Index('x', 'x') + Table('new_idx', m2, + Column('id1', Integer, primary_key=True), + Column('id2', Integer, primary_key=True), + Column('x', String(20)), + idx + ) + + diffs = self._fixture(m1, m2) + eq_(diffs, [('add_index', idx)]) + + def test_removed_idx_index_named_as_column(self): + m1 = MetaData() + m2 = MetaData() + + idx = Index('x', 'x') + Table('new_idx', m1, + Column('id1', Integer, primary_key=True), + Column('id2', Integer, primary_key=True), + Column('x', String(20)), + idx + ) + + Table('new_idx', m2, + Column('id1', Integer, primary_key=True), + Column('id2', Integer, primary_key=True), + Column('x', String(20)) + ) + + diffs = self._fixture(m1, m2) + eq_(diffs[0][0], 'remove_index') def test_unnamed_cols_changed(self): m1 = MetaData() @@ -410,6 +471,10 @@ class PGUniqueIndexTest(AutogenerateUniqueIndexTest): class MySQLUniqueIndexTest(AutogenerateUniqueIndexTest): reports_unnamed_constraints = True + def test_removed_idx_index_named_as_column(self): + # TODO: this should be an "assert fails" + pass + @classmethod def _get_bind(cls): return db_for_dialect('mysql') diff --git a/tests/test_autogenerate.py b/tests/test_autogenerate.py index a0f9216a..932a6562 100644 --- a/tests/test_autogenerate.py +++ b/tests/test_autogenerate.py @@ -114,7 +114,8 @@ class AutogenFixtureTest(object): self.bind = self._get_bind() def tearDown(self): - self.metadata.drop_all(self.bind) + if hasattr(self, 'metadata'): + self.metadata.drop_all(self.bind) clear_staging_env() @classmethod