@@ -519,13 +519,13 @@ def test_window_dimension_filter_with_regular_filter(layer):
519519 pytest .main ([__file__ , "-v" ])
520520
521521
522- def test_window_dim_filter_applied_as_outer_where_in_preagg (layer ):
523- """Test that window-dim filters are applied as outer WHERE in the preagg path.
522+ def test_window_dim_filter_pushed_into_model_subquery_in_preagg (layer ):
523+ """Test that window-dim filters are pushed into model subqueries in the preagg path.
524524
525525 When metrics come from multiple models (triggering pre-aggregation), a filter
526- on a window dimension must be applied as an outer WHERE on the final preagg
527- join so that ALL models' metrics are constrained. The window dim column is
528- projected through the owning model's preagg CTE to make it available .
526+ on a window dimension is pushed into the owning model's recursive generate()
527+ call. This preserves the preagg grain (no extra dimensions added) while the
528+ recursive generate() handles the window dim filter in its own outer WHERE .
529529 """
530530 from sidemantic .core .model import Relationship
531531
@@ -588,26 +588,24 @@ def test_window_dim_filter_applied_as_outer_where_in_preagg(layer):
588588 assert "orders_preagg" in sql , "Should use pre-aggregation path"
589589 assert "order_items_preagg" in sql , "Should use pre-aggregation path"
590590
591- # The window dim column should be projected in the orders preagg CTE
591+ # The window dim filter should be inside the orders preagg CTE (pushed into
592+ # the model subquery), not in the outer WHERE
592593 preagg_start = sql .index ("orders_preagg AS (" )
593594 preagg_end = sql .index ("order_items_preagg AS (" )
594595 orders_subquery = sql [preagg_start :preagg_end ]
595- assert "next_status" in orders_subquery , "Window dim column should be projected in orders preagg CTE "
596+ assert "next_status" in orders_subquery , "Window dim filter should be in orders preagg subquery "
596597
597- # The filter should appear in the outer WHERE referencing the preagg CTE,
598- # so that BOTH models' metrics are constrained by the filter
598+ # The outer query should NOT have the window dim filter (it's handled inside subquery)
599599 outer_query = sql [sql .rindex ("SELECT" ) :]
600- assert "orders_preagg.next_status" in outer_query , (
601- "Window-dim filter should be in outer WHERE referencing preagg CTE"
602- )
600+ assert "next_status" not in outer_query , "Window dim filter should NOT be in outer WHERE"
603601
604602
605- def test_multi_model_window_dim_filter_applied_as_outer_where_in_preagg (layer ):
606- """Test that multi-model window-dim filters are applied as outer WHERE in preagg.
603+ def test_multi_model_window_dim_filter_pushed_into_model_subquery_in_preagg (layer ):
604+ """Test that multi-model window-dim filters are pushed into model subqueries in preagg.
607605
608606 When a filter references columns from multiple models and at least one is a
609- window dimension, the window dim column is projected through the preagg CTE
610- and the filter is applied on the outer WHERE so both models are constrained .
607+ window dimension, the filter is pushed into the owning model's subquery so
608+ the recursive generate() handles it correctly without changing the preagg grain .
611609 """
612610 from sidemantic .core .model import Relationship
613611
@@ -672,24 +670,24 @@ def test_multi_model_window_dim_filter_applied_as_outer_where_in_preagg(layer):
672670 assert "orders_preagg" in sql , "Should use pre-aggregation path"
673671 assert "order_items_preagg" in sql , "Should use pre-aggregation path"
674672
675- # The window dim column should be projected in the orders preagg CTE
673+ # The window dim filter should be inside the orders preagg CTE
676674 preagg_start = sql .index ("orders_preagg AS (" )
677675 preagg_end = sql .index ("order_items_preagg AS (" )
678676 orders_subquery = sql [preagg_start :preagg_end ]
679- assert "next_status" in orders_subquery , "Window dim column should be projected in orders preagg CTE "
677+ assert "next_status" in orders_subquery , "Window dim filter should be in orders preagg subquery "
680678
681- # The filter should appear in the outer WHERE
679+ # The outer query should NOT have the window dim filter
682680 outer_query = sql [sql .rindex ("SELECT" ) :]
683- assert "orders_preagg. next_status" in outer_query , "Multi-model window- dim filter should be in outer WHERE"
681+ assert "next_status" not in outer_query , "Window dim filter should NOT be in outer WHERE"
684682
685683
686- def test_mixed_metric_and_window_dim_filter_applied_as_outer_where_in_preagg (layer ):
687- """Test that a filter referencing both a metric and a window dim is applied as outer WHERE .
684+ def test_mixed_metric_and_window_dim_filter_pushed_into_model_subquery_in_preagg (layer ):
685+ """Test that a filter referencing both a metric and a window dim is pushed into model subquery .
688686
689687 When a filter like "orders.next_status = 'complete' OR orders.revenue > 100"
690688 references both a window dimension (next_status) and a metric (revenue), the
691- window dim check takes priority and the filter is applied on the outer preagg
692- WHERE. The window dim column is projected through the preagg CTE .
689+ window dim check takes priority and the filter is pushed into the owning model's
690+ subquery to preserve the preagg grain .
693691 """
694692 from sidemantic .core .model import Relationship
695693
@@ -752,12 +750,12 @@ def test_mixed_metric_and_window_dim_filter_applied_as_outer_where_in_preagg(lay
752750 assert "orders_preagg" in sql , "Should use pre-aggregation path"
753751 assert "order_items_preagg" in sql , "Should use pre-aggregation path"
754752
755- # The window dim column should be projected in the orders preagg CTE
753+ # The window dim filter should be inside the orders preagg CTE
756754 preagg_start = sql .index ("orders_preagg AS (" )
757755 preagg_end = sql .index ("order_items_preagg AS (" )
758756 orders_subquery = sql [preagg_start :preagg_end ]
759- assert "next_status" in orders_subquery , "Window dim column should be projected in orders preagg CTE "
757+ assert "next_status" in orders_subquery , "Window dim filter should be in orders preagg subquery "
760758
761- # The filter should be in the outer WHERE, constraining both models
759+ # The outer query should NOT have the window dim filter
762760 outer_query = sql [sql .rindex ("SELECT" ) :]
763- assert "orders_preagg. next_status" in outer_query , "Mixed metric/window- dim filter should be in outer WHERE"
761+ assert "next_status" not in outer_query , "Window dim filter should NOT be in outer WHERE"
0 commit comments