Skip to content

Commit 0b819d2

Browse files
authored
Fix: Cleanup of materialized view snapshots (#5213)
1 parent 9f0ba65 commit 0b819d2

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

sqlmesh/core/snapshot/evaluator.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2378,14 +2378,19 @@ def migrate(
23782378
def delete(self, name: str, **kwargs: t.Any) -> None:
23792379
cascade = kwargs.pop("cascade", False)
23802380
try:
2381-
self.adapter.drop_view(name, cascade=cascade)
2381+
# Some engines (e.g., RisingWave) don’t fail when dropping a materialized view with a DROP VIEW statement,
2382+
# because views and materialized views don’t share the same namespace. Therefore, we should not ignore if the
2383+
# view doesn't exist and let the exception handler attempt to drop the materialized view.
2384+
self.adapter.drop_view(name, cascade=cascade, ignore_if_not_exists=False)
23822385
except Exception:
23832386
logger.debug(
23842387
"Failed to drop view '%s'. Trying to drop the materialized view instead",
23852388
name,
23862389
exc_info=True,
23872390
)
2388-
self.adapter.drop_view(name, materialized=True, cascade=cascade)
2391+
self.adapter.drop_view(
2392+
name, materialized=True, cascade=cascade, ignore_if_not_exists=True
2393+
)
23892394
logger.info("Dropped view '%s'", name)
23902395

23912396
def _is_materialized_view(self, model: Model) -> bool:

tests/core/test_snapshot_evaluator.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,64 @@ def create_and_cleanup(name: str, dev_table_only: bool):
470470
)
471471

472472

473+
def test_cleanup_view(adapter_mock, make_snapshot):
474+
evaluator = SnapshotEvaluator(adapter_mock)
475+
476+
model = SqlModel(
477+
name="catalog.test_schema.test_model",
478+
kind=ViewKind(materialized=False),
479+
query=parse_one("SELECT a FROM tbl"),
480+
)
481+
482+
snapshot = make_snapshot(model)
483+
snapshot.categorize_as(SnapshotChangeCategory.BREAKING, forward_only=True)
484+
485+
evaluator.promote([snapshot], EnvironmentNamingInfo(name="test_env"))
486+
evaluator.cleanup([SnapshotTableCleanupTask(snapshot=snapshot.table_info, dev_table_only=True)])
487+
488+
adapter_mock.get_data_object.assert_not_called()
489+
adapter_mock.drop_view.assert_called_once_with(
490+
f"catalog.sqlmesh__test_schema.test_schema__test_model__{snapshot.fingerprint.to_version()}__dev",
491+
cascade=True,
492+
ignore_if_not_exists=False,
493+
)
494+
495+
496+
def test_cleanup_materialized_view(adapter_mock, make_snapshot):
497+
evaluator = SnapshotEvaluator(adapter_mock)
498+
499+
model = SqlModel(
500+
name="catalog.test_schema.test_model",
501+
kind=ViewKind(materialized=True),
502+
query=parse_one("SELECT a FROM tbl"),
503+
)
504+
505+
snapshot = make_snapshot(model)
506+
snapshot.categorize_as(SnapshotChangeCategory.BREAKING, forward_only=True)
507+
508+
adapter_mock.drop_view.side_effect = [RuntimeError("failed to drop view"), None]
509+
510+
evaluator.promote([snapshot], EnvironmentNamingInfo(name="test_env"))
511+
evaluator.cleanup([SnapshotTableCleanupTask(snapshot=snapshot.table_info, dev_table_only=True)])
512+
513+
adapter_mock.get_data_object.assert_not_called()
514+
adapter_mock.drop_view.assert_has_calls(
515+
[
516+
call(
517+
f"catalog.sqlmesh__test_schema.test_schema__test_model__{snapshot.fingerprint.to_version()}__dev",
518+
cascade=True,
519+
ignore_if_not_exists=False,
520+
),
521+
call(
522+
f"catalog.sqlmesh__test_schema.test_schema__test_model__{snapshot.fingerprint.to_version()}__dev",
523+
materialized=True,
524+
cascade=True,
525+
ignore_if_not_exists=True,
526+
),
527+
]
528+
)
529+
530+
473531
def test_cleanup_fails(adapter_mock, make_snapshot):
474532
adapter_mock.drop_table.side_effect = RuntimeError("test_error")
475533

0 commit comments

Comments
 (0)