diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index c852b18298e44..1701ad115f3af 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -605,34 +605,9 @@ export class BaseQuery { } } - buildSqlAndParamsTest(exportAnnotatedSql) { - if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) { - if (this.externalPreAggregationQuery()) { // TODO performance - return this.externalQuery().buildSqlAndParams(exportAnnotatedSql); - } - } - const js_res = this.compilers.compiler.withQuery( - this, - () => this.cacheValue( - ['buildSqlAndParams', exportAnnotatedSql], - () => this.paramAllocator.buildSqlAndParams( - this.buildParamAnnotatedSql(), - exportAnnotatedSql, - this.shouldReuseParams - ), - { cache: this.queryCache } - ) - ); - console.log('js result: ', js_res[0]); - const rust = this.buildSqlAndParamsRust(exportAnnotatedSql); - console.log('rust result: ', rust[0]); - return js_res; - } - buildSqlAndParamsRust(exportAnnotatedSql) { - const order = this.options.order && R.pipe( - R.map((hash) => (!hash || !hash.id) ? null : hash), + R.map((hash) => ((!hash || !hash.id) ? null : hash)), R.reject(R.isNil), )(this.options.order); @@ -644,7 +619,7 @@ export class BaseQuery { joinRoot: this.join.root, joinGraph: this.joinGraph, cubeEvaluator: this.cubeEvaluator, - order: order, + order, filters: this.options.filters, limit: this.options.limit ? this.options.limit.toString() : null, rowLimit: this.options.rowLimit ? this.options.rowLimit.toString() : null, @@ -847,7 +822,6 @@ export class BaseQuery { } = this.fullKeyQueryAggregateMeasures(); if (!multipliedMeasures.length && !cumulativeMeasures.length && !multiStageMembers.length) { - console.log("!!!!! LLLOOOO!!!!"); return this.simpleQuery(); } @@ -1061,8 +1035,6 @@ export class BaseQuery { const allMemberChildren = this.collectAllMemberChildren(context); const memberToIsMultiStage = this.collectAllMultiStageMembers(allMemberChildren); - console.log("!!! measure to her ", measureToHierarchy); - const hasMultiStageMembers = (m) => { if (memberToIsMultiStage[m]) { return true; diff --git a/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.ts b/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.ts index f399bcfa72a7b..296a224446a57 100644 --- a/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.ts +++ b/packages/cubejs-schema-compiler/test/integration/clickhouse/ClickHouseDbRunner.ts @@ -5,7 +5,7 @@ import type { StartedTestContainer } from 'testcontainers'; import { format as formatSql } from 'sqlstring'; import { v4 as uuidv4 } from 'uuid'; import { ClickHouseQuery } from '../../../src/adapter/ClickHouseQuery'; -import { BaseDbRunner } from "../utils/BaseDbRunner"; +import { BaseDbRunner } from '../utils/BaseDbRunner'; process.env.TZ = 'GMT'; diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts index d6420fb5c0d40..f5491f153af06 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts @@ -2348,7 +2348,7 @@ describe('SQL Generation', () => { ] )); - it('rank measure', async () => runQueryTest( + it('rank measure 1', async () => runQueryTest( { measures: ['visitors.revenue_rank'], }, diff --git a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts index 3159fab098c41..1b1f28b9d1817 100644 --- a/packages/cubejs-schema-compiler/test/unit/base-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/base-query.test.ts @@ -1596,7 +1596,7 @@ describe('SQL Generation', () => { sql: 'product_id', type: 'avg', filters: [ - { sql: `{FILTER_PARAMS.Order.category.filter('category')}` } + { sql: '{FILTER_PARAMS.Order.category.filter(\'category\')}' } ] } ], @@ -1613,7 +1613,7 @@ describe('SQL Generation', () => { }, { name: 'proxied', - sql: `{FILTER_PARAMS.Order.type.filter("x => type = 'online'")}`, + sql: '{FILTER_PARAMS.Order.type.filter("x => type = \'online\'")}', type: 'boolean', } ] diff --git a/packages/cubejs-schema-compiler/test/unit/pre-aggregations.test.ts b/packages/cubejs-schema-compiler/test/unit/pre-aggregations.test.ts index cb6d653b4b23c..641f2aea699d5 100644 --- a/packages/cubejs-schema-compiler/test/unit/pre-aggregations.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/pre-aggregations.test.ts @@ -217,8 +217,8 @@ describe('pre-aggregations', () => { console.log(JSON.stringify(preAggregationsDescription, null, 2)); expect(preAggregationsDescription.length).toEqual(2); - expect(preAggregationsDescription[0].preAggregationId).toEqual("Orders.simple1"); - expect(preAggregationsDescription[1].preAggregationId).toEqual("Orders.simple2"); + expect(preAggregationsDescription[0].preAggregationId).toEqual('Orders.simple1'); + expect(preAggregationsDescription[1].preAggregationId).toEqual('Orders.simple2'); }); // @link https://github.com/cube-js/cube/issues/6623 diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs index e7d0cbab55ffa..ead66c3433594 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/cube_definition.rs @@ -1,5 +1,3 @@ -use super::dimension_definition::DimenstionDefinitionStatic; -use super::measure_definition::MeasureDefinitionStatic; use super::memeber_sql::{MemberSql, NativeMemberSql}; use cubenativeutils::wrappers::serializer::{ NativeDeserialize, NativeDeserializer, NativeSerialize, @@ -9,7 +7,6 @@ use cubenativeutils::wrappers::NativeObjectHandle; use cubenativeutils::CubeError; use serde::{Deserialize, Serialize}; use std::any::Any; -use std::collections::HashMap; use std::rc::Rc; #[derive(Serialize, Deserialize, Debug)] diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs index 4704c44b02e32..4e143cb86f703 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/builder/select.rs @@ -1,33 +1,29 @@ use crate::plan::{ - AliasedExpr, Cte, Expr, Filter, From, MemberExpression, OrderBy, Schema, Select, + AliasedExpr, Cte, Expr, Filter, From, MemberExpression, OrderBy, Schema, SchemaColumn, Select, SingleAliasedSource, SingleSource, }; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; -use crate::planner::sql_evaluator::symbols::MemberSymbol; use crate::planner::{BaseMember, VisitorContext}; use std::collections::HashMap; use std::rc::Rc; pub struct SelectBuilder { projection_columns: Vec, - from: From, + from: Rc, filter: Option, group_by: Vec, having: Option, order_by: Vec, - nodes_factory: SqlNodesFactory, ctes: Vec>, is_distinct: bool, limit: Option, offset: Option, - input_schema: Rc, result_schema: Schema, } impl SelectBuilder { - pub fn new(from: From, nodes_factory: SqlNodesFactory) -> Self { - let input_schema = from.schema.clone(); + pub fn new(from: Rc) -> Self { Self { projection_columns: vec![], from, @@ -35,34 +31,30 @@ impl SelectBuilder { group_by: vec![], having: None, order_by: vec![], - nodes_factory, ctes: vec![], is_distinct: false, limit: None, offset: None, - input_schema, result_schema: Schema::empty(), } } - pub fn add_projection_member( - &mut self, - member: &Rc, - source: Option, - alias: Option, - ) { + pub fn add_projection_member(&mut self, member: &Rc, alias: Option) { let alias = if let Some(alias) = alias { alias } else { - self.input_schema.resolve_member_alias(&member, &source) + member.alias_name() }; - let expr = Expr::Member(MemberExpression::new(member.clone(), source)); + + let expr = Expr::Member(MemberExpression::new(member.clone())); let aliased_expr = AliasedExpr { expr, alias: alias.clone(), }; self.projection_columns.push(aliased_expr); + self.result_schema + .add_column(SchemaColumn::new(alias.clone(), Some(member.full_name()))); } pub fn set_filter(&mut self, filter: Option) { @@ -127,9 +119,30 @@ impl SelectBuilder { } } - pub fn build(mut self) -> Select { + fn make_asteriks_schema(&self) -> Rc { + let schema = match &self.from.source { + crate::plan::FromSource::Empty => Rc::new(Schema::empty()), + crate::plan::FromSource::Single(source) => source.source.schema(), + crate::plan::FromSource::Join(join) => { + let mut schema = Schema::empty(); + schema.merge(join.root.source.schema().as_ref()); + for itm in join.joins.iter() { + schema.merge(itm.from.source.schema().as_ref()) + } + Rc::new(schema) + } + }; + schema + } + + pub fn build(self, mut nodes_factory: SqlNodesFactory) -> Select { let cube_references = self.make_cube_references(); - self.nodes_factory.set_cube_name_references(cube_references); + nodes_factory.set_cube_name_references(cube_references); + let schema = if self.projection_columns.is_empty() { + self.make_asteriks_schema() + } else { + Rc::new(self.result_schema) + }; Select { projection_columns: self.projection_columns, from: self.from, @@ -137,11 +150,12 @@ impl SelectBuilder { group_by: self.group_by, having: self.having, order_by: self.order_by, - context: Rc::new(VisitorContext::new(&self.nodes_factory)), + context: Rc::new(VisitorContext::new(&nodes_factory)), ctes: self.ctes, is_distinct: self.is_distinct, limit: self.limit, offset: self.offset, + schema, } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/cte.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/cte.rs index dba7dd3a9c7a3..4d3ee55a6fceb 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/cte.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/cte.rs @@ -1,4 +1,4 @@ -use super::{QueryPlan, Schema, Select}; +use super::{QueryPlan, Select}; use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; @@ -22,10 +22,6 @@ impl Cte { } } - pub fn make_schema(&self) -> Schema { - self.query.make_schema(Some(self.name().clone())) - } - pub fn query(&self) -> &Rc { &self.query } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/expression.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/expression.rs index f9189a04fc85f..be13ad59816e7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/expression.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/expression.rs @@ -1,4 +1,4 @@ -use super::Schema; +use super::QualifiedColumnName; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::{BaseMember, VisitorContext}; use cubenativeutils::CubeError; @@ -7,68 +7,45 @@ use std::rc::Rc; #[derive(Clone)] pub struct MemberExpression { pub member: Rc, - pub source: Option, } impl MemberExpression { - pub fn new(member: Rc, source: Option) -> Self { - Self { member, source } + pub fn new(member: Rc) -> Self { + Self { member } } pub fn to_sql( &self, - templates: &PlanSqlTemplates, + _templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { - if let Some(reference_column) = - schema.find_column_for_member(&self.member.full_name(), &self.source) - { - templates.column_reference(&reference_column.table_name, &reference_column.alias) - } else { - self.member.to_sql(context, schema) - } - } -} - -#[derive(Clone)] -pub struct ReferenceExpression { - pub reference: String, - pub source: Option, -} - -impl ReferenceExpression { - pub fn new(reference: String, source: Option) -> Self { - Self { reference, source } - } - - pub fn to_sql(&self, templates: &PlanSqlTemplates) -> Result { - templates.column_reference(&self.source, &self.reference) + self.member.to_sql(context) } } #[derive(Clone)] pub enum Expr { Member(MemberExpression), - Reference(ReferenceExpression), + Reference(QualifiedColumnName), } impl Expr { - pub fn new_member(member: Rc, source: Option) -> Self { - Self::Member(MemberExpression::new(member, source)) + pub fn new_member(member: Rc) -> Self { + Self::Member(MemberExpression::new(member)) } - pub fn new_reference(reference: String, source: Option) -> Self { - Self::Reference(ReferenceExpression::new(reference, source)) + pub fn new_reference(source: Option, reference: String) -> Self { + Self::Reference(QualifiedColumnName::new(source, reference)) } pub fn to_sql( &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { match self { - Self::Member(member) => member.to_sql(templates, context, schema), - Self::Reference(reference) => reference.to_sql(templates), + Self::Member(member) => member.to_sql(templates, context), + Self::Reference(reference) => { + templates.column_reference(reference.source(), &reference.name()) + } } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/filter.rs index 25f2c7c229191..b7e59049cb373 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/filter.rs @@ -1,4 +1,3 @@ -use super::Schema; use crate::planner::filter::BaseFilter; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::VisitorContext; @@ -54,7 +53,6 @@ impl FilterItem { &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { let res = match self { FilterItem::Group(group) => { @@ -62,7 +60,7 @@ impl FilterItem { let items_sql = group .items .iter() - .map(|itm| itm.to_sql(templates, context.clone(), schema.clone())) + .map(|itm| itm.to_sql(templates, context.clone())) .collect::, _>>()? .into_iter() .filter(|itm| !itm.is_empty()) @@ -75,7 +73,7 @@ impl FilterItem { } } FilterItem::Item(item) => { - let sql = item.to_sql(context.clone(), schema)?; + let sql = item.to_sql(context.clone())?; format!("({})", sql) } }; @@ -88,12 +86,11 @@ impl Filter { &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { let res = self .items .iter() - .map(|itm| itm.to_sql(templates, context.clone(), schema.clone())) + .map(|itm| itm.to_sql(templates, context.clone())) .collect::, _>>()? .join(" AND "); Ok(res) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/from.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/from.rs index a1667e89d7a4c..175fb662ca89b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/from.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/from.rs @@ -1,4 +1,4 @@ -use super::{Join, QueryPlan, Schema, SchemaCube, Select}; +use super::{Join, QueryPlan, Schema, Select}; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::{BaseCube, VisitorContext}; use cubenativeutils::CubeError; @@ -27,6 +27,14 @@ impl SingleSource { }; Ok(sql) } + + pub fn schema(&self) -> Rc { + match self { + SingleSource::Subquery(subquery) => subquery.schema(), + SingleSource::Cube(_) => Rc::new(Schema::empty()), + SingleSource::TableReference(_, schema) => schema.clone(), + } + } } #[derive(Clone)] @@ -72,14 +80,6 @@ impl SingleAliasedSource { templates.query_aliased(&sql, &self.alias) } - - pub fn make_schema(&self) -> Schema { - match &self.source { - SingleSource::Subquery(query) => query.make_schema(Some(self.alias.clone())), - SingleSource::Cube(cube) => Schema::empty(), //cube.schema().move_to_source(&self.alias), - SingleSource::TableReference(_, schema) => schema.move_to_source(&self.alias), - } - } } #[derive(Clone)] @@ -89,30 +89,17 @@ pub enum FromSource { Join(Rc), } -impl FromSource { - pub fn get_schema(&self) -> Rc { - let schema = match self { - FromSource::Empty => Schema::empty(), - FromSource::Single(source) => source.make_schema(), - FromSource::Join(join) => join.make_schema(), - }; - Rc::new(schema) - } -} - #[derive(Clone)] pub struct From { pub source: FromSource, - pub schema: Rc, } impl From { - pub fn new(source: FromSource) -> Self { - let schema = source.get_schema(); - Self { source, schema } + pub fn new(source: FromSource) -> Rc { + Rc::new(Self { source }) } - pub fn new_from_cube(cube: Rc, alias: Option) -> Self { + pub fn new_from_cube(cube: Rc, alias: Option) -> Rc { Self::new(FromSource::Single(SingleAliasedSource::new_from_cube( cube, alias, ))) @@ -122,23 +109,23 @@ impl From { reference: String, schema: Rc, alias: Option, - ) -> Self { + ) -> Rc { Self::new(FromSource::Single( SingleAliasedSource::new_from_table_reference(reference, schema, alias), )) } - pub fn new_from_join(join: Rc) -> Self { + pub fn new_from_join(join: Rc) -> Rc { Self::new(FromSource::Join(join)) } - pub fn new_from_subquery(plan: Rc, alias: String) -> Self { + pub fn new_from_subquery(plan: Rc, alias: String) -> Rc { Self::new(FromSource::Single(SingleAliasedSource::new_from_subquery( plan, alias, ))) } - pub fn new_from_subselect(plan: Rc, alias: String) -> Rc { Self::new(FromSource::Single(SingleAliasedSource::new_from_subquery( Rc::new(QueryPlan::Select(plan)), alias, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs index eb3a7347bd103..3a19170443d80 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs @@ -1,31 +1,28 @@ -use super::{Schema, SingleAliasedSource}; +use super::{Expr, SingleAliasedSource}; use crate::planner::sql_templates::PlanSqlTemplates; -use crate::planner::{BaseJoinCondition, BaseMember, VisitorContext}; +use crate::planner::{BaseJoinCondition, VisitorContext}; use cubenativeutils::CubeError; use lazy_static::lazy_static; use std::rc::Rc; pub struct RollingWindowJoinCondition { - data_source: String, time_series_source: String, trailing_interval: Option, leading_interval: Option, offset: String, - time_dimension: Rc, + time_dimension: Expr, } impl RollingWindowJoinCondition { pub fn new( - data_source: String, time_series_source: String, trailing_interval: Option, leading_interval: Option, offset: String, - time_dimension: Rc, + time_dimension: Expr, ) -> Self { Self { - data_source, time_series_source, trailing_interval, leading_interval, @@ -38,11 +35,9 @@ impl RollingWindowJoinCondition { &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { let mut conditions = vec![]; - let date_column_alias = - self.resolve_time_column_alias(templates, context.clone(), schema.clone())?; + let date_column = self.time_dimension.to_sql(templates, context)?; lazy_static! { static ref UNBOUNDED: Option = Some("unbounded".to_string()); @@ -63,7 +58,7 @@ impl RollingWindowJoinCondition { let sign = if self.offset == "start" { ">=" } else { ">" }; - conditions.push(format!("{date_column_alias} {sign} {trailing_start}")); + conditions.push(format!("{date_column} {sign} {trailing_start}")); } if self.leading_interval != *UNBOUNDED { @@ -81,7 +76,7 @@ impl RollingWindowJoinCondition { let sign = if self.offset == "end" { "<=" } else { "<" }; - conditions.push(format!("{date_column_alias} {sign} {leading_end}")); + conditions.push(format!("{date_column} {sign} {leading_end}")); } let result = if conditions.is_empty() { templates.always_true()? @@ -90,43 +85,17 @@ impl RollingWindowJoinCondition { }; Ok(result) } - - fn resolve_time_column_alias( - &self, - templates: &PlanSqlTemplates, - context: Rc, - schema: Rc, - ) -> Result { - let schema = schema.extract_source_schema(&self.data_source); - let source = Some(self.data_source.clone()); - if let Some(column) = - schema.find_column_for_member(&self.time_dimension.full_name(), &source) - { - templates.column_reference(&source, &column.alias.clone()) - } else { - self.time_dimension.to_sql(context.clone(), schema.clone()) - } - } } pub struct DimensionJoinCondition { - left_source: String, - right_source: String, - dimensions: Vec>, + conditions: Vec<(Expr, Expr)>, null_check: bool, } impl DimensionJoinCondition { - pub fn new( - left_source: String, - right_source: String, - dimensions: Vec>, - null_check: bool, - ) -> Self { + pub fn new(conditions: Vec<(Expr, Expr)>, null_check: bool) -> Self { Self { - left_source, - right_source, - dimensions, + conditions, null_check, } } @@ -135,15 +104,14 @@ impl DimensionJoinCondition { &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { - let result = if self.dimensions.is_empty() { + let result = if self.conditions.is_empty() { format!("1 = 1") } else { - self.dimensions + self.conditions .iter() - .map(|dim| -> Result { - self.dimension_condition(templates, context.clone(), dim, schema.clone()) + .map(|(left, right)| -> Result { + self.dimension_condition(templates, context.clone(), left, right) }) .collect::, _>>()? .join(" AND ") @@ -155,41 +123,12 @@ impl DimensionJoinCondition { &self, templates: &PlanSqlTemplates, context: Rc, - dimension: &Rc, - schema: Rc, - ) -> Result { - let left_column = self.resolve_member_alias( - templates, - context.clone(), - &self.left_source, - dimension, - schema.clone(), - )?; - let right_column = self.resolve_member_alias( - templates, - context.clone(), - &self.right_source, - dimension, - schema.clone(), - )?; - templates.join_by_dimension_conditions(&left_column, &right_column, self.null_check) - } - - fn resolve_member_alias( - &self, - templates: &PlanSqlTemplates, - context: Rc, - source: &String, - dimension: &Rc, - schema: Rc, + left_expr: &Expr, + right_expr: &Expr, ) -> Result { - let schema = schema.extract_source_schema(source); - let source = Some(source.clone()); - if let Some(column) = schema.find_column_for_member(&dimension.full_name(), &source) { - templates.column_reference(&source, &column.alias.clone()) - } else { - dimension.to_sql(context.clone(), schema.clone()) - } + let left_sql = left_expr.to_sql(templates, context.clone())?; + let right_sql = right_expr.to_sql(templates, context.clone())?; + templates.join_by_dimension_conditions(&left_sql, &right_sql, self.null_check) } } @@ -200,30 +139,18 @@ pub enum JoinCondition { } impl JoinCondition { - pub fn new_dimension_join( - left_source: String, - right_source: String, - dimensions: Vec>, - null_check: bool, - ) -> Self { - Self::DimensionJoinCondition(DimensionJoinCondition::new( - left_source, - right_source, - dimensions, - null_check, - )) + pub fn new_dimension_join(conditions: Vec<(Expr, Expr)>, null_check: bool) -> Self { + Self::DimensionJoinCondition(DimensionJoinCondition::new(conditions, null_check)) } pub fn new_rolling_join( - data_source: String, time_series_source: String, trailing_interval: Option, leading_interval: Option, offset: String, - time_dimension: Rc, + time_dimension: Expr, ) -> Self { Self::RollingWindowJoinCondition(RollingWindowJoinCondition::new( - data_source, time_series_source, trailing_interval, leading_interval, @@ -240,14 +167,11 @@ impl JoinCondition { &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { match &self { - JoinCondition::DimensionJoinCondition(cond) => cond.to_sql(templates, context, schema), - JoinCondition::BaseJoinCondition(cond) => cond.to_sql(context, schema), - JoinCondition::RollingWindowJoinCondition(cond) => { - cond.to_sql(templates, context, schema) - } + JoinCondition::DimensionJoinCondition(cond) => cond.to_sql(templates, context), + JoinCondition::BaseJoinCondition(cond) => cond.to_sql(context), + JoinCondition::RollingWindowJoinCondition(cond) => cond.to_sql(templates, context), } } } @@ -268,9 +192,8 @@ impl JoinItem { &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { - let on_sql = self.on.to_sql(templates, context.clone(), schema)?; + let on_sql = self.on.to_sql(templates, context.clone())?; let result = templates.join( &self.from.to_sql(templates, context)?, &on_sql, @@ -281,24 +204,15 @@ impl JoinItem { } impl Join { - pub fn make_schema(&self) -> Schema { - let mut schema = self.root.make_schema(); - for itm in self.joins.iter() { - schema.merge(itm.from.make_schema()); - } - schema - } - pub fn to_sql( &self, templates: &PlanSqlTemplates, context: Rc, ) -> Result { - let schema = Rc::new(self.make_schema()); let joins_sql = self .joins .iter() - .map(|j| j.to_sql(templates, context.clone(), schema.clone())) + .map(|j| j.to_sql(templates, context.clone())) .collect::, _>>()?; let res = format!( "{}\n{}", diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/mod.rs index 7dc4a188a3fbf..3799935638bd2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/mod.rs @@ -19,7 +19,7 @@ pub use from::{From, FromSource, SingleAliasedSource, SingleSource}; pub use join::{Join, JoinCondition, JoinItem, RollingWindowJoinCondition}; pub use order::OrderBy; pub use query_plan::QueryPlan; -pub use schema::{Schema, SchemaColumn, SchemaCube}; +pub use schema::{QualifiedColumnName, Schema, SchemaColumn}; pub use select::{AliasedExpr, Select}; pub use time_series::TimeSeries; pub use union::Union; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs index 8fd8b9144f214..8957321274111 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs @@ -10,11 +10,11 @@ pub enum QueryPlan { } impl QueryPlan { - pub fn make_schema(&self, self_alias: Option) -> Schema { + pub fn schema(&self) -> Rc { match self { - QueryPlan::Select(select) => select.make_schema(self_alias), - QueryPlan::Union(union) => union.make_schema(self_alias), - QueryPlan::TimeSeries(series) => series.make_schema(self_alias), + QueryPlan::Select(select) => select.schema(), + QueryPlan::Union(union) => union.schema(), + QueryPlan::TimeSeries(series) => series.schema(), } } pub fn to_sql(&self, templates: &PlanSqlTemplates) -> Result { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema.rs deleted file mode 100644 index ecda0d157938d..0000000000000 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::planner::sql_templates::PlanSqlTemplates; -use crate::planner::BaseMember; -use itertools::Itertools; -use std::rc::Rc; - -#[derive(Debug, Clone)] -pub struct SchemaColumn { - pub table_name: Option, - pub alias: String, - pub origin_member: Option, -} - -impl SchemaColumn { - pub fn new(table_name: Option, alias: String, origin_member: Option) -> Self { - Self { - table_name, - alias, - origin_member, - } - } -} - -#[derive(Debug, Clone)] -pub struct SchemaCube { - pub name: String, - pub alias: String, -} - -impl SchemaCube { - pub fn new(name: String, alias: String) -> Self { - Self { name, alias } - } -} - -#[derive(Debug)] -pub struct Schema { - columns: Vec, - cubes: Vec, -} - -impl Schema { - pub fn empty() -> Self { - Self::new(vec![], vec![]) - } - pub fn new(columns: Vec, cubes: Vec) -> Self { - Self { columns, cubes } - } - - pub fn add_column(&mut self, column: SchemaColumn) { - self.columns.push(column) - } - - pub fn add_cube(&mut self, cube: SchemaCube) { - self.cubes.push(cube) - } - pub fn merge(&mut self, other: Self) { - let Schema { - mut columns, - mut cubes, - } = other; - self.columns.append(&mut columns); - self.cubes.append(&mut cubes); - } - - pub fn resolve_member_alias( - &self, - member: &Rc, - source: &Option, - ) -> String { - if let Some(column) = self.find_column_for_member(&member.full_name(), source) { - column.alias.clone() - } else { - PlanSqlTemplates::memeber_alias_name( - member.cube_name(), - member.name(), - member.alias_suffix(), - ) - } - } - - pub fn resolve_member_reference( - &self, - member_name: &String, - source: &Option, - ) -> Option { - if let Some(column) = self.find_column_for_member(&member_name, source) { - Some(column.alias.clone()) - } else { - None - } - } - - pub fn resolve_cube_alias(&self, cube_name: &String) -> String { - if let Some(cube) = self.find_cube_by_origin_cube_name(cube_name) { - cube.alias.clone() - } else { - cube_name.clone() - } - } - - pub fn find_column_for_member( - &self, - member_name: &String, - source: &Option, - ) -> Option<&SchemaColumn> { - self.columns.iter().find(|col| { - if source.is_some() && source != &col.table_name { - return false; - } - if let Some(origin_member) = &col.origin_member { - origin_member == member_name - } else { - false - } - }) - } - - pub fn find_cube_by_origin_cube_name(&self, member_cube_name: &String) -> Option<&SchemaCube> { - self.cubes - .iter() - .find(|cube| &cube.name == member_cube_name) - } - - pub fn extract_source_schema(&self, source: &String) -> Rc { - let columns = self - .columns - .iter() - .filter(|col| col.table_name.is_some() && col.table_name.as_ref().unwrap() == source) - .cloned() - .collect_vec(); - let cubes = self - .cubes - .iter() - .filter(|cb| &cb.alias == source) - .cloned() - .collect_vec(); - Rc::new(Self { columns, cubes }) - } - - pub fn move_to_source(&self, source: &String) -> Self { - let mut columns = self.columns.clone(); - for col in columns.iter_mut() { - col.table_name = Some(source.clone()) - } - Self { - columns, - cubes: vec![], //we not fill cubes here - } - } -} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/column.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/column.rs new file mode 100644 index 0000000000000..8d39385af42af --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/column.rs @@ -0,0 +1,57 @@ +use std::fmt::Display; + +#[derive(Debug, Clone)] +pub struct QualifiedColumnName { + source: Option, + name: String, +} + +impl QualifiedColumnName { + pub fn new(source: Option, name: String) -> Self { + Self { source, name } + } + + pub fn source(&self) -> &Option { + &self.source + } + + pub fn name(&self) -> &String { + &self.name + } + + pub fn set_source(&mut self, source: Option) { + self.source = source; + } +} + +impl Display for QualifiedColumnName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(source) = &self.source { + write!(f, "{}.", source)? + } + write!(f, "{}", self.name) + } +} + +#[derive(Debug, Clone)] +pub struct SchemaColumn { + name: String, + origin_member: Option, +} + +impl SchemaColumn { + pub fn new(name: String, origin_member: Option) -> Self { + Self { + name, + origin_member, + } + } + + pub fn name(&self) -> &String { + &self.name + } + + pub fn origin_member(&self) -> &Option { + &self.origin_member + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/mod.rs new file mode 100644 index 0000000000000..18ffca6a0bee9 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/mod.rs @@ -0,0 +1,5 @@ +mod column; +mod schema; + +pub use column::{QualifiedColumnName, SchemaColumn}; +pub use schema::Schema; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/schema.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/schema.rs new file mode 100644 index 0000000000000..29c519dba835f --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/schema/schema.rs @@ -0,0 +1,67 @@ +use super::SchemaColumn; +use crate::planner::sql_templates::PlanSqlTemplates; +use crate::planner::BaseMember; +use itertools::Itertools; +use std::rc::Rc; + +#[derive(Debug)] +pub struct Schema { + columns: Vec, +} + +impl Schema { + pub fn empty() -> Self { + Self::new(vec![]) + } + pub fn new(columns: Vec) -> Self { + Self { columns } + } + + pub fn add_column(&mut self, column: SchemaColumn) { + self.columns.push(column) + } + + pub fn has_column(&self, column_name: &String) -> bool { + self.columns.iter().any(|col| col.name() == column_name) + } + + pub fn merge(&mut self, other: &Self) { + let res = self + .columns + .iter() + .chain(other.columns.iter()) + .unique_by(|col| col.name()) + .collect_vec(); + self.columns = res.into_iter().cloned().collect_vec(); + } + + pub fn resolve_member_alias(&self, member: &Rc) -> String { + if let Some(column) = self.find_column_for_member(&member.full_name()) { + column.name().clone() + } else { + PlanSqlTemplates::memeber_alias_name( + member.cube_name(), + member.name(), + member.alias_suffix(), + ) + } + } + + pub fn resolve_member_reference(&self, member_name: &String) -> Option { + if let Some(column) = self.find_column_for_member(&member_name) { + Some(column.name().clone()) + } else { + None + } + } + + pub fn find_column_for_member(&self, member_name: &String) -> Option<&SchemaColumn> { + self.columns.iter().find(|col| { + if let Some(origin_member) = &col.origin_member() { + origin_member == member_name + } else { + false + } + }) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/select.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/select.rs index a9b49ccf1d09a..2c37717014cf9 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/select.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/select.rs @@ -1,4 +1,4 @@ -use super::{Cte, Expr, Filter, From, OrderBy, Schema, SchemaColumn}; +use super::{Cte, Expr, Filter, From, OrderBy, Schema}; use crate::planner::sql_templates::PlanSqlTemplates; use crate::planner::sql_templates::{ TemplateGroupByColumn, TemplateOrderByColumn, TemplateProjectionColumn, @@ -21,9 +21,8 @@ impl AliasedExpr { &self, templates: &PlanSqlTemplates, context: Rc, - schema: Rc, ) -> Result { - let expr = self.expr.to_sql(templates, context, schema)?; + let expr = self.expr.to_sql(templates, context)?; let aliased = templates.column_aliased(&expr, &self.alias)?; Ok(TemplateProjectionColumn { expr, @@ -35,7 +34,7 @@ impl AliasedExpr { pub struct Select { pub(super) projection_columns: Vec, - pub(super) from: From, + pub(super) from: Rc, pub(super) filter: Option, pub(super) group_by: Vec, pub(super) having: Option, @@ -45,47 +44,19 @@ pub struct Select { pub(super) is_distinct: bool, pub(super) limit: Option, pub(super) offset: Option, + pub(super) schema: Rc, } impl Select { - pub fn make_schema(&self, self_alias: Option) -> Schema { - if self.projection_columns.is_empty() { - if let Some(self_alias) = self_alias { - self.from.schema.move_to_source(&self_alias) - } else { - Schema::empty() //FIXME - } - } else { - let mut schema = Schema::empty(); - for col in self.projection_columns.iter() { - match &col.expr { - Expr::Member(member) => { - let schema_col = SchemaColumn::new( - self_alias.clone(), - col.alias.clone(), - Some(member.member.full_name()), - ); - schema.add_column(schema_col); - } - Expr::Reference(reference) => { - let schema_col = SchemaColumn::new( - self_alias.clone(), - col.alias.clone(), - None, //"".to_string(), //member.member.full_name(), - ); - schema.add_column(schema_col); - } - } - } - schema - } + pub fn schema(&self) -> Rc { + self.schema.clone() } + pub fn to_sql(&self, templates: &PlanSqlTemplates) -> Result { - let schema = self.from.schema.clone(); let projection = if !self.projection_columns.is_empty() { self.projection_columns .iter() - .map(|p| p.to_sql(templates, self.context.clone(), schema.clone())) + .map(|p| p.to_sql(templates, self.context.clone())) .collect::, _>>()? } else { vec![TemplateProjectionColumn { @@ -96,7 +67,7 @@ impl Select { }; let where_condition = if let Some(filter) = &self.filter { - Some(filter.to_sql(templates, self.context.clone(), schema.clone())?) + Some(filter.to_sql(templates, self.context.clone())?) } else { None }; @@ -106,13 +77,13 @@ impl Select { .iter() .enumerate() .map(|(i, expr)| -> Result<_, CubeError> { - let expr = expr.to_sql(templates, self.context.clone(), schema.clone())?; + let expr = expr.to_sql(templates, self.context.clone())?; Ok(TemplateGroupByColumn { expr, index: i + 1 }) }) .collect::, _>>()?; let having = if let Some(having) = &self.having { - Some(having.to_sql(templates, self.context.clone(), schema.clone())?) + Some(having.to_sql(templates, self.context.clone())?) } else { None }; @@ -130,8 +101,7 @@ impl Select { .iter() .map(|itm| -> Result<_, CubeError> { let expr = templates.order_by( - &itm.expr - .to_sql(templates, self.context.clone(), schema.clone())?, + &itm.expr.to_sql(templates, self.context.clone())?, Some(itm.pos), !itm.desc, )?; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/time_series.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/time_series.rs index b6c2fab40dc19..43de9351a5405 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/time_series.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/time_series.rs @@ -1,22 +1,36 @@ use super::{Schema, SchemaColumn}; use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; +use std::rc::Rc; pub struct TimeSeries { pub time_dimension_name: String, pub from_date: Option, pub to_date: Option, pub seria: Vec>, + pub schema: Rc, } impl TimeSeries { - pub fn make_schema(&self, self_alias: Option) -> Schema { - let column = SchemaColumn::new( - self_alias, - format!("date_from"), - Some(self.time_dimension_name.clone()), - ); - Schema::new(vec![column], vec![]) + pub fn new( + time_dimension_name: String, + from_date: Option, + to_date: Option, + seria: Vec>, + ) -> Self { + let column = SchemaColumn::new(format!("date_from"), Some(time_dimension_name.clone())); + let schema = Rc::new(Schema::new(vec![column])); + Self { + time_dimension_name, + from_date, + to_date, + seria, + schema, + } + } + + pub fn schema(&self) -> Rc { + self.schema.clone() } pub fn to_sql(&self, templates: &PlanSqlTemplates) -> Result { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs index 2fb5f5615ecaf..ce367857b201e 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/union.rs @@ -1,21 +1,25 @@ use super::{QueryPlan, Schema}; use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; +use std::rc::Rc; pub struct Union { pub union: Vec, + pub schema: Rc, } 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() + let schema = if union.is_empty() { + Rc::new(Schema::empty()) } else { - self.union[0].make_schema(self_alias) - } + union[0].schema() + }; + Self { union, schema } + } + + pub fn schema(&self) -> Rc { + self.schema.clone() } pub fn to_sql(&self, templates: &PlanSqlTemplates) -> Result { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs index 6fb78a91c6711..ee9e8116f8506 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_cube.rs @@ -1,14 +1,13 @@ use super::query_tools::QueryTools; use super::sql_evaluator::MemberSymbol; use super::{evaluate_with_context, VisitorContext}; -use crate::plan::{Schema, SchemaColumn}; use cubenativeutils::CubeError; +use std::collections::HashSet; use std::rc::Rc; pub struct BaseCube { cube_name: String, - members: Vec, - schema: Schema, + members: HashSet, member_evaluator: Rc, query_tools: Rc, } @@ -20,32 +19,21 @@ impl BaseCube { ) -> Result, CubeError> { let members = query_tools .base_tools() - .all_cube_members(cube_name.clone())?; - let mut schema = Schema::empty(); - for member in members.iter() { - schema.add_column(SchemaColumn::new( - Some(cube_name.clone()), - member.clone(), - Some(format!("{}.{}", cube_name, member)), - )); - } + .all_cube_members(cube_name.clone())? + .into_iter() + .collect::>(); Ok(Rc::new(Self { cube_name, members, - schema, member_evaluator, query_tools, })) } pub fn to_sql(&self, context: Rc) -> Result { - let cube_sql = evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - Rc::new(Schema::empty()), - )?; + let cube_sql = + evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context)?; Ok(cube_sql) } @@ -53,12 +41,12 @@ impl BaseCube { &self.cube_name } - pub fn members(&self) -> &Vec { + pub fn members(&self) -> &HashSet { &self.members } - pub fn schema(&self) -> &Schema { - &self.schema + pub fn has_member(&self, name: &str) -> bool { + self.members.contains(name) } pub fn default_alias(&self) -> String { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs index d753add3b0634..6a1bca2d280de 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_dimension.rs @@ -1,7 +1,6 @@ use super::query_tools::QueryTools; use super::sql_evaluator::MemberSymbol; use super::{evaluate_with_context, BaseMember, VisitorContext}; -use crate::plan::Schema; use cubenativeutils::CubeError; use std::rc::Rc; @@ -14,18 +13,12 @@ pub struct BaseDimension { } impl BaseMember for BaseDimension { - fn to_sql(&self, context: Rc, schema: Rc) -> Result { - evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - schema, - ) + fn to_sql(&self, context: Rc) -> Result { + evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context) } fn alias_name(&self) -> String { - self.query_tools - .escape_column_name(&self.unescaped_alias_name()) + self.unescaped_alias_name() } fn member_evaluator(&self) -> Rc { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs index e4e3bc3b9415f..c90c901fbca68 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_join_condition.rs @@ -1,11 +1,10 @@ use super::query_tools::QueryTools; use super::sql_evaluator::SqlCall; -use super::{evaluate_sql_call_with_context, evaluate_with_context, VisitorContext}; -use crate::plan::Schema; +use super::{evaluate_sql_call_with_context, VisitorContext}; use cubenativeutils::CubeError; use std::rc::Rc; pub trait BaseJoinCondition { - fn to_sql(&self, context: Rc, schema: Rc) -> Result; + fn to_sql(&self, context: Rc) -> Result; } pub struct SqlJoinCondition { sql_call: Rc, @@ -24,7 +23,7 @@ impl SqlJoinCondition { } impl BaseJoinCondition for SqlJoinCondition { - fn to_sql(&self, context: Rc, schema: Rc) -> Result { - evaluate_sql_call_with_context(&self.sql_call, self.query_tools.clone(), context, schema) + fn to_sql(&self, context: Rc) -> Result { + evaluate_sql_call_with_context(&self.sql_call, self.query_tools.clone(), context) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs index 81a27aa4557f5..89e832620af1d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_measure.rs @@ -4,7 +4,6 @@ use super::{evaluate_with_context, BaseMember, VisitorContext}; use crate::cube_bridge::measure_definition::{ MeasureDefinition, RollingWindow, TimeShiftReference, }; -use crate::plan::Schema; use cubenativeutils::CubeError; use lazy_static::lazy_static; use regex::Regex; @@ -73,18 +72,12 @@ pub struct BaseMeasure { } impl BaseMember for BaseMeasure { - fn to_sql(&self, context: Rc, schema: Rc) -> Result { - evaluate_with_context( - &self.member_evaluator, - self.query_tools.clone(), - context, - schema, - ) + fn to_sql(&self, context: Rc) -> Result { + evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context) } fn alias_name(&self) -> String { - self.query_tools - .escape_column_name(&self.unescaped_alias_name()) + self.unescaped_alias_name() } fn member_evaluator(&self) -> Rc { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs index f0d0f9e715b42..7b60dedbbb728 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_member.rs @@ -1,13 +1,11 @@ use super::sql_evaluator::MemberSymbol; 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, schema: Rc) -> Result; + fn to_sql(&self, context: Rc) -> Result; fn alias_name(&self) -> String; fn member_evaluator(&self) -> Rc; fn full_name(&self) -> String { @@ -39,11 +37,4 @@ impl BaseMemberHelper { pub fn to_alias_vec(members: &Vec>) -> Vec { members.iter().map(|m| m.alias_name()).collect_vec() } - - pub fn to_reference_map(members: &Vec>) -> HashMap { - members - .iter() - .map(|m| (m.full_name(), m.alias_name())) - .collect::>() - } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs index 50a803ab0b472..296ba7c9161c7 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/base_time_dimension.rs @@ -2,7 +2,6 @@ use super::query_tools::QueryTools; use super::sql_evaluator::MemberSymbol; use super::BaseDimension; use super::{BaseMember, VisitorContext}; -use crate::plan::Schema; use cubenativeutils::CubeError; use std::rc::Rc; @@ -15,28 +14,12 @@ pub struct BaseTimeDimension { } impl BaseMember for BaseTimeDimension { - 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.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(field_sql) + fn to_sql(&self, context: Rc) -> Result { + self.dimension.to_sql(context) } fn alias_name(&self) -> String { - self.query_tools - .escape_column_name(&self.unescaped_alias_name()) + self.unescaped_alias_name() } fn member_evaluator(&self) -> Rc { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs index 0abe6b8fb2be6..b9e5a8829735d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs @@ -1,5 +1,4 @@ use super::filter_operator::FilterOperator; -use crate::plan::Schema; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::MemberSymbol; use crate::planner::sql_templates::filter::FilterTemplates; @@ -80,6 +79,10 @@ impl BaseFilter { }) } + pub fn member_evaluator(&self) -> &Rc { + &self.member_evaluator + } + pub fn values(&self) -> &Vec> { &self.values } @@ -92,17 +95,9 @@ impl BaseFilter { self.member_evaluator.full_name() } - 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, - )?; + pub fn to_sql(&self, context: Rc) -> Result { + let member_sql = + evaluate_with_context(&self.member_evaluator, self.query_tools.clone(), context)?; 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/planners/full_key_query_aggregate_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/full_key_query_aggregate_planner.rs index 295201d44a518..99ea934db4da5 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,11 +1,15 @@ use super::OrderPlanner; -use crate::plan::{Cte, Filter, From, JoinBuilder, JoinCondition, Select, SelectBuilder}; +use crate::plan::{ + Cte, Expr, Filter, From, JoinBuilder, JoinCondition, QualifiedColumnName, Select, SelectBuilder, +}; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; +use crate::planner::sql_evaluator::ReferencesBuilder; +use crate::planner::BaseMeasure; use crate::planner::BaseMemberHelper; use crate::planner::QueryProperties; -use crate::planner::{BaseMeasure, VisitorContext}; use cubenativeutils::CubeError; use itertools::Itertools; +use std::collections::HashMap; use std::rc::Rc; pub struct FullKeyAggregateQueryPlanner { @@ -30,61 +34,97 @@ impl FullKeyAggregateQueryPlanner { ))); } - let mut aggregate = - self.outer_measures_join_full_key_aggregate(&self.query_properties.measures(), joins)?; - if !ctes.is_empty() { - aggregate.set_ctes(ctes.clone()); - } + let aggregate = self.outer_measures_join_full_key_aggregate( + &self.query_properties.measures(), + joins, + ctes, + )?; - Ok(aggregate.build()) + Ok(aggregate) } fn outer_measures_join_full_key_aggregate( &self, outer_measures: &Vec>, joins: Vec>, - ) -> Result { + ctes: Vec>, + ) -> 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 on = JoinCondition::new_dimension_join( - left_alias, - right_alias, - dimensions_to_select.clone(), - true, - ); - join_builder.inner_join_subselect(join.clone(), format!("q_{}", i + 1), on); + for (i, join) in joins.iter().enumerate().skip(1) { + let left_alias = format!("q_{}", i - 1); + let right_alias = format!("q_{}", i); + let left_schema = joins[i - 1].schema(); + let right_schema = joins[i].schema(); + let conditions = dimensions_to_select + .iter() + .map(|dim| { + let alias_in_left_query = left_schema.resolve_member_alias(dim); + let left_ref = Expr::Reference(QualifiedColumnName::new( + Some(left_alias.clone()), + alias_in_left_query, + )); + let alias_in_right_query = right_schema.resolve_member_alias(dim); + let right_ref = Expr::Reference(QualifiedColumnName::new( + Some(right_alias.clone()), + alias_in_right_query, + )); + (left_ref, right_ref) + }) + .collect_vec(); + let on = JoinCondition::new_dimension_join(conditions, true); + join_builder.inner_join_subselect(join.clone(), format!("q_{}", i), on); } - let having = if self.query_properties.measures_filters().is_empty() { - None - } else { - Some(Filter { - items: self.query_properties.measures_filters().clone(), - }) - }; - let from = From::new_from_join(join_builder.build()); - let mut select_builder = SelectBuilder::new(from, self.context_factory.clone()); + let references_builder = ReferencesBuilder::new(from.clone()); + let mut render_references = HashMap::new(); - for member in self - .query_properties - .all_dimensions_and_measures(&vec![])? - .iter() - { - select_builder.add_projection_member(member, Some(format!("q_0")), None); + let mut select_builder = SelectBuilder::new(from.clone()); + + let dimensions_source = Some(format!("q_0")); + for member in dimensions_to_select.iter() { + references_builder.resolve_references_for_member( + member.member_evaluator(), + &dimensions_source, + &mut render_references, + )?; + let alias = references_builder + .resolve_alias_for_member(&member.full_name(), &dimensions_source); + select_builder.add_projection_member(member, alias); } for member in BaseMemberHelper::iter_as_base_member(&outer_measures) { - select_builder.add_projection_member(&member, None, None); + references_builder.resolve_references_for_member( + member.member_evaluator(), + &None, + &mut render_references, + )?; + let alias = references_builder.resolve_alias_for_member(&member.full_name(), &None); + select_builder.add_projection_member(&member, alias); } + let having = if self.query_properties.measures_filters().is_empty() { + None + } else { + let filter = Filter { + items: self.query_properties.measures_filters().clone(), + }; + references_builder.resolve_references_for_filter(&filter, &mut render_references)?; + Some(filter) + }; + 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) + if !ctes.is_empty() { + select_builder.set_ctes(ctes.clone()); + } + + let mut context_factory = self.context_factory.clone(); + context_factory.set_render_references(render_references); + + Ok(select_builder.build(context_factory)) } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs index cdb0bc1bd447a..d087448a73743 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/join_planner.rs @@ -24,7 +24,7 @@ impl JoinPlanner { pub fn make_join_node_with_prefix( &self, alias_prefix: &Option, /*TODO dimensions for subqueries*/ - ) -> Result { + ) -> Result, CubeError> { let join = self.query_tools.cached_data().join()?.clone(); self.make_join_node_impl(alias_prefix, join) } @@ -33,12 +33,12 @@ impl JoinPlanner { &self, alias_prefix: &Option, /*TODO dimensions for subqueries*/ join_hints: Vec, - ) -> Result { + ) -> Result, CubeError> { let join = self.query_tools.join_graph().build_join(join_hints)?; self.make_join_node_impl(alias_prefix, join) } - pub fn make_join_node(&self) -> Result { + pub fn make_join_node(&self) -> Result, CubeError> { self.make_join_node_with_prefix(&None) } @@ -46,7 +46,7 @@ impl JoinPlanner { &self, alias_prefix: &Option, join: Rc, - ) -> Result { + ) -> Result, CubeError> { let root = self.utils.cube_from_path(join.static_data().root.clone())?; let joins = join.joins()?; if joins.items().is_empty() { 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 a75be62232d80..461fbcc50bbae 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/planners/multi_stage/member_query_planner.rs @@ -3,21 +3,20 @@ use super::{ MultiStageQueryDescription, RollingWindowDescription, }; use crate::plan::{ - Cte, Expr, FilterGroup, FilterItem, From, JoinBuilder, JoinCondition, MemberExpression, - OrderBy, QueryPlan, Schema, SelectBuilder, TimeSeries, + Cte, Expr, From, JoinBuilder, JoinCondition, MemberExpression, OrderBy, QualifiedColumnName, + QueryPlan, Schema, SelectBuilder, TimeSeries, }; use crate::planner::planners::{ FullKeyAggregateQueryPlanner, MultipliedMeasuresQueryPlanner, OrderPlanner, SimpleQueryPlanner, }; use crate::planner::query_tools::QueryTools; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; +use crate::planner::sql_evaluator::ReferencesBuilder; use crate::planner::QueryProperties; -use crate::planner::{ - BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension, VisitorContext, -}; +use crate::planner::{BaseDimension, BaseMeasure, BaseMember, BaseMemberHelper, BaseTimeDimension}; use cubenativeutils::CubeError; use itertools::Itertools; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::rc::Rc; pub struct MultiStageMemberQueryPlanner { query_tools: Rc, @@ -70,12 +69,12 @@ impl MultiStageMemberQueryPlanner { .query_tools .base_tools() .generate_time_series(granularity, date_range.clone())?; - let time_seira = TimeSeries { - time_dimension_name: time_dimension.full_name(), - from_date: Some(from_date), - to_date: Some(to_date), + let time_seira = TimeSeries::new( + time_dimension.full_name(), + Some(from_date), + Some(to_date), seria, - }; + ); let query_plan = Rc::new(QueryPlan::TimeSeries(Rc::new(time_seira))); Ok(Rc::new(Cte::new(query_plan, format!("time_series")))) } @@ -87,6 +86,7 @@ impl MultiStageMemberQueryPlanner { cte_schemas: &HashMap>, ) -> Result, CubeError> { let inputs = self.input_cte_aliases(); + assert!(inputs.len() == 2); let dimensions = self.all_dimensions(); let root_alias = format!("time_series"); @@ -98,60 +98,72 @@ impl MultiStageMemberQueryPlanner { Some(root_alias.clone()), ); - for (i, input) in inputs.iter().skip(1).enumerate() { - let alias = format!("rolling_{}", i + 1); - let on = JoinCondition::new_rolling_join( - alias.clone(), - root_alias.clone(), - rolling_window_desc.trailing.clone(), - rolling_window_desc.leading.clone(), - rolling_window_desc.offset.clone(), - rolling_window_desc.time_dimension.clone(), - ); - let cte_schema = cte_schemas.get(input).unwrap().clone(); - join_builder.left_join_table_reference( - input.clone(), - cte_schema, - Some(format!("rolling_{}", i + 1)), - on, - ); - } + let input = &inputs[1]; + let alias = format!("rolling_source"); + let rolling_base_cte_schema = cte_schemas.get(input).unwrap().clone(); + let time_dimension_alias = + rolling_base_cte_schema.resolve_member_alias(&rolling_window_desc.time_dimension); + let on = JoinCondition::new_rolling_join( + root_alias.clone(), + rolling_window_desc.trailing.clone(), + rolling_window_desc.leading.clone(), + rolling_window_desc.offset.clone(), + Expr::Reference(QualifiedColumnName::new( + Some(alias.clone()), + time_dimension_alias, + )), + ); + join_builder.left_join_table_reference( + input.clone(), + rolling_base_cte_schema.clone(), + Some(alias.clone()), + on, + ); let from = From::new_from_join(join_builder.build()); let group_by = dimensions .iter() - .map(|dim| Expr::Member(MemberExpression::new(dim.clone(), None))) + .map(|dim| Expr::Member(MemberExpression::new(dim.clone()))) .collect_vec(); let mut context_factory = SqlNodesFactory::new(); context_factory.set_rolling_window(true); - //let node_context = context_factory.rolling_window_node_processor(); - - let mut select_builder = SelectBuilder::new(from, context_factory); + let references_builder = ReferencesBuilder::new(from.clone()); + let mut render_references = HashMap::new(); + let mut select_builder = SelectBuilder::new(from.clone()); for dim in dimensions.iter() { if dim.full_name() == rolling_window_desc.time_dimension.full_name() { - select_builder.add_projection_member( - &dim, - Some(root_alias.clone()), - Some( - cte_schemas - .get(&inputs[1]) - .unwrap() - .resolve_member_alias(&dim, &Some(inputs[1].clone())), - ), + render_references.insert( + dim.full_name(), + QualifiedColumnName::new(Some(root_alias.clone()), format!("date_from")), ); } else { - select_builder.add_projection_member(&dim, None, None); + references_builder.resolve_references_for_member( + dim.member_evaluator(), + &Some(alias.clone()), + &mut render_references, + )?; } + let alias = + references_builder.resolve_alias_for_member(&dim.full_name(), &Some(alias.clone())); + select_builder.add_projection_member(&dim, alias); } let query_member = self.query_member_as_base_member()?; - select_builder.add_projection_member(&query_member, None, None); + let query_member_base_name = rolling_base_cte_schema.resolve_member_alias(&query_member); + + context_factory.add_ungrouped_measure_reference( + query_member.full_name(), + QualifiedColumnName::new(Some(alias), query_member_base_name), + ); + context_factory.set_render_references(render_references); + + select_builder.add_projection_member(&query_member, None); select_builder.set_group_by(group_by); select_builder.set_order_by(self.query_order()?); - let select = select_builder.build(); + let select = select_builder.build(context_factory); Ok(Rc::new(Cte::new_from_select( Rc::new(select), @@ -177,7 +189,7 @@ impl MultiStageMemberQueryPlanner { } else { dimensions .iter() - .map(|dim| Expr::Member(MemberExpression::new(dim.clone(), None))) + .map(|dim| Expr::Member(MemberExpression::new(dim.clone()))) .collect_vec() }; @@ -205,16 +217,31 @@ impl MultiStageMemberQueryPlanner { _ => {} }; - let mut select_builder = SelectBuilder::new(from, context_factory); + let references_builder = ReferencesBuilder::new(from.clone()); + let mut render_references = HashMap::new(); + let mut select_builder = SelectBuilder::new(from.clone()); for dim in dimensions.iter() { - select_builder.add_projection_member(&dim, None, None); + references_builder.resolve_references_for_member( + dim.member_evaluator(), + &None, + &mut render_references, + )?; + let alias = references_builder.resolve_alias_for_member(&dim.full_name(), &None); + select_builder.add_projection_member(&dim, alias); } let query_member = self.query_member_as_base_member()?; - select_builder.add_projection_member(&query_member, None, None); + references_builder.resolve_references_for_member( + query_member.member_evaluator(), + &None, + &mut render_references, + )?; + let alias = references_builder.resolve_alias_for_member(&query_member.full_name(), &None); + select_builder.add_projection_member(&query_member, alias); select_builder.set_group_by(group_by); select_builder.set_order_by(order_by); - let select = select_builder.build(); + context_factory.set_render_references(render_references); + let select = select_builder.build(context_factory); Ok(Rc::new(Cte::new_from_select( Rc::new(select), @@ -237,36 +264,67 @@ impl MultiStageMemberQueryPlanner { 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 on = JoinCondition::new_dimension_join( - left_alias, - right_alias, - dimensions.clone(), - true, - ); + for (i, input) in inputs.iter().enumerate().skip(1) { + let left_alias = format!("q_{}", i - 1); + let right_alias = format!("q_{}", i); + let left_schema = cte_schemas.get(&inputs[i - 1]).unwrap().clone(); let cte_schema = cte_schemas.get(input).unwrap().clone(); + let conditions = dimensions + .iter() + .map(|dim| { + let alias_in_left_query = left_schema.resolve_member_alias(dim); + let left_ref = Expr::Reference(QualifiedColumnName::new( + Some(left_alias.clone()), + alias_in_left_query, + )); + let alias_in_right_query = cte_schema.resolve_member_alias(dim); + let right_ref = Expr::Reference(QualifiedColumnName::new( + Some(right_alias.clone()), + alias_in_right_query, + )); + (left_ref, right_ref) + }) + .collect_vec(); + let on = JoinCondition::new_dimension_join(conditions, true); join_builder.inner_join_table_reference( input.clone(), cte_schema, - Some(format!("q_{}", i + 1)), + Some(format!("q_{}", i)), on, ); } let from = From::new_from_join(join_builder.build()); - let mut select_builder = SelectBuilder::new(from, SqlNodesFactory::new()); + let references_builder = ReferencesBuilder::new(from.clone()); + let mut render_references = HashMap::new(); + let mut select_builder = SelectBuilder::new(from.clone()); + let root_source = Some(root_alias); for dim in dimensions.iter() { - select_builder.add_projection_member(dim, None, None) + references_builder.resolve_references_for_member( + dim.member_evaluator(), + &root_source, + &mut render_references, + )?; + let alias = references_builder.resolve_alias_for_member(&dim.full_name(), &root_source); + select_builder.add_projection_member(dim, alias) } for meas in self.input_measures()?.iter() { - select_builder.add_projection_member(meas, None, None) + references_builder.resolve_references_for_member( + meas.member_evaluator(), + &None, + &mut render_references, + )?; + let alias = references_builder.resolve_alias_for_member(&meas.full_name(), &None); + select_builder.add_projection_member(meas, alias) } select_builder.set_order_by(self.subquery_order()?); - Ok(QueryPlan::Select(Rc::new(select_builder.build()))) + let mut node_factory = SqlNodesFactory::new(); + node_factory.set_render_references(render_references); + Ok(QueryPlan::Select(Rc::new( + select_builder.build(node_factory), + ))) } fn plan_for_leaf_cte_query(&self) -> Result, CubeError> { @@ -295,11 +353,8 @@ impl MultiStageMemberQueryPlanner { false, )?; - let node_factory = if self.description.state().time_shifts().is_empty() { - SqlNodesFactory::new() - } else { - SqlNodesFactory::new_with_time_shifts(self.description.state().time_shifts().clone()) - }; + let mut node_factory = SqlNodesFactory::new(); + node_factory.set_time_shifts(self.description.state().time_shifts().clone()); /* if cte_query_properties .full_key_aggregate_measures()? 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 c7c5f8ecc251d..1a2f2955d71b8 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 @@ -10,7 +10,7 @@ use crate::planner::sql_evaluator::collectors::has_multi_stage_members; use crate::planner::sql_evaluator::collectors::member_childs; use crate::planner::sql_evaluator::sql_nodes::SqlNodesFactory; use crate::planner::sql_evaluator::MemberSymbol; -use crate::planner::{BaseDimension, BaseMeasure, VisitorContext}; +use crate::planner::{BaseDimension, BaseMeasure}; use crate::planner::{BaseTimeDimension, GranularityHelper, QueryProperties}; use cubenativeutils::CubeError; use itertools::Itertools; @@ -78,7 +78,7 @@ impl MultiStageQueryPlanner { descr.clone(), ) .plan_query(&cte_schemas)?; - cte_schemas.insert(descr.alias().clone(), Rc::new(res.make_schema())); + cte_schemas.insert(descr.alias().clone(), res.query().schema()); Ok(res) }) .collect::, _>>()?; @@ -97,12 +97,10 @@ impl MultiStageQueryPlanner { cte_schemas: &HashMap>, ) -> Rc