diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index c5de58b620094..07b7fd4db874a 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -4108,7 +4108,7 @@ export class BaseQuery { VAR_SAMP: 'VAR_SAMP({{ args_concat }})', COVAR_POP: 'COVAR_POP({{ args_concat }})', COVAR_SAMP: 'COVAR_SAMP({{ args_concat }})', - + GROUP_ANY: 'max({{ expr }})', COALESCE: 'COALESCE({{ args_concat }})', CONCAT: 'CONCAT({{ args_concat }})', FLOOR: 'FLOOR({{ args_concat }})', diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts index d38ce42485245..67ab4f84f4f66 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts @@ -577,7 +577,7 @@ describe('PreAggregations', () => { }); `); - it('simple pre-aggregation', async () => { + it('simple pre-aggregation 1', async () => { await compiler.compile(); const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, { @@ -2194,7 +2194,7 @@ describe('PreAggregations', () => { }); }); - if (getEnv('nativeSqlPlanner') && getEnv('nativeSqlPlannerPreAggregations')) { + if (true) { // getEnv('nativeSqlPlanner') && getEnv('nativeSqlPlannerPreAggregations')) { it('rollup lambda', async () => { await compiler.compile(); diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts index 25c37418b9824..6b7b1263a4959 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts @@ -862,7 +862,7 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL }) `); - it('simple join', async () => { + it('simple join 1', async () => { await compiler.compile(); console.log(joinGraph.buildJoin(['visitor_checkins', 'visitors'])); @@ -2539,7 +2539,11 @@ SELECT 1 AS revenue, cast('2024-01-01' AS timestamp) as time UNION ALL filters: [], order: [{ id: 'visitor_checkins.id' - }], + }, + { + id: 'visitor_checkins.created_at' + } + ], ungrouped: true }); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs index 2bf22f9719e37..b0851c2bb38a8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs @@ -20,6 +20,16 @@ pub struct CubeDefinitionStatic { pub is_calendar: Option, } +impl CubeDefinitionStatic { + pub fn resolved_alias(&self) -> &String { + if let Some(alias) = &self.sql_alias { + alias + } else { + &self.name + } + } +} + #[nativebridge::native_bridge(CubeDefinitionStatic)] pub trait CubeDefinition { #[nbridge(field, optional)] diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/aggregate_multiplied_subquery.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/aggregate_multiplied_subquery.rs index 64ae7a8af8c67..f609f84558165 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/aggregate_multiplied_subquery.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/aggregate_multiplied_subquery.rs @@ -1,18 +1,120 @@ use super::pretty_print::*; use super::*; +use cubenativeutils::CubeError; use std::rc::Rc; pub enum AggregateMultipliedSubquerySouce { - Cube, + Cube(Rc), MeasureSubquery(Rc), } +impl AggregateMultipliedSubquerySouce { + fn as_plan_node(&self) -> PlanNode { + match self { + Self::Cube(item) => item.as_plan_node(), + Self::MeasureSubquery(item) => item.as_plan_node(), + } + } + fn with_plan_node(&self, plan_node: PlanNode) -> Result { + Ok(match self { + Self::Cube(_) => Self::Cube(plan_node.into_logical_node()?), + Self::MeasureSubquery(_) => Self::MeasureSubquery(plan_node.into_logical_node()?), + }) + } +} + pub struct AggregateMultipliedSubquery { pub schema: Rc, - pub dimension_subqueries: Vec>, pub keys_subquery: Rc, - pub pk_cube: Rc, //FIXME may be duplication with information in keys_subquery - pub source: Rc, + pub source: AggregateMultipliedSubquerySouce, + pub dimension_subqueries: Vec>, +} + +impl LogicalNode for AggregateMultipliedSubquery { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::AggregateMultipliedSubquery(self.clone()) + } + + fn inputs(&self) -> Vec { + AggregateMultipliedSubqueryInputPacker::pack(self) + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + let AggregateMultipliedSubqueryInputUnPacker { + keys_subquery, + source, + dimension_subqueries, + } = AggregateMultipliedSubqueryInputUnPacker::new(&self, &inputs)?; + + let result = Self { + schema: self.schema.clone(), + keys_subquery: keys_subquery.clone().into_logical_node()?, + source: self.source.with_plan_node(source.clone())?, + dimension_subqueries: dimension_subqueries + .iter() + .map(|itm| itm.clone().into_logical_node()) + .collect::, _>>()?, + }; + + Ok(Rc::new(result)) + } + + fn node_name(&self) -> &'static str { + "AggregateMultipliedSubquery" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::AggregateMultipliedSubquery(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "AggregateMultipliedSubquery")) + } + } +} + +pub struct AggregateMultipliedSubqueryInputPacker; + +impl AggregateMultipliedSubqueryInputPacker { + pub fn pack(aggregate: &AggregateMultipliedSubquery) -> Vec { + let mut result = vec![]; + result.push(aggregate.keys_subquery.as_plan_node()); + result.push(aggregate.source.as_plan_node()); + result.extend( + aggregate + .dimension_subqueries + .iter() + .map(|itm| itm.as_plan_node()), + ); + result + } +} + +pub struct AggregateMultipliedSubqueryInputUnPacker<'a> { + keys_subquery: &'a PlanNode, + source: &'a PlanNode, + dimension_subqueries: &'a [PlanNode], +} + +impl<'a> AggregateMultipliedSubqueryInputUnPacker<'a> { + pub fn new( + aggregate: &AggregateMultipliedSubquery, + inputs: &'a Vec, + ) -> Result { + check_inputs_len(&inputs, Self::inputs_len(aggregate), aggregate.node_name())?; + + let keys_subquery = &inputs[0]; + let source = &inputs[1]; + let dimension_subqueries = &inputs[2..]; + + Ok(Self { + keys_subquery, + source, + dimension_subqueries, + }) + } + + fn inputs_len(aggregate: &AggregateMultipliedSubquery) -> usize { + 2 + aggregate.dimension_subqueries.len() + } } impl PrettyPrint for AggregateMultipliedSubquery { @@ -22,28 +124,25 @@ impl PrettyPrint for AggregateMultipliedSubquery { let details_state = state.new_level(); result.println("schema:", &state); self.schema.pretty_print(result, &details_state); - if !self.dimension_subqueries.is_empty() { - result.println("dimension_subqueries:", &state); - for subquery in self.dimension_subqueries.iter() { - subquery.pretty_print(result, &details_state); - } - } result.println("keys_subquery:", &state); self.keys_subquery.pretty_print(result, &details_state); result.println("source:", &state); - match self.source.as_ref() { - AggregateMultipliedSubquerySouce::Cube => { + match &self.source { + AggregateMultipliedSubquerySouce::Cube(cube) => { result.println("Cube:", &details_state); - self.pk_cube - .pretty_print(result, &details_state.new_level()); + cube.pretty_print(result, &details_state.new_level()); } AggregateMultipliedSubquerySouce::MeasureSubquery(measure_subquery) => { - result.println( - &format!("MeasureSubquery: {}", measure_subquery.measures.len()), - &details_state, - ); + result.println(&format!("MeasureSubquery: "), &details_state); measure_subquery.pretty_print(result, &details_state); } } + if !self.dimension_subqueries.is_empty() { + result.println("dimension_subqueries:", &state); + let details_state = state.new_level(); + for subquery in self.dimension_subqueries.iter() { + subquery.pretty_print(result, &details_state); + } + } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/cube.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/cube.rs index 73a2ab6bcc8fc..cd78e7f1c4685 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/cube.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/cube.rs @@ -1,5 +1,6 @@ use super::*; use crate::planner::BaseCube; +use cubenativeutils::CubeError; use std::rc::Rc; #[derive(Clone)] @@ -49,3 +50,29 @@ impl Cube { }) } } + +impl LogicalNode for Cube { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::Cube(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![] // Cube has no inputs + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 0, self.node_name())?; + Ok(self) + } + + fn node_name(&self) -> &'static str { + "Cube" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::Cube(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "Cube")) + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/dimension_subquery.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/dimension_subquery.rs index 555b54a8cd377..b5fd0fc40fe1d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/dimension_subquery.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/dimension_subquery.rs @@ -1,6 +1,7 @@ use super::pretty_print::*; use super::*; use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use std::rc::Rc; pub struct DimensionSubQuery { @@ -10,6 +11,38 @@ pub struct DimensionSubQuery { pub measure_for_subquery_dimension: Rc, } +impl LogicalNode for DimensionSubQuery { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::DimensionSubQuery(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![self.query.as_plan_node()] + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 1, self.node_name())?; + let query = &inputs[0]; + Ok(Rc::new(Self { + query: query.clone().into_logical_node()?, + primary_keys_dimensions: self.primary_keys_dimensions.clone(), + subquery_dimension: self.subquery_dimension.clone(), + measure_for_subquery_dimension: self.measure_for_subquery_dimension.clone(), + })) + } + + fn node_name(&self) -> &'static str { + "DimensionSubQuery" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::DimensionSubQuery(query) = plan_node { + Ok(query) + } else { + Err(cast_error(&plan_node, "DimensionSubQuery")) + } + } +} + impl PrettyPrint for DimensionSubQuery { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println("DimensionSubQuery: ", state); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/filter.rs index 4e10aaba769dc..603f6c4100f21 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/filter.rs @@ -24,6 +24,15 @@ impl LogicalFilter { Some(Filter { items }) } } + pub fn measures_filter(&self) -> Option { + if self.measures_filter.is_empty() { + None + } else { + Some(Filter { + items: self.measures_filter.clone(), + }) + } + } } impl PrettyPrint for LogicalFilter { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate.rs index ea13cdc5df52e..ee916e01763a0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate.rs @@ -1,5 +1,6 @@ use super::*; use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use std::rc::Rc; pub struct MultiStageSubqueryRef { @@ -21,7 +22,18 @@ impl PrettyPrint for MultiStageSubqueryRef { #[derive(Clone)] pub enum ResolvedMultipliedMeasures { ResolveMultipliedMeasures(Rc), - PreAggregation(Rc), + PreAggregation(Rc), +} + +impl ResolvedMultipliedMeasures { + pub fn schema(&self) -> Rc { + match self { + ResolvedMultipliedMeasures::ResolveMultipliedMeasures(resolve_multiplied_measures) => { + resolve_multiplied_measures.schema.clone() + } + ResolvedMultipliedMeasures::PreAggregation(simple_query) => simple_query.schema.clone(), + } + } } impl PrettyPrint for ResolvedMultipliedMeasures { @@ -38,22 +50,79 @@ impl PrettyPrint for ResolvedMultipliedMeasures { } } +#[derive(Clone)] pub struct FullKeyAggregate { - pub join_dimensions: Vec>, + pub schema: Rc, pub use_full_join_and_coalesce: bool, pub multiplied_measures_resolver: Option, pub multi_stage_subquery_refs: Vec>, } +impl LogicalNode for FullKeyAggregate { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::FullKeyAggregate(self.clone()) + } + + fn inputs(&self) -> Vec { + if let Some(resolver) = &self.multiplied_measures_resolver { + vec![match resolver { + ResolvedMultipliedMeasures::ResolveMultipliedMeasures(item) => item.as_plan_node(), + ResolvedMultipliedMeasures::PreAggregation(item) => item.as_plan_node(), + }] + } else { + vec![] + } + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + let multiplied_measures_resolver = if self.multiplied_measures_resolver.is_none() { + check_inputs_len(&inputs, 0, self.node_name())?; + None + } else { + check_inputs_len(&inputs, 1, self.node_name())?; + let input_source = &inputs[0]; + + Some(match self.multiplied_measures_resolver.as_ref().unwrap() { + ResolvedMultipliedMeasures::ResolveMultipliedMeasures(_) => { + ResolvedMultipliedMeasures::ResolveMultipliedMeasures( + input_source.clone().into_logical_node()?, + ) + } + ResolvedMultipliedMeasures::PreAggregation(_) => { + ResolvedMultipliedMeasures::PreAggregation( + input_source.clone().into_logical_node()?, + ) + } + }) + }; + + Ok(Rc::new(Self { + schema: self.schema.clone(), + use_full_join_and_coalesce: self.use_full_join_and_coalesce, + multiplied_measures_resolver, + multi_stage_subquery_refs: self.multi_stage_subquery_refs.clone(), + })) + } + + fn node_name(&self) -> &'static str { + "FullKeyAggregate" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::FullKeyAggregate(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "FullKeyAggregate")) + } + } +} + impl PrettyPrint for FullKeyAggregate { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println("FullKeyAggregate: ", state); let state = state.new_level(); let details_state = state.new_level(); - result.println( - &format!("join_dimensions: {}", print_symbols(&self.join_dimensions)), - &state, - ); + result.println(&format!("schema:"), &state); + self.schema.pretty_print(result, &details_state); result.println( &format!( "use_full_join_and_coalesce: {}", diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate_query.rs deleted file mode 100644 index 4f0f347c4560c..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate_query.rs +++ /dev/null @@ -1,55 +0,0 @@ -use super::*; -use crate::planner::query_properties::OrderByItem; -use std::rc::Rc; - -pub struct FullKeyAggregateQuery { - pub multistage_members: Vec>, - pub schema: Rc, - pub filter: Rc, - pub offset: Option, - pub limit: Option, - pub ungrouped: bool, - pub order_by: Vec, - pub source: Rc, -} - -impl PrettyPrint for FullKeyAggregateQuery { - fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { - result.println("FullKeyAggregateQuery: ", state); - let state = state.new_level(); - let details_state = state.new_level(); - if !self.multistage_members.is_empty() { - result.println("multistage_members:", &state); - for member in self.multistage_members.iter() { - member.pretty_print(result, &details_state); - } - } - - result.println("schema:", &state); - self.schema.pretty_print(result, &details_state); - result.println("filter:", &state); - self.filter.pretty_print(result, &details_state); - if let Some(offset) = &self.offset { - result.println(&format!("offset:{}", offset), &state); - } - if let Some(limit) = &self.limit { - result.println(&format!("limit:{}", limit), &state); - } - result.println(&format!("ungrouped:{}", self.ungrouped), &state); - if !self.order_by.is_empty() { - result.println("order_by:", &state); - for order_by in self.order_by.iter() { - result.println( - &format!( - "{} {}", - order_by.name(), - if order_by.desc() { "desc" } else { "asc" } - ), - &details_state, - ); - } - } - result.println("source:", &state); - self.source.pretty_print(result, &details_state); - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/join.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/join.rs index 17b9b39a5ffa2..7aa79c18273e5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/join.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/join.rs @@ -1,16 +1,16 @@ use super::pretty_print::*; -use super::Cube; -use super::SimpleQuery; -use crate::planner::sql_evaluator::{MemberSymbol, SqlCall}; +use super::*; +use crate::planner::sql_evaluator::SqlCall; +use cubenativeutils::CubeError; use std::rc::Rc; #[derive(Clone)] -pub struct CubeJoinItem { +pub struct LogicalJoinItem { pub cube: Rc, pub on_sql: Rc, } -impl PrettyPrint for CubeJoinItem { +impl PrettyPrint for LogicalJoinItem { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println(&format!("CubeJoinItem: "), state); let details_state = state.new_level(); @@ -19,47 +19,106 @@ impl PrettyPrint for CubeJoinItem { } #[derive(Clone)] -pub struct SubqueryDimensionJoinItem { - pub subquery: Rc, - pub dimension: Rc, - pub primary_keys_dimensions: Vec>, +pub struct LogicalJoin { + pub root: Rc, + pub joins: Vec, + pub dimension_subqueries: Vec>, } -impl PrettyPrint for SubqueryDimensionJoinItem { - fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { - result.println( - &format!( - "SubqueryDimensionJoinItem for dimension `{}`: ", - self.dimension.full_name() - ), - state, - ); - result.println("subquery:", state); - result.println("primary_keys_dimensions:", state); - let state = state.new_level(); - for dim in self.primary_keys_dimensions.iter() { - result.println(&format!("- {}", dim.full_name()), &state); +impl LogicalNode for LogicalJoin { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::LogicalJoin(self.clone()) + } + + fn inputs(&self) -> Vec { + LogicalJoinInputPacker::pack(self) + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + let LogicalJoinInputUnPacker { + root, + joins, + dimension_subqueries, + } = LogicalJoinInputUnPacker::new(&self, &inputs)?; + + let joins = self + .joins + .iter() + .zip(joins.iter()) + .map(|(self_item, item)| -> Result<_, CubeError> { + Ok(LogicalJoinItem { + cube: item.clone().into_logical_node()?, + on_sql: self_item.on_sql.clone(), + }) + }) + .collect::, _>>()?; + + let result = Self { + root: root.clone().into_logical_node()?, + joins, + dimension_subqueries: dimension_subqueries + .iter() + .map(|itm| itm.clone().into_logical_node()) + .collect::, _>>()?, + }; + + Ok(Rc::new(result)) + } + + fn node_name(&self) -> &'static str { + "LogicalJoin" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::LogicalJoin(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "LogicalJoin")) } } } -#[derive(Clone)] -pub enum LogicalJoinItem { - CubeJoinItem(CubeJoinItem), -} +pub struct LogicalJoinInputPacker; -impl PrettyPrint for LogicalJoinItem { - fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { - match self { - LogicalJoinItem::CubeJoinItem(item) => item.pretty_print(result, state), - } +impl LogicalJoinInputPacker { + pub fn pack(join: &LogicalJoin) -> Vec { + let mut result = vec![]; + result.push(join.root.as_plan_node()); + result.extend(join.joins.iter().map(|item| item.cube.as_plan_node())); + result.extend( + join.dimension_subqueries + .iter() + .map(|item| item.as_plan_node()), + ); + result } } -#[derive(Clone)] -pub struct LogicalJoin { - pub root: Rc, - pub joins: Vec, +pub struct LogicalJoinInputUnPacker<'a> { + root: &'a PlanNode, + joins: &'a [PlanNode], + dimension_subqueries: &'a [PlanNode], +} + +impl<'a> LogicalJoinInputUnPacker<'a> { + pub fn new(join: &LogicalJoin, inputs: &'a Vec) -> Result { + check_inputs_len(&inputs, Self::inputs_len(join), join.node_name())?; + + let root = &inputs[0]; + let joins_start = 1; + let joins_end = joins_start + join.joins.len(); + let joins = &inputs[joins_start..joins_end]; + let dimension_subqueries = &inputs[joins_end..]; + + Ok(Self { + root, + joins, + dimension_subqueries, + }) + } + + fn inputs_len(join: &LogicalJoin) -> usize { + 1 + join.joins.len() + join.dimension_subqueries.len() + } } impl PrettyPrint for LogicalJoin { @@ -74,5 +133,12 @@ impl PrettyPrint for LogicalJoin { for join in self.joins.iter() { join.pretty_print(result, &state); } + if !self.dimension_subqueries.is_empty() { + result.println("dimension_subqueries:", &state); + let details_state = state.new_level(); + for subquery in self.dimension_subqueries.iter() { + subquery.pretty_print(result, &details_state); + } + } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/keys_subquery.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/keys_subquery.rs index 94d88d424be04..506311593d175 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/keys_subquery.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/keys_subquery.rs @@ -1,38 +1,62 @@ use super::*; use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use std::rc::Rc; #[derive(Clone)] pub struct KeysSubQuery { - pub key_cube_name: String, - pub time_dimensions: Vec>, - pub dimensions: Vec>, - pub dimension_subqueries: Vec>, + pub pk_cube: Rc, + pub schema: Rc, pub primary_keys_dimensions: Vec>, pub filter: Rc, pub source: Rc, } +impl LogicalNode for KeysSubQuery { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::KeysSubQuery(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![self.pk_cube.as_plan_node(), self.source.as_plan_node()] + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 2, self.node_name())?; + let pk_cube = &inputs[0]; + let source = &inputs[1]; + + let res = Self { + pk_cube: pk_cube.clone().into_logical_node()?, + schema: self.schema.clone(), + primary_keys_dimensions: self.primary_keys_dimensions.clone(), + filter: self.filter.clone(), + source: source.clone().into_logical_node()?, + }; + Ok(Rc::new(res)) + } + + fn node_name(&self) -> &'static str { + "KeysSubQuery" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::KeysSubQuery(query) = plan_node { + Ok(query) + } else { + Err(cast_error(&plan_node, "KeysSubQuery")) + } + } +} + impl PrettyPrint for KeysSubQuery { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println("KeysSubQuery: ", state); let state = state.new_level(); let details_state = state.new_level(); - result.println(&format!("-key_cube_name: {}", self.key_cube_name), &state); - result.println( - &format!("-time_dimensions: {}", print_symbols(&self.time_dimensions)), - &state, - ); - result.println( - &format!("-dimensions: {}", print_symbols(&self.dimensions)), - &state, - ); - /* if !self.dimension_subqueries.is_empty() { - result.println("dimension_subqueries:", &state); - for subquery in self.dimension_subqueries.iter() { - subquery.pretty_print(result, &details_state); - } - } */ + result.println(&format!("pk_cube: {}", self.pk_cube.cube.name()), &state); + + result.println("schema:", &state); + self.schema.pretty_print(result, &details_state); result.println( &format!( "-primary_keys_dimensions: {}", diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/logical_node.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/logical_node.rs new file mode 100644 index 0000000000000..a70fe8229cf7e --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/logical_node.rs @@ -0,0 +1,109 @@ +use super::*; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub trait LogicalNode { + fn inputs(&self) -> Vec; + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError>; + + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError>; + + fn as_plan_node(self: &Rc) -> PlanNode; + + fn node_name(&self) -> &'static str; +} + +#[derive(Clone)] +pub enum PlanNode { + Query(Rc), + LogicalJoin(Rc), + FullKeyAggregate(Rc), + PreAggregation(Rc), + ResolveMultipliedMeasures(Rc), + AggregateMultipliedSubquery(Rc), + Cube(Rc), + MeasureSubquery(Rc), + DimensionSubQuery(Rc), + KeysSubQuery(Rc), + MultiStageGetDateRange(Rc), + MultiStageLeafMeasure(Rc), + MultiStageMeasureCalculation(Rc), + MultiStageTimeSeries(Rc), + MultiStageRollingWindow(Rc), + LogicalMultiStageMember(Rc), +} + +// Macro for applying a block to all enum variants +macro_rules! match_plan_node { + ($self:expr, $node:ident => $block:block) => { + match $self { + PlanNode::Query($node) => $block, + PlanNode::LogicalJoin($node) => $block, + PlanNode::FullKeyAggregate($node) => $block, + PlanNode::PreAggregation($node) => $block, + PlanNode::ResolveMultipliedMeasures($node) => $block, + PlanNode::AggregateMultipliedSubquery($node) => $block, + PlanNode::Cube($node) => $block, + PlanNode::MeasureSubquery($node) => $block, + PlanNode::DimensionSubQuery($node) => $block, + PlanNode::KeysSubQuery($node) => $block, + PlanNode::MultiStageGetDateRange($node) => $block, + PlanNode::MultiStageLeafMeasure($node) => $block, + PlanNode::MultiStageMeasureCalculation($node) => $block, + PlanNode::MultiStageTimeSeries($node) => $block, + PlanNode::MultiStageRollingWindow($node) => $block, + PlanNode::LogicalMultiStageMember($node) => $block, + } + }; +} + +impl PlanNode { + pub fn into_logical_node(self) -> Result, CubeError> { + T::try_from_plan_node(self) + } + + pub fn inputs(&self) -> Vec { + match_plan_node!(self, node => { + node.inputs() + }) + } + + pub fn node_name(&self) -> &'static str { + match_plan_node!(self, node => { + node.node_name() + }) + } + + pub fn with_inputs(self, inputs: Vec) -> Result { + let result = match_plan_node!(self, node => { + node.with_inputs(inputs)?.as_plan_node() + }); + Ok(result) + } +} + +pub(super) fn cast_error(plan_node: &PlanNode, target_type: &str) -> CubeError { + CubeError::internal(format!( + "Can't cast {} PlanNode into {}", + plan_node.node_name(), + target_type, + )) +} + +pub(super) fn check_inputs_len( + inputs: &Vec, + expected: usize, + node_type: &str, +) -> Result<(), CubeError> { + if inputs.len() == expected { + Ok(()) + } else { + Err(CubeError::internal(format!( + "For node {} expected {} inputs but received {}", + node_type, + expected, + inputs.len() + ))) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/regular_measures_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/logical_query_modifers.rs similarity index 57% rename from rust/cubesqlplanner/cubesqlplanner/src/logical_plan/regular_measures_query.rs rename to rust/cubesqlplanner/cubesqlplanner/src/logical_plan/logical_query_modifers.rs index b695df64f2ae6..2c78e45b7966f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/regular_measures_query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/logical_query_modifers.rs @@ -1,30 +1,15 @@ -use super::pretty_print::*; -use super::LogicalFilter; -use super::LogicalJoin; -use super::LogicalSchema; +use super::*; use crate::planner::query_properties::OrderByItem; -use std::rc::Rc; -#[derive(Clone)] -pub struct RegularMeasuresQuery { - pub schema: Rc, - pub filter: Rc, +pub struct LogicalQueryModifiers { pub offset: Option, pub limit: Option, pub ungrouped: bool, pub order_by: Vec, - pub source: Rc, } -impl PrettyPrint for RegularMeasuresQuery { +impl PrettyPrint for LogicalQueryModifiers { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { - result.println("RegularMeasuresQuery: ", state); - let state = state.new_level(); - let details_state = state.new_level(); - result.println("schema:", &state); - self.schema.pretty_print(result, &details_state); - result.println("filters:", &state); - self.filter.pretty_print(result, &details_state); if let Some(offset) = &self.offset { result.println(&format!("offset:{}", offset), &state); } @@ -33,6 +18,7 @@ impl PrettyPrint for RegularMeasuresQuery { } result.println(&format!("ungrouped:{}", self.ungrouped), &state); if !self.order_by.is_empty() { + let details_state = state.new_level(); result.println("order_by:", &state); for order_by in self.order_by.iter() { result.println( @@ -45,8 +31,5 @@ impl PrettyPrint for RegularMeasuresQuery { ); } } - - result.println("source:", &state); - self.source.pretty_print(result, &details_state); } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/measure_subquery.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/measure_subquery.rs index 78eb6635b0fd8..2e99dc5c7681d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/measure_subquery.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/measure_subquery.rs @@ -1,34 +1,47 @@ use super::*; -use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use std::rc::Rc; pub struct MeasureSubquery { - pub primary_keys_dimensions: Vec>, - pub measures: Vec>, - pub dimension_subqueries: Vec>, + pub schema: Rc, pub source: Rc, } +impl LogicalNode for MeasureSubquery { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::MeasureSubquery(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![self.source.as_plan_node()] + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 1, self.node_name())?; + let source = &inputs[0]; + Ok(Rc::new(Self { + schema: self.schema.clone(), + source: source.clone().into_logical_node()?, + })) + } + + fn node_name(&self) -> &'static str { + "MeasureSubquery" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::MeasureSubquery(query) = plan_node { + Ok(query) + } else { + Err(cast_error(&plan_node, "MeasureSubquery")) + } + } +} + impl PrettyPrint for MeasureSubquery { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { let details_state = state.new_level(); - result.println( - &format!( - "primary_key_dimensions: {}", - print_symbols(&self.primary_keys_dimensions) - ), - state, - ); - result.println( - &format!("measures: {}", print_symbols(&self.measures)), - state, - ); - result.println("dimension_subqueries:", state); - if !self.dimension_subqueries.is_empty() { - for subquery in self.dimension_subqueries.iter() { - subquery.pretty_print(result, &details_state); - } - } + result.println("schema:", &state); + self.schema.pretty_print(result, &details_state); result.println("source:", state); self.source.pretty_print(result, &details_state); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs index 09860945e97e7..2244c5d6b52b7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs @@ -3,35 +3,34 @@ mod cube; mod dimension_subquery; mod filter; mod full_key_aggregate; -mod full_key_aggregate_query; mod join; mod keys_subquery; +mod logical_node; +mod logical_query_modifers; mod measure_subquery; mod multistage; pub mod optimizers; mod pre_aggregation; pub mod pretty_print; mod query; -mod regular_measures_query; mod resolve_multiplied_measures; mod schema; -mod simple_query; +pub mod visitor; pub use aggregate_multiplied_subquery::*; pub use cube::*; pub use dimension_subquery::*; pub use filter::*; pub use full_key_aggregate::*; -pub use full_key_aggregate_query::*; pub use join::*; pub use keys_subquery::*; +pub use logical_node::*; +pub use logical_query_modifers::*; pub use measure_subquery::*; pub use multistage::*; pub use optimizers::*; pub use pre_aggregation::*; pub use pretty_print::*; pub use query::*; -pub use regular_measures_query::*; pub use resolve_multiplied_measures::*; pub use schema::*; -pub use simple_query::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/calculation.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/calculation.rs index c50ea61994919..4afd6949deeaa 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/calculation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/calculation.rs @@ -1,10 +1,11 @@ use crate::logical_plan::*; use crate::planner::query_properties::OrderByItem; use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use itertools::Itertools; use std::rc::Rc; -#[derive(PartialEq)] +#[derive(PartialEq, Clone)] pub enum MultiStageCalculationType { Rank, Aggregate, @@ -21,7 +22,7 @@ impl ToString for MultiStageCalculationType { } } -#[derive(PartialEq)] +#[derive(PartialEq, Clone)] pub enum MultiStageCalculationWindowFunction { Rank, Window, @@ -96,3 +97,40 @@ impl PrettyPrint for MultiStageMeasureCalculation { self.source.pretty_print(result, &details_state); } } + +impl LogicalNode for MultiStageMeasureCalculation { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::MultiStageMeasureCalculation(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![self.source.as_plan_node()] + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 1, self.node_name())?; + let source = &inputs[0]; + + Ok(Rc::new(Self { + schema: self.schema.clone(), + is_ungrouped: self.is_ungrouped, + calculation_type: self.calculation_type.clone(), + partition_by: self.partition_by.clone(), + window_function_to_use: self.window_function_to_use.clone(), + order_by: self.order_by.clone(), + source: source.clone().into_logical_node()?, + })) + } + + fn node_name(&self) -> &'static str { + "MultiStageMeasureCalculation" + } + + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::MultiStageMeasureCalculation(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "MultiStageMeasureCalculation")) + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/common.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/common.rs index b3eaa47b31531..10c4e8f0880dc 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/common.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/common.rs @@ -1,37 +1,19 @@ use crate::logical_plan::pretty_print::*; use crate::planner::planners::multi_stage::MultiStageAppliedState; -use crate::planner::BaseMember; -use itertools::Itertools; - impl PrettyPrint for MultiStageAppliedState { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { let details_state = state.new_level(); result.println( &format!( "-time_dimensions: {}", - print_symbols( - &self - .time_dimensions() - .iter() - .map(|d| d.member_evaluator()) - .collect_vec() - ) + print_symbols(&self.time_dimensions()) ), state, ); result.println( - &format!( - "-dimensions: {}", - print_symbols( - &self - .dimensions() - .iter() - .map(|d| d.member_evaluator()) - .collect_vec() - ) - ), + &format!("-dimensions: {}", print_symbols(&self.dimensions())), state, ); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/get_date_range.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/get_date_range.rs index 557297240c822..02db150fa8ec2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/get_date_range.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/get_date_range.rs @@ -1,12 +1,45 @@ use crate::logical_plan::*; use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use std::rc::Rc; + pub struct MultiStageGetDateRange { pub time_dimension: Rc, - pub dimension_subqueries: Vec>, pub source: Rc, } +impl LogicalNode for MultiStageGetDateRange { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::MultiStageGetDateRange(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![self.source.as_plan_node()] + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 1, self.node_name())?; + let source = &inputs[0]; + + Ok(Rc::new(Self { + time_dimension: self.time_dimension.clone(), + source: source.clone().into_logical_node()?, + })) + } + + fn node_name(&self) -> &'static str { + "MultiStageGetDateRange" + } + + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::MultiStageGetDateRange(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "MultiStageGetDateRange")) + } + } +} + impl PrettyPrint for MultiStageGetDateRange { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println("Get Date Range", state); @@ -16,12 +49,6 @@ impl PrettyPrint for MultiStageGetDateRange { &format!("time_dimension: {}", self.time_dimension.full_name()), &details_state, ); - if !self.dimension_subqueries.is_empty() { - result.println("dimension_subqueries:", &state); - for subquery in self.dimension_subqueries.iter() { - subquery.pretty_print(result, &details_state); - } - } result.println("source:", &state); self.source.pretty_print(result, &details_state); } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/leaf_measure.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/leaf_measure.rs index fe454c2c03f37..4793ba89f13a8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/leaf_measure.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/leaf_measure.rs @@ -1,6 +1,7 @@ use crate::logical_plan::*; use crate::planner::planners::multi_stage::TimeShiftState; use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use std::rc::Rc; pub struct MultiStageLeafMeasure { @@ -47,3 +48,38 @@ impl PrettyPrint for MultiStageLeafMeasure { self.query.pretty_print(result, &details_state); } } + +impl LogicalNode for MultiStageLeafMeasure { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::MultiStageLeafMeasure(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![self.query.as_plan_node()] + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 1, self.node_name())?; + let query = &inputs[0]; + + Ok(Rc::new(Self { + measure: self.measure.clone(), + render_measure_as_state: self.render_measure_as_state, + render_measure_for_ungrouped: self.render_measure_for_ungrouped, + time_shifts: self.time_shifts.clone(), + query: query.clone().into_logical_node()?, + })) + } + + fn node_name(&self) -> &'static str { + "MultiStageLeafMeasure" + } + + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::MultiStageLeafMeasure(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "MultiStageLeafMeasure")) + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/member.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/member.rs index 453f4a156416f..6e7d9db81d907 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/member.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/member.rs @@ -1,11 +1,35 @@ use crate::logical_plan::*; +use cubenativeutils::CubeError; +use std::rc::Rc; pub enum MultiStageMemberLogicalType { - LeafMeasure(MultiStageLeafMeasure), - MeasureCalculation(MultiStageMeasureCalculation), - GetDateRange(MultiStageGetDateRange), - TimeSeries(MultiStageTimeSeries), - RollingWindow(MultiStageRollingWindow), + LeafMeasure(Rc), + MeasureCalculation(Rc), + GetDateRange(Rc), + TimeSeries(Rc), + RollingWindow(Rc), +} + +impl MultiStageMemberLogicalType { + fn as_plan_node(&self) -> PlanNode { + match self { + Self::LeafMeasure(item) => item.as_plan_node(), + Self::MeasureCalculation(item) => item.as_plan_node(), + Self::GetDateRange(item) => item.as_plan_node(), + Self::TimeSeries(item) => item.as_plan_node(), + Self::RollingWindow(item) => item.as_plan_node(), + } + } + + fn with_plan_node(&self, plan_node: PlanNode) -> Result { + Ok(match self { + Self::LeafMeasure(_) => Self::LeafMeasure(plan_node.into_logical_node()?), + Self::MeasureCalculation(_) => Self::MeasureCalculation(plan_node.into_logical_node()?), + Self::GetDateRange(_) => Self::GetDateRange(plan_node.into_logical_node()?), + Self::TimeSeries(_) => Self::TimeSeries(plan_node.into_logical_node()?), + Self::RollingWindow(_) => Self::RollingWindow(plan_node.into_logical_node()?), + }) + } } impl PrettyPrint for MultiStageMemberLogicalType { @@ -25,6 +49,38 @@ pub struct LogicalMultiStageMember { pub member_type: MultiStageMemberLogicalType, } +impl LogicalNode for LogicalMultiStageMember { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::LogicalMultiStageMember(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![self.member_type.as_plan_node()] + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 1, self.node_name())?; + let input = inputs[0].clone(); + + Ok(Rc::new(Self { + name: self.name.clone(), + member_type: self.member_type.with_plan_node(input)?, + })) + } + + fn node_name(&self) -> &'static str { + "LogicalMultiStageMember" + } + + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::LogicalMultiStageMember(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "LogicalMultiStageMember")) + } + } +} + impl PrettyPrint for LogicalMultiStageMember { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println(&format!("MultiStageMember `{}`: ", self.name), state); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/rolling_window.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/rolling_window.rs index 9301ec0e77f7f..3a1555bb6a6e0 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/rolling_window.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/rolling_window.rs @@ -2,6 +2,7 @@ use crate::logical_plan::*; use crate::planner::query_properties::OrderByItem; use crate::planner::sql_evaluator::MemberSymbol; use crate::planner::Granularity; +use cubenativeutils::CubeError; use std::rc::Rc; pub struct MultiStageRegularRollingWindow { @@ -110,3 +111,30 @@ impl PrettyPrint for MultiStageRollingWindow { ); } } + +impl LogicalNode for MultiStageRollingWindow { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::MultiStageRollingWindow(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![] // MultiStageRollingWindow has no inputs + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 0, self.node_name())?; + Ok(self) + } + + fn node_name(&self) -> &'static str { + "MultiStageRollingWindow" + } + + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::MultiStageRollingWindow(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "MultiStageRollingWindow")) + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/time_series.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/time_series.rs index fe5cf3215a545..f44ceeebd849c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/time_series.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/multistage/time_series.rs @@ -1,5 +1,6 @@ use crate::logical_plan::*; use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; use std::rc::Rc; pub struct MultiStageTimeSeries { pub time_dimension: Rc, @@ -32,3 +33,30 @@ impl PrettyPrint for MultiStageTimeSeries { } } } + +impl LogicalNode for MultiStageTimeSeries { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::MultiStageTimeSeries(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![] // MultiStageTimeSeries has no inputs + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 0, self.node_name())?; + Ok(self) + } + + fn node_name(&self) -> &'static str { + "MultiStageTimeSeries" + } + + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::MultiStageTimeSeries(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "MultiStageTimeSeries")) + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/cube_names_collector.rs index 91d2d69311514..a46fd257ecf57 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/cube_names_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/cube_names_collector.rs @@ -1,160 +1,30 @@ +use crate::logical_plan::visitor::*; use crate::logical_plan::*; use cubenativeutils::CubeError; use itertools::Itertools; use std::collections::HashSet; use std::rc::Rc; -pub struct CubeNamesCollector { +struct CubeNamesCollector { cube_names: HashSet, } -impl CubeNamesCollector { - pub fn new() -> Self { - Self { - cube_names: HashSet::new(), - } - } - - pub fn collect(&mut self, query: &Query) -> Result<(), CubeError> { - match query { - Query::SimpleQuery(query) => self.collect_from_simple_query(query), - Query::FullKeyAggregateQuery(query) => { - self.collect_from_full_key_aggregate_query(query) - } - } - } - - pub fn result(self) -> Vec { - self.cube_names.into_iter().collect_vec() - } - - fn collect_from_simple_query(&mut self, query: &SimpleQuery) -> Result<(), CubeError> { - self.collect_from_simple_query_source(&query.source)?; - self.collect_from_dimension_subqueries(&query.dimension_subqueries)?; - Ok(()) - } - - fn collect_from_full_key_aggregate_query( - &mut self, - query: &FullKeyAggregateQuery, - ) -> Result<(), CubeError> { - self.collect_from_full_key_aggregate(&query.source)?; - for member in query.multistage_members.iter() { - self.collect_from_multi_stage_member(member)?; - } - Ok(()) - } - - fn collect_from_multi_stage_member( - &mut self, - member: &Rc, - ) -> Result<(), CubeError> { - match &member.member_type { - MultiStageMemberLogicalType::LeafMeasure(leaf_measure) => { - self.collect_from_multi_stage_leaf_measure(leaf_measure) - } - _ => Ok(()), - } - } - - fn collect_from_multi_stage_leaf_measure( - &mut self, - leaf_measure: &MultiStageLeafMeasure, - ) -> Result<(), CubeError> { - self.collect(&leaf_measure.query)?; - Ok(()) - } - - fn collect_from_measure_subquery( - &mut self, - subquery: &Rc, - ) -> Result<(), CubeError> { - self.collect_from_logical_join(&subquery.source)?; - self.collect_from_dimension_subqueries(&subquery.dimension_subqueries)?; - Ok(()) - } - - fn collect_from_full_key_aggregate( - &mut self, - full_key_aggregate: &Rc, - ) -> Result<(), CubeError> { - if let Some(resolve_multiplied_measures) = &full_key_aggregate.multiplied_measures_resolver - { - self.collect_from_resolved_multiplied_measures(resolve_multiplied_measures)?; - } - Ok(()) - } - - fn collect_from_resolved_multiplied_measures( - &mut self, - resolved_multiplied_measures: &ResolvedMultipliedMeasures, - ) -> Result<(), CubeError> { - match resolved_multiplied_measures { - ResolvedMultipliedMeasures::ResolveMultipliedMeasures(resolve_multiplied_measures) => { - self.collect_from_multiplied_measures_resolver(resolve_multiplied_measures)? - } - ResolvedMultipliedMeasures::PreAggregation(_) => {} - } - Ok(()) - } - fn collect_from_multiplied_measures_resolver( - &mut self, - resolver: &ResolveMultipliedMeasures, - ) -> Result<(), CubeError> { - for regular_subquery in resolver.regular_measure_subqueries.iter() { - self.collect_from_simple_query(®ular_subquery)?; - } - for aggregate_multiplied_subquery in resolver.aggregate_multiplied_subqueries.iter() { - self.collect_from_aggregate_multiplied_subquery(&aggregate_multiplied_subquery)?; - } - Ok(()) - } - - fn collect_from_aggregate_multiplied_subquery( - &mut self, - subquery: &Rc, - ) -> Result<(), CubeError> { - self.collect_from_logical_join(&subquery.keys_subquery.source)?; - match subquery.source.as_ref() { - AggregateMultipliedSubquerySouce::Cube => { - self.cube_names.insert(subquery.pk_cube.name.clone()); - } - AggregateMultipliedSubquerySouce::MeasureSubquery(measure_subquery) => { - self.collect_from_measure_subquery(&measure_subquery)?; - } - } - Ok(()) - } - - fn collect_from_simple_query_source( - &mut self, - source: &SimpleQuerySource, - ) -> Result<(), CubeError> { - match source { - SimpleQuerySource::LogicalJoin(join) => self.collect_from_logical_join(join), - SimpleQuerySource::PreAggregation(_) => Ok(()), - } - } - - fn collect_from_logical_join(&mut self, join: &Rc) -> Result<(), CubeError> { - self.cube_names.insert(join.root.name.clone()); - for join_item in join.joins.iter() { - match join_item { - LogicalJoinItem::CubeJoinItem(cube_join_item) => { - self.cube_names.insert(cube_join_item.cube.name.clone()); - } - } +impl LogicalNodeVisitor for CubeNamesCollector { + fn process_node(&mut self, node: &PlanNode) -> Result<(), CubeError> { + if let PlanNode::Cube(cube) = node { + self.cube_names.insert(cube.name.clone()); } Ok(()) } +} - fn collect_from_dimension_subqueries( - &mut self, - dimension_subqueries: &Vec>, - ) -> Result<(), CubeError> { - for subquery in dimension_subqueries.iter() { - self.collect(&subquery.query)?; - } - Ok(()) - } +pub fn collect_cube_names_from_node( + node: &Rc, +) -> Result, CubeError> { + let mut collector = CubeNamesCollector { + cube_names: HashSet::new(), + }; + let visitor = LogicalPlanVisitor::new(); + visitor.visit(&mut collector, node)?; + Ok(collector.cube_names.into_iter().collect_vec()) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/compiled_pre_aggregation.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/compiled_pre_aggregation.rs index d95c75af20d8b..39ad35db4b53b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/compiled_pre_aggregation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/compiled_pre_aggregation.rs @@ -25,6 +25,7 @@ pub struct PreAggregationUnion { #[derive(Clone)] pub struct PreAggregationTable { pub cube_name: String, + pub cube_alias: String, pub name: String, pub alias: Option, } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs index f61e941dedd2d..2be3c168f379e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs @@ -1,5 +1,4 @@ use super::CompiledPreAggregation; -use super::MatchState; use crate::plan::filter::FilterGroupOperator; use crate::plan::FilterItem; use crate::planner::filter::BaseFilter; @@ -12,6 +11,25 @@ use cubenativeutils::CubeError; use std::collections::HashMap; use std::rc::Rc; +#[derive(Clone, Debug, PartialEq)] +pub enum MatchState { + Partial, + Full, + NotMatched, +} + +impl MatchState { + pub fn combine(&self, other: &MatchState) -> MatchState { + if matches!(self, MatchState::NotMatched) || matches!(other, MatchState::NotMatched) { + return MatchState::NotMatched; + } + if matches!(self, MatchState::Partial) || matches!(other, MatchState::Partial) { + return MatchState::Partial; + } + MatchState::Full + } +} + pub struct DimensionMatcher<'a> { query_tools: Rc, pre_aggregation: &'a CompiledPreAggregation, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/mod.rs index 51c2da6e499db..c50619a4f867e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/mod.rs @@ -3,7 +3,6 @@ mod dimension_matcher; mod measure_matcher; mod optimizer; mod original_sql_collector; -mod original_sql_optimizer; mod pre_aggregations_compiler; pub use compiled_pre_aggregation::*; @@ -11,5 +10,4 @@ use dimension_matcher::*; use measure_matcher::*; pub use optimizer::*; pub use original_sql_collector::*; -pub use original_sql_optimizer::*; pub use pre_aggregations_compiler::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/optimizer.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/optimizer.rs index ee5db813f8ea7..d0d877d48d856 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/optimizer.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/optimizer.rs @@ -1,5 +1,6 @@ use super::PreAggregationsCompiler; use super::*; +use crate::logical_plan::visitor::{LogicalPlanRewriter, NodeRewriteResult}; use crate::logical_plan::*; use crate::plan::FilterItem; use crate::planner::query_tools::QueryTools; @@ -8,25 +9,6 @@ use cubenativeutils::CubeError; use std::collections::{HashMap, HashSet}; use std::rc::Rc; -#[derive(Clone, Debug, PartialEq)] -pub enum MatchState { - Partial, - Full, - NotMatched, -} - -impl MatchState { - pub fn combine(&self, other: &MatchState) -> MatchState { - if matches!(self, MatchState::NotMatched) || matches!(other, MatchState::NotMatched) { - return MatchState::NotMatched; - } - if matches!(self, MatchState::Partial) || matches!(other, MatchState::Partial) { - return MatchState::Partial; - } - MatchState::Full - } -} - pub struct PreAggregationOptimizer { query_tools: Rc, allow_multi_stage: bool, @@ -43,9 +25,7 @@ impl PreAggregationOptimizer { } pub fn try_optimize(&mut self, plan: Rc) -> Result>, CubeError> { - let mut cube_names_collector = CubeNamesCollector::new(); - cube_names_collector.collect(&plan)?; - let cube_names = cube_names_collector.result(); + let cube_names = collect_cube_names_from_node(&plan)?; let mut compiler = PreAggregationsCompiler::try_new(self.query_tools.clone(), &cube_names)?; let compiled_pre_aggregations = compiler.compile_all_pre_aggregations()?; @@ -69,147 +49,135 @@ impl PreAggregationOptimizer { query: Rc, pre_aggregation: &Rc, ) -> Result>, CubeError> { - match query.as_ref() { - Query::SimpleQuery(query) => self.try_rewrite_simple_query(query, pre_aggregation), - Query::FullKeyAggregateQuery(query) => { - self.try_rewrite_full_key_aggregate_query(query, pre_aggregation) - } + if query.multistage_members.is_empty() { + self.try_rewrite_simple_query(&query, pre_aggregation) + } else if !self.allow_multi_stage { + Ok(None) + } else { + self.try_rewrite_query_with_multistages(&query, pre_aggregation) } } fn try_rewrite_simple_query( &mut self, - query: &SimpleQuery, + query: &Rc, pre_aggregation: &Rc, ) -> Result>, CubeError> { if self.is_schema_and_filters_match(&query.schema, &query.filter, pre_aggregation)? { - let mut new_query = SimpleQuery::clone(&query); - new_query.source = SimpleQuerySource::PreAggregation( - self.make_pre_aggregation_source(pre_aggregation)?, - ); - Ok(Some(Rc::new(Query::SimpleQuery(new_query)))) + let mut new_query = query.as_ref().clone(); + new_query.source = + QuerySource::PreAggregation(self.make_pre_aggregation_source(pre_aggregation)?); + Ok(Some(Rc::new(new_query))) } else { Ok(None) } } - fn try_rewrite_full_key_aggregate_query( + fn try_rewrite_query_with_multistages( &mut self, - query: &FullKeyAggregateQuery, + query: &Rc, pre_aggregation: &Rc, ) -> Result>, CubeError> { - if !self.allow_multi_stage && !query.multistage_members.is_empty() { - return Ok(None); - } - if self.allow_multi_stage && !query.multistage_members.is_empty() { - return self - .try_rewrite_full_key_aggregate_query_with_multi_stages(query, pre_aggregation); + let rewriter = LogicalPlanRewriter::new(); + let mut has_unrewritten_leaf = false; + + let mut rewritten_multistages = Vec::new(); + for multi_stage in &query.multistage_members { + let rewritten = rewriter.rewrite_top_down_with(multi_stage.clone(), |plan_node| { + let res = match plan_node { + PlanNode::MultiStageLeafMeasure(multi_stage_leaf_measure) => { + if let Some(rewritten) = self.try_rewrite_query( + multi_stage_leaf_measure.query.clone(), + pre_aggregation, + )? { + let new_leaf = Rc::new(MultiStageLeafMeasure { + measure: multi_stage_leaf_measure.measure.clone(), + render_measure_as_state: multi_stage_leaf_measure + .render_measure_as_state + .clone(), + render_measure_for_ungrouped: multi_stage_leaf_measure + .render_measure_for_ungrouped + .clone(), + time_shifts: multi_stage_leaf_measure.time_shifts.clone(), + query: rewritten, + }); + NodeRewriteResult::rewritten(new_leaf.as_plan_node()) + } else { + has_unrewritten_leaf = true; + NodeRewriteResult::stop() + } + } + PlanNode::LogicalMultiStageMember(_) => NodeRewriteResult::pass(), + _ => NodeRewriteResult::stop(), + }; + Ok(res) + })?; + rewritten_multistages.push(rewritten); } - if self.is_schema_and_filters_match(&query.schema, &query.filter, pre_aggregation)? { - let source = SimpleQuerySource::PreAggregation( - self.make_pre_aggregation_source(pre_aggregation)?, - ); - let new_query = SimpleQuery { - schema: query.schema.clone(), - dimension_subqueries: vec![], - filter: query.filter.clone(), - offset: query.offset, - limit: query.limit, - ungrouped: query.ungrouped, - order_by: query.order_by.clone(), - source, - }; - Ok(Some(Rc::new(Query::SimpleQuery(new_query)))) - } else { - Ok(None) - } - } - - fn try_rewrite_full_key_aggregate_query_with_multi_stages( - &mut self, - query: &FullKeyAggregateQuery, - pre_aggregation: &Rc, - ) -> Result>, CubeError> { - let used_multi_stage_symbols = self.collect_multi_stage_symbols(&query.source); - let mut multi_stages_queries = query.multistage_members.clone(); - let mut rewrited_multistage = multi_stages_queries - .iter() - .map(|query| (query.name.clone(), false)) - .collect::>(); - - for (_, multi_stage_name) in used_multi_stage_symbols.iter() { - self.try_rewrite_multistage( - multi_stage_name, - &mut multi_stages_queries, - &mut rewrited_multistage, - pre_aggregation, - )?; - } - let all_multi_stage_rewrited = rewrited_multistage.values().all(|v| *v); - if !all_multi_stage_rewrited { + if has_unrewritten_leaf { return Ok(None); } - let source = if let Some(resolver_multiplied_measures) = - &query.source.multiplied_measures_resolver - { - if let ResolvedMultipliedMeasures::ResolveMultipliedMeasures( - resolver_multiplied_measures, - ) = resolver_multiplied_measures + let source = if let QuerySource::FullKeyAggregate(full_key_aggregate) = &query.source { + let fk_source = if let Some(resolver_multiplied_measures) = + &full_key_aggregate.multiplied_measures_resolver { - if self.is_schema_and_filters_match( - &resolver_multiplied_measures.schema, - &resolver_multiplied_measures.filter, - &pre_aggregation, - )? { - let pre_aggregation_source = - self.make_pre_aggregation_source(pre_aggregation)?; - - let pre_aggregation_query = SimpleQuery { - schema: resolver_multiplied_measures.schema.clone(), - dimension_subqueries: vec![], - filter: resolver_multiplied_measures.filter.clone(), - offset: None, - limit: None, - ungrouped: false, - order_by: vec![], - source: SimpleQuerySource::PreAggregation(pre_aggregation_source), - }; - Rc::new(FullKeyAggregate { - join_dimensions: query.source.join_dimensions.clone(), - use_full_join_and_coalesce: query.source.use_full_join_and_coalesce, - multiplied_measures_resolver: Some( - ResolvedMultipliedMeasures::PreAggregation(Rc::new( - pre_aggregation_query, - )), - ), - multi_stage_subquery_refs: query.source.multi_stage_subquery_refs.clone(), - }) + if let ResolvedMultipliedMeasures::ResolveMultipliedMeasures( + resolver_multiplied_measures, + ) = resolver_multiplied_measures + { + if self.is_schema_and_filters_match( + &resolver_multiplied_measures.schema, + &resolver_multiplied_measures.filter, + &pre_aggregation, + )? { + let pre_aggregation_source = + self.make_pre_aggregation_source(pre_aggregation)?; + + let pre_aggregation_query = Query { + schema: resolver_multiplied_measures.schema.clone(), + filter: resolver_multiplied_measures.filter.clone(), + modifers: Rc::new(LogicalQueryModifiers { + offset: None, + limit: None, + ungrouped: false, + order_by: vec![], + }), + source: QuerySource::PreAggregation(pre_aggregation_source), + multistage_members: vec![], + }; + Some(ResolvedMultipliedMeasures::PreAggregation(Rc::new( + pre_aggregation_query, + ))) + } else { + return Ok(None); + } } else { - return Ok(None); + Some(resolver_multiplied_measures.clone()) } } else { - query.source.clone() - } + None + }; + let mut result = full_key_aggregate.as_ref().clone(); + result.multiplied_measures_resolver = fk_source; + QuerySource::FullKeyAggregate(Rc::new(result)) } else { query.source.clone() }; - let result = FullKeyAggregateQuery { - multistage_members: multi_stages_queries, + let result = Query { + multistage_members: rewritten_multistages, schema: query.schema.clone(), filter: query.filter.clone(), - offset: query.offset, - limit: query.limit, - ungrouped: query.ungrouped, - order_by: query.order_by.clone(), + modifers: query.modifers.clone(), source, }; - Ok(Some(Rc::new(Query::FullKeyAggregateQuery(result)))) + + Ok(Some(Rc::new(result))) } - fn try_rewrite_multistage( + /* fn try_rewrite_multistage( &mut self, multi_stage_name: &String, multi_stage_queries: &mut Vec>, @@ -400,17 +368,12 @@ impl PreAggregationOptimizer { } } symbols - } + } */ fn make_pre_aggregation_source( &mut self, pre_aggregation: &Rc, ) -> Result, CubeError> { - /* let pre_aggregation_obj = self.query_tools.base_tools().get_pre_aggregation_by_name( - pre_aggregation.cube_name.clone(), - pre_aggregation.name.clone(), - )?; */ - //if let Some(table_name) = &pre_aggregation_obj.static_data().table_name { let schema = LogicalSchema { time_dimensions: vec![], dimensions: pre_aggregation diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_collector.rs index 183cdab2d3e7e..74ec6bd2508ed 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_collector.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_collector.rs @@ -13,10 +13,8 @@ impl OriginalSqlCollector { Self { query_tools } } - pub fn collect(&mut self, plan: &Query) -> Result, CubeError> { - let mut cube_names_collector = CubeNamesCollector::new(); - cube_names_collector.collect(&plan)?; - let cube_names = cube_names_collector.result(); + pub fn collect(&mut self, plan: &Rc) -> Result, CubeError> { + let cube_names = collect_cube_names_from_node(&plan)?; let mut result = HashMap::new(); for cube_name in cube_names.iter() { let pre_aggregations = self diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_optimizer.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_optimizer.rs index 4edb5beb33007..28a565772b1ac 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_optimizer.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/original_sql_optimizer.rs @@ -41,10 +41,7 @@ impl OriginalSqlOptimizer { multistage_members: query.multistage_members.clone(), schema: query.schema.clone(), filter: query.filter.clone(), - offset: query.offset, - limit: query.limit, - ungrouped: query.ungrouped, - order_by: query.order_by.clone(), + modifers: query.modifers.clone(), source: optimized_source.unwrap_or_else(|| query.source.clone()), })) } else { @@ -146,10 +143,7 @@ impl OriginalSqlOptimizer { .unwrap_or_else(|| query.dimension_subqueries.clone()), schema: query.schema.clone(), filter: query.filter.clone(), - offset: query.offset, - limit: query.limit, - ungrouped: query.ungrouped, - order_by: query.order_by.clone(), + modifers: query.modifers.clone(), })) } else { Ok(None) 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 1caef67833177..4d606cda130e7 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 @@ -157,8 +157,18 @@ impl PreAggregationsCompiler { let source = if static_data.pre_aggregation_type == "rollupJoin" { PreAggregationSource::Join(self.build_join_source(&measures, &dimensions, &rollups)?) } else { + let cube = self + .query_tools + .cube_evaluator() + .cube_from_path(name.cube_name.clone())?; + let cube_alias = if let Some(alias) = &cube.static_data().sql_alias { + alias.clone() + } else { + name.cube_name.clone() + }; PreAggregationSource::Single(PreAggregationTable { cube_name: name.cube_name.clone(), + cube_alias, name: name.name.clone(), alias: static_data.sql_alias.clone(), }) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs index e2ee80f9aaacf..955f73afa9ba8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs @@ -1,8 +1,8 @@ -use super::pre_aggregation::PreAggregationSource; use super::*; -use crate::planner::sql_evaluator::MemberSymbol; +use crate::{plan::QualifiedColumnName, planner::sql_evaluator::MemberSymbol}; +use cubenativeutils::CubeError; use itertools::Itertools; -use std::rc::Rc; +use std::{collections::HashMap, rc::Rc}; pub struct PreAggregation { pub name: String, @@ -16,6 +16,89 @@ pub struct PreAggregation { pub cube_name: String, } +impl LogicalNode for PreAggregation { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::PreAggregation(self.clone()) + } + + fn inputs(&self) -> Vec { + vec![] // PreAggregation has no inputs + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + check_inputs_len(&inputs, 0, self.node_name())?; + Ok(self) + } + + fn node_name(&self) -> &'static str { + "PreAggregation" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::PreAggregation(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "PreAggregation")) + } + } +} + +impl PreAggregation { + pub fn all_dimensions_refererences(&self) -> HashMap { + let mut res = HashMap::new(); + + for dim in self.dimensions.iter() { + let alias = dim.alias(); + res.insert( + dim.full_name(), + QualifiedColumnName::new(None, alias.clone()), + ); + } + for (dim, granularity) in self.time_dimensions.iter() { + let base_symbol = if let Ok(td) = dim.as_time_dimension() { + td.base_symbol().clone() + } else { + dim.clone() + }; + let suffix = if let Some(granularity) = &granularity { + format!("_{}", granularity.clone()) + } else { + "".to_string() + }; + let alias = format!("{}{}", base_symbol.alias(), suffix); + res.insert( + dim.full_name(), + QualifiedColumnName::new(None, alias.clone()), + ); + } + + if let PreAggregationSource::Join(join) = self.source.as_ref() { + 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 + } + pub fn all_measures_refererences(&self) -> HashMap { + self.measures + .iter() + .map(|measure| { + let alias = measure.alias(); + ( + measure.full_name(), + QualifiedColumnName::new(None, alias.clone()), + ) + }) + .collect() + } +} + impl PrettyPrint for PreAggregation { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println("PreAggregation: ", state); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs index 27b561a8f9f8c..9e1010dcfc590 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs @@ -1,15 +1,144 @@ use super::*; +use cubenativeutils::CubeError; +use std::rc::Rc; -pub enum Query { - SimpleQuery(SimpleQuery), - FullKeyAggregateQuery(FullKeyAggregateQuery), +#[derive(Clone)] +pub enum QuerySource { + LogicalJoin(Rc), + FullKeyAggregate(Rc), + PreAggregation(Rc), } -impl PrettyPrint for Query { +impl QuerySource { + fn as_plan_node(&self) -> PlanNode { + match self { + Self::LogicalJoin(item) => item.as_plan_node(), + Self::FullKeyAggregate(item) => item.as_plan_node(), + Self::PreAggregation(item) => item.as_plan_node(), + } + } + fn with_plan_node(&self, plan_node: PlanNode) -> Result { + Ok(match self { + Self::LogicalJoin(_) => Self::LogicalJoin(plan_node.into_logical_node()?), + Self::FullKeyAggregate(_) => Self::FullKeyAggregate(plan_node.into_logical_node()?), + Self::PreAggregation(_) => Self::PreAggregation(plan_node.into_logical_node()?), + }) + } +} + +impl PrettyPrint for QuerySource { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { match self { - Self::SimpleQuery(query) => query.pretty_print(result, state), - Self::FullKeyAggregateQuery(query) => query.pretty_print(result, state), + QuerySource::LogicalJoin(join) => join.pretty_print(result, state), + QuerySource::FullKeyAggregate(full_key) => full_key.pretty_print(result, state), + QuerySource::PreAggregation(pre_aggregation) => { + pre_aggregation.pretty_print(result, state) + } } } } +#[derive(Clone)] +pub struct Query { + pub multistage_members: Vec>, + pub schema: Rc, + pub filter: Rc, + pub modifers: Rc, + pub source: QuerySource, +} + +impl LogicalNode for Query { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::Query(self.clone()) + } + + fn inputs(&self) -> Vec { + QueryInputPacker::pack(self) + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + let QueryInputUnPacker { + multistage_members, + source, + } = QueryInputUnPacker::new(&self, &inputs)?; + + Ok(Rc::new(Self { + multistage_members: multistage_members + .iter() + .map(|member| member.clone().into_logical_node()) + .collect::, _>>()?, + schema: self.schema.clone(), + filter: self.filter.clone(), + modifers: self.modifers.clone(), + source: self.source.with_plan_node(source.clone())?, + })) + } + + fn node_name(&self) -> &'static str { + "Query" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::Query(query) = plan_node { + Ok(query) + } else { + Err(cast_error(&plan_node, "Query")) + } + } +} + +pub struct QueryInputPacker; + +impl QueryInputPacker { + pub fn pack(query: &Query) -> Vec { + let mut result = vec![]; + result.extend( + query + .multistage_members + .iter() + .map(|member| member.as_plan_node()), + ); + result.push(query.source.as_plan_node()); + result + } +} +pub struct QueryInputUnPacker<'a> { + multistage_members: &'a [PlanNode], + source: &'a PlanNode, +} + +impl<'a> QueryInputUnPacker<'a> { + pub fn new(query: &Query, inputs: &'a Vec) -> Result { + check_inputs_len(&inputs, Self::inputs_len(query), query.node_name())?; + let multistage_members = &inputs[0..query.multistage_members.len()]; + let source = &inputs[query.multistage_members.len()]; + Ok(Self { + multistage_members, + source, + }) + } + fn inputs_len(query: &Query) -> usize { + query.multistage_members.len() + 1 + } +} + +impl PrettyPrint for Query { + fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { + result.println("Query: ", state); + let state = state.new_level(); + let details_state = state.new_level(); + if !self.multistage_members.is_empty() { + result.println("multistage_members:", &state); + for member in self.multistage_members.iter() { + member.pretty_print(result, &details_state); + } + } + + result.println("schema:", &state); + self.schema.pretty_print(result, &details_state); + result.println("filters:", &state); + self.filter.pretty_print(result, &details_state); + self.modifers.pretty_print(result, &state); + + result.println("source:", &state); + self.source.pretty_print(result, &details_state); + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/resolve_multiplied_measures.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/resolve_multiplied_measures.rs index 498a48ba9ca39..87071c439ae7f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/resolve_multiplied_measures.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/resolve_multiplied_measures.rs @@ -1,13 +1,107 @@ use super::*; +use cubenativeutils::CubeError; use std::rc::Rc; pub struct ResolveMultipliedMeasures { pub schema: Rc, pub filter: Rc, - pub regular_measure_subqueries: Vec>, + pub regular_measure_subqueries: Vec>, pub aggregate_multiplied_subqueries: Vec>, } +impl LogicalNode for ResolveMultipliedMeasures { + fn as_plan_node(self: &Rc) -> PlanNode { + PlanNode::ResolveMultipliedMeasures(self.clone()) + } + + fn inputs(&self) -> Vec { + ResolveMultipliedMeasuresInputPacker::pack(self) + } + + fn with_inputs(self: Rc, inputs: Vec) -> Result, CubeError> { + let ResolveMultipliedMeasuresInputUnPacker { + regular_measure_subqueries, + aggregate_multiplied_subqueries, + } = ResolveMultipliedMeasuresInputUnPacker::new(&self, &inputs)?; + + let regular_measure_subqueries = regular_measure_subqueries + .iter() + .map(|i| Query::try_from_plan_node(i.clone())) + .collect::>()?; + + let aggregate_multiplied_subqueries = aggregate_multiplied_subqueries + .iter() + .map(|i| AggregateMultipliedSubquery::try_from_plan_node(i.clone())) + .collect::>()?; + + Ok(Rc::new(Self { + schema: self.schema.clone(), + filter: self.filter.clone(), + regular_measure_subqueries, + aggregate_multiplied_subqueries, + })) + } + + fn node_name(&self) -> &'static str { + "ResolveMultipliedMeasures" + } + fn try_from_plan_node(plan_node: PlanNode) -> Result, CubeError> { + if let PlanNode::ResolveMultipliedMeasures(item) = plan_node { + Ok(item) + } else { + Err(cast_error(&plan_node, "ResolveMultipliedMeasures")) + } + } +} + +pub struct ResolveMultipliedMeasuresInputPacker; + +impl ResolveMultipliedMeasuresInputPacker { + pub fn pack(resolve: &ResolveMultipliedMeasures) -> Vec { + let mut result = vec![]; + result.extend( + resolve + .regular_measure_subqueries + .iter() + .map(|i| i.as_plan_node()), + ); + result.extend( + resolve + .aggregate_multiplied_subqueries + .iter() + .map(|i| i.as_plan_node()), + ); + result + } +} + +pub struct ResolveMultipliedMeasuresInputUnPacker<'a> { + regular_measure_subqueries: &'a [PlanNode], + aggregate_multiplied_subqueries: &'a [PlanNode], +} + +impl<'a> ResolveMultipliedMeasuresInputUnPacker<'a> { + pub fn new( + resolve: &ResolveMultipliedMeasures, + inputs: &'a Vec, + ) -> Result { + check_inputs_len(&inputs, Self::inputs_len(resolve), resolve.node_name())?; + + let regular_end = resolve.regular_measure_subqueries.len(); + let regular_measure_subqueries = &inputs[0..regular_end]; + let aggregate_multiplied_subqueries = &inputs[regular_end..]; + + Ok(Self { + regular_measure_subqueries, + aggregate_multiplied_subqueries, + }) + } + + fn inputs_len(resolve: &ResolveMultipliedMeasures) -> usize { + resolve.regular_measure_subqueries.len() + resolve.aggregate_multiplied_subqueries.len() + } +} + impl PrettyPrint for ResolveMultipliedMeasures { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println("ResolveMultipliedMeasures: ", state); diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/schema.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/schema.rs index 372ce52cde5a6..afa9808098f21 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/schema.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/schema.rs @@ -4,6 +4,8 @@ use super::pretty_print::*; use crate::planner::sql_evaluator::MemberSymbol; use std::collections::HashSet; use std::rc::Rc; + +#[derive(Default, Clone)] pub struct LogicalSchema { pub time_dimensions: Vec>, pub dimensions: Vec>, @@ -11,6 +13,32 @@ pub struct LogicalSchema { pub multiplied_measures: HashSet, } +impl LogicalSchema { + pub fn set_time_dimensions(mut self, time_dimensions: Vec>) -> Self { + self.time_dimensions = time_dimensions; + self + } + + pub fn set_dimensions(mut self, dimensions: Vec>) -> Self { + self.dimensions = dimensions; + self + } + + pub fn set_measures(mut self, measures: Vec>) -> Self { + self.measures = measures; + self + } + + pub fn set_multiplied_measures(mut self, multiplied_measures: HashSet) -> Self { + self.multiplied_measures = multiplied_measures; + self + } + + pub fn into_rc(self) -> Rc { + Rc::new(self) + } +} + impl LogicalSchema { pub fn find_member_positions(&self, name: &str) -> Vec { let mut result = Vec::new(); @@ -39,6 +67,14 @@ impl LogicalSchema { pub fn all_dimensions(&self) -> impl Iterator> { self.dimensions.iter().chain(self.time_dimensions.iter()) } + + pub fn all_members(&self) -> impl Iterator> { + self.all_dimensions().chain(self.measures.iter()) + } + + pub fn has_dimensions(&self) -> bool { + !self.time_dimensions.is_empty() || !self.dimensions.is_empty() + } } impl PrettyPrint for LogicalSchema { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/simple_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/simple_query.rs deleted file mode 100644 index cc9fe645f5946..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/simple_query.rs +++ /dev/null @@ -1,71 +0,0 @@ -use super::*; -use crate::planner::query_properties::OrderByItem; -use std::rc::Rc; - -#[derive(Clone)] -pub enum SimpleQuerySource { - LogicalJoin(Rc), - PreAggregation(Rc), -} -impl PrettyPrint for SimpleQuerySource { - fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { - match self { - SimpleQuerySource::LogicalJoin(join) => join.pretty_print(result, state), - SimpleQuerySource::PreAggregation(pre_aggregation) => { - pre_aggregation.pretty_print(result, state) - } - } - } -} -#[derive(Clone)] -pub struct SimpleQuery { - pub schema: Rc, - pub dimension_subqueries: Vec>, - pub filter: Rc, - pub offset: Option, - pub limit: Option, - pub ungrouped: bool, - pub order_by: Vec, - pub source: SimpleQuerySource, -} - -impl PrettyPrint for SimpleQuery { - fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { - result.println("RegularMeasuresQuery: ", state); - let state = state.new_level(); - let details_state = state.new_level(); - result.println("schema:", &state); - self.schema.pretty_print(result, &details_state); - if !self.dimension_subqueries.is_empty() { - result.println("dimension_subqueries:", &state); - for subquery in self.dimension_subqueries.iter() { - subquery.pretty_print(result, &details_state); - } - } - result.println("filters:", &state); - self.filter.pretty_print(result, &details_state); - if let Some(offset) = &self.offset { - result.println(&format!("offset:{}", offset), &state); - } - if let Some(limit) = &self.limit { - result.println(&format!("limit:{}", limit), &state); - } - result.println(&format!("ungrouped:{}", self.ungrouped), &state); - if !self.order_by.is_empty() { - result.println("order_by:", &state); - for order_by in self.order_by.iter() { - result.println( - &format!( - "{} {}", - order_by.name(), - if order_by.desc() { "desc" } else { "asc" } - ), - &details_state, - ); - } - } - - result.println("source:", &state); - self.source.pretty_print(result, &details_state); - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/mod.rs new file mode 100644 index 0000000000000..b61975374bbbd --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/mod.rs @@ -0,0 +1,4 @@ +mod rewriter; +mod visitor; +pub use rewriter::*; +pub use visitor::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/rewriter.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/rewriter.rs new file mode 100644 index 0000000000000..e56b3a4e92cd9 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/rewriter.rs @@ -0,0 +1,110 @@ +use crate::logical_plan::{LogicalNode, PlanNode}; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub struct NodeRewriteResult { + rewritten: Option, + stop: bool, +} + +impl NodeRewriteResult { + pub fn rewritten(rewrited: PlanNode) -> Self { + Self { + rewritten: Some(rewrited), + stop: true, + } + } + + pub fn stop() -> Self { + Self { + rewritten: None, + stop: true, + } + } + + pub fn pass() -> Self { + Self { + rewritten: None, + stop: false, + } + } +} + +pub trait LogicalNodeRewriter { + fn process_node(&mut self, node: &PlanNode) -> Result; +} + +pub struct LogicalPlanRewriter {} + +impl LogicalPlanRewriter { + pub fn new() -> Self { + Self {} + } + + pub fn rewrite_top_down( + &self, + node: Rc, + node_visitor: &mut T, + ) -> Result, CubeError> { + let res = if let Some(rewrited) = + self.rewrite_top_down_impl(node.as_plan_node(), node_visitor)? + { + rewrited.into_logical_node()? + } else { + node + }; + Ok(res) + } + + pub fn rewrite_top_down_with( + &self, + node: Rc, + f: F, + ) -> Result, CubeError> + where + F: FnMut(&PlanNode) -> Result, + { + struct FnWrapper(F); + + impl LogicalNodeRewriter for FnWrapper + where + F: FnMut(&PlanNode) -> Result, + { + fn process_node(&mut self, node: &PlanNode) -> Result { + (self.0)(node) + } + } + + let mut wrapper = FnWrapper(f); + self.rewrite_top_down(node, &mut wrapper) + } + + fn rewrite_top_down_impl( + &self, + node: PlanNode, + node_visitor: &mut T, + ) -> Result, CubeError> { + let NodeRewriteResult { stop, rewritten } = node_visitor.process_node(&node)?; + if let Some(rewritten) = rewritten { + return Ok(Some(rewritten)); + } + if stop { + return Ok(None); + } + let mut has_changes = false; + let mut inputs = node.inputs(); + for input in inputs.iter_mut() { + if let Some(rewrited) = self.rewrite_top_down_impl(input.clone(), node_visitor)? { + *input = rewrited; + has_changes = true; + } + } + let res = if has_changes { + Some(node.with_inputs(inputs)?) + } else { + None + }; + + Ok(res) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/visitor.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/visitor.rs new file mode 100644 index 0000000000000..4a3eeb81e9515 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/visitor/visitor.rs @@ -0,0 +1,55 @@ +use crate::logical_plan::{LogicalNode, PlanNode}; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub trait LogicalNodeVisitor { + fn process_node(&mut self, node: &PlanNode) -> Result<(), CubeError>; +} + +pub struct LogicalPlanVisitor {} + +impl LogicalPlanVisitor { + pub fn new() -> Self { + Self {} + } + + pub fn visit( + &self, + node_visitor: &mut T, + node: &Rc, + ) -> Result<(), CubeError> { + self.visit_impl(node_visitor, &node.as_plan_node()) + } + + pub fn visit_with(&self, node: &Rc, f: F) -> Result<(), CubeError> + where + F: FnMut(&PlanNode) -> Result<(), CubeError>, + { + struct FnWrapper(F); + + impl LogicalNodeVisitor for FnWrapper + where + F: FnMut(&PlanNode) -> Result<(), CubeError>, + { + fn process_node(&mut self, node: &PlanNode) -> Result<(), CubeError> { + (self.0)(node) + } + } + + let mut wrapper = FnWrapper(f); + self.visit(&mut wrapper, node) + } + + fn visit_impl( + &self, + node_visitor: &mut T, + node: &PlanNode, + ) -> Result<(), CubeError> { + node_visitor.process_node(&node)?; + for input in node.inputs() { + self.visit_impl(node_visitor, &input)?; + } + + Ok(()) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs index b7099a954ab38..e3a8e890d5cb1 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs @@ -1,52 +1,21 @@ +use super::context::PushDownBuilderContext; +use super::{LogicalNodeProcessor, ProcessableNode}; use crate::logical_plan::*; use crate::plan::schema::QualifiedColumnName; use crate::plan::*; -use crate::planner::planners::multi_stage::TimeShiftState; use crate::planner::query_properties::OrderByItem; use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; use crate::planner::sql_evaluator::MemberSymbol; use crate::planner::sql_evaluator::ReferencesBuilder; use crate::planner::sql_templates::PlanSqlTemplates; -use crate::planner::BaseMemberHelper; -use crate::planner::SqlJoinCondition; -use crate::planner::{BaseMember, MemberSymbolRef}; use cubenativeutils::CubeError; use itertools::Itertools; use std::collections::HashMap; -use std::collections::HashSet; use std::rc::Rc; const TOTAL_COUNT: &str = "total_count"; const ORIGINAL_QUERY: &str = "original_query"; -#[derive(Clone, Debug, Default)] -struct PhysicalPlanBuilderContext { - pub alias_prefix: Option, - pub render_measure_as_state: bool, //Render measure as state, for example hll state for count_approx - pub render_measure_for_ungrouped: bool, - pub time_shifts: TimeShiftState, - pub original_sql_pre_aggregations: HashMap, -} - -impl PhysicalPlanBuilderContext { - pub fn make_sql_nodes_factory(&self) -> Result { - let mut factory = SqlNodesFactory::new(); - - let (time_shifts, calendar_time_shifts) = self.time_shifts.extract_time_shifts()?; - let common_time_shifts = TimeShiftState { - dimensions_shifts: time_shifts, - }; - - factory.set_time_shifts(common_time_shifts); - factory.set_calendar_time_shifts(calendar_time_shifts); - factory.set_count_approx_as_state(self.render_measure_as_state); - factory.set_ungrouped_measure(self.render_measure_for_ungrouped); - factory.set_original_sql_pre_aggregations(self.original_sql_pre_aggregations.clone()); - Ok(factory) - } -} - pub struct PhysicalPlanBuilder { query_tools: Rc, plan_sql_templates: PlanSqlTemplates, @@ -60,13 +29,31 @@ impl PhysicalPlanBuilder { } } + pub(super) fn query_tools(&self) -> &Rc { + &self.query_tools + } + + pub(super) fn qtools_and_templates(&self) -> (&Rc, &PlanSqlTemplates) { + (&self.query_tools, &self.plan_sql_templates) + } + + pub(super) fn process_node( + &self, + logical_node: &T, + context: &PushDownBuilderContext, + ) -> Result< as LogicalNodeProcessor<'_, T>>::PhysycalNode, CubeError> + { + let processor = T::ProcessorType::new(self); + processor.process(logical_node, context) + } + pub fn build( &self, logical_plan: Rc, original_sql_pre_aggregations: HashMap, total_query: bool, ) -> Result, CubeError> { - let mut context = PhysicalPlanBuilderContext::default(); + let mut context = PushDownBuilderContext::default(); context.original_sql_pre_aggregations = original_sql_pre_aggregations; let query = self.build_impl(logical_plan, &context)?; let query = if total_query { @@ -80,686 +67,55 @@ impl PhysicalPlanBuilder { fn build_total_count( &self, source: Rc; + fn new(builder: &'a PhysicalPlanBuilder) -> Self { + Self { builder } + } + + fn process( + &self, + aggregate_multiplied_subquery: &AggregateMultipliedSubquery, + context: &PushDownBuilderContext, + ) -> Result { + let mut render_references = HashMap::new(); + let query_tools = self.builder.query_tools(); + let keys_query = self.builder.process_node( + aggregate_multiplied_subquery.keys_subquery.as_ref(), + context, + )?; + + let keys_query_alias = format!("keys"); + + let mut join_builder = + JoinBuilder::new_from_subselect(keys_query.clone(), keys_query_alias.clone()); + + let mut context_factory = context.make_sql_nodes_factory()?; + let primary_keys_dimensions = &aggregate_multiplied_subquery + .keys_subquery + .primary_keys_dimensions; + let pk_cube = aggregate_multiplied_subquery.keys_subquery.pk_cube.clone(); + let pk_cube_alias = pk_cube + .cube + .default_alias_with_prefix(&Some(format!("{}_key", pk_cube.cube.default_alias()))); + + match &aggregate_multiplied_subquery.source { + AggregateMultipliedSubquerySouce::Cube(cube) => { + let conditions = primary_keys_dimensions + .iter() + .map(|dim| -> Result<_, CubeError> { + let alias_in_keys_query = keys_query.schema().resolve_member_alias(dim); + let keys_query_ref = Expr::Reference(QualifiedColumnName::new( + Some(keys_query_alias.clone()), + alias_in_keys_query, + )); + let pk_cube_expr = Expr::Member(MemberExpression::new(dim.clone())); + Ok(vec![(keys_query_ref, pk_cube_expr)]) + }) + .collect::, _>>()?; + + join_builder.left_join_cube( + cube.cube.clone(), + Some(pk_cube_alias.clone()), + JoinCondition::new_dimension_join(conditions, false), + ); + for dimension_subquery in aggregate_multiplied_subquery.dimension_subqueries.iter() + { + self.builder.add_subquery_join( + dimension_subquery.clone(), + &mut join_builder, + context, + )?; + } + } + AggregateMultipliedSubquerySouce::MeasureSubquery(measure_subquery) => { + let subquery = self + .builder + .process_node(measure_subquery.as_ref(), context)?; + let conditions = primary_keys_dimensions + .iter() + .map(|dim| -> Result<_, CubeError> { + let alias_in_keys_query = keys_query.schema().resolve_member_alias(dim); + let keys_query_ref = Expr::Reference(QualifiedColumnName::new( + Some(keys_query_alias.clone()), + alias_in_keys_query, + )); + let alias_in_measure_subquery = subquery.schema().resolve_member_alias(dim); + let measure_subquery_ref = Expr::Reference(QualifiedColumnName::new( + Some(pk_cube_alias.clone()), + alias_in_measure_subquery, + )); + Ok(vec![(keys_query_ref, measure_subquery_ref)]) + }) + .collect::, _>>()?; + let mut ungrouped_measure_references = HashMap::new(); + for meas in aggregate_multiplied_subquery.schema.measures.iter() { + ungrouped_measure_references.insert( + meas.full_name(), + QualifiedColumnName::new( + Some(pk_cube_alias.clone()), + subquery.schema().resolve_member_alias(meas), + ), + ); + } + + context_factory.set_ungrouped_measure_references(ungrouped_measure_references); + + join_builder.left_join_subselect( + subquery, + pk_cube_alias.clone(), + JoinCondition::new_dimension_join(conditions, false), + ); + } + } + + let from = From::new_from_join(join_builder.build()); + let references_builder = ReferencesBuilder::new(from.clone()); + let mut select_builder = SelectBuilder::new(from.clone()); + let mut group_by = Vec::new(); + + self.builder.resolve_subquery_dimensions_references( + &aggregate_multiplied_subquery.dimension_subqueries, + &references_builder, + &mut render_references, + )?; + + for member in aggregate_multiplied_subquery.schema.all_dimensions() { + references_builder.resolve_references_for_member( + member.clone(), + &None, + &mut render_references, + )?; + let alias = references_builder.resolve_alias_for_member(&member.full_name(), &None); + group_by.push(Expr::Member(MemberExpression::new(member.clone()))); + select_builder.add_projection_member(&member, alias); + } + for (measure, exists) in self + .builder + .measures_for_query(&aggregate_multiplied_subquery.schema.measures, &context) + { + if exists { + if matches!( + &aggregate_multiplied_subquery.source, + AggregateMultipliedSubquerySouce::Cube(_) + ) { + references_builder.resolve_references_for_member( + measure.clone(), + &None, + &mut render_references, + )?; + } + select_builder.add_projection_member(&measure, None); + } else { + select_builder.add_null_projection(&measure, None); + } + } + select_builder.set_group_by(group_by); + context_factory.set_render_references(render_references); + context_factory.set_rendered_as_multiplied_measures( + aggregate_multiplied_subquery + .schema + .multiplied_measures + .clone(), + ); + Ok(Rc::new( + select_builder.build(query_tools.clone(), context_factory), + )) + } +} + +impl ProcessableNode for AggregateMultipliedSubquery { + type ProcessorType<'a> = AggregateMultipliedSubqueryProcessor<'a>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/full_key_aggregate.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/full_key_aggregate.rs new file mode 100644 index 0000000000000..388e68f9add19 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/full_key_aggregate.rs @@ -0,0 +1,316 @@ +use super::super::{LogicalNodeProcessor, ProcessableNode, PushDownBuilderContext}; +use crate::logical_plan::{pretty_print, FullKeyAggregate, ResolvedMultipliedMeasures}; +use crate::physical_plan_builder::PhysicalPlanBuilder; +use crate::plan::{ + Expr, From, FromSource, JoinBuilder, JoinCondition, QualifiedColumnName, SelectBuilder, + SingleAliasedSource, Union, +}; +use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; +use crate::planner::sql_evaluator::ReferencesBuilder; +use cubenativeutils::CubeError; +use std::rc::Rc; + +trait FullKeyAggregateStrategy { + fn process( + &self, + full_key_aggregate: &FullKeyAggregate, + context: &PushDownBuilderContext, + ) -> Result, CubeError>; +} + +struct KeysFullKeyAggregateStrategy<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> KeysFullKeyAggregateStrategy<'a> { + pub fn new(builder: &'a PhysicalPlanBuilder) -> Rc { + Rc::new(Self { builder }) + } +} + +impl FullKeyAggregateStrategy for KeysFullKeyAggregateStrategy<'_> { + fn process( + &self, + full_key_aggregate: &FullKeyAggregate, + context: &PushDownBuilderContext, + ) -> Result, CubeError> { + let query_tools = self.builder.query_tools(); + let mut keys_queries = vec![]; + let mut data_queries = vec![]; + let mut keys_context = context.clone(); + keys_context.dimensions_query = true; + if let Some(resolved_multiplied_measures) = &full_key_aggregate.multiplied_measures_resolver + { + match resolved_multiplied_measures { + ResolvedMultipliedMeasures::ResolveMultipliedMeasures( + resolve_multiplied_measures, + ) => { + for regular_measure_query in resolve_multiplied_measures + .regular_measure_subqueries + .iter() + { + let keys_query = self + .builder + .process_node(regular_measure_query.as_ref(), &keys_context)?; + keys_queries.push(keys_query); + let query = self + .builder + .process_node(regular_measure_query.as_ref(), &context)?; + data_queries.push(query); + } + for multiplied_measure_query in resolve_multiplied_measures + .aggregate_multiplied_subqueries + .iter() + { + let keys_query = self + .builder + .process_node(multiplied_measure_query.as_ref(), &keys_context)?; + keys_queries.push(keys_query); + let query = self + .builder + .process_node(multiplied_measure_query.as_ref(), &context)?; + data_queries.push(query); + } + } + ResolvedMultipliedMeasures::PreAggregation(_simple_query) => todo!(), + } + } + for multi_stage_ref in full_key_aggregate.multi_stage_subquery_refs.iter() { + let multi_stage_schema = context.get_multi_stage_schema(&multi_stage_ref.name)?; + let multi_stage_source = SingleAliasedSource::new_from_table_reference( + multi_stage_ref.name.clone(), + multi_stage_schema.clone(), + None, + ); + let mut keys_select_builder = + SelectBuilder::new(From::new(FromSource::Single(multi_stage_source.clone()))); + for dim in full_key_aggregate.schema.all_dimensions() { + let alias = multi_stage_schema.resolve_member_alias(dim); + let reference = QualifiedColumnName::new(None, alias); + keys_select_builder.add_projection_member_reference(dim, reference); + } + let sql_context = SqlNodesFactory::new(); + keys_select_builder.set_distinct(); + let keys_select = + Rc::new(keys_select_builder.build(query_tools.clone(), sql_context.clone())); + keys_queries.push(keys_select); + + let data_select_builder = + SelectBuilder::new(From::new(FromSource::Single(multi_stage_source))); + let data_select = Rc::new(data_select_builder.build(query_tools.clone(), sql_context)); + data_queries.push(data_select); + } + if data_queries.is_empty() { + return Err(CubeError::internal(format!( + "FullKeyAggregate should have at least one source: {}", + pretty_print(full_key_aggregate) + ))); + } + + if data_queries.len() == 1 { + let select = data_queries[0].clone(); + let result = From::new_from_subselect(select, "fk_aggregate".to_string()); + return Ok(result); + } + + let keys_from = From::new_from_union( + Rc::new(Union::new_from_subselects(&keys_queries)), + "pk_aggregate_keys_source".to_string(), + ); + let references_builder = ReferencesBuilder::new(keys_from.clone()); + let mut keys_select_builder = SelectBuilder::new(keys_from); + + for member in full_key_aggregate.schema.all_dimensions() { + let alias = references_builder.resolve_alias_for_member(&member.full_name(), &None); + if alias.is_none() { + return Err(CubeError::internal(format!( + "Source for {} not found in full key aggregate subqueries", + member.full_name() + ))); + } + let reference = QualifiedColumnName::new(None, alias.unwrap()); + keys_select_builder.add_projection_member_reference(member, reference); + } + keys_select_builder.set_distinct(); + + let sql_context = SqlNodesFactory::new(); + let keys_select = Rc::new(keys_select_builder.build(query_tools.clone(), sql_context)); + + let keys_alias = "fk_aggregate_keys".to_string(); + + let mut join_builder = + JoinBuilder::new_from_subselect(keys_select.clone(), keys_alias.clone()); + + for (i, query) in data_queries.into_iter().enumerate() { + let query_alias = format!("q_{}", i); + let conditions = full_key_aggregate + .schema + .all_dimensions() + .map(|dim| -> Result<_, CubeError> { + let alias_in_keys_query = keys_select.schema().resolve_member_alias(dim); + let keys_query_ref = Expr::Reference(QualifiedColumnName::new( + Some(keys_alias.clone()), + alias_in_keys_query, + )); + let alias_in_data_query = query.schema().resolve_member_alias(dim); + let data_query_ref = Expr::Reference(QualifiedColumnName::new( + Some(query_alias.clone()), + alias_in_data_query, + )); + + Ok(vec![(keys_query_ref, data_query_ref)]) + }) + .collect::, _>>()?; + + join_builder.left_join_subselect( + query, + query_alias.clone(), + JoinCondition::new_dimension_join(conditions, true), + ); + } + + let result = join_builder.build(); + Ok(From::new_from_join(result)) + } +} + +struct InnerJoinFullKeyAggregateStrategy<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> InnerJoinFullKeyAggregateStrategy<'a> { + pub fn new(builder: &'a PhysicalPlanBuilder) -> Rc { + Rc::new(Self { builder }) + } +} + +impl FullKeyAggregateStrategy for InnerJoinFullKeyAggregateStrategy<'_> { + fn process( + &self, + full_key_aggregate: &FullKeyAggregate, + context: &PushDownBuilderContext, + ) -> Result, CubeError> { + let query_tools = self.builder.query_tools(); + let mut data_queries = vec![]; + if let Some(resolved_multiplied_measures) = &full_key_aggregate.multiplied_measures_resolver + { + match resolved_multiplied_measures { + ResolvedMultipliedMeasures::ResolveMultipliedMeasures( + resolve_multiplied_measures, + ) => { + for regular_measure_query in resolve_multiplied_measures + .regular_measure_subqueries + .iter() + { + let query = self + .builder + .process_node(regular_measure_query.as_ref(), &context)?; + data_queries.push(query); + } + for multiplied_measure_query in resolve_multiplied_measures + .aggregate_multiplied_subqueries + .iter() + { + let query = self + .builder + .process_node(multiplied_measure_query.as_ref(), &context)?; + data_queries.push(query); + } + } + ResolvedMultipliedMeasures::PreAggregation(_simple_query) => todo!(), + } + } + + for multi_stage_ref in full_key_aggregate.multi_stage_subquery_refs.iter() { + let multi_stage_schema = context.get_multi_stage_schema(&multi_stage_ref.name)?; + let multi_stage_source = SingleAliasedSource::new_from_table_reference( + multi_stage_ref.name.clone(), + multi_stage_schema.clone(), + None, + ); + let sql_context = SqlNodesFactory::new(); + + let data_select_builder = + SelectBuilder::new(From::new(FromSource::Single(multi_stage_source))); + let data_select = Rc::new(data_select_builder.build(query_tools.clone(), sql_context)); + data_queries.push(data_select); + } + + if data_queries.is_empty() { + return Err(CubeError::internal(format!( + "FullKeyAggregate should have at least one source: {}", + pretty_print(full_key_aggregate) + ))); + } + + if data_queries.len() == 1 { + let select = data_queries[0].clone(); + let result = From::new_from_subselect(select, "fk_aggregate".to_string()); + return Ok(result); + } + + let mut join_builder = + JoinBuilder::new_from_subselect(data_queries[0].clone(), "q_0".to_string()); + + for (i, query) in data_queries.iter().skip(1).enumerate() { + let prev_alias = format!("q_{}", i); + let query_alias = format!("q_{}", i + 1); + let conditions = full_key_aggregate + .schema + .all_dimensions() + .map(|dim| -> Result<_, CubeError> { + let alias_in_prev_query = data_queries[i].schema().resolve_member_alias(dim); + let prev_query_ref = Expr::Reference(QualifiedColumnName::new( + Some(prev_alias.clone()), + alias_in_prev_query, + )); + let alias_in_data_query = query.schema().resolve_member_alias(dim); + let data_query_ref = Expr::Reference(QualifiedColumnName::new( + Some(query_alias.clone()), + alias_in_data_query, + )); + + Ok(vec![(prev_query_ref, data_query_ref)]) + }) + .collect::, _>>()?; + + join_builder.inner_join_subselect( + query.clone(), + query_alias.clone(), + JoinCondition::new_dimension_join(conditions, true), + ); + } + + let result = join_builder.build(); + Ok(From::new_from_join(result)) + } +} + +pub struct FullKeyAggregateProcessor<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> LogicalNodeProcessor<'a, FullKeyAggregate> for FullKeyAggregateProcessor<'a> { + type PhysycalNode = Rc; + fn new(builder: &'a PhysicalPlanBuilder) -> Self { + Self { builder } + } + + fn process( + &self, + full_key_aggregate: &FullKeyAggregate, + context: &PushDownBuilderContext, + ) -> Result { + let strategy: Rc = + if full_key_aggregate.schema.has_dimensions() { + KeysFullKeyAggregateStrategy::new(self.builder) + } else { + InnerJoinFullKeyAggregateStrategy::new(self.builder) + }; + strategy.process(full_key_aggregate, context) + } +} + +impl ProcessableNode for FullKeyAggregate { + type ProcessorType<'a> = FullKeyAggregateProcessor<'a>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/full_key_aggregate_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/full_key_aggregate_query.rs new file mode 100644 index 0000000000000..06d91dacd7248 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/full_key_aggregate_query.rs @@ -0,0 +1,99 @@ +use super::super::{LogicalNodeProcessor, ProcessableNode, PushDownBuilderContext}; +use crate::logical_plan::{FullKeyAggregateQuery, SimpleQuery, SimpleQuerySource}; +use crate::physical_plan_builder::PhysicalPlanBuilder; +use crate::plan::{Expr, Filter, MemberExpression, QueryPlan, Select, SelectBuilder}; +use crate::planner::query_tools::QueryTools; +use crate::planner::sql_evaluator::ReferencesBuilder; +use crate::planner::sql_templates::PlanSqlTemplates; +use crate::planner::{BaseMember, MemberSymbolRef}; +use cubenativeutils::CubeError; +use itertools::Itertools; +use std::collections::HashMap; +use std::rc::Rc; + +pub struct FullKeyAggregateQueryProcessor<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> LogicalNodeProcessor<'a, FullKeyAggregateQuery> for FullKeyAggregateQueryProcessor<'a> { + type PhysycalNode = Rc; + fn new(builder: &'a PhysicalPlanBuilder) -> Self { + Self { builder } + } + + fn process( + &self, + keys_subquery: &KeysSubQuery, + context: &PushDownBuilderContext, + ) -> Result { + let query_tools = self.builder.query_tools(); + let mut render_references = HashMap::new(); + let alias_prefix = Some(format!( + "{}_key", + query_tools.alias_for_cube(&keys_subquery.pk_cube.cube.name())? + )); + + let mut context = context.clone(); + context.alias_prefix = alias_prefix; + + let source = self + .builder + .process_node(keys_subquery.source.as_ref(), &context)?; + + let references_builder = ReferencesBuilder::new(source.clone()); + let mut select_builder = SelectBuilder::new(source); + self.builder.resolve_subquery_dimensions_references( + &keys_subquery.source.dimension_subqueries, + &references_builder, + &mut render_references, + )?; + for member in keys_subquery + .schema + .all_dimensions() + .chain(keys_subquery.primary_keys_dimensions.iter()) + { + let alias = member.alias(); + select_builder.add_projection_member(member, Some(alias)); + } + + select_builder.set_distinct(); + select_builder.set_filter(keys_subquery.filter.all_filters()); + let mut context_factory = context.make_sql_nodes_factory()?; + context_factory.set_render_references(render_references); + let res = Rc::new(select_builder.build(query_tools.clone(), context_factory)); + Ok(res) + } +} + +impl ProcessableNode for KeysSubQuery { + type ProcessorType<'a> = KeysSubQueryProcessor<'a>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/logical_join.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/logical_join.rs new file mode 100644 index 0000000000000..41633d62304da --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/logical_join.rs @@ -0,0 +1,76 @@ +use super::super::{LogicalNodeProcessor, ProcessableNode, PushDownBuilderContext}; +use crate::logical_plan::LogicalJoin; +use crate::physical_plan_builder::PhysicalPlanBuilder; +use crate::plan::{From, JoinBuilder, JoinCondition}; +use crate::planner::SqlJoinCondition; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub struct LogicalJoinProcessor<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> LogicalNodeProcessor<'a, LogicalJoin> for LogicalJoinProcessor<'a> { + type PhysycalNode = Rc; + fn new(builder: &'a PhysicalPlanBuilder) -> Self { + Self { builder } + } + + fn process( + &self, + logical_join: &LogicalJoin, + context: &PushDownBuilderContext, + ) -> Result { + let root = logical_join.root.cube.clone(); + if logical_join.joins.is_empty() && logical_join.dimension_subqueries.is_empty() { + Ok(From::new_from_cube( + root.clone(), + Some(root.default_alias_with_prefix(&context.alias_prefix)), + )) + } else { + let mut join_builder = JoinBuilder::new_from_cube( + root.clone(), + Some(root.default_alias_with_prefix(&context.alias_prefix)), + ); + + for dimension_subquery in logical_join + .dimension_subqueries //TODO move dimension_subquery to + .iter() + .filter(|d| &d.subquery_dimension.cube_name() == root.name()) + { + self.builder.add_subquery_join( + dimension_subquery.clone(), + &mut join_builder, + context, + )?; + } + for join in logical_join.joins.iter() { + join_builder.left_join_cube( + join.cube.cube.clone(), + Some( + join.cube + .cube + .default_alias_with_prefix(&context.alias_prefix), + ), + JoinCondition::new_base_join(SqlJoinCondition::try_new(join.on_sql.clone())?), + ); + for dimension_subquery in logical_join + .dimension_subqueries + .iter() + .filter(|d| &d.subquery_dimension.cube_name() == join.cube.cube.name()) + { + self.builder.add_subquery_join( + dimension_subquery.clone(), + &mut join_builder, + context, + )?; + } + } + Ok(From::new_from_join(join_builder.build())) + } + } +} + +impl ProcessableNode for LogicalJoin { + type ProcessorType<'a> = LogicalJoinProcessor<'a>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/measure_subquery.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/measure_subquery.rs new file mode 100644 index 0000000000000..8d536917ff96e --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/measure_subquery.rs @@ -0,0 +1,65 @@ +use super::super::{LogicalNodeProcessor, ProcessableNode, PushDownBuilderContext}; +use crate::logical_plan::MeasureSubquery; +use crate::physical_plan_builder::PhysicalPlanBuilder; +use crate::plan::{Select, SelectBuilder}; +use crate::planner::sql_evaluator::ReferencesBuilder; +use cubenativeutils::CubeError; +use std::collections::HashMap; +use std::rc::Rc; + +pub struct MeasureSubqueryProcessor<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> LogicalNodeProcessor<'a, MeasureSubquery> for MeasureSubqueryProcessor<'a> { + type PhysycalNode = Rc; + fn new(builder: &'a PhysicalPlanBuilder) -> Self { + Self { builder } + } + + fn process( + &self, + logical_plan: &Query, + context: &PushDownBuilderContext, + ) -> Result { + let query_tools = self.builder.query_tools(); + let mut context_factory = context.make_sql_nodes_factory()?; + let mut render_references = HashMap::new(); + let mut context = context.clone(); + let mut ctes = vec![]; + + for multi_stage_member in logical_plan.multistage_members.iter() { + let query = self + .builder + .process_node(&multi_stage_member.member_type, &context)?; + let alias = multi_stage_member.name.clone(); + context.add_multi_stage_schema(alias.clone(), query.schema()); + ctes.push(Rc::new(Cte::new(Rc::new(query), alias))); + } + + let (from, is_pre_aggregation) = match &logical_plan.source { + QuerySource::LogicalJoin(join) => { + let from = self.builder.process_node(join.as_ref(), &context)?; + let references_builder = ReferencesBuilder::new(from.clone()); + self.builder.resolve_subquery_dimensions_references( + &join.dimension_subqueries, + &references_builder, + &mut render_references, + )?; + (from, false) + } + QuerySource::FullKeyAggregate(full_key_aggregate) => { + let from = self + .builder + .process_node(full_key_aggregate.as_ref(), &context)?; + (from, false) + } + QuerySource::PreAggregation(pre_aggregation) => { + let res = self + .builder + .process_node(pre_aggregation.as_ref(), &context)?; + for member in logical_plan.schema.time_dimensions.iter() { + context_factory.add_dimensions_with_ignored_timezone(member.full_name()); + } + context_factory.set_use_local_tz_in_date_range(true); + + let dimensions_references = pre_aggregation.all_dimensions_refererences(); + let measure_references = pre_aggregation.all_measures_refererences(); + + context_factory.set_pre_aggregation_measures_references(measure_references); + context_factory.set_pre_aggregation_dimensions_references(dimensions_references); + (res, true) + } + }; + + let references_builder = ReferencesBuilder::new(from.clone()); + + let mut select_builder = SelectBuilder::new(from); + select_builder.set_ctes(ctes); + context_factory.set_ungrouped(logical_plan.modifers.ungrouped); + + for member in logical_plan.schema.all_dimensions() { + references_builder.resolve_references_for_member( + member.clone(), + &None, + &mut render_references, + )?; + select_builder.add_projection_member(member, None); + } + + for (measure, exists) in self + .builder + .measures_for_query(&logical_plan.schema.measures, &context) + { + if exists { + references_builder.resolve_references_for_member( + measure.clone(), + &None, + &mut render_references, + )?; + select_builder.add_projection_member(&measure, None); + } else { + select_builder.add_null_projection(&measure, None); + } + } + + let filter = logical_plan.filter.all_filters(); + let having = logical_plan.filter.measures_filter(); + + if self.is_over_full_aggregated_source(logical_plan) { + references_builder.resolve_references_for_filter(&having, &mut render_references)?; + select_builder.set_filter(having); + } else { + if !logical_plan.modifers.ungrouped { + let group_by = logical_plan + .schema + .all_dimensions() + .map(|symbol| -> Result<_, CubeError> { + Ok(Expr::Member(MemberExpression::new(symbol.clone()))) + }) + .collect::, _>>()?; + select_builder.set_group_by(group_by); + } + select_builder.set_having(having); + select_builder.set_filter(filter); + } + + select_builder.set_limit(logical_plan.modifers.limit); + select_builder.set_offset(logical_plan.modifers.offset); + + context_factory + .set_rendered_as_multiplied_measures(logical_plan.schema.multiplied_measures.clone()); + if !is_pre_aggregation { + context_factory.set_render_references(render_references); + } + if logical_plan.modifers.ungrouped { + context_factory.set_ungrouped(true); + } + + select_builder.set_order_by( + self.builder + .make_order_by(&logical_plan.schema, &logical_plan.modifers.order_by)?, + ); + + let res = Rc::new(select_builder.build(query_tools.clone(), context_factory)); + Ok(res) + } +} + +impl ProcessableNode for Query { + type ProcessorType<'a> = QueryProcessor<'a>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/resolve_multiplied_measures.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/resolve_multiplied_measures.rs new file mode 100644 index 0000000000000..06911c112b1c2 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/resolve_multiplied_measures.rs @@ -0,0 +1,59 @@ +use super::super::{LogicalNodeProcessor, ProcessableNode, PushDownBuilderContext}; +use crate::logical_plan::ResolveMultipliedMeasures; +use crate::physical_plan_builder::PhysicalPlanBuilder; +use crate::plan::{QueryPlan, SingleSource}; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub struct ResolveMultipliedMeasuresProcessor<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> LogicalNodeProcessor<'a, ResolveMultipliedMeasures> + for ResolveMultipliedMeasuresProcessor<'a> +{ + type PhysycalNode = Vec; + fn new(builder: &'a PhysicalPlanBuilder) -> Self { + Self { builder } + } + + fn process( + &self, + resolve_multiplied_measures: &ResolveMultipliedMeasures, + context: &PushDownBuilderContext, + ) -> Result { + let mut joins = Vec::new(); + for (i, regular_measure_subquery) in resolve_multiplied_measures + .regular_measure_subqueries + .iter() + .enumerate() + { + let mut regular_measure_context = context.clone(); + regular_measure_context.alias_prefix = if i == 0 { + Some(format!("main")) + } else { + Some(format!("main_{}", i)) + }; + let select = self + .builder + .process_node(regular_measure_subquery.as_ref(), ®ular_measure_context)?; + let source = SingleSource::Subquery(Rc::new(QueryPlan::Select(select))); + joins.push(source); + } + for multiplied_measure_subquery in resolve_multiplied_measures + .aggregate_multiplied_subqueries + .iter() + { + let select = self + .builder + .process_node(multiplied_measure_subquery.as_ref(), context)?; + let source = SingleSource::Subquery(Rc::new(QueryPlan::Select(select))); + joins.push(source); + } + Ok(joins) + } +} + +impl ProcessableNode for ResolveMultipliedMeasures { + type ProcessorType<'a> = ResolveMultipliedMeasuresProcessor<'a>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/resolved_multiplied_measures.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/resolved_multiplied_measures.rs new file mode 100644 index 0000000000000..77ca50cdd44c1 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/resolved_multiplied_measures.rs @@ -0,0 +1,43 @@ +use super::super::{LogicalNodeProcessor, ProcessableNode, PushDownBuilderContext}; +use crate::logical_plan::ResolvedMultipliedMeasures; +use crate::physical_plan_builder::PhysicalPlanBuilder; +use crate::plan::SingleSource; +use cubenativeutils::CubeError; + +pub struct ResolvedMultipliedMeasuresProcessor<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> LogicalNodeProcessor<'a, ResolvedMultipliedMeasures> + for ResolvedMultipliedMeasuresProcessor<'a> +{ + type PhysycalNode = Vec; + fn new(builder: &'a PhysicalPlanBuilder) -> Self { + Self { builder } + } + + fn process( + &self, + resolved_multiplied_measures: &ResolvedMultipliedMeasures, + context: &PushDownBuilderContext, + ) -> Result { + match resolved_multiplied_measures { + ResolvedMultipliedMeasures::ResolveMultipliedMeasures(resolve_multiplied_measures) => { + self.builder + .process_node(resolve_multiplied_measures.as_ref(), context) + } + ResolvedMultipliedMeasures::PreAggregation(_pre_aggregation_query) => { + todo!() + /* let pre_aggregation_query = + self.build_simple_query(pre_aggregation_query, context)?; + let source = + SingleSource::Subquery(Rc::new(QueryPlan::Select(pre_aggregation_query))); + Ok(vec![source]) */ + } + } + } +} + +impl ProcessableNode for ResolvedMultipliedMeasures { + type ProcessorType<'a> = ResolvedMultipliedMeasuresProcessor<'a>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/simple_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/simple_query.rs new file mode 100644 index 0000000000000..d46af1940650b --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/processors/simple_query.rs @@ -0,0 +1,110 @@ +use super::super::{LogicalNodeProcessor, ProcessableNode, PushDownBuilderContext}; +use crate::logical_plan::{SimpleQuery, SimpleQuerySource}; +use crate::physical_plan_builder::PhysicalPlanBuilder; +use crate::plan::{Expr, Filter, MemberExpression, QueryPlan, Select, SelectBuilder}; +use crate::planner::query_tools::QueryTools; +use crate::planner::sql_templates::PlanSqlTemplates; +use crate::planner::{BaseMember, MemberSymbolRef}; +use cubenativeutils::CubeError; +use std::collections::HashMap; +use std::rc::Rc; + +pub struct SimpleQueryProcessor<'a> { + builder: &'a PhysicalPlanBuilder, +} + +impl<'a> LogicalNodeProcessor<'a, SimpleQuery> for SimpleQueryProcessor<'a> { + type PhysycalNode = Rc, alias: String) -> Rc { Self::new(FromSource::Single(SingleAliasedSource::new_from_subquery( Rc::new(QueryPlan::Select(plan)), @@ -144,6 +152,20 @@ impl From { ))) } + /* pub fn all_sources(&self) -> Vec { + match &self.source { + FromSource::Empty => vec![], + FromSource::Single(s) => vec![s.alias.clone()], + FromSource::Join(j) => { + let mut sources = vec![j.root.alias.clone()]; + for itm in j.joins.iter() { + sources.push(itm.from.alias.clone()); + } + sources + } + } + } */ + pub fn to_sql( &self, templates: &PlanSqlTemplates, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/column.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/column.rs index 8d39385af42af..401b1a700b281 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/column.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/column.rs @@ -22,6 +22,12 @@ impl QualifiedColumnName { pub fn set_source(&mut self, source: Option) { self.source = source; } + + pub fn set_source_if_none(&mut self, source: &str) { + if self.source.is_none() { + self.source = Some(source.to_string()); + } + } } impl Display for QualifiedColumnName { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/schema.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/schema.rs index cfbc4a43b75f7..2f4cb61c5ffbe 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/schema.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/schema.rs @@ -1,5 +1,5 @@ use super::SchemaColumn; -use crate::planner::BaseMember; +use crate::planner::sql_evaluator::MemberSymbol; use itertools::Itertools; use std::rc::Rc; @@ -34,11 +34,11 @@ impl Schema { self.columns = res.into_iter().cloned().collect_vec(); } - pub fn resolve_member_alias(&self, member: &Rc) -> String { + pub fn resolve_member_alias(&self, member: &Rc) -> String { if let Some(column) = self.find_column_for_member(&member.full_name()) { column.name().clone() } else { - member.alias_name() + member.alias() } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs index ce367857b201e..ed122ef5883df 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs @@ -1,5 +1,5 @@ use super::{QueryPlan, Schema}; -use crate::planner::sql_templates::PlanSqlTemplates; +use crate::{plan::Select, planner::sql_templates::PlanSqlTemplates}; use cubenativeutils::CubeError; use std::rc::Rc; @@ -18,6 +18,14 @@ impl Union { Self { union, schema } } + pub fn new_from_subselects(selects: &Vec>) -> Self { + let union = selects + .iter() + .map(|s| QueryPlan::Select(s.clone())) + .collect(); + Self::new(union) + } + pub fn schema(&self) -> Rc { self.schema.clone() } @@ -28,7 +36,7 @@ impl Union { .iter() .map(|q| q.to_sql(templates)) .collect::, _>>()? - .join(" UNION ALL "); + .join("\n UNION ALL \n"); Ok(res) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs index 4fb0d0989cd0f..ac9d255290626 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs @@ -43,12 +43,7 @@ impl BaseCube { context: Rc, templates: &PlanSqlTemplates, ) -> Result { - let cube_sql = evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - templates, - )?; + let cube_sql = evaluate_with_context(&self.member_evaluator, context, templates)?; Ok(cube_sql) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs deleted file mode 100644 index 4ef987ec294be..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs +++ /dev/null @@ -1,193 +0,0 @@ -use super::query_tools::QueryTools; -use super::sql_evaluator::{MemberExpressionSymbol, MemberSymbol, SqlCall}; -use super::{evaluate_with_context, BaseMember, BaseMemberHelper, VisitorContext}; -use crate::cube_bridge::dimension_definition::DimensionDefinition; -use crate::planner::sql_evaluator::MemberExpressionExpression; -use crate::planner::sql_templates::PlanSqlTemplates; -use cubenativeutils::CubeError; -use std::rc::Rc; - -pub struct BaseDimension { - dimension: String, - query_tools: Rc, - member_evaluator: Rc, - definition: Option>, - #[allow(dead_code)] - member_expression_definition: Option, - cube_name: String, - name: String, - default_alias: String, -} - -impl BaseMember for BaseDimension { - fn to_sql( - &self, - context: Rc, - templates: &PlanSqlTemplates, - ) -> Result { - evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - templates, - ) - } - - fn full_name(&self) -> String { - self.member_evaluator.full_name() - } - - fn alias_name(&self) -> String { - self.default_alias.clone() - } - - fn member_evaluator(&self) -> Rc { - self.member_evaluator.clone() - } - - fn as_base_member(self: Rc) -> Rc { - self.clone() - } - - fn cube_name(&self) -> &String { - &self.cube_name - } - - fn name(&self) -> &String { - &self.name - } -} - -impl BaseDimension { - pub fn try_new( - evaluation_node: Rc, - query_tools: Rc, - ) -> Result>, CubeError> { - let result = match evaluation_node.as_ref() { - MemberSymbol::Dimension(s) => { - let default_alias = BaseMemberHelper::default_alias( - &s.cube_name(), - &s.name(), - &None, - query_tools.clone(), - )?; - Some(Rc::new(Self { - dimension: s.full_name(), - query_tools: query_tools.clone(), - member_evaluator: evaluation_node.clone(), - cube_name: s.cube_name().clone(), - name: s.name().clone(), - definition: Some(s.definition().clone()), - member_expression_definition: None, - default_alias, - })) - } - MemberSymbol::MemberExpression(expression_symbol) => { - let full_name = expression_symbol.full_name(); - let cube_name = expression_symbol.cube_name().clone(); - let name = expression_symbol.name().clone(); - let member_expression_definition = expression_symbol.definition().clone(); - let default_alias = PlanSqlTemplates::alias_name(&name); - Some(Rc::new(Self { - dimension: full_name, - query_tools: query_tools.clone(), - member_evaluator: evaluation_node, - definition: None, - cube_name, - name, - member_expression_definition, - default_alias, - })) - } - _ => None, - }; - Ok(result) - } - - pub fn try_new_required( - evaluation_node: Rc, - query_tools: Rc, - ) -> Result, CubeError> { - if let Some(result) = Self::try_new(evaluation_node, query_tools)? { - Ok(result) - } else { - Err(CubeError::internal(format!( - "DimensionSymbol expected as evaluation node for BaseDimension" - ))) - } - } - - pub fn try_new_from_expression( - expression: Rc, - cube_name: String, - name: String, - member_expression_definition: Option, - query_tools: Rc, - ) -> Result, CubeError> { - let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), - name.clone(), - MemberExpressionExpression::SqlCall(expression), - member_expression_definition.clone(), - query_tools.base_tools().clone(), - )?; - let full_name = member_expression_symbol.full_name(); - let member_evaluator = MemberSymbol::new_member_expression(member_expression_symbol); - let default_alias = PlanSqlTemplates::alias_name(&name); - - Ok(Rc::new(Self { - dimension: full_name, - query_tools, - member_evaluator, - definition: None, - cube_name, - name, - member_expression_definition, - default_alias, - })) - } - - pub fn member_evaluator(&self) -> Rc { - self.member_evaluator.clone() - } - - pub fn definition(&self) -> Option> { - self.definition.clone() - } - - pub fn sql_call(&self) -> Result, CubeError> { - match self.member_evaluator.as_ref() { - MemberSymbol::Dimension(d) => { - if let Some(sql) = d.member_sql() { - Ok(sql.clone()) - } else { - Err(CubeError::user(format!( - "Dimension {} hasn't sql evaluator", - self.full_name() - ))) - } - } - _ => Err(CubeError::internal(format!( - "MemberSymbol::Dimension expected" - ))), - } - } - - pub fn dimension(&self) -> &String { - &self.dimension - } - - pub fn is_sub_query(&self) -> bool { - self.definition - .as_ref() - .is_some_and(|def| def.static_data().sub_query.unwrap_or(false)) - } - - pub fn propagate_filters_to_sub_query(&self) -> bool { - self.definition.as_ref().is_some_and(|def| { - def.static_data() - .propagate_filters_to_sub_query - .unwrap_or(false) - }) - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs index 29eb21cf1c56a..4c2ea585caadb 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs @@ -1,4 +1,3 @@ -use super::query_tools::QueryTools; use super::sql_evaluator::SqlCall; use super::{evaluate_sql_call_with_context, VisitorContext}; use crate::planner::sql_templates::PlanSqlTemplates; @@ -13,17 +12,10 @@ pub trait BaseJoinCondition { } pub struct SqlJoinCondition { sql_call: Rc, - query_tools: Rc, } impl SqlJoinCondition { - pub fn try_new( - query_tools: Rc, - sql_call: Rc, - ) -> Result, CubeError> { - Ok(Rc::new(Self { - sql_call, - query_tools, - })) + pub fn try_new(sql_call: Rc) -> Result, CubeError> { + Ok(Rc::new(Self { sql_call })) } } @@ -33,6 +25,6 @@ impl BaseJoinCondition for SqlJoinCondition { context: Rc, templates: &PlanSqlTemplates, ) -> Result { - evaluate_sql_call_with_context(&self.sql_call, self.query_tools.clone(), context, templates) + evaluate_sql_call_with_context(&self.sql_call, context, templates) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs deleted file mode 100644 index 52b653c538cfa..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs +++ /dev/null @@ -1,229 +0,0 @@ -use super::query_tools::QueryTools; -use super::sql_evaluator::{MeasureTimeShifts, MemberExpressionSymbol, MemberSymbol}; -use super::{evaluate_with_context, BaseMember, BaseMemberHelper, VisitorContext}; -use crate::cube_bridge::measure_definition::RollingWindow; -use crate::planner::sql_evaluator::MemberExpressionExpression; -use crate::planner::sql_templates::PlanSqlTemplates; -use cubenativeutils::CubeError; -use std::fmt::{Debug, Formatter}; -use std::rc::Rc; - -pub struct BaseMeasure { - measure: String, - query_tools: Rc, - member_evaluator: Rc, - #[allow(dead_code)] - member_expression_definition: Option, - cube_name: String, - name: String, - default_alias: String, -} - -impl Debug for BaseMeasure { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BaseMeasure") - .field("measure", &self.measure) - .field("default_alias", &self.default_alias) - .finish() - } -} - -impl BaseMember for BaseMeasure { - fn to_sql( - &self, - context: Rc, - templates: &PlanSqlTemplates, - ) -> Result { - evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - templates, - ) - } - - fn alias_name(&self) -> String { - self.default_alias.clone() - } - - fn member_evaluator(&self) -> Rc { - self.member_evaluator.clone() - } - - fn as_base_member(self: Rc) -> Rc { - self.clone() - } - - fn full_name(&self) -> String { - self.member_evaluator.full_name() - } - - fn cube_name(&self) -> &String { - &self.cube_name - } - - fn name(&self) -> &String { - &self.name - } -} - -impl BaseMeasure { - pub fn try_new( - evaluation_node: Rc, - query_tools: Rc, - ) -> Result>, CubeError> { - let res = match evaluation_node.as_ref() { - MemberSymbol::Measure(s) => { - let default_alias = BaseMemberHelper::default_alias( - &s.cube_name(), - &s.name(), - &None, - query_tools.clone(), - )?; - Some(Rc::new(Self { - measure: s.full_name(), - query_tools: query_tools.clone(), - member_evaluator: evaluation_node.clone(), - member_expression_definition: None, - cube_name: s.cube_name().clone(), - name: s.name().clone(), - default_alias, - })) - } - MemberSymbol::MemberExpression(expression_symbol) => { - let full_name = expression_symbol.full_name(); - let cube_name = expression_symbol.cube_name().clone(); - let name = expression_symbol.name().clone(); - let member_expression_definition = expression_symbol.definition().clone(); - let default_alias = PlanSqlTemplates::alias_name(&name); - Some(Rc::new(Self { - measure: full_name, - query_tools: query_tools.clone(), - member_evaluator: evaluation_node, - cube_name, - name, - member_expression_definition, - default_alias, - })) - } - _ => None, - }; - Ok(res) - } - - pub fn try_new_required( - evaluation_node: Rc, - query_tools: Rc, - ) -> Result, CubeError> { - if let Some(result) = Self::try_new(evaluation_node, query_tools)? { - Ok(result) - } else { - Err(CubeError::internal(format!( - "MeasureSymbol expected as evaluation node for BaseMeasure" - ))) - } - } - - pub fn try_new_from_expression( - expression: MemberExpressionExpression, - cube_name: String, - name: String, - member_expression_definition: Option, - query_tools: Rc, - ) -> Result, CubeError> { - let member_expression_symbol = MemberExpressionSymbol::try_new( - cube_name.clone(), - name.clone(), - expression, - member_expression_definition.clone(), - query_tools.base_tools().clone(), - )?; - let full_name = member_expression_symbol.full_name(); - let member_evaluator = MemberSymbol::new_member_expression(member_expression_symbol); - let default_alias = PlanSqlTemplates::alias_name(&name); - Ok(Rc::new(Self { - measure: full_name, - query_tools, - member_evaluator, - cube_name, - name, - member_expression_definition, - default_alias, - })) - } - - pub fn can_be_used_as_additive_in_multplied(&self) -> bool { - if let Ok(measure_symbol) = self.member_evaluator.as_measure() { - measure_symbol.can_used_as_addictive_in_multplied() - } else { - false - } - } - - pub fn member_evaluator(&self) -> &Rc { - &self.member_evaluator - } - - pub fn measure(&self) -> &String { - &self.measure - } - - pub fn cube_name(&self) -> &String { - &self.cube_name - } - - pub fn is_calculated(&self) -> bool { - if let Ok(measure_symbol) = self.member_evaluator.as_measure() { - measure_symbol.is_calculated() - } else { - true - } - } - - pub fn time_shift(&self) -> Option { - match self.member_evaluator.as_ref() { - MemberSymbol::Measure(measure_symbol) => measure_symbol.time_shift().clone(), - _ => None, - } - } - - pub fn is_multi_stage(&self) -> bool { - if let Ok(measure_symbol) = self.member_evaluator.as_measure() { - measure_symbol.is_multi_stage() - } else { - false - } - } - - pub fn rolling_window(&self) -> Option { - if let Ok(measure_symbol) = self.member_evaluator.as_measure() { - measure_symbol.rolling_window().clone() - } else { - None - } - } - - pub fn is_rolling_window(&self) -> bool { - self.rolling_window().is_some() - } - - pub fn is_running_total(&self) -> bool { - self.measure_type() == "runningTotal" - } - - pub fn is_cumulative(&self) -> bool { - self.is_rolling_window() || self.is_running_total() - } - - pub fn measure_type(&self) -> String { - if let Ok(measure_symbol) = self.member_evaluator.as_measure() { - measure_symbol.measure_type().clone() - } else { - "number".to_string() - } - } - - pub fn is_multi_stage_ungroupped(&self) -> bool { - self.is_calculated() || self.measure_type() == "rank" - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs deleted file mode 100644 index e51240b200168..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs +++ /dev/null @@ -1,141 +0,0 @@ -use super::query_tools::QueryTools; -use super::sql_evaluator::MemberSymbol; -use super::sql_templates::PlanSqlTemplates; -use super::{evaluate_with_context, VisitorContext}; -use cubenativeutils::CubeError; -use itertools::Itertools; -use std::rc::Rc; - -pub trait BaseMember { - fn to_sql( - &self, - context: Rc, - templates: &PlanSqlTemplates, - ) -> Result; - fn alias_name(&self) -> String; - fn member_evaluator(&self) -> Rc; - fn full_name(&self) -> String; - fn as_base_member(self: Rc) -> Rc; - fn cube_name(&self) -> &String; - fn name(&self) -> &String; - fn alias_suffix(&self) -> Option { - None - } -} - -pub struct MemberSymbolRef { - member_evaluator: Rc, - query_tools: Rc, - default_alias: String, - cube_name: String, - name: String, -} - -impl MemberSymbolRef { - pub fn try_new( - member_evaluator: Rc, - query_tools: Rc, - ) -> Result, CubeError> { - let default_alias = match member_evaluator.as_ref() { - &MemberSymbol::TimeDimension(_) - | &MemberSymbol::Dimension(_) - | &MemberSymbol::Measure(_) => BaseMemberHelper::default_alias( - &member_evaluator.cube_name(), - &member_evaluator.name(), - &member_evaluator.alias_suffix(), - query_tools.clone(), - )?, - MemberSymbol::MemberExpression(_) - | MemberSymbol::CubeName(_) - | MemberSymbol::CubeTable(_) => query_tools.alias_name(&member_evaluator.name()), - }; - let cube_name = member_evaluator.cube_name(); - let name = member_evaluator.name(); - Ok(Rc::new(Self { - member_evaluator, - default_alias, - query_tools, - cube_name, - name, - })) - } -} - -impl BaseMember for MemberSymbolRef { - fn to_sql( - &self, - context: Rc, - templates: &PlanSqlTemplates, - ) -> Result { - evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - templates, - ) - } - - fn alias_name(&self) -> String { - self.default_alias.clone() - } - - fn member_evaluator(&self) -> Rc { - self.member_evaluator.clone() - } - - fn full_name(&self) -> String { - self.member_evaluator.full_name() - } - - fn as_base_member(self: Rc) -> Rc { - self.clone() - } - - fn cube_name(&self) -> &String { - &self.cube_name - } - - fn name(&self) -> &String { - &self.name - } -} - -pub struct BaseMemberHelper {} - -impl BaseMemberHelper { - pub fn upcast_vec_to_base_member(vec: &Vec>) -> Vec> { - vec.iter() - .map(|itm| itm.clone().as_base_member()) - .collect_vec() - } - - pub fn iter_as_base_member<'a, T: BaseMember>( - vec: &'a Vec>, - ) -> impl Iterator> + 'a { - vec.iter().map(|itm| itm.clone().as_base_member()) - } - - pub fn to_alias_vec(members: &Vec>) -> Vec { - members.iter().map(|m| m.alias_name()).collect_vec() - } - - pub fn extract_symbols_from_members( - members: &Vec>, - ) -> Vec> { - members.iter().map(|m| m.member_evaluator()).collect_vec() - } - - pub fn default_alias( - cube_name: &String, - member_name: &String, - member_suffix: &Option, - query_tools: Rc, - ) -> Result { - let cube_alias = query_tools.alias_for_cube(cube_name)?; - Ok(PlanSqlTemplates::memeber_alias_name( - &cube_alias, - &member_name, - member_suffix, - )) - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs index ee4184a2e67d9..e07f73ca66651 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs @@ -3,8 +3,10 @@ use super::query_tools::QueryTools; use super::QueryProperties; use crate::cube_bridge::base_query_options::BaseQueryOptions; use crate::cube_bridge::pre_aggregation_obj::NativePreAggregationObj; -use crate::logical_plan::optimizers::*; +use crate::logical_plan::OriginalSqlCollector; +//use crate::logical_plan::optimizers::*; use crate::logical_plan::PreAggregation; +use crate::logical_plan::PreAggregationOptimizer; use crate::logical_plan::Query; use crate::physical_plan_builder::PhysicalPlanBuilder; use cubenativeutils::wrappers::inner_types::InnerTypes; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs deleted file mode 100644 index 6a28036061e55..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs +++ /dev/null @@ -1,262 +0,0 @@ -use super::query_tools::QueryTools; -use super::sql_evaluator::{Compiler, MemberSymbol, TimeDimensionSymbol}; -use super::BaseDimension; -use super::Granularity; -use super::GranularityHelper; -use super::QueryDateTime; -use super::{evaluate_with_context, BaseMember, BaseMemberHelper, VisitorContext}; -use crate::planner::sql_templates::PlanSqlTemplates; -use cubenativeutils::CubeError; -use std::rc::Rc; - -pub struct BaseTimeDimension { - dimension: Rc, - member_evaluator: Rc, - query_tools: Rc, - granularity: Option, - granularity_obj: Option, - date_range: Option>, - default_alias: String, - alias_suffix: String, -} - -impl BaseMember for BaseTimeDimension { - fn to_sql( - &self, - context: Rc, - templates: &PlanSqlTemplates, - ) -> Result { - evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - templates, - ) - } - - fn alias_name(&self) -> String { - self.default_alias.clone() - } - - fn member_evaluator(&self) -> Rc { - self.member_evaluator.clone() - } - fn full_name(&self) -> String { - self.member_evaluator.full_name() - } - - fn as_base_member(self: Rc) -> Rc { - self.clone() - } - - fn cube_name(&self) -> &String { - &self.dimension.cube_name() - } - - fn name(&self) -> &String { - &self.dimension.name() - } - - /* fn alias_suffix(&self) -> Option { - Some(self.alias_suffix.clone()) - } */ -} - -impl BaseTimeDimension { - pub fn try_new_from_td_symbol( - query_tools: Rc, - td_symbol: Rc, - ) -> Result, CubeError> { - let dimension = - BaseDimension::try_new_required(td_symbol.base_symbol().clone(), query_tools.clone())?; - let granularity = td_symbol.granularity().clone(); - let granularity_obj = td_symbol.granularity_obj().clone(); - let date_range = td_symbol.date_range_vec(); - let alias_suffix = td_symbol.alias_suffix(); - let default_alias = BaseMemberHelper::default_alias( - &dimension.cube_name(), - &dimension.name(), - &Some(alias_suffix.clone()), - query_tools.clone(), - )?; - let member_evaluator = MemberSymbol::new_time_dimension(td_symbol.clone()); - - Ok(Rc::new(Self { - dimension, - query_tools, - granularity, - granularity_obj, - date_range, - alias_suffix, - default_alias, - member_evaluator, - })) - } - - pub fn try_new_required( - query_tools: Rc, - member_evaluator: Rc, - compiler: &mut Compiler, - granularity: Option, - date_range: Option>, - ) -> Result, CubeError> { - let alias_suffix = if let Some(granularity) = &granularity { - granularity.clone() - } else { - "day".to_string() - }; - - let dimension = - BaseDimension::try_new_required(member_evaluator.clone(), query_tools.clone())?; - let default_alias = BaseMemberHelper::default_alias( - &dimension.cube_name(), - &dimension.name(), - &Some(alias_suffix.clone()), - query_tools.clone(), - )?; - - let granularity_obj = GranularityHelper::make_granularity_obj( - query_tools.cube_evaluator().clone(), - compiler, - query_tools.timezone().clone(), - &dimension.cube_name(), - &dimension.name(), - granularity.clone(), - )?; - - let date_range_tuple = if let Some(date_range) = &date_range { - assert_eq!(date_range.len(), 2); - Some((date_range[0].clone(), date_range[1].clone())) - } else { - None - }; - let member_evaluator = MemberSymbol::new_time_dimension(TimeDimensionSymbol::new( - member_evaluator.clone(), - granularity.clone(), - granularity_obj.clone(), - date_range_tuple, - )); - Ok(Rc::new(Self { - dimension, - query_tools, - granularity, - granularity_obj, - date_range, - alias_suffix, - default_alias, - member_evaluator, - })) - } - - pub fn change_granularity( - &self, - new_granularity: Option, - ) -> Result, CubeError> { - let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone(); - let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); - - let new_granularity_obj = GranularityHelper::make_granularity_obj( - self.query_tools.cube_evaluator().clone(), - &mut evaluator_compiler, - self.query_tools.timezone(), - &self.dimension.name(), - &self.dimension.cube_name(), - new_granularity.clone(), - )?; - let date_range_tuple = if let Some(date_range) = &self.date_range { - assert_eq!(date_range.len(), 2); - Some((date_range[0].clone(), date_range[1].clone())) - } else { - None - }; - let member_evaluator = MemberSymbol::new_time_dimension(TimeDimensionSymbol::new( - self.dimension.member_evaluator(), - new_granularity.clone(), - new_granularity_obj.clone(), - date_range_tuple, - )); - Ok(Rc::new(Self { - dimension: self.dimension.clone(), - granularity_obj: new_granularity_obj, - query_tools: self.query_tools.clone(), - granularity: new_granularity, - date_range: self.date_range.clone(), - alias_suffix: self.alias_suffix.clone(), - default_alias: self.default_alias.clone(), - member_evaluator, - })) - } - - pub fn get_granularity(&self) -> Option { - self.granularity.clone() - } - - pub fn get_granularity_obj(&self) -> &Option { - &self.granularity_obj - } - - pub fn resolved_granularity(&self) -> Result, CubeError> { - let res = if let Some(granularity_obj) = &self.granularity_obj { - Some(granularity_obj.resolved_granularity()?) - } else { - None - }; - Ok(res) - } - - pub fn has_granularity(&self) -> bool { - self.granularity.is_some() - } - - pub fn get_date_range(&self) -> Option> { - self.date_range.clone() - } - - pub fn get_range_for_time_series(&self) -> Result>, CubeError> { - let res = if let Some(date_range) = &self.date_range { - if date_range.len() != 2 { - return Err(CubeError::user(format!( - "Invalid date range: {:?}", - date_range - ))); - } else { - if let Some(granularity_obj) = &self.granularity_obj { - if !granularity_obj.is_predefined_granularity() { - let tz = self.query_tools.timezone(); - let start = QueryDateTime::from_date_str(tz, &date_range[0])?; - let start = granularity_obj.align_date_to_origin(start)?; - let end = QueryDateTime::from_date_str(tz, &date_range[1])?; - - Some(vec![start.to_string(), end.to_string()]) - } else { - Some(vec![date_range[0].clone(), date_range[1].clone()]) - } - } else { - Some(vec![date_range[0].clone(), date_range[1].clone()]) - } - } - } else { - None - }; - Ok(res) - } - - pub fn base_dimension(&self) -> Rc { - self.dimension.clone() - } - - pub fn base_member_evaluator(&self) -> Rc { - self.dimension.member_evaluator() - } - - pub fn unescaped_alias_name(&self) -> String { - let granularity = if let Some(granularity) = &self.granularity { - granularity - } else { - "day" - }; - - self.query_tools - .alias_name(&format!("{}_{}", self.dimension.dimension(), granularity)) - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs index 8cab5a19275ea..1c45f8cd1f043 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs @@ -121,12 +121,7 @@ impl BaseFilter { self.measure_filter_where(context, plan_templates) } else { let symbol = self.member_evaluator(); - let member_sql = evaluate_with_context( - &symbol, - self.query_tools.clone(), - context.clone(), - plan_templates, - )?; + let member_sql = evaluate_with_context(&symbol, context.clone(), plan_templates)?; let member_type = match symbol.as_ref() { MemberSymbol::Dimension(dimension_symbol) => Some( diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs index ec6bbcd17cc27..b66ad682da90c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_segment.rs @@ -9,7 +9,6 @@ use std::rc::Rc; pub struct BaseSegment { full_name: String, - query_tools: Rc, member_evaluator: Rc, cube_name: String, name: String, @@ -41,7 +40,6 @@ impl BaseSegment { Ok(Rc::new(Self { full_name, - query_tools, member_evaluator, cube_name, name, @@ -52,12 +50,7 @@ impl BaseSegment { context: Rc, plan_templates: &PlanSqlTemplates, ) -> Result { - evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - plan_templates, - ) + evaluate_with_context(&self.member_evaluator, context, plan_templates) } pub fn full_name(&self) -> String { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs index 0c20f83b63075..c00a61eedd2d9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/compiler.rs @@ -3,9 +3,7 @@ use super::FilterOperator; use crate::cube_bridge::base_query_options::FilterItem as NativeFilterItem; use crate::plan::filter::{FilterGroup, FilterGroupOperator, FilterItem}; use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::Compiler; -use crate::planner::BaseMember; -use crate::planner::BaseTimeDimension; +use crate::planner::sql_evaluator::{Compiler, MemberSymbol}; use cubenativeutils::CubeError; use std::rc::Rc; use std::str::FromStr; @@ -40,16 +38,18 @@ impl<'a> FilterCompiler<'a> { Ok(()) } - pub fn add_time_dimension_item(&mut self, item: &BaseTimeDimension) -> Result<(), CubeError> { - if let Some(date_range) = item.get_date_range() { - let filter = BaseFilter::try_new( - self.query_tools.clone(), - item.member_evaluator(), - FilterType::Dimension, - FilterOperator::InDateRange, - Some(date_range.into_iter().map(|v| Some(v)).collect()), - )?; - self.time_dimension_filters.push(FilterItem::Item(filter)); + pub fn add_time_dimension_item(&mut self, item: &Rc) -> Result<(), CubeError> { + if let Ok(td_item) = item.as_time_dimension() { + if let Some(date_range) = td_item.date_range_vec() { + let filter = BaseFilter::try_new( + self.query_tools.clone(), + item.clone(), + FilterType::Dimension, + FilterOperator::InDateRange, + Some(date_range.into_iter().map(|v| Some(v)).collect()), + )?; + self.time_dimension_filters.push(FilterItem::Item(filter)); + } } Ok(()) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs index 6919e19be0801..1f89b8d4ff7fe 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs @@ -1,10 +1,6 @@ pub mod base_cube; -pub mod base_dimension; pub mod base_join_condition; -pub mod base_measure; -pub mod base_member; pub mod base_query; -pub mod base_time_dimension; pub mod filter; pub mod time_dimension; @@ -18,12 +14,8 @@ pub mod utils; pub mod visitor_context; pub use base_cube::BaseCube; -pub use base_dimension::BaseDimension; pub use base_join_condition::{BaseJoinCondition, SqlJoinCondition}; -pub use base_measure::BaseMeasure; -pub use base_member::{BaseMember, BaseMemberHelper, MemberSymbolRef}; pub use base_query::BaseQuery; -pub use base_time_dimension::BaseTimeDimension; 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/common_utils.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs index ea9ba1d53d24d..ede38a2726abd 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs @@ -1,5 +1,6 @@ use crate::planner::query_tools::QueryTools; -use crate::planner::{BaseCube, BaseDimension}; +use crate::planner::sql_evaluator::MemberSymbol; +use crate::planner::BaseCube; use cubenativeutils::CubeError; use std::rc::Rc; @@ -23,7 +24,7 @@ impl CommonUtils { pub fn primary_keys_dimensions( &self, cube_name: &String, - ) -> Result>, CubeError> { + ) -> Result>, CubeError> { let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone(); let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); let primary_keys = self @@ -39,9 +40,8 @@ impl CommonUtils { .iter() .map(|d| -> Result<_, CubeError> { let full_name = format!("{}.{}", cube_name, d); - let evaluator = evaluator_compiler.add_dimension_evaluator(full_name.clone())?; - let dim = BaseDimension::try_new_required(evaluator, self.query_tools.clone())?; - Ok(dim) + let symbol = evaluator_compiler.add_dimension_evaluator(full_name.clone())?; + Ok(symbol) }) .collect::, _>>()?; Ok(dims) 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 1d411f9713cea..ff227faac63b8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/dimension_subquery_planner.rs @@ -1,11 +1,12 @@ use super::{CommonUtils, QueryPlanner}; -use crate::logical_plan::DimensionSubQuery; +use crate::logical_plan::{pretty_print_rc, DimensionSubQuery}; use crate::plan::{FilterItem, QualifiedColumnName}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::collect_sub_query_dimensions; -use crate::planner::sql_evaluator::MemberExpressionExpression; +use crate::planner::sql_evaluator::{ + MemberExpressionExpression, MemberExpressionSymbol, MemberSymbol, +}; use crate::planner::QueryProperties; -use crate::planner::{BaseDimension, BaseMeasure, BaseMember}; use cubenativeutils::CubeError; use std::cell::{Ref, RefCell}; use std::collections::HashMap; @@ -15,7 +16,7 @@ pub struct DimensionSubqueryPlanner { utils: CommonUtils, query_tools: Rc, query_properties: Rc, - sub_query_dims: HashMap>>, + sub_query_dims: HashMap>>, dimensions_refs: RefCell>, } @@ -30,11 +31,11 @@ impl DimensionSubqueryPlanner { } } pub fn try_new( - dimensions: &Vec>, + dimensions: &Vec>, query_tools: Rc, query_properties: Rc, ) -> Result { - let mut sub_query_dims: HashMap>> = HashMap::new(); + let mut sub_query_dims: HashMap>> = HashMap::new(); for subquery_dimension in dimensions.iter() { let cube_name = subquery_dimension.cube_name().clone(); sub_query_dims @@ -54,7 +55,7 @@ impl DimensionSubqueryPlanner { pub fn plan_queries( &self, - dimensions: &Vec>, + dimensions: &Vec>, ) -> Result>, CubeError> { let mut result = Vec::new(); for subquery_dimension in dimensions.iter() { @@ -65,21 +66,33 @@ impl DimensionSubqueryPlanner { fn plan_query( &self, - subquery_dimension: Rc, + subquery_dimension: Rc, ) -> Result, CubeError> { let dim_name = subquery_dimension.name(); let cube_name = subquery_dimension.cube_name().clone(); + let dimension_symbol = subquery_dimension.as_dimension()?; + let primary_keys_dimensions = self.utils.primary_keys_dimensions(&cube_name)?; - let expression = subquery_dimension.sql_call()?; - let measure = BaseMeasure::try_new_from_expression( - MemberExpressionExpression::SqlCall(expression), + + let expression = if let Some(sql_call) = dimension_symbol.member_sql() { + sql_call.clone() + } else { + return Err(CubeError::user(format!( + "Subquery dimension {} must have `sql` field", + subquery_dimension.full_name() + ))); + }; + + let member_expression_symbol = MemberExpressionSymbol::try_new( cube_name.clone(), dim_name.clone(), + MemberExpressionExpression::SqlCall(expression), None, - self.query_tools.clone(), + self.query_tools.base_tools().clone(), )?; + let measure = MemberSymbol::new_member_expression(member_expression_symbol); - let (dimensions_filters, time_dimensions_filters) = if subquery_dimension + let (dimensions_filters, time_dimensions_filters) = if dimension_symbol .propagate_filters_to_sub_query() { let dimensions_filters = self @@ -114,13 +127,11 @@ impl DimensionSubqueryPlanner { let sub_query = query_planner.plan()?; let result = Rc::new(DimensionSubQuery { query: sub_query, - primary_keys_dimensions: primary_keys_dimensions - .into_iter() - .map(|d| d.member_evaluator()) - .collect(), - subquery_dimension: subquery_dimension.member_evaluator(), - measure_for_subquery_dimension: measure.member_evaluator().clone(), + primary_keys_dimensions, + subquery_dimension, + measure_for_subquery_dimension: measure, }); + pretty_print_rc(&result); Ok(result) } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs index e819b416d557c..834507f7580e7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs @@ -1,7 +1,6 @@ use crate::logical_plan::*; use crate::planner::QueryProperties; use cubenativeutils::CubeError; -use itertools::Itertools; use std::rc::Rc; pub struct FullKeyAggregateQueryPlanner { @@ -28,18 +27,22 @@ impl FullKeyAggregateQueryPlanner { resolve_multiplied_measures.clone(), ) }); - let join_dimensions = self - .query_properties - .dimension_symbols() - .iter() - .chain(self.query_properties.time_dimension_symbols().iter()) - .cloned() - .collect_vec(); + let measures = if let Some(multiplied_source) = &resolved_multiplied_source { + multiplied_source.schema().measures.clone() + } else { + Vec::new() + }; + + let schema = LogicalSchema::default() + .set_dimensions(self.query_properties.dimensions().clone()) + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .set_measures(measures) + .into_rc(); Ok(Rc::new(FullKeyAggregate { multiplied_measures_resolver: resolved_multiplied_source, multi_stage_subquery_refs, use_full_join_and_coalesce: true, - join_dimensions, + schema, })) } @@ -51,33 +54,38 @@ impl FullKeyAggregateQueryPlanner { ) -> Result, CubeError> { let source = self.plan_logical_source(resolve_multiplied_measures, multi_stage_subqueries)?; + let source = QuerySource::FullKeyAggregate(source); + let multiplied_measures = self .query_properties .full_key_aggregate_measures()? .rendered_as_multiplied_measures .clone(); - let schema = Rc::new(LogicalSchema { - dimensions: self.query_properties.dimension_symbols(), - measures: self.query_properties.measure_symbols(), - time_dimensions: self.query_properties.time_dimension_symbols(), - multiplied_measures, - }); + let schema = LogicalSchema::default() + .set_dimensions(self.query_properties.dimensions().clone()) + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .set_measures(self.query_properties.measures().clone()) + .set_multiplied_measures(multiplied_measures) + .into_rc(); + let logical_filter = Rc::new(LogicalFilter { dimensions_filters: self.query_properties.dimensions_filters().clone(), time_dimensions_filters: self.query_properties.time_dimensions_filters().clone(), measures_filter: self.query_properties.measures_filters().clone(), segments: self.query_properties.segments().clone(), }); - let result = FullKeyAggregateQuery { + let result = Query { schema, multistage_members: all_multistage_members, filter: logical_filter, - offset: self.query_properties.offset(), - limit: self.query_properties.row_limit(), - ungrouped: self.query_properties.ungrouped(), - order_by: self.query_properties.order_by().clone(), + modifers: Rc::new(LogicalQueryModifiers { + offset: self.query_properties.offset(), + limit: self.query_properties.row_limit(), + ungrouped: self.query_properties.ungrouped(), + order_by: self.query_properties.order_by().clone(), + }), source, }; - Ok(Rc::new(Query::FullKeyAggregateQuery(result))) + Ok(Rc::new(result)) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs index a10363b57afe5..05b02d35abe6b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs @@ -43,14 +43,16 @@ impl JoinPlanner { pub fn make_join_logical_plan_with_join_hints( &self, join_hints: Vec, + dimension_subqueries: Vec>, ) -> Result, CubeError> { let join = self.query_tools.join_graph().build_join(join_hints)?; - self.make_join_logical_plan(join) + self.make_join_logical_plan(join, dimension_subqueries) } pub fn make_join_logical_plan( &self, join: Rc, + dimension_subqueries: Vec>, ) -> Result, CubeError> { let root_definition = self.utils.cube_from_path(join.static_data().root.clone())?; let root = Cube::new(root_definition); @@ -62,10 +64,14 @@ impl JoinPlanner { .cube_from_path(join_definition.static_data().original_to.clone())?; let cube = Cube::new(cube_definition); let on_sql = self.compile_join_condition(join_definition.clone())?; - joins.push(LogicalJoinItem::CubeJoinItem(CubeJoinItem { cube, on_sql })); + joins.push(LogicalJoinItem { cube, on_sql }); } - Ok(Rc::new(LogicalJoin { root, joins })) + Ok(Rc::new(LogicalJoin { + root, + joins, + dimension_subqueries, + })) } pub fn compile_join_condition( diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs index f54a026e85130..158c1617054c6 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/applied_state.rs @@ -2,7 +2,6 @@ use crate::plan::{FilterGroup, FilterItem}; use crate::planner::filter::FilterOperator; use crate::planner::planners::multi_stage::time_shift_state::TimeShiftState; use crate::planner::sql_evaluator::{DimensionTimeShift, MeasureTimeShifts, MemberSymbol}; -use crate::planner::{BaseDimension, BaseMember, BaseTimeDimension}; use cubenativeutils::CubeError; use itertools::Itertools; use std::cmp::PartialEq; @@ -11,8 +10,8 @@ use std::rc::Rc; #[derive(Clone)] pub struct MultiStageAppliedState { - time_dimensions: Vec>, - dimensions: Vec>, + time_dimensions: Vec>, + dimensions: Vec>, time_dimensions_filters: Vec, dimensions_filters: Vec, measures_filters: Vec, @@ -22,8 +21,8 @@ pub struct MultiStageAppliedState { impl MultiStageAppliedState { pub fn new( - time_dimensions: Vec>, - dimensions: Vec>, + time_dimensions: Vec>, + dimensions: Vec>, time_dimensions_filters: Vec, dimensions_filters: Vec, measures_filters: Vec, @@ -52,13 +51,13 @@ impl MultiStageAppliedState { } } - pub fn add_dimensions(&mut self, dimensions: Vec>) { + pub fn add_dimensions(&mut self, dimensions: Vec>) { self.dimensions = self .dimensions .iter() .cloned() .chain(dimensions.into_iter()) - .unique_by(|d| d.member_evaluator().full_name()) + .unique_by(|d| d.full_name()) .collect_vec(); } @@ -170,24 +169,18 @@ impl MultiStageAppliedState { } pub fn time_dimensions_symbols(&self) -> Vec> { - self.time_dimensions - .iter() - .map(|d| d.member_evaluator().clone()) - .collect() + self.time_dimensions().clone() } pub fn dimensions_symbols(&self) -> Vec> { - self.dimensions - .iter() - .map(|d| d.member_evaluator().clone()) - .collect() + self.dimensions.clone() } pub fn all_dimensions_symbols(&self) -> Vec> { self.time_dimensions .iter() - .map(|d| d.member_evaluator().clone()) - .chain(self.dimensions.iter().map(|d| d.member_evaluator().clone())) + .cloned() + .chain(self.dimensions.iter().cloned()) .collect() } @@ -203,19 +196,19 @@ impl MultiStageAppliedState { &self.measures_filters } - pub fn dimensions(&self) -> &Vec> { + pub fn dimensions(&self) -> &Vec> { &self.dimensions } - pub fn time_dimensions(&self) -> &Vec> { + pub fn time_dimensions(&self) -> &Vec> { &self.time_dimensions } - pub fn set_time_dimensions(&mut self, time_dimensions: Vec>) { + pub fn set_time_dimensions(&mut self, time_dimensions: Vec>) { self.time_dimensions = time_dimensions; } - pub fn set_dimensions(&mut self, dimensions: Vec>) { + pub fn set_dimensions(&mut self, dimensions: Vec>) { self.dimensions = dimensions; } @@ -407,7 +400,7 @@ impl PartialEq for MultiStageAppliedState { .dimensions .iter() .zip(other.dimensions.iter()) - .all(|(a, b)| a.member_evaluator().full_name() == b.member_evaluator().full_name()); + .all(|(a, b)| a.full_name() == b.full_name()); dims_eq && self.time_dimensions_filters == other.time_dimensions_filters && self.dimensions_filters == other.dimensions_filters @@ -421,11 +414,7 @@ impl Debug for MultiStageAppliedState { f.debug_struct("MultiStageAppliedState") .field( "dimensions", - &self - .dimensions - .iter() - .map(|d| d.member_evaluator().full_name()) - .join(", "), + &self.dimensions.iter().map(|d| d.full_name()).join(", "), ) .field("time_shifts", &self.time_shifts) .finish() diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member.rs index 9ec4c91996ebd..4286f94474c79 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member.rs @@ -1,10 +1,9 @@ use crate::planner::sql_evaluator::{MeasureTimeShifts, MemberSymbol}; -use crate::planner::BaseTimeDimension; use std::rc::Rc; #[derive(Clone)] pub struct TimeSeriesDescription { - pub time_dimension: Rc, + pub time_dimension: Rc, pub date_range_cte: Option, } @@ -12,7 +11,7 @@ pub struct TimeSeriesDescription { pub enum MultiStageLeafMemberType { Measure, TimeSeries(Rc), - TimeSeriesGetRange(Rc), + TimeSeriesGetRange(Rc), } #[derive(Clone)] @@ -36,15 +35,15 @@ pub enum RollingWindowType { #[derive(Clone)] pub struct RollingWindowDescription { - pub time_dimension: Rc, - pub base_time_dimension: Rc, + pub time_dimension: Rc, + pub base_time_dimension: Rc, pub rolling_window: RollingWindowType, } impl RollingWindowDescription { pub fn new_regular( - time_dimension: Rc, - base_time_dimension: Rc, + time_dimension: Rc, + base_time_dimension: Rc, trailing: Option, leading: Option, offset: String, @@ -62,8 +61,8 @@ impl RollingWindowDescription { } pub fn new_to_date( - time_dimension: Rc, - base_time_dimension: Rc, + time_dimension: Rc, + base_time_dimension: Rc, granularity: String, ) -> Self { Self { @@ -74,8 +73,8 @@ impl RollingWindowDescription { } pub fn new_running_total( - time_dimension: Rc, - base_time_dimension: Rc, + time_dimension: Rc, + base_time_dimension: Rc, ) -> Self { Self { time_dimension, 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 a481a23c26dba..bdc8b1dea0728 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 @@ -6,14 +6,11 @@ use crate::logical_plan::*; use crate::planner::planners::{multi_stage::RollingWindowType, QueryPlanner, SimpleQueryPlanner}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::MemberSymbol; -use crate::planner::{ - BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension, GranularityHelper, -}; +use crate::planner::GranularityHelper; use crate::planner::{OrderByItem, QueryProperties}; use cubenativeutils::CubeError; use itertools::Itertools; -use std::collections::HashSet; use std::rc::Rc; pub struct MultiStageMemberQueryPlanner { @@ -57,7 +54,7 @@ impl MultiStageMemberQueryPlanner { fn plan_time_series_get_range_query( &self, - time_dimension: Rc, + time_dimension: Rc, ) -> Result, CubeError> { let cte_query_properties = QueryProperties::try_new_from_precompiled( self.query_tools.clone(), @@ -81,17 +78,15 @@ impl MultiStageMemberQueryPlanner { let simple_query_planer = SimpleQueryPlanner::new(self.query_tools.clone(), cte_query_properties); - let (source, subquery_dimension_queries) = - simple_query_planer.source_and_subquery_dimensions()?; + let source = simple_query_planer.source_and_subquery_dimensions()?; let result = MultiStageGetDateRange { - time_dimension: time_dimension.member_evaluator(), - dimension_subqueries: subquery_dimension_queries, + time_dimension: time_dimension.clone(), source, }; let member = LogicalMultiStageMember { name: self.description.alias().clone(), - member_type: MultiStageMemberLogicalType::GetDateRange(result), + member_type: MultiStageMemberLogicalType::GetDateRange(Rc::new(result)), }; Ok(Rc::new(member)) @@ -103,13 +98,13 @@ impl MultiStageMemberQueryPlanner { ) -> Result, CubeError> { let time_dimension = time_series_description.time_dimension.clone(); let result = MultiStageTimeSeries { - time_dimension: time_dimension.member_evaluator().clone(), - date_range: time_dimension.get_date_range().clone(), + time_dimension: time_dimension.clone(), + date_range: time_dimension.as_time_dimension()?.date_range_vec(), get_date_range_multistage_ref: time_series_description.date_range_cte.clone(), }; Ok(Rc::new(LogicalMultiStageMember { name: self.description.alias().clone(), - member_type: MultiStageMemberLogicalType::TimeSeries(result), + member_type: MultiStageMemberLogicalType::TimeSeries(Rc::new(result)), })) } @@ -138,8 +133,8 @@ impl MultiStageMemberQueryPlanner { self.query_tools.cube_evaluator().clone(), &mut evaluator_compiler, self.query_tools.timezone().clone(), - time_dimension.cube_name(), - time_dimension.name(), + &time_dimension.cube_name(), + &time_dimension.name(), Some(query_granularity.clone()), )? else { @@ -157,14 +152,14 @@ impl MultiStageMemberQueryPlanner { RollingWindowType::RunningTotal => MultiStageRollingWindowType::RunningTotal, }; - let logical_schema = Rc::new(LogicalSchema { - time_dimensions: self.description.state().time_dimensions_symbols(), - dimensions: self.description.state().dimensions_symbols(), - measures: vec![self.description.member().evaluation_node().clone()], - multiplied_measures: HashSet::new(), - }); + let schema = LogicalSchema::default() + .set_dimensions(self.query_properties.dimensions().clone()) + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .set_measures(vec![self.description.member().evaluation_node().clone()]) + .into_rc(); + let result = MultiStageRollingWindow { - schema: logical_schema, + schema, is_ungrouped: self.description.member().is_ungrupped(), rolling_window, order_by: self.query_order_by()?, @@ -176,14 +171,12 @@ impl MultiStageMemberQueryPlanner { name: inputs[1].0.clone(), symbols: inputs[1].1.clone(), }, - rolling_time_dimension: rolling_window_desc.time_dimension.member_evaluator(), - time_dimension_in_measure_input: rolling_window_desc - .base_time_dimension - .member_evaluator(), //time dimension in measure input can have different granularity + rolling_time_dimension: rolling_window_desc.time_dimension.clone(), + time_dimension_in_measure_input: rolling_window_desc.base_time_dimension.clone(), }; Ok(Rc::new(LogicalMultiStageMember { name: self.description.alias().clone(), - member_type: MultiStageMemberLogicalType::RollingWindow(result), + member_type: MultiStageMemberLogicalType::RollingWindow(Rc::new(result)), })) } @@ -191,8 +184,6 @@ impl MultiStageMemberQueryPlanner { &self, multi_stage_member: &MultiStageInodeMember, ) -> Result, CubeError> { - let input_dimensions = self.all_input_dimensions(); - let partition_by = self.member_partition_by_logical( &multi_stage_member.reduce_by_symbols(), &multi_stage_member.group_by_symbols(), @@ -210,12 +201,11 @@ impl MultiStageMemberQueryPlanner { _ => MultiStageCalculationWindowFunction::None, }; - let logical_schema = LogicalSchema { - time_dimensions: self.description.state().time_dimensions_symbols(), - dimensions: self.description.state().dimensions_symbols(), - measures: vec![self.description.member().evaluation_node().clone()], - multiplied_measures: HashSet::new(), - }; + let schema = LogicalSchema::default() + .set_dimensions(self.description.state().dimensions_symbols()) + .set_time_dimensions(self.description.state().time_dimensions_symbols()) + .set_measures(vec![self.description.member().evaluation_node().clone()]) + .into_rc(); let calculation_type = match multi_stage_member.inode_type() { MultiStageInodeMemberType::Rank => MultiStageCalculationType::Rank, @@ -239,18 +229,17 @@ impl MultiStageMemberQueryPlanner { }) .collect_vec(); + let full_key_aggregate_schema = self.input_schema(); let result = MultiStageMeasureCalculation { - schema: Rc::new(logical_schema), + schema, is_ungrouped: self.description.member().is_ungrupped(), calculation_type, partition_by, window_function_to_use, order_by: self.query_order_by()?, + source: Rc::new(FullKeyAggregate { - join_dimensions: input_dimensions - .iter() - .map(|d| d.member_evaluator().clone()) - .collect(), + schema: full_key_aggregate_schema, use_full_join_and_coalesce: true, multiplied_measures_resolver: None, multi_stage_subquery_refs: input_sources, @@ -259,21 +248,18 @@ impl MultiStageMemberQueryPlanner { let result = LogicalMultiStageMember { name: self.description.alias().clone(), - member_type: MultiStageMemberLogicalType::MeasureCalculation(result), + member_type: MultiStageMemberLogicalType::MeasureCalculation(Rc::new(result)), }; Ok(Rc::new(result)) } fn plan_for_leaf_cte_query(&self) -> Result, CubeError> { let member_node = self.description.member_node(); - let measures = - if let Some(measure) = //TODO rewrite it!! - BaseMeasure::try_new(member_node.clone(), self.query_tools.clone())? - { - vec![measure] - } else { - vec![] - }; + let measures = if member_node.as_measure().is_ok() { + vec![member_node.clone()] + } else { + vec![] + }; let cte_query_properties = QueryProperties::try_new_from_precompiled( self.query_tools.clone(), @@ -306,34 +292,41 @@ impl MultiStageMemberQueryPlanner { }; let result = LogicalMultiStageMember { name: self.description.alias().clone(), - member_type: MultiStageMemberLogicalType::LeafMeasure(leaf_measure_plan), + member_type: MultiStageMemberLogicalType::LeafMeasure(Rc::new(leaf_measure_plan)), }; Ok(Rc::new(result)) } - fn all_dimensions(&self) -> Vec> { - BaseMemberHelper::iter_as_base_member(self.description.state().dimensions()) - .chain(BaseMemberHelper::iter_as_base_member( - self.description.state().time_dimensions(), - )) + fn all_dimensions(&self) -> Vec> { + self.description + .state() + .dimensions() + .iter() + .cloned() + .chain(self.description.state().time_dimensions().iter().cloned()) .collect_vec() } - fn input_dimensions(&self) -> Vec> { - self.description + fn input_schema(&self) -> Rc { + let dimensions = self + .description .input() .iter() - .flat_map(|descr| descr.state().dimensions().clone()) + .flat_map(|descr| descr.state().dimensions_symbols().clone()) .unique_by(|dim| dim.full_name()) - .collect_vec() - } + .collect_vec(); + let time_dimensions = self + .description + .input() + .iter() + .flat_map(|descr| descr.state().time_dimensions_symbols().clone()) + .unique_by(|dim| dim.full_name()) + .collect_vec(); - fn all_input_dimensions(&self) -> Vec> { - BaseMemberHelper::iter_as_base_member(&self.input_dimensions()) - .chain(BaseMemberHelper::iter_as_base_member( - self.description.state().time_dimensions(), - )) - .collect_vec() + LogicalSchema::default() + .set_dimensions(dimensions) + .set_time_dimensions(time_dimensions) + .into_rc() } fn input_cte_aliases(&self) -> Vec<(String, Vec>)> { @@ -345,23 +338,12 @@ impl MultiStageMemberQueryPlanner { .collect_vec() } - fn query_member_as_measure(&self) -> Result>, CubeError> { - BaseMeasure::try_new( - self.description.member_node().clone(), - self.query_tools.clone(), - ) - } - fn member_partition_by_logical( &self, reduce_by: &Vec>, group_by: &Option>>, ) -> Vec> { - let dimensions = self - .all_dimensions() - .into_iter() - .map(|d| d.member_evaluator().clone()) - .collect_vec(); + let dimensions = self.all_dimensions(); let dimensions = if !reduce_by.is_empty() { dimensions .into_iter() @@ -382,8 +364,9 @@ impl MultiStageMemberQueryPlanner { } fn query_order_by(&self) -> Result, CubeError> { - let measures = if let Some(measure) = self.query_member_as_measure()? { - vec![measure] + let member_node = self.description.member_node(); + let measures = if member_node.as_measure().is_ok() { + vec![member_node.clone()] } else { vec![] }; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/multi_stage_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/multi_stage_query_planner.rs index df2bb46baa155..eca4d78eeb19e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/multi_stage_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/multi_stage_query_planner.rs @@ -9,9 +9,8 @@ use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::has_multi_stage_members; use crate::planner::sql_evaluator::collectors::member_childs; use crate::planner::sql_evaluator::MemberSymbol; -use crate::planner::BaseMember; +use crate::planner::GranularityHelper; use crate::planner::QueryProperties; -use crate::planner::{BaseDimension, BaseMeasure, BaseTimeDimension, GranularityHelper}; use cubenativeutils::CubeError; use itertools::Itertools; use std::rc::Rc; @@ -42,8 +41,8 @@ impl MultiStageQueryPlanner { .query_properties .all_members(false) .into_iter() - .filter_map(|memb: Rc| -> Option> { - match has_multi_stage_members(&memb.member_evaluator(), false) { + .filter_map(|memb| -> Option> { + match has_multi_stage_members(&memb, false) { Ok(true) => Some(Ok(memb)), Ok(false) => None, Err(e) => Some(Err(e)), @@ -66,11 +65,8 @@ impl MultiStageQueryPlanner { let top_level_ctes = multi_stage_members .into_iter() .map(|memb| -> Result<_, CubeError> { - let description = self.make_queries_descriptions( - memb.member_evaluator().clone(), - state.clone(), - &mut descriptions, - )?; + let description = + self.make_queries_descriptions(memb.clone(), state.clone(), &mut descriptions)?; let result = ( description.alias().clone(), vec![description.member_node().clone()], @@ -109,9 +105,7 @@ impl MultiStageQueryPlanner { &self, base_member: Rc, ) -> Result<(MultiStageInodeMember, bool), CubeError> { - let inode = if let Some(measure) = - BaseMeasure::try_new(base_member.clone(), self.query_tools.clone())? - { + let inode = if let Ok(measure) = base_member.as_measure() { let member_type = if measure.measure_type() == "rank" { MultiStageInodeMemberType::Rank } else if !measure.is_calculated() { @@ -120,22 +114,16 @@ impl MultiStageQueryPlanner { MultiStageInodeMemberType::Calculate }; - let time_shift = measure.time_shift(); + let time_shift = measure.time_shift().clone(); let is_ungrupped = match &member_type { MultiStageInodeMemberType::Rank | MultiStageInodeMemberType::Calculate => true, _ => self.query_properties.ungrouped(), }; - let (reduce_by, add_group_by, group_by) = - if let Ok(measure_symbol) = measure.member_evaluator().as_measure() { - ( - measure_symbol.reduce_by().clone().unwrap_or_default(), - measure_symbol.add_group_by().clone().unwrap_or_default(), - measure_symbol.group_by().clone(), - ) - } else { - (vec![], vec![], None) - }; + + let reduce_by = measure.reduce_by().clone().unwrap_or_default(); + let add_group_by = measure.add_group_by().clone().unwrap_or_default(); + let group_by = measure.group_by().clone(); ( MultiStageInodeMember::new( member_type, @@ -208,13 +196,7 @@ impl MultiStageQueryPlanner { let (multi_stage_member, is_ungrupped) = self.create_multi_stage_inode_member(member.clone())?; - let dimensions_to_add = multi_stage_member - .add_group_by_symbols() - .iter() - .map(|symbol| { - BaseDimension::try_new_required(symbol.clone(), self.query_tools.clone()) - }) - .collect::, _>>()?; + let dimensions_to_add = multi_stage_member.add_group_by_symbols(); let new_state = if !dimensions_to_add.is_empty() || multi_stage_member.time_shift().is_some() @@ -222,7 +204,7 @@ impl MultiStageQueryPlanner { { let mut new_state = state.clone_state(); if !dimensions_to_add.is_empty() { - new_state.add_dimensions(dimensions_to_add); + new_state.add_dimensions(dimensions_to_add.clone()); } if let Some(time_shift) = multi_stage_member.time_shift() { new_state.add_time_shifts(time_shift.clone())?; @@ -301,14 +283,15 @@ impl MultiStageQueryPlanner { let ungrouped = measure.is_rolling_window() && !measure.is_addictive(); - let mut time_dimensions = self.query_properties.time_dimensions().clone(); - for dim in self.query_properties.dimension_symbols() { - let dim = dim.resolve_reference_chain(); - if let Ok(time_dimension_symbol) = dim.as_time_dimension() { - let time_dimension = BaseTimeDimension::try_new_from_td_symbol( - self.query_tools.clone(), - time_dimension_symbol, - )?; + let mut time_dimensions = self + .query_properties + .time_dimensions() + .iter() + .map(|d| d.as_time_dimension()) + .collect::, _>>()?; + for dim in self.query_properties.dimensions() { + let dim = dim.clone().resolve_reference_chain(); + if let Ok(time_dimension) = dim.as_time_dimension() { time_dimensions.push(time_dimension); } } @@ -324,7 +307,7 @@ impl MultiStageQueryPlanner { } let uniq_time_dimensions = time_dimensions .iter() - .unique_by(|a| (a.cube_name(), a.name(), a.get_date_range())) + .unique_by(|a| (a.cube_name(), a.name(), a.date_range_vec())) .collect_vec(); if uniq_time_dimensions.len() != 1 { return Err(CubeError::internal( @@ -335,6 +318,7 @@ impl MultiStageQueryPlanner { let time_dimension = GranularityHelper::find_dimension_with_min_granularity(&time_dimensions)?; + let time_dimension = MemberSymbol::new_time_dimension(time_dimension); let (base_rolling_state, base_time_dimension) = self.make_rolling_base_state( time_dimension.clone(), @@ -412,7 +396,7 @@ impl MultiStageQueryPlanner { fn add_time_series_get_range_query( &self, - time_dimension: Rc, + time_dimension: Rc, state: Rc, descriptions: &mut Vec>, ) -> Result, CubeError> { @@ -427,7 +411,7 @@ impl MultiStageQueryPlanner { MultiStageMemberType::Leaf(MultiStageLeafMemberType::TimeSeriesGetRange( time_dimension.clone(), )), - time_dimension.member_evaluator(), + time_dimension.clone(), true, false, ), @@ -443,7 +427,7 @@ impl MultiStageQueryPlanner { fn add_time_series( &self, - time_dimension: Rc, + time_dimension: Rc, state: Rc, descriptions: &mut Vec>, ) -> Result, CubeError> { @@ -452,7 +436,11 @@ impl MultiStageQueryPlanner { { description.clone() } else { - let get_range_query_description = if time_dimension.get_date_range().is_some() { + let get_range_query_description = if time_dimension + .as_time_dimension()? + .date_range_vec() + .is_some() + { None } else { Some(self.add_time_series_get_range_query( @@ -469,7 +457,7 @@ impl MultiStageQueryPlanner { date_range_cte: get_range_query_description.map(|d| d.alias().clone()), }, ))), - time_dimension.member_evaluator(), + time_dimension.clone(), true, false, ), @@ -530,11 +518,12 @@ impl MultiStageQueryPlanner { fn make_rolling_base_state( &self, - time_dimension: Rc, + time_dimension: Rc, rolling_window: &RollingWindow, state: Rc, - ) -> Result<(Rc, Rc), CubeError> { - let time_dimension_base_name = time_dimension.base_dimension().full_name(); + ) -> Result<(Rc, Rc), CubeError> { + let time_dimension_symbol = time_dimension.as_time_dimension()?; + let time_dimension_base_name = time_dimension_symbol.base_symbol().full_name(); let mut new_state = state.clone_state(); let trailing_granularity = GranularityHelper::granularity_from_interval(&rolling_window.trailing); @@ -544,10 +533,12 @@ impl MultiStageQueryPlanner { GranularityHelper::min_granularity(&trailing_granularity, &leading_granularity)?; let result_granularity = GranularityHelper::min_granularity( &window_granularity, - &time_dimension.resolved_granularity()?, + &time_dimension_symbol.resolved_granularity()?, )?; - let new_time_dimension = time_dimension.change_granularity(result_granularity.clone())?; + let new_time_dimension_symbol = time_dimension_symbol + .change_granularity(self.query_tools.clone(), result_granularity.clone())?; + let new_time_dimension = MemberSymbol::new_time_dimension(new_time_dimension_symbol); //We keep only one time_dimension in the leaf query because, even if time_dimension values have different granularity, in the leaf query we need to group by the lowest granularity. new_state.set_time_dimensions(vec![new_time_dimension.clone()]); @@ -556,7 +547,7 @@ impl MultiStageQueryPlanner { .clone() .into_iter() .filter(|d| { - d.member_evaluator() + d.clone() .resolve_reference_chain() .as_time_dimension() .is_err() 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 ab2ec6d8bd07b..db6ccc1f1cc43 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 @@ -6,9 +6,8 @@ use crate::planner::sql_evaluator::collectors::{ collect_cube_names, collect_join_hints, collect_join_hints_for_measures, collect_sub_query_dimensions_from_members, collect_sub_query_dimensions_from_symbols, }; -use crate::planner::{ - BaseMeasure, BaseMember, BaseMemberHelper, FullKeyAggregateMeasures, QueryProperties, -}; +use crate::planner::sql_evaluator::MemberSymbol; +use crate::planner::{FullKeyAggregateMeasures, QueryProperties}; use cubenativeutils::CubeError; use itertools::Itertools; use std::rc::Rc; @@ -101,16 +100,18 @@ impl MultipliedMeasuresQueryPlanner { .iter() .map(|m| m.measure()), ) - .map(|m| m.member_evaluator().clone()) + .map(|m| m.clone()) .collect_vec(); - let schema = Rc::new(LogicalSchema { - time_dimensions: self.query_properties.time_dimension_symbols(), - dimensions: self.query_properties.dimension_symbols(), - measures: all_measures, - multiplied_measures: full_key_aggregate_measures - .rendered_as_multiplied_measures - .clone(), - }); + let schema = LogicalSchema::default() + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .set_dimensions(self.query_properties.dimensions().clone()) + .set_measures(all_measures) + .set_multiplied_measures( + full_key_aggregate_measures + .rendered_as_multiplied_measures + .clone(), + ) + .into_rc(); let logical_filter = Rc::new(LogicalFilter { dimensions_filters: self.query_properties.dimensions_filters().clone(), time_dimensions_filters: self.query_properties.time_dimensions_filters().clone(), @@ -130,19 +131,13 @@ impl MultipliedMeasuresQueryPlanner { fn aggregate_subquery_plan( &self, key_cube_name: &String, - measures: &Vec>, + measures: &Vec>, key_join: Rc, ) -> Result, CubeError> { - let measures_symbols = measures - .iter() - .map(|m| m.member_evaluator().clone()) - .collect(); - let subquery_dimensions = collect_sub_query_dimensions_from_symbols( - &measures_symbols, - &self.join_planner, - &key_join, - self.query_tools.clone(), - )?; + let pk_cube = self.common_utils.cube_from_path(key_cube_name.clone())?; + let pk_cube = Cube::new(pk_cube); + let subquery_dimensions = + collect_sub_query_dimensions_from_symbols(&measures, &self.join_planner, &key_join)?; let dimension_subquery_planner = DimensionSubqueryPlanner::try_new( &subquery_dimensions, @@ -152,24 +147,20 @@ impl MultipliedMeasuresQueryPlanner { let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; - let primary_keys_dimensions = self - .common_utils - .primary_keys_dimensions(key_cube_name)? - .into_iter() - .map(|d| d.as_base_member()) - .collect_vec(); + let primary_keys_dimensions = self.common_utils.primary_keys_dimensions(key_cube_name)?; let keys_subquery = - self.key_query(&primary_keys_dimensions, key_join.clone(), key_cube_name)?; - let schema = Rc::new(LogicalSchema { - time_dimensions: self.query_properties.time_dimension_symbols(), - dimensions: self.query_properties.dimension_symbols(), - measures: measures_symbols, - multiplied_measures: self - .full_key_aggregate_measures - .rendered_as_multiplied_measures - .clone(), - }); - let pk_cube = self.common_utils.cube_from_path(key_cube_name.clone())?; + self.key_query(&primary_keys_dimensions, key_join.clone(), pk_cube.clone())?; + + let schema = LogicalSchema::default() + .set_dimensions(self.query_properties.dimensions().clone()) + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .set_measures(measures.clone()) + .set_multiplied_measures( + self.full_key_aggregate_measures + .rendered_as_multiplied_measures + .clone(), + ) + .into_rc(); let should_build_join_for_measure_select = self.check_should_build_join_for_measure_select(measures, key_cube_name)?; let source = if should_build_join_for_measure_select { @@ -178,16 +169,12 @@ impl MultipliedMeasuresQueryPlanner { &measures, &primary_keys_dimensions, )?; - Rc::new(AggregateMultipliedSubquerySouce::MeasureSubquery( - measure_subquery, - )) + AggregateMultipliedSubquerySouce::MeasureSubquery(measure_subquery) } else { - Rc::new(AggregateMultipliedSubquerySouce::Cube) + AggregateMultipliedSubquerySouce::Cube(pk_cube) }; - let cube = Cube::new(pk_cube); Ok(Rc::new(AggregateMultipliedSubquery { schema, - pk_cube: cube, keys_subquery, dimension_subqueries: subquery_dimension_queries, source, @@ -196,12 +183,12 @@ impl MultipliedMeasuresQueryPlanner { fn check_should_build_join_for_measure_select( &self, - measures: &Vec>, + measures: &Vec>, key_cube_name: &String, ) -> Result { for measure in measures.iter() { let member_expression_over_dimensions_cubes = - if let Ok(member_expression) = measure.member_evaluator().as_member_expression() { + if let Ok(member_expression) = measure.as_member_expression() { member_expression.cube_names_if_dimension_only_expression()? } else { None @@ -209,9 +196,9 @@ impl MultipliedMeasuresQueryPlanner { let cubes = if let Some(cubes) = member_expression_over_dimensions_cubes { cubes } else { - collect_cube_names(measure.member_evaluator())? + collect_cube_names(&measure)? }; - let join_hints = collect_join_hints(measure.member_evaluator())?; + 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)?; if *measures_join @@ -231,15 +218,11 @@ impl MultipliedMeasuresQueryPlanner { fn aggregate_subquery_measure( &self, key_join: Rc, - measures: &Vec>, - primary_keys_dimensions: &Vec>, + measures: &Vec>, + primary_keys_dimensions: &Vec>, ) -> Result, CubeError> { - let subquery_dimensions = collect_sub_query_dimensions_from_members( - &BaseMemberHelper::iter_as_base_member(measures).collect_vec(), - &self.join_planner, - &key_join, - self.query_tools.clone(), - )?; + let subquery_dimensions = + collect_sub_query_dimensions_from_members(&measures, &self.join_planner, &key_join)?; let dimension_subquery_planner = DimensionSubqueryPlanner::try_new( &subquery_dimensions, self.query_tools.clone(), @@ -247,45 +230,31 @@ impl MultipliedMeasuresQueryPlanner { )?; let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; - let join_hints = collect_join_hints_for_measures(measures)?; + let join_hints = collect_join_hints_for_measures(&measures)?; let source = self .join_planner - .make_join_logical_plan_with_join_hints(join_hints)?; + .make_join_logical_plan_with_join_hints(join_hints, subquery_dimension_queries)?; - let result = MeasureSubquery { - primary_keys_dimensions: primary_keys_dimensions - .iter() - .map(|dim| dim.member_evaluator().clone()) - .collect(), - measures: measures - .iter() - .map(|meas| meas.member_evaluator().clone()) - .collect(), - dimension_subqueries: subquery_dimension_queries, - source, - }; + let schema = LogicalSchema::default() + .set_dimensions(primary_keys_dimensions.clone()) + .set_measures(measures.clone()) + .into_rc(); + + let result = MeasureSubquery { schema, source }; Ok(Rc::new(result)) } fn regular_measures_subquery( &self, - measures: &Vec>, + measures: &Vec>, join: Rc, - ) -> Result, CubeError> { - let measures_symbols = measures - .iter() - .map(|m| m.member_evaluator().clone()) - .collect(); - let all_symbols = - self.query_properties - .get_member_symbols(true, true, false, true, &measures_symbols); + ) -> Result, CubeError> { + let all_symbols = self + .query_properties + .get_member_symbols(true, true, false, true, &measures); - let subquery_dimensions = collect_sub_query_dimensions_from_symbols( - &all_symbols, - &self.join_planner, - &join, - self.query_tools.clone(), - )?; + let subquery_dimensions = + collect_sub_query_dimensions_from_symbols(&all_symbols, &self.join_planner, &join)?; let dimension_subquery_planner = DimensionSubqueryPlanner::try_new( &subquery_dimensions, @@ -295,17 +264,20 @@ impl MultipliedMeasuresQueryPlanner { let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; - let source = self.join_planner.make_join_logical_plan(join)?; + let source = self + .join_planner + .make_join_logical_plan(join, subquery_dimension_queries.clone())?; - let schema = Rc::new(LogicalSchema { - dimensions: self.query_properties.dimension_symbols(), - time_dimensions: self.query_properties.time_dimension_symbols(), - measures: measures_symbols, - multiplied_measures: self - .full_key_aggregate_measures - .rendered_as_multiplied_measures - .clone(), - }); + let schema = LogicalSchema::default() + .set_dimensions(self.query_properties.dimensions().clone()) + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .set_measures(measures.clone()) + .set_multiplied_measures( + self.full_key_aggregate_measures + .rendered_as_multiplied_measures + .clone(), + ) + .into_rc(); let logical_filter = Rc::new(LogicalFilter { dimensions_filters: self.query_properties.dimensions_filters().clone(), @@ -314,42 +286,33 @@ impl MultipliedMeasuresQueryPlanner { segments: self.query_properties.segments().clone(), }); - let query = SimpleQuery { + let query = Query { schema, filter: logical_filter, - offset: None, - limit: None, - ungrouped: self.query_properties.ungrouped(), - dimension_subqueries: subquery_dimension_queries, - source: SimpleQuerySource::LogicalJoin(source), - order_by: vec![], + modifers: Rc::new(LogicalQueryModifiers { + offset: None, + limit: None, + ungrouped: self.query_properties.ungrouped(), + order_by: vec![], + }), + source: QuerySource::LogicalJoin(source), + multistage_members: vec![], }; Ok(Rc::new(query)) } fn key_query( &self, - dimensions: &Vec>, + dimensions: &Vec>, key_join: Rc, - key_cube_name: &String, + key_cube: Rc, ) -> Result, CubeError> { - let source = self.join_planner.make_join_logical_plan(key_join.clone())?; - - let dimensions_symbols = dimensions - .iter() - .map(|d| d.member_evaluator().clone()) - .collect(); - let all_symbols = self.query_properties - .get_member_symbols(true, true, false, true, &dimensions_symbols); + .get_member_symbols(true, true, false, true, &dimensions); - let subquery_dimensions = collect_sub_query_dimensions_from_symbols( - &all_symbols, - &self.join_planner, - &key_join, - self.query_tools.clone(), - )?; + let subquery_dimensions = + collect_sub_query_dimensions_from_symbols(&all_symbols, &self.join_planner, &key_join)?; let dimension_subquery_planner = DimensionSubqueryPlanner::try_new( &subquery_dimensions, @@ -359,6 +322,10 @@ impl MultipliedMeasuresQueryPlanner { let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; + let source = self + .join_planner + .make_join_logical_plan(key_join.clone(), subquery_dimension_queries)?; + let logical_filter = Rc::new(LogicalFilter { dimensions_filters: self.query_properties.dimensions_filters().clone(), time_dimensions_filters: self.query_properties.time_dimensions_filters().clone(), @@ -366,14 +333,18 @@ impl MultipliedMeasuresQueryPlanner { segments: self.query_properties.segments().clone(), }); + let schema = LogicalSchema::default() + .set_dimensions(self.query_properties.dimensions().clone()) + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .into_rc(); + let keys_query = KeysSubQuery { - time_dimensions: self.query_properties.time_dimension_symbols(), - dimensions: self.query_properties.dimension_symbols(), - primary_keys_dimensions: dimensions_symbols, + schema, + primary_keys_dimensions: dimensions.clone(), filter: logical_filter, source, - dimension_subqueries: subquery_dimension_queries, - key_cube_name: key_cube_name.clone(), + //dimension_subqueries: subquery_dimension_queries, + pk_cube: key_cube, }; Ok(Rc::new(keys_query)) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs index 263db7b5437b9..af9b64029bc53 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs @@ -1,5 +1,6 @@ use crate::plan::{Expr, MemberExpression, OrderBy}; -use crate::planner::{BaseMember, OrderByItem, QueryProperties}; +use crate::planner::sql_evaluator::MemberSymbol; +use crate::planner::{OrderByItem, QueryProperties}; use std::rc::Rc; pub struct OrderPlanner { @@ -20,7 +21,7 @@ impl OrderPlanner { pub fn custom_order( order_by: &Vec, - members: &Vec>, + members: &Vec>, ) -> Vec { let mut result = Vec::new(); for itm in order_by.iter() { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs index a41db911f7258..ec7911cd4b88e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs @@ -21,47 +21,46 @@ impl SimpleQueryPlanner { } pub fn plan(&self) -> Result, CubeError> { - let (source, subquery_dimension_queries) = self.source_and_subquery_dimensions()?; + let source = self.source_and_subquery_dimensions()?; let multiplied_measures = self .query_properties .full_key_aggregate_measures()? .rendered_as_multiplied_measures .clone(); - let schema = Rc::new(LogicalSchema { - dimensions: self.query_properties.dimension_symbols(), - measures: self.query_properties.measure_symbols(), - time_dimensions: self.query_properties.time_dimension_symbols(), - multiplied_measures, - }); + let schema = LogicalSchema::default() + .set_dimensions(self.query_properties.dimensions().clone()) + .set_measures(self.query_properties.measures().clone()) + .set_time_dimensions(self.query_properties.time_dimensions().clone()) + .set_multiplied_measures(multiplied_measures) + .into_rc(); let logical_filter = Rc::new(LogicalFilter { dimensions_filters: self.query_properties.dimensions_filters().clone(), time_dimensions_filters: self.query_properties.time_dimensions_filters().clone(), measures_filter: self.query_properties.measures_filters().clone(), segments: self.query_properties.segments().clone(), }); - let result = SimpleQuery { + let result = Query { + multistage_members: vec![], schema, filter: logical_filter, - offset: self.query_properties.offset(), - limit: self.query_properties.row_limit(), - ungrouped: self.query_properties.ungrouped(), - order_by: self.query_properties.order_by().clone(), - dimension_subqueries: subquery_dimension_queries, - source: SimpleQuerySource::LogicalJoin(source), + modifers: Rc::new(LogicalQueryModifiers { + offset: self.query_properties.offset(), + limit: self.query_properties.row_limit(), + ungrouped: self.query_properties.ungrouped(), + order_by: self.query_properties.order_by().clone(), + }), + source: QuerySource::LogicalJoin(source), }; - Ok(Rc::new(Query::SimpleQuery(result))) + Ok(Rc::new(result)) } - pub fn source_and_subquery_dimensions( - &self, - ) -> Result<(Rc, Vec>), CubeError> { + pub fn source_and_subquery_dimensions(&self) -> Result, CubeError> { let join = self.query_properties.simple_query_join()?; let subquery_dimensions = collect_sub_query_dimensions_from_symbols( - &self.query_properties.all_member_symbols(false), + &self.query_properties.all_members(false), &self.join_planner, &join, - self.query_tools.clone(), )?; let dimension_subquery_planner = DimensionSubqueryPlanner::try_new( &subquery_dimensions, @@ -70,7 +69,9 @@ impl SimpleQueryPlanner { )?; let subquery_dimension_queries = dimension_subquery_planner.plan_queries(&subquery_dimensions)?; - let source = self.join_planner.make_join_logical_plan(join.clone())?; - Ok((source, subquery_dimension_queries)) + let source = self + .join_planner + .make_join_logical_plan(join.clone(), subquery_dimension_queries)?; + Ok(source) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index 73c3587c80d43..7cdf2d006587a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -3,16 +3,18 @@ use super::filter::BaseSegment; use super::query_tools::QueryTools; use crate::cube_bridge::join_hints::JoinHintItem; use crate::cube_bridge::member_expression::MemberExpressionExpressionDef; -use crate::planner::sql_evaluator::MemberExpressionExpression; +use crate::planner::sql_evaluator::{ + MemberExpressionExpression, MemberExpressionSymbol, TimeDimensionSymbol, +}; +use crate::planner::GranularityHelper; use super::sql_evaluator::MemberSymbol; -use super::{BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension}; use crate::cube_bridge::base_query_options::BaseQueryOptions; use crate::cube_bridge::join_definition::JoinDefinition; use crate::cube_bridge::options_member::OptionsMember; -use crate::plan::{Expr, Filter, FilterItem, MemberExpression}; +use crate::plan::{Filter, FilterItem}; use crate::planner::sql_evaluator::collectors::{ - collect_multiplied_measures, has_cumulative_members, has_multi_stage_members, + collect_multiplied_measures, has_multi_stage_members, }; use cubenativeutils::CubeError; use itertools::Itertools; @@ -48,16 +50,16 @@ impl OrderByItem { #[derive(Debug, Clone)] pub struct MultipliedMeasure { - measure: Rc, + measure: Rc, cube_name: String, //May differ from cube_name of the measure for a member_expression that refers to a dimension. } impl MultipliedMeasure { - pub fn new(measure: Rc, cube_name: String) -> Rc { + pub fn new(measure: Rc, cube_name: String) -> Rc { Rc::new(Self { measure, cube_name }) } - pub fn measure(&self) -> &Rc { + pub fn measure(&self) -> &Rc { &self.measure } @@ -69,8 +71,8 @@ impl MultipliedMeasure { #[derive(Default, Clone, Debug)] pub struct FullKeyAggregateMeasures { pub multiplied_measures: Vec>, - pub regular_measures: Vec>, - pub multi_stage_measures: Vec>, + pub regular_measures: Vec>, + pub multi_stage_measures: Vec>, pub rendered_as_multiplied_measures: HashSet, } @@ -86,20 +88,20 @@ impl FullKeyAggregateMeasures { #[derive(Clone)] pub struct QueryProperties { - measures: Vec>, - dimensions: Vec>, + measures: Vec>, + dimensions: Vec>, dimensions_filters: Vec, time_dimensions_filters: Vec, measures_filters: Vec, segments: Vec, - time_dimensions: Vec>, + time_dimensions: Vec>, order_by: Vec, row_limit: Option, offset: Option, query_tools: Rc, ignore_cumulative: bool, ungrouped: bool, - multi_fact_join_groups: Vec<(Rc, Vec>)>, + multi_fact_join_groups: Vec<(Rc, Vec>)>, pre_aggregation_query: bool, total_query: bool, query_join_hints: Rc>, @@ -118,9 +120,7 @@ impl QueryProperties { .iter() .map(|d| match d { OptionsMember::MemberName(member_name) => { - let evaluator = - evaluator_compiler.add_dimension_evaluator(member_name.clone())?; - BaseDimension::try_new_required(evaluator, query_tools.clone()) + evaluator_compiler.add_dimension_evaluator(member_name.clone()) } OptionsMember::MemberExpression(member_expression) => { let cube_name = @@ -135,7 +135,7 @@ impl QueryProperties { } else { "".to_string() }; - let expression_evaluator = match member_expression.expression()? { + let expression_call = match member_expression.expression()? { MemberExpressionExpressionDef::Sql(sql) => { evaluator_compiler.compile_sql_call(&cube_name, sql)? } @@ -145,13 +145,16 @@ impl QueryProperties { ))); } }; - BaseDimension::try_new_from_expression( - expression_evaluator, - cube_name, - name, + let member_expression_symbol = MemberExpressionSymbol::try_new( + cube_name.clone(), + name.clone(), + MemberExpressionExpression::SqlCall(expression_call), member_expression.static_data().definition.clone(), - query_tools.clone(), - ) + query_tools.base_tools().clone(), + )?; + Ok(MemberSymbol::new_member_expression( + member_expression_symbol, + )) } }) .collect::, _>>()? @@ -163,16 +166,30 @@ impl QueryProperties { { time_dimensions .iter() - .map(|d| { - let evaluator = + .map(|d| -> Result, CubeError> { + let base_symbol = evaluator_compiler.add_dimension_evaluator(d.dimension.clone())?; - BaseTimeDimension::try_new_required( - query_tools.clone(), - evaluator, + let granularity_obj = GranularityHelper::make_granularity_obj( + query_tools.cube_evaluator().clone(), &mut evaluator_compiler, + query_tools.timezone().clone(), + &base_symbol.cube_name(), + &base_symbol.name(), d.granularity.clone(), - d.date_range.clone(), - ) + )?; + let date_range_tuple = if let Some(date_range) = &d.date_range { + assert_eq!(date_range.len(), 2); + Some((date_range[0].clone(), date_range[1].clone())) + } else { + None + }; + let symbol = MemberSymbol::new_time_dimension(TimeDimensionSymbol::new( + base_symbol, + d.granularity.clone(), + granularity_obj, + date_range_tuple, + )); + Ok(symbol) }) .collect::, _>>()? } else { @@ -184,9 +201,7 @@ impl QueryProperties { .iter() .map(|d| match d { OptionsMember::MemberName(member_name) => { - let evaluator = - evaluator_compiler.add_measure_evaluator(member_name.clone())?; - BaseMeasure::try_new_required(evaluator, query_tools.clone()) + evaluator_compiler.add_measure_evaluator(member_name.clone()) } OptionsMember::MemberExpression(member_expression) => { let cube_name = @@ -235,13 +250,14 @@ impl QueryProperties { } }; - BaseMeasure::try_new_from_expression( + let member_expression_symbol = MemberExpressionSymbol::try_new( + cube_name.clone(), + name.clone(), expression, - cube_name, - name, member_expression.static_data().definition.clone(), - query_tools.clone(), - ) + query_tools.base_tools().clone(), + )?; + Ok(MemberSymbol::new_member_expression(member_expression_symbol)) } }) .collect::, _>>()? @@ -336,7 +352,13 @@ impl QueryProperties { //FIXME may be this filter should be applied on other place let time_dimensions = time_dimensions .into_iter() - .filter(|dim| dim.has_granularity()) + .filter(|dim| { + if let Ok(td) = dim.as_time_dimension() { + td.has_granularity() + } else { + true + } + }) .collect_vec(); let order_by = if let Some(order) = &options.static_data().order { @@ -344,13 +366,13 @@ impl QueryProperties { .iter() .map(|o| -> Result<_, CubeError> { let evaluator = if let Some(found) = - dimensions.iter().find(|d| d.name() == &o.id) + dimensions.iter().find(|d| d.name() == o.id) { - found.member_evaluator().clone() - } else if let Some(found) = time_dimensions.iter().find(|d| d.name() == &o.id) { - found.member_evaluator().clone() - } else if let Some(found) = measures.iter().find(|d| d.name() == &o.id) { - found.member_evaluator().clone() + found.clone() + } else if let Some(found) = time_dimensions.iter().find(|d| d.name() == o.id) { + found.clone() + } else if let Some(found) = measures.iter().find(|d| d.name() == o.id) { + found.clone() } else { evaluator_compiler.add_auto_resolved_member_evaluator(o.id.clone())? }; @@ -413,9 +435,9 @@ impl QueryProperties { pub fn try_new_from_precompiled( query_tools: Rc, - measures: Vec>, - dimensions: Vec>, - time_dimensions: Vec>, + measures: Vec>, + dimensions: Vec>, + time_dimensions: Vec>, time_dimensions_filters: Vec, dimensions_filters: Vec, measures_filters: Vec, @@ -470,8 +492,8 @@ impl QueryProperties { pub fn compute_join_multi_fact_groups_with_measures( &self, - measures: &Vec>, - ) -> Result, Vec>)>, CubeError> { + measures: &Vec>, + ) -> Result, Vec>)>, CubeError> { Self::compute_join_multi_fact_groups( self.query_join_hints.clone(), self.query_tools.clone(), @@ -492,20 +514,20 @@ impl QueryProperties { pub fn compute_join_multi_fact_groups( query_join_hints: Rc>, query_tools: Rc, - measures: &Vec>, - dimensions: &Vec>, - time_dimensions: &Vec>, + measures: &Vec>, + dimensions: &Vec>, + time_dimensions: &Vec>, time_dimensions_filters: &Vec, dimensions_filters: &Vec, measures_filters: &Vec, segments: &Vec, - ) -> Result, Vec>)>, CubeError> { + ) -> Result, Vec>)>, CubeError> { let dimensions_join_hints = query_tools .cached_data_mut() - .join_hints_for_base_member_vec(&dimensions)?; + .join_hints_for_member_symbol_vec(&dimensions)?; let time_dimensions_join_hints = query_tools .cached_data_mut() - .join_hints_for_base_member_vec(&time_dimensions)?; + .join_hints_for_member_symbol_vec(&time_dimensions)?; let time_dimensions_filters_join_hints = query_tools .cached_data_mut() .join_hints_for_filter_item_vec(&time_dimensions_filters)?; @@ -541,9 +563,8 @@ impl QueryProperties { measures .iter() .map(|m| -> Result<_, CubeError> { - let measure_join_hints = query_tools - .cached_data_mut() - .join_hints_for_member(m.member_evaluator())?; + let measure_join_hints = + query_tools.cached_data_mut().join_hints_for_member(m)?; let join = query_tools.cached_data_mut().join_by_hints( vec![measure_join_hints] .into_iter() @@ -591,36 +612,15 @@ impl QueryProperties { Ok(self.multi_fact_join_groups.first().unwrap().0.clone()) } - pub fn measures(&self) -> &Vec> { + pub fn measures(&self) -> &Vec> { &self.measures } - pub fn dimensions(&self) -> &Vec> { + pub fn dimensions(&self) -> &Vec> { &self.dimensions } - pub fn dimension_symbols(&self) -> Vec> { - self.dimensions - .iter() - .map(|d| d.member_evaluator().clone()) - .collect() - } - - pub fn time_dimension_symbols(&self) -> Vec> { - self.time_dimensions - .iter() - .map(|d| d.member_evaluator().clone()) - .collect() - } - - pub fn measure_symbols(&self) -> Vec> { - self.measures - .iter() - .map(|d| d.member_evaluator().clone()) - .collect() - } - - pub fn time_dimensions(&self) -> &Vec> { + pub fn time_dimensions(&self) -> &Vec> { &self.time_dimensions } @@ -684,67 +684,25 @@ impl QueryProperties { &self.segments } - pub fn all_dimensions_and_measures( - &self, - measures: &Vec>, - ) -> Result>, CubeError> { - let result = BaseMemberHelper::iter_as_base_member(&self.dimensions) - .chain(BaseMemberHelper::iter_as_base_member(&self.time_dimensions)) - .chain(BaseMemberHelper::iter_as_base_member(&measures)) - .collect_vec(); - Ok(result) - } - - pub fn dimensions_for_select(&self) -> Vec> { - let time_dimensions = self - .time_dimensions - .iter() - .map(|d| -> Rc { d.clone() }); - let dimensions = self - .dimensions - .iter() - .map(|d| -> Rc { d.clone() }); - dimensions.chain(time_dimensions).collect() - } - - pub fn dimensions_for_select_append( - &self, - append: &Vec>, - ) -> Vec> { - let time_dimensions = BaseMemberHelper::iter_as_base_member(&self.time_dimensions); - let append_dims = append.iter().cloned(); - let dimensions = BaseMemberHelper::iter_as_base_member(&self.dimensions); - dimensions - .chain(time_dimensions) - .chain(append_dims) - .collect() - } - - pub fn all_members(&self, exclude_time_dimensions: bool) -> Vec> { - let dimensions = self - .dimensions - .iter() - .map(|d| -> Rc { d.clone() }); - let measures = self - .measures - .iter() - .map(|m| -> Rc { m.clone() }); + pub fn all_members(&self, exclude_time_dimensions: bool) -> Vec> { + let dimensions = self.dimensions.iter().cloned(); + let measures = self.measures.iter().cloned(); if exclude_time_dimensions { dimensions.chain(measures).collect_vec() } else { - let time_dimensions = self - .time_dimensions - .iter() - .map(|d| -> Rc { d.base_dimension().clone() }); + let time_dimensions = self.time_dimensions.iter().map(|d| { + if let Ok(td) = d.as_time_dimension() { + td.base_symbol().clone() + } else { + d.clone() + } + }); dimensions .chain(time_dimensions) .chain(measures) .collect_vec() } } - pub fn all_member_symbols(&self, exclude_time_dimensions: bool) -> Vec> { - self.get_member_symbols(!exclude_time_dimensions, true, true, true, &vec![]) - } pub fn get_member_symbols( &self, @@ -756,13 +714,13 @@ impl QueryProperties { ) -> Vec> { let mut members = additional_symbols.clone(); if include_time_dimensions { - members.append(&mut self.time_dimension_symbols()); + members.extend(self.time_dimensions.iter().cloned()); } if include_dimensions { - members.append(&mut self.dimension_symbols()); + members.extend(self.dimensions.iter().cloned()); } if include_measures { - members.append(&mut self.measure_symbols()); + members.extend(self.measures.iter().cloned()); } if include_filters { self.fill_all_filter_symbols(&mut members); @@ -781,7 +739,7 @@ impl QueryProperties { } } - pub fn group_by(&self) -> Vec { + /* pub fn group_by(&self) -> Vec { if self.ungrouped { vec![] } else { @@ -795,61 +753,30 @@ impl QueryProperties { ) .collect() } - } + } */ pub fn default_order( - dimensions: &Vec>, - time_dimensions: &Vec>, - measures: &Vec>, + dimensions: &Vec>, + time_dimensions: &Vec>, + measures: &Vec>, ) -> Vec { let mut result = Vec::new(); - if let Some(granularity_dim) = time_dimensions.iter().find(|d| d.has_granularity()) { - result.push(OrderByItem::new( - granularity_dim.member_evaluator().clone(), - false, - )); + if let Some(granularity_dim) = time_dimensions.iter().find(|d| { + if let Ok(td) = d.as_time_dimension() { + td.has_granularity() + } else { + false + } + }) { + result.push(OrderByItem::new(granularity_dim.clone(), false)); } else if !measures.is_empty() && !dimensions.is_empty() { - result.push(OrderByItem::new( - measures[0].member_evaluator().clone(), - true, - )); + result.push(OrderByItem::new(measures[0].clone(), true)); } else if !dimensions.is_empty() { - result.push(OrderByItem::new( - dimensions[0].member_evaluator().clone(), - false, - )); - } - result - } - - pub fn all_filtered_members(&self) -> HashSet { - let mut result = HashSet::new(); - for item in self.dimensions_filters().iter() { - self.fill_members_from_filter_item(item, &mut result); - } - for item in self.time_dimensions_filters().iter() { - self.fill_members_from_filter_item(item, &mut result); - } - for item in self.measures_filters().iter() { - self.fill_members_from_filter_item(item, &mut result); + result.push(OrderByItem::new(dimensions[0].clone(), false)); } result } - fn fill_members_from_filter_item(&self, item: &FilterItem, members: &mut HashSet) { - match item { - FilterItem::Group(group) => { - for item in group.items.iter() { - self.fill_members_from_filter_item(item, members) - } - } - FilterItem::Item(item) => { - members.insert(item.member_name()); - } - FilterItem::Segment(_) => {} - } - } - pub fn is_simple_query(&self) -> Result { let full_aggregate_measure = self.full_key_aggregate_measures()?; if full_aggregate_measure.multiplied_measures.is_empty() @@ -862,23 +789,11 @@ impl QueryProperties { } } - pub fn should_use_time_series(&self) -> Result { - for member in self.all_members(false) { - if has_cumulative_members(&member.member_evaluator())? { - return Ok(true); - } - } - Ok(false) - } - pub fn full_key_aggregate_measures(&self) -> Result { let mut result = FullKeyAggregateMeasures::default(); let measures = self.all_used_measures()?; for m in measures.iter() { - if has_multi_stage_members( - m.member_evaluator(), - self.ignore_cumulative || self.pre_aggregation_query, - )? { + if has_multi_stage_members(m, self.ignore_cumulative || self.pre_aggregation_query)? { result.multi_stage_measures.push(m.clone()) } else { let join = self @@ -887,17 +802,18 @@ impl QueryProperties { .expect("No join groups returned for single measure multi-fact join group") .0 .clone(); - for item in collect_multiplied_measures( - self.query_tools.clone(), - m.member_evaluator(), - join, - )? { + for item in collect_multiplied_measures(m, join)? { if item.multiplied { result .rendered_as_multiplied_measures .insert(item.measure.full_name()); } - if item.multiplied && !item.measure.can_be_used_as_additive_in_multplied() { + if item.multiplied + && !item + .measure + .as_measure()? + .can_used_as_addictive_in_multplied() + { result .multiplied_measures .push(MultipliedMeasure::new(item.measure.clone(), item.cube_name)); @@ -911,7 +827,7 @@ impl QueryProperties { Ok(result) } - fn all_used_measures(&self) -> Result>, CubeError> { + fn all_used_measures(&self) -> Result>, CubeError> { let mut measures = self.measures.clone(); for item in self.measures_filters.iter() { self.fill_missed_measures_from_filter(item, &mut measures)?; @@ -922,7 +838,7 @@ impl QueryProperties { fn fill_missed_measures_from_filter( &self, item: &FilterItem, - measures: &mut Vec>, + measures: &mut Vec>, ) -> Result<(), CubeError> { match item { FilterItem::Group(group) => { @@ -933,10 +849,7 @@ impl QueryProperties { FilterItem::Item(item) => { let item_member_name = item.member_name(); if !measures.iter().any(|m| m.full_name() == item_member_name) { - measures.push(BaseMeasure::try_new_required( - item.member_evaluator().clone(), - self.query_tools.clone(), - )?); + measures.push(item.member_evaluator().clone()); } } FilterItem::Segment(_) => {} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs index e3f9b48399988..90def6016957d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs @@ -1,5 +1,5 @@ use super::sql_evaluator::{Compiler, MemberSymbol}; -use super::{BaseMember, ParamsAllocator}; +use super::ParamsAllocator; use crate::cube_bridge::base_tools::BaseTools; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::join_definition::JoinDefinition; @@ -52,15 +52,6 @@ impl QueryToolsCachedData { } } - pub fn join_hints_for_base_member_vec( - &mut self, - vec: &Vec>, - ) -> Result>>, CubeError> { - vec.iter() - .map(|b| self.join_hints_for_member(&b.member_evaluator())) - .collect::, _>>() - } - pub fn join_hints_for_member_symbol_vec( &mut self, vec: &Vec>, 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 e98b27a23753b..2ea8970429b53 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,6 +1,5 @@ use crate::cube_bridge::join_hints::JoinHintItem; use crate::planner::sql_evaluator::{MemberSymbol, TraversalVisitor}; -use crate::planner::BaseMeasure; use cubenativeutils::CubeError; use itertools::Itertools; use std::rc::Rc; @@ -84,11 +83,11 @@ pub fn collect_join_hints(node: &Rc) -> Result, } pub fn collect_join_hints_for_measures( - measures: &Vec>, + measures: &Vec>, ) -> Result, CubeError> { let mut visitor = JoinHintsCollector::new(); for meas in measures.iter() { - visitor.apply(&meas.member_evaluator(), &())?; + visitor.apply(&meas, &())?; } let res = visitor.extract_result(); Ok(res) 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 f824efaa7f709..26836acbe5796 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 @@ -1,7 +1,5 @@ use crate::cube_bridge::join_definition::JoinDefinition; -use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::{MemberSymbol, TraversalVisitor}; -use crate::planner::BaseMeasure; use cubenativeutils::CubeError; use std::collections::HashSet; use std::rc::Rc; @@ -63,25 +61,19 @@ impl TraversalVisitor for CompositeMeasuresCollector { #[derive(Debug)] pub struct MeasureResult { pub multiplied: bool, - pub measure: Rc, + pub measure: Rc, pub cube_name: String, } pub struct MultipliedMeasuresCollector { - query_tools: Rc, composite_measures: HashSet, colllected_measures: Vec, join: Rc, } impl MultipliedMeasuresCollector { - pub fn new( - query_tools: Rc, - composite_measures: HashSet, - join: Rc, - ) -> Self { + pub fn new(composite_measures: HashSet, join: Rc) -> Self { Self { - query_tools, composite_measures, join, colllected_measures: vec![], @@ -115,8 +107,7 @@ impl TraversalVisitor for MultipliedMeasuresCollector { if !self.composite_measures.contains(&full_name) { self.colllected_measures.push(MeasureResult { multiplied, - measure: BaseMeasure::try_new(node.clone(), self.query_tools.clone())? - .unwrap(), + measure: node.clone(), cube_name: node.cube_name(), }) } @@ -136,17 +127,15 @@ impl TraversalVisitor for MultipliedMeasuresCollector { } pub fn collect_multiplied_measures( - query_tools: Rc, node: &Rc, join: Rc, ) -> Result, CubeError> { if let Ok(member_expression) = node.as_member_expression() { if let Some(cube_names) = member_expression.cube_names_if_dimension_only_expression()? { let result = if cube_names.is_empty() { - let measure = BaseMeasure::try_new(node.clone(), query_tools.clone())?.unwrap(); vec![MeasureResult { - cube_name: measure.cube_name().clone(), - measure, + cube_name: node.cube_name().clone(), + measure: node.clone(), multiplied: false, }] } else if cube_names.len() == 1 { @@ -157,10 +146,9 @@ pub fn collect_multiplied_measures( .get(&cube_name) .unwrap_or(&false) .clone(); - let measure = BaseMeasure::try_new(node.clone(), query_tools.clone())?.unwrap(); vec![MeasureResult { - measure, + measure: node.clone(), cube_name, multiplied, }] @@ -178,8 +166,7 @@ pub fn collect_multiplied_measures( let mut composite_collector = CompositeMeasuresCollector::new(); composite_collector.apply(node, &CompositeMeasureCollectorState::new(None))?; let composite_measures = composite_collector.extract_result(); - let mut visitor = - MultipliedMeasuresCollector::new(query_tools.clone(), composite_measures, join.clone()); + let mut visitor = MultipliedMeasuresCollector::new(composite_measures, join.clone()); visitor.apply(node, &())?; let result = visitor.extract_result(); Ok(result) 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 a3b4de986b798..3f95e194bc3e8 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 @@ -1,8 +1,6 @@ use crate::cube_bridge::join_definition::JoinDefinition; use crate::planner::planners::JoinPlanner; -use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::{DimensionSymbol, MemberSymbol, TraversalVisitor}; -use crate::planner::{BaseDimension, BaseMember}; use cubenativeutils::CubeError; use itertools::Itertools; use std::rc::Rc; @@ -63,21 +61,18 @@ impl TraversalVisitor for SubQueryDimensionsCollector { } pub fn collect_sub_query_dimensions_from_members( - members: &Vec>, + members: &Vec>, join_planner: &JoinPlanner, join: &Rc, - query_tools: Rc, -) -> Result>, CubeError> { - let symbols = members.iter().map(|m| m.member_evaluator()).collect_vec(); - collect_sub_query_dimensions_from_symbols(&symbols, join_planner, join, query_tools) +) -> Result>, CubeError> { + collect_sub_query_dimensions_from_symbols(&members, join_planner, join) } pub fn collect_sub_query_dimensions_from_symbols( members: &Vec>, join_planner: &JoinPlanner, join: &Rc, - query_tools: Rc, -) -> Result>, CubeError> { +) -> Result>, CubeError> { let mut visitor = SubQueryDimensionsCollector::new(); for member in members.iter() { visitor.apply(&member, &())?; @@ -88,11 +83,7 @@ pub fn collect_sub_query_dimensions_from_symbols( visitor.apply(&dep, &())?; } } - visitor - .extract_result() - .into_iter() - .map(|s| BaseDimension::try_new_required(s, query_tools.clone())) - .collect::, CubeError>>() + Ok(visitor.extract_result()) } pub fn collect_sub_query_dimensions( diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/references_builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/references_builder.rs index 01b2a88bcdc0e..fb61e3cf6249f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/references_builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/references_builder.rs @@ -107,11 +107,13 @@ impl ReferencesBuilder { pub fn resolve_references_for_filter( &self, - filter: &Filter, + filter: &Option, references: &mut HashMap, ) -> Result<(), CubeError> { - for itm in filter.items.iter() { - self.resolve_references_for_filter_item(itm, references)?; + if let Some(filter) = filter { + for itm in filter.items.iter() { + self.resolve_references_for_filter_item(itm, references)?; + } } Ok(()) } 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 9bcb1979f1c46..7afdb2e5b8c5a 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 @@ -25,6 +25,9 @@ impl CubeNameSymbol { pub fn cube_name(&self) -> &String { &self.cube_name } + pub fn alias(&self) -> String { + PlanSqlTemplates::alias_name(&self.cube_name) + } } pub struct CubeNameSymbolFactory { @@ -127,6 +130,10 @@ impl CubeTableSymbol { pub fn cube_name(&self) -> &String { &self.cube_name } + + pub fn alias(&self) -> String { + PlanSqlTemplates::alias_name(&self.cube_name) + } } pub struct CubeTableSymbolFactory { 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 dacbe81fd7a0f..13d120f4fc114 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 @@ -35,6 +35,7 @@ pub struct CalendarDimensionTimeShift { pub struct DimensionSymbol { cube_name: String, name: String, + alias: String, member_sql: Option>, latitude: Option>, longitude: Option>, @@ -53,6 +54,7 @@ impl DimensionSymbol { pub fn new( cube_name: String, name: String, + alias: String, member_sql: Option>, is_reference: bool, is_view: bool, @@ -67,6 +69,7 @@ impl DimensionSymbol { Rc::new(Self { cube_name, name, + alias, member_sql, is_reference, latitude, @@ -126,6 +129,10 @@ impl DimensionSymbol { format!("{}.{}", self.cube_name, self.name) } + pub fn alias(&self) -> String { + self.alias.clone() + } + pub fn owned_by_cube(&self) -> bool { self.definition.static_data().owned_by_cube.unwrap_or(true) } @@ -142,6 +149,13 @@ impl DimensionSymbol { &self.definition.static_data().dimension_type } + pub fn propagate_filters_to_sub_query(&self) -> bool { + self.definition + .static_data() + .propagate_filters_to_sub_query + .unwrap_or(false) + } + pub fn is_reference(&self) -> bool { self.is_reference } @@ -419,6 +433,8 @@ impl SymbolFactory for DimensionSymbolFactory { }; let cube = cube_evaluator.cube_from_path(cube_name.clone())?; + let alias = + PlanSqlTemplates::memeber_alias_name(cube.static_data().resolved_alias(), &name, &None); let is_view = cube.static_data().is_view.unwrap_or(false); let is_calendar = cube.static_data().is_calendar.unwrap_or(false); let mut is_self_time_shift_pk = false; @@ -464,6 +480,7 @@ impl SymbolFactory for DimensionSymbolFactory { Ok(MemberSymbol::new_dimension(DimensionSymbol::new( cube_name, name, + alias, sql, is_reference, is_view, 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 be71028fc369a..7452bf45f1448 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 @@ -64,6 +64,7 @@ pub enum MeasureTimeShifts { pub struct MeasureSymbol { cube_name: String, name: String, + alias: String, owned_by_cube: bool, measure_type: String, rolling_window: Option, @@ -86,6 +87,7 @@ impl MeasureSymbol { pub fn new( cube_name: String, name: String, + alias: String, member_sql: Option>, is_reference: bool, is_view: bool, @@ -106,6 +108,7 @@ impl MeasureSymbol { Rc::new(Self { cube_name, name, + alias, member_sql, is_reference, is_view, @@ -135,6 +138,7 @@ impl MeasureSymbol { Rc::new(Self { cube_name: self.cube_name.clone(), name: self.name.clone(), + alias: self.alias.clone(), owned_by_cube: self.owned_by_cube, measure_type, rolling_window: None, @@ -217,6 +221,7 @@ impl MeasureSymbol { Ok(Rc::new(Self { cube_name: self.cube_name.clone(), name: self.name.clone(), + alias: self.alias.clone(), owned_by_cube: self.owned_by_cube, measure_type: result_measure_type, rolling_window: self.rolling_window.clone(), @@ -240,6 +245,10 @@ impl MeasureSymbol { format!("{}.{}", self.cube_name, self.name) } + pub fn alias(&self) -> String { + self.alias.clone() + } + pub fn is_splitted_source(&self) -> bool { self.is_splitted_source } @@ -686,6 +695,8 @@ impl SymbolFactory for MeasureSymbolFactory { let owned_by_cube = definition.static_data().owned_by_cube.unwrap_or(true); let is_multi_stage = definition.static_data().multi_stage.unwrap_or(false); let cube = cube_evaluator.cube_from_path(cube_name.clone())?; + let alias = + PlanSqlTemplates::memeber_alias_name(cube.static_data().resolved_alias(), &name, &None); let is_view = cube.static_data().is_view.unwrap_or(false); @@ -705,6 +716,7 @@ impl SymbolFactory for MeasureSymbolFactory { Ok(MemberSymbol::new_measure(MeasureSymbol::new( cube_name, name, + alias, sql, 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 76e10f82543c7..a1c97877712e3 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 @@ -67,6 +67,10 @@ impl MemberExpressionSymbol { format!("expr:{}.{}", self.cube_name, self.name) } + pub fn alias(&self) -> String { + PlanSqlTemplates::alias_name(&self.name) + } + pub fn is_reference(&self) -> bool { self.is_reference } 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 b572373427ba8..300310e360dcd 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,8 +4,6 @@ use super::{ CubeNameSymbol, CubeTableSymbol, DimensionSymbol, MeasureSymbol, MemberExpressionSymbol, TimeDimensionSymbol, }; -use crate::planner::query_tools::QueryTools; -use crate::planner::{BaseMember, MemberSymbolRef}; use std::fmt::Debug; use std::rc::Rc; @@ -88,6 +86,18 @@ impl MemberSymbol { Self::MemberExpression(e) => e.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::CubeName(c) => c.alias(), + Self::CubeTable(c) => c.alias(), + Self::MemberExpression(e) => e.alias(), + } + } + pub fn name(&self) -> String { match self { Self::Dimension(d) => d.name().clone(), @@ -255,12 +265,4 @@ impl MemberSymbol { pub fn is_leaf(&self) -> bool { self.get_dependencies().is_empty() } - - // To back compatibility with code that use BaseMembers - pub fn as_base_member( - self: Rc, - query_tools: Rc, - ) -> Result, CubeError> { - MemberSymbolRef::try_new(self, query_tools).map(|r| r.as_base_member()) - } } 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 4f5b4a839db72..4db4ab24b61fe 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 @@ -11,6 +11,7 @@ use std::rc::Rc; pub struct TimeDimensionSymbol { base_symbol: Rc, full_name: String, + alias: String, granularity: Option, granularity_obj: Option, date_range: Option<(String, String)>, @@ -30,8 +31,10 @@ impl TimeDimensionSymbol { "day".to_string() }; let full_name = format!("{}_{}", base_symbol.full_name(), name_suffix); + let alias = format!("{}_{}", base_symbol.alias(), name_suffix); Rc::new(Self { base_symbol, + alias, granularity, granularity_obj, full_name, @@ -48,10 +51,49 @@ impl TimeDimensionSymbol { &self.granularity } + pub fn has_granularity(&self) -> bool { + self.granularity.is_some() + } + pub fn granularity_obj(&self) -> &Option { &self.granularity_obj } + pub fn resolved_granularity(&self) -> Result, CubeError> { + let res = if let Some(granularity_obj) = &self.granularity_obj { + Some(granularity_obj.resolved_granularity()?) + } else { + None + }; + Ok(res) + } + + pub fn change_granularity( + &self, + query_tools: Rc, + new_granularity: Option, + ) -> Result, CubeError> { + let evaluator_compiler_cell = query_tools.evaluator_compiler().clone(); + let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); + + let new_granularity_obj = GranularityHelper::make_granularity_obj( + query_tools.cube_evaluator().clone(), + &mut evaluator_compiler, + query_tools.timezone(), + &&self.base_symbol.cube_name(), + &self.base_symbol.name(), + new_granularity.clone(), + )?; + let date_range_tuple = self.date_range.clone(); + let result = TimeDimensionSymbol::new( + self.base_symbol.clone(), + new_granularity.clone(), + new_granularity_obj.clone(), + date_range_tuple, + ); + Ok(result) + } + pub fn full_name(&self) -> String { self.full_name.clone() } @@ -60,6 +102,10 @@ impl TimeDimensionSymbol { self.alias_suffix.clone() } + pub fn alias(&self) -> String { + self.alias.clone() + } + pub fn owned_by_cube(&self) -> bool { self.base_symbol.owned_by_cube() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs index 82c05856ece9a..f5b9f47208f78 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs @@ -208,6 +208,11 @@ impl PlanSqlTemplates { ) } + pub fn group_any(&self, expr: &str) -> Result { + self.render + .render_template("functions/GROUP_ANY", context! { expr => expr }) + } + pub fn is_null_expr(&self, expr: &str, negate: bool) -> Result { self.render.render_template( "expressions/is_null", diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/time_dimension/granularity_helper.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/time_dimension/granularity_helper.rs index 336113a76c8a0..d53ab63a24027 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/time_dimension/granularity_helper.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/time_dimension/granularity_helper.rs @@ -1,6 +1,6 @@ use crate::cube_bridge::evaluator::CubeEvaluator; use crate::planner::sql_evaluator::Compiler; -use crate::planner::BaseTimeDimension; +use crate::planner::sql_evaluator::TimeDimensionSymbol; use crate::planner::Granularity; use chrono::prelude::*; use chrono_tz::Tz; @@ -49,28 +49,33 @@ impl GranularityHelper { } pub fn find_dimension_with_min_granularity( - dimensions: &Vec>, - ) -> Result, CubeError> { + dimensions: &Vec>, + ) -> Result, CubeError> { if dimensions.is_empty() { return Err(CubeError::internal( "No dimensions provided for find_dimension_with_min_granularity".to_string(), )); } let first = Ok(dimensions[0].clone()); - dimensions.iter().skip(1).fold(first, |acc, d| match acc { - Ok(min_dim) => { - let min_granularity = Self::min_granularity( - &min_dim.resolved_granularity()?, - &d.resolved_granularity()?, - )?; - if min_granularity == min_dim.get_granularity() { - Ok(min_dim) - } else { - Ok(d.clone()) + dimensions + .iter() + .skip(1) + .fold(first, |acc, d| -> Result<_, CubeError> { + match acc { + Ok(min_dim) => { + let min_granularity = Self::min_granularity( + &min_dim.resolved_granularity()?, + &d.resolved_granularity()?, + )?; + if &min_granularity == min_dim.granularity() { + Ok(min_dim) + } else { + Ok(d.clone()) + } + } + Err(e) => Err(e), } - } - Err(e) => Err(e), - }) + }) } pub fn granularity_from_interval(interval: &Option) -> Option { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs index 2e72bebbc8a1f..8d3bd1a85265b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs @@ -13,17 +13,23 @@ pub struct FiltersContext { } pub struct VisitorContext { + query_tools: Rc, node_processor: Rc, all_filters: Option, //To pass to FILTER_PARAMS and FILTER_GROUP filters_context: FiltersContext, } impl VisitorContext { - pub fn new(nodes_factory: &SqlNodesFactory, all_filters: Option) -> Self { + pub fn new( + query_tools: Rc, + nodes_factory: &SqlNodesFactory, + all_filters: Option, + ) -> Self { let filters_context = FiltersContext { use_local_tz: nodes_factory.use_local_tz_in_date_range(), }; Self { + query_tools, node_processor: nodes_factory.default_node_processor(), all_filters, filters_context, @@ -41,15 +47,18 @@ impl VisitorContext { pub fn filters_context(&self) -> &FiltersContext { &self.filters_context } + + pub fn query_tools(&self) -> Rc { + self.query_tools.clone() + } } pub fn evaluate_with_context( node: &Rc, - query_tools: Rc, context: Rc, templates: &PlanSqlTemplates, ) -> Result { - let visitor = context.make_visitor(query_tools); + let visitor = context.make_visitor(context.query_tools()); let node_processor = context.node_processor(); visitor.apply(node, node_processor, templates) @@ -57,11 +66,10 @@ pub fn evaluate_with_context( pub fn evaluate_sql_call_with_context( sql_call: &Rc, - query_tools: Rc, context: Rc, templates: &PlanSqlTemplates, ) -> Result { - let visitor = context.make_visitor(query_tools.clone()); + let visitor = context.make_visitor(context.query_tools()); let node_processor = context.node_processor(); - sql_call.eval(&visitor, node_processor, query_tools, templates) + sql_call.eval(&visitor, node_processor, context.query_tools(), templates) }