Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions sqlmesh/core/plan/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def __init__(

self._backfill_models = backfill_models
self._end = end or default_end
self._default_start = default_start
self._apply = apply
self._console = console or get_console()
self._choices: t.Dict[SnapshotId, SnapshotChangeCategory] = {}
Expand Down Expand Up @@ -802,6 +803,25 @@ def _ensure_valid_date_range(self) -> None:
f"Plan end date: '{time_like_to_str(end)}' cannot be in the future (execution time: '{time_like_to_str(self.execution_time)}')"
)

# Validate model-specific start/end dates
if (start := self.start or self._default_start) and (end := self.end):
start_ts = to_datetime(start)
end_ts = to_datetime(end)
if start_ts > end_ts:
models_to_check: t.Set[str] = (
set(self._backfill_models or [])
| set(self._context_diff.modified_snapshots.keys())
| {s.name for s in self._context_diff.added}
| set((self._end_override_per_model or {}).keys())
)
for model_name in models_to_check:
if snapshot := self._model_fqn_to_snapshot.get(model_name):
if snapshot.node.start is None or to_datetime(snapshot.node.start) > end_ts:
raise PlanError(
f"Model '{model_name}': Start date / time '({time_like_to_str(start_ts)})' can't be greater than end date / time '({time_like_to_str(end_ts)})'.\n"
f"Set the `start` attribute in your project config model defaults to avoid this issue."
)

def _ensure_no_broken_references(self) -> None:
for snapshot in self._context_diff.snapshots.values():
broken_references = {
Expand Down
56 changes: 56 additions & 0 deletions tests/core/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3007,3 +3007,59 @@ def test_uppercase_gateway_external_models(tmp_path):
assert len(uppercase_in_yaml_models) == 1, (
f"External model with uppercase gateway in YAML should be found. Found {len(uppercase_in_yaml_models)} models"
)


def test_plan_no_start_configured():
context = Context(config=Config())
context.upsert_model(
load_sql_based_model(
parse(
"""
MODEL(
name db.xvg,
kind INCREMENTAL_BY_TIME_RANGE (
time_column ds
),
cron '@daily'
);

SELECT id, ds FROM (VALUES
('1', '2020-01-01'),
) data(id, ds)
WHERE ds BETWEEN @start_ds AND @end_ds
"""
)
)
)

prod_plan = context.plan(auto_apply=True)
assert len(prod_plan.new_snapshots) == 1

context.upsert_model(
load_sql_based_model(
parse(
"""
MODEL(
name db.xvg,
kind INCREMENTAL_BY_TIME_RANGE (
time_column ds
),
cron '@daily',
physical_properties ('some_prop' = 1),
);

SELECT id, ds FROM (VALUES
('1', '2020-01-01'),
) data(id, ds)
WHERE ds BETWEEN @start_ds AND @end_ds
"""
)
)
)

# This should raise an error because the model has no start configured and the end time is less than the start time which will be calculated from the intervals
with pytest.raises(
PlanError,
match=r"Model '.*xvg.*': Start date / time .* can't be greater than end date / time .*\.\nSet the `start` attribute in your project config model defaults to avoid this issue",
):
context.plan("dev", execution_time="1999-01-05")
Loading