@@ -679,10 +679,11 @@ def _classify_filters_for_pushdown(
679679 Tuple of (pushdown_filters_by_model, main_query_filters, window_dim_filters_by_model)
680680 - pushdown_filters_by_model: Dict mapping model name to list of filters for that model
681681 - main_query_filters: Filters that reference multiple models or metrics (can't push down)
682- - window_dim_filters_by_model: Dict mapping model name to filters on window dimensions.
683- These can't be pushed into CTE WHERE (window not yet evaluated) but reference a
684- single model. In the standard path they belong in the outer query; in the preagg
685- path they are pushed into each model's sub-query (which has its own outer WHERE).
682+ - window_dim_filters_by_model: Dict mapping model name to filters that reference
683+ a window dimension on that model. These can't be pushed into CTE WHERE (window
684+ not yet evaluated). In the standard path they belong in the outer query; in the
685+ preagg path they are pushed into each model's sub-query (which has its own outer
686+ WHERE). Multi-model filters are keyed by the model that owns the window dim.
686687 """
687688 pushdown_filters = {model : [] for model in all_models }
688689 main_query_filters = []
@@ -711,7 +712,7 @@ def _classify_filters_for_pushdown(
711712 # Find all table references in the filter
712713 referenced_models = set ()
713714 references_metric = False
714- references_window_dim = False
715+ window_dim_models : set [ str ] = set ()
715716
716717 for column in parsed .find_all (exp .Column ):
717718 table_name = column .table
@@ -732,22 +733,23 @@ def _classify_filters_for_pushdown(
732733 if model :
733734 dim = model .get_dimension (column_name )
734735 if dim and dim .window is not None :
735- references_window_dim = True
736+ window_dim_models . add ( clean_name )
736737
737738 # Filters that reference metrics must stay in main query (can't push down)
738739 # because metrics don't exist in CTEs (only _raw columns)
739740 if references_metric :
740741 main_query_filters .append (filter_expr )
741- # Single-model window-dim filters: kept separate so callers can
742- # handle them appropriately. In the standard path they go to the
743- # outer WHERE; in the preagg path they are pushed into each model's
744- # sub-query (which has its own outer WHERE after window evaluation).
745- elif references_window_dim and len (referenced_models ) == 1 :
746- model_name = list (referenced_models )[0 ]
747- window_dim_filters [model_name ].append (filter_expr )
748- elif references_window_dim :
749- # Window-dim filter spanning multiple models: outer query
750- main_query_filters .append (filter_expr )
742+ # Window-dim filters: kept separate so callers can handle them
743+ # appropriately. In the standard path they go to the outer WHERE;
744+ # in the preagg path they are pushed into each model's sub-query
745+ # (which has its own outer WHERE after window evaluation).
746+ # For multi-model filters, route to each model that owns a window
747+ # dim column so the preagg path includes them in the correct
748+ # sub-queries (the recursive generate() call will join in any
749+ # additional models referenced by the filter).
750+ elif window_dim_models :
751+ for wdm in window_dim_models :
752+ window_dim_filters [wdm ].append (filter_expr )
751753 # If filter references exactly one model and no metrics, push it down
752754 elif len (referenced_models ) == 1 :
753755 model = list (referenced_models )[0 ]
0 commit comments