From 40add93b74b5eb34c63e5027cc42177148b8b981 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Thu, 5 Mar 2026 12:34:47 +0100 Subject: [PATCH 01/21] in work --- .../src/planner/sql_evaluator/compiler.rs | 19 +- .../planner/sql_evaluator/sql_call_builder.rs | 207 ++++------ .../symbols/common/symbol_path.rs | 370 ++++++++++++++---- 3 files changed, 375 insertions(+), 221 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index 6c133522863ff..fe4819827ae99 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -57,18 +57,15 @@ impl Compiler { &mut self, name: String, ) -> Result, CubeError> { - let path = name.split(".").map(|s| s.to_string()).collect::>(); - if self.cube_evaluator.is_measure(path.clone())? { - Ok(self.add_measure_evaluator(name)?) - } else if self.cube_evaluator.is_dimension(path.clone())? { - Ok(self.add_dimension_evaluator(name)?) - } else if self.cube_evaluator.is_segment(path.clone())? { - Ok(self.add_segment_evaluator(name)?) - } else { - Err(CubeError::internal(format!( - "Cannot resolve evaluator of member {}. Only dimensions, measures and segments can be autoresolved", + let path = SymbolPath::parse(self.cube_evaluator.clone(), &name)?; + match path.path_type() { + SymbolPathType::Dimension => self.add_dimension_evaluator_impl(path), + SymbolPathType::Measure => self.add_measure_evaluator_impl(path), + SymbolPathType::Segment => self.add_segment_evaluator(path.full_name().clone()), + _ => Err(CubeError::internal(format!( + "Cannot auto-resolve {}. Only dimensions, measures and segments", name - ))) + ))), } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs index 99a32ff5352f9..ef26733d96617 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs @@ -2,7 +2,7 @@ use super::symbols::MemberSymbol; use super::Compiler; use super::{ CubeRef, SqlCall, SqlCallDependency, SqlCallFilterGroupItem, SqlCallFilterParamsItem, - SqlDependency, + SqlDependency, SymbolPath, SymbolPathType, }; use crate::cube_bridge::base_tools::BaseTools; use crate::cube_bridge::evaluator::CubeEvaluator; @@ -100,142 +100,91 @@ impl<'a> SqlCallBuilder<'a> { ) -> Result { assert!(!dep_path.is_empty()); - self.process_dependency_item(current_cube_name, dep_path, vec![]) - .map_err(|e| CubeError::user(format!("Error in `{}`: {}", dep_path.join("."), e))) - } - - fn process_dependency_item( - &mut self, - current_cube_name: &String, - path_tail: &[String], - processed_path: Vec, - ) -> Result { - assert!(!path_tail.is_empty()); - if let Some(member) = self.try_process_member_dependency_item( - current_cube_name, - path_tail, - processed_path.clone(), - ) { - Ok(member) - } else if let Some(cube_name) = self.get_cube_name(¤t_cube_name, &path_tail[0])? { - self.process_cube_dependency_item(&cube_name, path_tail, processed_path) - } else { - Err(CubeError::user(format!( - "Undefined property {}", - path_tail[0] - ))) - } - } - - fn try_process_member_dependency_item( - &mut self, - current_cube_name: &String, - path_tail: &[String], - processed_path: Vec, - ) -> Option { - if let Ok(member_symbol) = self.build_evaluator(¤t_cube_name, &path_tail[0]) { - if let Ok(dimension) = member_symbol.as_dimension() { - if dimension.is_time() && path_tail.len() == 2 { - let granularity = &path_tail[1]; - if let Ok(Some(granularity_obj)) = GranularityHelper::make_granularity_obj( + let symbol_path = SymbolPath::parse_parts( + self.cube_evaluator.clone(), + Some(current_cube_name), + dep_path, + ) + .map_err(|e| CubeError::user(format!("Error in `{}`: {}", dep_path.join("."), e)))?; + + let path = symbol_path.path().clone(); + + match symbol_path.path_type() { + SymbolPathType::Dimension => { + if let Some(granularity) = symbol_path.granularity().clone() { + let base = self + .compiler + .add_dimension_evaluator(symbol_path.full_name().clone())?; + let granularity_obj = GranularityHelper::make_granularity_obj( self.cube_evaluator.clone(), self.compiler, - ¤t_cube_name, - &path_tail[0], + symbol_path.cube_name(), + symbol_path.symbol_name(), Some(granularity.clone()), - ) { - let time_dim_symbol = - MemberSymbol::new_time_dimension(TimeDimensionSymbol::new( - member_symbol, - Some(granularity.clone()), - Some(granularity_obj), - None, - )); - let result = SqlCallDependency { - path: processed_path, - symbol: SqlDependency::Symbol(time_dim_symbol), - }; - return Some(result); + )?; + if let Some(granularity_obj) = granularity_obj { + let time_dim = MemberSymbol::new_time_dimension(TimeDimensionSymbol::new( + base, + Some(granularity), + Some(granularity_obj), + None, + )); + Ok(SqlCallDependency { + path, + symbol: SqlDependency::Symbol(time_dim), + }) } else { - return None; + Err(CubeError::user(format!( + "Invalid granularity: {}", + granularity + ))) } + } else { + let member = self + .compiler + .add_dimension_evaluator(symbol_path.full_name().clone())?; + Ok(SqlCallDependency { + path, + symbol: SqlDependency::Symbol(member), + }) } } - if path_tail.len() > 1 { - return None; + SymbolPathType::Measure => { + let member = self + .compiler + .add_measure_evaluator(symbol_path.full_name().clone())?; + Ok(SqlCallDependency { + path, + symbol: SqlDependency::Symbol(member), + }) + } + SymbolPathType::Segment => { + let member = self + .compiler + .add_segment_evaluator(symbol_path.full_name().clone())?; + Ok(SqlCallDependency { + path, + symbol: SqlDependency::Symbol(member), + }) + } + SymbolPathType::CubeName => { + let symbol = self + .compiler + .add_cube_name_evaluator(symbol_path.cube_name().clone())?; + Ok(SqlCallDependency { + path: path.clone(), + symbol: SqlDependency::CubeRef(CubeRef::Name { symbol, path }), + }) + } + SymbolPathType::CubeTable => { + let symbol = self + .compiler + .add_cube_table_evaluator(symbol_path.cube_name().clone())?; + Ok(SqlCallDependency { + path: path.clone(), + symbol: SqlDependency::CubeRef(CubeRef::Table { symbol, path }), + }) } - let result = SqlCallDependency { - path: processed_path, - symbol: SqlDependency::Symbol(member_symbol), - }; - Some(result) - } else { - None - } - } - - fn process_cube_dependency_item( - &mut self, - cube_name: &String, - path_tail: &[String], - mut processed_path: Vec, - ) -> Result { - processed_path.push(cube_name.clone()); - if path_tail.len() == 1 { - let symbol = self.compiler.add_cube_name_evaluator(cube_name.clone())?; - let result = SqlCallDependency { - path: processed_path.clone(), - symbol: SqlDependency::CubeRef(CubeRef::Name { - symbol, - path: processed_path, - }), - }; - return Ok(result); - } - if path_tail.len() == 2 && path_tail[1] == "__sql_fn" { - let symbol = self.compiler.add_cube_table_evaluator(cube_name.clone())?; - let result = SqlCallDependency { - path: processed_path.clone(), - symbol: SqlDependency::CubeRef(CubeRef::Table { - symbol, - path: processed_path, - }), - }; - return Ok(result); - } - self.process_dependency_item(&cube_name, &path_tail[1..], processed_path) - } - - fn get_cube_name( - &mut self, - current_cube: &String, - cube_name: &String, - ) -> Result, CubeError> { - if self.is_current_cube(cube_name) { - Ok(Some(current_cube.clone())) - } else if self.cube_evaluator.cube_exists(cube_name.clone())? { - Ok(Some(cube_name.clone())) - } else { - Ok(None) - } - } - - fn is_current_cube(&self, name: &str) -> bool { - match name { - "CUBE" | "TABLE" => true, - _ => false, } } - - fn build_evaluator( - &mut self, - current_cube_name: &String, - name: &String, - ) -> Result, CubeError> { - let dep_full_name = format!("{}.{}", current_cube_name, name); - let res = self - .compiler - .add_auto_resolved_member_evaluator(dep_full_name.clone())?; - Ok(res) - } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs index fe05d49b0e835..0a595851443cc 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/symbol_path.rs @@ -4,11 +4,13 @@ use cubenativeutils::CubeError; use crate::cube_bridge::evaluator::CubeEvaluator; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SymbolPathType { Dimension, Measure, Segment, + CubeName, + CubeTable, } #[derive(Debug, Clone)] @@ -29,7 +31,11 @@ impl SymbolPath { symbol_name: String, granularity: Option, ) -> Self { - let full_name = format!("{}.{}", cube_name, symbol_name); + let full_name = if symbol_name.is_empty() { + cube_name.clone() + } else { + format!("{}.{}", cube_name, symbol_name) + }; Self { path_type, path, @@ -41,46 +47,140 @@ impl SymbolPath { } pub fn parse(cube_evaluator: Rc, path: &str) -> Result { - let parts = path.split(".").collect::>(); - if parts.len() < 2 { - return Err(CubeError::user(format!("Invalid symbol path: {}", path))); + let parts: Vec = path.split('.').map(|s| s.to_string()).collect(); + Self::parse_parts(cube_evaluator, None, &parts) + } + + pub fn parse_parts( + cube_evaluator: Rc, + current_cube: Option<&str>, + parts: &[String], + ) -> Result { + Self::resolve_parts(cube_evaluator, current_cube, parts, vec![]) + } + + fn resolve_parts( + cube_evaluator: Rc, + current_cube: Option<&str>, + parts: &[String], + path: Vec, + ) -> Result { + if parts.is_empty() { + return Err(CubeError::user("Empty path".to_string())); } - if let Some(dim_path) = - Self::try_parse_as_dimension_with_granularity(cube_evaluator.clone(), &parts)? - { - return Ok(dim_path); + // Step 1: If current_cube set, try resolving parts[0] as member + if let Some(cube_name) = current_cube { + if let Some(result) = + Self::try_resolve_as_member(cube_evaluator.clone(), cube_name, parts, &path)? + { + return Ok(result); + } } - let path_to_check = vec![ - parts[parts.len() - 2].to_string(), - parts[parts.len() - 1].to_string(), - ]; - - let path_type = if cube_evaluator.is_dimension(path_to_check.clone())? { - SymbolPathType::Dimension - } else if cube_evaluator.is_measure(path_to_check.clone())? { - SymbolPathType::Measure - } else if cube_evaluator.is_segment(path_to_check.clone())? { - SymbolPathType::Segment - } else { - return Err(CubeError::user(format!( - "Symbol path doesn't refer to a dimension, measure or segment: {}", - path + // Step 2: Try resolving parts[0] as cube reference + let resolved = Self::resolve_cube_name(cube_evaluator.clone(), current_cube, &parts[0])?; + + if let Some(cube_name) = resolved { + let mut new_path = path; + new_path.push(cube_name.clone()); + + if parts.len() == 1 { + return Ok(Self::new( + SymbolPathType::CubeName, + new_path, + cube_name, + String::new(), + None, + )); + } + if parts.len() >= 2 && parts[1] == "__sql_fn" { + return Ok(Self::new( + SymbolPathType::CubeTable, + new_path, + cube_name, + String::new(), + None, + )); + } + return Self::resolve_parts(cube_evaluator, Some(&cube_name), &parts[1..], new_path); + } + + Err(CubeError::user(format!("Cannot resolve: {}", parts[0]))) + } + + fn try_resolve_as_member( + evaluator: Rc, + cube_name: &str, + parts: &[String], + path: &[String], + ) -> Result, CubeError> { + let check_path = vec![cube_name.to_string(), parts[0].clone()]; + + if evaluator.is_dimension(check_path.clone())? { + if parts.len() == 1 { + return Ok(Some(Self::new( + SymbolPathType::Dimension, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + None, + ))); + } + if parts.len() == 2 { + let dim = evaluator.dimension_by_path(format!("{}.{}", cube_name, parts[0]))?; + if dim.static_data().dimension_type == "time" { + return Ok(Some(Self::new( + SymbolPathType::Dimension, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + Some(parts[1].clone()), + ))); + } + } + // Dimension with extra parts (non-time) — not a member match + return Ok(None); + } + + // Measures/segments can't have extra parts + if parts.len() > 1 { + return Ok(None); + } + + if evaluator.is_measure(check_path.clone())? { + return Ok(Some(Self::new( + SymbolPathType::Measure, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + None, ))); - }; + } + if evaluator.is_segment(check_path)? { + return Ok(Some(Self::new( + SymbolPathType::Segment, + path.to_vec(), + cube_name.to_string(), + parts[0].clone(), + None, + ))); + } + Ok(None) + } - let path = parts[0..parts.len() - 2] - .iter() - .map(|s| s.to_string()) - .collect(); - Ok(Self::new( - path_type, - path, - path_to_check[0].clone(), - path_to_check[1].clone(), - None, - )) + fn resolve_cube_name( + evaluator: Rc, + current_cube: Option<&str>, + name: &str, + ) -> Result, CubeError> { + if matches!(name, "CUBE" | "TABLE") { + return Ok(current_cube.map(|s| s.to_string())); + } + if evaluator.cube_exists(name.to_string())? { + return Ok(Some(name.to_string())); + } + Ok(None) } pub fn path_type(&self) -> &SymbolPathType { @@ -114,35 +214,6 @@ impl SymbolPath { pub fn granularity(&self) -> &Option { &self.granularity } - - fn try_parse_as_dimension_with_granularity( - cube_evaluator: Rc, - parts: &[&str], - ) -> Result, CubeError> { - if parts.len() > 2 { - let path_to_check = vec![ - parts[parts.len() - 3].to_string(), - parts[parts.len() - 2].to_string(), - ]; - if cube_evaluator.is_dimension(path_to_check.clone())? { - let path_type = SymbolPathType::Dimension; - let path = parts[0..parts.len() - 3] - .iter() - .map(|s| s.to_string()) - .collect(); - - let granularity = Some(parts[parts.len() - 1].to_string()); - return Ok(Some(Self::new( - path_type, - path, - path_to_check[0].clone(), - path_to_check[1].clone(), - granularity, - ))); - } - } - Ok(None) - } } #[cfg(test)] @@ -151,8 +222,7 @@ mod tests { use crate::test_fixtures::cube_bridge::{MockCubeEvaluator, MockSchema}; use indoc::indoc; - #[test] - fn test_symbol_path_parsing() { + fn create_test_evaluator() -> Rc { let schema = MockSchema::from_yaml(indoc! {" cubes: - name: users @@ -164,9 +234,15 @@ mod tests { - name: id type: number sql: id + - name: source + type: string + sql: source measures: - name: count type: count + segments: + - name: google + sql: \"{CUBE.source} = 'google'\" - name: orders sql: SELECT * FROM orders dimensions: @@ -179,55 +255,187 @@ mod tests { sql: amount "}) .unwrap(); + Rc::new(MockCubeEvaluator::new(schema)) + } - let evaluator = Rc::new(MockCubeEvaluator::new(schema)); - + #[test] + fn test_parse_simple_dimension() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.created_at").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &Vec::::new()); + assert_eq!(result.path(), &vec!["users".to_string()]); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_cross_cube_dimension() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "orders.users.created_at").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &vec!["orders".to_string()]); + assert_eq!( + result.path(), + &vec!["orders".to_string(), "users".to_string()] + ); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_time_dimension_with_granularity() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.created_at.day").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &Vec::::new()); + assert_eq!(result.path(), &vec!["users".to_string()]); assert_eq!(result.granularity(), &Some("day".to_string())); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_cross_cube_time_dimension_with_granularity() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "orders.users.created_at.day").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "created_at"); assert_eq!(result.full_name(), "users.created_at"); - assert_eq!(result.path(), &vec!["orders".to_string()]); + assert_eq!( + result.path(), + &vec!["orders".to_string(), "users".to_string()] + ); assert_eq!(result.granularity(), &Some("day".to_string())); - assert!(matches!(result.path_type, SymbolPathType::Dimension)); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + } + #[test] + fn test_parse_measure() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.count").unwrap(); assert_eq!(result.cube_name(), "users"); assert_eq!(result.symbol_name(), "count"); assert_eq!(result.full_name(), "users.count"); - assert_eq!(result.path(), &Vec::::new()); + assert_eq!(result.path(), &vec!["users".to_string()]); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Measure)); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + } + #[test] + fn test_parse_cross_cube_measure() { + let evaluator = create_test_evaluator(); let result = SymbolPath::parse(evaluator.clone(), "users.orders.total").unwrap(); assert_eq!(result.cube_name(), "orders"); assert_eq!(result.symbol_name(), "total"); assert_eq!(result.full_name(), "orders.total"); - assert_eq!(result.path(), &vec!["users".to_string()]); + assert_eq!( + result.path(), + &vec!["users".to_string(), "orders".to_string()] + ); assert_eq!(result.granularity(), &None); - assert!(matches!(result.path_type, SymbolPathType::Measure)); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + } + + #[test] + fn test_parse_parts_cube_alias() { + let evaluator = create_test_evaluator(); + let parts = vec!["CUBE".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::CubeName); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.path(), &vec!["users".to_string()]); + } + + #[test] + fn test_parse_parts_table_alias() { + let evaluator = create_test_evaluator(); + let parts = vec!["TABLE".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::CubeName); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.path(), &vec!["users".to_string()]); + } + + #[test] + fn test_parse_parts_cube_table() { + let evaluator = create_test_evaluator(); + let parts = vec!["CUBE".to_string(), "__sql_fn".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::CubeTable); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.path(), &vec!["users".to_string()]); + } + + #[test] + fn test_parse_parts_simple_member() { + let evaluator = create_test_evaluator(); + let parts = vec!["source".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "source"); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_parse_parts_time_dimension_with_granularity() { + let evaluator = create_test_evaluator(); + let parts = vec!["created_at".to_string(), "day".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Dimension); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "created_at"); + assert_eq!(result.granularity(), &Some("day".to_string())); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_parse_parts_cross_cube_member() { + let evaluator = create_test_evaluator(); + let parts = vec!["orders".to_string(), "total".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + assert_eq!(result.cube_name(), "orders"); + assert_eq!(result.symbol_name(), "total"); + assert_eq!(result.path(), &vec!["orders".to_string()]); + } + + #[test] + fn test_parse_parts_measure() { + let evaluator = create_test_evaluator(); + let parts = vec!["count".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Measure); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "count"); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_parse_parts_segment() { + let evaluator = create_test_evaluator(); + let parts = vec!["google".to_string()]; + let result = SymbolPath::parse_parts(evaluator.clone(), Some("users"), &parts).unwrap(); + assert_eq!(result.path_type(), &SymbolPathType::Segment); + assert_eq!(result.cube_name(), "users"); + assert_eq!(result.symbol_name(), "google"); + assert_eq!(result.path(), &Vec::::new()); + } + + #[test] + fn test_cache_name_without_granularity() { + let evaluator = create_test_evaluator(); + let result = SymbolPath::parse(evaluator.clone(), "users.count").unwrap(); + assert_eq!(result.cache_name(), "users.count"); + } + + #[test] + fn test_cache_name_with_granularity() { + let evaluator = create_test_evaluator(); + let result = SymbolPath::parse(evaluator.clone(), "users.created_at.day").unwrap(); + assert_eq!(result.cache_name(), "users.created_at.day"); } } From 69a83b0bc2d1740d0b3ebfbe5358efca1deb5b66 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Thu, 5 Mar 2026 15:11:13 +0100 Subject: [PATCH 02/21] in work --- .../src/logical_plan/pre_aggregation.rs | 17 ++---- .../src/planner/filter/base_segment.rs | 1 + .../planners/dimension_subquery_planner.rs | 1 + .../src/planner/query_properties.rs | 2 + .../multiplied_measures_collector.rs | 2 +- .../src/planner/sql_evaluator/compiler.rs | 34 +++++++----- .../symbols/common/compiled_member_path.rs | 46 ++++++++++++++++ .../sql_evaluator/symbols/common/mod.rs | 2 + .../sql_evaluator/symbols/dimension_symbol.rs | 45 ++++++++++------ .../sql_evaluator/symbols/measure_symbol.rs | 54 +++++++++++-------- .../symbols/member_expression_symbol.rs | 32 ++++++----- .../sql_evaluator/symbols/member_symbol.rs | 51 +++++++----------- .../symbols/time_dimension_symbol.rs | 30 ++++++++--- 13 files changed, 199 insertions(+), 118 deletions(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs index ab7063d1df2a5..b5a35d2c67fdb 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs @@ -98,10 +98,7 @@ impl PreAggregation { for dim in self.dimensions().iter() { let alias = dim.alias(); - res.insert( - dim.full_name(), - QualifiedColumnName::new(None, alias.clone()), - ); + res.insert(dim.full_name(), QualifiedColumnName::new(None, alias)); } for dim in self.time_dimensions().iter() { let (base_symbol, granularity) = if let Ok(td) = dim.as_time_dimension() { @@ -117,7 +114,7 @@ impl PreAggregation { let alias = format!("{}{}", base_symbol.alias(), suffix); res.insert( base_symbol.full_name(), - QualifiedColumnName::new(None, alias.clone()), + QualifiedColumnName::new(None, alias), ); } @@ -130,10 +127,7 @@ impl PreAggregation { for item in join.items.iter() { for member in item.from_members.iter().chain(item.to_members.iter()) { let alias = member.alias(); - res.insert( - member.full_name(), - QualifiedColumnName::new(None, alias.clone()), - ); + res.insert(member.full_name(), QualifiedColumnName::new(None, alias)); } } } @@ -145,10 +139,7 @@ impl PreAggregation { .iter() .map(|measure| { let alias = measure.alias(); - ( - measure.full_name(), - QualifiedColumnName::new(None, alias.clone()), - ) + (measure.full_name(), QualifiedColumnName::new(None, alias)) }) .collect() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs index b1770df460ede..f182f35d9a63a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs @@ -36,6 +36,7 @@ impl BaseSegment { None, None, query_tools.base_tools().clone(), + vec![cube_name.clone()], )?; let full_name = full_name.unwrap_or(member_expression_symbol.full_name()); let member_evaluator = MemberSymbol::new_member_expression(member_expression_symbol); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs index b8d9802ad816e..889a7722d9b15 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs @@ -90,6 +90,7 @@ impl DimensionSubqueryPlanner { None, None, self.query_tools.base_tools().clone(), + vec![cube_name.clone()], )?; let measure = MemberSymbol::new_member_expression(member_expression_symbol); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index 85d8280d67322..1135fe5f50131 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -156,6 +156,7 @@ impl QueryProperties { member_expression.static_data().definition.clone(), None, query_tools.base_tools().clone(), + vec![cube_name.clone()], )?; Ok(MemberSymbol::new_member_expression( member_expression_symbol, @@ -266,6 +267,7 @@ impl QueryProperties { member_expression.static_data().definition.clone(), None, query_tools.base_tools().clone(), + vec![cube_name.clone()], )?; Ok(MemberSymbol::new_member_expression(member_expression_symbol)) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs index 26836acbe5796..4762a85d9d905 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs @@ -100,7 +100,7 @@ impl TraversalVisitor for MultipliedMeasuresCollector { .join .static_data() .multiplication_factor - .get(e.cube_name()) + .get(&e.cube_name()) .unwrap_or(&false) .clone(); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index fe4819827ae99..4dfd55caa4d50 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -61,7 +61,7 @@ impl Compiler { match path.path_type() { SymbolPathType::Dimension => self.add_dimension_evaluator_impl(path), SymbolPathType::Measure => self.add_measure_evaluator_impl(path), - SymbolPathType::Segment => self.add_segment_evaluator(path.full_name().clone()), + SymbolPathType::Segment => self.add_segment_evaluator_impl(path), _ => Err(CubeError::internal(format!( "Cannot auto-resolve {}. Only dimensions, measures and segments", name @@ -98,7 +98,7 @@ impl Compiler { let path = SymbolPath::parse(self.cube_evaluator.clone(), &dimension)?; match path.path_type() { SymbolPathType::Segment => { - let symbol = self.add_segment_evaluator(path.full_name().clone())?; + let symbol = self.add_segment_evaluator_impl(path)?; let me = symbol.as_member_expression()?; Ok(MemberSymbol::new_member_expression(me.with_parenthesized())) } @@ -121,27 +121,33 @@ impl Compiler { } pub fn add_segment_evaluator(&mut self, name: String) -> Result, CubeError> { - if let Some(exists) = self.exists_member(CacheSymbolType::Segment, &name) { + let path = SymbolPath::parse(self.cube_evaluator.clone(), &name)?; + self.add_segment_evaluator_impl(path) + } + + fn add_segment_evaluator_impl( + &mut self, + path: SymbolPath, + ) -> Result, CubeError> { + let full_name = path.full_name().clone(); + if let Some(exists) = self.exists_member(CacheSymbolType::Segment, &full_name) { return Ok(exists.clone()); } - let path = self - .cube_evaluator - .parse_path("segments".to_string(), name.clone())?; - let cube_name = path[0].clone(); - let member_name = path[1].clone(); - let definition = self.cube_evaluator.segment_by_path(name.clone())?; - let sql_call = self.compile_sql_call(&cube_name, definition.sql()?)?; - let alias = PlanSqlTemplates::member_alias_name(&cube_name, &member_name, &None); + let definition = self.cube_evaluator.segment_by_path(full_name.clone())?; + let sql_call = self.compile_sql_call(path.cube_name(), definition.sql()?)?; + let alias = + PlanSqlTemplates::member_alias_name(path.cube_name(), path.symbol_name(), &None); let symbol = MemberExpressionSymbol::try_new( - cube_name, - member_name, + path.cube_name().clone(), + path.symbol_name().clone(), MemberExpressionExpression::SqlCall(sql_call), None, Some(alias), self.base_tools.clone(), + path.path().clone(), )?; let result = MemberSymbol::new_member_expression(symbol); - let key = (CacheSymbolType::Segment, name); + let key = (CacheSymbolType::Segment, full_name); self.members.insert(key, result.clone()); Ok(result) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs new file mode 100644 index 0000000000000..c0a39d5f03c24 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs @@ -0,0 +1,46 @@ +#[derive(Clone, Debug)] +pub struct CompiledMemberPath { + full_name: String, + cube_name: String, + name: String, + alias: String, + path: Vec, +} + +impl CompiledMemberPath { + pub fn new( + full_name: String, + cube_name: String, + name: String, + alias: String, + path: Vec, + ) -> Self { + Self { + full_name, + cube_name, + name, + alias, + path, + } + } + + pub fn full_name(&self) -> &String { + &self.full_name + } + + pub fn cube_name(&self) -> &String { + &self.cube_name + } + + pub fn name(&self) -> &String { + &self.name + } + + pub fn alias(&self) -> &String { + &self.alias + } + + pub fn path(&self) -> &Vec { + &self.path + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs index ed8bf27a461d2..e278e2e966bff 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/mod.rs @@ -1,11 +1,13 @@ mod aggregation_type; mod case; +mod compiled_member_path; mod dimension_type; mod static_filter; mod symbol_path; pub use aggregation_type::*; pub use case::*; +pub use compiled_member_path::*; pub use dimension_type::*; pub use static_filter::*; pub use symbol_path::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index 2444bd9137cef..8268ccf0d057f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -1,4 +1,5 @@ use super::common::Case; +use super::common::CompiledMemberPath; use super::dimension_kinds::{ CaseDimension, DimensionKind, GeoDimension, RegularDimension, SwitchDimension, }; @@ -28,9 +29,8 @@ pub struct CalendarDimensionTimeShift { #[derive(Clone)] pub struct DimensionSymbol { cube: Rc, - name: String, + compiled_path: CompiledMemberPath, kind: DimensionKind, - alias: String, is_reference: bool, // Symbol is a direct reference to another symbol without any calculations is_view: bool, add_group_by: Option>>, @@ -45,9 +45,8 @@ pub struct DimensionSymbol { impl DimensionSymbol { pub fn new( cube: Rc, - name: String, + compiled_path: CompiledMemberPath, kind: DimensionKind, - alias: String, is_reference: bool, is_view: bool, add_group_by: Option>>, @@ -60,9 +59,8 @@ impl DimensionSymbol { ) -> Rc { Rc::new(Self { cube, - name, + compiled_path, kind, - alias, is_reference, is_view, add_group_by, @@ -83,8 +81,8 @@ impl DimensionSymbol { templates: &PlanSqlTemplates, ) -> Result { self.kind.evaluate_sql( - &self.name, - &self.full_name(), + self.compiled_path.name(), + self.compiled_path.full_name(), visitor, node_processor, query_tools, @@ -139,12 +137,16 @@ impl DimensionSymbol { self.time_shift_pk_full_name.clone() } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + pub fn full_name(&self) -> String { - format!("{}.{}", self.cube.cube_name(), self.name) + self.compiled_path.full_name().clone() } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn owned_by_cube(&self) -> bool { @@ -235,16 +237,20 @@ impl DimensionSymbol { self.kind.get_cube_refs() } - pub fn cube_name(&self) -> &String { - self.cube.cube_name() + pub fn cube_name(&self) -> String { + self.compiled_path.cube_name().clone() } pub fn join_map(&self) -> &Option>> { self.cube.join_map() } - pub fn name(&self) -> &String { - &self.name + pub fn name(&self) -> String { + self.compiled_path.name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path.path() } pub fn calendar_time_shift_for_interval( @@ -470,11 +476,18 @@ impl SymbolFactory for DimensionSymbolFactory { let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; + let compiled_path = CompiledMemberPath::new( + path.full_name().clone(), + path.cube_name().clone(), + path.symbol_name().clone(), + alias, + path.path().clone(), + ); + let symbol = MemberSymbol::new_dimension(DimensionSymbol::new( cube_symbol, - path.symbol_name().clone(), + compiled_path, kind, - alias, is_reference, is_view, add_group_by, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index 444299b749d7c..965fe82fecd52 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -1,4 +1,4 @@ -use super::common::{AggregationType, Case}; +use super::common::{AggregationType, Case, CompiledMemberPath}; use super::measure_kinds::{CalculatedMeasure, CalculatedMeasureType, MeasureKind}; use super::SymbolPath; use super::{MemberSymbol, SymbolFactory}; @@ -73,8 +73,7 @@ pub enum MeasureTimeShifts { #[derive(Clone)] pub struct MeasureSymbol { cube: Rc, - name: String, - alias: String, + compiled_path: CompiledMemberPath, kind: MeasureKind, rolling_window: Option, is_multi_stage: bool, @@ -94,8 +93,7 @@ pub struct MeasureSymbol { impl MeasureSymbol { pub fn new( cube: Rc, - name: String, - alias: String, + compiled_path: CompiledMemberPath, is_reference: bool, is_view: bool, case: Option, @@ -112,8 +110,7 @@ impl MeasureSymbol { ) -> Rc { Rc::new(Self { cube, - name, - alias, + compiled_path, is_reference, is_view, case, @@ -149,8 +146,7 @@ impl MeasureSymbol { }; Rc::new(Self { cube: self.cube.clone(), - name: self.name.clone(), - alias: self.alias.clone(), + compiled_path: self.compiled_path.clone(), kind, rolling_window: None, is_multi_stage: false, @@ -180,7 +176,7 @@ impl MeasureSymbol { if !self.kind.can_replace_type_with(&new_measure_type) { return Err(CubeError::user(format!( "Unsupported measure type replacement for {}: {} => {}", - self.name, + self.compiled_path.name(), self.kind.measure_type_str(), new_measure_type ))); @@ -195,7 +191,7 @@ impl MeasureSymbol { if !result_kind.supports_additional_filters() { return Err(CubeError::user(format!( "Unsupported additional filters for measure {} type {}", - self.name, + self.compiled_path.name(), result_kind.measure_type_str() ))); } @@ -203,8 +199,7 @@ impl MeasureSymbol { } Ok(Rc::new(Self { cube: self.cube.clone(), - name: self.name.clone(), - alias: self.alias.clone(), + compiled_path: self.compiled_path.clone(), kind: result_kind, rolling_window: self.rolling_window.clone(), is_multi_stage: self.is_multi_stage, @@ -228,12 +223,16 @@ impl MeasureSymbol { Rc::new(new) } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + pub fn full_name(&self) -> String { - format!("{}.{}", self.cube.cube_name(), self.name) + self.compiled_path.full_name().clone() } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn is_splitted_source(&self) -> bool { @@ -459,16 +458,20 @@ impl MeasureSymbol { self.is_multi_stage } - pub fn cube_name(&self) -> &String { - &self.cube.cube_name() + pub fn cube_name(&self) -> String { + self.compiled_path.cube_name().clone() } pub fn join_map(&self) -> &Option>> { self.cube.join_map() } - pub fn name(&self) -> &String { - &self.name + pub fn name(&self) -> String { + self.compiled_path.name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path.path() } } @@ -593,7 +596,7 @@ impl SymbolFactory for MeasureSymbolFactory { } } else { shifts.insert( - dimension_name, + dimension_name.clone(), DimensionTimeShift { interval: interval.clone(), name: name.clone(), @@ -730,10 +733,17 @@ impl SymbolFactory for MeasureSymbolFactory { let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; - Ok(MemberSymbol::new_measure(MeasureSymbol::new( - cube_symbol, + let compiled_path = CompiledMemberPath::new( + path.full_name().clone(), + path.cube_name().clone(), path.symbol_name().clone(), alias, + path.path().clone(), + ); + + Ok(MemberSymbol::new_measure(MeasureSymbol::new( + cube_symbol, + compiled_path, is_reference, is_view, case, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs index fbe71cf49b536..7f573e054f8f9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs @@ -1,3 +1,4 @@ +use super::common::CompiledMemberPath; use super::MemberSymbol; use crate::cube_bridge::base_tools::BaseTools; use crate::planner::query_tools::QueryTools; @@ -17,9 +18,7 @@ pub enum MemberExpressionExpression { #[derive(Clone)] pub struct MemberExpressionSymbol { - cube_name: String, - name: String, - alias: String, + compiled_path: CompiledMemberPath, expression: MemberExpressionExpression, #[allow(dead_code)] definition: Option, @@ -35,16 +34,17 @@ impl MemberExpressionSymbol { definition: Option, alias: Option, _base_tools: Rc, + path: Vec, ) -> Result, CubeError> { + let full_name = format!("expr:{}.{}", cube_name, name); let alias = alias.unwrap_or_else(|| PlanSqlTemplates::alias_name(&name)); let is_reference = match &expression { MemberExpressionExpression::SqlCall(sql_call) => sql_call.is_direct_reference(), MemberExpressionExpression::PatchedSymbol(_symbol) => false, }; + let compiled_path = CompiledMemberPath::new(full_name, cube_name, name, alias, path); Ok(Rc::new(Self { - cube_name, - name, - alias, + compiled_path, expression, definition, is_reference, @@ -80,12 +80,16 @@ impl MemberExpressionSymbol { Rc::new(result) } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + pub fn full_name(&self) -> String { - format!("expr:{}.{}", self.cube_name, self.name) + self.compiled_path.full_name().clone() } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn is_reference(&self) -> bool { @@ -170,12 +174,16 @@ impl MemberExpressionSymbol { } } - pub fn cube_name(&self) -> &String { - &self.cube_name + pub fn cube_name(&self) -> String { + self.compiled_path.cube_name().clone() + } + + pub fn name(&self) -> String { + self.compiled_path.name().clone() } - pub fn name(&self) -> &String { - &self.name + pub fn path(&self) -> &Vec { + self.compiled_path.path() } pub fn definition(&self) -> &Option { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index ddb7887d79e35..b09ef8cdeb1f9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -4,6 +4,7 @@ use itertools::Itertools; use crate::planner::sql_evaluator::collectors::has_multi_stage_members; use crate::planner::sql_evaluator::{Case, CubeRef, SqlCall}; +use super::common::CompiledMemberPath; use super::{DimensionSymbol, MeasureSymbol, MemberExpressionSymbol, TimeDimensionSymbol}; use std::fmt::Debug; use std::rc::Rc; @@ -34,15 +35,8 @@ impl Debug for MemberSymbol { impl PartialEq for MemberSymbol { fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Dimension(l0), Self::Dimension(r0)) => l0.full_name() == r0.full_name(), - (Self::TimeDimension(l0), Self::TimeDimension(r0)) => l0.full_name() == r0.full_name(), - (Self::Measure(l0), Self::Measure(r0)) => l0.full_name() == r0.full_name(), - (Self::MemberExpression(l0), Self::MemberExpression(r0)) => { - l0.full_name() == r0.full_name() - } - _ => false, - } + self.full_name() == other.full_name() + && std::mem::discriminant(self) == std::mem::discriminant(other) } } @@ -63,40 +57,33 @@ impl MemberSymbol { Rc::new(Self::TimeDimension(symbol)) } - pub fn full_name(&self) -> String { + pub fn compiled_path(&self) -> &CompiledMemberPath { match self { - Self::Dimension(d) => d.full_name(), - Self::TimeDimension(d) => d.full_name(), - Self::Measure(m) => m.full_name(), - Self::MemberExpression(e) => e.full_name().clone(), + Self::Dimension(d) => d.compiled_path(), + Self::TimeDimension(d) => d.compiled_path(), + Self::Measure(m) => m.compiled_path(), + Self::MemberExpression(e) => e.compiled_path(), } } + pub fn full_name(&self) -> String { + self.compiled_path().full_name().clone() + } + pub fn alias(&self) -> String { - match self { - Self::Dimension(d) => d.alias(), - Self::TimeDimension(d) => d.alias(), - Self::Measure(m) => m.alias(), - Self::MemberExpression(e) => e.alias(), - } + self.compiled_path().alias().clone() } pub fn name(&self) -> String { - match self { - Self::Dimension(d) => d.name().clone(), - Self::TimeDimension(d) => d.name(), - Self::Measure(m) => m.name().clone(), - Self::MemberExpression(e) => e.name().clone(), - } + self.compiled_path().name().clone() } pub fn cube_name(&self) -> String { - match self { - Self::Dimension(d) => d.cube_name().clone(), - Self::TimeDimension(d) => d.cube_name(), - Self::Measure(m) => m.cube_name().clone(), - Self::MemberExpression(e) => e.cube_name().clone(), - } + self.compiled_path().cube_name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path().path() } pub fn is_multi_stage(&self) -> bool { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs index b449fe1e473e2..3d5242a113644 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs @@ -1,3 +1,4 @@ +use super::common::CompiledMemberPath; use super::MemberSymbol; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::CubeRef; @@ -11,8 +12,7 @@ use std::rc::Rc; #[derive(Clone)] pub struct TimeDimensionSymbol { base_symbol: Rc, - full_name: String, - alias: String, + compiled_path: CompiledMemberPath, granularity: Option, granularity_obj: Option, date_range: Option<(String, String)>, @@ -33,12 +33,18 @@ impl TimeDimensionSymbol { }; let full_name = format!("{}_{}", base_symbol.full_name(), name_suffix); let alias = format!("{}_{}", base_symbol.alias(), name_suffix); + let compiled_path = CompiledMemberPath::new( + full_name, + base_symbol.cube_name().clone(), + base_symbol.name().clone(), + alias, + base_symbol.path().clone(), + ); Rc::new(Self { base_symbol, - alias, + compiled_path, granularity, granularity_obj, - full_name, date_range, alias_suffix: name_suffix, }) @@ -94,8 +100,12 @@ impl TimeDimensionSymbol { Ok(result) } + pub fn compiled_path(&self) -> &CompiledMemberPath { + &self.compiled_path + } + pub fn full_name(&self) -> String { - self.full_name.clone() + self.compiled_path.full_name().clone() } pub fn alias_suffix(&self) -> String { @@ -103,7 +113,7 @@ impl TimeDimensionSymbol { } pub fn alias(&self) -> String { - self.alias.clone() + self.compiled_path.alias().clone() } pub fn owned_by_cube(&self) -> bool { @@ -184,7 +194,11 @@ impl TimeDimensionSymbol { } pub fn cube_name(&self) -> String { - self.base_symbol.cube_name() + self.compiled_path.cube_name().clone() + } + + pub fn path(&self) -> &Vec { + self.compiled_path.path() } pub fn join_map(&self) -> &Option>> { @@ -223,7 +237,7 @@ impl TimeDimensionSymbol { } pub fn name(&self) -> String { - self.base_symbol.name() + self.compiled_path.name().clone() } pub fn date_range_granularity( From 906c4ec98ea28a977adcca623f251ba00d57a5c1 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Thu, 5 Mar 2026 15:39:24 +0100 Subject: [PATCH 03/21] in work --- .../src/planner/filter/base_segment.rs | 10 ++++------ .../planners/dimension_subquery_planner.rs | 4 ++-- .../src/planner/query_properties.rs | 16 ++++++++-------- .../collectors/join_hints_collector.rs | 7 +------ .../src/planner/sql_evaluator/compiler.rs | 4 ++-- .../symbols/common/compiled_member_path.rs | 19 +++++++++++++++---- .../sql_evaluator/symbols/dimension_symbol.rs | 10 +++------- .../sql_evaluator/symbols/measure_symbol.rs | 11 ++--------- .../symbols/member_expression_symbol.rs | 12 ++++++------ .../sql_evaluator/symbols/member_symbol.rs | 4 ++++ .../symbols/time_dimension_symbol.rs | 7 ++----- .../test_fixtures/test_utils/test_context.rs | 12 +++++------- 12 files changed, 54 insertions(+), 62 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs index f182f35d9a63a..4d5198db52b44 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs @@ -1,6 +1,5 @@ -use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::{ - MemberExpressionExpression, MemberExpressionSymbol, MemberSymbol, SqlCall, + CubeTableSymbol, MemberExpressionExpression, MemberExpressionSymbol, MemberSymbol, SqlCall, }; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::{evaluate_with_context, VisitorContext}; @@ -24,18 +23,17 @@ impl PartialEq for BaseSegment { impl BaseSegment { pub fn try_new( expression: Rc, - cube_name: String, + cube_symbol: Rc, name: String, full_name: Option, - query_tools: Rc, ) -> Result, CubeError> { + let cube_name = cube_symbol.cube_name().clone(); let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), + cube_symbol, name.clone(), MemberExpressionExpression::SqlCall(expression), None, None, - query_tools.base_tools().clone(), vec![cube_name.clone()], )?; let full_name = full_name.unwrap_or(member_expression_symbol.full_name()); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs index 889a7722d9b15..004eee15ef352 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs @@ -83,13 +83,13 @@ impl DimensionSubqueryPlanner { ))); }; + let cube_symbol = self.query_tools.evaluator_compiler().borrow_mut().add_cube_table_evaluator(cube_name.clone())?; let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), + cube_symbol, dim_name.clone(), MemberExpressionExpression::SqlCall(expression), None, None, - self.query_tools.base_tools().clone(), vec![cube_name.clone()], )?; let measure = MemberSymbol::new_member_expression(member_expression_symbol); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index 1135fe5f50131..9fd96f54c50c4 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -149,13 +149,13 @@ impl QueryProperties { ))); } }; + let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), + cube_symbol, name.clone(), MemberExpressionExpression::SqlCall(expression_call), member_expression.static_data().definition.clone(), None, - query_tools.base_tools().clone(), vec![cube_name.clone()], )?; Ok(MemberSymbol::new_member_expression( @@ -260,13 +260,13 @@ impl QueryProperties { } }; + let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), + cube_symbol, name.clone(), expression, member_expression.static_data().definition.clone(), None, - query_tools.base_tools().clone(), vec![cube_name.clone()], )?; Ok(MemberSymbol::new_member_expression(member_expression_symbol)) @@ -301,12 +301,12 @@ impl QueryProperties { .segment_by_path(member_name.clone())?; let expression_evaluator = evaluator_compiler .compile_sql_call(&cube_name, definition.sql()?)?; + let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; BaseSegment::try_new( expression_evaluator, - cube_name, + cube_symbol, name, Some(member_name.clone()), - query_tools.clone(), ) } OptionsMember::MemberExpression(member_expression) => { @@ -333,12 +333,12 @@ impl QueryProperties { ))); } }; + let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; BaseSegment::try_new( expression_evaluator, - cube_name, + cube_symbol, name, None, - query_tools.clone(), ) } }?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index a0b9f248b46ee..74d08a37a1bee 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -102,12 +102,7 @@ pub fn collect_join_hints(node: &Rc) -> Result, visitor.apply(node, &())?; let mut collected_hints = visitor.extract_result(); - let join_map = match node.as_ref() { - MemberSymbol::Dimension(d) => d.join_map(), - MemberSymbol::TimeDimension(d) => d.join_map(), - MemberSymbol::Measure(m) => m.join_map(), - _ => &None, - }; + let join_map = node.join_map(); if let Some(join_map) = join_map { for hint in collected_hints.iter_mut() { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index 4dfd55caa4d50..5abd7fbd939a5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -137,13 +137,13 @@ impl Compiler { let sql_call = self.compile_sql_call(path.cube_name(), definition.sql()?)?; let alias = PlanSqlTemplates::member_alias_name(path.cube_name(), path.symbol_name(), &None); + let cube_symbol = self.add_cube_table_evaluator(path.cube_name().clone())?; let symbol = MemberExpressionSymbol::try_new( - path.cube_name().clone(), + cube_symbol, path.symbol_name().clone(), MemberExpressionExpression::SqlCall(sql_call), None, Some(alias), - self.base_tools.clone(), path.path().clone(), )?; let result = MemberSymbol::new_member_expression(symbol); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs index c0a39d5f03c24..1abc58806e11d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs @@ -1,7 +1,10 @@ +use crate::planner::sql_evaluator::CubeTableSymbol; +use std::rc::Rc; + #[derive(Clone, Debug)] pub struct CompiledMemberPath { + cube: Rc, full_name: String, - cube_name: String, name: String, alias: String, path: Vec, @@ -9,15 +12,15 @@ pub struct CompiledMemberPath { impl CompiledMemberPath { pub fn new( + cube: Rc, full_name: String, - cube_name: String, name: String, alias: String, path: Vec, ) -> Self { Self { + cube, full_name, - cube_name, name, alias, path, @@ -29,7 +32,15 @@ impl CompiledMemberPath { } pub fn cube_name(&self) -> &String { - &self.cube_name + self.cube.cube_name() + } + + pub fn cube(&self) -> &Rc { + &self.cube + } + + pub fn join_map(&self) -> &Option>> { + self.cube.join_map() } pub fn name(&self) -> &String { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index 8268ccf0d057f..4d38f5cfafc9e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -12,7 +12,7 @@ use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::{ sql_nodes::SqlNode, Compiler, CubeRef, SqlCall, SqlEvaluatorVisitor, }; -use crate::planner::sql_evaluator::{CubeTableSymbol, TimeDimensionSymbol}; +use crate::planner::sql_evaluator::TimeDimensionSymbol; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::GranularityHelper; use crate::planner::SqlInterval; @@ -28,7 +28,6 @@ pub struct CalendarDimensionTimeShift { #[derive(Clone)] pub struct DimensionSymbol { - cube: Rc, compiled_path: CompiledMemberPath, kind: DimensionKind, is_reference: bool, // Symbol is a direct reference to another symbol without any calculations @@ -44,7 +43,6 @@ pub struct DimensionSymbol { impl DimensionSymbol { pub fn new( - cube: Rc, compiled_path: CompiledMemberPath, kind: DimensionKind, is_reference: bool, @@ -58,7 +56,6 @@ impl DimensionSymbol { propagate_filters_to_sub_query: bool, ) -> Rc { Rc::new(Self { - cube, compiled_path, kind, is_reference, @@ -242,7 +239,7 @@ impl DimensionSymbol { } pub fn join_map(&self) -> &Option>> { - self.cube.join_map() + self.compiled_path.join_map() } pub fn name(&self) -> String { @@ -477,15 +474,14 @@ impl SymbolFactory for DimensionSymbolFactory { let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; let compiled_path = CompiledMemberPath::new( + cube_symbol, path.full_name().clone(), - path.cube_name().clone(), path.symbol_name().clone(), alias, path.path().clone(), ); let symbol = MemberSymbol::new_dimension(DimensionSymbol::new( - cube_symbol, compiled_path, kind, is_reference, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index 965fe82fecd52..c48b27a144755 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -7,7 +7,6 @@ use crate::cube_bridge::measure_definition::{MeasureDefinition, RollingWindow}; use crate::cube_bridge::member_sql::MemberSql; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::find_owned_by_cube_child; -use crate::planner::sql_evaluator::CubeTableSymbol; use crate::planner::sql_evaluator::{ sql_nodes::SqlNode, Compiler, CubeRef, SqlCall, SqlEvaluatorVisitor, }; @@ -72,7 +71,6 @@ pub enum MeasureTimeShifts { #[derive(Clone)] pub struct MeasureSymbol { - cube: Rc, compiled_path: CompiledMemberPath, kind: MeasureKind, rolling_window: Option, @@ -92,7 +90,6 @@ pub struct MeasureSymbol { impl MeasureSymbol { pub fn new( - cube: Rc, compiled_path: CompiledMemberPath, is_reference: bool, is_view: bool, @@ -109,7 +106,6 @@ impl MeasureSymbol { group_by: Option>>, ) -> Rc { Rc::new(Self { - cube, compiled_path, is_reference, is_view, @@ -145,7 +141,6 @@ impl MeasureSymbol { self.kind.clone() }; Rc::new(Self { - cube: self.cube.clone(), compiled_path: self.compiled_path.clone(), kind, rolling_window: None, @@ -198,7 +193,6 @@ impl MeasureSymbol { measure_filters.extend(add_filters); } Ok(Rc::new(Self { - cube: self.cube.clone(), compiled_path: self.compiled_path.clone(), kind: result_kind, rolling_window: self.rolling_window.clone(), @@ -463,7 +457,7 @@ impl MeasureSymbol { } pub fn join_map(&self) -> &Option>> { - self.cube.join_map() + self.compiled_path.join_map() } pub fn name(&self) -> String { @@ -734,15 +728,14 @@ impl SymbolFactory for MeasureSymbolFactory { let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; let compiled_path = CompiledMemberPath::new( + cube_symbol, path.full_name().clone(), - path.cube_name().clone(), path.symbol_name().clone(), alias, path.path().clone(), ); Ok(MemberSymbol::new_measure(MeasureSymbol::new( - cube_symbol, compiled_path, is_reference, is_view, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs index 7f573e054f8f9..73c33f0523fc3 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs @@ -1,9 +1,10 @@ use super::common::CompiledMemberPath; use super::MemberSymbol; -use crate::cube_bridge::base_tools::BaseTools; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::member_childs; -use crate::planner::sql_evaluator::{sql_nodes::SqlNode, CubeRef, SqlCall, SqlEvaluatorVisitor}; +use crate::planner::sql_evaluator::{ + sql_nodes::SqlNode, CubeRef, CubeTableSymbol, SqlCall, SqlEvaluatorVisitor, +}; use crate::planner::sql_templates::PlanSqlTemplates; use crate::utils::debug::DebugSql; use cubenativeutils::CubeError; @@ -28,21 +29,20 @@ pub struct MemberExpressionSymbol { impl MemberExpressionSymbol { pub fn try_new( - cube_name: String, + cube: Rc, name: String, expression: MemberExpressionExpression, definition: Option, alias: Option, - _base_tools: Rc, path: Vec, ) -> Result, CubeError> { - let full_name = format!("expr:{}.{}", cube_name, name); + let full_name = format!("expr:{}.{}", cube.cube_name(), name); let alias = alias.unwrap_or_else(|| PlanSqlTemplates::alias_name(&name)); let is_reference = match &expression { MemberExpressionExpression::SqlCall(sql_call) => sql_call.is_direct_reference(), MemberExpressionExpression::PatchedSymbol(_symbol) => false, }; - let compiled_path = CompiledMemberPath::new(full_name, cube_name, name, alias, path); + let compiled_path = CompiledMemberPath::new(cube, full_name, name, alias, path); Ok(Rc::new(Self { compiled_path, expression, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index b09ef8cdeb1f9..08c606c9cd256 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -86,6 +86,10 @@ impl MemberSymbol { self.compiled_path().path() } + pub fn join_map(&self) -> &Option>> { + self.compiled_path().join_map() + } + pub fn is_multi_stage(&self) -> bool { match self { Self::Dimension(d) => d.is_multi_stage(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs index 3d5242a113644..b887e3eebccb2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs @@ -34,8 +34,8 @@ impl TimeDimensionSymbol { let full_name = format!("{}_{}", base_symbol.full_name(), name_suffix); let alias = format!("{}_{}", base_symbol.alias(), name_suffix); let compiled_path = CompiledMemberPath::new( + base_symbol.compiled_path().cube().clone(), full_name, - base_symbol.cube_name().clone(), base_symbol.name().clone(), alias, base_symbol.path().clone(), @@ -202,10 +202,7 @@ impl TimeDimensionSymbol { } pub fn join_map(&self) -> &Option>> { - match self.base_symbol.as_ref() { - MemberSymbol::Dimension(d) => d.join_map(), - _ => &None, - } + self.compiled_path.join_map() } pub fn is_multi_stage(&self) -> bool { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 66e4d36d209e8..8e7cef68b22fc 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -87,17 +87,15 @@ impl TestContext { .query_tools .cube_evaluator() .segment_by_path(path.to_string())?; - let expression = self - .query_tools - .evaluator_compiler() - .borrow_mut() - .compile_sql_call(&cube_name, definition.sql()?)?; + let mut compiler = self.query_tools.evaluator_compiler().borrow_mut(); + let expression = compiler.compile_sql_call(&cube_name, definition.sql()?)?; + let cube_symbol = compiler.add_cube_table_evaluator(cube_name.clone())?; + drop(compiler); BaseSegment::try_new( expression, - cube_name, + cube_symbol, name, Some(path.to_string()), - self.query_tools.clone(), ) } From 4382a862bba301819f7316a2d34bc003c5f19871 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Thu, 5 Mar 2026 16:54:07 +0100 Subject: [PATCH 04/21] in work --- .../src/cube_bridge/join_hints.rs | 2 +- .../pre_aggregations_compiler.rs | 21 +++--- .../cubesqlplanner/src/planner/join_hints.rs | 69 +++++++++++++++++++ .../cubesqlplanner/src/planner/mod.rs | 2 + .../planners/dimension_subquery_planner.rs | 3 +- .../src/planner/planners/join_planner.rs | 13 ++-- .../multi_stage/member_query_planner.rs | 3 +- .../multiplied_measures_query_planner.rs | 6 +- .../src/planner/query_properties.rs | 12 ++-- .../cubesqlplanner/src/planner/query_tools.rs | 15 ++-- .../collectors/join_hints_collector.rs | 9 +-- 11 files changed, 119 insertions(+), 36 deletions(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs index b164cb23b6c99..5e8950efa9672 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/join_hints.rs @@ -4,7 +4,7 @@ use cubenativeutils::wrappers::NativeObjectHandle; use cubenativeutils::CubeError; use serde::Serialize; -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Ord, PartialOrd)] pub enum JoinHintItem { Single(String), Vector(Vec), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs index c39cbe8948528..c05aafcbf1a61 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs @@ -1,6 +1,7 @@ use super::CompiledPreAggregation; use super::PreAggregationSource; use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::join_hints::JoinHints; use crate::cube_bridge::member_sql::MemberSql; use crate::cube_bridge::pre_aggregation_description::PreAggregationDescription; use crate::logical_plan::PreAggregationJoin; @@ -342,10 +343,12 @@ impl PreAggregationsCompiler { .cloned() .chain(dimensions.iter().cloned()) .collect_vec(); - let pre_aggr_join_hints = collect_cube_names_from_symbols(&all_symbols)? - .into_iter() - .map(|v| JoinHintItem::Single(v)) - .collect_vec(); + let pre_aggr_join_hints = JoinHints::from_items( + collect_cube_names_from_symbols(&all_symbols)? + .into_iter() + .map(|v| JoinHintItem::Single(v)) + .collect_vec(), + ); let join_planner = JoinPlanner::new(self.query_tools.clone()); let pre_aggrs_for_join = rollups @@ -364,10 +367,12 @@ impl PreAggregationsCompiler { .cloned() .chain(join_pre_aggr.dimensions.iter().cloned()) .collect_vec(); - let join_pre_aggr_join_hints = collect_cube_names_from_symbols(&all_symbols)? - .into_iter() - .map(|v| JoinHintItem::Single(v)) - .collect_vec(); + let join_pre_aggr_join_hints = JoinHints::from_items( + collect_cube_names_from_symbols(&all_symbols)? + .into_iter() + .map(|v| JoinHintItem::Single(v)) + .collect_vec(), + ); existing_joins.append( &mut join_planner.resolve_join_members_by_hints(&join_pre_aggr_join_hints)?, ); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs new file mode 100644 index 0000000000000..826ff7c5768db --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs @@ -0,0 +1,69 @@ +use crate::cube_bridge::join_hints::JoinHintItem; + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct JoinHints { + items: Vec, +} + +impl JoinHints { + pub fn new() -> Self { + Self { items: Vec::new() } + } + + pub fn from_items(mut items: Vec) -> Self { + items.sort(); + items.dedup(); + Self { items } + } + + pub fn insert(&mut self, item: JoinHintItem) { + match self.items.binary_search(&item) { + Ok(_) => {} + Err(pos) => self.items.insert(pos, item), + } + } + + pub fn extend(&mut self, other: &JoinHints) { + for item in other.items.iter() { + self.insert(item.clone()); + } + } + + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } + + pub fn len(&self) -> usize { + self.items.len() + } + + pub fn items(&self) -> &[JoinHintItem] { + &self.items + } + + pub fn iter(&self) -> std::slice::Iter<'_, JoinHintItem> { + self.items.iter() + } + + pub fn into_items(self) -> Vec { + self.items + } +} + +impl IntoIterator for JoinHints { + type Item = JoinHintItem; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.items.into_iter() + } +} + +impl<'a> IntoIterator for &'a JoinHints { + type Item = &'a JoinHintItem; + type IntoIter = std::slice::Iter<'a, JoinHintItem>; + + fn into_iter(self) -> Self::IntoIter { + self.items.iter() + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs index 142eb8be7ba96..90cccfb6104ea 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs @@ -2,6 +2,7 @@ pub mod base_cube; pub mod base_join_condition; pub mod base_query; pub mod filter; +pub mod join_hints; pub mod time_dimension; pub mod params_allocator; @@ -17,6 +18,7 @@ pub mod visitor_context; pub use base_cube::BaseCube; pub use base_join_condition::{BaseJoinCondition, SqlJoinCondition}; pub use base_query::BaseQuery; +pub use join_hints::JoinHints; pub use params_allocator::ParamsAllocator; pub use query_properties::{FullKeyAggregateMeasures, OrderByItem, QueryProperties}; pub use time_dimension::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs index 004eee15ef352..a3b5459216011 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs @@ -1,4 +1,5 @@ use super::{CommonUtils, QueryPlanner}; +use crate::planner::join_hints::JoinHints; use crate::logical_plan::{pretty_print_rc, DimensionSubQuery}; use crate::plan::{FilterItem, QualifiedColumnName}; use crate::planner::query_tools::QueryTools; @@ -123,7 +124,7 @@ impl DimensionSubqueryPlanner { false, false, false, - Rc::new(vec![]), + Rc::new(JoinHints::new()), true, self.query_properties.disable_external_pre_aggregations(), )?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs index 0c0f813f4d192..93b5dc2385a01 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs @@ -1,6 +1,6 @@ use super::CommonUtils; use crate::cube_bridge::join_definition::JoinDefinition; -use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::join_hints::JoinHints; use crate::cube_bridge::join_item::JoinItem; use crate::logical_plan::*; use crate::planner::query_tools::QueryTools; @@ -42,10 +42,13 @@ impl JoinPlanner { pub fn make_join_logical_plan_with_join_hints( &self, - join_hints: Vec, + join_hints: JoinHints, dimension_subqueries: Vec>, ) -> Result, CubeError> { - let join = self.query_tools.join_graph().build_join(join_hints)?; + let join = self + .query_tools + .join_graph() + .build_join(join_hints.into_items())?; self.make_join_logical_plan(join, dimension_subqueries) } @@ -93,12 +96,12 @@ impl JoinPlanner { pub fn resolve_join_members_by_hints( &self, - join_hints: &Vec, + join_hints: &JoinHints, ) -> Result, CubeError> { let join = self .query_tools .join_graph() - .build_join(join_hints.clone())?; + .build_join(join_hints.items().to_vec())?; self.resolve_join_members(join) } pub fn resolve_join_members( diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs index 1d1382903dae6..d58175c3671ca 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs @@ -4,6 +4,7 @@ use super::{ }; use crate::logical_plan::*; use crate::planner::planners::{multi_stage::RollingWindowType, QueryPlanner, SimpleQueryPlanner}; +use crate::planner::join_hints::JoinHints; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::MemberSymbol; use crate::planner::GranularityHelper; @@ -74,7 +75,7 @@ impl MultiStageMemberQueryPlanner { true, false, false, - Rc::new(vec![]), + Rc::new(JoinHints::new()), true, self.query_properties.disable_external_pre_aggregations(), )?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs index a00c333e2bdfe..e96c6644a2b3b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -200,7 +200,7 @@ impl MultipliedMeasuresQueryPlanner { }; let join_hints = collect_join_hints(&measure)?; if cubes.iter().any(|cube| cube != key_cube_name) { - let measures_join = self.query_tools.join_graph().build_join(join_hints)?; + let measures_join = self.query_tools.join_graph().build_join(join_hints.into_items())?; if *measures_join .static_data() .multiplication_factor @@ -230,10 +230,10 @@ impl MultipliedMeasuresQueryPlanner { )?; let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; - let join_hints = collect_join_hints_for_measures(&measures)?; + let measure_join_hints = collect_join_hints_for_measures(&measures)?; let source = self .join_planner - .make_join_logical_plan_with_join_hints(join_hints, subquery_dimension_queries)?; + .make_join_logical_plan_with_join_hints(measure_join_hints, subquery_dimension_queries)?; let schema = LogicalSchema::default() .set_dimensions(primary_keys_dimensions.clone()) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index 9fd96f54c50c4..d11c36baa702d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -1,7 +1,7 @@ use super::filter::compiler::FilterCompiler; use super::filter::BaseSegment; use super::query_tools::QueryTools; -use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::join_hints::JoinHints; use crate::cube_bridge::member_expression::MemberExpressionExpressionDef; use crate::planner::sql_evaluator::{ apply_static_filter_to_filter_item, apply_static_filter_to_symbol, MemberExpressionExpression, @@ -105,7 +105,7 @@ pub struct QueryProperties { multi_fact_join_groups: Vec<(Rc, Vec>)>, pre_aggregation_query: bool, total_query: bool, - query_join_hints: Rc>, + query_join_hints: Rc, allow_multi_stage: bool, disable_external_pre_aggregations: bool, pre_aggregation_id: Option, @@ -407,7 +407,7 @@ impl QueryProperties { }; let ungrouped = options.static_data().ungrouped.unwrap_or(false); - let query_join_hints = Rc::new(options.join_hints()?.unwrap_or_default()); + let query_join_hints = Rc::new(JoinHints::from_items(options.join_hints()?.unwrap_or_default())); let pre_aggregation_query = options.static_data().pre_aggregation_query.unwrap_or(false); let total_query = options.static_data().total_query.unwrap_or(false); @@ -457,7 +457,7 @@ impl QueryProperties { ungrouped: bool, pre_aggregation_query: bool, total_query: bool, - query_join_hints: Rc>, + query_join_hints: Rc, allow_multi_stage: bool, disable_external_pre_aggregations: bool, ) -> Result, CubeError> { @@ -577,7 +577,7 @@ impl QueryProperties { } pub fn compute_join_multi_fact_groups( - query_join_hints: Rc>, + query_join_hints: Rc, query_tools: Rc, measures: &Vec>, dimensions: &Vec>, @@ -730,7 +730,7 @@ impl QueryProperties { self.row_limit } - pub fn query_join_hints(&self) -> &Rc> { + pub fn query_join_hints(&self) -> &Rc { &self.query_join_hints } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs index a32a4be8b465e..48b21dc5351d0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs @@ -5,6 +5,7 @@ use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::join_definition::JoinDefinition; use crate::cube_bridge::join_graph::JoinGraph; use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::join_hints::JoinHints; use crate::cube_bridge::join_item::JoinItemStatic; use crate::cube_bridge::security_context::SecurityContext; use crate::cube_bridge::sql_templates_render::SqlTemplatesRender; @@ -19,8 +20,8 @@ use std::collections::HashMap; use std::rc::Rc; pub struct QueryToolsCachedData { - join_hints: HashMap>>, - join_hints_to_join_key: HashMap>>, Rc>, + join_hints: HashMap>, + join_hints_to_join_key: HashMap>, Rc>, join_key_to_join: HashMap, Rc>, } @@ -42,7 +43,7 @@ impl QueryToolsCachedData { pub fn join_hints_for_member( &mut self, node: &Rc, - ) -> Result>, CubeError> { + ) -> Result, CubeError> { let full_name = node.full_name(); if let Some(val) = self.join_hints.get(&full_name) { Ok(val.clone()) @@ -56,7 +57,7 @@ impl QueryToolsCachedData { pub fn join_hints_for_member_symbol_vec( &mut self, vec: &Vec>, - ) -> Result>>, CubeError> { + ) -> Result>, CubeError> { vec.iter() .map(|b| self.join_hints_for_member(b)) .collect::, _>>() @@ -65,7 +66,7 @@ impl QueryToolsCachedData { pub fn join_hints_for_filter_item_vec( &mut self, vec: &Vec, - ) -> Result>>, CubeError> { + ) -> Result>, CubeError> { let mut member_symbols = Vec::new(); for i in vec.iter() { i.find_all_member_evaluators(&mut member_symbols); @@ -78,7 +79,7 @@ impl QueryToolsCachedData { pub fn join_by_hints( &mut self, - hints: Vec>>, + hints: Vec>, join_fn: impl FnOnce(Vec) -> Result, CubeError>, ) -> Result<(Rc, Rc), CubeError> { if let Some(key) = self.join_hints_to_join_key.get(&hints) { @@ -87,7 +88,7 @@ impl QueryToolsCachedData { let join = join_fn( hints .iter() - .flat_map(|h| h.as_ref().iter().cloned()) + .flat_map(|h| h.iter().cloned()) .collect(), )?; let join_key = Rc::new(JoinKey { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index 74d08a37a1bee..c558e52ba9b3d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -1,4 +1,5 @@ use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::join_hints::JoinHints; use crate::planner::sql_evaluator::{CubeRef, MemberSymbol, TraversalVisitor}; use cubenativeutils::CubeError; use itertools::Itertools; @@ -97,7 +98,7 @@ impl TraversalVisitor for JoinHintsCollector { } } -pub fn collect_join_hints(node: &Rc) -> Result, CubeError> { +pub fn collect_join_hints(node: &Rc) -> Result { let mut visitor = JoinHintsCollector::new(); visitor.apply(node, &())?; let mut collected_hints = visitor.extract_result(); @@ -123,17 +124,17 @@ pub fn collect_join_hints(node: &Rc) -> Result, } } - Ok(collected_hints) + Ok(JoinHints::from_items(collected_hints)) } pub fn collect_join_hints_for_measures( measures: &Vec>, -) -> Result, CubeError> { +) -> Result { let mut visitor = JoinHintsCollector::new(); for meas in measures.iter() { visitor.apply(&meas, &())?; } let res = visitor.extract_result(); - Ok(res) + Ok(JoinHints::from_items(res)) } From 6123c530faed47a9c539760b254d4eb6967fd56e Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Thu, 5 Mar 2026 16:54:23 +0100 Subject: [PATCH 05/21] fmt --- .../pre_aggregations_compiler.rs | 2 +- .../planners/dimension_subquery_planner.rs | 8 +++++-- .../src/planner/planners/join_planner.rs | 2 +- .../multi_stage/member_query_planner.rs | 2 +- .../multiplied_measures_query_planner.rs | 12 ++++++---- .../src/planner/query_properties.rs | 22 +++++++++---------- .../cubesqlplanner/src/planner/query_tools.rs | 9 ++------ .../sql_evaluator/symbols/dimension_symbol.rs | 2 +- .../test_fixtures/test_utils/test_context.rs | 7 +----- 9 files changed, 32 insertions(+), 34 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs index c05aafcbf1a61..22bec416c1f99 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/pre_aggregations_compiler.rs @@ -1,13 +1,13 @@ use super::CompiledPreAggregation; use super::PreAggregationSource; use crate::cube_bridge::join_hints::JoinHintItem; -use crate::planner::join_hints::JoinHints; use crate::cube_bridge::member_sql::MemberSql; use crate::cube_bridge::pre_aggregation_description::PreAggregationDescription; use crate::logical_plan::PreAggregationJoin; use crate::logical_plan::PreAggregationJoinItem; use crate::logical_plan::PreAggregationTable; use crate::logical_plan::PreAggregationUnion; +use crate::planner::join_hints::JoinHints; use crate::planner::planners::JoinPlanner; use crate::planner::planners::ResolvedJoinItem; use crate::planner::query_tools::QueryTools; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs index a3b5459216011..548d0a00660b8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs @@ -1,7 +1,7 @@ use super::{CommonUtils, QueryPlanner}; -use crate::planner::join_hints::JoinHints; use crate::logical_plan::{pretty_print_rc, DimensionSubQuery}; use crate::plan::{FilterItem, QualifiedColumnName}; +use crate::planner::join_hints::JoinHints; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::collect_sub_query_dimensions; use crate::planner::sql_evaluator::{ @@ -84,7 +84,11 @@ impl DimensionSubqueryPlanner { ))); }; - let cube_symbol = self.query_tools.evaluator_compiler().borrow_mut().add_cube_table_evaluator(cube_name.clone())?; + let cube_symbol = self + .query_tools + .evaluator_compiler() + .borrow_mut() + .add_cube_table_evaluator(cube_name.clone())?; let member_expression_symbol = MemberExpressionSymbol::try_new( cube_symbol, dim_name.clone(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs index 93b5dc2385a01..552491b115a9a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs @@ -1,8 +1,8 @@ use super::CommonUtils; use crate::cube_bridge::join_definition::JoinDefinition; -use crate::planner::join_hints::JoinHints; use crate::cube_bridge::join_item::JoinItem; use crate::logical_plan::*; +use crate::planner::join_hints::JoinHints; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::MemberSymbol; use crate::planner::sql_evaluator::SqlCall; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs index d58175c3671ca..ead41dcdff63f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs @@ -3,8 +3,8 @@ use super::{ MultiStageQueryDescription, RollingWindowDescription, TimeSeriesDescription, }; use crate::logical_plan::*; -use crate::planner::planners::{multi_stage::RollingWindowType, QueryPlanner, SimpleQueryPlanner}; use crate::planner::join_hints::JoinHints; +use crate::planner::planners::{multi_stage::RollingWindowType, QueryPlanner, SimpleQueryPlanner}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::MemberSymbol; use crate::planner::GranularityHelper; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs index e96c6644a2b3b..8fa3683dd902c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -200,7 +200,10 @@ impl MultipliedMeasuresQueryPlanner { }; let join_hints = collect_join_hints(&measure)?; if cubes.iter().any(|cube| cube != key_cube_name) { - let measures_join = self.query_tools.join_graph().build_join(join_hints.into_items())?; + let measures_join = self + .query_tools + .join_graph() + .build_join(join_hints.into_items())?; if *measures_join .static_data() .multiplication_factor @@ -231,9 +234,10 @@ impl MultipliedMeasuresQueryPlanner { let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; let measure_join_hints = collect_join_hints_for_measures(&measures)?; - let source = self - .join_planner - .make_join_logical_plan_with_join_hints(measure_join_hints, subquery_dimension_queries)?; + let source = self.join_planner.make_join_logical_plan_with_join_hints( + measure_join_hints, + subquery_dimension_queries, + )?; let schema = LogicalSchema::default() .set_dimensions(primary_keys_dimensions.clone()) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index d11c36baa702d..8865a924eea1e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -1,8 +1,8 @@ use super::filter::compiler::FilterCompiler; use super::filter::BaseSegment; use super::query_tools::QueryTools; -use crate::planner::join_hints::JoinHints; use crate::cube_bridge::member_expression::MemberExpressionExpressionDef; +use crate::planner::join_hints::JoinHints; use crate::planner::sql_evaluator::{ apply_static_filter_to_filter_item, apply_static_filter_to_symbol, MemberExpressionExpression, MemberExpressionSymbol, TimeDimensionSymbol, @@ -149,7 +149,8 @@ impl QueryProperties { ))); } }; - let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; + let cube_symbol = + evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; let member_expression_symbol = MemberExpressionSymbol::try_new( cube_symbol, name.clone(), @@ -301,7 +302,8 @@ impl QueryProperties { .segment_by_path(member_name.clone())?; let expression_evaluator = evaluator_compiler .compile_sql_call(&cube_name, definition.sql()?)?; - let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; + let cube_symbol = + evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; BaseSegment::try_new( expression_evaluator, cube_symbol, @@ -333,13 +335,9 @@ impl QueryProperties { ))); } }; - let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; - BaseSegment::try_new( - expression_evaluator, - cube_symbol, - name, - None, - ) + let cube_symbol = + evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; + BaseSegment::try_new(expression_evaluator, cube_symbol, name, None) } }?; Ok(FilterItem::Segment(segment)) @@ -407,7 +405,9 @@ impl QueryProperties { }; let ungrouped = options.static_data().ungrouped.unwrap_or(false); - let query_join_hints = Rc::new(JoinHints::from_items(options.join_hints()?.unwrap_or_default())); + let query_join_hints = Rc::new(JoinHints::from_items( + options.join_hints()?.unwrap_or_default(), + )); let pre_aggregation_query = options.static_data().pre_aggregation_query.unwrap_or(false); let total_query = options.static_data().total_query.unwrap_or(false); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs index 48b21dc5351d0..ce1ff3c4460cd 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs @@ -5,11 +5,11 @@ use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::join_definition::JoinDefinition; use crate::cube_bridge::join_graph::JoinGraph; use crate::cube_bridge::join_hints::JoinHintItem; -use crate::planner::join_hints::JoinHints; use crate::cube_bridge::join_item::JoinItemStatic; use crate::cube_bridge::security_context::SecurityContext; use crate::cube_bridge::sql_templates_render::SqlTemplatesRender; use crate::plan::FilterItem; +use crate::planner::join_hints::JoinHints; use crate::planner::sql_evaluator::collectors::collect_join_hints; use crate::planner::sql_templates::PlanSqlTemplates; use chrono_tz::Tz; @@ -85,12 +85,7 @@ impl QueryToolsCachedData { if let Some(key) = self.join_hints_to_join_key.get(&hints) { Ok((key.clone(), self.join_key_to_join.get(key).unwrap().clone())) } else { - let join = join_fn( - hints - .iter() - .flat_map(|h| h.iter().cloned()) - .collect(), - )?; + let join = join_fn(hints.iter().flat_map(|h| h.iter().cloned()).collect())?; let join_key = Rc::new(JoinKey { root: join.static_data().root.to_string(), joins: join diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index 4d38f5cfafc9e..67c35c9f633b8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -9,10 +9,10 @@ use crate::cube_bridge::dimension_definition::DimensionDefinition; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::member_sql::MemberSql; use crate::planner::query_tools::QueryTools; +use crate::planner::sql_evaluator::TimeDimensionSymbol; use crate::planner::sql_evaluator::{ sql_nodes::SqlNode, Compiler, CubeRef, SqlCall, SqlEvaluatorVisitor, }; -use crate::planner::sql_evaluator::TimeDimensionSymbol; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::GranularityHelper; use crate::planner::SqlInterval; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 8e7cef68b22fc..168e9cd6fb04f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -91,12 +91,7 @@ impl TestContext { let expression = compiler.compile_sql_call(&cube_name, definition.sql()?)?; let cube_symbol = compiler.add_cube_table_evaluator(cube_name.clone())?; drop(compiler); - BaseSegment::try_new( - expression, - cube_symbol, - name, - Some(path.to_string()), - ) + BaseSegment::try_new(expression, cube_symbol, name, Some(path.to_string())) } #[allow(dead_code)] From 328b7c5522e8881920235da1195408127712d1fe Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Thu, 5 Mar 2026 16:59:56 +0100 Subject: [PATCH 06/21] fmt --- .../cubesqlplanner/src/planner/join_hints.rs | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs index 826ff7c5768db..6dfbd34c2589e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs @@ -67,3 +67,87 @@ impl<'a> IntoIterator for &'a JoinHints { self.items.iter() } } + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + fn s(name: &str) -> JoinHintItem { + JoinHintItem::Single(name.to_string()) + } + + fn v(names: &[&str]) -> JoinHintItem { + JoinHintItem::Vector(names.iter().map(|n| n.to_string()).collect()) + } + + fn hash_of(h: &JoinHints) -> u64 { + let mut hasher = DefaultHasher::new(); + h.hash(&mut hasher); + hasher.finish() + } + + #[test] + fn test_from_items_normalizes_and_deduplicates() { + let hints = JoinHints::from_items(vec![ + s("orders"), + v(&["users", "orders"]), + s("orders"), + s("abc"), + ]); + + assert_eq!(hints.len(), 3); + // sorted: Single comes before Vector for same content, and Singles are alphabetical + assert_eq!(hints.items()[0], s("abc")); + assert_eq!(hints.items()[1], s("orders")); + assert_eq!(hints.items()[2], v(&["users", "orders"])); + + // Different insertion order → same result + let hints2 = JoinHints::from_items(vec![ + s("abc"), + v(&["users", "orders"]), + s("orders"), + ]); + assert_eq!(hints, hints2); + assert_eq!(hash_of(&hints), hash_of(&hints2)); + } + + #[test] + fn test_insert_and_extend_preserve_invariant() { + let mut a = JoinHints::new(); + assert!(a.is_empty()); + + a.insert(s("orders")); + a.insert(s("abc")); + a.insert(s("orders")); // duplicate + assert_eq!(a.len(), 2); + assert_eq!(a.items()[0], s("abc")); + assert_eq!(a.items()[1], s("orders")); + + let b = JoinHints::from_items(vec![s("orders"), v(&["a", "b"]), s("zzz")]); + a.extend(&b); + assert_eq!(a.len(), 4); + // abc, orders, zzz (Singles sorted), then Vector + assert_eq!(a.items()[0], s("abc")); + assert_eq!(a.items()[1], s("orders")); + assert_eq!(a.items()[2], s("zzz")); + assert_eq!(a.items()[3], v(&["a", "b"])); + } + + #[test] + fn test_into_items_and_into_iter() { + let hints = JoinHints::from_items(vec![s("b"), s("a"), v(&["x", "y"])]); + let cloned = hints.clone(); + + // into_iter + let collected: Vec<_> = cloned.into_iter().collect(); + assert_eq!(collected.len(), 3); + + // into_items + let items = hints.into_items(); + assert_eq!(items.len(), 3); + assert_eq!(items[0], s("a")); + assert_eq!(items[1], s("b")); + } +} From 30a7693ae1d83668437737d8517b0c566a53e63f Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 6 Mar 2026 18:12:47 +0100 Subject: [PATCH 07/21] in work --- .../cubesqlplanner/src/planner/join_hints.rs | 6 +- .../src/tests/join_hints_collector.rs | 85 +++++++++++++++++++ .../cubesqlplanner/src/tests/mod.rs | 1 + 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs index 6dfbd34c2589e..109b22a671c6a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/join_hints.rs @@ -104,11 +104,7 @@ mod tests { assert_eq!(hints.items()[2], v(&["users", "orders"])); // Different insertion order → same result - let hints2 = JoinHints::from_items(vec![ - s("abc"), - v(&["users", "orders"]), - s("orders"), - ]); + let hints2 = JoinHints::from_items(vec![s("abc"), v(&["users", "orders"]), s("orders")]); assert_eq!(hints, hints2); assert_eq!(hash_of(&hints), hash_of(&hints2)); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs new file mode 100644 index 0000000000000..c59b2b0816591 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs @@ -0,0 +1,85 @@ +use crate::cube_bridge::join_hints::JoinHintItem; +use crate::planner::sql_evaluator::collectors::{ + collect_join_hints, collect_join_hints_for_measures, +}; +use crate::test_fixtures::cube_bridge::MockSchema; +use crate::test_fixtures::test_utils::TestContext; + +fn s(name: &str) -> JoinHintItem { + JoinHintItem::Single(name.to_string()) +} + +fn v(names: &[&str]) -> JoinHintItem { + JoinHintItem::Vector(names.iter().map(|n| n.to_string()).collect()) +} + +#[test] +fn test_collect_join_hints_single_cube() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + + let dim = ctx.create_dimension("orders.status").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("orders")]); + + let measure = ctx.create_measure("orders.count").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("orders")]); + + let measure = ctx.create_measure("customers.payments").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("customers")]); +} + +#[test] +fn test_collect_join_hints_cross_cube_measure() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + + let measure = ctx.create_measure("customers.payments_per_order").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 2); + assert!(hints.items().contains(&s("customers"))); + assert!(hints.items().contains(&s("orders"))); +} + +#[test] +fn test_collect_join_hints_view_symbols() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/diamond_joins.yaml")).unwrap(); + + let dim = ctx.create_dimension("a_with_b_and_c.code").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[v(&["cube_a", "cube_b", "cube_c"])]); + + let dim = ctx.create_dimension("a_with_b_and_c.name").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("cube_a")]); + + // View measure from root join_path + let measure = ctx.create_measure("a_with_b_and_c.total_value").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("cube_a")]); +} + +#[test] +fn test_collect_join_hints_for_measures_multiple() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + + let m1 = ctx.create_measure("orders.count").unwrap(); + let m2 = ctx.create_measure("customers.count").unwrap(); + let hints = collect_join_hints_for_measures(&vec![m1, m2]).unwrap(); + + assert_eq!(hints.len(), 2); + assert!(hints.items().contains(&s("orders"))); + assert!(hints.items().contains(&s("customers"))); + + let m1 = ctx.create_measure("orders.count").unwrap(); + let m2 = ctx.create_measure("orders.max_amount").unwrap(); + let hints = collect_join_hints_for_measures(&vec![m1, m2]).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("orders")]); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs index 376256b06b7d3..50b638cbe8079 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs @@ -1,6 +1,7 @@ mod common_sql_generation; mod cube_evaluator; mod dimension_symbol; +mod join_hints_collector; mod measure_symbol; mod pre_aggregation_sql_generation; mod utils; From 4abbffb454d62cd449bb8a6977420503d4f08268 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 6 Mar 2026 19:25:53 +0100 Subject: [PATCH 08/21] in work --- .../collectors/calc_group_dims_collector.rs | 3 +- .../collectors/cube_names_collector.rs | 5 +- .../collectors/has_cumulative_members.rs | 1 - .../collectors/has_multi_stage_members.rs | 1 - .../collectors/join_hints_collector.rs | 13 ++--- .../collectors/member_childs_collector.rs | 1 - .../multiplied_measures_collector.rs | 2 - .../collectors/sub_query_dimensions.rs | 3 +- .../src/planner/sql_evaluator/compiler.rs | 20 ++++---- .../src/planner/sql_evaluator/sql_call.rs | 15 ------ .../planner/sql_evaluator/sql_call_builder.rs | 48 ++++--------------- .../sql_evaluator/symbols/common/case.rs | 36 -------------- .../symbols/dimension_kinds/case_dimension.rs | 9 ---- .../symbols/dimension_kinds/geo.rs | 7 --- .../symbols/dimension_kinds/mod.rs | 9 ---- .../symbols/dimension_kinds/regular.rs | 6 --- .../symbols/dimension_kinds/switch.rs | 8 ---- .../sql_evaluator/symbols/dimension_symbol.rs | 4 -- .../symbols/measure_kinds/aggregated.rs | 8 ---- .../symbols/measure_kinds/calculated.rs | 8 ---- .../symbols/measure_kinds/count.rs | 13 ----- .../symbols/measure_kinds/mod.rs | 9 ---- .../sql_evaluator/symbols/measure_symbol.rs | 17 ------- .../symbols/member_expression_symbol.rs | 13 ----- .../sql_evaluator/symbols/member_symbol.rs | 9 ---- .../symbols/time_dimension_symbol.rs | 12 ----- .../src/planner/sql_evaluator/visitor.rs | 16 ++----- .../src/tests/common_sql_generation.rs | 2 +- 28 files changed, 35 insertions(+), 263 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs index 9dc6e187dfe95..072c1eb19999a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs @@ -27,7 +27,6 @@ impl TraversalVisitor for CalcGroupDimsCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _: &Self::State, ) -> Result, CubeError> { match node.as_ref() { @@ -38,7 +37,7 @@ impl TraversalVisitor for CalcGroupDimsCollector { } } MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), path, &()) + return self.on_node_traverse(e.base_symbol(), &()) } MemberSymbol::Measure(_) => {} MemberSymbol::MemberExpression(_) => {} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs index 085ed8bc872c0..194379f31f8b8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs @@ -24,12 +24,12 @@ impl TraversalVisitor for CubeNamesCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _: &Self::State, ) -> Result, CubeError> { match node.as_ref() { MemberSymbol::Dimension(e) => { if !e.is_view() { + let path = node.path(); if !path.is_empty() { for p in path { self.names.insert(p.clone()); @@ -43,10 +43,11 @@ impl TraversalVisitor for CubeNamesCollector { } } MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), path, &()) + return self.on_node_traverse(e.base_symbol(), &()) } MemberSymbol::Measure(e) => { if !e.is_view() { + let path = node.path(); if !path.is_empty() { for p in path { self.names.insert(p.clone()); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs index 5848fc779ce17..15059c729e572 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_cumulative_members.rs @@ -23,7 +23,6 @@ impl TraversalVisitor for HasCumulativeMembersCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, _: &Self::State, ) -> Result, CubeError> { if let MemberSymbol::Measure(s) = node.as_ref() { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs index 4e092c62162a3..360555be0fd14 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/has_multi_stage_members.rs @@ -25,7 +25,6 @@ impl TraversalVisitor for HasMultiStageMembersCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, _: &Self::State, ) -> Result, CubeError> { match node.as_ref() { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index c558e52ba9b3d..e5f9ff457ddb9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -24,7 +24,6 @@ impl TraversalVisitor for JoinHintsCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _: &Self::State, ) -> Result, CubeError> { if node.is_multi_stage() { @@ -34,10 +33,10 @@ impl TraversalVisitor for JoinHintsCollector { self.apply(item, &())?; } } - for (dep, path) in dim.get_dependencies_with_path().into_iter() { + for dep in dim.get_dependencies().into_iter() { if let Ok(dim) = dep.as_dimension() { if dim.is_multi_stage() { - self.on_node_traverse(&dep, &path, &())?; + self.on_node_traverse(&dep, &())?; } } } @@ -48,6 +47,7 @@ impl TraversalVisitor for JoinHintsCollector { match node.as_ref() { MemberSymbol::Dimension(e) => { if !e.is_view() { + let path = node.path(); if !path.is_empty() { if path.len() == 1 { self.hints.push(JoinHintItem::Single(path[0].clone())) @@ -62,11 +62,11 @@ impl TraversalVisitor for JoinHintsCollector { return Ok(None); } } - MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), path, &()) - } + MemberSymbol::TimeDimension(e) => return self.on_node_traverse(e.base_symbol(), &()), MemberSymbol::Measure(e) => { if !e.is_view() { + let path = node.path(); + println!("!!! path: {:?}", path); if !path.is_empty() { if path.len() == 1 { self.hints.push(JoinHintItem::Single(path[0].clone())) @@ -123,6 +123,7 @@ pub fn collect_join_hints(node: &Rc) -> Result, - _path: &Vec, state: &Self::State, ) -> Result, CubeError> { if state.is_root { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs index 4762a85d9d905..dcfc235564cf0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/multiplied_measures_collector.rs @@ -36,7 +36,6 @@ impl TraversalVisitor for CompositeMeasuresCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, state: &Self::State, ) -> Result, CubeError> { let res = match node.as_ref() { @@ -90,7 +89,6 @@ impl TraversalVisitor for MultipliedMeasuresCollector { fn on_node_traverse( &mut self, node: &Rc, - _path: &Vec, _: &Self::State, ) -> Result, CubeError> { let res = match node.as_ref() { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs index 3f95e194bc3e8..436d948472cb5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/sub_query_dimensions.rs @@ -38,7 +38,6 @@ impl TraversalVisitor for SubQueryDimensionsCollector { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, _state: &Self::State, ) -> Result, CubeError> { match node.as_ref() { @@ -54,7 +53,7 @@ impl TraversalVisitor for SubQueryDimensionsCollector { } Ok(Some(())) } - MemberSymbol::TimeDimension(dim) => self.on_node_traverse(dim.base_symbol(), path, &()), + MemberSymbol::TimeDimension(dim) => self.on_node_traverse(dim.base_symbol(), &()), _ => Ok(Some(())), } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index 5abd7fbd939a5..ebacde133adf9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -59,9 +59,9 @@ impl Compiler { ) -> Result, CubeError> { let path = SymbolPath::parse(self.cube_evaluator.clone(), &name)?; match path.path_type() { - SymbolPathType::Dimension => self.add_dimension_evaluator_impl(path), - SymbolPathType::Measure => self.add_measure_evaluator_impl(path), - SymbolPathType::Segment => self.add_segment_evaluator_impl(path), + SymbolPathType::Dimension => self.add_dimension_evaluator_by_path(path), + SymbolPathType::Measure => self.add_measure_evaluator_by_path(path), + SymbolPathType::Segment => self.add_segment_evaluator_by_path(path), _ => Err(CubeError::internal(format!( "Cannot auto-resolve {}. Only dimensions, measures and segments", name @@ -74,10 +74,10 @@ impl Compiler { measure: String, ) -> Result, CubeError> { let path = SymbolPath::parse(self.cube_evaluator.clone(), &measure)?; - self.add_measure_evaluator_impl(path) + self.add_measure_evaluator_by_path(path) } - fn add_measure_evaluator_impl( + pub fn add_measure_evaluator_by_path( &mut self, path: SymbolPath, ) -> Result, CubeError> { @@ -98,15 +98,15 @@ impl Compiler { let path = SymbolPath::parse(self.cube_evaluator.clone(), &dimension)?; match path.path_type() { SymbolPathType::Segment => { - let symbol = self.add_segment_evaluator_impl(path)?; + let symbol = self.add_segment_evaluator_by_path(path)?; let me = symbol.as_member_expression()?; Ok(MemberSymbol::new_member_expression(me.with_parenthesized())) } - _ => self.add_dimension_evaluator_impl(path), + _ => self.add_dimension_evaluator_by_path(path), } } - fn add_dimension_evaluator_impl( + pub fn add_dimension_evaluator_by_path( &mut self, path: SymbolPath, ) -> Result, CubeError> { @@ -122,10 +122,10 @@ impl Compiler { pub fn add_segment_evaluator(&mut self, name: String) -> Result, CubeError> { let path = SymbolPath::parse(self.cube_evaluator.clone(), &name)?; - self.add_segment_evaluator_impl(path) + self.add_segment_evaluator_by_path(path) } - fn add_segment_evaluator_impl( + pub fn add_segment_evaluator_by_path( &mut self, path: SymbolPath, ) -> Result, CubeError> { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs index c6989cefc838d..992f58e2861f7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs @@ -419,13 +419,6 @@ impl SqlCall { .collect() } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - self.deps - .iter() - .filter_map(|d| d.symbol.as_symbol().map(|s| (s.clone(), d.path.clone()))) - .collect() - } - pub fn extract_symbol_deps(&self, result: &mut Vec>) { for dep in self.deps.iter() { if let Some(s) = dep.symbol.as_symbol() { @@ -434,14 +427,6 @@ impl SqlCall { } } - pub fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - for dep in self.deps.iter() { - if let Some(s) = dep.symbol.as_symbol() { - result.push((s.clone(), dep.path.clone())) - } - } - } - pub fn extract_cube_refs(&self, result: &mut Vec) { for dep in self.deps.iter() { if let SqlDependency::CubeRef(cr) = &dep.symbol { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs index ef26733d96617..d7586e4e0317b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs @@ -111,48 +111,18 @@ impl<'a> SqlCallBuilder<'a> { match symbol_path.path_type() { SymbolPathType::Dimension => { - if let Some(granularity) = symbol_path.granularity().clone() { - let base = self - .compiler - .add_dimension_evaluator(symbol_path.full_name().clone())?; - let granularity_obj = GranularityHelper::make_granularity_obj( - self.cube_evaluator.clone(), - self.compiler, - symbol_path.cube_name(), - symbol_path.symbol_name(), - Some(granularity.clone()), - )?; - if let Some(granularity_obj) = granularity_obj { - let time_dim = MemberSymbol::new_time_dimension(TimeDimensionSymbol::new( - base, - Some(granularity), - Some(granularity_obj), - None, - )); - Ok(SqlCallDependency { - path, - symbol: SqlDependency::Symbol(time_dim), - }) - } else { - Err(CubeError::user(format!( - "Invalid granularity: {}", - granularity - ))) - } - } else { - let member = self - .compiler - .add_dimension_evaluator(symbol_path.full_name().clone())?; - Ok(SqlCallDependency { - path, - symbol: SqlDependency::Symbol(member), - }) - } + let member = self + .compiler + .add_dimension_evaluator_by_path(symbol_path.clone())?; + Ok(SqlCallDependency { + path, + symbol: SqlDependency::Symbol(member), + }) } SymbolPathType::Measure => { let member = self .compiler - .add_measure_evaluator(symbol_path.full_name().clone())?; + .add_measure_evaluator_by_path(symbol_path.clone())?; Ok(SqlCallDependency { path, symbol: SqlDependency::Symbol(member), @@ -161,7 +131,7 @@ impl<'a> SqlCallBuilder<'a> { SymbolPathType::Segment => { let member = self .compiler - .add_segment_evaluator(symbol_path.full_name().clone())?; + .add_segment_evaluator_by_path(symbol_path.clone())?; Ok(SqlCallDependency { path, symbol: SqlDependency::Symbol(member), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs index 4066a50df1857..eaaf7bcdd966c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/case.rs @@ -52,18 +52,6 @@ impl CaseDefinition { sql.extract_symbol_deps(result); } } - fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - for itm in self.items.iter() { - itm.sql.extract_symbol_deps_with_path(result); - if let CaseLabel::Sql(sql) = &itm.label { - sql.extract_symbol_deps_with_path(result); - } - } - if let CaseLabel::Sql(sql) = &self.else_label { - sql.extract_symbol_deps_with_path(result); - } - } - fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, @@ -130,13 +118,6 @@ impl CaseSwitchItem { } } - fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - match self { - CaseSwitchItem::Sql(sql_call) => sql_call.extract_symbol_deps_with_path(result), - CaseSwitchItem::Member(member_symbol) => result.push((member_symbol.clone(), vec![])), - } - } - fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, @@ -247,16 +228,6 @@ impl CaseSwitchDefinition { else_sql.extract_symbol_deps(result); } } - fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - self.switch.extract_symbol_deps_with_path(result); - for itm in self.items.iter() { - itm.sql.extract_symbol_deps_with_path(result); - } - if let Some(else_sql) = &self.else_sql { - else_sql.extract_symbol_deps_with_path(result); - } - } - fn get_switch_values(&self) -> Option> { if let CaseSwitchItem::Member(member) = &self.switch { if let Ok(switch_dim) = member.as_dimension() { @@ -411,13 +382,6 @@ impl Case { Case::CaseSwitch(def) => def.extract_symbol_deps(result), } } - pub fn extract_symbol_deps_with_path(&self, result: &mut Vec<(Rc, Vec)>) { - match self { - Case::Case(def) => def.extract_symbol_deps_with_path(result), - Case::CaseSwitch(def) => def.extract_symbol_deps_with_path(result), - } - } - pub fn case_switch_dimension(&self) -> Option> { if let Case::CaseSwitch(case) = &self { if let CaseSwitchItem::Member(member) = &case.switch { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs index d903bd2a1795d..b84df2734a140 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/case_dimension.rs @@ -69,15 +69,6 @@ impl CaseDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(member_sql) = &self.member_sql { - member_sql.extract_symbol_deps_with_path(&mut deps); - } - self.case.extract_symbol_deps_with_path(&mut deps); - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs index 30096c1b5ed17..c5ebd21e700a8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/geo.rs @@ -32,13 +32,6 @@ impl GeoDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - self.latitude.extract_symbol_deps_with_path(&mut deps); - self.longitude.extract_symbol_deps_with_path(&mut deps); - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs index 0e7e8040ecccd..7ad346fcbff29 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/mod.rs @@ -58,15 +58,6 @@ impl DimensionKind { } } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - match self { - Self::Regular(r) => r.get_dependencies_with_path(), - Self::Geo(g) => g.get_dependencies_with_path(), - Self::Switch(s) => s.get_dependencies_with_path(), - Self::Case(c) => c.get_dependencies_with_path(), - } - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs index edcf8a549c088..c1a88920a70df 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/regular.rs @@ -45,12 +45,6 @@ impl RegularDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - self.member_sql.extract_symbol_deps_with_path(&mut deps); - deps - } - pub fn get_cube_refs(&self) -> Vec { self.member_sql.get_cube_refs() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs index 917e0e0c7b124..a8a8e2ed98bd2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_kinds/switch.rs @@ -51,14 +51,6 @@ impl SwitchDimension { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(member_sql) = &self.member_sql { - member_sql.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index 67c35c9f633b8..d60813f692567 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -226,10 +226,6 @@ impl DimensionSymbol { self.kind.get_dependencies() } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - self.kind.get_dependencies_with_path() - } - pub fn get_cube_refs(&self) -> Vec { self.kind.get_cube_refs() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs index b04208e28a6ca..0a4593bd28567 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/aggregated.rs @@ -58,14 +58,6 @@ impl AggregatedMeasure { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(sql) = &self.member_sql { - sql.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs index d5909956f85ef..95e3b3b32dab8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/calculated.rs @@ -86,14 +86,6 @@ impl CalculatedMeasure { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(sql) = &self.member_sql { - sql.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs index d49bfb849ed3f..d43b9e9d9ca17 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/count.rs @@ -72,19 +72,6 @@ impl CountMeasure { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - match &self.sql { - CountSql::Explicit(sql) => sql.extract_symbol_deps_with_path(&mut deps), - CountSql::Auto(pk_sqls) => { - for pk in pk_sqls { - pk.extract_symbol_deps_with_path(&mut deps); - } - } - } - deps - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs index 7c75d4580194f..5e2ba9ad77d1c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_kinds/mod.rs @@ -90,15 +90,6 @@ impl MeasureKind { } } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - match self { - Self::Count(c) => c.get_dependencies_with_path(), - Self::Aggregated(a) => a.get_dependencies_with_path(), - Self::Calculated(c) => c.get_dependencies_with_path(), - Self::Rank => vec![], - } - } - pub fn apply_to_deps) -> Result, CubeError>>( &self, f: &F, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index c48b27a144755..a4b64cfd4e202 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -339,23 +339,6 @@ impl MeasureSymbol { refs } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = self.kind.get_dependencies_with_path(); - for filter in self.measure_filters.iter() { - filter.extract_symbol_deps_with_path(&mut deps); - } - for filter in self.measure_drill_filters.iter() { - filter.extract_symbol_deps_with_path(&mut deps); - } - for order in self.measure_order_by.iter() { - order.sql_call().extract_symbol_deps_with_path(&mut deps); - } - if let Some(case) = &self.case { - case.extract_symbol_deps_with_path(&mut deps); - } - deps - } - pub fn can_used_as_addictive_in_multplied(&self) -> bool { match &self.kind { MeasureKind::Aggregated(agg) => agg.agg_type().is_distinct(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs index 73c33f0523fc3..8fe955ed2da57 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs @@ -137,19 +137,6 @@ impl MemberExpressionSymbol { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - match &self.expression { - MemberExpressionExpression::SqlCall(sql_call) => { - sql_call.extract_symbol_deps_with_path(&mut deps) - } - MemberExpressionExpression::PatchedSymbol(member_symbol) => { - deps.push((member_symbol.clone(), vec![])) - } - } - deps - } - pub fn get_cube_refs(&self) -> Vec { let mut refs = vec![]; match &self.expression { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index 08c606c9cd256..1769833ffce82 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -147,15 +147,6 @@ impl MemberSymbol { } } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - match self { - Self::Dimension(d) => d.get_dependencies_with_path(), - Self::TimeDimension(d) => d.get_dependencies_with_path(), - Self::Measure(m) => m.get_dependencies_with_path(), - Self::MemberExpression(e) => e.get_dependencies_with_path(), - } - } - pub fn get_cube_refs(&self) -> Vec { match self { Self::Dimension(d) => d.get_cube_refs(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs index b887e3eebccb2..fa2f90e1a8d2d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs @@ -170,18 +170,6 @@ impl TimeDimensionSymbol { deps } - pub fn get_dependencies_with_path(&self) -> Vec<(Rc, Vec)> { - let mut deps = vec![]; - if let Some(granularity_obj) = &self.granularity_obj { - if let Some(calendar_sql) = granularity_obj.calendar_sql() { - calendar_sql.extract_symbol_deps_with_path(&mut deps); - } - } - - deps.append(&mut self.base_symbol.get_dependencies_with_path()); - deps - } - pub fn get_cube_refs(&self) -> Vec { let mut refs = vec![]; if let Some(granularity_obj) = &self.granularity_obj { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs index df1c324c37c4f..26cba72a006b5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs @@ -7,7 +7,6 @@ pub trait TraversalVisitor { fn on_node_traverse( &mut self, node: &Rc, - path: &Vec, state: &Self::State, ) -> Result, CubeError>; @@ -16,18 +15,9 @@ pub trait TraversalVisitor { } fn apply(&mut self, node: &Rc, state: &Self::State) -> Result<(), CubeError> { - self.apply_with_path(node, &vec![], state) - } - - fn apply_with_path( - &mut self, - node: &Rc, - path: &Vec, - state: &Self::State, - ) -> Result<(), CubeError> { - if let Some(state) = self.on_node_traverse(node, path, state)? { - for (dep, dep_path) in node.get_dependencies_with_path() { - self.apply_with_path(&dep, &dep_path, &state)? + if let Some(state) = self.on_node_traverse(node, state)? { + for dep in node.get_dependencies() { + self.apply(&dep, &state)? } for cube_ref in node.get_cube_refs() { self.on_cube_ref(&cube_ref, &state)?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs index b9f8fc7dcfa88..a89f7694b5cde 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs @@ -33,7 +33,7 @@ fn test_simple_paths_in_request_sql() { let query_yaml = indoc! {" measures: - - cube_c.cube_a.count + - cube_a.count dimensions: - cube_a.cube_c.code "}; From 333adfb332675d5a013c723291f5f30c7fb5936b Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 6 Mar 2026 19:36:49 +0100 Subject: [PATCH 09/21] in work --- .../src/planner/sql_evaluator/sql_call_builder.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs index d7586e4e0317b..bbaa4dab67c66 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs @@ -1,4 +1,3 @@ -use super::symbols::MemberSymbol; use super::Compiler; use super::{ CubeRef, SqlCall, SqlCallDependency, SqlCallFilterGroupItem, SqlCallFilterParamsItem, @@ -8,8 +7,6 @@ use crate::cube_bridge::base_tools::BaseTools; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::member_sql::*; use crate::cube_bridge::security_context::SecurityContext; -use crate::planner::sql_evaluator::TimeDimensionSymbol; -use crate::planner::GranularityHelper; use cubenativeutils::CubeError; use std::rc::Rc; From e9083230777c3d5908c39736147fe995052822a0 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Fri, 6 Mar 2026 19:43:04 +0100 Subject: [PATCH 10/21] in work --- .../collectors/join_hints_collector.rs | 2 -- .../src/tests/common_sql_generation.rs | 29 +++++++++++++++++++ ...on__diamond_join_over_direct_path_sql.snap | 10 +++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__common_sql_generation__diamond_join_over_direct_path_sql.snap diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index e5f9ff457ddb9..1911bf320b883 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -66,7 +66,6 @@ impl TraversalVisitor for JoinHintsCollector { MemberSymbol::Measure(e) => { if !e.is_view() { let path = node.path(); - println!("!!! path: {:?}", path); if !path.is_empty() { if path.len() == 1 { self.hints.push(JoinHintItem::Single(path[0].clone())) @@ -123,7 +122,6 @@ pub fn collect_join_hints(node: &Rc) -> Result Date: Sun, 8 Mar 2026 12:24:50 +0100 Subject: [PATCH 11/21] in work --- .../multiplied_measures_query_planner.rs | 2 +- .../collectors/join_hints_collector.rs | 1 + .../cube_bridge/mock_join_graph.rs | 4 +- .../src/test_fixtures/cube_bridge/mod.rs | 1 + .../yaml_files/common/many_to_one_views.yaml | 65 +++++++ .../test_fixtures/test_utils/test_context.rs | 17 ++ .../src/tests/member_expressions_on_views.rs | 172 ++++++++++++++++++ .../cubesqlplanner/src/tests/mod.rs | 1 + 8 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs index 8fa3683dd902c..3d25f17e91f88 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -210,7 +210,7 @@ impl MultipliedMeasuresQueryPlanner { .get(key_cube_name) .unwrap_or(&false) { - return Err(CubeError::user(format!("{}' references cubes that lead to row multiplication. Please rewrite it using sub query.", measure.full_name()))); + return Err(CubeError::user(format!("{}' references cubes ({}) that lead to row multiplication. Please rewrite it using sub query.", measure.full_name(), cubes.join(", ")))); } return Ok(true); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index 1911bf320b883..5b5a354eceb99 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -123,6 +123,7 @@ pub fn collect_join_hints(node: &Rc) -> Result bool { let relationship = &join.join.static_data().relationship; - (join.from == cube && relationship == "hasMany") - || (join.to == cube && relationship == "belongsTo") + (join.from == cube && (relationship == "hasMany" || relationship == "one_to_many")) + || (join.to == cube && (relationship == "belongsTo" || relationship == "many_to_one")) } pub(crate) fn find_multiplication_factor_for(&self, cube: &str, joins: &[JoinEdge]) -> bool { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs index 6c0bc53e8cbb3..c468058ab1294 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/mod.rs @@ -56,6 +56,7 @@ pub use mock_join_graph::MockJoinGraph; pub use mock_join_item::MockJoinItem; pub use mock_join_item_definition::MockJoinItemDefinition; pub use mock_measure_definition::MockMeasureDefinition; +pub use mock_member_expression_definition::MockMemberExpressionDefinition; pub use mock_member_order_by::MockMemberOrderBy; pub use mock_member_sql::MockMemberSql; pub use mock_pre_aggregation_description::MockPreAggregationDescription; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml new file mode 100644 index 0000000000000..0d9aaf1faa1f5 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/schemas/yaml_files/common/many_to_one_views.yaml @@ -0,0 +1,65 @@ +cubes: + - name: many_to_one_root + sql: "SELECT * FROM many_to_one_root" + joins: + - name: many_to_one_child + relationship: many_to_one + sql: "{many_to_one_root}.child_id = {many_to_one_child.id}" + dimensions: + - name: id + type: number + sql: id + primary_key: true + - name: child_id + type: number + sql: child_id + - name: root_dim + type: string + sql: dim + - name: root_test_dim + type: string + sql: test_dim + measures: + - name: root_val_sum + type: sum + sql: val + - name: root_val_avg + type: avg + sql: val + + - name: many_to_one_child + sql: "SELECT * FROM many_to_one_child" + dimensions: + - name: id + type: number + sql: id + primary_key: true + - name: child_dim + type: string + sql: dim + - name: child_test_dim + type: string + sql: test_dim + measures: + - name: child_val_sum + type: sum + sql: val + - name: child_val_avg + type: avg + sql: val + +views: + - name: many_to_one_view + cubes: + - join_path: many_to_one_root + includes: + - root_dim + - root_test_dim + - root_val_sum + - root_val_avg + - join_path: many_to_one_root.many_to_one_child + includes: + - child_dim + - child_test_dim + - child_val_sum + - child_val_avg diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 168e9cd6fb04f..13a46a109b983 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -53,6 +53,11 @@ impl TestContext { &self.query_tools } + #[allow(dead_code)] + pub fn security_context(&self) -> &Rc { + &self.security_context + } + #[allow(dead_code)] pub fn create_symbol(&self, member_path: &str) -> Result, CubeError> { self.query_tools @@ -256,6 +261,18 @@ impl TestContext { let (sql, _) = self.build_sql_with_used_pre_aggregations(query)?; Ok(sql) } + + #[allow(dead_code)] + pub fn build_sql_from_options( + &self, + options: Rc, + ) -> Result { + let request = QueryProperties::try_new(self.query_tools.clone(), options)?; + let planner = TopLevelPlanner::new(request, self.query_tools.clone(), false); + let (sql, _) = planner.plan()?; + Ok(sql) + } + pub fn build_sql_with_used_pre_aggregations( &self, query: &str, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs new file mode 100644 index 0000000000000..fe5d37796d349 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs @@ -0,0 +1,172 @@ +use crate::cube_bridge::member_expression::MemberExpressionExpressionDef; +use crate::cube_bridge::member_sql::MemberSql; +use crate::cube_bridge::options_member::OptionsMember; +use crate::test_fixtures::cube_bridge::{ + members_from_strings, MockBaseQueryOptions, MockMemberExpressionDefinition, MockMemberSql, + MockSchema, +}; +use crate::test_fixtures::test_utils::TestContext; +use indoc::indoc; +use std::rc::Rc; + +fn create_test_context() -> TestContext { + let schema = MockSchema::from_yaml_file("common/many_to_one_views.yaml"); + TestContext::new(schema).unwrap() +} + +fn make_member_expression(expression_name: &str, cube_name: &str, sql: &str) -> OptionsMember { + let member_sql: Rc = Rc::new(MockMemberSql::new(sql).unwrap()); + let expr = MockMemberExpressionDefinition::builder() + .expression_name(Some(expression_name.to_string())) + .name(Some(expression_name.to_string())) + .cube_name(Some(cube_name.to_string())) + .expression(MemberExpressionExpressionDef::Sql(member_sql)) + .build(); + OptionsMember::MemberExpression(Rc::new(expr)) +} + +fn build_query_with_member_expression( + ctx: &TestContext, + extra_measure: OptionsMember, +) -> Result { + let mut measures = members_from_strings(vec![ + "many_to_one_view.root_val_avg", + "many_to_one_view.child_val_avg", + ]); + measures.push(extra_measure); + + let options = Rc::new( + MockBaseQueryOptions::builder() + .cube_evaluator(ctx.query_tools().cube_evaluator().clone()) + .base_tools(ctx.query_tools().base_tools().clone()) + .join_graph(ctx.query_tools().join_graph().clone()) + .security_context(ctx.security_context().clone()) + .measures(Some(measures)) + .dimensions(Some(members_from_strings(vec![ + "many_to_one_view.root_dim", + "many_to_one_view.child_dim", + ]))) + .build(), + ); + + ctx.build_sql_from_options(options) +} + +/// Base query with avg measures from both cubes through the view. +/// This is the root cause: many_to_one_child measures get multiplied +/// when joined with many_to_one_root. +#[test] +fn test_many_to_one_view_base_query() { + let ctx = create_test_context(); + + let query_yaml = indoc! {" + measures: + - many_to_one_view.root_val_avg + - many_to_one_view.child_val_avg + dimensions: + - many_to_one_view.root_dim + - many_to_one_view.child_dim + "}; + + let result = ctx.build_sql(query_yaml); + assert!(result.is_err(), "Expected row multiplication error"); + let err = result.unwrap_err().to_string(); + assert!( + err.contains("row multiplication"), + "Expected 'row multiplication' in error, got: {}", + err + ); +} + +/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > one_sum +/// Expression: SUM(1) +#[test] +fn test_many_to_one_view_one_sum() { + let ctx = create_test_context(); + let expr = make_member_expression("one_sum", "many_to_one_view", "SUM(1)"); + let result = build_query_with_member_expression(&ctx, expr).unwrap(); +} + +/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > root_val_sum +/// Expression: ${many_to_one_view.root_val_sum} +#[test] +fn test_many_to_one_view_root_val_sum() { + let ctx = create_test_context(); + let expr = make_member_expression( + "root_val_sum_expr", + "many_to_one_view", + "{many_to_one_view.root_val_sum}", + ); + let result = build_query_with_member_expression(&ctx, expr); + + assert!(result.is_err(), "Expected row multiplication error"); + let err = result.unwrap_err().to_string(); + assert!( + err.contains("row multiplication"), + "Expected 'row multiplication' in error, got: {}", + err + ); +} + +/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > root_distinct_dim +/// Expression: COUNT(DISTINCT ${many_to_one_view.root_test_dim}) +#[test] +fn test_many_to_one_view_root_distinct_dim() { + let ctx = create_test_context(); + let expr = make_member_expression( + "root_distinct_dim", + "many_to_one_view", + "COUNT(DISTINCT {many_to_one_view.root_test_dim})", + ); + let result = build_query_with_member_expression(&ctx, expr); + + assert!(result.is_err(), "Expected row multiplication error"); + let err = result.unwrap_err().to_string(); + assert!( + err.contains("row multiplication"), + "Expected 'row multiplication' in error, got: {}", + err + ); +} + +/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > child_val_sum +/// Expression: ${many_to_one_view.child_val_sum} +#[test] +fn test_many_to_one_view_child_val_sum() { + let ctx = create_test_context(); + let expr = make_member_expression( + "child_val_sum_expr", + "many_to_one_view", + "{many_to_one_view.child_val_sum}", + ); + let result = build_query_with_member_expression(&ctx, expr); + + assert!(result.is_err(), "Expected row multiplication error"); + let err = result.unwrap_err().to_string(); + assert!( + err.contains("row multiplication"), + "Expected 'row multiplication' in error, got: {}", + err + ); +} + +/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > child_distinct_dim +/// Expression: COUNT(DISTINCT ${many_to_one_view.child_test_dim}) +#[test] +fn test_many_to_one_view_child_distinct_dim() { + let ctx = create_test_context(); + let expr = make_member_expression( + "child_distinct_dim", + "many_to_one_view", + "COUNT(DISTINCT {many_to_one_view.child_test_dim})", + ); + let result = build_query_with_member_expression(&ctx, expr); + + assert!(result.is_err(), "Expected row multiplication error"); + let err = result.unwrap_err().to_string(); + assert!( + err.contains("row multiplication"), + "Expected 'row multiplication' in error, got: {}", + err + ); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs index 50b638cbe8079..54227421eaf4d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs @@ -3,5 +3,6 @@ mod cube_evaluator; mod dimension_symbol; mod join_hints_collector; mod measure_symbol; +mod member_expressions_on_views; mod pre_aggregation_sql_generation; mod utils; From 2e6e563cf3b1890440e9a9540ca4d8eec343dfcb Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 13:31:16 +0100 Subject: [PATCH 12/21] in work --- .../collectors/join_hints_collector.rs | 1 - .../src/tests/cube_names_collector.rs | 66 ++++++++++++++++ .../src/tests/join_hints_collector.rs | 79 +++++++++++++++++++ .../cubesqlplanner/src/tests/mod.rs | 1 + 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index 5b5a354eceb99..1911bf320b883 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -123,7 +123,6 @@ pub fn collect_join_hints(node: &Rc) -> Result TestContext { + TestContext::new(MockSchema::from_yaml_file("common/many_to_one_views.yaml")).unwrap() +} + +#[test] +fn test_cube_names_single_cube_dimension() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + let dim = ctx.create_dimension("orders.status").unwrap(); + let names = collect_cube_names(&dim).unwrap(); + assert_eq!(names, vec!["orders"]); +} + +#[test] +fn test_cube_names_single_cube_measure() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + let measure = ctx.create_measure("orders.count").unwrap(); + let names = collect_cube_names(&measure).unwrap(); + assert_eq!(names, vec!["orders"]); +} + +#[test] +fn test_cube_names_cross_cube_measure() { + let ctx = TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap(); + let measure = ctx.create_measure("customers.payments_per_order").unwrap(); + let mut names = collect_cube_names(&measure).unwrap(); + names.sort(); + assert_eq!(names, vec!["customers", "orders"]); +} + +#[test] +fn test_cube_names_many_to_one_view_root_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.root_dim").unwrap(); + let names = collect_cube_names(&dim).unwrap(); + assert_eq!(names, vec!["many_to_one_root"]); +} + +#[test] +fn test_cube_names_many_to_one_view_child_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.child_dim").unwrap(); + let mut names = collect_cube_names(&dim).unwrap(); + names.sort(); + assert_eq!(names, vec!["many_to_one_child", "many_to_one_root"]); +} + +#[test] +fn test_cube_names_many_to_one_view_root_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx.create_measure("many_to_one_view.root_val_avg").unwrap(); + let names = collect_cube_names(&measure).unwrap(); + assert_eq!(names, vec!["many_to_one_root"]); +} + +#[test] +fn test_cube_names_many_to_one_view_child_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx.create_measure("many_to_one_view.child_val_avg").unwrap(); + let mut names = collect_cube_names(&measure).unwrap(); + names.sort(); + assert_eq!(names, vec!["many_to_one_child", "many_to_one_root"]); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs index c59b2b0816591..85e5ec700d285 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs @@ -4,6 +4,7 @@ use crate::planner::sql_evaluator::collectors::{ }; use crate::test_fixtures::cube_bridge::MockSchema; use crate::test_fixtures::test_utils::TestContext; +use indoc::indoc; fn s(name: &str) -> JoinHintItem { JoinHintItem::Single(name.to_string()) @@ -83,3 +84,81 @@ fn test_collect_join_hints_for_measures_multiple() { assert_eq!(hints.len(), 1); assert_eq!(hints.items(), &[s("orders")]); } + +// --- many_to_one view tests --- + +fn many_to_one_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/many_to_one_views.yaml")).unwrap() +} + +#[test] +fn test_join_hints_many_to_one_view_root_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.root_dim").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("many_to_one_root")]); +} + +#[test] +fn test_join_hints_many_to_one_view_child_dim() { + let ctx = many_to_one_ctx(); + let dim = ctx.create_dimension("many_to_one_view.child_dim").unwrap(); + let hints = collect_join_hints(&dim).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!( + hints.items(), + &[v(&["many_to_one_root", "many_to_one_child"])] + ); +} + +#[test] +fn test_join_hints_many_to_one_view_root_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx.create_measure("many_to_one_view.root_val_avg").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!(hints.items(), &[s("many_to_one_root")]); +} + +#[test] +fn test_join_hints_many_to_one_view_child_measure() { + let ctx = many_to_one_ctx(); + let measure = ctx.create_measure("many_to_one_view.child_val_avg").unwrap(); + let hints = collect_join_hints(&measure).unwrap(); + assert_eq!(hints.len(), 1); + assert_eq!( + hints.items(), + &[v(&["many_to_one_root", "many_to_one_child"])] + ); +} + +#[test] +fn test_join_hints_many_to_one_view_combined_measures() { + let ctx = many_to_one_ctx(); + let m1 = ctx.create_measure("many_to_one_view.root_val_avg").unwrap(); + let m2 = ctx.create_measure("many_to_one_view.child_val_avg").unwrap(); + let hints = collect_join_hints_for_measures(&vec![m1, m2]).unwrap(); + assert_eq!(hints.len(), 2); + assert!(hints.items().contains(&s("many_to_one_root"))); + assert!(hints.items().contains(&v(&["many_to_one_root", "many_to_one_child"]))); +} + +#[test] +fn test_many_to_one_view_build_sql() { + let ctx = many_to_one_ctx(); + let query = indoc! {" + measures: + - many_to_one_view.root_val_avg + - many_to_one_view.child_val_avg + dimensions: + - many_to_one_view.root_dim + - many_to_one_view.child_dim + "}; + let result = ctx.build_sql(query); + match &result { + Ok(sql) => println!("SQL generated:\n{}", sql), + Err(e) => println!("Error: {}", e), + } + assert!(result.is_ok(), "Should generate SQL without row multiplication error: {:?}", result.err()); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs index 54227421eaf4d..bf39171a1abdb 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs @@ -1,5 +1,6 @@ mod common_sql_generation; mod cube_evaluator; +mod cube_names_collector; mod dimension_symbol; mod join_hints_collector; mod measure_symbol; From efc0636f9775d5a628d1fe714102bbbdd702d40d Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 13:57:29 +0100 Subject: [PATCH 13/21] in work --- .../multiplied_measures_query_planner.rs | 7 ++- .../symbols/common/compiled_member_path.rs | 12 ++++ .../sql_evaluator/symbols/dimension_symbol.rs | 4 ++ .../sql_evaluator/symbols/measure_symbol.rs | 4 ++ .../symbols/member_expression_symbol.rs | 4 ++ .../sql_evaluator/symbols/member_symbol.rs | 27 +++++++++ .../symbols/time_dimension_symbol.rs | 4 ++ .../src/tests/member_expressions_on_views.rs | 59 +++++-------------- 8 files changed, 75 insertions(+), 46 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs index 3d25f17e91f88..4a048fa8df809 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -187,8 +187,9 @@ impl MultipliedMeasuresQueryPlanner { key_cube_name: &String, ) -> Result { for measure in measures.iter() { + let owned_measure = measure.with_own_path(); let member_expression_over_dimensions_cubes = - if let Ok(member_expression) = measure.as_member_expression() { + if let Ok(member_expression) = owned_measure.as_member_expression() { member_expression.cube_names_if_dimension_only_expression()? } else { None @@ -196,9 +197,9 @@ impl MultipliedMeasuresQueryPlanner { let cubes = if let Some(cubes) = member_expression_over_dimensions_cubes { cubes } else { - collect_cube_names(&measure)? + collect_cube_names(&owned_measure)? }; - let join_hints = collect_join_hints(&measure)?; + let join_hints = collect_join_hints(&owned_measure)?; if cubes.iter().any(|cube| cube != key_cube_name) { let measures_join = self .query_tools diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs index 1abc58806e11d..bb156860b1956 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs @@ -54,4 +54,16 @@ impl CompiledMemberPath { pub fn path(&self) -> &Vec { &self.path } + + /// Returns a copy with the path reduced to just the owning cube, + /// stripping any join chain prefix from views or other contexts. + pub fn own_path(&self) -> Self { + Self { + cube: self.cube.clone(), + full_name: self.full_name.clone(), + name: self.name.clone(), + alias: self.alias.clone(), + path: vec![self.cube_name().clone()], + } + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index d60813f692567..49fd74f41e4d1 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -138,6 +138,10 @@ impl DimensionSymbol { &self.compiled_path } + pub fn set_own_path(&mut self) { + self.compiled_path = self.compiled_path.own_path(); + } + pub fn full_name(&self) -> String { self.compiled_path.full_name().clone() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index a4b64cfd4e202..754d7d8539b8a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -221,6 +221,10 @@ impl MeasureSymbol { &self.compiled_path } + pub fn set_own_path(&mut self) { + self.compiled_path = self.compiled_path.own_path(); + } + pub fn full_name(&self) -> String { self.compiled_path.full_name().clone() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs index 8fe955ed2da57..77d50de8125ff 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs @@ -84,6 +84,10 @@ impl MemberExpressionSymbol { &self.compiled_path } + pub fn set_own_path(&mut self) { + self.compiled_path = self.compiled_path.own_path(); + } + pub fn full_name(&self) -> String { self.compiled_path.full_name().clone() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index 1769833ffce82..31e755fb42d75 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -197,6 +197,33 @@ impl MemberSymbol { false } + /// Returns a copy of this symbol with the path reduced to just the owning cube, + /// stripping any join chain prefix from views or other contexts. + pub fn with_own_path(&self) -> Rc { + match self { + Self::Dimension(d) => { + let mut new = (**d).clone(); + new.set_own_path(); + Rc::new(Self::Dimension(Rc::new(new))) + } + Self::TimeDimension(d) => { + let mut new = (**d).clone(); + new.set_own_path(); + Rc::new(Self::TimeDimension(Rc::new(new))) + } + Self::Measure(m) => { + let mut new = (**m).clone(); + new.set_own_path(); + Rc::new(Self::Measure(Rc::new(new))) + } + Self::MemberExpression(e) => { + let mut new = (**e).clone(); + new.set_own_path(); + Rc::new(Self::MemberExpression(Rc::new(new))) + } + } + } + pub fn owned_by_cube(&self) -> bool { match self { Self::Dimension(d) => d.owned_by_cube(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs index fa2f90e1a8d2d..3da971c2bc202 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs @@ -104,6 +104,10 @@ impl TimeDimensionSymbol { &self.compiled_path } + pub fn set_own_path(&mut self) { + self.compiled_path = self.compiled_path.own_path(); + } + pub fn full_name(&self) -> String { self.compiled_path.full_name().clone() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs index fe5d37796d349..cbe82d087a9f6 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs @@ -52,9 +52,6 @@ fn build_query_with_member_expression( ctx.build_sql_from_options(options) } -/// Base query with avg measures from both cubes through the view. -/// This is the root cause: many_to_one_child measures get multiplied -/// when joined with many_to_one_root. #[test] fn test_many_to_one_view_base_query() { let ctx = create_test_context(); @@ -69,26 +66,20 @@ fn test_many_to_one_view_base_query() { "}; let result = ctx.build_sql(query_yaml); - assert!(result.is_err(), "Expected row multiplication error"); - let err = result.unwrap_err().to_string(); assert!( - err.contains("row multiplication"), - "Expected 'row multiplication' in error, got: {}", - err + result.is_ok(), + "Should generate SQL without row multiplication error: {:?}", + result.err() ); } -/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > one_sum -/// Expression: SUM(1) #[test] fn test_many_to_one_view_one_sum() { let ctx = create_test_context(); let expr = make_member_expression("one_sum", "many_to_one_view", "SUM(1)"); - let result = build_query_with_member_expression(&ctx, expr).unwrap(); + let _result = build_query_with_member_expression(&ctx, expr).unwrap(); } -/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > root_val_sum -/// Expression: ${many_to_one_view.root_val_sum} #[test] fn test_many_to_one_view_root_val_sum() { let ctx = create_test_context(); @@ -98,18 +89,13 @@ fn test_many_to_one_view_root_val_sum() { "{many_to_one_view.root_val_sum}", ); let result = build_query_with_member_expression(&ctx, expr); - - assert!(result.is_err(), "Expected row multiplication error"); - let err = result.unwrap_err().to_string(); assert!( - err.contains("row multiplication"), - "Expected 'row multiplication' in error, got: {}", - err + result.is_ok(), + "Should generate SQL without row multiplication error: {:?}", + result.err() ); } -/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > root_distinct_dim -/// Expression: COUNT(DISTINCT ${many_to_one_view.root_test_dim}) #[test] fn test_many_to_one_view_root_distinct_dim() { let ctx = create_test_context(); @@ -119,18 +105,13 @@ fn test_many_to_one_view_root_distinct_dim() { "COUNT(DISTINCT {many_to_one_view.root_test_dim})", ); let result = build_query_with_member_expression(&ctx, expr); - - assert!(result.is_err(), "Expected row multiplication error"); - let err = result.unwrap_err().to_string(); assert!( - err.contains("row multiplication"), - "Expected 'row multiplication' in error, got: {}", - err + result.is_ok(), + "Should generate SQL without row multiplication error: {:?}", + result.err() ); } -/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > child_val_sum -/// Expression: ${many_to_one_view.child_val_sum} #[test] fn test_many_to_one_view_child_val_sum() { let ctx = create_test_context(); @@ -140,18 +121,13 @@ fn test_many_to_one_view_child_val_sum() { "{many_to_one_view.child_val_sum}", ); let result = build_query_with_member_expression(&ctx, expr); - - assert!(result.is_err(), "Expected row multiplication error"); - let err = result.unwrap_err().to_string(); assert!( - err.contains("row multiplication"), - "Expected 'row multiplication' in error, got: {}", - err + result.is_ok(), + "Should generate SQL without row multiplication error: {:?}", + result.err() ); } -/// Reproduces: member-expressions-on-views.test.js > many_to_one_view > child_distinct_dim -/// Expression: COUNT(DISTINCT ${many_to_one_view.child_test_dim}) #[test] fn test_many_to_one_view_child_distinct_dim() { let ctx = create_test_context(); @@ -161,12 +137,9 @@ fn test_many_to_one_view_child_distinct_dim() { "COUNT(DISTINCT {many_to_one_view.child_test_dim})", ); let result = build_query_with_member_expression(&ctx, expr); - - assert!(result.is_err(), "Expected row multiplication error"); - let err = result.unwrap_err().to_string(); assert!( - err.contains("row multiplication"), - "Expected 'row multiplication' in error, got: {}", - err + result.is_ok(), + "Should generate SQL without row multiplication error: {:?}", + result.err() ); } From 1084b1f7d90c981866f30927c7d3c37c39143dae Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 14:04:03 +0100 Subject: [PATCH 14/21] in work --- .../src/tests/join_hints_collector.rs | 8 +--- .../src/tests/member_expressions_on_views.rs | 47 ++++++------------- ...ector__many_to_one_view_build_sql.snap.new | 27 +++++++++++ ...iews__many_to_one_view_base_query.snap.new | 27 +++++++++++ ...ny_to_one_view_child_distinct_dim.snap.new | 27 +++++++++++ ...s__many_to_one_view_child_val_sum.snap.new | 27 +++++++++++ ...n_views__many_to_one_view_one_sum.snap.new | 27 +++++++++++ ...any_to_one_view_root_distinct_dim.snap.new | 27 +++++++++++ ...ws__many_to_one_view_root_val_sum.snap.new | 27 +++++++++++ 9 files changed, 205 insertions(+), 39 deletions(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs index 85e5ec700d285..4c0fb2001c316 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs @@ -155,10 +155,6 @@ fn test_many_to_one_view_build_sql() { - many_to_one_view.root_dim - many_to_one_view.child_dim "}; - let result = ctx.build_sql(query); - match &result { - Ok(sql) => println!("SQL generated:\n{}", sql), - Err(e) => println!("Error: {}", e), - } - assert!(result.is_ok(), "Should generate SQL without row multiplication error: {:?}", result.err()); + let sql = ctx.build_sql(query).unwrap(); + insta::assert_snapshot!(sql); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs index cbe82d087a9f6..d025eb37341c6 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs @@ -28,7 +28,7 @@ fn make_member_expression(expression_name: &str, cube_name: &str, sql: &str) -> fn build_query_with_member_expression( ctx: &TestContext, extra_measure: OptionsMember, -) -> Result { +) -> String { let mut measures = members_from_strings(vec![ "many_to_one_view.root_val_avg", "many_to_one_view.child_val_avg", @@ -49,7 +49,7 @@ fn build_query_with_member_expression( .build(), ); - ctx.build_sql_from_options(options) + ctx.build_sql_from_options(options).unwrap() } #[test] @@ -65,19 +65,16 @@ fn test_many_to_one_view_base_query() { - many_to_one_view.child_dim "}; - let result = ctx.build_sql(query_yaml); - assert!( - result.is_ok(), - "Should generate SQL without row multiplication error: {:?}", - result.err() - ); + let sql = ctx.build_sql(query_yaml).unwrap(); + insta::assert_snapshot!(sql); } #[test] fn test_many_to_one_view_one_sum() { let ctx = create_test_context(); let expr = make_member_expression("one_sum", "many_to_one_view", "SUM(1)"); - let _result = build_query_with_member_expression(&ctx, expr).unwrap(); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); } #[test] @@ -88,12 +85,8 @@ fn test_many_to_one_view_root_val_sum() { "many_to_one_view", "{many_to_one_view.root_val_sum}", ); - let result = build_query_with_member_expression(&ctx, expr); - assert!( - result.is_ok(), - "Should generate SQL without row multiplication error: {:?}", - result.err() - ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); } #[test] @@ -104,12 +97,8 @@ fn test_many_to_one_view_root_distinct_dim() { "many_to_one_view", "COUNT(DISTINCT {many_to_one_view.root_test_dim})", ); - let result = build_query_with_member_expression(&ctx, expr); - assert!( - result.is_ok(), - "Should generate SQL without row multiplication error: {:?}", - result.err() - ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); } #[test] @@ -120,12 +109,8 @@ fn test_many_to_one_view_child_val_sum() { "many_to_one_view", "{many_to_one_view.child_val_sum}", ); - let result = build_query_with_member_expression(&ctx, expr); - assert!( - result.is_ok(), - "Should generate SQL without row multiplication error: {:?}", - result.err() - ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); } #[test] @@ -136,10 +121,6 @@ fn test_many_to_one_view_child_distinct_dim() { "many_to_one_view", "COUNT(DISTINCT {many_to_one_view.child_test_dim})", ); - let result = build_query_with_member_expression(&ctx, expr); - assert!( - result.is_ok(), - "Should generate SQL without row multiplication error: {:?}", - result.err() - ); + let sql = build_query_with_member_expression(&ctx, expr); + insta::assert_snapshot!(sql); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new new file mode 100644 index 0000000000000..28d726a998262 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new @@ -0,0 +1,27 @@ +--- +source: cubesqlplanner/src/tests/join_hints_collector.rs +assertion_line: 159 +expression: sql +snapshot_kind: text +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new new file mode 100644 index 0000000000000..dcb1996ca210f --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new @@ -0,0 +1,27 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +assertion_line: 69 +expression: sql +snapshot_kind: text +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new new file mode 100644 index 0000000000000..2a3135ef657c8 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new @@ -0,0 +1,27 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +assertion_line: 125 +expression: sql +snapshot_kind: text +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_1"."child_distinct_dim" "child_distinct_dim" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg", COUNT(DISTINCT "many_to_one_child_key_many_to_one_child".test_dim) "child_distinct_dim" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new new file mode 100644 index 0000000000000..8bec4108b53d5 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new @@ -0,0 +1,27 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +assertion_line: 113 +expression: sql +snapshot_kind: text +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_1"."many_to_one_child__child_val_sum" "child_val_sum_expr" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg", sum("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_sum" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new new file mode 100644 index 0000000000000..4a1178278f3ef --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new @@ -0,0 +1,27 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +assertion_line: 77 +expression: sql +snapshot_kind: text +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."one_sum" "one_sum" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg", SUM(1) "one_sum" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new new file mode 100644 index 0000000000000..bb7799bfd1a1f --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new @@ -0,0 +1,27 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +assertion_line: 101 +expression: sql +snapshot_kind: text +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."root_distinct_dim" "root_distinct_dim" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg", COUNT(DISTINCT "many_to_one_root".test_dim) "root_distinct_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new new file mode 100644 index 0000000000000..fa206163ad6cb --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new @@ -0,0 +1,27 @@ +--- +source: cubesqlplanner/src/tests/member_expressions_on_views.rs +assertion_line: 89 +expression: sql +snapshot_kind: text +--- +SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."many_to_one_root__root_val_sum" "root_val_sum_expr" +FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" +FROM (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2 + UNION ALL +SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "pk_aggregate_keys_source") AS "fk_aggregate_keys" +LEFT JOIN (SELECT "many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child".dim "many_to_one_view__child_dim", avg("many_to_one_root".val) "many_to_one_root__root_val_avg", sum("many_to_one_root".val) "many_to_one_root__root_val_sum" +FROM many_to_one_root AS "many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child" ON "many_to_one_root".child_id = "many_to_one_child".id +GROUP BY 1, 2) AS "q_0" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_0"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_0"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_0"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_0"."many_to_one_view__child_dim" IS NULL)))) +LEFT JOIN (SELECT "keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", avg("many_to_one_child_key_many_to_one_child".val) "many_to_one_child__child_val_avg" +FROM (SELECT DISTINCT "many_to_one_child_key_many_to_one_root".dim "many_to_one_view__root_dim", "many_to_one_child_key_many_to_one_child".dim "many_to_one_view__child_dim", "many_to_one_child_key_many_to_one_child".id "many_to_one_child__id" +FROM many_to_one_root AS "many_to_one_child_key_many_to_one_root" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON "many_to_one_child_key_many_to_one_root".child_id = "many_to_one_child_key_many_to_one_child".id) AS "keys" +LEFT JOIN many_to_one_child AS "many_to_one_child_key_many_to_one_child" ON (("keys"."many_to_one_child__id" = "many_to_one_child_key_many_to_one_child".id)) +GROUP BY 1, 2) AS "q_1" ON (("fk_aggregate_keys"."many_to_one_view__root_dim" = "q_1"."many_to_one_view__root_dim" OR (("fk_aggregate_keys"."many_to_one_view__root_dim" IS NULL) AND ("q_1"."many_to_one_view__root_dim" IS NULL)))) AND (("fk_aggregate_keys"."many_to_one_view__child_dim" = "q_1"."many_to_one_view__child_dim" OR (("fk_aggregate_keys"."many_to_one_view__child_dim" IS NULL) AND ("q_1"."many_to_one_view__child_dim" IS NULL)))) +ORDER BY 3 DESC From ee8e4e5ea9edeed9e740212df66921943a1eb644 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 14:04:36 +0100 Subject: [PATCH 15/21] in work --- ...ests__join_hints_collector__many_to_one_view_build_sql.snap} | 2 -- ...mber_expressions_on_views__many_to_one_view_base_query.snap} | 2 -- ...ressions_on_views__many_to_one_view_child_distinct_dim.snap} | 2 -- ...r_expressions_on_views__many_to_one_view_child_val_sum.snap} | 2 -- ..._member_expressions_on_views__many_to_one_view_one_sum.snap} | 2 -- ...pressions_on_views__many_to_one_view_root_distinct_dim.snap} | 2 -- ...er_expressions_on_views__many_to_one_view_root_val_sum.snap} | 2 -- 7 files changed, 14 deletions(-) rename rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/{cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new => cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap} (98%) rename rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/{cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new => cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap} (98%) rename rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/{cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new => cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap} (98%) rename rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/{cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new => cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap} (98%) rename rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/{cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new => cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap} (98%) rename rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/{cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new => cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap} (98%) rename rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/{cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new => cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap} (98%) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap similarity index 98% rename from rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new rename to rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap index 28d726a998262..86239d66cc267 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap.new +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__join_hints_collector__many_to_one_view_build_sql.snap @@ -1,8 +1,6 @@ --- source: cubesqlplanner/src/tests/join_hints_collector.rs -assertion_line: 159 expression: sql -snapshot_kind: text --- SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg" FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap similarity index 98% rename from rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new rename to rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap index dcb1996ca210f..5a3fb46b49556 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap.new +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_base_query.snap @@ -1,8 +1,6 @@ --- source: cubesqlplanner/src/tests/member_expressions_on_views.rs -assertion_line: 69 expression: sql -snapshot_kind: text --- SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg" FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap similarity index 98% rename from rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new rename to rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap index 2a3135ef657c8..052fb0141cd91 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap.new +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_distinct_dim.snap @@ -1,8 +1,6 @@ --- source: cubesqlplanner/src/tests/member_expressions_on_views.rs -assertion_line: 125 expression: sql -snapshot_kind: text --- SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_1"."child_distinct_dim" "child_distinct_dim" FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap similarity index 98% rename from rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new rename to rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap index 8bec4108b53d5..3233c3ade620f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap.new +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_child_val_sum.snap @@ -1,8 +1,6 @@ --- source: cubesqlplanner/src/tests/member_expressions_on_views.rs -assertion_line: 113 expression: sql -snapshot_kind: text --- SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_1"."many_to_one_child__child_val_sum" "child_val_sum_expr" FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap similarity index 98% rename from rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new rename to rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap index 4a1178278f3ef..f8276ecd8f7c3 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap.new +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_one_sum.snap @@ -1,8 +1,6 @@ --- source: cubesqlplanner/src/tests/member_expressions_on_views.rs -assertion_line: 77 expression: sql -snapshot_kind: text --- SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."one_sum" "one_sum" FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap similarity index 98% rename from rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new rename to rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap index bb7799bfd1a1f..f0d250eaf4881 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap.new +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_distinct_dim.snap @@ -1,8 +1,6 @@ --- source: cubesqlplanner/src/tests/member_expressions_on_views.rs -assertion_line: 101 expression: sql -snapshot_kind: text --- SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."root_distinct_dim" "root_distinct_dim" FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap similarity index 98% rename from rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new rename to rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap index fa206163ad6cb..44771f9c5fc77 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap.new +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/snapshots/cubesqlplanner__tests__member_expressions_on_views__many_to_one_view_root_val_sum.snap @@ -1,8 +1,6 @@ --- source: cubesqlplanner/src/tests/member_expressions_on_views.rs -assertion_line: 89 expression: sql -snapshot_kind: text --- SELECT "fk_aggregate_keys"."many_to_one_view__root_dim" "many_to_one_view__root_dim", "fk_aggregate_keys"."many_to_one_view__child_dim" "many_to_one_view__child_dim", "q_0"."many_to_one_root__root_val_avg" "many_to_one_view__root_val_avg", "q_1"."many_to_one_child__child_val_avg" "many_to_one_view__child_val_avg", "q_0"."many_to_one_root__root_val_sum" "root_val_sum_expr" FROM (SELECT DISTINCT "many_to_one_view__root_dim" "many_to_one_view__root_dim", "many_to_one_view__child_dim" "many_to_one_view__child_dim" From 4e3a53f2f13ba1dcb8645041d7cbc47d5d4faf9d Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 14:05:15 +0100 Subject: [PATCH 16/21] in work --- .../collectors/calc_group_dims_collector.rs | 4 +--- .../sql_evaluator/collectors/cube_names_collector.rs | 4 +--- .../src/test_fixtures/test_utils/test_context.rs | 4 +++- .../cubesqlplanner/src/tests/cube_names_collector.rs | 4 +++- .../cubesqlplanner/src/tests/join_hints_collector.rs | 12 +++++++++--- .../src/tests/member_expressions_on_views.rs | 5 +---- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs index 072c1eb19999a..b8e00ff721e3c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/calc_group_dims_collector.rs @@ -36,9 +36,7 @@ impl TraversalVisitor for CalcGroupDimsCollector { return Ok(None); } } - MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), &()) - } + MemberSymbol::TimeDimension(e) => return self.on_node_traverse(e.base_symbol(), &()), MemberSymbol::Measure(_) => {} MemberSymbol::MemberExpression(_) => {} }; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs index 194379f31f8b8..34d4082858989 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs @@ -42,9 +42,7 @@ impl TraversalVisitor for CubeNamesCollector { return Ok(None); } } - MemberSymbol::TimeDimension(e) => { - return self.on_node_traverse(e.base_symbol(), &()) - } + MemberSymbol::TimeDimension(e) => return self.on_node_traverse(e.base_symbol(), &()), MemberSymbol::Measure(e) => { if !e.is_view() { let path = node.path(); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 13a46a109b983..1ef3d6b8e47ce 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -54,7 +54,9 @@ impl TestContext { } #[allow(dead_code)] - pub fn security_context(&self) -> &Rc { + pub fn security_context( + &self, + ) -> &Rc { &self.security_context } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs index 7e75a3a7bfecd..e6d1e192ed74a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_names_collector.rs @@ -59,7 +59,9 @@ fn test_cube_names_many_to_one_view_root_measure() { #[test] fn test_cube_names_many_to_one_view_child_measure() { let ctx = many_to_one_ctx(); - let measure = ctx.create_measure("many_to_one_view.child_val_avg").unwrap(); + let measure = ctx + .create_measure("many_to_one_view.child_val_avg") + .unwrap(); let mut names = collect_cube_names(&measure).unwrap(); names.sort(); assert_eq!(names, vec!["many_to_one_child", "many_to_one_root"]); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs index 4c0fb2001c316..ca654dce6dd72 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/join_hints_collector.rs @@ -124,7 +124,9 @@ fn test_join_hints_many_to_one_view_root_measure() { #[test] fn test_join_hints_many_to_one_view_child_measure() { let ctx = many_to_one_ctx(); - let measure = ctx.create_measure("many_to_one_view.child_val_avg").unwrap(); + let measure = ctx + .create_measure("many_to_one_view.child_val_avg") + .unwrap(); let hints = collect_join_hints(&measure).unwrap(); assert_eq!(hints.len(), 1); assert_eq!( @@ -137,11 +139,15 @@ fn test_join_hints_many_to_one_view_child_measure() { fn test_join_hints_many_to_one_view_combined_measures() { let ctx = many_to_one_ctx(); let m1 = ctx.create_measure("many_to_one_view.root_val_avg").unwrap(); - let m2 = ctx.create_measure("many_to_one_view.child_val_avg").unwrap(); + let m2 = ctx + .create_measure("many_to_one_view.child_val_avg") + .unwrap(); let hints = collect_join_hints_for_measures(&vec![m1, m2]).unwrap(); assert_eq!(hints.len(), 2); assert!(hints.items().contains(&s("many_to_one_root"))); - assert!(hints.items().contains(&v(&["many_to_one_root", "many_to_one_child"]))); + assert!(hints + .items() + .contains(&v(&["many_to_one_root", "many_to_one_child"]))); } #[test] diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs index d025eb37341c6..e28e1131dc891 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/member_expressions_on_views.rs @@ -25,10 +25,7 @@ fn make_member_expression(expression_name: &str, cube_name: &str, sql: &str) -> OptionsMember::MemberExpression(Rc::new(expr)) } -fn build_query_with_member_expression( - ctx: &TestContext, - extra_measure: OptionsMember, -) -> String { +fn build_query_with_member_expression(ctx: &TestContext, extra_measure: OptionsMember) -> String { let mut measures = members_from_strings(vec![ "many_to_one_view.root_val_avg", "many_to_one_view.child_val_avg", From f60b59752e7b96c142db003ba513f1e4f345d75d Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 15:30:28 +0100 Subject: [PATCH 17/21] in work --- .../symbols/common/compiled_member_path.rs | 6 +- .../src/tests/compiled_member_path.rs | 216 ++++++++++++++++++ .../cubesqlplanner/src/tests/mod.rs | 1 + 3 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs index bb156860b1956..b37414f2f669a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs @@ -16,8 +16,12 @@ impl CompiledMemberPath { full_name: String, name: String, alias: String, - path: Vec, + mut path: Vec, ) -> Self { + let cn = cube.cube_name(); + if path.is_empty() || path.last() != Some(cn) { + path.push(cn.clone()); + } Self { cube, full_name, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs new file mode 100644 index 0000000000000..da35648d6d4b6 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/compiled_member_path.rs @@ -0,0 +1,216 @@ +use crate::planner::sql_evaluator::MemberSymbol; +use crate::test_fixtures::cube_bridge::MockSchema; +use crate::test_fixtures::test_utils::TestContext; +use std::rc::Rc; + +fn simple_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/simple.yaml")).unwrap() +} + +fn visitors_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/visitors.yaml")).unwrap() +} + +fn many_to_one_ctx() -> TestContext { + TestContext::new(MockSchema::from_yaml_file("common/many_to_one_views.yaml")).unwrap() +} + +fn dep_paths(symbol: &Rc) -> Vec<(String, Vec)> { + let mut result: Vec<_> = symbol + .get_dependencies() + .into_iter() + .map(|d| (d.full_name(), d.path().clone())) + .collect(); + result.sort_by(|a, b| a.0.cmp(&b.0)); + result +} + +// --- Simple dimension/measure path --- + +#[test] +fn test_simple_dimension_path() { + let ctx = simple_ctx(); + let dim = ctx.create_dimension("orders.status").unwrap(); + assert_eq!(dim.path(), &vec!["orders".to_string()]); + assert_eq!(dim.cube_name(), "orders"); +} + +#[test] +fn test_simple_measure_path() { + let ctx = simple_ctx(); + let m = ctx.create_measure("orders.count").unwrap(); + assert_eq!(m.path(), &vec!["orders".to_string()]); + assert_eq!(m.cube_name(), "orders"); +} + +// --- View paths --- + +#[test] +fn test_view_dimension_has_underlying_path() { + let ctx = simple_ctx(); + + // orders_with_customer.name → join_path: orders.customers + let dim = ctx.create_dimension("orders_with_customer.name").unwrap(); + assert!(dim.as_dimension().unwrap().is_view()); + assert_eq!(dim.path(), &vec!["orders_with_customer".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "customers.name"); + assert_eq!( + deps[0].1, + vec!["orders".to_string(), "customers".to_string()] + ); +} + +#[test] +fn test_many_to_one_view_child_has_underlying_path() { + let ctx = many_to_one_ctx(); + + // many_to_one_view.child_dim → join_path: many_to_one_root.many_to_one_child + let dim = ctx.create_dimension("many_to_one_view.child_dim").unwrap(); + assert!(dim.as_dimension().unwrap().is_view()); + assert_eq!(dim.path(), &vec!["many_to_one_view".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "many_to_one_child.child_dim"); + assert_eq!( + deps[0].1, + vec![ + "many_to_one_root".to_string(), + "many_to_one_child".to_string() + ] + ); +} + +// --- Dependencies from SQL templates: same cube --- + +#[test] +fn test_dep_path_same_cube_member_ref() { + // visitors.visitor_id_proxy: sql = "{visitors.visitor_id}" + let ctx = visitors_ctx(); + let dim = ctx.create_dimension("visitors.visitor_id_proxy").unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.visitor_id"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +#[test] +fn test_dep_path_short_member_ref() { + let ctx = visitors_ctx(); + let dim = ctx.create_dimension("visitors.visitor_id_twice").unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.visitor_id"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +// --- Dependencies from SQL templates: cross-cube --- + +#[test] +fn test_dep_path_cross_cube_member_ref() { + // visitors.minVisitorCheckinDate: sql = "{visitor_checkins.minDate}" + let ctx = visitors_ctx(); + let dim = ctx + .create_dimension("visitors.minVisitorCheckinDate") + .unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitor_checkins.minDate"); + assert_eq!(deps[0].1, vec!["visitor_checkins".to_string()]); +} + +// --- Dependencies from SQL: multiple refs --- + +#[test] +fn test_dep_path_multiple_refs_in_sql() { + let ctx = visitors_ctx(); + let dim = ctx.create_dimension("visitors.source_concat_id").unwrap(); + assert_eq!(dim.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&dim); + assert_eq!(deps.len(), 2); + assert_eq!(deps[0].0, "visitors.source"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); + assert_eq!(deps[1].0, "visitors.visitor_id"); + assert_eq!(deps[1].1, vec!["visitors".to_string()]); +} + +// --- Measure dependencies --- + +#[test] +fn test_dep_path_measure_self_ref() { + // visitors.total_revenue_proxy: sql = "{total_revenue}" + let ctx = visitors_ctx(); + let m = ctx.create_measure("visitors.total_revenue_proxy").unwrap(); + assert_eq!(m.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&m); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.total_revenue"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +#[test] +fn test_dep_path_measure_cross_cube_ref() { + // customers.payments_per_order: sql = "{payments} / {orders.count}" + let ctx = simple_ctx(); + let m = ctx.create_measure("customers.payments_per_order").unwrap(); + assert_eq!(m.path(), &vec!["customers".to_string()]); + + let deps = dep_paths(&m); + assert_eq!(deps.len(), 2); + assert_eq!(deps[0].0, "customers.payments"); + assert_eq!(deps[0].1, vec!["customers".to_string()]); + assert_eq!(deps[1].0, "orders.count"); + assert_eq!(deps[1].1, vec!["orders".to_string()]); +} + +#[test] +fn test_dep_path_measure_mixed_refs() { + let ctx = visitors_ctx(); + let m = ctx + .create_measure("visitors.total_revenue_per_count") + .unwrap(); + assert_eq!(m.path(), &vec!["visitors".to_string()]); + + let deps = dep_paths(&m); + assert_eq!(deps.len(), 2); + assert_eq!(deps[0].0, "visitors.count"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); + assert_eq!(deps[1].0, "visitors.total_revenue"); + assert_eq!(deps[1].1, vec!["visitors".to_string()]); +} + +// --- Segment path --- + +#[test] +fn test_segment_compiled_path() { + let ctx = visitors_ctx(); + let seg = ctx.create_symbol("visitors.google").unwrap(); + assert_eq!(seg.path(), &vec!["visitors".to_string()]); + assert_eq!(seg.cube_name(), "visitors"); + + let deps = dep_paths(&seg); + assert_eq!(deps.len(), 1); + assert_eq!(deps[0].0, "visitors.source"); + assert_eq!(deps[0].1, vec!["visitors".to_string()]); +} + +// --- Time dimension path --- + +#[test] +fn test_time_dimension_path() { + let ctx = simple_ctx(); + let td = ctx.create_dimension("orders.created_at.day").unwrap(); + assert_eq!(td.path(), &vec!["orders".to_string()]); + assert_eq!(td.cube_name(), "orders"); +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs index bf39171a1abdb..774caafedd4ce 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs @@ -1,3 +1,4 @@ +mod compiled_member_path; mod common_sql_generation; mod cube_evaluator; mod cube_names_collector; From bca652d82da31896be3961d02a7cbcfa4de57de9 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 20:48:52 +0100 Subject: [PATCH 18/21] in work --- .../cubesqlplanner/src/planner/base_cube.rs | 5 +- .../src/planner/planners/common_utils.rs | 2 +- .../planners/dimension_subquery_planner.rs | 2 +- .../src/planner/query_properties.rs | 8 +-- .../collectors/cube_names_collector.rs | 9 +-- .../collectors/join_hints_collector.rs | 9 ++- .../src/planner/sql_evaluator/compiler.rs | 30 +++++---- .../sql_evaluator/cube_ref_evaluator.rs | 4 +- .../src/planner/sql_evaluator/sql_call.rs | 66 +++++++------------ .../planner/sql_evaluator/sql_call_builder.rs | 35 +++------- .../sql_evaluator/symbols/cube_symbol.rs | 36 ++++++++-- .../sql_evaluator/symbols/dimension_symbol.rs | 2 +- .../sql_evaluator/symbols/measure_symbol.rs | 2 +- .../test_fixtures/test_utils/test_context.rs | 2 +- .../src/tests/cube_evaluator/compilation.rs | 4 +- 15 files changed, 104 insertions(+), 112 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs index b8f7d8661efd5..8a941c25ece15 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs @@ -43,10 +43,7 @@ impl BaseCube { context: Rc, templates: &PlanSqlTemplates, ) -> Result { - let cube_ref = CubeRef::Table { - symbol: self.cube_table_symbol.clone(), - path: vec![], - }; + let cube_ref = CubeRef::Table(self.cube_table_symbol.clone()); let visitor = context.make_visitor(context.query_tools()); let node_processor = context.node_processor(); visitor.evaluate_cube_ref(&cube_ref, node_processor, templates) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs index ede38a2726abd..945e043395c3c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs @@ -17,7 +17,7 @@ impl CommonUtils { let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone(); let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); - let evaluator = evaluator_compiler.add_cube_table_evaluator(cube_path.to_string())?; + let evaluator = evaluator_compiler.add_cube_table_evaluator(cube_path.to_string(), vec![])?; BaseCube::try_new(cube_path.to_string(), self.query_tools.clone(), evaluator) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs index 548d0a00660b8..efce0efc61108 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs @@ -88,7 +88,7 @@ impl DimensionSubqueryPlanner { .query_tools .evaluator_compiler() .borrow_mut() - .add_cube_table_evaluator(cube_name.clone())?; + .add_cube_table_evaluator(cube_name.clone(), vec![])?; let member_expression_symbol = MemberExpressionSymbol::try_new( cube_symbol, dim_name.clone(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index 8865a924eea1e..0ff2c417f47e7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -150,7 +150,7 @@ impl QueryProperties { } }; let cube_symbol = - evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; + evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; let member_expression_symbol = MemberExpressionSymbol::try_new( cube_symbol, name.clone(), @@ -261,7 +261,7 @@ impl QueryProperties { } }; - let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; + let cube_symbol = evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; let member_expression_symbol = MemberExpressionSymbol::try_new( cube_symbol, name.clone(), @@ -303,7 +303,7 @@ impl QueryProperties { let expression_evaluator = evaluator_compiler .compile_sql_call(&cube_name, definition.sql()?)?; let cube_symbol = - evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; + evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; BaseSegment::try_new( expression_evaluator, cube_symbol, @@ -336,7 +336,7 @@ impl QueryProperties { } }; let cube_symbol = - evaluator_compiler.add_cube_table_evaluator(cube_name.clone())?; + evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; BaseSegment::try_new(expression_evaluator, cube_symbol, name, None) } }?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs index 34d4082858989..840b36958312a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/cube_names_collector.rs @@ -61,13 +61,10 @@ impl TraversalVisitor for CubeNamesCollector { } fn on_cube_ref(&mut self, cube_ref: &CubeRef, _state: &Self::State) -> Result<(), CubeError> { - if let CubeRef::Name { symbol, path, .. } = cube_ref { - if !path.is_empty() { - for p in path { - self.names.insert(p.clone()); - } + if let CubeRef::Name(symbol) = cube_ref { + for p in symbol.path() { + self.names.insert(p.clone()); } - self.names.insert(symbol.cube_name().clone()); } Ok(()) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs index 1911bf320b883..51c428685e161 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/collectors/join_hints_collector.rs @@ -83,11 +83,10 @@ impl TraversalVisitor for JoinHintsCollector { } fn on_cube_ref(&mut self, cube_ref: &CubeRef, _state: &Self::State) -> Result<(), CubeError> { - if let CubeRef::Name { symbol, path, .. } = cube_ref { - if !path.is_empty() { - let mut path = path.clone(); - path.push(symbol.cube_name().clone()); - self.hints.push(JoinHintItem::Vector(path)); + if let CubeRef::Name(symbol) = cube_ref { + let path = symbol.path(); + if path.len() > 1 { + self.hints.push(JoinHintItem::Vector(path.clone())); } else { self.hints .push(JoinHintItem::Single(symbol.cube_name().clone())); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index ebacde133adf9..c2ec651b3d314 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -31,8 +31,8 @@ pub struct Compiler { security_context: Rc, timezone: Tz, members: HashMap<(CacheSymbolType, String), Rc>, - cube_names: HashMap>, - cube_tables: HashMap>, + cube_names: HashMap, Rc>, + cube_tables: HashMap, Rc>, } impl Compiler { @@ -137,7 +137,7 @@ impl Compiler { let sql_call = self.compile_sql_call(path.cube_name(), definition.sql()?)?; let alias = PlanSqlTemplates::member_alias_name(path.cube_name(), path.symbol_name(), &None); - let cube_symbol = self.add_cube_table_evaluator(path.cube_name().clone())?; + let cube_symbol = self.add_cube_table_evaluator(path.cube_name().clone(), vec![])?; let symbol = MemberExpressionSymbol::try_new( cube_symbol, path.symbol_name().clone(), @@ -155,13 +155,17 @@ impl Compiler { pub fn add_cube_name_evaluator( &mut self, cube_name: String, + path: Vec, ) -> Result, CubeError> { - if let Some(exists) = self.cube_names.get(&cube_name) { + let cache_key = + CubeNameSymbol::normalize_path(path.clone(), &cube_name); + if let Some(exists) = self.cube_names.get(&cache_key) { Ok(exists.clone()) } else { - let result = CubeNameSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone())? - .build(self)?; - self.cube_names.insert(cube_name, result.clone()); + let result = + CubeNameSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone(), path)? + .build(self)?; + self.cube_names.insert(cache_key, result.clone()); Ok(result) } } @@ -169,13 +173,17 @@ impl Compiler { pub fn add_cube_table_evaluator( &mut self, cube_name: String, + path: Vec, ) -> Result, CubeError> { - if let Some(exists) = self.cube_tables.get(&cube_name) { + let cache_key = + CubeNameSymbol::normalize_path(path.clone(), &cube_name); + if let Some(exists) = self.cube_tables.get(&cache_key) { Ok(exists.clone()) } else { - let result = CubeTableSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone())? - .build(self)?; - self.cube_tables.insert(cube_name, result.clone()); + let result = + CubeTableSymbolFactory::try_new(&cube_name, self.cube_evaluator.clone(), path)? + .build(self)?; + self.cube_tables.insert(cache_key, result.clone()); Ok(result) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs index 1bf94e8f7e824..de638d5d9954b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/cube_ref_evaluator.rs @@ -32,12 +32,12 @@ impl CubeRefEvaluator { templates: &PlanSqlTemplates, ) -> Result { match cube_ref { - CubeRef::Name { symbol, .. } => { + CubeRef::Name(symbol) => { let name = symbol.evaluate_sql()?; let alias = self.resolve_cube_alias(&name); templates.quote_identifier(&alias) } - CubeRef::Table { symbol, .. } => { + CubeRef::Table(symbol) => { if let Some(pre_agg) = self.original_sql_pre_aggregations.get(symbol.cube_name()) { return Ok(pre_agg.clone()); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs index 992f58e2861f7..29dfc1354a520 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs @@ -13,41 +13,35 @@ use std::rc::Rc; #[derive(Clone, Debug)] pub enum CubeRef { - Name { - symbol: Rc, - path: Vec, - }, - Table { - symbol: Rc, - path: Vec, - }, + Name(Rc), + Table(Rc), } impl CubeRef { pub fn cube_name(&self) -> &String { match self { - CubeRef::Name { symbol, .. } => symbol.cube_name(), - CubeRef::Table { symbol, .. } => symbol.cube_name(), + CubeRef::Name(symbol) => symbol.cube_name(), + CubeRef::Table(symbol) => symbol.cube_name(), } } pub fn path(&self) -> &Vec { match self { - CubeRef::Name { path, .. } => path, - CubeRef::Table { path, .. } => path, + CubeRef::Name(symbol) => symbol.path(), + CubeRef::Table(symbol) => symbol.path(), } } pub fn as_name(&self) -> Option<&Rc> { match self { - CubeRef::Name { symbol, .. } => Some(symbol), + CubeRef::Name(symbol) => Some(symbol), _ => None, } } pub fn as_table(&self) -> Option<&Rc> { match self { - CubeRef::Table { symbol, .. } => Some(symbol), + CubeRef::Table(symbol) => Some(symbol), _ => None, } } @@ -105,12 +99,6 @@ impl SqlCallArg { } } -#[derive(Debug, Clone)] -pub struct SqlCallDependency { - pub path: Vec, - pub symbol: SqlDependency, -} - #[derive(Debug, Clone)] pub struct SqlCallFilterParamsItem { pub filter_symbol_name: String, @@ -125,7 +113,7 @@ pub struct SqlCallFilterGroupItem { #[derive(Clone, Debug)] pub struct SqlCall { template: SqlTemplate, - deps: Vec, + deps: Vec, filter_params: Vec, filter_groups: Vec, security_context: SecutityContextProps, @@ -134,7 +122,7 @@ pub struct SqlCall { impl SqlCall { pub(super) fn new( template: SqlTemplate, - deps: Vec, + deps: Vec, filter_params: Vec, filter_groups: Vec, security_context: SecutityContextProps, @@ -213,7 +201,7 @@ impl SqlCall { if self.deps.is_empty() { true } else { - self.deps.iter().any(|dep| dep.symbol.is_cube_ref()) + self.deps.iter().any(|dep| dep.is_cube_ref()) } } @@ -221,7 +209,7 @@ impl SqlCall { self.deps .iter() .filter_map(|dep| { - if let SqlDependency::CubeRef(CubeRef::Name { symbol, .. }) = &dep.symbol { + if let SqlDependency::CubeRef(CubeRef::Name(symbol)) = dep { Some(symbol.clone()) } else { None @@ -266,7 +254,7 @@ impl SqlCall { let deps = self .deps .iter() - .map(|dep| match &dep.symbol { + .map(|dep| match dep { SqlDependency::Symbol(m) => visitor.apply(m, node_processor.clone(), templates), SqlDependency::CubeRef(cr) => { visitor.evaluate_cube_ref(cr, node_processor.clone(), templates) @@ -402,26 +390,26 @@ impl SqlCall { pub fn resolve_direct_reference(&self) -> Option> { if self.is_direct_reference() { - self.deps[0].symbol.as_symbol().cloned() + self.deps[0].as_symbol().cloned() } else { None } } pub fn dependencies_count(&self) -> usize { - self.deps.iter().filter(|d| d.symbol.is_symbol()).count() + self.deps.iter().filter(|d| d.is_symbol()).count() } pub fn get_dependencies(&self) -> Vec> { self.deps .iter() - .filter_map(|d| d.symbol.as_symbol().cloned()) + .filter_map(|d| d.as_symbol().cloned()) .collect() } pub fn extract_symbol_deps(&self, result: &mut Vec>) { for dep in self.deps.iter() { - if let Some(s) = dep.symbol.as_symbol() { + if let Some(s) = dep.as_symbol() { result.push(s.clone()) } } @@ -429,18 +417,8 @@ impl SqlCall { pub fn extract_cube_refs(&self, result: &mut Vec) { for dep in self.deps.iter() { - if let SqlDependency::CubeRef(cr) = &dep.symbol { - let cube_ref = match cr { - CubeRef::Name { symbol, .. } => CubeRef::Name { - symbol: symbol.clone(), - path: dep.path.clone(), - }, - CubeRef::Table { symbol, .. } => CubeRef::Table { - symbol: symbol.clone(), - path: dep.path.clone(), - }, - }; - result.push(cube_ref); + if let SqlDependency::CubeRef(cr) = dep { + result.push(cr.clone()); } } } @@ -451,8 +429,8 @@ impl SqlCall { ) -> Result, CubeError> { let mut result = self.clone(); for dep in result.deps.iter_mut() { - if let SqlDependency::Symbol(ref s) = dep.symbol { - dep.symbol = SqlDependency::Symbol(s.apply_recursive(f)?); + if let SqlDependency::Symbol(ref s) = dep { + *dep = SqlDependency::Symbol(s.apply_recursive(f)?); } } Ok(Rc::new(result)) @@ -471,7 +449,7 @@ impl crate::utils::debug::DebugSql for SqlCall { let deps = self .deps .iter() - .map(|dep| match &dep.symbol { + .map(|dep| match dep { SqlDependency::Symbol(s) => { if expand_deps { s.debug_sql(true) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs index bbaa4dab67c66..5617bad221a07 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs @@ -1,7 +1,7 @@ use super::Compiler; use super::{ - CubeRef, SqlCall, SqlCallDependency, SqlCallFilterGroupItem, SqlCallFilterParamsItem, - SqlDependency, SymbolPath, SymbolPathType, + CubeRef, SqlCall, SqlCallFilterGroupItem, SqlCallFilterParamsItem, SqlDependency, SymbolPath, + SymbolPathType, }; use crate::cube_bridge::base_tools::BaseTools; use crate::cube_bridge::evaluator::CubeEvaluator; @@ -94,7 +94,7 @@ impl<'a> SqlCallBuilder<'a> { &mut self, current_cube_name: &String, dep_path: &Vec, - ) -> Result { + ) -> Result { assert!(!dep_path.is_empty()); let symbol_path = SymbolPath::parse_parts( @@ -111,46 +111,31 @@ impl<'a> SqlCallBuilder<'a> { let member = self .compiler .add_dimension_evaluator_by_path(symbol_path.clone())?; - Ok(SqlCallDependency { - path, - symbol: SqlDependency::Symbol(member), - }) + Ok(SqlDependency::Symbol(member)) } SymbolPathType::Measure => { let member = self .compiler .add_measure_evaluator_by_path(symbol_path.clone())?; - Ok(SqlCallDependency { - path, - symbol: SqlDependency::Symbol(member), - }) + Ok(SqlDependency::Symbol(member)) } SymbolPathType::Segment => { let member = self .compiler .add_segment_evaluator_by_path(symbol_path.clone())?; - Ok(SqlCallDependency { - path, - symbol: SqlDependency::Symbol(member), - }) + Ok(SqlDependency::Symbol(member)) } SymbolPathType::CubeName => { let symbol = self .compiler - .add_cube_name_evaluator(symbol_path.cube_name().clone())?; - Ok(SqlCallDependency { - path: path.clone(), - symbol: SqlDependency::CubeRef(CubeRef::Name { symbol, path }), - }) + .add_cube_name_evaluator(symbol_path.cube_name().clone(), path)?; + Ok(SqlDependency::CubeRef(CubeRef::Name(symbol))) } SymbolPathType::CubeTable => { let symbol = self .compiler - .add_cube_table_evaluator(symbol_path.cube_name().clone())?; - Ok(SqlCallDependency { - path: path.clone(), - symbol: SqlDependency::CubeRef(CubeRef::Table { symbol, path }), - }) + .add_cube_table_evaluator(symbol_path.cube_name().clone(), path)?; + Ok(SqlDependency::CubeRef(CubeRef::Table(symbol))) } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs index f2ac27522b0c1..e1d48fc34821e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs @@ -12,11 +12,20 @@ use std::rc::Rc; #[derive(Debug)] pub struct CubeNameSymbol { cube_name: String, + path: Vec, } impl CubeNameSymbol { - pub fn new(cube_name: String) -> Rc { - Rc::new(Self { cube_name }) + pub fn new(cube_name: String, path: Vec) -> Rc { + let path = Self::normalize_path(path, &cube_name); + Rc::new(Self { cube_name, path }) + } + + pub(crate) fn normalize_path(mut path: Vec, cube_name: &str) -> Vec { + if path.is_empty() || path.last().map(|s| s.as_str()) != Some(cube_name) { + path.push(cube_name.to_string()); + } + path } pub fn evaluate_sql(&self) -> Result { @@ -25,6 +34,9 @@ impl CubeNameSymbol { pub fn cube_name(&self) -> &String { &self.cube_name } + pub fn path(&self) -> &Vec { + &self.path + } pub fn alias(&self) -> String { PlanSqlTemplates::alias_name(&self.cube_name) } @@ -32,30 +44,34 @@ impl CubeNameSymbol { pub struct CubeNameSymbolFactory { cube_name: String, + path: Vec, } impl CubeNameSymbolFactory { pub fn try_new( full_name: &String, _cube_evaluator: Rc, + path: Vec, ) -> Result { //TODO check that cube exists Ok(Self { cube_name: full_name.clone(), + path, }) } } impl CubeNameSymbolFactory { pub fn build(self, _compiler: &mut Compiler) -> Result, CubeError> { - let Self { cube_name } = self; - Ok(CubeNameSymbol::new(cube_name)) + let Self { cube_name, path } = self; + Ok(CubeNameSymbol::new(cube_name, path)) } } #[derive(Debug)] pub struct CubeTableSymbol { cube_name: String, + path: Vec, member_sql: Option>, alias: String, is_table_sql: bool, @@ -65,13 +81,16 @@ pub struct CubeTableSymbol { impl CubeTableSymbol { pub fn new( cube_name: String, + path: Vec, member_sql: Option>, alias: String, is_table_sql: bool, join_map: Option>>, ) -> Rc { + let path = CubeNameSymbol::normalize_path(path, &cube_name); Rc::new(Self { cube_name, + path, member_sql, alias, is_table_sql, @@ -118,6 +137,10 @@ impl CubeTableSymbol { &self.cube_name } + pub fn path(&self) -> &Vec { + &self.path + } + pub fn alias(&self) -> String { self.alias.clone() } @@ -129,6 +152,7 @@ impl CubeTableSymbol { pub struct CubeTableSymbolFactory { cube_name: String, + path: Vec, sql: Option>, definition: Rc, is_table_sql: bool, @@ -138,6 +162,7 @@ impl CubeTableSymbolFactory { pub fn try_new( cube_name: &String, cube_evaluator: Rc, + path: Vec, ) -> Result { let definition = cube_evaluator.cube_from_path(cube_name.clone())?; let table_sql = definition.sql_table()?; @@ -146,6 +171,7 @@ impl CubeTableSymbolFactory { let sql = table_sql.or(sql); Ok(Self { cube_name: cube_name.clone(), + path, sql, definition, is_table_sql, @@ -155,6 +181,7 @@ impl CubeTableSymbolFactory { pub fn build(self, compiler: &mut Compiler) -> Result, CubeError> { let Self { cube_name, + path, sql, definition, is_table_sql, @@ -171,6 +198,7 @@ impl CubeTableSymbolFactory { }; Ok(CubeTableSymbol::new( cube_name, + path, sql, alias, is_table_sql, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index 49fd74f41e4d1..c34c97f1809b2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -471,7 +471,7 @@ impl SymbolFactory for DimensionSymbolFactory { .propagate_filters_to_sub_query .unwrap_or(false); - let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; + let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone(), vec![])?; let compiled_path = CompiledMemberPath::new( cube_symbol, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index 754d7d8539b8a..bcc049218fb97 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -712,7 +712,7 @@ impl SymbolFactory for MeasureSymbolFactory { && add_group_by.is_none() && group_by.is_none()); - let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone())?; + let cube_symbol = compiler.add_cube_table_evaluator(path.cube_name().clone(), vec![])?; let compiled_path = CompiledMemberPath::new( cube_symbol, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs index 1ef3d6b8e47ce..77523563a54b0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs @@ -96,7 +96,7 @@ impl TestContext { .segment_by_path(path.to_string())?; let mut compiler = self.query_tools.evaluator_compiler().borrow_mut(); let expression = compiler.compile_sql_call(&cube_name, definition.sql()?)?; - let cube_symbol = compiler.add_cube_table_evaluator(cube_name.clone())?; + let cube_symbol = compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; drop(compiler); BaseSegment::try_new(expression, cube_symbol, name, Some(path.to_string())) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs index b0fcc9c61eb1c..0ec8136aacb25 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/cube_evaluator/compilation.rs @@ -277,7 +277,7 @@ fn test_add_cube_table_evaluator() { let symbol = test_compiler .compiler - .add_cube_table_evaluator("visitors".to_string()) + .add_cube_table_evaluator("visitors".to_string(), vec![]) .unwrap(); assert_eq!(symbol.cube_name(), "visitors"); @@ -291,7 +291,7 @@ fn test_add_cube_name_evaluator() { let symbol = test_compiler .compiler - .add_cube_name_evaluator("visitors".to_string()) + .add_cube_name_evaluator("visitors".to_string(), vec![]) .unwrap(); assert_eq!(symbol.cube_name(), "visitors"); From 48e76bbf9501b95dc7918a8a974a0b423a501571 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 20:53:43 +0100 Subject: [PATCH 19/21] in work --- .../planners/multiplied_measures_query_planner.rs | 2 +- .../symbols/common/compiled_member_path.rs | 4 ++-- .../sql_evaluator/symbols/dimension_symbol.rs | 4 ++-- .../planner/sql_evaluator/symbols/measure_symbol.rs | 4 ++-- .../symbols/member_expression_symbol.rs | 4 ++-- .../planner/sql_evaluator/symbols/member_symbol.rs | 12 ++++++------ .../sql_evaluator/symbols/time_dimension_symbol.rs | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs index 4a048fa8df809..2c18af329f0dc 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -187,7 +187,7 @@ impl MultipliedMeasuresQueryPlanner { key_cube_name: &String, ) -> Result { for measure in measures.iter() { - let owned_measure = measure.with_own_path(); + let owned_measure = measure.with_stripped_join_prefix(); let member_expression_over_dimensions_cubes = if let Ok(member_expression) = owned_measure.as_member_expression() { member_expression.cube_names_if_dimension_only_expression()? diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs index b37414f2f669a..4f0f28d35e224 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs @@ -60,8 +60,8 @@ impl CompiledMemberPath { } /// Returns a copy with the path reduced to just the owning cube, - /// stripping any join chain prefix from views or other contexts. - pub fn own_path(&self) -> Self { + /// stripping any join chain prefix (e.g. from views or cross-cube references). + pub fn strip_join_prefix(&self) -> Self { Self { cube: self.cube.clone(), full_name: self.full_name.clone(), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index c34c97f1809b2..5954cb400f529 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -138,8 +138,8 @@ impl DimensionSymbol { &self.compiled_path } - pub fn set_own_path(&mut self) { - self.compiled_path = self.compiled_path.own_path(); + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); } pub fn full_name(&self) -> String { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs index bcc049218fb97..bcda67f7e4497 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -221,8 +221,8 @@ impl MeasureSymbol { &self.compiled_path } - pub fn set_own_path(&mut self) { - self.compiled_path = self.compiled_path.own_path(); + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); } pub fn full_name(&self) -> String { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs index 77d50de8125ff..69b218bad843f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_expression_symbol.rs @@ -84,8 +84,8 @@ impl MemberExpressionSymbol { &self.compiled_path } - pub fn set_own_path(&mut self) { - self.compiled_path = self.compiled_path.own_path(); + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); } pub fn full_name(&self) -> String { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index 31e755fb42d75..921da1fee1741 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs @@ -198,27 +198,27 @@ impl MemberSymbol { } /// Returns a copy of this symbol with the path reduced to just the owning cube, - /// stripping any join chain prefix from views or other contexts. - pub fn with_own_path(&self) -> Rc { + /// stripping any join chain prefix (e.g. from views or cross-cube references). + pub fn with_stripped_join_prefix(&self) -> Rc { match self { Self::Dimension(d) => { let mut new = (**d).clone(); - new.set_own_path(); + new.strip_join_prefix(); Rc::new(Self::Dimension(Rc::new(new))) } Self::TimeDimension(d) => { let mut new = (**d).clone(); - new.set_own_path(); + new.strip_join_prefix(); Rc::new(Self::TimeDimension(Rc::new(new))) } Self::Measure(m) => { let mut new = (**m).clone(); - new.set_own_path(); + new.strip_join_prefix(); Rc::new(Self::Measure(Rc::new(new))) } Self::MemberExpression(e) => { let mut new = (**e).clone(); - new.set_own_path(); + new.strip_join_prefix(); Rc::new(Self::MemberExpression(Rc::new(new))) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs index 3da971c2bc202..35003830ae56d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/time_dimension_symbol.rs @@ -104,8 +104,8 @@ impl TimeDimensionSymbol { &self.compiled_path } - pub fn set_own_path(&mut self) { - self.compiled_path = self.compiled_path.own_path(); + pub fn strip_join_prefix(&mut self) { + self.compiled_path = self.compiled_path.strip_join_prefix(); } pub fn full_name(&self) -> String { From 9d000eaced6a251eda269ef869bd2ecf70756015 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 21:25:16 +0100 Subject: [PATCH 20/21] in work --- .../cubesqlplanner/src/planner/sql_evaluator/sql_call.rs | 2 +- .../sql_evaluator/symbols/common/compiled_member_path.rs | 9 +++------ .../cubesqlplanner/src/tests/common_sql_generation.rs | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs index 29dfc1354a520..08dc7235cb380 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call.rs @@ -156,7 +156,7 @@ impl SqlCall { ) } else { Err(CubeError::internal( - "SqlCall::eval called for fuction that return string".to_string(), + "SqlCall::eval called for function that returns string".to_string(), )) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs index 4f0f28d35e224..e8c9c8fa7dbc3 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/common/compiled_member_path.rs @@ -1,4 +1,4 @@ -use crate::planner::sql_evaluator::CubeTableSymbol; +use crate::planner::sql_evaluator::{CubeNameSymbol, CubeTableSymbol}; use std::rc::Rc; #[derive(Clone, Debug)] @@ -16,12 +16,9 @@ impl CompiledMemberPath { full_name: String, name: String, alias: String, - mut path: Vec, + path: Vec, ) -> Self { - let cn = cube.cube_name(); - if path.is_empty() || path.last() != Some(cn) { - path.push(cn.clone()); - } + let path = CubeNameSymbol::normalize_path(path, cube.cube_name()); Self { cube, full_name, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs index 292cc0c8401f2..d2db65ca7a2f4 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs @@ -66,7 +66,7 @@ fn test_simple_paths_in_time_dimension_request_sql() { let sql = test_context .build_sql(query_yaml) .expect("Should generate SQL"); - println!("{}", sql); + assert!( sql.contains(r#"ON "cube_a".c_id = "cube_c".id"#), From dd69d33e186791e76acf5e0f4671191f7c6f25a2 Mon Sep 17 00:00:00 2001 From: Aleksandr Romanenko Date: Sun, 8 Mar 2026 21:27:23 +0100 Subject: [PATCH 21/21] fmt --- .../src/planner/planners/common_utils.rs | 3 ++- .../cubesqlplanner/src/planner/query_properties.rs | 12 ++++++------ .../src/planner/sql_evaluator/compiler.rs | 6 ++---- .../src/tests/common_sql_generation.rs | 1 - rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs index 945e043395c3c..3cb36feb14d48 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs @@ -17,7 +17,8 @@ impl CommonUtils { let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone(); let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); - let evaluator = evaluator_compiler.add_cube_table_evaluator(cube_path.to_string(), vec![])?; + let evaluator = + evaluator_compiler.add_cube_table_evaluator(cube_path.to_string(), vec![])?; BaseCube::try_new(cube_path.to_string(), self.query_tools.clone(), evaluator) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index 0ff2c417f47e7..95891a4a3916d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -149,8 +149,8 @@ impl QueryProperties { ))); } }; - let cube_symbol = - evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; + let cube_symbol = evaluator_compiler + .add_cube_table_evaluator(cube_name.clone(), vec![])?; let member_expression_symbol = MemberExpressionSymbol::try_new( cube_symbol, name.clone(), @@ -302,8 +302,8 @@ impl QueryProperties { .segment_by_path(member_name.clone())?; let expression_evaluator = evaluator_compiler .compile_sql_call(&cube_name, definition.sql()?)?; - let cube_symbol = - evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; + let cube_symbol = evaluator_compiler + .add_cube_table_evaluator(cube_name.clone(), vec![])?; BaseSegment::try_new( expression_evaluator, cube_symbol, @@ -335,8 +335,8 @@ impl QueryProperties { ))); } }; - let cube_symbol = - evaluator_compiler.add_cube_table_evaluator(cube_name.clone(), vec![])?; + let cube_symbol = evaluator_compiler + .add_cube_table_evaluator(cube_name.clone(), vec![])?; BaseSegment::try_new(expression_evaluator, cube_symbol, name, None) } }?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index c2ec651b3d314..5c25f368d441f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -157,8 +157,7 @@ impl Compiler { cube_name: String, path: Vec, ) -> Result, CubeError> { - let cache_key = - CubeNameSymbol::normalize_path(path.clone(), &cube_name); + let cache_key = CubeNameSymbol::normalize_path(path.clone(), &cube_name); if let Some(exists) = self.cube_names.get(&cache_key) { Ok(exists.clone()) } else { @@ -175,8 +174,7 @@ impl Compiler { cube_name: String, path: Vec, ) -> Result, CubeError> { - let cache_key = - CubeNameSymbol::normalize_path(path.clone(), &cube_name); + let cache_key = CubeNameSymbol::normalize_path(path.clone(), &cube_name); if let Some(exists) = self.cube_tables.get(&cache_key) { Ok(exists.clone()) } else { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs index d2db65ca7a2f4..77efa96d8421c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/common_sql_generation.rs @@ -67,7 +67,6 @@ fn test_simple_paths_in_time_dimension_request_sql() { .build_sql(query_yaml) .expect("Should generate SQL"); - assert!( sql.contains(r#"ON "cube_a".c_id = "cube_c".id"#), "SQL should contain join condition between cube_a and cube_c" diff --git a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs index 774caafedd4ce..fcb42c70992b7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/tests/mod.rs @@ -1,5 +1,5 @@ -mod compiled_member_path; mod common_sql_generation; +mod compiled_member_path; mod cube_evaluator; mod cube_names_collector; mod dimension_symbol;