2929from sqlmesh .core .snapshot .definition import check_ready_intervals
3030from sqlmesh .core .snapshot .definition import (
3131 Interval ,
32+ SnapshotEvaluationTriggers ,
3233 expand_range ,
3334 parent_snapshots_by_name ,
3435)
@@ -223,6 +224,7 @@ def run(
223224 ignore_cron : bool = False ,
224225 end_bounded : bool = False ,
225226 selected_snapshots : t .Optional [t .Set [str ]] = None ,
227+ selected_snapshots_auto_upstream : t .Optional [t .Set [str ]] = None ,
226228 circuit_breaker : t .Optional [t .Callable [[], bool ]] = None ,
227229 deployability_index : t .Optional [DeployabilityIndex ] = None ,
228230 auto_restatement_enabled : bool = False ,
@@ -239,6 +241,7 @@ def run(
239241 ignore_cron = ignore_cron ,
240242 end_bounded = end_bounded ,
241243 selected_snapshots = selected_snapshots ,
244+ selected_snapshots_auto_upstream = selected_snapshots_auto_upstream ,
242245 circuit_breaker = circuit_breaker ,
243246 deployability_index = deployability_index ,
244247 auto_restatement_enabled = auto_restatement_enabled ,
@@ -374,7 +377,7 @@ def run_merged_intervals(
374377 run_environment_statements : bool = False ,
375378 audit_only : bool = False ,
376379 restatements : t .Optional [t .Dict [SnapshotId , Interval ]] = None ,
377- auto_restatement_triggers : t .Dict [SnapshotId , t . List [ SnapshotId ] ] = {},
380+ snapshot_evaluation_triggers : t .Dict [SnapshotId , SnapshotEvaluationTriggers ] = {},
378381 ) -> t .Tuple [t .List [NodeExecutionFailedError [SchedulingUnit ]], t .List [SchedulingUnit ]]:
379382 """Runs precomputed batches of missing intervals.
380383
@@ -477,7 +480,9 @@ def evaluate_node(node: SchedulingUnit) -> None:
477480 evaluation_duration_ms ,
478481 num_audits - num_audits_failed ,
479482 num_audits_failed ,
480- auto_restatement_triggers = auto_restatement_triggers .get (snapshot .snapshot_id ),
483+ snapshot_evaluation_triggers = snapshot_evaluation_triggers .get (
484+ snapshot .snapshot_id
485+ ),
481486 )
482487
483488 try :
@@ -588,6 +593,7 @@ def _run_or_audit(
588593 ignore_cron : bool = False ,
589594 end_bounded : bool = False ,
590595 selected_snapshots : t .Optional [t .Set [str ]] = None ,
596+ selected_snapshots_auto_upstream : t .Optional [t .Set [str ]] = None ,
591597 circuit_breaker : t .Optional [t .Callable [[], bool ]] = None ,
592598 deployability_index : t .Optional [DeployabilityIndex ] = None ,
593599 auto_restatement_enabled : bool = False ,
@@ -611,6 +617,7 @@ def _run_or_audit(
611617 end_bounded: If set to true, the evaluated intervals will be bounded by the target end date, disregarding lookback,
612618 allow_partials, and other attributes that could cause the intervals to exceed the target end date.
613619 selected_snapshots: A set of snapshot names to run. If not provided, all snapshots will be run.
620+ selected_snapshots_auto_upstream: The set of selected_snapshots that were automatically added because they're upstream of a selected snapshot.
614621 circuit_breaker: An optional handler which checks if the run should be aborted.
615622 deployability_index: Determines snapshots that are deployable in the context of this render.
616623 auto_restatement_enabled: Whether to enable auto restatements.
@@ -666,6 +673,42 @@ def _run_or_audit(
666673 if not merged_intervals :
667674 return CompletionStatus .NOTHING_TO_DO
668675
676+ merged_intervals_snapshots = {
677+ snapshot .snapshot_id : snapshot for snapshot in merged_intervals .keys ()
678+ }
679+ select_snapshot_triggers : t .Dict [SnapshotId , t .List [SnapshotId ]] = {}
680+ if selected_snapshots and selected_snapshots_auto_upstream :
681+ # actually selected snapshots are their own triggers
682+ selected_snapshots_no_auto_upstream = (
683+ selected_snapshots - selected_snapshots_auto_upstream
684+ )
685+ select_snapshot_triggers = {
686+ s_id : [s_id ]
687+ for s_id in [
688+ snapshot_id
689+ for snapshot_id in merged_intervals_snapshots
690+ if snapshot_id .name in selected_snapshots_no_auto_upstream
691+ ]
692+ }
693+
694+ # trace upstream by reversing dag of all snapshots to evaluate
695+ reversed_intervals_dag = snapshots_to_dag (merged_intervals_snapshots .values ()).reversed
696+ for s_id in reversed_intervals_dag :
697+ if s_id not in select_snapshot_triggers :
698+ triggers = []
699+ for parent_s_id in merged_intervals_snapshots [s_id ].parents :
700+ triggers .extend (select_snapshot_triggers [parent_s_id ])
701+ select_snapshot_triggers [s_id ] = list (dict .fromkeys (triggers ))
702+
703+ all_snapshot_triggers : t .Dict [SnapshotId , SnapshotEvaluationTriggers ] = {
704+ s_id : SnapshotEvaluationTriggers (
705+ ignore_cron = ignore_cron ,
706+ auto_restatement_triggers = auto_restatement_triggers .get (s_id , []),
707+ select_snapshot_triggers = select_snapshot_triggers .get (s_id , []),
708+ )
709+ for s_id in merged_intervals_snapshots
710+ if ignore_cron or s_id in auto_restatement_triggers or s_id in select_snapshot_triggers
711+ }
669712 errors , _ = self .run_merged_intervals (
670713 merged_intervals = merged_intervals ,
671714 deployability_index = deployability_index ,
@@ -677,7 +720,7 @@ def _run_or_audit(
677720 run_environment_statements = run_environment_statements ,
678721 audit_only = audit_only ,
679722 restatements = remove_intervals ,
680- auto_restatement_triggers = auto_restatement_triggers ,
723+ snapshot_evaluation_triggers = all_snapshot_triggers ,
681724 )
682725
683726 return CompletionStatus .FAILURE if errors else CompletionStatus .SUCCESS
0 commit comments