alembic icon indicating copy to clipboard operation
alembic copied to clipboard

generalized "recreate foreign key" / "recreate constraint" / "drop constraint if needed" types of directives + autogenerate?

Open sqlalchemy-bot opened this issue 10 years ago • 15 comments

Migrated issue, originally created by Stéphane Klein (@harobed)

With MSSql, drop_column have some specific options https://github.com/zzzeek/alembic/blob/master/alembic/operations.py#L504 like : "mssql_drop_foreign_key".

I suggest to append specifics options for Mysql :

  • "mysql_drop_contraints"
  • "mysql_drop_foreign_key"

What do you think about this suggest ?

sqlalchemy-bot avatar Oct 13 '14 09:10 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

whats the error mysql gives for both of these conditions when the drop column is otherwise emitted?

sqlalchemy-bot avatar Oct 13 '14 14:10 sqlalchemy-bot

Stéphane Klein (@harobed) wrote:

#!

Traceback (most recent call last):
  File "/home/myproject/webapp-venv/bin/alembic", line 9, in <module>
    load_entry_point('alembic==0.6.5', 'console_scripts', 'alembic')()
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/config.py", line 298, in main
    CommandLine(prog=prog).main(argv=argv)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/config.py", line 293, in main
    self.run_cmd(cfg, options)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/config.py", line 279, in run_cmd
    **dict((k, getattr(options, k)) for k in kwarg)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/command.py", line 125, in upgrade
    script.run_env()
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/script.py", line 203, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/util.py", line 212, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/compat.py", line 58, in load_module_py
    mod = imp.load_source(module_id, path, fp)
  File "/home/myproject/src/myproject-api-webapp/alembic/env.py", line 67, in <module>
    run_migrations_online()
  File "/home/myproject/src/myproject-api-webapp/alembic/env.py", line 60, in run_migrations_online
    context.run_migrations()
  File "<string>", line 7, in run_migrations
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/environment.py", line 688, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/migration.py", line 258, in run_migrations
    change(**kw)
  File "/home/myproject/src/myproject-api-webapp/alembic/versions/45dd19c3a9e5_remove_application_id_price_by_mounth_.py", line 24, in upgrade
    op.drop_column('rel_end_user_plans_history', 'application_id')
  File "<string>", line 7, in drop_column
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/operations.py", line 477, in drop_column
    **kw
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/ddl/impl.py", line 130, in drop_column
    self._exec(base.DropColumn(table_name, column, schema=schema))
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/alembic/ddl/impl.py", line 76, in _exec
    conn.execute(construct, *multiparams, **params)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 662, in execute
    params)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 720, in _execute_ddl
    compiled
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 874, in _execute_context
    context)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1024, in _handle_dbapi_exception
    exc_info
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 163, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 867, in _execute_context
    context)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 324, in do_execute
    cursor.execute(statement, parameters)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/MySQLdb/cursors.py", line 205, in execute
    self.errorhandler(self, exc, value)
  File "/home/myproject/webapp-venv/local/lib/python2.7/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue
sqlalchemy.exc.OperationalError: (OperationalError) (1025, "Error on rename of './api/#sql-a1d_4c' to './api/rel_end_user_plans_history' (errno: 150)") 'ALTER TABLE rel_end_user_plans_history DROP COLUMN application_id' ()

sqlalchemy-bot avatar Oct 13 '14 15:10 sqlalchemy-bot

Stéphane Klein (@harobed) wrote:

This is without :

    op.drop_constraint(
        'rel_end_user_plans_history_ibfk_3',
        'rel_end_user_plans_history',
        type_='foreignkey'
    )

sqlalchemy-bot avatar Oct 13 '14 15:10 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

and there's no cascade option, OK. I'd go with just mysql_drop_constraints if possible; an FK is a constraint. However, the trick here is that the whole thing ideally can work in offline mode so has to figure out the names of these constraints in an inline fashion.

sqlalchemy-bot avatar Oct 13 '14 15:10 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

looking at #247, these constraints are the ones that the column to be dropped owns, right? It seems like #247 would help a lot with this if we implement autogenerate as including the constraints explicitly first. that might not replace the desirability of these flags but it would be a more general solution.

sqlalchemy-bot avatar Nov 21 '14 16:11 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

seems to be just foreign keys owned by the column. I can drop cols mentioned in unique constraints and indexes.

sqlalchemy-bot avatar Nov 21 '14 16:11 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

note also #184 which proposes allowing dialect-specific flags to render within autogenerate.

sqlalchemy-bot avatar Apr 07 '15 14:04 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

Issue #440 was marked as a duplicate of this issue.

sqlalchemy-bot avatar Aug 03 '17 19:08 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

we need CHECK constraints because MariaDB both adds them and also temporarily won't let us drop a column that has them (will be fixed in an upcoming 10.2).

sqlalchemy-bot avatar Aug 03 '17 19:08 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

well at the moment I don't see how this is possible for CHECK constraints - trying mariadb 10.2, there's no way to get the list of columns inside the CHECK other than using SHOW CREATE TABLE and parsing; and it's not possible to use SHOW CREATE TABLE within server-side SQL. So only adding CHECK constraints to SQLAlchemy reflection, regexing on the Python side would make this possible, which rules out this being a SQL side flag like we do for SQL Server.

sqlalchemy-bot avatar Aug 03 '17 21:08 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

note that CHECK constraints is an issue repaired on the MariaDB side.

for this issue need to :

  1. re-confirm that we can't rename a MySQL column that is referred to by a foreign key, and also maybe check this for mariadb / mysql / versions etc.

  2. determine if inline SQL can handle this without the name of the constraint needing to be passed, e.g. a DROP that is serviced from getting the constraint names in the information schema.

  3. assuming #2 is a no-go, consider having autogenerate be intelligent about this kind of thing and to actually generate the migration like this:

with op.conditional_recreate_fk("foobar", ["c1", "c2"], ["remote.c1" ,"remote.c2"], onupdate="cascade"):
    op.rename_constraint(....)

that is, we introduce "condtiional_recreate" as well as "conditional_drop" directives, which look at things like if we are on MySQL, or SQL Server, etc. and then emit the DROP/CREATE if necessary.

this is a much more generalized feature than the "mssql_drop_constraint" flag. More thought would need to go into this.

sqlalchemy-bot avatar Nov 27 '17 22:11 sqlalchemy-bot

Changes by Stéphane Klein (@harobed):

  • edited description

sqlalchemy-bot avatar Oct 13 '14 09:10 sqlalchemy-bot

Changes by Michael Bayer (@zzzeek):

  • removed labels: feature
  • added labels: feature

sqlalchemy-bot avatar Oct 28 '14 17:10 sqlalchemy-bot

Changes by Michael Bayer (@zzzeek):

  • set milestone to "tier 1"

sqlalchemy-bot avatar Oct 28 '14 17:10 sqlalchemy-bot

Changes by Michael Bayer (@zzzeek):

  • changed title from "Append "mysql_drop_contraints" and "mysql_drop_for" to "generalized "recreate foreign key" / "recreate con"

sqlalchemy-bot avatar Nov 27 '17 22:11 sqlalchemy-bot