Merge pull request #13624 from ic4f/dev_alembic_fix3

Check if column exists before executing upgrade/downgrade operations in select revisions
This commit is contained in:
Sergey Golitsynskiy
2022-03-28 12:34:32 -04:00
committed by GitHub
2 changed files with 17 additions and 34 deletions
@@ -10,8 +10,8 @@ from sqlalchemy import Column
from galaxy.model.custom_types import JSONType
from galaxy.model.migrations.util import (
column_exists,
drop_column,
ignore_add_column_error,
)
# revision identifiers, used by Alembic.
@@ -20,12 +20,15 @@ down_revision = "e7b6dcb09efd"
branch_labels = None
depends_on = None
# database object names user in this revision
table_name = "workflow"
column_name = "source_metadata"
def upgrade():
table, column = "workflow", "source_metadata"
with ignore_add_column_error(table, column):
op.add_column(table, Column(column, JSONType))
if not column_exists(table_name, column_name):
op.add_column(table_name, Column(column_name, JSONType))
def downgrade():
drop_column("workflow", "source_metadata")
drop_column(table_name, column_name)
+9 -29
View File
@@ -1,38 +1,18 @@
import logging
from contextlib import contextmanager
from alembic import op
from sqlalchemy.exc import (
OperationalError,
ProgrammingError,
)
from sqlalchemy import inspect
log = logging.getLogger(__name__)
def drop_column(table, column):
with op.batch_alter_table(table) as batch_op:
batch_op.drop_column(column)
def drop_column(table_name, column_name):
with op.batch_alter_table(table_name) as batch_op:
batch_op.drop_column(column_name)
@contextmanager
def ignore_add_column_error(table, column):
"""
Use this context manager to wrap statements in upgrade/downgrade functions
in revision files when a statement may cause an error that may be safely
ignored. For example, in revision b182f655505f, if an upgrade operation is
executed on a database that has been previoiusly upgraded via SQLAlchemy
Migrate to version 181, a subsequent upgrade to Alembic may cause such as
error. For more details, see https://github.com/galaxyproject/galaxy/issues/13528.
We are checking for 2 different error types: OperationalError is raised
for SQLite, ProgrammingError is raised for PostgreSQL.
"""
statement = f"ALTER TABLE {table} ADD COLUMN {column}"
try:
yield
except (ProgrammingError, OperationalError) as e:
if e.statement.startswith(statement):
log.error(f"Ignoring error: {e}")
else:
raise e
def column_exists(table_name, column_name):
bind = op.get_context().bind
insp = inspect(bind)
columns = insp.get_columns(table_name)
return any(c["name"] == column_name for c in columns)