1414
1515from sqlalchemy import delete , BigInteger , Boolean , Column , DateTime , Float , Integer , MetaData , String , Table , Text , create_engine , insert , inspect , select , text , update
1616from sqlalchemy .dialects import mysql
17+ from sqlalchemy .engine import make_url
1718from ..core import UnsupportedOperationError , safe_error_identity as _safe_error_identity
1819from .capabilities import (
1920 active_connection , dolt_operational_write_enabled , effective_profile ,
@@ -1226,9 +1227,11 @@ def _catalog_missing_columns(
12261227
12271228def _catalog_missing_columns_are_migratable (
12281229 missing : Mapping [str , set [str ]],
1230+ * , allowed : Mapping [str , set [str ]] | None = None ,
12291231) -> bool :
1232+ allowed_columns = _MIGRATABLE_CATALOG_COLUMNS if allowed is None else allowed
12301233 return bool (missing ) and all (
1231- columns <= _MIGRATABLE_CATALOG_COLUMNS .get (table_name , set ())
1234+ columns <= allowed_columns .get (table_name , set ())
12321235 for table_name , columns in missing .items ()
12331236 )
12341237
@@ -1353,7 +1356,10 @@ def _catalog_dataset_bijection_state(
13531356 return "valid" if set (legacy_rows ) == set (normative_rows ) else "unverified"
13541357
13551358
1356- def _catalog_state (connection : Any , normative : Any , legacy : Iterable [Table ]) -> str :
1359+ def _catalog_state (
1360+ connection : Any , normative : Any , legacy : Iterable [Table ],
1361+ * , allowed_migrations : Mapping [str , set [str ]] | None = None ,
1362+ ) -> str :
13571363 inspector = inspect (connection )
13581364 existing_tables = set (inspector .get_table_names ())
13591365 existing_views = set (inspector .get_view_names ())
@@ -1408,7 +1414,9 @@ def _catalog_state(connection: Any, normative: Any, legacy: Iterable[Table]) ->
14081414 if missing_columns :
14091415 return (
14101416 "migration_required"
1411- if _catalog_missing_columns_are_migratable (missing_columns )
1417+ if _catalog_missing_columns_are_migratable (
1418+ missing_columns , allowed = allowed_migrations ,
1419+ )
14121420 else "unverified"
14131421 )
14141422 mapping_state = _catalog_dataset_bijection_state (
@@ -1421,19 +1429,32 @@ def _catalog_state(connection: Any, normative: Any, legacy: Iterable[Table]) ->
14211429
14221430def _require_verified_catalog (
14231431 connection : Any , normative : Any , legacy : Iterable [Table ],
1432+ * , allowed_migrations : Mapping [str , set [str ]] | None = None ,
14241433) -> None :
1425- state = _catalog_state (connection , normative , legacy )
1426- if state != "verified" :
1434+ state = _catalog_state (
1435+ connection , normative , legacy , allowed_migrations = allowed_migrations ,
1436+ )
1437+ accepted_states = (
1438+ {"verified" , "migration_required" }
1439+ if allowed_migrations is not None
1440+ else {"verified" }
1441+ )
1442+ if state not in accepted_states :
14271443 raise UnsupportedOperationError (
14281444 f"The selected OpenStatSpec catalog is { state } ; run explicit catalog initialization first."
14291445 )
14301446
14311447
1432- def require_verified_catalog (connection : Any ) -> None :
1433- """Require full OpenStatSpec catalog ownership, shape, and dataset bijection."""
1448+ def require_verified_catalog (
1449+ connection : Any ,
1450+ * , allowed_migrations : Mapping [str , set [str ]] | None = None ,
1451+ ) -> None :
1452+ """Require catalog ownership, shape, bijection, and only explicit migrations."""
14341453 metadata = MetaData ()
14351454 legacy , normative = _catalog_layout (metadata )
1436- _require_verified_catalog (connection , normative , legacy )
1455+ _require_verified_catalog (
1456+ connection , normative , legacy , allowed_migrations = allowed_migrations ,
1457+ )
14371458
14381459
14391460def _catalog_snapshot (
@@ -1554,6 +1575,14 @@ def initialize_wide_catalog(
15541575) -> dict [str , Any ]:
15551576 """Install or explicitly migrate a dedicated catalog after server preflight."""
15561577 validate_connection_url (database_url )
1578+ parsed_url = make_url (database_url )
1579+ if (
1580+ parsed_url .get_backend_name () == "sqlite"
1581+ and parsed_url .database in {None , "" , ":memory:" }
1582+ ):
1583+ raise UnsupportedOperationError (
1584+ "Catalog initialization requires a persistent SQLite database URL."
1585+ )
15571586 profile , active = effective_profile (
15581587 database_url , dolt_conformance_source = dolt_conformance_source ,
15591588 )
0 commit comments