diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 78c7d0a6f597..f0f51c306c8a 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -69,6 +69,7 @@ inspect, Integer, join, + MetaData, not_, Numeric, or_, @@ -122,6 +123,7 @@ TrimmedString, UUIDType, ) +from galaxy.model.database_object_names import NAMING_CONVENTION from galaxy.model.item_attrs import ( get_item_annotation_str, UsesAnnotations, @@ -225,8 +227,9 @@ def get_uuid(uuid: Optional[Union[UUID, str]] = None) -> UUID: class Base(_HasTable, metaclass=DeclarativeMeta): __abstract__ = True + metadata = MetaData(naming_convention=NAMING_CONVENTION) + mapper_registry.metadata = metadata registry = mapper_registry - metadata = mapper_registry.metadata __init__ = mapper_registry.constructor @classmethod diff --git a/lib/galaxy/model/database_object_names.py b/lib/galaxy/model/database_object_names.py new file mode 100644 index 000000000000..802f40c65e12 --- /dev/null +++ b/lib/galaxy/model/database_object_names.py @@ -0,0 +1,44 @@ +""" +Naming convention and helper functions for generating names of database +constraints and indexes. +""" +from typing import ( + List, + Union, +) + +from galaxy.util import listify + +# Naming convention applied to database constraints and indexes. +# All except "ix" are conventions used by PostgreSQL by default. +# We keep the "ix" template consistent with historical Galaxy usage. +NAMING_CONVENTION = { + "pk": "%(table_name)s_pkey", + "fk": "%(table_name)s_%(column_0_name)s_fkey", + "uq": "%(table_name)s_%(column_0_name)s_key", + "ck": "%(table_name)s_%(column_0_name)s_check", + "ix": "ix_%(table_name)s_%(column_0_name)s", +} + + +def build_foreign_key_name(table_name: str, column_names: Union[str, List]) -> str: + columns = _as_str(column_names) + return f"{table_name}_{columns}_fkey" + + +def build_unique_constraint_name(table_name: str, column_names: Union[str, List]) -> str: + columns = _as_str(column_names) + return f"{table_name}_{columns}_key" + + +def build_check_constraint_name(table_name: str, column_name: str) -> str: + return f"{table_name}_{column_name}_check" + + +def build_index_name(table_name: str, column_names: Union[str, List]) -> str: + columns = _as_str(column_names) + return f"ix_{table_name}_{columns}" + + +def _as_str(column_names: Union[str, List]) -> str: + return "_".join(listify(column_names)) diff --git a/lib/galaxy/model/migrations/alembic/env.py b/lib/galaxy/model/migrations/alembic/env.py index 7634448338e7..5c3ec11f8298 100644 --- a/lib/galaxy/model/migrations/alembic/env.py +++ b/lib/galaxy/model/migrations/alembic/env.py @@ -11,6 +11,7 @@ from alembic.script.base import Script from sqlalchemy import create_engine +from galaxy.model import Base from galaxy.model.migrations import ( GXY, ModelId, @@ -18,7 +19,7 @@ ) config = context.config -target_metadata = None # Not implemented: used for autogenerate, which we don't use here. +target_metadata = Base.metadata log = logging.getLogger(__name__) diff --git a/lib/galaxy/model/migrations/alembic/versions_gxy/3a2914d703ca_add_index_wf_r_s_s__workflow_invocation_.py b/lib/galaxy/model/migrations/alembic/versions_gxy/3a2914d703ca_add_index_wf_r_s_s__workflow_invocation_.py index 3ee69a43c746..60805a2b7b5e 100644 --- a/lib/galaxy/model/migrations/alembic/versions_gxy/3a2914d703ca_add_index_wf_r_s_s__workflow_invocation_.py +++ b/lib/galaxy/model/migrations/alembic/versions_gxy/3a2914d703ca_add_index_wf_r_s_s__workflow_invocation_.py @@ -5,6 +5,7 @@ Create Date: 2023-03-07 15:06:55.682273 """ +from galaxy.model.database_object_names import build_index_name from galaxy.model.migrations.util import ( create_index, drop_index, @@ -17,12 +18,12 @@ depends_on = None table_name = "workflow_request_step_states" -columns = ["workflow_invocation_id"] -index_name = "ix_workflow_request_step_states_workflow_invocation_id" +column_name = "workflow_invocation_id" +index_name = build_index_name(table_name, column_name) def upgrade(): - create_index(index_name, table_name, columns) + create_index(index_name, table_name, [column_name]) def downgrade(): diff --git a/lib/galaxy/model/migrations/alembic/versions_gxy/caa7742f7bca_add_index_wf_r_i_p__workflow_invocation_.py b/lib/galaxy/model/migrations/alembic/versions_gxy/caa7742f7bca_add_index_wf_r_i_p__workflow_invocation_.py index 8932fd5fd88f..2081ba2c183c 100644 --- a/lib/galaxy/model/migrations/alembic/versions_gxy/caa7742f7bca_add_index_wf_r_i_p__workflow_invocation_.py +++ b/lib/galaxy/model/migrations/alembic/versions_gxy/caa7742f7bca_add_index_wf_r_i_p__workflow_invocation_.py @@ -5,6 +5,7 @@ Create Date: 2023-03-07 15:10:44.943542 """ +from galaxy.model.database_object_names import build_index_name from galaxy.model.migrations.util import ( create_index, drop_index, @@ -18,12 +19,12 @@ table_name = "workflow_request_input_parameters" -columns = ["workflow_invocation_id"] -index_name = "ix_workflow_request_input_parameters_workflow_invocation_id" +column_name = "workflow_invocation_id" +index_name = build_index_name(table_name, column_name) def upgrade(): - create_index(index_name, table_name, columns) + create_index(index_name, table_name, [column_name]) def downgrade(): diff --git a/lib/galaxy/model/migrations/alembic/versions_gxy/d0583094c8cd_add_quota_source_labels.py b/lib/galaxy/model/migrations/alembic/versions_gxy/d0583094c8cd_add_quota_source_labels.py index 368953517d7f..d7099bd36cdd 100644 --- a/lib/galaxy/model/migrations/alembic/versions_gxy/d0583094c8cd_add_quota_source_labels.py +++ b/lib/galaxy/model/migrations/alembic/versions_gxy/d0583094c8cd_add_quota_source_labels.py @@ -13,6 +13,7 @@ String, ) +from galaxy.model.database_object_names import build_index_name from galaxy.model.migrations.util import ( add_column, create_index, @@ -31,6 +32,10 @@ branch_labels = None depends_on = None +old_index_name = build_index_name("default_quota_association", "type") +new_index_name = build_index_name("quota", "quota_source_label") +unique_constraint_name = "uqsu_unique_label_per_user" # leave unchanged + def upgrade(): with transaction(): @@ -43,17 +48,15 @@ def upgrade(): # user had an index on disk_usage - does that make any sense? -John Column("disk_usage", Numeric(15, 0)), ) - create_unique_constraint( - "uqsu_unique_label_per_user", "user_quota_source_usage", ["user_id", "quota_source_label"] - ) - drop_index("ix_default_quota_association_type", "default_quota_association") - create_index("ix_quota_quota_source_label", "quota", ["quota_source_label"]) + create_unique_constraint(unique_constraint_name, "user_quota_source_usage", ["user_id", "quota_source_label"]) + drop_index(old_index_name, "default_quota_association") + create_index(new_index_name, "quota", ["quota_source_label"]) def downgrade(): with transaction(): - drop_index("ix_quota_quota_source_label", "quota") - create_index("ix_default_quota_association_type", "default_quota_association", ["type"], unique=True) - drop_constraint("uqsu_unique_label_per_user", "user_quota_source_usage") + drop_index(new_index_name, "quota") + create_index(old_index_name, "default_quota_association", ["type"], unique=True) + drop_constraint(unique_constraint_name, "user_quota_source_usage") drop_table("user_quota_source_usage") drop_column("quota", "quota_source_label") diff --git a/lib/galaxy/model/migrations/alembic/versions_gxy/e0e3bb173ee6_add_column_deleted_to_api_keys.py b/lib/galaxy/model/migrations/alembic/versions_gxy/e0e3bb173ee6_add_column_deleted_to_api_keys.py index f7282e94172c..4cba2792fe43 100644 --- a/lib/galaxy/model/migrations/alembic/versions_gxy/e0e3bb173ee6_add_column_deleted_to_api_keys.py +++ b/lib/galaxy/model/migrations/alembic/versions_gxy/e0e3bb173ee6_add_column_deleted_to_api_keys.py @@ -10,6 +10,7 @@ Column, ) +from galaxy.model.database_object_names import build_index_name from galaxy.model.migrations.util import ( add_column, drop_column, @@ -27,6 +28,7 @@ # database object names used in this revision table_name = "api_keys" column_name = "deleted" +index_name = build_index_name(table_name, column_name) def upgrade(): @@ -35,5 +37,5 @@ def upgrade(): def downgrade(): with transaction(): - drop_index("ix_api_keys_deleted", table_name) + drop_index(index_name, table_name) drop_column(table_name, column_name) diff --git a/test/unit/data/model/test_database_object_names.py b/test/unit/data/model/test_database_object_names.py new file mode 100644 index 000000000000..e0ff6788264e --- /dev/null +++ b/test/unit/data/model/test_database_object_names.py @@ -0,0 +1,37 @@ +from galaxy.model.database_object_names import ( + build_check_constraint_name, + build_foreign_key_name, + build_index_name, + build_unique_constraint_name, +) + + +def test_foreign_key_single_column(): + assert build_foreign_key_name("foo", "bar_id") == "foo_bar_id_fkey" + + +def test_foreign_key_composite(): + assert build_foreign_key_name("foo", ["bar_id", "buz_id"]) == "foo_bar_id_buz_id_fkey" + assert build_foreign_key_name("foo", ["bar_id", "buz_id", "bam_id"]) == "foo_bar_id_buz_id_bam_id_fkey" + + +def test_unique_constraint_single_column(): + assert build_unique_constraint_name("foo", "bar") == "foo_bar_key" + + +def test_unique_constraint_composite(): + assert build_unique_constraint_name("foo", ["bar", "buz"]) == "foo_bar_buz_key" + assert build_unique_constraint_name("foo", ["bar", "buz", "bam"]) == "foo_bar_buz_bam_key" + + +def test_check_constraint(): + assert build_check_constraint_name("foo", "bar") == "foo_bar_check" + + +def test_index_single_column(): + assert build_index_name("foo", "bar") == "ix_foo_bar" + + +def test_index_composite(): + assert build_index_name("foo", ["bar", "buz"]) == "ix_foo_bar_buz" + assert build_index_name("foo", ["bar", "buz", "bam"]) == "ix_foo_bar_buz_bam"