@@ -11,6 +11,7 @@ use crate::physical_plan::{
1111} ;
1212use crate :: physical_plan_builder:: PhysicalPlanBuilder ;
1313use crate :: planner:: sql_templates:: PlanSqlTemplates ;
14+ use crate :: planner:: MemberSymbol ;
1415use crate :: planner:: SqlJoinCondition ;
1516use cubenativeutils:: CubeError ;
1617use std:: rc:: Rc ;
@@ -73,68 +74,110 @@ impl PreAggregationProcessor<'_> {
7374 Ok ( from)
7475 }
7576
77+ /// Resolve the column a union branch stores a lambda member under. The
78+ /// lambda exposes the first member rollup's symbols, but each branch keeps
79+ /// its own cube aliases (e.g. `requests_stream__tenant_id` vs the lambda's
80+ /// `requests__tenant_id`).
81+ fn find_branch_member (
82+ lambda_member : & Rc < MemberSymbol > ,
83+ branch_members : & [ Rc < MemberSymbol > ] ,
84+ branch_cube_name : & str ,
85+ ) -> Result < Rc < MemberSymbol > , CubeError > {
86+ if let Some ( member) = branch_members
87+ . iter ( )
88+ . find ( |m| m. full_name ( ) == lambda_member. full_name ( ) )
89+ {
90+ return Ok ( member. clone ( ) ) ;
91+ }
92+ let short_name = lambda_member. name ( ) ;
93+ if let Some ( member) = branch_members
94+ . iter ( )
95+ . find ( |m| m. name ( ) == short_name && m. cube_name ( ) == branch_cube_name)
96+ {
97+ return Ok ( member. clone ( ) ) ;
98+ }
99+ Err ( CubeError :: internal ( format ! (
100+ "Lambda pre-aggregation member '{}' has no match in union branch '{}'" ,
101+ lambda_member. full_name( ) ,
102+ branch_cube_name
103+ ) ) )
104+ }
105+
76106 fn make_pre_aggregation_union_source (
77107 & self ,
78108 pre_aggregation : & PreAggregation ,
79109 union : & PreAggregationUnion ,
80110 ) -> Result < Rc < From > , CubeError > {
81111 if union. items . len ( ) == 1 {
82- let table_source = self . make_pre_aggregation_table_source ( & union. items [ 0 ] ) ?;
112+ let table_source = self . make_pre_aggregation_table_source ( & union. items [ 0 ] . table ) ?;
83113 return Ok ( From :: new ( FromSource :: Single ( table_source) ) ) ;
84114 }
85115 let query_tools = self . builder . query_tools ( ) ;
86116
87117 let mut union_sources = Vec :: new ( ) ;
88118 for item in union. items . iter ( ) {
89- let table_source = self . make_pre_aggregation_table_source ( & item) ?;
119+ let branch_cube_name = & item. table . cube_name ;
120+ let table_source = self . make_pre_aggregation_table_source ( & item. table ) ?;
90121 let from = From :: new ( FromSource :: Single ( table_source) ) ;
91122 let mut select_builder = SelectBuilder :: new ( from) ;
92123 for dim in pre_aggregation. dimensions ( ) . iter ( ) {
93- let name_in_table =
94- PlanSqlTemplates :: member_alias_name ( & item . cube_alias , & dim . name ( ) , & None ) ;
95- let alias = dim. alias ( ) ;
124+ // Read this branch's stored column for the lambda dimension and
125+ // project it under the lambda's unified alias.
126+ let branch_dim = Self :: find_branch_member ( dim, & item . dimensions , branch_cube_name ) ? ;
96127 select_builder. add_projection_reference_member (
97128 & dim,
98- QualifiedColumnName :: new ( None , name_in_table ) ,
99- Some ( alias) ,
129+ QualifiedColumnName :: new ( None , branch_dim . alias ( ) ) ,
130+ Some ( dim . alias ( ) ) ,
100131 ) ;
101132 }
133+ // Match time dimensions on their base member so the granularity
134+ // suffix is applied consistently on both the read and output sides.
135+ let branch_time_bases = item
136+ . time_dimensions
137+ . iter ( )
138+ . map ( |td| {
139+ if let Ok ( t) = td. as_time_dimension ( ) {
140+ t. base_symbol ( ) . clone ( )
141+ } else {
142+ td. clone ( )
143+ }
144+ } )
145+ . collect :: < Vec < _ > > ( ) ;
102146 for dim in pre_aggregation. time_dimensions ( ) . iter ( ) {
103- let ( alias , granularity) = if let Ok ( td) = dim. as_time_dimension ( ) {
104- ( td. base_symbol ( ) . alias ( ) , td. granularity ( ) . clone ( ) )
147+ let ( lambda_base , granularity) = if let Ok ( td) = dim. as_time_dimension ( ) {
148+ ( td. base_symbol ( ) . clone ( ) , td. granularity ( ) . clone ( ) )
105149 } else {
106- ( dim. alias ( ) , None )
150+ ( dim. clone ( ) , None )
107151 } ;
108152
109- let name_in_table = PlanSqlTemplates :: member_alias_name (
110- & item. cube_alias ,
111- & dim. name ( ) ,
112- & granularity,
113- ) ;
153+ let branch_base =
154+ Self :: find_branch_member ( & lambda_base, & branch_time_bases, branch_cube_name) ?;
114155
115- let suffix = if let Some ( granularity) = granularity {
116- format ! ( "_{}" , granularity. clone( ) )
156+ let read_suffix = if let Some ( granularity) = & granularity {
157+ format ! ( "_{}" , granularity)
158+ } else {
159+ String :: new ( )
160+ } ;
161+ let name_in_table = format ! ( "{}{}" , branch_base. alias( ) , read_suffix) ;
162+
163+ let out_suffix = if let Some ( granularity) = & granularity {
164+ format ! ( "_{}" , granularity)
117165 } else {
118166 "_day" . to_string ( )
119167 } ;
120- let alias = format ! ( "{}{}" , alias, suffix ) ;
168+ let alias = format ! ( "{}{}" , lambda_base . alias( ) , out_suffix ) ;
121169 select_builder. add_projection_reference_member (
122170 & dim,
123- QualifiedColumnName :: new ( None , name_in_table. clone ( ) ) ,
171+ QualifiedColumnName :: new ( None , name_in_table) ,
124172 Some ( alias) ,
125173 ) ;
126174 }
127175 for meas in pre_aggregation. measures ( ) . iter ( ) {
128- let name_in_table = PlanSqlTemplates :: member_alias_name (
129- & item. cube_alias ,
130- & meas. name ( ) ,
131- & meas. alias_suffix ( ) ,
132- ) ;
133- let alias = meas. alias ( ) ;
176+ let branch_meas = Self :: find_branch_member ( meas, & item. measures , branch_cube_name) ?;
134177 select_builder. add_projection_reference_member (
135178 & meas,
136- QualifiedColumnName :: new ( None , name_in_table . clone ( ) ) ,
137- Some ( alias) ,
179+ QualifiedColumnName :: new ( None , branch_meas . alias ( ) ) ,
180+ Some ( meas . alias ( ) ) ,
138181 ) ;
139182 }
140183 let context = SqlNodesFactory :: new ( ) ;
0 commit comments