alembic: `ValueError: Constraint must have a name` in alembic 1.10.0

Describe the bug

ValueError: Constraint must have a name in alembic 1.10.0.

Expected behavior

Migration succeeds.

To Reproduce Please try to provide a Minimal, Complete, and Verifiable example, with the migration script and/or the SQLAlchemy tables or models involved. See also Reporting Bugs on the website.

I’m trying to create a minimal reproducer now, but the migration script that caused the error looks like this:

"""Update run status constraint with killed

Revision ID: cfd24bdc0731
Revises: 89d4b8295536
Create Date: 2019-10-11 15:55:10.853449

"""
import alembic
from alembic import op
from packaging.version import Version
from sqlalchemy import CheckConstraint, Enum

from mlflow.entities import RunStatus, ViewType
from mlflow.entities.lifecycle_stage import LifecycleStage
from mlflow.store.tracking.dbmodels.models import SqlRun, SourceTypes

# revision identifiers, used by Alembic.
revision = "cfd24bdc0731"
down_revision = "2b4d017a5e9b"
branch_labels = None
depends_on = None

old_run_statuses = [
    RunStatus.to_string(RunStatus.SCHEDULED),
    RunStatus.to_string(RunStatus.FAILED),
    RunStatus.to_string(RunStatus.FINISHED),
    RunStatus.to_string(RunStatus.RUNNING),
]

new_run_statuses = [*old_run_statuses, RunStatus.to_string(RunStatus.KILLED)]

# Certain SQL backends (e.g., SQLite) do not preserve CHECK constraints during migrations.
# For these backends, CHECK constraints must be specified as table arguments. Here, we define
# the collection of CHECK constraints that should be preserved when performing the migration.
# The "status" constraint is excluded from this set because it is explicitly modified
# within the migration's `upgrade()` routine.
check_constraint_table_args = [
    CheckConstraint(SqlRun.source_type.in_(SourceTypes), name="source_type"),
    CheckConstraint(
        SqlRun.lifecycle_stage.in_(LifecycleStage.view_type_to_stages(ViewType.ALL)),
        name="runs_lifecycle_stage",
    ),
]


def upgrade():
    # In alembic >= 1.7.0, `table_args` is unnecessary since CHECK constraints are preserved
    # during migrations.
    table_args = (
        [] if Version(alembic.__version__) >= Version("1.7.0") else check_constraint_table_args
    )
    with op.batch_alter_table("runs", table_args=table_args) as batch_op:
        # Transform the "status" column to an `Enum` and define a new check constraint. Specify
        # `native_enum=False` to create a check constraint rather than a
        # database-backend-dependent enum (see https://docs.sqlalchemy.org/en/13/core/
        # type_basics.html#sqlalchemy.types.Enum.params.native_enum)
        batch_op.alter_column(
            "status",
            type_=Enum(
                *new_run_statuses,
                create_constraint=True,
                native_enum=False,
            ),
            existing_type=Enum(
                *old_run_statuses,
                create_constraint=True,
                native_enum=False,
                name="status",
            ),
        )


def downgrade():
    # Omit downgrade logic for now - we don't currently provide users a command/API for
    # reverting a database migration, instead recommending that they take a database backup
    # before running the migration.
    pass

Error

Traceback (most recent call last):
  File "a.py", line 5, in <module>
    with mlflow.start_run():
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/fluent.py", line 278, in start_run
    client = MlflowClient()
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/client.py", line 69, in __init__
    self._tracking_client = TrackingServiceClient(final_tracking_uri)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/_tracking_service/client.py", line 51, in __init__
    self.store
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/_tracking_service/client.py", line 55, in store
    return utils._get_store(self.tracking_uri)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/_tracking_service/utils.py", line 217, in _get_store
    return _tracking_store_registry.get_store(store_uri, artifact_uri)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/_tracking_service/registry.py", line 39, in get_store
    return self._get_store_with_resolved_uri(resolved_store_uri, artifact_uri)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/_tracking_service/registry.py", line 49, in _get_store_with_resolved_uri
    return builder(store_uri=resolved_store_uri, artifact_uri=artifact_uri)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/tracking/_tracking_service/utils.py", line 150, in _get_sqlalchemy_store
    return SqlAlchemyStore(store_uri, artifact_uri)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/store/tracking/sqlalchemy_store.py", line 134, in __init__
    mlflow.store.db.utils._initialize_tables(self.engine)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/store/db/utils.py", line 82, in _initialize_tables
    _upgrade_db(engine)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/store/db/utils.py", line 206, in _upgrade_db
    command.upgrade(config, "heads")
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/command.py", line 378, in upgrade
    script.run_env()
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/script/base.py", line 576, in run_env
    util.load_python_file(self.dir, "env.py")
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/util/pyfiles.py", line 94, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/util/pyfiles.py", line 110, in load_module_py
    spec.loader.exec_module(module)  # type: ignore
  File "<frozen importlib._bootstrap_external>", line 843, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/haru/Desktop/repositories/mlflow/mlflow/store/db_migrations/env.py", line 86, in <module>
    run_migrations_online()
  File "/home/haru/Desktop/repositories/mlflow/mlflow/store/db_migrations/env.py", line 80, in run_migrations_online
    context.run_migrations()
  File "<string>", line 8, in run_migrations
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/runtime/environment.py", line 867, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/runtime/migration.py", line 624, in run_migrations
    step.migration_fn(**kw)
  File "/home/haru/Desktop/repositories/mlflow/mlflow/store/db_migrations/versions/cfd24bdc0731_update_run_status_constraint_with_killed.py", line 57, in upgrade
    batch_op.alter_column(
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/contextlib.py", line 120, in __exit__
    next(self.gen)
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/operations/base.py", line 383, in batch_alter_table
    impl.flush()
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/operations/batch.py", line 158, in flush
    fn(*arg, **kw)
  File "/home/haru/miniconda3/envs/mlflow-dev-env/lib/python3.8/site-packages/alembic/operations/batch.py", line 667, in add_constraint
    raise ValueError("Constraint must have a name")
ValueError: Constraint must have a name

Versions.

  • OS: Ubuntu 22.04
  • Python: 3.8
  • Alembic: 1.10.0
  • SQLAlchemy: 2.0.0
  • Database: SQLite
  • DBAPI:

Additional context

The migration script works fine in alembic 1.9.4.

Have a nice day!

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 16 (8 by maintainers)

Most upvoted comments

I’m not even creating a constraint and still running into this issue for alembic==1.13.0

        with op.batch_alter_table('employees', schema=None) as batch_op:
            batch_op.add_column(employee_number)
            batch_op.add_column(family_head_name)
            batch_op.add_column(medical_registration_number)
            batch_op.add_column(medical_registration_valid_till_date)

yields

   with op.batch_alter_table('employees', schema=None) as batch_op:
  File "~/.pyenv/versions/3.11.4/lib/python3.11/contextlib.py", line 144, in __exit__
    next(self.gen)
  File "~/Code/venv/lib/python3.11/site-packages/alembic/operations/base.py", line 375, in batch_alter_table
    impl.flush()
  File "~/Code/venv/lib/python3.11/site-packages/alembic/operations/batch.py", line 159, in flush
    fn(*arg, **kw)
  File "~/Code/venv/lib/python3.11/site-packages/alembic/operations/batch.py", line 670, in add_constraint
    raise ValueError("Constraint must have a name")
ValueError: Constraint must have a name

I’m not even creating a constraint and still running into this issue for alembic==1.13.0

    with op.batch_alter_table('channels', schema=None) as batch_op:
        batch_op.create_unique_constraint(None, ['tg_id'])

Can confirm for alembic==1.13.1