alembic icon indicating copy to clipboard operation
alembic copied to clipboard

op.add_column() and op.drop_column() should check for SchemaType, run through the events for that type, or perhaps just create/drop

Open sqlalchemy-bot opened this issue 12 years ago • 14 comments

Migrated issue, originally created by Wichert Akkerman (@wichert)

I have a data model which has a simple enum column:

    size_type = schema.Column(
        types.Enum('normal', 'plus', name='size_type_type'),
        nullable=False, default='normal')

as part of an upgrade routine I need to add that column if it is missing:

    alembic.add_column('article_variant',
            ArticleVariant.__table__.c['size_type'].copy())

alembic is an operation instance here. This operation breaks due to a missing check if the enum type already exists. The resulting error is:

sqlalchemy.exc.ProgrammingError: (ProgrammingError) type "size_type_type" does not exist
LINE 1: ALTER TABLE article_variant ADD COLUMN size_type size_type_t...
                                                         ^
 'ALTER TABLE article_variant ADD COLUMN size_type size_type_type NOT NULL' {}

sqlalchemy-bot avatar Nov 19 '12 17:11 sqlalchemy-bot

Wichert Akkerman (@wichert) wrote:

In case it is useful I added a local workaround which adds the type if it is missing:

context = alembic.get_context()
if context.bind.dialect.name == 'postgresql':
    has_size_type = context.bind.execute(
            "select exists (select 1 from pg_type "
            "where typname='size_type')").scalar()
    if not has_size_type:
        alembic.execute("CREATE TYPE size_type AS ENUM ('normal', 'plus')")

sqlalchemy-bot avatar Nov 19 '12 17:11 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

The creation/dropping of the associated type for ENUM and on some platforms boolean is a "schema event" which responds to metadata or Table oncreate/drop. There's currently not an event for "on column add/drop". So Alembic's add_column() and drop_column() should probably run through the "on table create/drop" events for the type.

What you should do right now is use the create() method on the enumerated type:

ArticleVariant.table.c.size_type.create(alembic.get_bind(), checkfirst=True)

sqlalchemy-bot avatar Nov 19 '12 18:11 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

correction:

bind = alembic.get_bind()
typ = ArticleVariant.__table__.c.size_type.type
impl = typ.dialect_impl(bind.dialect)
impl.create(bind, checkfirst=True)

or this:

bind = alembic.get_bind()
typ = ArticleVariant.__table__.c.size_type.type
typ._on_table_create(ArticleVariant.__table__, bind)

sqlalchemy-bot avatar Nov 19 '12 18:11 sqlalchemy-bot

sc68cal (@sc68cal) wrote:

Just want to close the loop - I used a similar bit of code that @wichert posted for a migration in OpenStack Neutron - just don't forget to also add code to the downgrade part of your script to delete the type

sqlalchemy-bot avatar Feb 06 '14 20:02 sqlalchemy-bot

Anonymous wrote:

Slightly different for me I had to do

bind = op.get_bind()
typ = ArticleVariant.__table__.c.size_type.type
typ._on_table_create(ArticleVariant.__table__, bind, checkfirst=True)

sqlalchemy-bot avatar Jan 16 '15 15:01 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

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

sqlalchemy-bot avatar Mar 09 '15 01:03 sqlalchemy-bot

Maik Derstappen (@mrtango) wrote:

I have created a helper file which use the reflection.Inspector and the get_columns method. This also works for checking befor dropping a column. http://www.derstappen-it.de/tech-blog/sqlalchemie-alembic-check-if-table-has-column

But it would be very usefull to have that in the alembic package.

sqlalchemy-bot avatar Jun 17 '15 09:06 sqlalchemy-bot

Changes by Michael Bayer (@zzzeek):

  • changed title from "Adding column with native enum fails" to "op.add_column() and op.drop_column() should check "

sqlalchemy-bot avatar Nov 19 '12 18:11 sqlalchemy-bot

Changes by Michael Bayer (@zzzeek):

  • added labels: postgresql typing issues

sqlalchemy-bot avatar Jun 07 '13 01:06 sqlalchemy-bot

It would be very useful to have this in alembic.

davidjb99 avatar May 06 '21 15:05 davidjb99

Looks like this is still a problem

sjlawson avatar Jun 23 '21 22:06 sjlawson

please see https://github.com/sqlalchemy/alembic/issues/868#issuecomment-867914756 for rationale why none of these issues are resolved.

zzzeek avatar Jun 24 '21 20:06 zzzeek

Easy workaround:

def downgrade():
    op.execute("""DROP TYPE name_of_enum""")

dwickwire avatar Jul 28 '22 11:07 dwickwire

Alternative workaround:

from alembic import op
import sqlalchemy as sa

def downgrade():
    sa.Enum(name="name_of_enum").drop(op.get_bind(), checkfirst=False)

*Answer copied from here: https://github.com/miguelgrinberg/Flask-Migrate/issues/48

nitishlobo avatar Mar 06 '23 14:03 nitishlobo