-
-
Notifications
You must be signed in to change notification settings - Fork 309
Description
Describe the bug
We have a table that is currently un-versioned, and inconsistent. When running autogenerate against this table, there is an index that is changing case. As per MySQL documentation, see link below, "Column, index, stored routine, and event names are not case-sensitive on any platform, nor are column aliases.". However, alembic is trying to create the new index first, before dropping the old one, which results in a "Duplicate key name" error.
https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivity.html
Expected behavior
I think there are two options here. The first is probably the better option. Even if the engine ignores case, there are good reasons that developers use things like "camel case" in naming conventions, and we want the table definitions to be consistent with this.
-
The expected behaviour here should be that alembic does the drop operation first, and then the create. This way the index can be renamed properly. I was able to work around this error by manually swapping the statements in the autogenerate output. However, I don't think it's acceptable to have to look for these over manually every time. We have hundreds of tables to audit here and shouldn't have to manually verify every index in the script.
-
The other option is to just ignore case, like MySQL does, and do nothing to the index. Although, in this case, the index will not match naming that's in the Python class definition; which could cause other issues elsewhere.
To Reproduce
Create this table in an existing database before alembic migration:
CREATE TABLE `SorterHistory` (
`eventType` char(20) NOT NULL,
`lpn` char(20) DEFAULT NULL,
`carton` char(20) DEFAULT NULL,
`orderNumber` char(20) DEFAULT NULL,
`chute` char(10) DEFAULT NULL,
`tray` char(6) DEFAULT NULL,
`remSortQty` int(11) DEFAULT NULL,
`reason` char(20) DEFAULT NULL,
`workList` char(20) DEFAULT NULL,
`gaylordNumber` char(20) DEFAULT NULL,
`shipVia` char(4) DEFAULT NULL,
`createTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`idx` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`idx`),
KEY `lpn` (`lpn`),
KEY `carton` (`carton`),
KEY `OrderNumber` (`orderNumber`),
KEY `createTime` (`createTime`),
KEY `eventType` (`eventType`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
This is the new schema we're trying to apply using alembic:
naming_convention={
'ix': '%(column_0N_name)s',
'uq': '%(column_0N_name)s',
'ck': '%(column_0N_name)s',
'fk': 'fk_%(table_name)s_%(referred_table_name)s',
'pk': '%(column_0N_name)s',
}
SorterHistory_base = declarative_base(metadata=MetaData(naming_convention=naming_convention))
metadata = SorterHistory_base.metadata
class SorterHistory(SorterHistory_base):
__tablename__ = 'SorterHistory'
__table_args__ = {'mysql_engine': 'InnoDB'}
eventType = Column(String(20), nullable=False, index=True)
lpn = Column(String(20), index=True)
carton = Column(String(20), index=True)
orderNumber = Column(String(20), index=True)
chute = Column(String(10))
tray = Column(String(6))
remSortQty = Column(Integer)
reason = Column(String(20))
workList = Column(String(20))
gaylordNumber = Column(String(20))
shipVia = Column(String(4))
cartonNumber = Column(String(20))
createTime = Column(TIMESTAMP, nullable=False, index=True, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"))
idx = Column(BIGINT(unsigned=True), primary_key=True, autoincrement=True)
After running alembic revision --autogenerate we get this in our version script. You can clearly see that the order of operations is going to cause an error here because of case insensitivity on the index name.
op.create_index(op.f('orderNumber'), 'SorterHistory', ['orderNumber'], unique=False)
op.drop_index('OrderNumber', table_name='SorterHistory')
Here is the full stack trace when running alembic upgrade head.
Traceback (most recent call last):
File "/home/kshutt/mandate/bin/alembic", line 11, in <module>
sys.exit(main())
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/config.py", line 559, in main
CommandLine(prog=prog).main(argv=argv)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/config.py", line 553, in main
self.run_cmd(cfg, options)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/config.py", line 533, in run_cmd
**dict((k, getattr(options, k, None)) for k in kwarg)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/command.py", line 294, in upgrade
script.run_env()
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/script/base.py", line 481, in run_env
util.load_python_file(self.dir, "env.py")
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/util/pyfiles.py", line 97, in load_python_file
module = load_module_py(module_id, path)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/util/compat.py", line 217, in load_module_py
mod = imp.load_source(module_id, path, fp)
File "alembic/env.py", line 204, in <module>
run_migrations_online()
File "alembic/env.py", line 180, in run_migrations_online
context.run_migrations(engine_name=name)
File "<string>", line 8, in run_migrations
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/runtime/environment.py", line 813, in run_migrations
self.get_context().run_migrations(**kw)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/runtime/migration.py", line 560, in run_migrations
step.migration_fn(**kw)
File "/home/kshutt/cofe/database/migrations/WineDirect/alembic/versions/da62181f1b57_.py", line 20, in upgrade
globals()["upgrade_%s" % engine_name]()
File "/home/kshutt/cofe/database/migrations/WineDirect/alembic/versions/da62181f1b57_.py", line 453, in upgrade_WD_ECOM
op.create_index(op.f('orderNumber'), 'SorterHistory', ['orderNumber'], unique=False)
File "<string>", line 8, in create_index
File "<string>", line 3, in create_index
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/operations/ops.py", line 871, in create_index
return operations.invoke(op)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/operations/base.py", line 354, in invoke
return fn(self, operation)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/operations/toimpl.py", line 87, in create_index
operations.impl.create_index(idx)
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/ddl/impl.py", line 300, in create_index
self._exec(schema.CreateIndex(index))
File "/home/kshutt/mandate/lib/python2.7/site-packages/alembic/ddl/impl.py", line 146, in _exec
return conn.execute(construct, multiparams)
File "/home/kshutt/mandate/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1011, in execute
return meth(self, multiparams, params)
File "/home/kshutt/mandate/lib/python2.7/site-packages/sqlalchemy/sql/ddl.py", line 72, in _execute_on_connection
return connection._execute_ddl(self, multiparams, params)
File "/home/kshutt/mandate/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1073, in _execute_ddl
compiled,
File "/home/kshutt/mandate/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1317, in _execute_context
e, statement, parameters, cursor, context
File "/home/kshutt/mandate/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1511, in _handle_dbapi_exception
sqlalchemy_exception, with_traceback=exc_info[2], from_=e
File "/home/kshutt/mandate/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1277, in _execute_context
cursor, statement, parameters, context
File "/home/kshutt/mandate/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 609, in do_execute
cursor.execute(statement, parameters)
File "/home/kshutt/mandate/lib/python2.7/site-packages/MySQLdb/cursors.py", line 209, in execute
res = self._query(query)
File "/home/kshutt/mandate/lib/python2.7/site-packages/MySQLdb/cursors.py", line 315, in _query
db.query(q)
File "/home/kshutt/mandate/lib/python2.7/site-packages/MySQLdb/connections.py", line 239, in query
_mysql.connection.query(self, query)
sqlalchemy.exc.OperationalError: (MySQLdb._exceptions.OperationalError) (1061, "Duplicate key name 'orderNumber'")
[SQL: CREATE INDEX `orderNumber` ON `SorterHistory` (`orderNumber`)]
(Background on this error at: http://sqlalche.me/e/13/e3q8)
Versions.
- OS: CentOS Linux release 7.9.2009 (all patches applied up to 1/27/2021)
- Python: 2.7.5
- Alembic: 1.5.2
- SQLAlchemy: 1.3.22
- Database: mysql Ver 14.14 Distrib 5.7.32-35, for Linux (x86_64) using 6.2
- DBAPI: mysqlclient 1.4.6, MySQL-python 1.2.5
Additional context
Please feel free to ask questions if additional information is needed here.
Have a nice day!