diff --git a/migrations/dimensions-config/1fae088c80b6.py b/migrations/dimensions-config/1fae088c80b6.py index 42341c3..e3b80b6 100644 --- a/migrations/dimensions-config/1fae088c80b6.py +++ b/migrations/dimensions-config/1fae088c80b6.py @@ -17,6 +17,7 @@ from alembic import op from lsst.daf.butler import Timespan from lsst.daf.butler_migrate.butler_attributes import ButlerAttributes +from lsst.daf.butler_migrate.naming import make_string_length_constraint from lsst.daf.butler_migrate.timespan import create_timespan_column_definitions, format_timespan_value from lsst.utils import doImportType @@ -98,7 +99,7 @@ def _migrate_groups(ctx: _Context) -> None: _LOG.info("Creating group table") check_constraints = [] if ctx.is_sqlite: - check_constraints = [_make_string_check_constraint("instrument", 32, "group_len_instrument")] + check_constraints = [make_string_length_constraint("instrument", 32, "group_len_instrument")] table = op.create_table( "group", sa.Column("instrument", sa.String(32), primary_key=True), diff --git a/python/lsst/daf/butler_migrate/naming.py b/python/lsst/daf/butler_migrate/naming.py index 3bf7eea..c7d9d79 100644 --- a/python/lsst/daf/butler_migrate/naming.py +++ b/python/lsst/daf/butler_migrate/naming.py @@ -32,13 +32,13 @@ from typing import TYPE_CHECKING +import sqlalchemy + from .shrink import shrinkDatabaseEntityName if TYPE_CHECKING: from collections.abc import Iterable - import sqlalchemy - def primary_key_name(table: str, bind: sqlalchemy.engine.Connection) -> str: """Return name of a primary key constraint for a table. @@ -180,3 +180,31 @@ def is_foreign_key_index(table: str, index_name: str) -> bool: def is_regular_index(table: str, index_name: str) -> bool: return index_name.startswith(f"{table}_idx_") + + +def make_string_length_constraint( + column_name: str, max_length: int, constraint_name: str +) -> sqlalchemy.schema.CheckConstraint: + """Create a check constraint that guarantees a string column has a length + that is non-zero and less than a specified maximum. + + These constraints are used by Butler in sqlite databases to emulate + VARCHARs with a specific length. + + Parameters + ---------- + column_name : `str` + The name of the column to create the constraint on. + max_length : `int` + The maximum length allowed for strings stored in this column. + constraint_name : `str` + An arbitrary identifier for the constraint. + + Returns + ------- + check_constraint : `sqlalchemy.schema.CheckConstraint` + The generated check constraint. + """ + return sqlalchemy.schema.CheckConstraint( + f'length("{column_name}")<={max_length} AND length("{column_name}")>=1', name=constraint_name + )