Skip to content

Commit b68f7ce

Browse files
committed
Scope audit migration and reject ephemeral catalog URLs
1 parent b45a4d0 commit b68f7ce

1 file changed

Lines changed: 37 additions & 8 deletions

File tree

‎src/openstatspec/sql/wide.py‎

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from sqlalchemy import delete, BigInteger, Boolean, Column, DateTime, Float, Integer, MetaData, String, Table, Text, create_engine, insert, inspect, select, text, update
1616
from sqlalchemy.dialects import mysql
17+
from sqlalchemy.engine import make_url
1718
from ..core import UnsupportedOperationError, safe_error_identity as _safe_error_identity
1819
from .capabilities import (
1920
active_connection, dolt_operational_write_enabled, effective_profile,
@@ -1226,9 +1227,11 @@ def _catalog_missing_columns(
12261227

12271228
def _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

14221430
def _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

14391460
def _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

Comments
 (0)