Skip to content

Commit cabbd5c

Browse files
authored
Fix: complete lookback support for custom materializations (#5278)
1 parent 9e9159d commit cabbd5c

File tree

3 files changed

+107
-2
lines changed

3 files changed

+107
-2
lines changed

sqlmesh/core/model/meta.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
SCDType2ByTimeKind,
3030
TimeColumn,
3131
ViewKind,
32-
_IncrementalBy,
3332
model_kind_validator,
3433
OnAdditiveChange,
3534
)
@@ -414,7 +413,7 @@ def column_descriptions(self) -> t.Dict[str, str]:
414413
@property
415414
def lookback(self) -> int:
416415
"""The incremental lookback window."""
417-
return (self.kind.lookback if isinstance(self.kind, _IncrementalBy) else 0) or 0
416+
return getattr(self.kind, "lookback", 0) or 0
418417

419418
def lookback_start(self, start: TimeLike) -> TimeLike:
420419
if self.lookback == 0:

tests/core/test_model.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7686,6 +7686,75 @@ class MyTestStrategy(CustomMaterialization):
76867686
)
76877687

76887688

7689+
def test_custom_kind_lookback_property():
7690+
"""Test that CustomKind's lookback property is correctly accessed via ModelMeta.lookback.
7691+
7692+
This test verifies the fix for issue #5268 where CustomKind models were not respecting
7693+
the lookback parameter because the isinstance check for _IncrementalBy failed.
7694+
"""
7695+
7696+
# Test 1: CustomKind with lookback = 3
7697+
class MyTestStrategy(CustomMaterialization):
7698+
pass
7699+
7700+
expressions = d.parse(
7701+
"""
7702+
MODEL (
7703+
name db.custom_table,
7704+
kind CUSTOM (
7705+
materialization 'MyTestStrategy',
7706+
lookback 3
7707+
)
7708+
);
7709+
SELECT a, b FROM upstream
7710+
"""
7711+
)
7712+
7713+
model = load_sql_based_model(expressions)
7714+
assert model.kind.is_custom
7715+
7716+
# Verify that the kind itself has lookback = 3
7717+
kind = t.cast(CustomKind, model.kind)
7718+
assert kind.lookback == 3
7719+
7720+
# The bug: model.lookback should return 3, but with the old implementation
7721+
# using isinstance(self.kind, _IncrementalBy), it would return 0
7722+
assert model.lookback == 3, "CustomKind lookback not accessible via model.lookback property"
7723+
7724+
# Test 2: CustomKind without lookback (should default to 0)
7725+
expressions_no_lookback = d.parse(
7726+
"""
7727+
MODEL (
7728+
name db.custom_table_no_lookback,
7729+
kind CUSTOM (
7730+
materialization 'MyTestStrategy'
7731+
)
7732+
);
7733+
SELECT a, b FROM upstream
7734+
"""
7735+
)
7736+
7737+
model_no_lookback = load_sql_based_model(expressions_no_lookback)
7738+
assert model_no_lookback.lookback == 0
7739+
7740+
# Test 3: Ensure IncrementalByTimeRangeKind still works correctly
7741+
incremental_expressions = d.parse(
7742+
"""
7743+
MODEL (
7744+
name db.incremental_table,
7745+
kind INCREMENTAL_BY_TIME_RANGE (
7746+
time_column ds,
7747+
lookback 5
7748+
)
7749+
);
7750+
SELECT ds, a, b FROM upstream
7751+
"""
7752+
)
7753+
7754+
incremental_model = load_sql_based_model(incremental_expressions)
7755+
assert incremental_model.lookback == 5
7756+
7757+
76897758
def test_time_column_format_in_custom_kind():
76907759
class TimeColumnCustomKind(CustomKind): # type: ignore[no-untyped-def]
76917760
_time_column: TimeColumn

tests/core/test_snapshot.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,43 @@ def test_lookback(make_snapshot):
681681
assert snapshot.missing_intervals("2023-01-28", "2023-01-30", "2023-01-31 04:00:00") == []
682682

683683

684+
def test_lookback_custom_materialization(make_snapshot):
685+
from sqlmesh import CustomMaterialization
686+
687+
class MyTestStrategy(CustomMaterialization):
688+
pass
689+
690+
expressions = parse(
691+
"""
692+
MODEL (
693+
name name,
694+
kind CUSTOM (
695+
materialization 'MyTestStrategy',
696+
lookback 2
697+
),
698+
start '2023-01-01',
699+
cron '0 5 * * *',
700+
);
701+
702+
SELECT ds FROM parent.tbl
703+
"""
704+
)
705+
706+
snapshot = make_snapshot(load_sql_based_model(expressions))
707+
708+
assert snapshot.missing_intervals("2023-01-01", "2023-01-01") == [
709+
(to_timestamp("2023-01-01"), to_timestamp("2023-01-02")),
710+
]
711+
712+
snapshot.add_interval("2023-01-01", "2023-01-04")
713+
assert snapshot.missing_intervals("2023-01-01", "2023-01-04") == []
714+
assert snapshot.missing_intervals("2023-01-01", "2023-01-05") == [
715+
(to_timestamp("2023-01-03"), to_timestamp("2023-01-04")),
716+
(to_timestamp("2023-01-04"), to_timestamp("2023-01-05")),
717+
(to_timestamp("2023-01-05"), to_timestamp("2023-01-06")),
718+
]
719+
720+
684721
def test_seed_intervals(make_snapshot):
685722
snapshot_a = make_snapshot(
686723
SeedModel(

0 commit comments

Comments
 (0)