diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 3c1bc87fa3f85..fa4cd6c077734 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -3214,24 +3214,35 @@ export class BaseQuery { DATE: 'DATE({{ args_concat }})', }, statements: { - select: 'SELECT {% if distinct %}DISTINCT {% endif %}' + + select: '{% if ctes %} WITH \n' + + '{{ ctes | join(\',\n\') }}\n' + + '{% endif %}' + + 'SELECT {% if distinct %}DISTINCT {% endif %}' + '{{ select_concat | map(attribute=\'aliased\') | join(\', \') }} {% if from %}\n' + 'FROM (\n' + '{{ from | indent(2, true) }}\n' + - ') AS {{ from_alias }}{% endif %}' + + ') AS {{ from_alias }}{% elif from_prepared %}\n' + + 'FROM {{ from_prepared }}' + + '{% endif %}' + '{% if filter %}\nWHERE {{ filter }}{% endif %}' + '{% if group_by %}\nGROUP BY {{ group_by }}{% endif %}' + + '{% if having %}\nHAVING {{ having }}{% endif %}' + '{% if order_by %}\nORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}{% endif %}' + '{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}' + '{% if offset is not none %}\nOFFSET {{ offset }}{% endif %}', group_by_exprs: '{{ group_by | map(attribute=\'index\') | join(\', \') }}', + join: '{{ join_type }} JOIN {{ source }} ON {{ condition }}', + cte: '{{ alias }} AS ({{ query | indent(2, true) }})' }, expressions: { + column_reference: '{% if table_name %}{{ table_name }}.{% endif %}{{ name }}', column_aliased: '{{expr}} {{quoted_alias}}', + query_aliased: '{{ query }} AS {{ quoted_alias }}', case: 'CASE{% if expr %} {{ expr }}{% endif %}{% for when, then in when_then %} WHEN {{ when }} THEN {{ then }}{% endfor %}{% if else_expr %} ELSE {{ else_expr }}{% endif %} END', is_null: '{{ expr }} IS {% if negate %}NOT {% endif %}NULL', binary: '({{ left }} {{ op }} {{ right }})', sort: '{{ expr }} {% if asc %}ASC{% else %}DESC{% endif %} NULLS {% if nulls_first %}FIRST{% else %}LAST{% endif %}', + order_by: '{% if index %} {{ index }} {% else %} {{ expr }} {% endif %} {% if asc %}ASC{% else %}DESC{% endif %}{% if nulls_first %} NULLS FIRST{% endif %}', cast: 'CAST({{ expr }} AS {{ data_type }})', window_function: '{{ fun_call }} OVER ({% if partition_by_concat %}PARTITION BY {{ partition_by_concat }}{% if order_by_concat or window_frame %} {% endif %}{% endif %}{% if order_by_concat %}ORDER BY {{ order_by_concat }}{% if window_frame %} {% endif %}{% endif %}{% if window_frame %}{{ window_frame }}{% endif %})', window_frame_bounds: '{{ frame_type }} BETWEEN {{ frame_start }} AND {{ frame_end }}', @@ -3260,7 +3271,8 @@ export class BaseQuery { gt: '{{ column }} > {{ param }}', gte: '{{ column }} >= {{ param }}', lt: '{{ column }} < {{ param }}', - lte: '{{ column }} <= {{ param }}' + lte: '{{ column }} <= {{ param }}', + always_true: '1 == 1' }, quotes: { @@ -3270,6 +3282,10 @@ export class BaseQuery { params: { param: '?' }, + join_types: { + inner: 'INNER', + left: 'LEFT' + }, window_frame_types: { rows: 'ROWS', range: 'RANGE', diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/sql_templates_render.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/sql_templates_render.rs index 5377a74e9bb09..c28d6bd26494a 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/sql_templates_render.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/sql_templates_render.rs @@ -9,6 +9,7 @@ use std::marker::PhantomData; pub trait SqlTemplatesRender { fn contains_template(&self, template_name: &str) -> bool; fn render_template(&self, name: &str, ctx: Value) -> Result; + fn get_template(&self, template_name: &str) -> Result<&String, CubeError>; } pub struct NativeSqlTemplatesRender { @@ -44,6 +45,12 @@ impl SqlTemplatesRender for NativeSqlTemplatesRender { self.templates.contains_key(template_name) } + fn get_template(&self, template_name: &str) -> Result<&String, CubeError> { + self.templates + .get(template_name) + .ok_or_else(|| CubeError::user("{template_name} template not found".to_string())) + } + fn render_template(&self, name: &str, ctx: Value) -> Result { Ok(self .jinja diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/aggregation.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/aggregation.rs deleted file mode 100644 index f2c4506c3e870..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/aggregation.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::filter::Filter; -use super::select::Select; -use datafusion::logical_expr::Expr; - -pub struct Aggregation { - select: Select, - group_by: Vec, - aggregates: Vec, - having: Option, -} - -pub struct Join { - select: Select, - group_by: Vec, - having: Option, -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder.rs deleted file mode 100644 index 7ac78de4ac614..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn build_sql_query_and_params() -> String { - "SELECT".to_string() -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/join.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/join.rs new file mode 100644 index 0000000000000..8b2abcb1cf587 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/join.rs @@ -0,0 +1,125 @@ +use crate::plan::{Join, JoinCondition, JoinItem, QueryPlan, Schema, Select, SingleAliasedSource}; +use crate::planner::BaseCube; +use std::rc::Rc; + +pub struct JoinBuilder { + root: SingleAliasedSource, + joins: Vec, +} + +impl JoinBuilder { + pub fn new(root: SingleAliasedSource) -> Self { + Self { + root, + joins: vec![], + } + } + + pub fn new_from_cube(cube: Rc, alias: Option) -> Self { + Self::new(SingleAliasedSource::new_from_cube(cube, alias)) + } + + pub fn new_from_table_reference( + reference: String, + schema: Rc, + alias: Option, + ) -> Self { + Self::new(SingleAliasedSource::new_from_table_reference( + reference, schema, alias, + )) + } + + pub fn new_from_subquery(plan: Rc, alias: String) -> Self { + Self::new(SingleAliasedSource::new_from_subquery(plan, alias)) + } + + pub fn new_from_subselect(plan: Rc, alias: String, on: JoinCondition) { + self.join_subselect(subquery, alias, on, false) + } + + pub fn inner_join_subselect(&mut self, subquery: Rc, + alias: String, + on: JoinCondition, + is_inner: bool, + ) { + let subquery = Rc::new(QueryPlan::Select(subquery)); + let from = SingleAliasedSource::new_from_subquery(subquery, alias); + self.joins.push(JoinItem { from, on, is_inner }) + } + + fn join_cube( + &mut self, + cube: Rc, + alias: Option, + on: JoinCondition, + is_inner: bool, + ) { + let from = SingleAliasedSource::new_from_cube(cube, alias); + self.joins.push(JoinItem { from, on, is_inner }) + } + + fn join_table_reference( + &mut self, + reference: String, + schema: Rc, + alias: Option, + on: JoinCondition, + is_inner: bool, + ) { + let from = SingleAliasedSource::new_from_table_reference(reference, schema, alias); + self.joins.push(JoinItem { from, on, is_inner }) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/mod.rs new file mode 100644 index 0000000000000..846318606c10d --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/mod.rs @@ -0,0 +1,5 @@ +pub mod join; +pub mod select; + +pub use join::JoinBuilder; +pub use select::SelectBuilder; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs new file mode 100644 index 0000000000000..cf5c00864b6ff --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs @@ -0,0 +1,107 @@ +use crate::plan::{ + AliasedExpr, Cte, Expr, Filter, From, MemberExpression, OrderBy, Schema, Select, +}; +use crate::planner::{BaseMember, VisitorContext}; +use std::rc::Rc; + +pub struct SelectBuilder { + projection_columns: Vec, + from: From, + filter: Option, + group_by: Vec, + having: Option, + order_by: Vec, + context: Rc, + ctes: Vec>, + is_distinct: bool, + limit: Option, + offset: Option, + input_schema: Rc, +} + +impl SelectBuilder { + pub fn new(from: From, context: VisitorContext) -> Self { + let input_schema = from.schema.clone(); + Self { + projection_columns: vec![], + from, + filter: None, + group_by: vec![], + having: None, + order_by: vec![], + context: Rc::new(context), + ctes: vec![], + is_distinct: false, + limit: None, + offset: None, + input_schema, + } + } + + pub fn add_projection_member( + &mut self, + member: &Rc, + source: Option, + alias: Option, + ) { + let alias = if let Some(alias) = alias { + alias + } else { + self.input_schema.resolve_member_alias(&member, &source) + }; + let expr = Expr::Member(MemberExpression::new(member.clone(), source)); + let aliased_expr = AliasedExpr { + expr, + alias: alias.clone(), + }; + + self.projection_columns.push(aliased_expr); + } + + pub fn set_filter(&mut self, filter: Option) { + self.filter = filter; + } + + pub fn set_group_by(&mut self, group_by: Vec) { + self.group_by = group_by; + } + + pub fn set_having(&mut self, having: Option) { + self.having = having; + } + + pub fn set_order_by(&mut self, order_by: Vec) { + self.order_by = order_by; + } + + pub fn set_distinct(&mut self) { + self.is_distinct = true; + } + + pub fn set_limit(&mut self, limit: Option) { + self.limit = limit; + } + + pub fn set_offset(&mut self, offset: Option) { + self.offset = offset; + } + pub fn set_ctes(&mut self, ctes: Vec>) { + self.ctes = ctes; + } + + pub fn build(self) -> Select { + Select { + projection_columns: self.projection_columns, + from: self.from, + filter: self.filter, + group_by: self.group_by, + having: self.having, + order_by: self.order_by, + context: self.context.clone(), + ctes: self.ctes, + is_distinct: self.is_distinct, + limit: self.limit, + offset: self.offset, + } + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/cte.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/cte.rs new file mode 100644 index 0000000000000..dba7dd3a9c7a3 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/cte.rs @@ -0,0 +1,41 @@ +use super::{QueryPlan, Schema, Select}; +use crate::planner::sql_templates::PlanSqlTemplates; +use cubenativeutils::CubeError; + +use std::rc::Rc; + +#[derive(Clone)] +pub struct Cte { + query: Rc, + name: String, +} + +impl Cte { + pub fn new(query: Rc, name: String) -> Self { + Self { query, name } + } + + pub fn new_from_select(select: Rc, alias: String) -> Self { + Self::new(FromSource::Single(SingleAliasedSource::new_from_subquery( + Rc::new(QueryPlan::Select(plan)), + alias, + ))) + } + + pub fn to_sql( + &self, + templates: &PlanSqlTemplates, + context: Rc, + ) -> Result { let sql = match &self.source { FromSource::Empty => format!(""), - FromSource::Cube(cube) => { - let cubesql = cube.to_sql(context.clone())?; - format!(" {} ", cubesql) - } + FromSource::Single(source) => source.to_sql(templates, context.clone())?, FromSource::Join(j) => { - format!("{}", j.to_sql(context.clone())?) - } - FromSource::Subquery(s) => s.to_sql()?, - FromSource::TableReference(r, alias) => { - if let Some(alias) = alias { - format!(" {} as {} ", r, alias) - } else { - format!(" {} ", r) - } + format!("{}", j.to_sql(templates, context.clone())?) } }; Ok(sql) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs index ab920ca067ba1..19e8283bc5e68 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs @@ -1,79 +1,182 @@ -use super::{QueryPlan, Select, Subquery}; -use crate::planner::{BaseCube, BaseJoinCondition, VisitorContext}; +use super::{Schema, SingleAliasedSource}; +use crate::planner::sql_templates::PlanSqlTemplates; +use crate::planner::{BaseJoinCondition, BaseMember, VisitorContext}; use cubenativeutils::CubeError; use std::rc::Rc; -pub enum JoinSource { - Subquery(Subquery), - Cube(Rc), - TableReference(String, String), +pub struct DimensionJoinCondition { + left_source: String, + right_source: String, + dimensions: Vec>, + null_check: bool, } -impl JoinSource { - pub fn new_from_query_plan(plan: Rc, alias: String) -> Self { - Self::Subquery(Subquery::new(plan, alias)) +impl DimensionJoinCondition { + pub fn new( + left_source: String, + right_source: String, + dimensions: Vec>, + null_check: bool, + ) -> Self { + Self { + left_source, + right_source, + dimensions, + null_check, + } } - pub fn new_from_select(plan: Rc, alias: String) -> Self { Self { query: Rc::new(QueryPlan::Select(select)), - alias, } } @@ -29,8 +28,8 @@ impl Subquery { &self.alias } - pub fn to_sql(&self) -> Result { - let sql = format!("({}) AS {}", self.query.to_sql()?, self.alias); + pub fn to_sql(&self, templates: &PlanSqlTemplates) -> Result { + let sql = format!("({})", self.query.to_sql(templates)?); Ok(sql) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs index 7648ece9a9247..2fb5f5615ecaf 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs @@ -1,4 +1,5 @@ -use super::QueryPlan; +use super::{QueryPlan, Schema}; +use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; pub struct Union { @@ -9,12 +10,19 @@ impl Union { pub fn new(union: Vec) -> Self { Self { union } } + pub fn make_schema(&self, self_alias: Option) -> Schema { + if self.union.is_empty() { + Schema::empty() + } else { + self.union[0].make_schema(self_alias) + } + } - pub fn to_sql(&self) -> Result { + pub fn to_sql(&self, templates: &PlanSqlTemplates) -> Result { let res = self .union .iter() - .map(|q| q.to_sql()) + .map(|q| q.to_sql(templates)) .collect::, _>>()? .join(" UNION ALL "); Ok(res) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs index 336aa99e9ee2f..8bfb22bfa5d14 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs @@ -1,8 +1,10 @@ use super::query_tools::QueryTools; use super::sql_evaluator::EvaluationNode; use super::{evaluate_with_context, VisitorContext}; +use crate::plan::Schema; use cubenativeutils::CubeError; use std::rc::Rc; + pub struct BaseCube { cube_name: String, member_evaluator: Rc, @@ -22,18 +24,29 @@ impl BaseCube { } pub fn to_sql(&self, context: Rc) -> Result { - let cube_sql = self.table_sql(context.clone())?; - let cube_alias = self.query_tools.escape_column_name( - &self - .query_tools - .cube_alias_name(&self.cube_name, context.cube_alias_prefix()), - ); - let as_syntax_join = "AS"; //FIXME should be from JS BaseQuery + let cube_sql = evaluate_with_context( + &self.member_evaluator, + self.query_tools.clone(), + context, + Rc::new(Schema::empty()), + )?; + Ok(cube_sql) + } + + pub fn name(&self) -> &String { + &self.cube_name + } - Ok(format!("{} {} {}", cube_sql, as_syntax_join, cube_alias)) + pub fn default_alias(&self) -> String { + self.query_tools.alias_name(&self.cube_name) } - pub fn table_sql(&self, context: Rc) -> Result { - evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context) + pub fn default_alias_with_prefix(&self, prefix: &Option) -> String { + let alias = self.default_alias(); + if let Some(prefix) = prefix { + format!("{prefix}_{alias}") + } else { + alias + } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs index 411d28a951960..be725704d084d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs @@ -1,6 +1,7 @@ use super::query_tools::QueryTools; -use super::sql_evaluator::{EvaluationNode, MemberSymbolType}; +use super::sql_evaluator::{EvaluationNode, MemberSymbol, MemberSymbolType}; use super::{evaluate_with_context, BaseMember, VisitorContext}; +use crate::plan::Schema; use cubenativeutils::CubeError; use std::rc::Rc; @@ -8,13 +9,18 @@ pub struct BaseDimension { dimension: String, query_tools: Rc, member_evaluator: Rc, + cube_name: String, + name: String, } impl BaseMember for BaseDimension { - fn to_sql(&self, context: Rc) -> Result { - let alias_name = self.alias_name(); - - Ok(format!("{} {}", self.dimension_sql(context)?, alias_name)) + fn to_sql(&self, context: Rc, schema: Rc) -> Result { + evaluate_with_context( + &self.member_evaluator, + self.query_tools.clone(), + context, + schema, + ) } fn alias_name(&self) -> String { @@ -25,35 +31,48 @@ impl BaseMember for BaseDimension { 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( - dimension: String, - query_tools: Rc, - member_evaluator: Rc, - ) -> Result, CubeError> { - Ok(Rc::new(Self { - dimension, - query_tools, - member_evaluator, - })) - } - - pub fn try_new_from_precompiled( evaluation_node: Rc, query_tools: Rc, - ) -> Option> { - match evaluation_node.symbol() { + ) -> Result>, CubeError> { + let result = match evaluation_node.symbol() { MemberSymbolType::Dimension(s) => 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(), })), _ => 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" + ))) } } @@ -68,8 +87,4 @@ impl BaseDimension { pub fn unescaped_alias_name(&self) -> String { self.query_tools.alias_name(&self.dimension) } - - pub fn dimension_sql(&self, context: Rc) -> Result { - evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context) - } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs index f21d3d469ad2b..0765782bb13af 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs @@ -1,10 +1,11 @@ use super::query_tools::QueryTools; use super::sql_evaluator::EvaluationNode; -use super::{evaluate_with_context, BaseDimension, BaseMember, VisitorContext}; +use super::{evaluate_with_context, VisitorContext}; +use crate::plan::Schema; use cubenativeutils::CubeError; use std::rc::Rc; pub trait BaseJoinCondition { - fn to_sql(&self, context: Rc) -> Result; + fn to_sql(&self, context: Rc, schema: Rc) -> Result; } pub struct SqlJoinCondition { member_evaluator: Rc, @@ -23,86 +24,12 @@ impl SqlJoinCondition { } impl BaseJoinCondition for SqlJoinCondition { - fn to_sql(&self, context: Rc) -> Result { - evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context) - } -} - -pub struct PrimaryJoinCondition { - query_tools: Rc, - dimensions: Vec>, -} - -impl PrimaryJoinCondition { - pub fn try_new( - query_tools: Rc, - dimensions: Vec>, - ) -> Result, CubeError> { - Ok(Rc::new(Self { - query_tools, - dimensions, - })) - } -} - -impl BaseJoinCondition for PrimaryJoinCondition { - fn to_sql(&self, context: Rc) -> Result { - let result = self - .dimensions - .iter() - .map(|dim| -> Result { - Ok(format!( - "{}.{} = {}", - self.query_tools.escape_column_name("keys"), - dim.alias_name(), - dim.dimension_sql(context.clone())? - )) - }) - .collect::, _>>()? - .join(" AND "); - Ok(result) - } -} - -pub struct DimensionJoinCondition { - left_alias: String, - right_alias: String, - dimensions: Rc>, -} - -impl DimensionJoinCondition { - pub fn try_new( - left_alias: String, - right_alias: String, - dimensions: Rc>, - ) -> Result, CubeError> { - Ok(Rc::new(Self { - left_alias, - right_alias, - dimensions, - })) - } -} - -impl BaseJoinCondition for DimensionJoinCondition { - fn to_sql(&self, _context: Rc) -> Result { - let res = if self.dimensions.is_empty() { - "1 = 1".to_string() - } else { - self - .dimensions - .iter() - .map(|alias| { - format!( - "({left_alias}.{alias} = {right_alias}.{alias} OR ({left_alias}.{alias} IS NULL AND {right_alias}.{alias} IS NULL))", - left_alias = self.left_alias, - right_alias = self.right_alias, - alias = alias, - ) - }) - .collect::>() - .join(" AND ") - }; - Ok(res) + fn to_sql(&self, context: Rc, schema: Rc) -> Result { + evaluate_with_context( + &self.member_evaluator, + self.query_tools.clone(), + context, + schema, + ) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs index 44d63f200a8fa..936f107a1ab1f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs @@ -2,6 +2,7 @@ use super::query_tools::QueryTools; use super::sql_evaluator::{EvaluationNode, MemberSymbol, MemberSymbolType}; use super::{evaluate_with_context, BaseMember, VisitorContext}; use crate::cube_bridge::measure_definition::{MeasureDefinition, TimeShiftReference}; +use crate::plan::Schema; use cubenativeutils::CubeError; use lazy_static::lazy_static; use regex::Regex; @@ -66,14 +67,17 @@ pub struct BaseMeasure { definition: Rc, time_shifts: Vec, cube_name: String, + name: String, } impl BaseMember for BaseMeasure { - fn to_sql(&self, context: Rc) -> Result { - let sql = evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context)?; - let alias_name = self.alias_name(); - - Ok(format!("{} {}", sql, alias_name)) + fn to_sql(&self, context: Rc, schema: Rc) -> Result { + evaluate_with_context( + &self.member_evaluator, + self.query_tools.clone(), + context, + schema, + ) } fn alias_name(&self) -> String { @@ -88,40 +92,18 @@ impl BaseMember for BaseMeasure { fn as_base_member(self: Rc) -> Rc { self.clone() } + + fn cube_name(&self) -> &String { + &self.cube_name + } + + fn name(&self) -> &String { + &self.name + } } impl BaseMeasure { pub fn try_new( - measure: String, - query_tools: Rc, - member_evaluator: Rc, - ) -> Result, CubeError> { - let cube_name = query_tools - .cube_evaluator() - .cube_from_path(measure.clone())? - .static_data() - .name - .clone(); - let definition = match member_evaluator.symbol() { - MemberSymbolType::Measure(m) => Ok(m.definition().clone()), - _ => Err(CubeError::internal(format!( - "wrong type of member_evaluator for measure: {}", - measure - ))), - }?; - - let time_shifts = Self::parse_time_shifts(&definition)?; - Ok(Rc::new(Self { - measure, - query_tools, - definition, - member_evaluator, - cube_name, - time_shifts, - })) - } - - pub fn try_new_from_precompiled( evaluation_node: Rc, query_tools: Rc, ) -> Result>, CubeError> { @@ -134,6 +116,7 @@ impl BaseMeasure { member_evaluator: evaluation_node.clone(), definition: s.definition().clone(), cube_name: s.cube_name().clone(), + name: s.name().clone(), time_shifts, })) } @@ -142,6 +125,19 @@ impl BaseMeasure { 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" + ))) + } + } + fn parse_time_shifts( definition: &Rc, ) -> Result, CubeError> { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs index 6f67ef610e471..3c995c7fb52fb 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs @@ -1,18 +1,24 @@ use super::sql_evaluator::EvaluationNode; use super::VisitorContext; +use crate::plan::Schema; use cubenativeutils::CubeError; use itertools::Itertools; use std::collections::HashMap; use std::rc::Rc; pub trait BaseMember { - fn to_sql(&self, context: Rc) -> Result; + fn to_sql(&self, context: Rc, schema: Rc) -> Result; fn alias_name(&self) -> String; fn member_evaluator(&self) -> Rc; fn full_name(&self) -> String { self.member_evaluator().full_name() } fn as_base_member(self: Rc) -> Rc; + fn cube_name(&self) -> &String; + fn name(&self) -> &String; + fn alias_suffix(&self) -> Option { + None + } } pub struct BaseMemberHelper {} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs index 191fdcb7e61c2..fd1362ddad8ea 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_query.rs @@ -1,9 +1,13 @@ -use super::planners::FullKeyAggregateQueryPlanner; +use super::planners::{ + FullKeyAggregateQueryPlanner, MultiStageQueryPlanner, MultipliedMeasuresQueryPlanner, + SimpleQueryPlanner, +}; use super::query_tools::QueryTools; use super::QueryProperties; use crate::cube_bridge::base_query_options::BaseQueryOptions; use crate::plan::Select; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; +use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::wrappers::inner_types::InnerTypes; use cubenativeutils::wrappers::object::NativeArray; use cubenativeutils::wrappers::serializer::NativeSerialize; @@ -41,7 +45,9 @@ impl BaseQuery { pub fn build_sql_and_params(&self) -> Result, CubeError> { let plan = self.build_sql_and_params_impl()?; - let sql = plan.to_sql()?; + let templates = PlanSqlTemplates::new(self.query_tools.templates_render()); + + let sql = plan.to_sql(&templates)?; let (result_sql, params) = self.query_tools.build_sql_and_params(&sql, true)?; let res = self.context.empty_array(); @@ -53,11 +59,29 @@ impl BaseQuery { } fn build_sql_and_params_impl(&self) -> Result { - let full_key_aggregate_query_builder = FullKeyAggregateQueryPlanner::new( - self.query_tools.clone(), - self.request.clone(), - SqlNodesFactory::new(), - ); - full_key_aggregate_query_builder.plan() + if self.request.is_simple_query()? { + let planner = SimpleQueryPlanner::new( + self.query_tools.clone(), + self.request.clone(), + SqlNodesFactory::new(), + ); + planner.plan() + } else { + let multiplied_measures_query_planner = MultipliedMeasuresQueryPlanner::new( + self.query_tools.clone(), + self.request.clone(), + SqlNodesFactory::new(), + ); + let multi_stage_query_planner = + MultiStageQueryPlanner::new(self.query_tools.clone(), self.request.clone()); + let full_key_aggregate_planner = + FullKeyAggregateQueryPlanner::new(self.request.clone(), SqlNodesFactory::new()); + let mut subqueries = multiplied_measures_query_planner.plan_queries()?; + let (multi_stage_ctes, multi_stage_subqueries) = + multi_stage_query_planner.plan_queries()?; + subqueries.extend(multi_stage_subqueries.into_iter()); + let result = full_key_aggregate_planner.plan(subqueries, multi_stage_ctes)?; + Ok(result) + } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs index 3e6fe3296d916..38fa14ff5f7e7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs @@ -2,6 +2,7 @@ use super::query_tools::QueryTools; use super::sql_evaluator::EvaluationNode; use super::BaseDimension; use super::{BaseMember, VisitorContext}; +use crate::plan::Schema; use cubenativeutils::CubeError; use std::rc::Rc; @@ -13,21 +14,23 @@ pub struct BaseTimeDimension { } impl BaseMember for BaseTimeDimension { - fn to_sql(&self, context: Rc) -> Result { - let alias_name = self.alias_name(); - + fn to_sql( + &self, + context: Rc, + source_schema: Rc, + ) -> Result { let field_sql = if let Some(granularity) = &self.granularity { let converted_tz = self .query_tools .base_tools() - .convert_tz(self.dimension.dimension_sql(context)?)?; + .convert_tz(self.dimension.to_sql(context, source_schema)?)?; self.query_tools .base_tools() .time_grouped_column(granularity.clone(), converted_tz)? } else { unimplemented!("Time dimensions without granularity not supported yet") }; - Ok(format!("{} {}", field_sql, alias_name)) + Ok(field_sql) } fn alias_name(&self) -> String { @@ -42,18 +45,34 @@ impl BaseMember for BaseTimeDimension { 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 { + let granularity = if let Some(granularity) = &self.granularity { + granularity + } else { + "day" + }; + Some(granularity.to_string()) + } } impl BaseTimeDimension { - pub fn try_new( - dimension: String, + pub fn try_new_required( query_tools: Rc, member_evaluator: Rc, granularity: Option, date_range: Option>, ) -> Result, CubeError> { Ok(Rc::new(Self { - dimension: BaseDimension::try_new(dimension, query_tools.clone(), member_evaluator)?, + dimension: BaseDimension::try_new_required(member_evaluator, query_tools.clone())?, query_tools, granularity, date_range, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs index 2ade187f9ffcf..02247f982135e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs @@ -1,4 +1,5 @@ use super::filter_operator::FilterOperator; +use crate::plan::Schema; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::EvaluationNode; use crate::planner::sql_templates::filter::FilterTemplates; @@ -61,9 +62,17 @@ impl BaseFilter { self.member_evaluator.full_name() } - pub fn to_sql(&self, context: Rc) -> Result { - let member_sql = - evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context)?; + pub fn to_sql( + &self, + context: Rc, + schema: Rc, + ) -> Result { + let member_sql = evaluate_with_context( + &self.member_evaluator, + self.query_tools.clone(), + context, + schema, + )?; let res = match self.filter_operator { FilterOperator::Equal => self.equals_where(&member_sql)?, FilterOperator::NotEqual => self.not_equals_where(&member_sql)?, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs index 31b279fe68f12..a6def37da64c1 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/mod.rs @@ -17,11 +17,11 @@ pub mod visitor_context; pub use base_cube::BaseCube; pub use base_dimension::BaseDimension; -pub use base_join_condition::{BaseJoinCondition, PrimaryJoinCondition, SqlJoinCondition}; +pub use base_join_condition::{BaseJoinCondition, SqlJoinCondition}; pub use base_measure::BaseMeasure; pub use base_member::{BaseMember, BaseMemberHelper}; pub use base_query::BaseQuery; pub use base_time_dimension::BaseTimeDimension; pub use params_allocator::ParamsAllocator; -pub use query_properties::{OrderByItem, QueryProperties}; +pub use query_properties::{FullKeyAggregateMeasures, OrderByItem, QueryProperties}; pub use visitor_context::{evaluate_with_context, VisitorContext}; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs index 2ef1ba1d2b0cb..442700dca052d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/common_utils.rs @@ -1,5 +1,5 @@ use crate::planner::query_tools::QueryTools; -use crate::planner::{BaseCube, BaseDimension}; +use crate::planner::{BaseCube, BaseDimension, BaseMember}; use cubenativeutils::CubeError; use std::rc::Rc; @@ -23,7 +23,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 @@ -36,10 +36,11 @@ impl CommonUtils { let dims = primary_keys .iter() - .map(|d| { + .map(|d| -> Result<_, CubeError> { let full_name = format!("{}.{}", cube_name, d); let evaluator = evaluator_compiler.add_dimension_evaluator(full_name.clone())?; - BaseDimension::try_new(full_name, self.query_tools.clone(), evaluator) + let dim = BaseDimension::try_new_required(evaluator, self.query_tools.clone())?; + Ok(dim.as_base_member()) }) .collect::, _>>()?; Ok(dims) 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 7f382111ef5eb..d6bf872a608e0 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,82 +1,39 @@ -use super::{CommonUtils, JoinPlanner, MultiStageQueryPlanner, OrderPlanner, SimpleQueryPlanner}; -use crate::plan::{Filter, From, FromSource, Join, JoinItem, JoinSource, Select}; -use crate::planner::base_join_condition::DimensionJoinCondition; -use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::collectors::{ - collect_multiplied_measures, has_multi_stage_members, -}; +use super::OrderPlanner; +use crate::plan::{Cte, Filter, From, JoinBuilder, JoinCondition, Select, SelectBuilder}; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; -use crate::planner::BaseMember; +use crate::planner::BaseMemberHelper; use crate::planner::QueryProperties; -use crate::planner::{BaseDimension, BaseMeasure, PrimaryJoinCondition, VisitorContext}; +use crate::planner::{BaseMeasure, VisitorContext}; use cubenativeutils::CubeError; use itertools::Itertools; -use std::collections::HashMap; use std::rc::Rc; pub struct FullKeyAggregateQueryPlanner { - query_tools: Rc, query_properties: Rc, - join_planner: JoinPlanner, order_planner: OrderPlanner, - multi_stage_planner: MultiStageQueryPlanner, - common_utils: CommonUtils, context_factory: Rc, } impl FullKeyAggregateQueryPlanner { pub fn new( - query_tools: Rc, query_properties: Rc, context_factory: Rc, ) -> Self { Self { - join_planner: JoinPlanner::new(query_tools.clone()), order_planner: OrderPlanner::new(query_properties.clone()), - common_utils: CommonUtils::new(query_tools.clone()), - multi_stage_planner: MultiStageQueryPlanner::new( - query_tools.clone(), - query_properties.clone(), - ), - query_tools, query_properties, context_factory, } } - pub fn plan(self) -> Result { - let measures = self.full_key_aggregate_measures()?; - - if measures.is_simple_query() { - let simple_query_builder = SimpleQueryPlanner::new( - self.query_tools.clone(), - self.query_properties.clone(), - self.context_factory.clone(), - ); - return simple_query_builder.plan(); + pub fn plan(self, joins: Vec>, ctes: Vec>) -> Result { + if self.query_properties.is_simple_query()? { + return Err(CubeError::internal(format!( + "FullKeyAggregateQueryPlanner should not be used for simple query" + ))); } - let mut joins = Vec::new(); - - if !measures.regular_measures.is_empty() { - let regular_subquery = self.regular_measures_subquery(&measures.regular_measures)?; - joins.push(regular_subquery); - } - - for (cube_name, measures) in measures - .multiplied_measures - .clone() - .into_iter() - .into_group_map_by(|m| m.cube_name().clone()) - { - let aggregate_subquery = self.aggregate_subquery(&cube_name, &measures)?; - joins.push(aggregate_subquery); - } - - let (cte_queries, cte_aliases) = self.multi_stage_planner.get_cte_queries()?; - for alias in cte_aliases { - joins.push(self.multi_stage_planner.cte_select(&alias)); - } + let measures = self.query_properties.full_key_aggregate_measures()?; let inner_measures = measures .multiplied_measures @@ -91,56 +48,34 @@ impl FullKeyAggregateQueryPlanner { &self.query_properties.measures(), joins, )?; + if !ctes.is_empty() { + aggregate.set_ctes(ctes.clone()); + } - aggregate.ctes = cte_queries; - - Ok(aggregate) + Ok(aggregate.build()) } fn outer_measures_join_full_key_aggregate( &self, - inner_measures: &Vec>, + _inner_measures: &Vec>, outer_measures: &Vec>, joins: Vec>, - ) -> Result { - let root = JoinSource::new_from_select(joins[0].clone(), format!("q_0")); - let mut join_items = vec![]; - let dimensions_to_select = self - .query_properties - .dimensions_for_select() - .iter() - .map(|d| d.alias_name()) - .collect_vec(); - let dimensions_to_select = Rc::new(dimensions_to_select); + ) -> Result { + let mut join_builder = JoinBuilder::new_from_subselect(joins[0].clone(), format!("q_0")); + let dimensions_to_select = self.query_properties.dimensions_for_select(); for (i, join) in joins.iter().skip(1).enumerate() { let left_alias = format!("q_{}", i); let right_alias = format!("q_{}", i + 1); - let from = JoinSource::new_from_select( - join.clone(), - self.query_tools.escape_column_name(&format!("q_{}", i + 1)), + let on = JoinCondition::new_dimension_join( + left_alias, + right_alias, + dimensions_to_select.clone(), + true, ); - let join_item = JoinItem { - from, - on: DimensionJoinCondition::try_new( - left_alias, - right_alias, - dimensions_to_select.clone(), - )?, - is_inner: true, - }; - join_items.push(join_item); + join_builder.inner_join_subselect(join.clone(), format!("q_{}", i + 1), on); } - let references = inner_measures - .iter() - .map(|m| Ok((m.measure().clone(), m.alias_name()))) - .collect::, CubeError>>()?; - - let context = VisitorContext::new( - None, - self.context_factory - .with_render_references_default_node_processor(references), - ); + let context = VisitorContext::new(None, self.context_factory.default_node_processor()); let having = if self.query_properties.measures_filters().is_empty() { None @@ -150,157 +85,25 @@ impl FullKeyAggregateQueryPlanner { }) }; - let select = Select { - projection: self - .query_properties - .dimensions_references_and_measures("q_0", outer_measures)?, - from: From::new(FromSource::Join(Rc::new(Join { - root, - joins: join_items, - }))), - filter: None, - group_by: vec![], - having, - order_by: self.order_planner.default_order(), - context, - ctes: vec![], - is_distinct: false, - limit: self.query_properties.row_limit(), - offset: self.query_properties.offset(), - }; - Ok(select) - } - - fn full_key_aggregate_measures(&self) -> Result { - let mut result = FullKeyAggregateMeasures::default(); - for m in self.query_properties.measures().iter() { - if has_multi_stage_members(m.member_evaluator())? { - result.multi_stage_measures.push(m.clone()) - } else if let Some(multiple) = - collect_multiplied_measures(self.query_tools.clone(), m.member_evaluator())? - { - if multiple.multiplied { - result.multiplied_measures.push(m.clone()); - } else { - result.regular_measures.push(m.clone()); - } - } else { - result.regular_measures.push(m.clone()); - } - } - Ok(result) - } - - fn regular_measures_subquery( - &self, - measures: &Vec>, - ) -> Result, CubeError> { - let source = self.join_planner.make_join_node()?; - let select = Select { - projection: self - .query_properties - .select_all_dimensions_and_measures(measures)?, - from: source, - filter: self.query_properties.all_filters(), - group_by: self.query_properties.group_by(), - having: None, - order_by: vec![], - ctes: vec![], - context: VisitorContext::new_with_cube_alias_prefix( - self.context_factory.clone(), - "main".to_string(), - ), - is_distinct: false, - limit: None, - offset: None, - }; - Ok(Rc::new(select)) - } - - fn aggregate_subquery( - &self, - key_cube_name: &String, - measures: &Vec>, - ) -> Result, CubeError> { - let primary_keys_dimensions = self.common_utils.primary_keys_dimensions(key_cube_name)?; - let keys_query = self.key_query(&primary_keys_dimensions, key_cube_name)?; - - let pk_cube = - JoinSource::new_from_cube(self.common_utils.cube_from_path(key_cube_name.clone())?); - let mut joins = vec![]; - joins.push(JoinItem { - from: pk_cube, - on: PrimaryJoinCondition::try_new(self.query_tools.clone(), primary_keys_dimensions)?, - is_inner: false, - }); - let join = Rc::new(Join { - root: JoinSource::new_from_select( - keys_query, - self.query_tools.escape_column_name("keys"), - ), //FIXME replace with constant - joins, - }); - let select = Select { - projection: self.query_properties.dimensions_references_and_measures( - &self.query_tools.escape_column_name("keys"), - &measures, - )?, - from: From::new(FromSource::Join(join)), - filter: None, - group_by: self.query_properties.group_by(), - having: None, - order_by: vec![], - ctes: vec![], - context: VisitorContext::new_with_cube_alias_prefix( - self.context_factory.clone(), - format!("{}_key", key_cube_name), - ), - is_distinct: false, - limit: None, - offset: None, - }; - Ok(Rc::new(select)) - } + let from = From::new_from_join(join_builder.build()); + let mut select_builder = SelectBuilder::new(from, context); - fn key_query( - &self, - dimensions: &Vec>, - key_cube_name: &String, - ) -> Result, CubeError> { - let source = self.join_planner.make_join_node()?; - let dimensions = self + for member in self .query_properties - .dimensions_for_select_append(dimensions); - - let select = Select { - projection: self.query_properties.columns_to_expr(&dimensions), - from: source, - filter: self.query_properties.all_filters(), - group_by: vec![], - having: None, - order_by: vec![], - ctes: vec![], - context: VisitorContext::new_with_cube_alias_prefix( - self.context_factory.clone(), - format!("{}_key", key_cube_name), - ), - is_distinct: true, - limit: None, - offset: None, - }; - Ok(Rc::new(select)) - } -} + .all_dimensions_and_measures(&vec![])? + .iter() + { + select_builder.add_projection_member(member, Some(format!("q_0")), None); + } -#[derive(Default)] -struct FullKeyAggregateMeasures { - pub multiplied_measures: Vec>, - pub multi_stage_measures: Vec>, - pub regular_measures: Vec>, -} + for member in BaseMemberHelper::iter_as_base_member(&outer_measures) { + select_builder.add_projection_member(&member, None, None); + } -impl FullKeyAggregateMeasures { - pub fn is_simple_query(&self) -> bool { - self.multi_stage_measures.is_empty() && self.multiplied_measures.is_empty() + select_builder.set_order_by(self.order_planner.default_order()); + select_builder.set_having(having); + select_builder.set_limit(self.query_properties.row_limit()); + select_builder.set_offset(self.query_properties.offset()); + Ok(select_builder) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs index 2fc63b38ec2a3..7d4aa92edf638 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs @@ -1,6 +1,6 @@ use super::CommonUtils; use crate::cube_bridge::memeber_sql::MemberSql; -use crate::plan::{From, FromSource, Join, JoinItem, JoinSource}; +use crate::plan::{From, JoinBuilder, JoinCondition}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::EvaluationNode; use crate::planner::SqlJoinCondition; @@ -20,40 +20,46 @@ impl JoinPlanner { } } - pub fn make_join_node(&self, /*TODO dimensions for subqueries*/) -> Result { + pub fn make_join_node_with_prefix( + &self, + alias_prefix: &Option, /*TODO dimensions for subqueries*/ + ) -> Result { let join = self.query_tools.cached_data().join()?.clone(); let root = self.utils.cube_from_path(join.static_data().root.clone())?; let joins = join.joins()?; if joins.items().is_empty() { - Ok(From::new_from_cube(root)) + Ok(From::new_from_cube(root, None)) } else { - let join_items = joins - .items() - .iter() - .map(|join| { - let definition = join.join()?; - let evaluator = self.compile_join_condition( - &join.static_data().original_from, - definition.sql()?, - )?; - Ok(JoinItem { - from: JoinSource::new_from_cube( - self.utils - .cube_from_path(join.static_data().original_to.clone())?, - ), - on: SqlJoinCondition::try_new(self.query_tools.clone(), evaluator)?, - is_inner: false, - }) - }) - .collect::, CubeError>>()?; - let result = From::new(FromSource::Join(Rc::new(Join { - root: JoinSource::new_from_cube(root), - joins: join_items, - }))); + let mut join_builder = JoinBuilder::new_from_cube( + root.clone(), + Some(root.default_alias_with_prefix(alias_prefix)), + ); + for join in joins.items().iter() { + let definition = join.join()?; + let evaluator = self + .compile_join_condition(&join.static_data().original_from, definition.sql()?)?; + let on = JoinCondition::new_base_join(SqlJoinCondition::try_new( + self.query_tools.clone(), + evaluator, + )?); + let cube = self + .utils + .cube_from_path(join.static_data().original_to.clone())?; + join_builder.left_join_cube( + cube.clone(), + Some(cube.default_alias_with_prefix(alias_prefix)), + on, + ); + } + let result = From::new_from_join(join_builder.build()); Ok(result) } } + pub fn make_join_node(&self) -> Result { + self.make_join_node_with_prefix(&None) + } + fn compile_join_condition( &self, cube_name: &String, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/mod.rs index 0d022aa122249..8356778b08ac6 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/mod.rs @@ -3,6 +3,7 @@ pub mod full_key_query_aggregate_planner; pub mod join_planner; pub mod multi_stage; pub mod multi_stage_query_planner; +pub mod multiplied_measures_query_planner; pub mod order_planner; pub mod simple_query_planer; @@ -10,5 +11,6 @@ pub use common_utils::CommonUtils; pub use full_key_query_aggregate_planner::FullKeyAggregateQueryPlanner; pub use join_planner::JoinPlanner; pub use multi_stage_query_planner::MultiStageQueryPlanner; +pub use multiplied_measures_query_planner::MultipliedMeasuresQueryPlanner; pub use order_planner::OrderPlanner; pub use simple_query_planer::SimpleQueryPlanner; 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 e01ecf266e620..1ba7e8943dd89 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 @@ -1,17 +1,18 @@ use super::MultiStageQueryDescription; use crate::plan::{ - Expr, FilterGroup, FilterItem, From, FromSource, Join, JoinItem, JoinSource, OrderBy, - QueryPlan, Select, Subquery, + Cte, Expr, FilterGroup, FilterItem, From, JoinBuilder, JoinCondition, MemberExpression, + OrderBy, QueryPlan, Schema, SelectBuilder, +}; +use crate::planner::planners::{ + FullKeyAggregateQueryPlanner, MultipliedMeasuresQueryPlanner, OrderPlanner, SimpleQueryPlanner, }; -use crate::planner::base_join_condition::DimensionJoinCondition; -use crate::planner::planners::{FullKeyAggregateQueryPlanner, OrderPlanner}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; use crate::planner::QueryProperties; use crate::planner::{BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, VisitorContext}; use cubenativeutils::CubeError; use itertools::Itertools; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; pub struct MultiStageMemberQueryPlanner { query_tools: Rc, @@ -32,34 +33,38 @@ impl MultiStageMemberQueryPlanner { } } - pub fn plan_query(&self) -> Result, CubeError> { + pub fn plan_query( + &self, + cte_schemas: &HashMap>, + ) -> Result, CubeError> { if self.description.is_leaf() { self.plan_for_leaf_cte_query() } else { - self.plan_for_cte_query() + self.plan_for_cte_query(cte_schemas) } } - fn plan_for_cte_query(&self) -> Result, CubeError> { + fn plan_for_cte_query( + &self, + cte_schemas: &HashMap>, + ) -> Result, CubeError> { let query_member = self.query_member_as_measure()?.unwrap(); let dimensions = self.all_dimensions(); let dimensions_aliases = BaseMemberHelper::to_alias_vec(&dimensions); let from = From::new_from_subquery( - Rc::new(self.make_input_join()?), + Rc::new(self.make_input_join(cte_schemas)?), format!("{}_join", self.description.alias()), ); - let mut projection = dimensions_aliases - .iter() - .map(|d| Expr::Reference(None, d.clone())) - .collect_vec(); - let group_by = if query_member.is_multi_stage_ungroupped() { vec![] } else { - projection.clone() + dimensions + .iter() + .map(|dim| Expr::Member(MemberExpression::new(dim.clone(), None))) + .collect_vec() }; let order_by = if query_member.is_multi_stage_ungroupped() { @@ -68,105 +73,86 @@ impl MultiStageMemberQueryPlanner { self.query_order()? }; - projection.push(Expr::Field(query_member.clone())); - - let references = BaseMemberHelper::to_reference_map(&self.all_input_members()?); - let partition_by = self.member_partition_by(query_member.reduce_by(), query_member.group_by()); let context_factory = SqlNodesFactory::new(); let node_context = if query_member.measure_type() == "rank" { - context_factory.multi_stage_rank_node_processor(partition_by, references) + context_factory.multi_stage_rank_node_processor(partition_by) } else if !query_member.is_calculated() && partition_by != dimensions_aliases { - context_factory.multi_stage_window_node_processor(partition_by, references) + context_factory.multi_stage_window_node_processor(partition_by) } else { - context_factory.with_render_references_default_node_processor(references) + context_factory.default_node_processor() }; - let select = Select { - projection, - from, - filter: None, - group_by, - having: None, - order_by, - context: VisitorContext::new(None, node_context), - ctes: vec![], - is_distinct: false, - limit: None, - offset: None, - }; + let mut select_builder = SelectBuilder::new(from, VisitorContext::new(None, node_context)); + for dim in dimensions.iter() { + select_builder.add_projection_member(&dim, None, None); + } + select_builder.add_projection_member(&query_member.as_base_member(), None, None); + select_builder.set_group_by(group_by); + select_builder.set_order_by(order_by); + let select = select_builder.build(); - Ok(Rc::new(Subquery::new_from_select( + Ok(Rc::new(Cte::new_from_select( Rc::new(select), self.description.alias().clone(), ))) } - fn make_input_join(&self) -> Result { + fn make_input_join( + &self, + cte_schemas: &HashMap>, + ) -> Result { let inputs = self.input_cte_aliases(); - let dimensions_aliases = BaseMemberHelper::to_alias_vec(&self.all_input_dimensions()); - let measures_aliases = BaseMemberHelper::to_alias_vec(&self.input_measures()?); + let dimensions = self.all_input_dimensions(); let root_alias = format!("q_0"); - let root = JoinSource::new_from_reference(inputs[0].clone(), root_alias.clone()); - let mut join_items = vec![]; - let dimensions_aliases = Rc::new(dimensions_aliases.clone()); + let cte_schema = cte_schemas.get(&inputs[0]).unwrap().clone(); + let mut join_builder = JoinBuilder::new_from_table_reference( + inputs[0].clone(), + cte_schema, + Some(root_alias.clone()), + ); for (i, input) in inputs.iter().skip(1).enumerate() { let left_alias = format!("q_{}", i); let right_alias = format!("q_{}", i + 1); - let from = JoinSource::new_from_reference( + let on = JoinCondition::new_dimension_join( + left_alias, + right_alias, + dimensions.clone(), + true, + ); + let cte_schema = cte_schemas.get(input).unwrap().clone(); + join_builder.inner_join_table_reference( input.clone(), - self.query_tools.escape_column_name(&format!("q_{}", i + 1)), + cte_schema, + Some(format!("q_{}", i + 1)), + on, ); - let join_item = JoinItem { - from, - on: DimensionJoinCondition::try_new( - left_alias, - right_alias, - dimensions_aliases.clone(), - )?, - is_inner: true, - }; - join_items.push(join_item); } - let projection = dimensions_aliases - .iter() - .map(|d| Expr::Reference(Some(root_alias.clone()), d.clone())) - .chain( - measures_aliases - .iter() - .map(|m| Expr::Reference(None, m.clone())), - ) - .collect_vec(); + let from = From::new_from_join(join_builder.build()); + let mut select_builder = + SelectBuilder::new(from, VisitorContext::default(SqlNodesFactory::new())); - let select = Select { - projection, - from: From::new(FromSource::Join(Rc::new(Join { - root, - joins: join_items, - }))), - filter: None, - group_by: vec![], - having: None, - order_by: self.subquery_order()?, - context: VisitorContext::default(SqlNodesFactory::new()), - ctes: vec![], - is_distinct: false, - limit: None, - offset: None, - }; - Ok(QueryPlan::Select(Rc::new(select))) + for dim in dimensions.iter() { + select_builder.add_projection_member(dim, None, None) + } + for meas in self.input_measures()?.iter() { + select_builder.add_projection_member(meas, None, None) + } + select_builder.set_order_by(self.subquery_order()?); + + Ok(QueryPlan::Select(Rc::new(select_builder.build()))) } - fn plan_for_leaf_cte_query(&self) -> Result, CubeError> { + fn plan_for_leaf_cte_query(&self) -> Result, CubeError> { let member_node = self.description.member_node(); let measures = if let Some(measure) = - BaseMeasure::try_new_from_precompiled(member_node.clone(), self.query_tools.clone())? + BaseMeasure::try_new(member_node.clone(), self.query_tools.clone())? { vec![measure] } else { @@ -175,7 +161,8 @@ impl MultiStageMemberQueryPlanner { let allowed_filter_members = self.description.state().allowed_filter_members().clone(); - let cte_query_properties = QueryProperties::new_from_precompiled( + let cte_query_properties = QueryProperties::try_new_from_precompiled( + self.query_tools.clone(), measures, self.description.state().dimensions().clone(), self.query_properties.time_dimensions().clone(), @@ -194,7 +181,7 @@ impl MultiStageMemberQueryPlanner { vec![], None, None, - ); + )?; let node_factory = if self.description.state().time_shifts().is_empty() { SqlNodesFactory::new() @@ -202,14 +189,37 @@ impl MultiStageMemberQueryPlanner { SqlNodesFactory::new_with_time_shifts(self.description.state().time_shifts().clone()) }; - let full_key_aggregate_query_builder = FullKeyAggregateQueryPlanner::new( - self.query_tools.clone(), - cte_query_properties.clone(), - node_factory, - ); - let cte_select = full_key_aggregate_query_builder.plan()?; - let result = - Subquery::new_from_select(Rc::new(cte_select), self.description.alias().clone()); + if cte_query_properties + .full_key_aggregate_measures()? + .has_multi_stage_measures() + { + return Err(CubeError::internal(format!( + "Leaf multi stage query cannot contain multi stage member" + ))); + } + + let cte_select = if cte_query_properties.is_simple_query()? { + let planner = SimpleQueryPlanner::new( + self.query_tools.clone(), + cte_query_properties.clone(), + node_factory.clone(), + ); + planner.plan()? + } else { + let multiplied_measures_query_planner = MultipliedMeasuresQueryPlanner::new( + self.query_tools.clone(), + cte_query_properties.clone(), + node_factory.clone(), + ); + let full_key_aggregate_planner = FullKeyAggregateQueryPlanner::new( + cte_query_properties.clone(), + node_factory.clone(), + ); + let subqueries = multiplied_measures_query_planner.plan_queries()?; + let result = full_key_aggregate_planner.plan(subqueries, vec![])?; + result + }; + let result = Cte::new_from_select(Rc::new(cte_select), self.description.alias().clone()); Ok(Rc::new(result)) } @@ -268,12 +278,7 @@ impl MultiStageMemberQueryPlanner { .description .input() .iter() - .map(|m| { - BaseMeasure::try_new_from_precompiled( - m.member_node().clone(), - self.query_tools.clone(), - ) - }) + .map(|m| BaseMeasure::try_new(m.member_node().clone(), self.query_tools.clone())) .collect::, _>>()? .into_iter() .filter_map(|m| m) @@ -305,7 +310,7 @@ impl MultiStageMemberQueryPlanner { } fn query_member_as_measure(&self) -> Result>, CubeError> { - BaseMeasure::try_new_from_precompiled( + BaseMeasure::try_new( self.description.member_node().clone(), self.query_tools.clone(), ) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs index 158500731192d..fb7e4b6d00a6f 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage_query_planner.rs @@ -1,6 +1,6 @@ use super::multi_stage::MultiStageMemberQueryPlanner; use super::multi_stage::{MultiStageAppliedState, MultiStageQueryDescription}; -use crate::plan::{Expr, From, Select, Subquery}; +use crate::plan::{Cte, From, Schema, Select, SelectBuilder}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::collectors::has_multi_stage_members; use crate::planner::sql_evaluator::collectors::member_childs; @@ -9,6 +9,8 @@ use crate::planner::sql_evaluator::EvaluationNode; use crate::planner::QueryProperties; use crate::planner::{BaseDimension, BaseMeasure, VisitorContext}; use cubenativeutils::CubeError; +use itertools::Itertools; +use std::collections::HashMap; use std::rc::Rc; pub struct MultiStageQueryPlanner { @@ -23,7 +25,7 @@ impl MultiStageQueryPlanner { query_properties, } } - pub fn get_cte_queries(&self) -> Result<(Vec>, Vec), CubeError> { + pub fn plan_queries(&self) -> Result<(Vec>, Vec>), CubeError> { let multi_stage_members = self .query_properties .all_members(false) @@ -60,34 +62,41 @@ impl MultiStageQueryPlanner { }) .collect::, _>>()?; + let mut cte_schemas = HashMap::new(); let all_queries = descriptions .into_iter() - .map(|descr| { - MultiStageMemberQueryPlanner::new( + .map(|descr| -> Result<_, CubeError> { + let res = MultiStageMemberQueryPlanner::new( self.query_tools.clone(), self.query_properties.clone(), descr.clone(), ) - .plan_query() + .plan_query(&cte_schemas)?; + cte_schemas.insert(descr.alias().clone(), Rc::new(res.make_schema())); + Ok(res) }) .collect::, _>>()?; - Ok((all_queries, top_level_ctes)) + + let cte_joins = top_level_ctes + .iter() + .map(|alias| self.cte_select(alias, &cte_schemas)) + .collect_vec(); + + Ok((all_queries, cte_joins)) } - pub fn cte_select(&self, alias: &String) -> Rc { + let schema = cte_schemas.get(alias).unwrap().clone(); + let select_builder = SelectBuilder::new( + From::new_from_table_reference(alias.clone(), schema, None), + VisitorContext::default(SqlNodesFactory::new()), + ); + + Rc::new(select_builder.build()) } fn make_queries_descriptions( @@ -105,7 +114,7 @@ impl MultiStageQueryPlanner { }; let (dimensions_to_add, time_shifts) = if let Some(measure) = - BaseMeasure::try_new_from_precompiled(member.clone(), self.query_tools.clone())? + BaseMeasure::try_new(member.clone(), self.query_tools.clone())? { let dimensions_to_add = if let Some(add_group_by) = measure.add_group_by() { add_group_by @@ -160,6 +169,6 @@ impl MultiStageQueryPlanner { let evaluator_compiler_cell = self.query_tools.evaluator_compiler().clone(); let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); let evaluator = evaluator_compiler.add_dimension_evaluator(name.clone())?; - BaseDimension::try_new(name.clone(), self.query_tools.clone(), evaluator) + BaseDimension::try_new_required(evaluator, self.query_tools.clone()) } } 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 new file mode 100644 index 0000000000000..6ac49c628cea6 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multiplied_measures_query_planner.rs @@ -0,0 +1,158 @@ +use super::{CommonUtils, JoinPlanner}; +use crate::plan::{From, JoinBuilder, JoinCondition, Select, SelectBuilder}; +use crate::planner::query_tools::QueryTools; +use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; +use crate::planner::BaseMember; +use crate::planner::QueryProperties; +use crate::planner::{BaseMeasure, VisitorContext}; +use cubenativeutils::CubeError; +use itertools::Itertools; +use std::rc::Rc; + +pub struct MultipliedMeasuresQueryPlanner { + query_properties: Rc, + join_planner: JoinPlanner, + common_utils: CommonUtils, + context_factory: Rc, +} + +impl MultipliedMeasuresQueryPlanner { + pub fn new( + query_tools: Rc, + query_properties: Rc, + context_factory: Rc, + ) -> Self { + Self { + join_planner: JoinPlanner::new(query_tools.clone()), + common_utils: CommonUtils::new(query_tools.clone()), + query_properties, + context_factory, + } + } + + pub fn plan_queries(&self) -> Result>, CubeError> { + if self.query_properties.is_simple_query()? { + return Err(CubeError::internal(format!( + "MultipliedMeasuresQueryPlanner should not be used for simple query" + ))); + } + + let measures = self.query_properties.full_key_aggregate_measures()?; + + let mut joins = Vec::new(); + + if !measures.regular_measures.is_empty() { + let regular_subquery = self.regular_measures_subquery(&measures.regular_measures)?; + joins.push(regular_subquery); + } + + for (cube_name, measures) in measures + .multiplied_measures + .clone() + .into_iter() + .into_group_map_by(|m| m.cube_name().clone()) + { + let aggregate_subquery = self.aggregate_subquery(&cube_name, &measures)?; + joins.push(aggregate_subquery); + } + Ok(joins) + } + + fn aggregate_subquery( + &self, + key_cube_name: &String, + measures: &Vec>, + ) -> Result, CubeError> { + let primary_keys_dimensions = self.common_utils.primary_keys_dimensions(key_cube_name)?; + let keys_query = self.key_query(&primary_keys_dimensions, key_cube_name)?; + let keys_query_alias = format!("keys"); + + let mut join_builder = + JoinBuilder::new_from_subselect(keys_query, keys_query_alias.clone()); + + let pk_cube = self.common_utils.cube_from_path(key_cube_name.clone())?; + let pk_cube_alias = + pk_cube.default_alias_with_prefix(&Some(format!("{key_cube_name}_key"))); + join_builder.left_join_cube( + pk_cube.clone(), + Some(pk_cube_alias.clone()), + JoinCondition::new_dimension_join( + keys_query_alias, + pk_cube_alias, + primary_keys_dimensions, + false, + ), + ); + + let mut select_builder = SelectBuilder::new( + From::new_from_join(join_builder.build()), + VisitorContext::new_with_cube_alias_prefix( + self.context_factory.clone(), + format!("{}_key", key_cube_name), + ), + ); + for member in self + .query_properties + .all_dimensions_and_measures(&measures)? + .iter() + { + select_builder.add_projection_member(member, None, None); + } + select_builder.set_group_by(self.query_properties.group_by()); + Ok(Rc::new(select_builder.build())) + } + + fn regular_measures_subquery( + &self, + measures: &Vec>, + ) -> Result, CubeError> { + let source = self + .join_planner + .make_join_node_with_prefix(&Some(format!("main")))?; + let mut select_builder = SelectBuilder::new( + source, + VisitorContext::new_with_cube_alias_prefix( + self.context_factory.clone(), + "main".to_string(), + ), + ); + for member in self + .query_properties + .all_dimensions_and_measures(&measures)? + .iter() + { + select_builder.add_projection_member(member, None, None); + } + select_builder.set_filter(self.query_properties.all_filters()); + select_builder.set_group_by(self.query_properties.group_by()); + Ok(Rc::new(select_builder.build())) + } + + fn key_query( + &self, + dimensions: &Vec>, + key_cube_name: &String, + ) -> Result, CubeError> { + let source = self + .join_planner + .make_join_node_with_prefix(&Some(format!("{}_key", key_cube_name)))?; + let dimensions = self + .query_properties + .dimensions_for_select_append(dimensions); + + let mut select_builder = SelectBuilder::new( + source, + VisitorContext::new_with_cube_alias_prefix( + self.context_factory.clone(), + format!("{}_key", key_cube_name), + ), + ); + for member in dimensions.iter() { + select_builder.add_projection_member(&member, None, None); + } + select_builder.set_distinct(); + select_builder.set_filter(self.query_properties.all_filters()); + + Ok(Rc::new(select_builder.build())) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs index 50a16a10877ef..ada3ec3df38f9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/order_planner.rs @@ -1,4 +1,4 @@ -use crate::plan::{Expr, OrderBy}; +use crate::plan::{Expr, MemberExpression, OrderBy}; use crate::planner::{BaseMember, OrderByItem, QueryProperties}; use std::rc::Rc; @@ -30,7 +30,7 @@ impl OrderPlanner { .find(|(_, m)| m.full_name().to_lowercase() == itm.name().to_lowercase()) { result.push(OrderBy::new( - Expr::Field(member.clone()), + Expr::Member(MemberExpression::new(member.clone(), None)), pos + 1, itm.desc(), )); 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 ad2e17ac1ce13..981bd6ad4dcfa 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs @@ -1,5 +1,5 @@ use super::{JoinPlanner, OrderPlanner}; -use crate::plan::{Filter, Select}; +use crate::plan::{Filter, Select, SelectBuilder}; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; use crate::planner::QueryProperties; @@ -36,21 +36,24 @@ impl SimpleQueryPlanner { items: self.query_properties.measures_filters().clone(), }) }; - let select = Select { - projection: self - .query_properties - .select_all_dimensions_and_measures(self.query_properties.measures())?, - from: self.join_planner.make_join_node()?, - filter, - group_by: self.query_properties.group_by(), - having, - order_by: self.order_planner.default_order(), - context: VisitorContext::default(self.context_factory.clone()), - ctes: vec![], - is_distinct: false, - limit: self.query_properties.row_limit(), - offset: self.query_properties.offset(), - }; - Ok(select) + let mut select_builder = SelectBuilder::new( + self.join_planner.make_join_node()?, + VisitorContext::default(self.context_factory.clone()), + ); + for member in self + .query_properties + .all_dimensions_and_measures(self.query_properties.measures())? + .iter() + { + select_builder.add_projection_member(member, None, None); + } + select_builder.set_filter(filter); + select_builder.set_group_by(self.query_properties.group_by()); + select_builder.set_order_by(self.order_planner.default_order()); + select_builder.set_having(having); + select_builder.set_limit(self.query_properties.row_limit()); + select_builder.set_offset(self.query_properties.offset()); + let res = select_builder.build(); + Ok(res) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs index b55e34209d3ef..384ff238c12f2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs @@ -1,8 +1,12 @@ use super::filter::compiler::FilterCompiler; use super::query_tools::QueryTools; -use super::{BaseDimension, BaseMeasure, BaseMember, BaseTimeDimension}; +use super::{BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension}; use crate::cube_bridge::base_query_options::BaseQueryOptions; -use crate::plan::{Expr, Filter, FilterItem}; +use crate::plan::{Expr, Filter, FilterItem, MemberExpression}; +use crate::planner::sql_evaluator::collectors::{ + collect_multiplied_measures, has_multi_stage_members, +}; +use crate::planner::sql_evaluator::EvaluationNode; use cubenativeutils::CubeError; use itertools::Itertools; use std::collections::HashSet; @@ -28,6 +32,29 @@ impl OrderByItem { } } +enum SymbolAggregateType { + Regular, + Multiplied, + MultiStage, +} + +#[derive(Default, Clone)] +pub struct FullKeyAggregateMeasures { + pub multiplied_measures: Vec>, + pub regular_measures: Vec>, + pub multi_stage_measures: Vec>, +} + +impl FullKeyAggregateMeasures { + pub fn has_multiplied_measures(&self) -> bool { + !self.multiplied_measures.is_empty() + } + + pub fn has_multi_stage_measures(&self) -> bool { + !self.multi_stage_measures.is_empty() + } +} + #[derive(Clone)] pub struct QueryProperties { measures: Vec>, @@ -39,6 +66,7 @@ pub struct QueryProperties { order_by: Vec, row_limit: Option, offset: Option, + query_tools: Rc, } impl QueryProperties { @@ -54,7 +82,7 @@ impl QueryProperties { .iter() .map(|d| { let evaluator = evaluator_compiler.add_dimension_evaluator(d.clone())?; - BaseDimension::try_new(d.clone(), query_tools.clone(), evaluator) + BaseDimension::try_new_required(evaluator, query_tools.clone()) }) .collect::, _>>()? } else { @@ -68,8 +96,7 @@ impl QueryProperties { .map(|d| { let evaluator = evaluator_compiler.add_dimension_evaluator(d.dimension.clone())?; - BaseTimeDimension::try_new( - d.dimension.clone(), + BaseTimeDimension::try_new_required( query_tools.clone(), evaluator, d.granularity.clone(), @@ -86,7 +113,7 @@ impl QueryProperties { .iter() .map(|m| { let evaluator = evaluator_compiler.add_measure_evaluator(m.clone())?; - BaseMeasure::try_new(m.clone(), query_tools.clone(), evaluator) + BaseMeasure::try_new_required(evaluator, query_tools.clone()) }) .collect::, _>>()? } else { @@ -144,10 +171,12 @@ impl QueryProperties { order_by, row_limit, offset, + query_tools, })) } - pub fn new_from_precompiled( + pub fn try_new_from_precompiled( + query_tools: Rc, measures: Vec>, dimensions: Vec>, time_dimensions: Vec>, @@ -157,14 +186,14 @@ impl QueryProperties { order_by: Vec, row_limit: Option, offset: Option, - ) -> Rc { + ) -> Result, CubeError> { let order_by = if order_by.is_empty() { Self::default_order(&dimensions, &time_dimensions, &measures) } else { order_by }; - Rc::new(Self { + Ok(Rc::new(Self { measures, dimensions, time_dimensions, @@ -174,7 +203,8 @@ impl QueryProperties { order_by, row_limit, offset, - }) + query_tools, + })) } pub fn measures(&self) -> &Vec> { @@ -209,38 +239,10 @@ impl QueryProperties { self.offset } - pub fn set_measures(&mut self, measures: Vec>) { - self.measures = measures; - } - - pub fn set_dimensions(&mut self, dimensions: Vec>) { - self.dimensions = dimensions; - } - - pub fn set_time_dimensions(&mut self, time_dimensions: Vec>) { - self.time_dimensions = time_dimensions; - } - - pub fn set_time_dimensions_filters(&mut self, time_dimensions_filters: Vec) { - self.time_dimensions_filters = time_dimensions_filters - } - - pub fn set_dimensions_filters(&mut self, dimenstions_filters: Vec) { - self.dimensions_filters = dimenstions_filters - } - - pub fn set_measures_filters(&mut self, measures_filters: Vec) { - self.measures_filters = measures_filters - } - pub fn order_by(&self) -> &Vec { &self.order_by } - pub fn set_order_by(&mut self, order_by: Vec) { - self.order_by = order_by; - } - pub fn set_order_by_to_default(&mut self) { self.order_by = Self::default_order(&self.dimensions, &self.time_dimensions, &self.measures); @@ -260,29 +262,15 @@ impl QueryProperties { } } - pub fn select_all_dimensions_and_measures( - &self, - measures: &Vec>, - ) -> Result, CubeError> { - let measures = measures.iter().map(|m| Expr::Field(m.clone())); - let time_dimensions = self.time_dimensions.iter().map(|d| Expr::Field(d.clone())); - let dimensions = self.dimensions.iter().map(|d| Expr::Field(d.clone())); - Ok(dimensions.chain(time_dimensions).chain(measures).collect()) - } - - pub fn dimensions_references_and_measures( + pub fn all_dimensions_and_measures( &self, - cube_name: &str, measures: &Vec>, - ) -> Result, CubeError> { - let dimensions_refs = self - .dimensions_for_select() - .into_iter() - .map(|d| Ok(Expr::Reference(Some(cube_name.to_string()), d.alias_name()))); - let measures = measures.iter().map(|m| Ok(Expr::Field(m.clone()))); - dimensions_refs - .chain(measures) - .collect::, _>>() + ) -> 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> { @@ -299,27 +287,17 @@ impl QueryProperties { pub fn dimensions_for_select_append( &self, - append: &Vec>, + append: &Vec>, ) -> Vec> { - let time_dimensions = self - .time_dimensions - .iter() - .map(|d| -> Rc { d.clone() }); - let append_dims = append.iter().map(|d| -> Rc { d.clone() }); - let dimensions = self - .dimensions - .iter() - .map(|d| -> Rc { d.clone() }); + 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 columns_to_expr(&self, columns: &Vec>) -> Vec { - columns.iter().map(|d| Expr::Field(d.clone())).collect_vec() - } - pub fn all_members(&self, exclude_time_dimensions: bool) -> Vec> { let dimensions = self .dimensions @@ -346,8 +324,12 @@ impl QueryProperties { pub fn group_by(&self) -> Vec { self.dimensions .iter() - .map(|f| Expr::Field(f.clone())) - .chain(self.time_dimensions.iter().map(|f| Expr::Field(f.clone()))) + .map(|f| Expr::Member(MemberExpression::new(f.clone(), None))) + .chain( + self.time_dimensions + .iter() + .map(|f| Expr::Member(MemberExpression::new(f.clone(), None))), + ) .collect() } @@ -366,6 +348,7 @@ impl QueryProperties { } result } + pub fn all_filtered_members(&self) -> HashSet { let mut result = HashSet::new(); for item in self.dimensions_filters().iter() { @@ -392,4 +375,48 @@ impl QueryProperties { } } } + + pub fn is_simple_query(&self) -> Result { + for member in self.all_members(false) { + match self.get_symbol_aggregate_type(&member.member_evaluator())? { + SymbolAggregateType::Regular => {} + _ => return Ok(false), + } + } + Ok(true) + } + + pub fn full_key_aggregate_measures(&self) -> Result { + let mut result = FullKeyAggregateMeasures::default(); + let measures = self.measures(); + for m in measures.iter() { + match self.get_symbol_aggregate_type(m.member_evaluator())? { + SymbolAggregateType::Regular => result.regular_measures.push(m.clone()), + SymbolAggregateType::Multiplied => result.multiplied_measures.push(m.clone()), + SymbolAggregateType::MultiStage => result.multi_stage_measures.push(m.clone()), + } + } + + Ok(result) + } + + fn get_symbol_aggregate_type( + &self, + symbol: &Rc, + ) -> Result { + let symbol_type = if has_multi_stage_members(symbol)? { + SymbolAggregateType::MultiStage + } else if let Some(multiple) = + collect_multiplied_measures(self.query_tools.clone(), symbol)? + { + if multiple.multiplied { + SymbolAggregateType::Multiplied + } else { + SymbolAggregateType::Regular + } + } else { + SymbolAggregateType::Regular + }; + Ok(symbol_type) + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs index a8cc51d490ef3..d20896c4bec23 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs @@ -118,19 +118,14 @@ impl QueryTools { } } - pub fn auto_prefix_with_cube_name( - &self, - cube_name: &str, - sql: &str, - prefix: &Option, - ) -> String { + pub fn auto_prefix_with_cube_name(&self, cube_name: &str, sql: &str) -> String { lazy_static! { static ref SINGLE_MEMBER_RE: Regex = Regex::new(r"^[_a-zA-Z][_a-zA-Z0-9]*$").unwrap(); } if SINGLE_MEMBER_RE.is_match(sql) { format!( "{}.{}", - self.escape_column_name(&self.cube_alias_name(&cube_name, prefix)), + self.escape_column_name(&self.cube_alias_name(&cube_name, &None)), sql ) } else { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs index 67878766521ef..97c9eac59b4e3 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs @@ -2,8 +2,7 @@ use super::collectors::JoinHintsCollector; use super::dependecy::DependenciesBuilder; use super::{ CubeNameSymbolFactory, CubeTableSymbolFactory, DimensionSymbolFactory, EvaluationNode, - JoinConditionSymbolFactory, MeasureSymbolFactory, MemberSymbolFactory, SimpleSqlSymbolFactory, - TraversalVisitor, + MeasureSymbolFactory, SimpleSqlSymbolFactory, SymbolFactory, TraversalVisitor, }; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::memeber_sql::MemberSql; @@ -23,7 +22,7 @@ impl Compiler { } } - pub fn add_evaluator( + pub fn add_evaluator( &mut self, full_name: &String, factory: T, @@ -98,7 +97,7 @@ impl Compiler { ) -> Result, CubeError> { self.add_evaluator_impl( &cube_name, - JoinConditionSymbolFactory::try_new(&cube_name, sql)?, + SimpleSqlSymbolFactory::try_new(&cube_name, sql)?, ) } @@ -121,10 +120,7 @@ impl Compiler { Ok(collector.extract_result()) } - fn exists_member( - &self, - full_name: &String, - ) -> Option> { + fn exists_member(&self, full_name: &String) -> Option> { if T::is_cachable() { let key = (T::symbol_name(), full_name.clone()); self.members.get(&key).cloned() @@ -133,7 +129,7 @@ impl Compiler { } } - fn add_evaluator_impl( + fn add_evaluator_impl( &mut self, full_name: &String, factory: T, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/evaluation_node.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/evaluation_node.rs index 9756a1cf04a2d..02355626a2c28 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/evaluation_node.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/evaluation_node.rs @@ -1,7 +1,7 @@ use super::dependecy::Dependency; use super::{ - CubeNameSymbol, CubeTableSymbol, DimensionSymbol, JoinConditionSymbol, MeasureSymbol, - MemberSymbolType, SimpleSqlSymbol, + CubeNameSymbol, CubeTableSymbol, DimensionSymbol, MeasureSymbol, MemberSymbolType, + SimpleSqlSymbol, }; use std::rc::Rc; @@ -43,13 +43,6 @@ impl EvaluationNode { }) } - pub fn new_join_condition(symbol: JoinConditionSymbol, deps: Vec) -> Rc { - Rc::new(Self { - symbol: MemberSymbolType::JoinCondition(symbol), - deps, - }) - } - pub fn new_simple_sql(symbol: SimpleSqlSymbol, deps: Vec) -> Rc { Rc::new(Self { symbol: MemberSymbolType::SimpleSql(symbol), diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs index 9ac38f5cf4737..40a62074f3962 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/mod.rs @@ -2,6 +2,7 @@ pub mod collectors; pub mod compiler; mod dependecy; pub mod evaluation_node; +pub mod sql_node_transformers; pub mod sql_nodes; pub mod sql_visitor; pub mod symbols; @@ -13,8 +14,7 @@ pub use evaluation_node::EvaluationNode; pub use sql_visitor::SqlEvaluatorVisitor; pub use symbols::{ CubeNameSymbol, CubeNameSymbolFactory, CubeTableSymbol, CubeTableSymbolFactory, - DimensionSymbol, DimensionSymbolFactory, JoinConditionSymbol, JoinConditionSymbolFactory, - MeasureSymbol, MeasureSymbolFactory, MemberSymbol, MemberSymbolFactory, MemberSymbolType, - SimpleSqlSymbol, SimpleSqlSymbolFactory, + DimensionSymbol, DimensionSymbolFactory, MeasureSymbol, MeasureSymbolFactory, MemberSymbol, + MemberSymbolType, SimpleSqlSymbol, SimpleSqlSymbolFactory, SymbolFactory, }; -pub use visitor::{EvaluatorVisitor, TraversalVisitor}; +pub use visitor::TraversalVisitor; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_node_transformers/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_node_transformers/mod.rs new file mode 100644 index 0000000000000..1c0aeef099360 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_node_transformers/mod.rs @@ -0,0 +1,3 @@ +pub mod set_schema; + +pub use set_schema::set_schema; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_node_transformers/set_schema.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_node_transformers/set_schema.rs new file mode 100644 index 0000000000000..d7e580edcac19 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_node_transformers/set_schema.rs @@ -0,0 +1,86 @@ +use crate::plan::schema::Schema; +use crate::planner::sql_evaluator::sql_nodes::final_measure::FinalMeasureSqlNode; +use crate::planner::sql_evaluator::sql_nodes::{ + AutoPrefixSqlNode, EvaluateSqlNode, MeasureFilterSqlNode, MultiStageRankNode, + MultiStageWindowNode, RenderReferencesSqlNode, RootSqlNode, SqlNode, TimeShiftSqlNode, +}; +use std::rc::Rc; + +pub fn set_schema(node_processors: Rc, schema: Rc) -> Rc { + set_schema_impl(node_processors, schema) +} + +pub fn set_schema_impl(sql_node: Rc, schema: Rc) -> Rc { + if let Some(auto_prefix) = sql_node + .clone() + .as_any() + .downcast_ref::() + { + let input = set_schema_impl(auto_prefix.input().clone(), schema.clone()); + AutoPrefixSqlNode::new_with_schema(input, schema) + } else if let Some(_) = sql_node.clone().as_any().downcast_ref::() { + sql_node + } else if let Some(final_measure) = sql_node + .clone() + .as_any() + .downcast_ref::() + { + let input = set_schema_impl(final_measure.input().clone(), schema.clone()); + FinalMeasureSqlNode::new(input) + } else if let Some(measure_filter) = sql_node + .clone() + .as_any() + .downcast_ref::() + { + let input = set_schema_impl(measure_filter.input().clone(), schema.clone()); + MeasureFilterSqlNode::new(input) + } else if let Some(multi_stage_rank) = sql_node + .clone() + .as_any() + .downcast_ref::() + { + let else_processor = + set_schema_impl(multi_stage_rank.else_processor().clone(), schema.clone()); + MultiStageRankNode::new(else_processor, multi_stage_rank.partition().clone()) + } else if let Some(multi_stage_window) = sql_node + .clone() + .as_any() + .downcast_ref::() + { + let input = set_schema_impl(multi_stage_window.input().clone(), schema.clone()); + let else_processor = + set_schema_impl(multi_stage_window.else_processor().clone(), schema.clone()); + MultiStageWindowNode::new( + input, + else_processor, + multi_stage_window.partition().clone(), + ) + } else if let Some(render_references) = sql_node + .clone() + .as_any() + .downcast_ref::() + { + let input = set_schema_impl(render_references.input().clone(), schema.clone()); + RenderReferencesSqlNode::new_with_schema(input, schema) + } else if let Some(root_node) = sql_node.clone().as_any().downcast_ref::() { + let dimension_processor = + set_schema_impl(root_node.dimension_processor().clone(), schema.clone()); + let measure_processor = + set_schema_impl(root_node.measure_processor().clone(), schema.clone()); + let cube_name_processor = + set_schema_impl(root_node.cube_name_processor().clone(), schema.clone()); + let default_processor = + set_schema_impl(root_node.default_processor().clone(), schema.clone()); + RootSqlNode::new( + dimension_processor, + measure_processor, + cube_name_processor, + default_processor, + ) + } else if let Some(time_shift) = sql_node.clone().as_any().downcast_ref::() { + let input = set_schema_impl(time_shift.input().clone(), schema.clone()); + TimeShiftSqlNode::new(time_shift.shifts().clone(), input) + } else { + unreachable!("Not all nodes are implemented"); + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/auto_prefix.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/auto_prefix.rs index 5d881ca2dd349..e0ce9d3093ab9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/auto_prefix.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/auto_prefix.rs @@ -1,17 +1,35 @@ use super::SqlNode; +use crate::plan::Schema; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbol, MemberSymbolType}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub struct AutoPrefixSqlNode { input: Rc, + schema: Rc, } impl AutoPrefixSqlNode { pub fn new(input: Rc) -> Rc { - Rc::new(Self { input }) + Rc::new(Self { + input, + schema: Rc::new(Schema::empty()), + }) + } + + pub fn new_with_schema(input: Rc, schema: Rc) -> Rc { + Rc::new(Self { input, schema }) + } + + pub fn input(&self) -> &Rc { + &self.input + } + + pub fn schema(&self) -> &Rc { + &self.schema } } @@ -21,21 +39,33 @@ impl SqlNode for AutoPrefixSqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { - let prefix = visitor.cube_alias_prefix().clone(); - let input = self.input.to_sql(visitor, node, query_tools.clone())?; + let input = + self.input + .to_sql(visitor, node, query_tools.clone(), node_processor.clone())?; let res = match node.symbol() { MemberSymbolType::Dimension(ev) => { - query_tools.auto_prefix_with_cube_name(&ev.cube_name(), &input, &prefix) + let cube_alias = self.schema.resolve_cube_alias(&ev.cube_name()); + query_tools.auto_prefix_with_cube_name(&cube_alias, &input) } MemberSymbolType::Measure(ev) => { - query_tools.auto_prefix_with_cube_name(&ev.cube_name(), &input, &prefix) + let cube_alias = self.schema.resolve_cube_alias(&ev.cube_name()); + query_tools.auto_prefix_with_cube_name(&cube_alias, &input) } MemberSymbolType::CubeName(_) => { - query_tools.escape_column_name(&query_tools.cube_alias_name(&input, &prefix)) + let cube_alias = self.schema.resolve_cube_alias(&input); + query_tools.escape_column_name(&cube_alias) } _ => input, }; Ok(res) } + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.input.clone()] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/evaluate_sql.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/evaluate_sql.rs index 1c1ba35312ebc..0acd90c5f868d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/evaluate_sql.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/evaluate_sql.rs @@ -1,9 +1,9 @@ use super::SqlNode; use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::visitor::EvaluatorVisitor; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub struct EvaluateSqlNode {} @@ -20,15 +20,23 @@ impl SqlNode for EvaluateSqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, _query_tools: Rc, + node_processor: Rc, ) -> Result { - let args = visitor.evaluate_deps(node)?; + let args = visitor.evaluate_deps(node, node_processor.clone())?; match node.symbol() { MemberSymbolType::Dimension(ev) => ev.evaluate_sql(args), MemberSymbolType::Measure(ev) => ev.evaluate_sql(args), MemberSymbolType::CubeTable(ev) => ev.evaluate_sql(args), MemberSymbolType::CubeName(ev) => ev.evaluate_sql(args), - MemberSymbolType::JoinCondition(ev) => ev.evaluate_sql(args), MemberSymbolType::SimpleSql(ev) => ev.evaluate_sql(args), } } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs index 07946e51e3624..9d8729896e84d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/factory.rs @@ -24,27 +24,16 @@ impl SqlNodesFactory { let auto_prefix_processor = AutoPrefixSqlNode::new(evaluate_sql_processor.clone()); let measure_filter_processor = MeasureFilterSqlNode::new(auto_prefix_processor.clone()); let final_measure_processor = FinalMeasureSqlNode::new(measure_filter_processor.clone()); - RootSqlNode::new( + let root_node = RootSqlNode::new( self.dimension_processor(auto_prefix_processor.clone()), final_measure_processor.clone(), auto_prefix_processor.clone(), evaluate_sql_processor.clone(), - ) - } - - pub fn with_render_references_default_node_processor( - &self, - references: HashMap, - ) -> Rc { - let default_processor = self.default_node_processor(); - RenderReferencesSqlNode::new(references, default_processor) + ); + RenderReferencesSqlNode::new(root_node) } - pub fn multi_stage_rank_node_processor( - &self, - partition: Vec, - references: HashMap, - ) -> Rc { + pub fn multi_stage_rank_node_processor(&self, partition: Vec) -> Rc { let evaluate_sql_processor = EvaluateSqlNode::new(); let auto_prefix_processor = AutoPrefixSqlNode::new(evaluate_sql_processor.clone()); let measure_filter_processor = MeasureFilterSqlNode::new(auto_prefix_processor.clone()); @@ -58,15 +47,11 @@ impl SqlNodesFactory { auto_prefix_processor.clone(), evaluate_sql_processor.clone(), ); - let references_processor = RenderReferencesSqlNode::new(references, root_processor); + let references_processor = RenderReferencesSqlNode::new(root_processor); references_processor } - pub fn multi_stage_window_node_processor( - &self, - partition: Vec, - references: HashMap, - ) -> Rc { + pub fn multi_stage_window_node_processor(&self, partition: Vec) -> Rc { let evaluate_sql_processor = EvaluateSqlNode::new(); let auto_prefix_processor = AutoPrefixSqlNode::new(evaluate_sql_processor.clone()); let measure_filter_processor = MeasureFilterSqlNode::new(auto_prefix_processor.clone()); @@ -84,7 +69,7 @@ impl SqlNodesFactory { auto_prefix_processor.clone(), evaluate_sql_processor.clone(), ); - let references_processor = RenderReferencesSqlNode::new(references, root_processor); + let references_processor = RenderReferencesSqlNode::new(root_processor); references_processor } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/final_measure.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/final_measure.rs index 5815d56304851..2fd786a986f9e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/final_measure.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/final_measure.rs @@ -3,6 +3,7 @@ use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub struct FinalMeasureSqlNode { @@ -13,6 +14,10 @@ impl FinalMeasureSqlNode { pub fn new(input: Rc) -> Rc { Rc::new(Self { input }) } + + pub fn input(&self) -> &Rc { + &self.input + } } impl SqlNode for FinalMeasureSqlNode { @@ -21,10 +26,16 @@ impl SqlNode for FinalMeasureSqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { let res = match node.symbol() { MemberSymbolType::Measure(ev) => { - let input = self.input.to_sql(visitor, node, query_tools.clone())?; + let input = self.input.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )?; if ev.is_calculated() { input @@ -41,4 +52,12 @@ impl SqlNode for FinalMeasureSqlNode { }; Ok(res) } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.input.clone()] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/measure_filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/measure_filter.rs index b6a96c6265e58..52a6683190fea 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/measure_filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/measure_filter.rs @@ -1,9 +1,9 @@ use super::SqlNode; use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::visitor::EvaluatorVisitor; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub struct MeasureFilterSqlNode { @@ -14,6 +14,10 @@ impl MeasureFilterSqlNode { pub fn new(input: Rc) -> Rc { Rc::new(Self { input }) } + + pub fn input(&self) -> &Rc { + &self.input + } } impl SqlNode for MeasureFilterSqlNode { @@ -22,8 +26,11 @@ impl SqlNode for MeasureFilterSqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { - let input = self.input.to_sql(visitor, node, query_tools.clone())?; + let input = + self.input + .to_sql(visitor, node, query_tools.clone(), node_processor.clone())?; let res = match node.symbol() { MemberSymbolType::Measure(ev) => { let measure_filters = ev.measure_filters(); @@ -31,7 +38,10 @@ impl SqlNode for MeasureFilterSqlNode { let filters = measure_filters .iter() .map(|filter| -> Result { - Ok(format!("({})", visitor.apply(filter)?)) + Ok(format!( + "({})", + visitor.apply(filter, node_processor.clone())? + )) }) .collect::, _>>()? .join(" AND "); @@ -53,4 +63,12 @@ impl SqlNode for MeasureFilterSqlNode { }; Ok(res) } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.input.clone()] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_rank.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_rank.rs index aa270574473d6..79780132b46a5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_rank.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_rank.rs @@ -1,8 +1,9 @@ use super::SqlNode; use crate::planner::query_tools::QueryTools; +use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType}; -use crate::planner::sql_evaluator::{EvaluatorVisitor, SqlEvaluatorVisitor}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub struct MultiStageRankNode { @@ -17,6 +18,14 @@ impl MultiStageRankNode { partition, }) } + + pub fn else_processor(&self) -> &Rc { + &self.else_processor + } + + pub fn partition(&self) -> &Vec { + &self.partition + } } impl SqlNode for MultiStageRankNode { @@ -25,6 +34,7 @@ impl SqlNode for MultiStageRankNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { let res = match node.symbol() { MemberSymbolType::Measure(m) => { @@ -34,7 +44,8 @@ impl SqlNode for MultiStageRankNode { .measure_order_by() .iter() .map(|item| -> Result { - let sql = visitor.apply(item.evaluation_node())?; + let sql = visitor + .apply(item.evaluation_node(), node_processor.clone())?; Ok(format!("{} {}", sql, item.direction())) }) .collect::, _>>()? @@ -50,8 +61,12 @@ impl SqlNode for MultiStageRankNode { }; format!("rank() OVER ({partition_by}{order_by})") } else { - self.else_processor - .to_sql(visitor, node, query_tools.clone())? + self.else_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )? } } _ => { @@ -62,4 +77,12 @@ impl SqlNode for MultiStageRankNode { }; Ok(res) } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.else_processor.clone()] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_window.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_window.rs index d6676e6c907a7..5007ccc7a0b95 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_window.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/multi_stage_window.rs @@ -2,6 +2,7 @@ use super::SqlNode; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType, SqlEvaluatorVisitor}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub struct MultiStageWindowNode { @@ -22,6 +23,18 @@ impl MultiStageWindowNode { partition, }) } + + pub fn input(&self) -> &Rc { + &self.input + } + + pub fn else_processor(&self) -> &Rc { + &self.else_processor + } + + pub fn partition(&self) -> &Vec { + &self.partition + } } impl SqlNode for MultiStageWindowNode { @@ -30,11 +43,17 @@ impl SqlNode for MultiStageWindowNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { let res = match node.symbol() { MemberSymbolType::Measure(m) => { if m.is_multi_stage() && !m.is_calculated() { - let input_sql = self.input.to_sql(visitor, node, query_tools.clone())?; + let input_sql = self.input.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )?; let partition_by = if self.partition.is_empty() { "".to_string() @@ -44,8 +63,12 @@ impl SqlNode for MultiStageWindowNode { let measure_type = m.measure_type(); format!("{measure_type}({measure_type}({input_sql})) OVER ({partition_by})") } else { - self.else_processor - .to_sql(visitor, node, query_tools.clone())? + self.else_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )? } } _ => { @@ -56,4 +79,12 @@ impl SqlNode for MultiStageWindowNode { }; Ok(res) } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.input.clone(), self.else_processor.clone()] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/render_references.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/render_references.rs index edbbdd3906ccc..6f6a94ec01070 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/render_references.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/render_references.rs @@ -1,19 +1,31 @@ use super::SqlNode; +use crate::plan::Schema; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType}; use cubenativeutils::CubeError; -use std::collections::HashMap; +use std::any::Any; use std::rc::Rc; pub struct RenderReferencesSqlNode { - references: HashMap, input: Rc, + schema: Rc, } impl RenderReferencesSqlNode { - pub fn new(references: HashMap, input: Rc) -> Rc { - Rc::new(Self { references, input }) + pub fn new(input: Rc) -> Rc { + Rc::new(Self { + input, + schema: Rc::new(Schema::empty()), + }) + } + + pub fn new_with_schema(input: Rc, schema: Rc) -> Rc { + Rc::new(Self { input, schema }) + } + + pub fn input(&self) -> &Rc { + &self.input } } @@ -23,17 +35,39 @@ impl SqlNode for RenderReferencesSqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { - let reference = match node.symbol() { - MemberSymbolType::Dimension(ev) => self.references.get(&ev.full_name()).cloned(), - MemberSymbolType::Measure(ev) => self.references.get(&ev.full_name()).cloned(), + let reference_column = match node.symbol() { + MemberSymbolType::Dimension(ev) => { + self.schema.find_column_for_member(&ev.full_name(), &None) + } + MemberSymbolType::Measure(ev) => { + self.schema.find_column_for_member(&ev.full_name(), &None) + } _ => None, }; - if let Some(reference) = reference { - Ok(reference) + if let Some(reference_column) = reference_column { + let table_ref = reference_column.table_name.as_ref().map_or_else( + || format!(""), + |table_name| format!("{}.", query_tools.escape_column_name(table_name)), + ); + Ok(format!( + "{}{}", + table_ref, + query_tools.escape_column_name(&reference_column.alias) + )) } else { - self.input.to_sql(visitor, node, query_tools.clone()) + self.input + .to_sql(visitor, node, query_tools.clone(), node_processor.clone()) } } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.input.clone()] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/root_processor.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/root_processor.rs index 58326209f22c4..6788f7115000c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/root_processor.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/root_processor.rs @@ -3,6 +3,7 @@ use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub struct RootSqlNode { @@ -26,6 +27,22 @@ impl RootSqlNode { default_processor, }) } + + pub fn dimension_processor(&self) -> &Rc { + &self.dimension_processor + } + + pub fn measure_processor(&self) -> &Rc { + &self.measure_processor + } + + pub fn cube_name_processor(&self) -> &Rc { + &self.cube_name_processor + } + + pub fn default_processor(&self) -> &Rc { + &self.default_processor + } } impl SqlNode for RootSqlNode { @@ -34,24 +51,47 @@ impl SqlNode for RootSqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { let res = match node.symbol() { - MemberSymbolType::Dimension(_) => { - self.dimension_processor - .to_sql(visitor, node, query_tools.clone())? - } - MemberSymbolType::Measure(_) => { - self.measure_processor - .to_sql(visitor, node, query_tools.clone())? - } - MemberSymbolType::CubeName(_) => { - self.cube_name_processor - .to_sql(visitor, node, query_tools.clone())? - } - _ => self - .default_processor - .to_sql(visitor, node, query_tools.clone())?, + MemberSymbolType::Dimension(_) => self.dimension_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )?, + MemberSymbolType::Measure(_) => self.measure_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )?, + MemberSymbolType::CubeName(_) => self.cube_name_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )?, + _ => self.default_processor.to_sql( + visitor, + node, + query_tools.clone(), + node_processor.clone(), + )?, }; Ok(res) } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![ + self.dimension_processor.clone(), + self.measure_processor.clone(), + self.cube_name_processor.clone(), + self.default_processor.clone(), + ] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/sql_node.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/sql_node.rs index fec5693968436..1415f1d24bfc8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/sql_node.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/sql_node.rs @@ -1,6 +1,7 @@ use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::{EvaluationNode, SqlEvaluatorVisitor}; use cubenativeutils::CubeError; +use std::any::Any; use std::rc::Rc; pub trait SqlNode { @@ -9,5 +10,10 @@ pub trait SqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result; + + fn as_any(self: Rc) -> Rc; + + fn childs(&self) -> Vec>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/time_shift.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/time_shift.rs index 09db82623dd89..677095ded21ef 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/time_shift.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_nodes/time_shift.rs @@ -3,6 +3,7 @@ use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use crate::planner::sql_evaluator::{EvaluationNode, MemberSymbolType}; use cubenativeutils::CubeError; +use std::any::Any; use std::collections::HashMap; use std::rc::Rc; @@ -15,6 +16,14 @@ impl TimeShiftSqlNode { pub fn new(shifts: HashMap, input: Rc) -> Rc { Rc::new(Self { shifts, input }) } + + pub fn shifts(&self) -> &HashMap { + &self.shifts + } + + pub fn input(&self) -> &Rc { + &self.input + } } impl SqlNode for TimeShiftSqlNode { @@ -23,8 +32,11 @@ impl SqlNode for TimeShiftSqlNode { visitor: &mut SqlEvaluatorVisitor, node: &Rc, query_tools: Rc, + node_processor: Rc, ) -> Result { - let input = self.input.to_sql(visitor, node, query_tools.clone())?; + let input = + self.input + .to_sql(visitor, node, query_tools.clone(), node_processor.clone())?; let res = match node.symbol() { MemberSymbolType::Dimension(ev) => { if let Some(shift) = self.shifts.get(&ev.full_name()) { @@ -37,4 +49,12 @@ impl SqlNode for TimeShiftSqlNode { }; Ok(res) } + + fn as_any(self: Rc) -> Rc { + self.clone() + } + + fn childs(&self) -> Vec> { + vec![self.input.clone()] + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs index cfafa9d714edc..9c1a83574ea91 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_visitor.rs @@ -1,8 +1,7 @@ -use super::dependecy::ContextSymbolDep; +use super::dependecy::{ContextSymbolDep, Dependency}; use super::sql_nodes::SqlNode; -use super::visitor::EvaluatorVisitor; use super::EvaluationNode; -use crate::cube_bridge::memeber_sql::{ContextSymbolArg, MemberSqlArg}; +use crate::cube_bridge::memeber_sql::{ContextSymbolArg, MemberSqlArg, MemberSqlStruct}; use crate::planner::query_tools::QueryTools; use cubenativeutils::CubeError; use std::rc::Rc; @@ -10,37 +9,24 @@ use std::rc::Rc; #[derive(Clone)] pub struct SqlEvaluatorVisitor { query_tools: Rc, - cube_alias_prefix: Option, - node_processor: Rc, } impl SqlEvaluatorVisitor { - pub fn new( - query_tools: Rc, - cube_alias_prefix: Option, - node_processor: Rc, - ) -> Self { - Self { - query_tools, - cube_alias_prefix, - node_processor, - } + pub fn new(query_tools: Rc) -> Self { + Self { query_tools } } - pub fn cube_alias_prefix(&self) -> &Option { - &self.cube_alias_prefix - } -} - -impl EvaluatorVisitor for SqlEvaluatorVisitor { - fn apply(&mut self, node: &Rc) -> Result { - self.on_node_enter(node)?; - let node_processor = self.node_processor.clone(); - let result = node_processor.to_sql(self, node, self.query_tools.clone())?; + pub fn apply( + &mut self, + node: &Rc, + node_processor: Rc, + ) -> Result { + let result = + node_processor.to_sql(self, node, self.query_tools.clone(), node_processor.clone())?; Ok(result) } - fn apply_context_symbol( + pub fn apply_context_symbol( &mut self, context_symbol: &ContextSymbolDep, ) -> Result { @@ -61,4 +47,50 @@ impl EvaluatorVisitor for SqlEvaluatorVisitor { }; Ok(res) } + + pub fn evaluate_deps( + &mut self, + node: &Rc, + node_processor: Rc, + ) -> Result, CubeError> { + node.deps() + .iter() + .map(|d| self.evaluate_single_dep(&d, node_processor.clone())) + .collect() + } + + fn evaluate_single_dep( + &mut self, + dep: &Dependency, + node_processor: Rc, + ) -> Result { + match dep { + Dependency::SingleDependency(dep) => Ok(MemberSqlArg::String( + self.apply(dep, node_processor.clone())?, + )), + Dependency::StructDependency(dep) => { + let mut res = MemberSqlStruct::default(); + if let Some(sql_fn) = &dep.sql_fn { + res.sql_fn = Some(self.apply(sql_fn, node_processor.clone())?); + } + if let Some(to_string_fn) = &dep.to_string_fn { + res.to_string_fn = Some(self.apply(to_string_fn, node_processor.clone())?); + } + for (k, v) in dep.properties.iter() { + match v { + Dependency::SingleDependency(dep) => { + res.properties + .insert(k.clone(), self.apply(dep, node_processor.clone())?); + } + Dependency::StructDependency(_) => unimplemented!(), + Dependency::ContextDependency(_) => unimplemented!(), + } + } + Ok(MemberSqlArg::Struct(res)) + } + Dependency::ContextDependency(contex_symbol) => { + self.apply_context_symbol(contex_symbol) + } + } + } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/cube_symbol.rs index 4b0281ac7eb0e..d6a24c0cc56c2 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 @@ -1,11 +1,8 @@ -use super::{MemberSymbol, MemberSymbolFactory}; +use super::SymbolFactory; use crate::cube_bridge::cube_definition::CubeDefinition; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::memeber_sql::{MemberSql, MemberSqlArg}; -use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::{ - dependecy::Dependency, Compiler, EvaluationNode, SqlEvaluatorVisitor, -}; +use crate::planner::sql_evaluator::{dependecy::Dependency, Compiler, EvaluationNode}; use cubenativeutils::CubeError; use lazy_static::lazy_static; use regex::Regex; @@ -23,19 +20,7 @@ impl CubeNameSymbol { pub fn evaluate_sql(&self, _args: Vec) -> Result { Ok(self.cube_name.clone()) } - pub fn default_evaluate_sql( - &self, - visitor: &SqlEvaluatorVisitor, - tools: Rc, - ) -> Result { - Ok(tools.escape_column_name( - &tools.cube_alias_name(&self.cube_name, visitor.cube_alias_prefix()), - )) - } -} - -impl MemberSymbol for CubeNameSymbol { - fn cube_name(&self) -> &String { + pub fn cube_name(&self) -> &String { &self.cube_name } } @@ -56,7 +41,7 @@ impl CubeNameSymbolFactory { } } -impl MemberSymbolFactory for CubeNameSymbolFactory { +impl SymbolFactory for CubeNameSymbolFactory { fn symbol_name() -> String { "cube_name".to_string() } @@ -128,10 +113,7 @@ impl CubeTableSymbol { }; Ok(res) } -} - -impl MemberSymbol for CubeTableSymbol { - fn cube_name(&self) -> &String { + pub fn cube_name(&self) -> &String { &self.cube_name } } @@ -170,7 +152,7 @@ impl CubeTableSymbolFactory { } } -impl MemberSymbolFactory for CubeTableSymbolFactory { +impl SymbolFactory for CubeTableSymbolFactory { fn symbol_name() -> String { "cube_table".to_string() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs index c8e6340d9faf9..8286888a6a7f6 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/dimension_symbol.rs @@ -1,9 +1,8 @@ -use super::{MemberSymbol, MemberSymbolFactory}; +use super::{MemberSymbol, SymbolFactory}; use crate::cube_bridge::dimension_definition::DimensionDefinition; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::memeber_sql::{MemberSql, MemberSqlArg}; -use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::{Compiler, Dependency, EvaluationNode, SqlEvaluatorVisitor}; +use crate::planner::sql_evaluator::{Compiler, Dependency, EvaluationNode}; use cubenativeutils::CubeError; use std::rc::Rc; @@ -29,23 +28,11 @@ impl DimensionSymbol { definition, } } + pub fn evaluate_sql(&self, args: Vec) -> Result { let sql = self.member_sql.call(args)?; Ok(sql) } - pub fn default_evaluate_sql( - &self, - visitor: &SqlEvaluatorVisitor, - args: Vec, - tools: Rc, - ) -> Result { - let sql = tools.auto_prefix_with_cube_name( - &self.cube_name, - &self.member_sql.call(args)?, - visitor.cube_alias_prefix(), - ); - Ok(sql) - } pub fn full_name(&self) -> String { format!("{}.{}", self.cube_name, self.name) @@ -64,6 +51,10 @@ impl MemberSymbol for DimensionSymbol { fn cube_name(&self) -> &String { &self.cube_name } + + fn name(&self) -> &String { + &self.name + } } pub struct DimensionSymbolFactory { @@ -93,7 +84,7 @@ impl DimensionSymbolFactory { } } -impl MemberSymbolFactory for DimensionSymbolFactory { +impl SymbolFactory for DimensionSymbolFactory { fn symbol_name() -> String { "dimension".to_string() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/join_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/join_symbol.rs deleted file mode 100644 index b68bcbeeb803b..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/join_symbol.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::{MemberSymbol, MemberSymbolFactory}; -use crate::cube_bridge::memeber_sql::{MemberSql, MemberSqlArg}; -use crate::planner::sql_evaluator::{Compiler, Dependency, EvaluationNode}; -use cubenativeutils::CubeError; -use std::rc::Rc; - -pub struct JoinConditionSymbol { - cube_name: String, - member_sql: Rc, -} - -impl JoinConditionSymbol { - pub fn new(cube_name: String, member_sql: Rc) -> Self { - Self { - cube_name, - member_sql, - } - } - pub fn evaluate_sql(&self, args: Vec) -> Result { - self.member_sql.call(args) - } -} - -impl MemberSymbol for JoinConditionSymbol { - fn cube_name(&self) -> &String { - &self.cube_name - } -} - -pub struct JoinConditionSymbolFactory { - cube_name: String, - sql: Rc, -} - -impl JoinConditionSymbolFactory { - pub fn try_new(cube_name: &String, sql: Rc) -> Result { - Ok(Self { - cube_name: cube_name.clone(), - sql, - }) - } -} - -impl MemberSymbolFactory for JoinConditionSymbolFactory { - fn symbol_name() -> String { - "join".to_string() - } - - fn is_cachable() -> bool { - false - } - - fn cube_name(&self) -> &String { - &self.cube_name - } - - fn deps_names(&self) -> Result, CubeError> { - Ok(self.sql.args_names().clone()) - } - - fn member_sql(&self) -> Option> { - Some(self.sql.clone()) - } - - fn build( - self, - deps: Vec, - _compiler: &mut Compiler, - ) -> Result, CubeError> { - let Self { cube_name, sql } = self; - Ok(EvaluationNode::new_join_condition( - JoinConditionSymbol::new(cube_name, sql), - deps, - )) - } -} 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 40bcd64c88ca6..e6ecf3db0e0c5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/measure_symbol.rs @@ -1,9 +1,8 @@ -use super::{MemberSymbol, MemberSymbolFactory}; +use super::{MemberSymbol, SymbolFactory}; use crate::cube_bridge::evaluator::CubeEvaluator; use crate::cube_bridge::measure_definition::{MeasureDefinition, TimeShiftReference}; use crate::cube_bridge::memeber_sql::{MemberSql, MemberSqlArg}; -use crate::planner::query_tools::QueryTools; -use crate::planner::sql_evaluator::{Compiler, Dependency, EvaluationNode, SqlEvaluatorVisitor}; +use crate::planner::sql_evaluator::{Compiler, Dependency, EvaluationNode}; use cubenativeutils::CubeError; use std::rc::Rc; @@ -100,25 +99,6 @@ impl MeasureSymbol { &self.definition.static_data().time_shift_references } - pub fn default_evaluate_sql( - &self, - visitor: &SqlEvaluatorVisitor, - args: Vec, - tools: Rc, - ) -> Result { - let sql = tools.auto_prefix_with_cube_name( - &self.cube_name, - &self.member_sql.call(args)?, - visitor.cube_alias_prefix(), - ); - if self.is_calculated() { - Ok(sql) - } else { - let measure_type = &self.definition.static_data().measure_type; - Ok(format!("{}({})", measure_type, sql)) - } - } - pub fn is_multi_stage(&self) -> bool { self.definition.static_data().multi_stage.unwrap_or(false) } @@ -128,6 +108,9 @@ impl MemberSymbol for MeasureSymbol { fn cube_name(&self) -> &String { &self.cube_name } + fn name(&self) -> &String { + &self.name + } } pub struct MeasureSymbolFactory { @@ -170,7 +153,7 @@ impl MeasureSymbolFactory { } } -impl MemberSymbolFactory for MeasureSymbolFactory { +impl SymbolFactory for MeasureSymbolFactory { fn symbol_name() -> String { "measure".to_string() } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol.rs index 16f96bff64bef..b8137abda10cb 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 @@ -1,24 +1,4 @@ -use crate::cube_bridge::memeber_sql::MemberSql; -use crate::planner::sql_evaluator::dependecy::Dependency; -use crate::planner::sql_evaluator::{Compiler, EvaluationNode}; -use cubenativeutils::CubeError; -use std::rc::Rc; - pub trait MemberSymbol { fn cube_name(&self) -> &String; -} - -pub trait MemberSymbolFactory: Sized { - fn symbol_name() -> String; //FIXME maybe Enum should be used - fn is_cachable() -> bool { - true - } - fn cube_name(&self) -> &String; - fn deps_names(&self) -> Result, CubeError>; - fn member_sql(&self) -> Option>; - fn build( - self, - deps: Vec, - compiler: &mut Compiler, - ) -> Result, CubeError>; + fn name(&self) -> &String; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol_type.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol_type.rs index 6d72d0c44e0db..830c8e7f4e367 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol_type.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/member_symbol_type.rs @@ -1,13 +1,9 @@ -use super::{ - CubeNameSymbol, CubeTableSymbol, DimensionSymbol, JoinConditionSymbol, MeasureSymbol, - MemberSymbol, SimpleSqlSymbol, -}; +use super::{CubeNameSymbol, CubeTableSymbol, DimensionSymbol, MeasureSymbol, SimpleSqlSymbol}; pub enum MemberSymbolType { Dimension(DimensionSymbol), Measure(MeasureSymbol), CubeName(CubeNameSymbol), CubeTable(CubeTableSymbol), - JoinCondition(JoinConditionSymbol), SimpleSql(SimpleSqlSymbol), } @@ -18,7 +14,6 @@ impl MemberSymbolType { MemberSymbolType::Measure(m) => m.full_name(), MemberSymbolType::CubeName(c) => c.cube_name().clone(), MemberSymbolType::CubeTable(c) => c.cube_name().clone(), - MemberSymbolType::JoinCondition(_) => "".to_string(), MemberSymbolType::SimpleSql(_) => "".to_string(), } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs index b16c1531a0382..7b8ba1e7e42f2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/mod.rs @@ -1,17 +1,17 @@ mod cube_symbol; mod dimension_symbol; -mod join_symbol; mod measure_symbol; mod member_symbol; mod member_symbol_type; mod simple_sql; +mod symbol_factory; pub use cube_symbol::{ CubeNameSymbol, CubeNameSymbolFactory, CubeTableSymbol, CubeTableSymbolFactory, }; pub use dimension_symbol::{DimensionSymbol, DimensionSymbolFactory}; -pub use join_symbol::{JoinConditionSymbol, JoinConditionSymbolFactory}; pub use measure_symbol::{MeasureSymbol, MeasureSymbolFactory}; -pub use member_symbol::{MemberSymbol, MemberSymbolFactory}; +pub use member_symbol::MemberSymbol; pub use member_symbol_type::MemberSymbolType; pub use simple_sql::{SimpleSqlSymbol, SimpleSqlSymbolFactory}; +pub use symbol_factory::SymbolFactory; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/simple_sql.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/simple_sql.rs index bea2b22418296..52d21e298ad5c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/simple_sql.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/simple_sql.rs @@ -1,4 +1,4 @@ -use super::{MemberSymbol, MemberSymbolFactory}; +use super::SymbolFactory; use crate::cube_bridge::memeber_sql::{MemberSql, MemberSqlArg}; use crate::planner::sql_evaluator::{Compiler, Dependency, EvaluationNode}; use cubenativeutils::CubeError; @@ -27,12 +27,6 @@ impl SimpleSqlSymbol { } } -impl MemberSymbol for SimpleSqlSymbol { - fn cube_name(&self) -> &String { - &self.cube_name - } -} - pub struct SimpleSqlSymbolFactory { cube_name: String, sql: Rc, @@ -47,7 +41,7 @@ impl SimpleSqlSymbolFactory { } } -impl MemberSymbolFactory for SimpleSqlSymbolFactory { +impl SymbolFactory for SimpleSqlSymbolFactory { fn is_cachable() -> bool { false } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/symbol_factory.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/symbol_factory.rs new file mode 100644 index 0000000000000..198bcd85c6710 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/symbols/symbol_factory.rs @@ -0,0 +1,20 @@ +use crate::cube_bridge::memeber_sql::MemberSql; +use crate::planner::sql_evaluator::dependecy::Dependency; +use crate::planner::sql_evaluator::{Compiler, EvaluationNode}; +use cubenativeutils::CubeError; +use std::rc::Rc; + +pub trait SymbolFactory: Sized { + fn symbol_name() -> String; //FIXME maybe Enum should be used + fn is_cachable() -> bool { + true + } + fn cube_name(&self) -> &String; + fn deps_names(&self) -> Result, CubeError>; + fn member_sql(&self) -> Option>; + fn build( + self, + deps: Vec, + compiler: &mut Compiler, + ) -> Result, CubeError>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs index 3e17f7289d8fb..0a9a49b14f681 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/visitor.rs @@ -1,8 +1,4 @@ -use super::{ - dependecy::{ContextSymbolDep, Dependency}, - EvaluationNode, -}; -use crate::cube_bridge::memeber_sql::{MemberSqlArg, MemberSqlStruct}; +use super::{dependecy::Dependency, EvaluationNode}; use cubenativeutils::CubeError; use std::rc::Rc; @@ -52,60 +48,3 @@ pub trait TraversalVisitor { } } } - -pub trait EvaluatorVisitor { - fn on_node_enter(&mut self, _node: &Rc) -> Result<(), CubeError> { - Ok(()) - } - fn evaluate_deps(&mut self, node: &Rc) -> Result, CubeError> { - node.deps() - .iter() - .map(|d| self.evaluate_single_dep(&d, &node)) - .collect() - } - - fn evaluate_single_dep( - &mut self, - dep: &Dependency, - node: &Rc, - ) -> Result { - default_single_dep_evaluator(self, dep, node) - } - - fn apply(&mut self, node: &Rc) -> Result; - - fn apply_context_symbol( - &mut self, - contex_symbol: &ContextSymbolDep, - ) -> Result; -} - -pub fn default_single_dep_evaluator( - visitor: &mut V, - dep: &Dependency, - _node: &Rc, -) -> Result { - match dep { - Dependency::SingleDependency(dep) => Ok(MemberSqlArg::String(visitor.apply(dep)?)), - Dependency::StructDependency(dep) => { - let mut res = MemberSqlStruct::default(); - if let Some(sql_fn) = &dep.sql_fn { - res.sql_fn = Some(visitor.apply(sql_fn)?); - } - if let Some(to_string_fn) = &dep.to_string_fn { - res.to_string_fn = Some(visitor.apply(to_string_fn)?); - } - for (k, v) in dep.properties.iter() { - match v { - Dependency::SingleDependency(dep) => { - res.properties.insert(k.clone(), visitor.apply(dep)?); - } - Dependency::StructDependency(_) => unimplemented!(), - Dependency::ContextDependency(_) => unimplemented!(), - } - } - Ok(MemberSqlArg::Struct(res)) - } - Dependency::ContextDependency(contex_symbol) => visitor.apply_context_symbol(contex_symbol), - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/mod.rs index 34d102d768c20..8e47b7f5a4e2e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/mod.rs @@ -1 +1,7 @@ pub mod filter; +pub mod plan; +pub mod structs; + +pub use filter::FilterTemplates; +pub use plan::PlanSqlTemplates; +pub use structs::{TemplateGroupByColumn, TemplateOrderByColumn, TemplateProjectionColumn}; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs new file mode 100644 index 0000000000000..aca5695ef8544 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs @@ -0,0 +1,241 @@ +use super::{TemplateGroupByColumn, TemplateOrderByColumn, TemplateProjectionColumn}; +use crate::cube_bridge::sql_templates_render::SqlTemplatesRender; +use convert_case::{Case, Casing}; +use cubenativeutils::CubeError; +use minijinja::context; +use std::rc::Rc; + +pub struct PlanSqlTemplates { + render: Rc, +} + +impl PlanSqlTemplates { + pub fn new(render: Rc) -> Self { + Self { render } + } + + pub fn alias_name(name: &str) -> String { + name.to_case(Case::Snake).replace(".", "__") + } + + pub fn memeber_alias_name(cube_name: &str, name: &str, suffix: Option) -> String { + let suffix = if let Some(suffix) = suffix { + format!("_{}", suffix) + } else { + format!("") + }; + format!( + "{}__{}{}", + Self::alias_name(cube_name), + Self::alias_name(name), + suffix + ) + } + + pub fn quote_identifier(&self, column_name: &str) -> Result { + let quote = self.render.get_template("quotes/identifiers")?; + let escape = self.render.get_template("quotes/escape")?; + Ok(format!( + "{}{}{}", + quote, + column_name.replace(quote, escape), + quote + )) + } + + pub fn column_aliased(&self, expr: &str, alias: &str) -> Result { + let quoted_alias = self.quote_identifier(alias)?; + self.render.render_template( + "expressions/column_aliased", + context! { expr => expr, quoted_alias => quoted_alias }, + ) + } + + pub fn column_reference( + &self, + table_name: &Option, + name: &str, + ) -> Result { + let table_name = if let Some(table_name) = table_name { + Some(self.quote_identifier(table_name)?) + } else { + None + }; + let name = self.quote_identifier(name)?; + self.render.render_template( + "expressions/column_reference", + context! { table_name => table_name, name => name }, + ) + } + + pub fn is_null_expr(&self, expr: &str, negate: bool) -> Result { + self.render.render_template( + "expressions/is_null", + context! { expr => expr, negate => negate }, + ) + } + pub fn always_true(&self) -> Result { + Ok(self.render.get_template("filters/always_true")?.clone()) + } + + pub fn query_aliased(&self, query: &str, alias: &str) -> Result { + let quoted_alias = self.quote_identifier(alias)?; + self.render.render_template( + "expressions/query_aliased", + context! { query => query, quoted_alias => quoted_alias }, + ) + } + + pub fn order_by( + &self, + expr: &str, + index: Option, + asc: bool, + ) -> Result { + self.render.render_template( + "expressions/order_by", + context! { + expr => expr, + index => index, + asc => asc + }, + ) + } + + pub fn group_by(&self, items: Vec) -> Result { + self.render.render_template( + "statements/group_by_exprs", + context! { + group_by => items + }, + ) + } + + pub fn cte(&self, query: &str, alias: &str) -> Result { + self.render.render_template( + "statements/cte", + context! { + query => query, + alias => alias + }, + ) + } + + pub fn select( + &self, + ctes: Vec, + from: &str, + projection: Vec, + where_condition: Option, + group_by: Vec, + having: Option, + order_by: Vec, + limit: Option, + offset: Option, + distinct: bool, + ) -> Result { + self.render.render_template( + "statements/select", + context! { + from_prepared => from, + select_concat => projection, + group_by => self.group_by(group_by)?, + projection => projection, + order_by => order_by, + filter => where_condition, + having => having, + limit => limit, + offset => offset, + distinct => distinct, + ctes => ctes, + }, + ) + } + + /* pub fn select( + &self, + from: String, + projection: Vec, + group_by: Vec, + group_descs: Vec>, + aggregate: Vec, + alias: String, + filter: Option, + _having: Option, + order_by: Vec, + limit: Option, + offset: Option, + distinct: bool, + ) -> Result { + let group_by = self.to_template_columns(group_by)?; + let aggregate = self.to_template_columns(aggregate)?; + let projection = self.to_template_columns(projection)?; + let order_by = self.to_template_columns(order_by)?; + let select_concat = group_by + .iter() + .chain(aggregate.iter()) + .chain(projection.iter()) + .map(|c| c.clone()) + .collect::>(); + let quoted_from_alias = self.quote_identifier(&alias)?; + let has_grouping_sets = group_descs.iter().any(|d| d.is_some()); + let group_by_expr = if has_grouping_sets { + self.group_by_with_grouping_sets(&group_by, &group_descs)? + } else { + self.render_template( + "statements/group_by_exprs", + context! { group_by => group_by }, + )? + }; + self.render.render_template( + "statements/select", + context! { + from => from, + select_concat => select_concat, + group_by => group_by_expr, + aggregate => aggregate, + projection => projection, + order_by => order_by, + filter => filter, + from_alias => quoted_from_alias, + limit => limit, + offset => offset, + distinct => distinct, + }, + ) + } */ + + pub fn join(&self, source: &str, condition: &str, is_inner: bool) -> Result { + let join_type = if is_inner { + self.render.get_template("join_types/inner")? + } else { + self.render.get_template("join_types/left")? + }; + self.render.render_template( + "statements/join", + context! { source => source, condition => condition, join_type => join_type }, + ) + } + + pub fn join_by_dimension_conditions( + &self, + left_column: &String, + right_column: &String, + null_check: bool, + ) -> Result { + let null_check = if null_check { + format!( + " OR ({} AND {})", + self.is_null_expr(&left_column, false)?, + self.is_null_expr(&right_column, false)? + ) + } else { + format!("") + }; + + Ok(format!( + "({} = {}{})", + left_column, right_column, null_check + )) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/structs.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/structs.rs new file mode 100644 index 0000000000000..39924ba8793b4 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/structs.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TemplateProjectionColumn { + pub expr: String, + pub alias: String, + pub aliased: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TemplateGroupByColumn { + pub expr: String, + pub index: usize, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TemplateOrderByColumn { + pub expr: String, +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs index 50d07a0053560..378e869c14be3 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/visitor_context.rs @@ -1,7 +1,8 @@ use super::query_tools::QueryTools; use super::sql_evaluator::sql_nodes::{SqlNode, SqlNodesFactory}; use super::sql_evaluator::EvaluationNode; -use crate::planner::sql_evaluator::visitor::EvaluatorVisitor; +use crate::plan::Schema; +use crate::planner::sql_evaluator::sql_node_transformers::set_schema; use crate::planner::sql_evaluator::SqlEvaluatorVisitor; use cubenativeutils::CubeError; use std::rc::Rc; @@ -12,33 +13,33 @@ pub struct VisitorContext { } impl VisitorContext { - pub fn new(cube_alias_prefix: Option, node_processor: Rc) -> Rc { - Rc::new(Self { + pub fn new(cube_alias_prefix: Option, node_processor: Rc) -> Self { + Self { cube_alias_prefix, node_processor, - }) + } } pub fn new_with_cube_alias_prefix( nodes_factory: Rc, cube_alias_prefix: String, - ) -> Rc { + ) -> Self { Self::new( Some(cube_alias_prefix), nodes_factory.default_node_processor(), ) } - pub fn default(nodes_factory: Rc) -> Rc { + pub fn default(nodes_factory: Rc) -> Self { Self::new(Default::default(), nodes_factory.default_node_processor()) } pub fn make_visitor(&self, query_tools: Rc) -> SqlEvaluatorVisitor { - SqlEvaluatorVisitor::new( - query_tools, - self.cube_alias_prefix.clone(), - self.node_processor.clone(), - ) + SqlEvaluatorVisitor::new(query_tools) + } + + pub fn node_processor(&self) -> Rc { + self.node_processor.clone() } pub fn cube_alias_prefix(&self) -> &Option { @@ -50,7 +51,10 @@ pub fn evaluate_with_context( node: &Rc, query_tools: Rc, context: Rc, + source_schema: Rc, ) -> Result { let mut visitor = context.make_visitor(query_tools); - visitor.apply(node) + let node_processor = set_schema(context.node_processor(), source_schema); + + visitor.apply(node, node_processor) }