Skip to content

Commit cd7efd6

Browse files
Fix: Create state sync without version check for destroy
1 parent d2d8c2f commit cd7efd6

2 files changed

Lines changed: 91 additions & 0 deletions

File tree

sqlmesh/core/context.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,6 +2674,10 @@ def _context_diff(
26742674
)
26752675

26762676
def _destroy(self) -> None:
2677+
# Create state_sync directly to avoid potential errors from get_versions() in the property
2678+
if not self._state_sync:
2679+
self._state_sync = self._new_state_sync()
2680+
26772681
# Invalidate all environments, including prod
26782682
for environment in self.state_reader.get_environments():
26792683
self.state_sync.invalidate_environment(name=environment.name, protect_prod=False)

tests/core/test_integration.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
SnapshotInfoLike,
6868
SnapshotTableInfo,
6969
)
70+
from sqlmesh.core.state_sync.base import Versions, SCHEMA_VERSION
7071
from sqlmesh.utils import CorrelationId
7172
from sqlmesh.utils.date import TimeLike, now, to_date, to_datetime, to_timestamp
7273
from sqlmesh.utils.errors import NoChangesPlanError, SQLMeshError, PlanError, ConfigError
@@ -6291,6 +6292,92 @@ def test_destroy(copy_to_temp_path):
62916292
assert not cache_path.exists()
62926293

62936294

6295+
@use_terminal_console
6296+
def test_destroy_with_version_mismatch(copy_to_temp_path):
6297+
paths = copy_to_temp_path("tests/fixtures/multi_virtual_layer")
6298+
path = Path(paths[0])
6299+
first_db_path = str(path / "db_1.db")
6300+
second_db_path = str(path / "db_2.db")
6301+
6302+
config = Config(
6303+
gateways={
6304+
"first": GatewayConfig(
6305+
connection=DuckDBConnectionConfig(database=first_db_path),
6306+
variables={"overriden_var": "gateway_1"},
6307+
),
6308+
"second": GatewayConfig(
6309+
connection=DuckDBConnectionConfig(database=second_db_path),
6310+
variables={"overriden_var": "gateway_2"},
6311+
),
6312+
},
6313+
model_defaults=ModelDefaultsConfig(dialect="duckdb"),
6314+
model_naming=NameInferenceConfig(infer_names=True),
6315+
default_gateway="first",
6316+
gateway_managed_virtual_layer=True,
6317+
variables={"overriden_var": "global", "global_one": 88},
6318+
)
6319+
6320+
context = Context(paths=paths, config=config)
6321+
plan = context.plan_builder().build()
6322+
assert len(plan.new_snapshots) == 4
6323+
context.apply(plan)
6324+
6325+
# Create dev environment with a change
6326+
model = context.get_model("db_1.first_schema.model_one")
6327+
context.upsert_model(model.copy(update={"query": model.query.select("'c' AS extra")}))
6328+
plan = context.plan_builder("dev").build()
6329+
context.apply(plan)
6330+
6331+
# Mock get_versions to raise an error by bumping the version
6332+
def mock_get_versions(validate=True):
6333+
if validate:
6334+
raise SQLMeshError(
6335+
f"SQLMesh (local) is using version '{SCHEMA_VERSION}' which is behind '{SCHEMA_VERSION + 1}' (remote). "
6336+
f"Please upgrade SQLMesh."
6337+
)
6338+
return Versions(
6339+
schema_version=SCHEMA_VERSION + 1,
6340+
sqlglot_version="1.0.0",
6341+
sqlmesh_version="1.0.0",
6342+
)
6343+
6344+
# Clearstate sync and patch to return a state sync with mocked get_versions
6345+
context._state_sync = None
6346+
original_new_state_sync = context._new_state_sync
6347+
6348+
def patched_new_state_sync():
6349+
state_sync = original_new_state_sync()
6350+
state_sync.get_versions = mock_get_versions
6351+
return state_sync
6352+
6353+
with patch.object(context, "_new_state_sync", side_effect=patched_new_state_sync):
6354+
# Ensure accessing state_sync property would fail due to get_versions validation
6355+
with pytest.raises(SQLMeshError):
6356+
_ = context.state_sync
6357+
6358+
context._state_sync = None
6359+
6360+
# This should not raise an error even though there is a version mismatch
6361+
context._destroy()
6362+
6363+
# Verify the state tables have been removed though
6364+
state_tables = {
6365+
"_environments",
6366+
"_snapshots",
6367+
"_intervals",
6368+
"_auto_restatements",
6369+
"_environment_statements",
6370+
"_intervals",
6371+
"_plan_dags",
6372+
"_versions",
6373+
}
6374+
for table_name in state_tables:
6375+
with pytest.raises(
6376+
Exception, match=f"Catalog Error: Table with name {table_name} does not exist!"
6377+
):
6378+
context.fetchdf(f"SELECT * FROM db_1.sqlmesh.{table_name}")
6379+
6380+
62946381
@use_terminal_console
62956382
def test_audits_running_on_metadata_changes(tmp_path: Path):
62966383
def setup_senario(model_before: str, model_after: str):

0 commit comments

Comments
 (0)