@@ -185,6 +185,8 @@ class SnapshotIntervals(PydanticModel):
185185 intervals : Intervals = []
186186 dev_intervals : Intervals = []
187187 pending_restatement_intervals : Intervals = []
188+ last_altered_ts : t .Optional [int ] = None
189+ dev_last_altered_ts : t .Optional [int ] = None
188190
189191 @property
190192 def snapshot_id (self ) -> t .Optional [SnapshotId ]:
@@ -205,6 +207,12 @@ def add_dev_interval(self, start: int, end: int) -> None:
205207 def add_pending_restatement_interval (self , start : int , end : int ) -> None :
206208 self ._add_interval (start , end , "pending_restatement_intervals" )
207209
210+ def update_last_altered_ts (self , last_altered_ts : t .Optional [int ]) -> None :
211+ self ._update_last_altered_ts (last_altered_ts , "last_altered_ts" )
212+
213+ def update_dev_last_altered_ts (self , last_altered_ts : t .Optional [int ]) -> None :
214+ self ._update_last_altered_ts (last_altered_ts , "dev_last_altered_ts" )
215+
208216 def remove_interval (self , start : int , end : int ) -> None :
209217 self ._remove_interval (start , end , "intervals" )
210218
@@ -224,6 +232,13 @@ def _add_interval(self, start: int, end: int, interval_attr: str) -> None:
224232 target_intervals = merge_intervals ([* target_intervals , (start , end )])
225233 setattr (self , interval_attr , target_intervals )
226234
235+ def _update_last_altered_ts (
236+ self , last_altered_ts : t .Optional [int ], last_altered_attr : str
237+ ) -> None :
238+ if last_altered_ts :
239+ existing_last_altered_ts = getattr (self , last_altered_attr )
240+ setattr (self , last_altered_attr , max (existing_last_altered_ts or 0 , last_altered_ts ))
241+
227242 def _remove_interval (self , start : int , end : int , interval_attr : str ) -> None :
228243 target_intervals = getattr (self , interval_attr )
229244 target_intervals = remove_interval (target_intervals , start , end )
@@ -713,6 +728,10 @@ class Snapshot(PydanticModel, SnapshotInfoMixin):
713728 dev_table_suffix : str = "dev"
714729 table_naming_convention : TableNamingConvention = TableNamingConvention .default
715730 forward_only : bool = False
731+ # Physical table last modified timestamp, not to be confused with the "updated_ts" field
732+ # which is for the snapshot record itself
733+ last_altered_ts : t .Optional [int ] = None
734+ dev_last_altered_ts : t .Optional [int ] = None
716735
717736 @field_validator ("ttl" )
718737 @classmethod
@@ -751,6 +770,7 @@ def hydrate_with_intervals_by_version(
751770 )
752771 for interval in snapshot_intervals :
753772 snapshot .merge_intervals (interval )
773+
754774 result .append (snapshot )
755775
756776 return result
@@ -957,12 +977,20 @@ def merge_intervals(self, other: t.Union[Snapshot, SnapshotIntervals]) -> None:
957977 if not apply_effective_from or end <= effective_from_ts :
958978 self .add_interval (start , end )
959979
980+ if other .last_altered_ts :
981+ self .last_altered_ts = max (self .last_altered_ts or 0 , other .last_altered_ts )
982+
960983 if self .dev_version == other .dev_version :
961984 # Merge dev intervals if the dev versions match which would mean
962985 # that this and the other snapshot are pointing to the same dev table.
963986 for start , end in other .dev_intervals :
964987 self .add_interval (start , end , is_dev = True )
965988
989+ if other .dev_last_altered_ts :
990+ self .dev_last_altered_ts = max (
991+ self .dev_last_altered_ts or 0 , other .dev_last_altered_ts
992+ )
993+
966994 self .pending_restatement_intervals = merge_intervals (
967995 [* self .pending_restatement_intervals , * other .pending_restatement_intervals ]
968996 )
@@ -1081,6 +1109,7 @@ def check_ready_intervals(
10811109 python_env = signals .python_env ,
10821110 dialect = self .model .dialect ,
10831111 path = self .model ._path ,
1112+ snapshot = self ,
10841113 kwargs = kwargs ,
10851114 )
10861115 except SQLMeshError as e :
@@ -2421,6 +2450,7 @@ def check_ready_intervals(
24212450 python_env : t .Dict [str , Executable ],
24222451 dialect : DialectType = None ,
24232452 path : t .Optional [Path ] = None ,
2453+ snapshot : t .Optional [Snapshot ] = None ,
24242454 kwargs : t .Optional [t .Dict ] = None ,
24252455) -> Intervals :
24262456 checked_intervals : Intervals = []
@@ -2436,6 +2466,7 @@ def check_ready_intervals(
24362466 provided_args = (batch ,),
24372467 provided_kwargs = (kwargs or {}),
24382468 context = context ,
2469+ snapshot = snapshot ,
24392470 )
24402471 except Exception as ex :
24412472 raise SignalEvalError (format_evaluated_code_exception (ex , python_env ))
0 commit comments