Skip to content

Commit 3f46ccb

Browse files
authored
Fix: Correctly handle missing intervals for forward-only snapshtos when effective_from is set (#1160)
1 parent 966d85b commit 3f46ccb

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

sqlmesh/core/plan/definition.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,14 @@ def _missing_intervals(self) -> t.Dict[t.Tuple[str, str], Intervals]:
407407
for snapshot in self.snapshots
408408
}
409409

410-
for _, old in self.context_diff.modified_snapshots.values():
411-
snapshots[(old.name, old.version_get_or_generate())] = old
410+
for new, old in self.context_diff.modified_snapshots.values():
411+
# Never override forward-only snapshots to preserve the effect
412+
# of the effective_from setting. Instead re-merge the intervals.
413+
if not new.is_forward_only:
414+
snapshots[(old.name, old.version_get_or_generate())] = old
415+
else:
416+
new.intervals = []
417+
new.merge_intervals(old)
412418

413419
self.__missing_intervals = {
414420
(snapshot.name, snapshot.version_get_or_generate()): missing

tests/core/test_plan.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -436,24 +436,30 @@ def test_broken_references(make_snapshot, mocker: MockerFixture):
436436

437437
def test_effective_from(make_snapshot, mocker: MockerFixture):
438438
snapshot = make_snapshot(SqlModel(name="a", query=parse_one("select 1, ds FROM a")))
439-
snapshot.categorize_as(SnapshotChangeCategory.FORWARD_ONLY)
439+
snapshot.categorize_as(SnapshotChangeCategory.BREAKING)
440+
snapshot.add_interval("2023-01-01", "2023-03-01")
441+
442+
updated_snapshot = make_snapshot(SqlModel(name="a", query=parse_one("select 2, ds FROM a")))
440443

441444
context_diff_mock = mocker.Mock()
442-
context_diff_mock.snapshots = {"a": snapshot}
445+
context_diff_mock.snapshots = {"a": updated_snapshot}
443446
context_diff_mock.added = set()
444447
context_diff_mock.removed = set()
445-
context_diff_mock.modified_snapshots = {}
446-
context_diff_mock.new_snapshots = {snapshot.snapshot_id: snapshot}
448+
context_diff_mock.modified_snapshots = {"a": (updated_snapshot, snapshot)}
449+
context_diff_mock.new_snapshots = {updated_snapshot.snapshot_id: updated_snapshot}
447450
context_diff_mock.added_materialized_models = set()
448451

449452
with pytest.raises(
450453
PlanError,
451454
match="Effective date can only be set for a forward-only plan.",
452455
):
453456
plan = Plan(context_diff_mock)
454-
plan.effective_from = "2023-01-01"
457+
plan.effective_from = "2023-02-01"
455458

456-
plan = Plan(context_diff_mock, forward_only=True)
459+
plan = Plan(
460+
context_diff_mock, forward_only=True, start="2023-01-01", end="2023-03-01", is_dev=True
461+
)
462+
updated_snapshot.add_interval("2023-01-01", "2023-03-01")
457463

458464
with pytest.raises(
459465
PlanError,
@@ -462,15 +468,21 @@ def test_effective_from(make_snapshot, mocker: MockerFixture):
462468
plan.effective_from = now() + timedelta(days=1)
463469

464470
assert plan.effective_from is None
465-
assert snapshot.effective_from is None
471+
assert updated_snapshot.effective_from is None
472+
assert not plan.missing_intervals
466473

467-
plan.effective_from = "2023-01-01"
468-
assert plan.effective_from == "2023-01-01"
469-
assert snapshot.effective_from == "2023-01-01"
474+
plan.effective_from = "2023-02-01"
475+
assert plan.effective_from == "2023-02-01"
476+
assert updated_snapshot.effective_from == "2023-02-01"
477+
478+
assert len(plan.missing_intervals) == 1
479+
missing_intervals = plan.missing_intervals[0]
480+
assert missing_intervals.intervals[0][0] == to_timestamp("2023-02-01")
481+
assert missing_intervals.intervals[-1][-1] == to_timestamp("2023-03-02")
470482

471483
plan.effective_from = None
472484
assert plan.effective_from is None
473-
assert snapshot.effective_from is None
485+
assert updated_snapshot.effective_from is None
474486

475487

476488
def test_new_environment_no_changes(make_snapshot, mocker: MockerFixture):

0 commit comments

Comments
 (0)