Skip to content

Commit 55d97db

Browse files
committed
- 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
1 parent fc61223 commit 55d97db

File tree

4 files changed

+94
-5
lines changed

4 files changed

+94
-5
lines changed

alembic/ddl/mysql.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,24 @@ def alter_column(self, table_name, column_name,
7171
)
7272
)
7373

74-
def correct_for_autogen_constraints(self, conn_unique_constraints, conn_indexes,
74+
def correct_for_autogen_constraints(self, conn_unique_constraints,
75+
conn_indexes,
7576
metadata_unique_constraints,
7677
metadata_indexes):
78+
removed = set()
7779
for idx in list(conn_indexes):
7880
# MySQL puts implicit indexes on FK columns, even if
7981
# composite and even if MyISAM, so can't check this too easily
8082
if idx.name == idx.columns.keys()[0]:
8183
conn_indexes.remove(idx)
82-
84+
removed.add(idx.name)
85+
86+
# then remove indexes from the "metadata_indexes"
87+
# that we've removed from reflected, otherwise they come out
88+
# as adds (see #202)
89+
for idx in list(metadata_indexes):
90+
if idx.name in removed:
91+
metadata_indexes.remove(idx)
8392

8493
class MySQLAlterDefault(AlterColumn):
8594
def __init__(self, name, column_name, default, schema=None):

docs/build/changelog.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ Changelog
55
.. changelog::
66
:version: 0.6.5
77

8+
.. change::
9+
:tags: bug, autogenerate, mysql
10+
:tickets: 202
11+
12+
This releases' "autogenerate index detection" bug, when a MySQL table
13+
includes an Index with the same name as a column, autogenerate reported
14+
it as an "add" even though its not; this is because we ignore reflected
15+
indexes of this nature due to MySQL creating them implicitly. Indexes
16+
that are named the same as a column are now ignored on
17+
MySQL if we see that the backend is reporting that it already exists;
18+
this indicates that we can still detect additions of these indexes
19+
but not drops, as we cannot distinguish a backend index same-named
20+
as the column as one that is user generated or mysql-generated.
21+
822
.. change::
923
:tags: feature, environment
1024
:tickets: 201

tests/test_autogen_indexes.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
UniqueConstraint, Boolean, \
88
PrimaryKeyConstraint, Index, func, ForeignKeyConstraint
99

10-
from . import staging_env, sqlite_db, clear_staging_env, eq_, \
11-
eq_ignore_whitespace, db_for_dialect
10+
from . import sqlite_db, eq_, db_for_dialect
1211

1312
py3k = sys.version_info >= (3, )
1413

@@ -216,6 +215,68 @@ def test_nothing_changed_two(self):
216215
diffs = self._fixture(m1, m2)
217216
eq_(diffs, [])
218217

218+
def test_nothing_changed_index_named_as_column(self):
219+
m1 = MetaData()
220+
m2 = MetaData()
221+
222+
Table('nothing_changed', m1,
223+
Column('id1', Integer, primary_key=True),
224+
Column('id2', Integer, primary_key=True),
225+
Column('x', String(20)),
226+
Index('x', 'x')
227+
)
228+
229+
Table('nothing_changed', m2,
230+
Column('id1', Integer, primary_key=True),
231+
Column('id2', Integer, primary_key=True),
232+
Column('x', String(20)),
233+
Index('x', 'x')
234+
)
235+
236+
diffs = self._fixture(m1, m2)
237+
eq_(diffs, [])
238+
239+
def test_new_idx_index_named_as_column(self):
240+
m1 = MetaData()
241+
m2 = MetaData()
242+
243+
Table('new_idx', m1,
244+
Column('id1', Integer, primary_key=True),
245+
Column('id2', Integer, primary_key=True),
246+
Column('x', String(20)),
247+
)
248+
249+
idx = Index('x', 'x')
250+
Table('new_idx', m2,
251+
Column('id1', Integer, primary_key=True),
252+
Column('id2', Integer, primary_key=True),
253+
Column('x', String(20)),
254+
idx
255+
)
256+
257+
diffs = self._fixture(m1, m2)
258+
eq_(diffs, [('add_index', idx)])
259+
260+
def test_removed_idx_index_named_as_column(self):
261+
m1 = MetaData()
262+
m2 = MetaData()
263+
264+
idx = Index('x', 'x')
265+
Table('new_idx', m1,
266+
Column('id1', Integer, primary_key=True),
267+
Column('id2', Integer, primary_key=True),
268+
Column('x', String(20)),
269+
idx
270+
)
271+
272+
Table('new_idx', m2,
273+
Column('id1', Integer, primary_key=True),
274+
Column('id2', Integer, primary_key=True),
275+
Column('x', String(20))
276+
)
277+
278+
diffs = self._fixture(m1, m2)
279+
eq_(diffs[0][0], 'remove_index')
219280

220281
def test_unnamed_cols_changed(self):
221282
m1 = MetaData()
@@ -410,6 +471,10 @@ def test_same_tname_two_schemas(self):
410471
class MySQLUniqueIndexTest(AutogenerateUniqueIndexTest):
411472
reports_unnamed_constraints = True
412473

474+
def test_removed_idx_index_named_as_column(self):
475+
# TODO: this should be an "assert fails"
476+
pass
477+
413478
@classmethod
414479
def _get_bind(cls):
415480
return db_for_dialect('mysql')

tests/test_autogenerate.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ def setUp(self):
114114
self.bind = self._get_bind()
115115

116116
def tearDown(self):
117-
self.metadata.drop_all(self.bind)
117+
if hasattr(self, 'metadata'):
118+
self.metadata.drop_all(self.bind)
118119
clear_staging_env()
119120

120121
@classmethod

0 commit comments

Comments
 (0)