|
42 | 42 | yesterday_ds, |
43 | 43 | to_timestamp, |
44 | 44 | time_like_to_str, |
| 45 | + is_relative, |
45 | 46 | ) |
46 | 47 | from sqlmesh.utils.errors import NoChangesPlanError, PlanError |
47 | 48 |
|
@@ -139,7 +140,14 @@ def __init__( |
139 | 140 | self._include_unmodified = include_unmodified |
140 | 141 | self._restate_models = set(restate_models) if restate_models is not None else None |
141 | 142 | self._effective_from = effective_from |
| 143 | + |
| 144 | + # note: this deliberately doesnt default to now() here. |
| 145 | + # There may be an significant delay between the PlanBuilder producing a Plan and the Plan actually being run |
| 146 | + # so if execution_time=None is passed to the PlanBuilder, then the resulting Plan should also have execution_time=None |
| 147 | + # in order to prevent the Plan that was intended to run "as at now" from having "now" fixed to some time in the past |
| 148 | + # ref: https://github.com/TobikoData/sqlmesh/pull/4702#discussion_r2140696156 |
142 | 149 | self._execution_time = execution_time |
| 150 | + |
143 | 151 | self._backfill_models = backfill_models |
144 | 152 | self._end = end or default_end |
145 | 153 | self._apply = apply |
@@ -176,20 +184,20 @@ def is_start_and_end_allowed(self) -> bool: |
176 | 184 |
|
177 | 185 | @property |
178 | 186 | def start(self) -> t.Optional[TimeLike]: |
179 | | - if self._start and self._execution_time: |
180 | | - return to_datetime(self._start, relative_base=to_datetime(self._execution_time)) |
| 187 | + if self._start and is_relative(self._start): |
| 188 | + # only do this for relative expressions otherwise inclusive date strings like '2020-01-01' can be turned into exclusive timestamps eg '2020-01-01 00:00:00' |
| 189 | + return to_datetime( |
| 190 | + self._start, relative_base=to_datetime(self._execution_time or now()) |
| 191 | + ) |
181 | 192 | return self._start |
182 | 193 |
|
183 | 194 | @property |
184 | 195 | def end(self) -> t.Optional[TimeLike]: |
185 | | - if self._end and self._execution_time: |
186 | | - return to_datetime(self._end, relative_base=to_datetime(self._execution_time)) |
| 196 | + if self._end and is_relative(self._end): |
| 197 | + # only do this for relative expressions otherwise inclusive date strings like '2020-01-01' can be turned into exclusive timestamps eg '2020-01-01 00:00:00' |
| 198 | + return to_datetime(self._end, relative_base=to_datetime(self._execution_time or now())) |
187 | 199 | return self._end |
188 | 200 |
|
189 | | - @property |
190 | | - def execution_time(self) -> TimeLike: |
191 | | - return self._execution_time or now() |
192 | | - |
193 | 201 | def set_start(self, new_start: TimeLike) -> PlanBuilder: |
194 | 202 | self._start = new_start |
195 | 203 | self.override_start = True |
@@ -274,7 +282,10 @@ def build(self) -> Plan: |
274 | 282 | ) |
275 | 283 |
|
276 | 284 | restatements = self._build_restatements( |
277 | | - dag, earliest_interval_start(self._context_diff.snapshots.values(), self.execution_time) |
| 285 | + dag, |
| 286 | + earliest_interval_start( |
| 287 | + self._context_diff.snapshots.values(), self._execution_time or now() |
| 288 | + ), |
278 | 289 | ) |
279 | 290 | models_to_backfill = self._build_models_to_backfill(dag, restatements) |
280 | 291 |
|
@@ -307,7 +318,7 @@ def build(self) -> Plan: |
307 | 318 | selected_models_to_backfill=self._backfill_models, |
308 | 319 | models_to_backfill=models_to_backfill, |
309 | 320 | effective_from=self._effective_from, |
310 | | - execution_time=self.execution_time, |
| 321 | + execution_time=self._execution_time, |
311 | 322 | end_bounded=self._end_bounded, |
312 | 323 | ensure_finalized_snapshots=self._ensure_finalized_snapshots, |
313 | 324 | user_provided_flags=self._user_provided_flags, |
@@ -764,9 +775,10 @@ def _ensure_valid_date_range(self) -> None: |
764 | 775 | ) |
765 | 776 |
|
766 | 777 | if end := self.end: |
767 | | - if to_datetime(end) > to_datetime(self.execution_time): |
| 778 | + execution_time = self._execution_time or now() |
| 779 | + if to_datetime(end) > to_datetime(execution_time): |
768 | 780 | raise PlanError( |
769 | | - f"Plan end date: '{time_like_to_str(end)}' cannot be in the future (execution time: '{time_like_to_str(self.execution_time)}')" |
| 781 | + f"Plan end date: '{time_like_to_str(end)}' cannot be in the future (execution time: '{time_like_to_str(execution_time)}')" |
770 | 782 | ) |
771 | 783 |
|
772 | 784 | def _ensure_no_forward_only_revert(self) -> None: |
|
0 commit comments