diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index c83c50a50fa38..9478a93a8bdda 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -638,7 +638,7 @@ export class BaseQuery { } if (this.useNativeSqlPlanner) { - let isRelatedToPreAggregation = false; +/* let isRelatedToPreAggregation = false; if (this.options.preAggregationQuery) { isRelatedToPreAggregation = true; } else if (!this.options.disableExternalPreAggregations && this.externalQueryClass) { @@ -658,7 +658,7 @@ export class BaseQuery { if (isRelatedToPreAggregation) { return this.newQueryWithoutNative().buildSqlAndParams(exportAnnotatedSql); - } + } */ return this.buildSqlAndParamsRust(exportAnnotatedSql); } @@ -698,8 +698,8 @@ export class BaseQuery { offset: this.options.offset ? this.options.offset.toString() : null, baseTools: this, ungrouped: this.options.ungrouped, - exportAnnotatedSql: exportAnnotatedSql === true - + exportAnnotatedSql: exportAnnotatedSql === true, + preAggregationQuery: this.options.preAggregationQuery }; const buildResult = nativeBuildSqlAndParams(queryParams); @@ -715,6 +715,9 @@ export class BaseQuery { const res = buildResult.result; // FIXME res[1] = [...res[1]]; + if (res[2]) { + this.preAggregations.preAggregationForQuery = res[2]; + } return res; } @@ -738,6 +741,10 @@ export class BaseQuery { return timeSeriesFromCustomInterval(granularityInterval, dateRange, moment(origin), { timestampPrecision: 3 }); } + getPreAggregationByName(cube, preAggregationName) { + return this.preAggregations.getRollupPreAggregationByName(cube, preAggregationName); + } + get shouldReuseParams() { return false; } diff --git a/packages/cubejs-schema-compiler/src/adapter/PreAggregations.js b/packages/cubejs-schema-compiler/src/adapter/PreAggregations.js index 66d90a4b430be..92fdab52751ef 100644 --- a/packages/cubejs-schema-compiler/src/adapter/PreAggregations.js +++ b/packages/cubejs-schema-compiler/src/adapter/PreAggregations.js @@ -515,7 +515,7 @@ export class PreAggregations { transformedQuery.filterDimensionsSingleValueEqual || {}, ) )); - + const backAlias = (references) => references.map(r => ( Array.isArray(r) ? [transformedQuery.allBackAliasMembers[r[0]] || r[0], r[1]] : @@ -819,6 +819,29 @@ export class PreAggregations { )(preAggregations); } + getRollupPreAggregationByName(cube, preAggregationName) { + const canUsePreAggregation = () => true; + const preAggregation = R.pipe( + R.toPairs, + // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + R.filter(([k, a]) => a.type === 'rollup' || a.type === 'rollupJoin' || a.type === 'rollupLambda'), + // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + R.find(([k, a]) => k === preAggregationName) + )(this.query.cubeEvaluator.preAggregationsForCube(cube)); + if (preAggregation) { + const tableName = this.preAggregationTableName(cube, preAggregation[0], preAggregation[1]); + const preAggObj = preAggregation ? this.evaluatedPreAggregationObj(cube, preAggregation[0], preAggregation[1], canUsePreAggregation) : {}; + return { + tableName, + ...preAggObj + }; + } else { + return {}; + } + } + // TODO check multiplication factor didn't change buildRollupJoin(preAggObj, preAggObjsToJoin) { return this.query.cacheValue( diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts b/packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts index 809e31e5c2768..01b87d451380a 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts +++ b/packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts @@ -516,6 +516,13 @@ export class CubeEvaluator extends CubeSymbols { return this.cubeFromPath(path).preAggregations || {}; } + public preAggregationsForCubeAsArray(path: string) { + return Object.entries(this.cubeFromPath(path).preAggregations || {}).map(([name, preAggregation]) => ({ + name, + ...(preAggregation as Record) + })); + } + /** * Returns pre-aggregations filtered by the specified selector. */ diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts index a19cd02de00cb..796a5029c6817 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/pre-aggregations.test.ts @@ -496,7 +496,7 @@ describe('PreAggregations', () => { }); `); - it('simple pre-aggregation', () => compiler.compile().then(() => { + it('simple pre-aggregation 1', () => compiler.compile().then(() => { const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, { measures: [ 'visitors.count' @@ -513,6 +513,12 @@ describe('PreAggregations', () => { preAggregationsSchema: '' }); + const queryAndParams = query.buildSqlAndParams(); + console.log(queryAndParams); + console.log("!!!! pre aggrs", query.preAggregations?.preAggregationForQuery); + console.log("!!!! pre aggrs fun", query.preAggregations?.preAggregationForQuery.preAggregation); + expect(query.preAggregations?.preAggregationForQuery?.canUsePreAggregation).toEqual(true); + return dbRunner.evaluateQueryWithPreAggregations(query).then(res => { expect(res).toEqual( [ @@ -880,7 +886,7 @@ describe('PreAggregations', () => { }); })); - it('multiplied measure match', () => compiler.compile().then(() => { + it('multiplied measure match 1', () => compiler.compile().then(() => { const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, { measures: [ 'visitors.count' diff --git a/packages/cubejs-schema-compiler/test/integration/utils/BaseDbRunner.ts b/packages/cubejs-schema-compiler/test/integration/utils/BaseDbRunner.ts index 939badf6ac9b2..8d7e0ee64fd42 100644 --- a/packages/cubejs-schema-compiler/test/integration/utils/BaseDbRunner.ts +++ b/packages/cubejs-schema-compiler/test/integration/utils/BaseDbRunner.ts @@ -171,8 +171,6 @@ export class BaseDbRunner { public async evaluateQueryWithPreAggregations(query, seed = this.nextSeed++) { const preAggregationsDescription = query.preAggregations?.preAggregationsDescription(); - // console.log(preAggregationsDescription); - await Promise.all(preAggregationsDescription.map( async desc => { if (desc.partitionGranularity) { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs index 4d7e20ab83d8e..da4ebe8da8c6b 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs @@ -61,6 +61,8 @@ pub struct BaseQueryOptionsStatic { pub ungrouped: Option, #[serde(rename = "exportAnnotatedSql")] pub export_annotated_sql: bool, + #[serde(rename = "preAggregationQuery")] + pub pre_aggregation_query: Option, } #[nativebridge::native_bridge(BaseQueryOptionsStatic)] diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs index 8823f8f507bfb..b02077f81f863 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs @@ -1,8 +1,10 @@ use super::base_query_options::FilterItem; use super::filter_group::{FilterGroup, NativeFilterGroup}; use super::filter_params::{FilterParams, NativeFilterParams}; +use cubenativeutils::wrappers::NativeArray; use super::security_context::{NativeSecurityContext, SecurityContext}; use super::sql_templates_render::{NativeSqlTemplatesRender, SqlTemplatesRender}; +use super::pre_aggregation_obj::{NativePreAggregationObj, PreAggregationObj}; use super::sql_utils::{NativeSqlUtils, SqlUtils}; use cubenativeutils::wrappers::serializer::{ NativeDeserialize, NativeDeserializer, NativeSerialize, @@ -58,4 +60,6 @@ pub trait BaseTools { source: String, origin: String, ) -> Result; + + fn get_pre_aggregation_by_name(&self, cube_name: String, name: String) -> Result, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs index 64518acd4346d..c66a41d485321 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs @@ -4,10 +4,14 @@ use super::dimension_definition::{ }; use super::measure_definition::{MeasureDefinition, NativeMeasureDefinition}; use super::member_sql::{MemberSql, NativeMemberSql}; +use super::pre_aggregation_description::{ + NativePreAggregationDescription, PreAggregationDescription, +}; use super::segment_definition::{NativeSegmentDefinition, SegmentDefinition}; use cubenativeutils::wrappers::serializer::{ NativeDeserialize, NativeDeserializer, NativeSerialize, }; +use cubenativeutils::wrappers::NativeArray; use cubenativeutils::wrappers::NativeContextHolder; use cubenativeutils::wrappers::NativeObjectHandle; use cubenativeutils::CubeError; @@ -51,4 +55,9 @@ pub trait CubeEvaluator { sql: Rc, ) -> Result, CubeError>; fn resolve_granularity(&self, path: Vec) -> Result; + #[nbridge(vec)] + fn pre_aggregations_for_cube_as_array( + &self, + cube_name: String, + ) -> Result>, CubeError>; } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs index 1788defaab761..fd1e1108b4056 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/mod.rs @@ -21,8 +21,10 @@ pub mod member_expression; pub mod member_order_by; pub mod member_sql; pub mod options_member; +pub mod pre_aggregation_description; pub mod security_context; pub mod segment_definition; pub mod sql_templates_render; pub mod sql_utils; pub mod struct_with_sql_member; +pub mod pre_aggregation_obj; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/pre_aggregation_description.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/pre_aggregation_description.rs new file mode 100644 index 0000000000000..d615f025776ae --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/pre_aggregation_description.rs @@ -0,0 +1,32 @@ +use super::member_sql::{MemberSql, NativeMemberSql}; +use cubenativeutils::wrappers::serializer::{ + NativeDeserialize, NativeDeserializer, NativeSerialize, +}; +use cubenativeutils::wrappers::NativeArray; +use cubenativeutils::wrappers::NativeContextHolder; +use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::CubeError; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; + +#[derive(Serialize, Deserialize, Debug)] +pub struct PreAggregationDescriptionStatic { + pub name: String, + #[serde(rename = "type")] + pub pre_aggregation_type: String, + pub granularity: Option, + pub external: Option, +} + +#[nativebridge::native_bridge(PreAggregationDescriptionStatic)] +pub trait PreAggregationDescription { + #[nbridge(field, optional)] + fn measure_references(&self) -> Result>, CubeError>; + + #[nbridge(field, optional)] + fn dimension_references(&self) -> Result>, CubeError>; + + #[nbridge(field, optional)] + fn time_dimension_reference(&self) -> Result>, CubeError>; +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/pre_aggregation_obj.rs b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/pre_aggregation_obj.rs new file mode 100644 index 0000000000000..fa45e6ef9bd65 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/pre_aggregation_obj.rs @@ -0,0 +1,26 @@ +use super::member_sql::{MemberSql, NativeMemberSql}; +use cubenativeutils::wrappers::serializer::{ + NativeDeserialize, NativeDeserializer, NativeSerialize, +}; +use cubenativeutils::wrappers::NativeArray; +use cubenativeutils::wrappers::NativeContextHolder; +use cubenativeutils::wrappers::NativeObjectHandle; +use cubenativeutils::CubeError; +use serde::{Deserialize, Serialize}; +use std::any::Any; +use std::rc::Rc; + +#[derive(Serialize, Deserialize, Debug)] +pub struct PreAggregationObjStatic { + #[serde(rename = "tableName")] + pub table_name: Option, + #[serde(rename = "preAggregationName")] + pub pre_aggregation_name: Option, + pub cube: Option, + #[serde(rename = "preAggregationId")] + pub pre_aggregation_id: Option, +} + +#[nativebridge::native_bridge(PreAggregationObjStatic)] +pub trait PreAggregationObj { +} \ No newline at end of file diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate_query.rs index 4f0f347c4560c..3fda950142b8d 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate_query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/full_key_aggregate_query.rs @@ -1,6 +1,6 @@ use super::*; -use crate::planner::query_properties::OrderByItem; use std::rc::Rc; +use crate::planner::query_properties::OrderByItem; pub struct FullKeyAggregateQuery { pub multistage_members: Vec>, @@ -14,7 +14,7 @@ pub struct FullKeyAggregateQuery { } impl PrettyPrint for FullKeyAggregateQuery { - fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { + fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { result.println("FullKeyAggregateQuery: ", state); let state = state.new_level(); let details_state = state.new_level(); @@ -39,17 +39,10 @@ impl PrettyPrint for FullKeyAggregateQuery { if !self.order_by.is_empty() { result.println("order_by:", &state); for order_by in self.order_by.iter() { - result.println( - &format!( - "{} {}", - order_by.name(), - if order_by.desc() { "desc" } else { "asc" } - ), - &details_state, - ); + result.println(&format!("{} {}", order_by.name(), if order_by.desc() { "desc" } else { "asc" }), &details_state); } } result.println("source:", &state); self.source.pretty_print(result, &details_state); } -} +} \ No newline at end of file diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs index 724e1a8f79ac7..55084374a2b42 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/mod.rs @@ -8,12 +8,14 @@ mod join; mod keys_subquery; mod measure_subquery; mod multistage; -mod pretty_print; +pub mod pretty_print; mod query; mod regular_measures_query; mod resolve_multiplied_measures; mod schema; mod simple_query; +mod pre_aggregation; +pub mod optimizers; pub use aggregate_multiplied_subquery::*; pub use cube::*; @@ -31,3 +33,5 @@ pub use regular_measures_query::*; pub use resolve_multiplied_measures::*; pub use schema::*; pub use simple_query::*; +pub use optimizers::*; +pub use pre_aggregation::*; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/cube_names_collector.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/cube_names_collector.rs new file mode 100644 index 0000000000000..aea286b396efa --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/cube_names_collector.rs @@ -0,0 +1,135 @@ +use crate::logical_plan::*; +use cubenativeutils::CubeError; +use itertools::Itertools; +use std::collections::HashSet; +use std::rc::Rc; + +pub struct CubeNamesCollector { + cube_names: HashSet, +} + +impl CubeNamesCollector { + pub fn new() -> Self { + Self { + cube_names: HashSet::new(), + } + } + + pub fn collect(&mut self, query: &Query) -> Result<(), CubeError> { + match query { + Query::SimpleQuery(query) => self.collect_from_simple_query(query), + Query::FullKeyAggregateQuery(query) => { + self.collect_from_full_key_aggregate_query(query) + } + } + } + + pub fn result(self) -> Vec { + self.cube_names.into_iter().collect_vec() + } + + fn collect_from_simple_query(&mut self, query: &SimpleQuery) -> Result<(), CubeError> { + self.collect_from_simple_query_source(&query.source)?; + self.collect_from_dimension_subqueries(&query.dimension_subqueries)?; + Ok(()) + } + + fn collect_from_full_key_aggregate_query( + &mut self, + query: &FullKeyAggregateQuery, + ) -> Result<(), CubeError> { + self.collect_from_full_key_aggregate(&query.source)?; + Ok(()) + } + + fn collect_from_measure_subquery( + &mut self, + subquery: &Rc, + ) -> Result<(), CubeError> { + self.collect_from_logical_join(&subquery.source)?; + self.collect_from_dimension_subqueries(&subquery.dimension_subqueries)?; + Ok(()) + } + + fn collect_from_full_key_aggregate( + &mut self, + full_key_aggregate: &Rc, + ) -> Result<(), CubeError> { + for source in full_key_aggregate.sources.iter() { + self.collect_from_full_key_aggregate_source(source)?; + } + Ok(()) + } + fn collect_from_full_key_aggregate_source( + &mut self, + source: &FullKeyAggregateSource, + ) -> Result<(), CubeError> { + match source { + FullKeyAggregateSource::ResolveMultipliedMeasures(resolve_multiplied_measures) => { + self.collect_from_multiplied_measures_resolver(resolve_multiplied_measures) + } + FullKeyAggregateSource::MultiStageSubqueryRef(multi_stage_subquery_ref) => Ok(()), + } + } + + fn collect_from_multiplied_measures_resolver( + &mut self, + resolver: &ResolveMultipliedMeasures, + ) -> Result<(), CubeError> { + for regular_subquery in resolver.regular_measure_subqueries.iter() { + self.collect_from_simple_query(®ular_subquery)?; + } + for aggregate_multiplied_subquery in resolver.aggregate_multiplied_subqueries.iter() { + self.collect_from_aggregate_multiplied_subquery(&aggregate_multiplied_subquery)?; + } + Ok(()) + } + + fn collect_from_aggregate_multiplied_subquery( + &mut self, + subquery: &Rc, + ) -> Result<(), CubeError> { + self.collect_from_logical_join(&subquery.keys_subquery.source)?; + match subquery.source.as_ref() { + AggregateMultipliedSubquerySouce::Cube => { + self.cube_names.insert(subquery.pk_cube.name().clone()); + } + AggregateMultipliedSubquerySouce::MeasureSubquery(measure_subquery) => { + self.collect_from_measure_subquery(&measure_subquery)?; + } + } + Ok(()) + } + + fn collect_from_simple_query_source( + &mut self, + source: &SimpleQuerySource, + ) -> Result<(), CubeError> { + match source { + SimpleQuerySource::LogicalJoin(join) => self.collect_from_logical_join(join), + SimpleQuerySource::PreAggregation(_) => Ok(()), + } + } + + fn collect_from_logical_join(&mut self, join: &Rc) -> Result<(), CubeError> { + self.cube_names.insert(join.root.name.clone()); + for join_item in join.joins.iter() { + match join_item { + LogicalJoinItem::CubeJoinItem(cube_join_item) => { + self.cube_names.insert(cube_join_item.cube.name.clone()); + } + } + } + Ok(()) + } + + fn collect_from_dimension_subqueries( + &mut self, + dimension_subqueries: &Vec>, + ) -> Result<(), CubeError> { + for subquery in dimension_subqueries.iter() { + self.collect(&subquery.query)?; + } + Ok(()) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/helper.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/helper.rs new file mode 100644 index 0000000000000..557852abcc887 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/helper.rs @@ -0,0 +1,55 @@ +use itertools::Itertools; + +use crate::logical_plan::*; +use crate::plan::{FilterItem, Filter}; +use std::rc::Rc; +use crate::planner::sql_evaluator::MemberSymbol; + +pub struct OptimizerHelper; + + +impl OptimizerHelper { + pub fn new() -> Self { + Self + } + pub fn all_dimensions(&self, schema: &Rc, filters: &Rc) -> Vec> { + let mut result = schema.dimensions.clone(); + self.fill_members_from_filters(&filters.dimensions_filters, &mut result); + self.fill_members_from_filters(&filters.segments, &mut result); + result.into_iter().unique_by(|s| s.full_name()).collect() + } + + pub fn all_measures(&self, schema: &Rc, filters: &Rc) -> Vec> { + let mut result = schema.measures.clone(); + self.fill_members_from_filters(&filters.measures_filter, &mut result); + result.into_iter().unique_by(|s| s.full_name()).collect() + } + + pub fn all_time_dimensions(&self, schema: &Rc, filters: &Rc) -> Vec> { + let mut result = schema.time_dimensions.clone(); + result.into_iter().unique_by(|s| s.full_name()).collect() + } + + fn fill_members_from_filters(&self, filters: &Vec, members: &mut Vec>) { + for item in filters.iter() { + self.fill_members_from_filter_item(item, members); + } + } + + fn fill_members_from_filter_item(&self, item: &FilterItem, members: &mut Vec>) { + match item { + FilterItem::Group(group) => { + for item in group.items.iter() { + self.fill_members_from_filter_item(item, members) + } + } + FilterItem::Item(item) => { + members.push(item.member_evaluator().clone()); + } + FilterItem::Segment(segment) => { + members.push(segment.member_evaluator().clone()); + } + } + } +} + diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/mod.rs new file mode 100644 index 0000000000000..8dbc3d4879a6d --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/common/mod.rs @@ -0,0 +1,6 @@ +mod cube_names_collector; +mod helper; + +pub use cube_names_collector::*; +pub use helper::*; + diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/mod.rs new file mode 100644 index 0000000000000..5e6528d85d575 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/mod.rs @@ -0,0 +1,5 @@ +mod pre_aggregation; +mod common; + +pub use pre_aggregation::*; +pub use common::*; \ No newline at end of file diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/compiled_pre_aggregation.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/compiled_pre_aggregation.rs new file mode 100644 index 0000000000000..950cb6c1293f7 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/compiled_pre_aggregation.rs @@ -0,0 +1,128 @@ +use cubenativeutils::CubeError; +use crate::cube_bridge::member_sql::MemberSql; +use crate::cube_bridge::pre_aggregation_description::PreAggregationDescription; +use crate::planner::query_tools::QueryTools; +use crate::planner::sql_evaluator::MemberSymbol; +use std::fmt::Debug; +use std::rc::Rc; +#[derive(Clone)] +pub struct CompiledPreAggregation { + pub cube_name: String, + pub name: String, + pub granularity: Option, + pub external: Option, + pub measures: Vec>, + pub dimensions: Vec>, + pub time_dimensions: Vec<(Rc, Option)>, +} + + +impl Debug for CompiledPreAggregation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CompiledPreAggregation") + .field("cube_name", &self.cube_name) + .field("name", &self.name) + .field("granularity", &self.granularity) + .field("external", &self.external) + .field("measures", &self.measures) + .field("dimensions", &self.dimensions) + .field("time_dimensions", &self.time_dimensions) + .finish() + } +} + +impl CompiledPreAggregation { + pub fn try_new( + query_tools: Rc, + cube_name: &String, + description: Rc, + ) -> Result, CubeError> { + let static_data = description.static_data(); + let measures = if let Some(refs) = description.measure_references()? { + Self::symbols_from_ref(query_tools.clone(), cube_name, refs, Self::check_if_measure)? + } else { + Vec::new() + }; + let dimensions = if let Some(refs) = description.dimension_references()? { + Self::symbols_from_ref( + query_tools.clone(), + cube_name, + refs, + Self::check_if_dimension, + )? + } else { + Vec::new() + }; + let time_dimensions = if let Some(refs) = description.time_dimension_reference()? { + let dims = Self::symbols_from_ref( + query_tools.clone(), + cube_name, + refs, + Self::check_if_time_dimension, + )?; + if dims.len() != 1 { + return Err(CubeError::user(format!( + "Pre aggregation should contains only one time dimension" + ))); + } + vec![(dims[0].clone(), static_data.granularity.clone())] //TODO remove unwrap + } else { + Vec::new() + }; + let res = Rc::new(Self { + name: static_data.name.clone(), + cube_name: cube_name.clone(), + granularity: static_data.granularity.clone(), + external: static_data.external, + measures, + dimensions, + time_dimensions, + }); + Ok(res) + } + + fn symbols_from_ref Result<(), CubeError>>( + query_tools: Rc, + cube_name: &String, + ref_func: Rc, + check_type_fn: F, + ) -> Result>, CubeError> { + let evaluator_compiler_cell = query_tools.evaluator_compiler().clone(); + let mut evaluator_compiler = evaluator_compiler_cell.borrow_mut(); + let sql_call = evaluator_compiler.compile_sql_call(cube_name, ref_func)?; + let mut res = Vec::new(); + for symbol in sql_call.get_dependencies().iter() { + check_type_fn(&symbol)?; + res.push(symbol.clone()); + } + Ok(res) + } + + fn check_if_measure(symbol: &MemberSymbol) -> Result<(), CubeError> { + symbol + .as_measure() + .map_err(|_| CubeError::user(format!("Pre-aggregation measure must be a measure")))?; + Ok(()) + } + + fn check_if_dimension(symbol: &MemberSymbol) -> Result<(), CubeError> { + symbol.as_dimension().map_err(|_| { + CubeError::user(format!("Pre-aggregation dimension must be a dimension")) + })?; + Ok(()) + } + + fn check_if_time_dimension(symbol: &MemberSymbol) -> Result<(), CubeError> { + let dimension = symbol.as_dimension().map_err(|_| { + CubeError::user(format!( + "Pre-aggregation time dimension must be a dimension" + )) + })?; + if dimension.dimension_type() != "time" { + return Err(CubeError::user(format!( + "Pre-aggregation time dimension must be a dimension" + ))); + } + Ok(()) + } +} diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs new file mode 100644 index 0000000000000..6e9004fbd7eb2 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/dimension_matcher.rs @@ -0,0 +1,52 @@ +use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; +use std::rc::Rc; +use super::CompiledPreAggregation; +use std::collections::HashSet; +use super::MatchState; + +pub struct DimensionMatcher { + only_addictive: bool, + pre_aggregation_dimensions: HashSet, +} + +impl DimensionMatcher { + pub fn new(pre_aggregation: &CompiledPreAggregation, only_addictive: bool) -> Self { + let pre_aggregation_dimensions = pre_aggregation.dimensions.iter().map(|d| d.full_name()).collect(); + Self { only_addictive, pre_aggregation_dimensions } + } + + pub fn try_match(&self, symbol: &Rc) -> Result { + let mut result = match symbol.as_ref() { + MemberSymbol::Dimension(dimension) => { + if dimension.owned_by_cube() { + if self.pre_aggregation_dimensions.contains(&dimension.full_name()) { + return Ok(MatchState::Full); + } else { + return Ok(MatchState::NotMatched); + } + } + MatchState::Full + } + MemberSymbol::MemberExpression(member_expression) => { + MatchState::Partial + } + _ => return Ok(MatchState::NotMatched) + }; + + if symbol.get_dependencies().is_empty() { + return Ok(MatchState::NotMatched); + } + + for dep in symbol.get_dependencies() { + let dep_match = self.try_match(&dep)?; + if dep_match == MatchState::NotMatched { + return Ok(MatchState::NotMatched); + } + result = result.combine(&dep_match); + } + Ok(result) + } + + +} \ No newline at end of file diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/measure_matcher.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/measure_matcher.rs new file mode 100644 index 0000000000000..d43cf016b3650 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/measure_matcher.rs @@ -0,0 +1,44 @@ +use crate::planner::sql_evaluator::MemberSymbol; +use cubenativeutils::CubeError; +use std::rc::Rc; +use super::CompiledPreAggregation; +use std::collections::HashSet; +pub struct MeasureMatcher { + only_addictive: bool, + pre_aggregation_measures: HashSet, +} + +impl MeasureMatcher { + pub fn new(pre_aggregation: &CompiledPreAggregation, only_addictive: bool) -> Self { + let pre_aggregation_measures = pre_aggregation.measures.iter().map(|m| m.full_name()).collect(); + Self { only_addictive, pre_aggregation_measures } + } + + pub fn try_match(&self, symbol: &Rc) -> Result { + match symbol.as_ref() { + MemberSymbol::Measure(measure) => { + if self.pre_aggregation_measures.contains(&measure.full_name()) { + if !self.only_addictive || measure.is_addictive() { + return Ok(true); + } + } + } + MemberSymbol::MemberExpression(member_expression) => { + } + _ => return Ok(false) + } + + if symbol.get_dependencies().is_empty() { + return Ok(false); + } + + for dep in symbol.get_dependencies() { + if !self.try_match(&dep)? { + return Ok(false); + } + } + Ok(true) + } + + +} \ No newline at end of file diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/mod.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/mod.rs new file mode 100644 index 0000000000000..d311046b32153 --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/mod.rs @@ -0,0 +1,10 @@ +mod optimizer; +mod compiled_pre_aggregation; +mod measure_matcher; +mod dimension_matcher; + +pub use optimizer::*; +pub use compiled_pre_aggregation::*; +use measure_matcher::*; +use dimension_matcher::*; + diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/optimizer.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/optimizer.rs new file mode 100644 index 0000000000000..533854ac4309c --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/optimizers/pre_aggregation/optimizer.rs @@ -0,0 +1,216 @@ +use crate::planner::query_tools::QueryTools; +use crate::planner::GranularityHelper; +use std::rc::Rc; +use cubenativeutils::CubeError; +use super::*; +use crate::logical_plan::*; +use crate::planner::sql_evaluator::MemberSymbol; +use std::collections::{HashMap, HashSet}; +use crate::cube_bridge::pre_aggregation_obj::PreAggregationObj; + +#[derive(Clone, Debug, PartialEq)] +pub enum MatchState { + Partial, + Full, + NotMatched, +} + +impl MatchState { + pub fn combine(&self, other: &MatchState) -> MatchState { + if matches!(self, MatchState::NotMatched) || matches!(other, MatchState::NotMatched) { + return MatchState::NotMatched; + } + if matches!(self, MatchState::Partial) + || matches!(other, MatchState::Partial) + { + return MatchState::Partial; + } + return MatchState::Full; + } +} + +pub struct PreAggregationOptimizer { + query_tools: Rc, + used_pre_aggregations: HashMap<(String, String), Rc>, +} + +impl PreAggregationOptimizer { + pub fn new(query_tools: Rc) -> Self { + Self { query_tools, used_pre_aggregations: HashMap::new() } + } + + pub fn try_optimize(&mut self, plan: Rc) -> Result>, CubeError> { + let mut cube_names_collector = CubeNamesCollector::new(); + cube_names_collector.collect(&plan)?; + let cube_names = cube_names_collector.result(); + println!("!!! Cube names: {:?}", cube_names); + + let mut compiled_pre_aggregations = Vec::new(); + for cube_name in cube_names.iter() { + let pre_aggregations = self + .query_tools + .cube_evaluator() + .pre_aggregations_for_cube_as_array(cube_name.clone())?; + for pre_aggregation in pre_aggregations.iter() { + let compiled = CompiledPreAggregation::try_new( + self.query_tools.clone(), + cube_name, + pre_aggregation.clone(), + )?; + compiled_pre_aggregations.push(compiled); + } + } + + for pre_aggregation in compiled_pre_aggregations.iter() { + let new_query = self.try_rewrite_query(plan.clone(), pre_aggregation)?; + if new_query.is_some() { + return Ok(new_query); + } + } + + Ok(None) + } + + pub fn get_used_pre_aggregations(&self) -> Vec> { + self.used_pre_aggregations.values().cloned().collect() + } + + fn try_rewrite_query(&mut self, query: Rc, pre_aggregation: &Rc) -> Result>, CubeError> { + + match query.as_ref() { + Query::SimpleQuery(query) => self.try_rewrite_simple_query(query, pre_aggregation), + Query::FullKeyAggregateQuery(query) => self.try_rewrite_full_key_aggregate_query(query, pre_aggregation), + } + } + + fn try_rewrite_simple_query(&mut self, query: &SimpleQuery, pre_aggregation: &Rc) -> Result>, CubeError> { + + if self.is_schema_and_filters_match(&query.schema, &query.filter, pre_aggregation)? { + let mut new_query = SimpleQuery::clone(&query); + new_query.source = SimpleQuerySource::PreAggregation(self.make_pre_aggregation_source(pre_aggregation)?); + Ok(Some(Rc::new(Query::SimpleQuery(new_query)))) + } else { + Ok(None) + } + } + + fn try_rewrite_full_key_aggregate_query(&mut self, query: &FullKeyAggregateQuery, pre_aggregation: &Rc) -> Result>, CubeError> { + if self.is_schema_and_filters_match(&query.schema, &query.filter, pre_aggregation)? { + let source = SimpleQuerySource::PreAggregation(self.make_pre_aggregation_source(pre_aggregation)?); + let mut new_query = SimpleQuery { + schema: query.schema.clone(), + dimension_subqueries: vec![], + filter: query.filter.clone(), + offset: query.offset, + limit: query.limit, + ungrouped: query.ungrouped, + order_by: query.order_by.clone(), + source, + }; + Ok(Some(Rc::new(Query::SimpleQuery(new_query)))) + } else { + Ok(None) + } + } + + fn make_pre_aggregation_source(&mut self, pre_aggregation: &Rc) -> Result, CubeError> { + let pre_aggregation_obj = self.query_tools.base_tools().get_pre_aggregation_by_name(pre_aggregation.cube_name.clone(), pre_aggregation.name.clone())?; + if let Some(table_name) = &pre_aggregation_obj.static_data().table_name { + + let schema = LogicalSchema { + time_dimensions: vec![], + dimensions: pre_aggregation.dimensions.iter().cloned().chain(pre_aggregation.time_dimensions.iter().map(|(d, _)| d.clone())).collect(), + measures: pre_aggregation.measures.iter().cloned().collect(), + multiplied_measures: HashSet::new(), + }; + let pre_aggregation = PreAggregation { + name: pre_aggregation.name.clone(), + time_dimensions: pre_aggregation.time_dimensions.clone(), + dimensions: pre_aggregation.dimensions.clone(), + measures: pre_aggregation.measures.clone(), + schema: Rc::new(schema), + external: pre_aggregation.external.unwrap_or_default(), + granularity: pre_aggregation.granularity.clone(), + table_name: table_name.clone(), + cube_name: pre_aggregation.cube_name.clone(), + }; + self.used_pre_aggregations.insert((pre_aggregation.cube_name.clone(), pre_aggregation.name.clone()), pre_aggregation_obj.clone()); + Ok(Rc::new(pre_aggregation)) + } else { + Err(CubeError::internal(format!("Cannot find pre aggregation object for cube {} and name {}", pre_aggregation.cube_name, pre_aggregation.name))) + } + } + + fn is_schema_and_filters_match(&self, schema: &Rc, filters: &Rc, pre_aggregation: &CompiledPreAggregation) -> Result { + let helper = OptimizerHelper::new(); + let dimensions = helper.all_dimensions(schema, filters); + let time_dimensions = helper.all_time_dimensions(schema, filters); + + let mut match_state = self.match_dimensions(&dimensions, pre_aggregation)?; + for time_dimension in time_dimensions.iter() { + match_state = match_state.combine(&self.match_time_dimension(time_dimension, pre_aggregation)?); + } + + if match_state == MatchState::NotMatched { + return Ok(false); + } + let all_measures = helper.all_measures(schema, filters); + let measures_match = self.try_match_measures(&all_measures, pre_aggregation, match_state == MatchState::Partial)?; + Ok(measures_match) + } + + fn try_match_measures(&self, measures: &Vec>, pre_aggregation: &CompiledPreAggregation, only_addictive: bool) -> Result { + let matcher = MeasureMatcher::new(pre_aggregation, only_addictive); + for measure in measures.iter() { + if !matcher.try_match(measure)? { + return Ok(false); + } + } + Ok(true) + } + + + fn match_dimensions(&self, dimensions: &Vec>, pre_aggregation: &CompiledPreAggregation) -> Result { + let mut pre_aggrs_dims = pre_aggregation.dimensions.iter().map(|d| (d.full_name(), false)).collect::>(); + for dimension in dimensions.iter() { + if let Some(found) = pre_aggrs_dims.get_mut(&dimension.full_name()) { + *found = true; + } else { + return Ok(MatchState::NotMatched); + } + } + if pre_aggrs_dims.values().all(|v| *v) { + Ok(MatchState::Full) + } else { + Ok(MatchState::Partial) + } + } + + fn match_time_dimension(&self, time_dimension: &Rc, pre_aggregation: &CompiledPreAggregation) -> Result { + let time_dimension = time_dimension.as_time_dimension()?; + let result = if let Some((pre_aggregation_time_dimension, pre_aggr_granularity)) = pre_aggregation + .time_dimensions + .iter() + .find(|(pre_agg_dim, _)| pre_agg_dim.full_name() == time_dimension.base_symbol().full_name()) + { + if pre_aggr_granularity == time_dimension.granularity() { + MatchState::Full + } else if pre_aggr_granularity.is_none() || GranularityHelper::is_predefined_granularity(pre_aggr_granularity.as_ref().unwrap()) { + let min_granularity = GranularityHelper::min_granularity( + &time_dimension.granularity(), + &pre_aggr_granularity, + )?; + if &min_granularity == pre_aggr_granularity { + MatchState::Partial + } else { + MatchState::NotMatched + } + } else { + MatchState::NotMatched //TODO Custom granularities!!! + } + } else { + MatchState::NotMatched + }; + Ok(result) + } +} \ No newline at end of file diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs new file mode 100644 index 0000000000000..78ef4f871371e --- /dev/null +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/pre_aggregation.rs @@ -0,0 +1,46 @@ +use super::optimizers::CompiledPreAggregation; +use super::*; +use std::rc::Rc; +use crate::planner::sql_evaluator::MemberSymbol; +use itertools::Itertools; + +pub struct PreAggregation { + pub name: String, + pub schema: Rc, + pub measures: Vec>, + pub dimensions: Vec>, + pub time_dimensions: Vec<(Rc, Option)>, + pub external: bool, + pub granularity: Option, + pub table_name: String, + pub cube_name: String, + +} + + + + +impl PrettyPrint for PreAggregation { + fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { + result.println("PreAggregation: ", state); + let state = state.new_level(); + let details_state = state.new_level(); + result.println(&format!("name: {}", self.name), &state); + result.println(&format!("cube_name: {}", self.cube_name), &state); + result.println(&format!("table_name: {}", self.table_name), &state); + result.println(&format!("external: {}", self.external), &state); + result.println(&format!("granularity: {}", self.granularity.clone().unwrap_or("None".to_string())), &state); + result.println( + &format!("-time_dimensions: {}", &self.time_dimensions.iter().map(|(d, granularity)| format!("({} {})", d.full_name(), granularity.clone().unwrap_or("None".to_string()))).join(", ")), + &state, + ); + result.println( + &format!("-dimensions: {}", print_symbols(&self.dimensions)), + &state, + ); + result.println( + &format!("-measures: {}", print_symbols(&self.measures)), + &state, + ); + } +} \ No newline at end of file diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs index 74c92a8c0c452..27b561a8f9f8c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/query.rs @@ -8,8 +8,8 @@ pub enum Query { impl PrettyPrint for Query { fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { match self { - Query::SimpleQuery(query) => query.pretty_print(result, state), - Query::FullKeyAggregateQuery(query) => query.pretty_print(result, state), + Self::SimpleQuery(query) => query.pretty_print(result, state), + Self::FullKeyAggregateQuery(query) => query.pretty_print(result, state), } } } diff --git a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/simple_query.rs b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/simple_query.rs index 0c5b5148740ba..2a93755df7c82 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/simple_query.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/logical_plan/simple_query.rs @@ -1,6 +1,21 @@ use super::*; use crate::planner::query_properties::OrderByItem; use std::rc::Rc; + +#[derive(Clone)] +pub enum SimpleQuerySource { + LogicalJoin(Rc), + PreAggregation(Rc), +} +impl PrettyPrint for SimpleQuerySource { + fn pretty_print(&self, result: &mut PrettyPrintResult, state: &PrettyPrintState) { + match self { + SimpleQuerySource::LogicalJoin(join) => join.pretty_print(result, state), + SimpleQuerySource::PreAggregation(pre_aggregation) => pre_aggregation.pretty_print(result, state), + } + } +} +#[derive(Clone)] pub struct SimpleQuery { pub schema: Rc, pub dimension_subqueries: Vec>, @@ -9,7 +24,7 @@ pub struct SimpleQuery { pub limit: Option, pub ungrouped: bool, pub order_by: Vec, - pub source: Rc, + pub source: SimpleQuerySource, } impl PrettyPrint for SimpleQuery { diff --git a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs index e3b3441f2b8bc..276f7d382c321 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/physical_plan_builder/builder.rs @@ -1,4 +1,5 @@ use crate::logical_plan::*; +use crate::planner::BaseMemberHelper; use crate::plan::schema::QualifiedColumnName; use crate::plan::*; use crate::planner::query_properties::OrderByItem; @@ -82,15 +83,23 @@ impl PhysicalPlanBuilder { context: &PhysicalPlanBuilderContext, ) -> Result, CubeError> { let mut render_references = HashMap::new(); - let from = self.process_logical_join( - &logical_plan.source, - context, - &logical_plan.dimension_subqueries, - &mut render_references, - )?; + let mut measure_references = HashMap::new(); + let from = match &logical_plan.source { + SimpleQuerySource::LogicalJoin(join) => { + self.process_logical_join( + &join, + context, + &logical_plan.dimension_subqueries, + &mut render_references, + )? + }, + SimpleQuerySource::PreAggregation(pre_aggregation) => self.process_pre_aggregation(pre_aggregation, context, &mut render_references, &mut measure_references)?, + }; + let mut select_builder = SelectBuilder::new(from); let mut context_factory = context.make_sql_nodes_factory(); context_factory.set_ungrouped(logical_plan.ungrouped); + context_factory.set_pre_aggregation_measures_references(measure_references); let mut group_by = Vec::new(); for member in logical_plan.schema.dimensions.iter() { @@ -144,6 +153,47 @@ impl PhysicalPlanBuilder { Ok(res) } + fn process_pre_aggregation(&self, pre_aggregation: &Rc, context: &PhysicalPlanBuilderContext, render_references: &mut HashMap, measure_references: &mut HashMap) -> Result, CubeError> { + let mut pre_aggregation_schema = Schema::empty(); + let pre_aggregation_alias = PlanSqlTemplates::memeber_alias_name(&pre_aggregation.cube_name, &pre_aggregation.name, &None); + for dim in pre_aggregation.dimensions.iter() { + let alias = BaseMemberHelper::default_alias( + &dim.cube_name(), + &dim.name(), + &dim.alias_suffix(), + self.query_tools.clone(), + )?; + render_references.insert(dim.full_name(), QualifiedColumnName::new(Some(pre_aggregation_alias.clone()), alias.clone())); + pre_aggregation_schema.add_column(SchemaColumn::new(alias, Some(dim.full_name()))); + } + for (dim, granularity) in pre_aggregation.time_dimensions.iter() { + let alias = BaseMemberHelper::default_alias( + &dim.cube_name(), + &dim.name(), + granularity, + self.query_tools.clone(), + )?; + render_references.insert(dim.full_name(), QualifiedColumnName::new(Some(pre_aggregation_alias.clone()), alias.clone())); + if let Some(granularity) = &granularity { + + render_references.insert(format!("{}_{}", dim.full_name(), granularity), QualifiedColumnName::new(Some(pre_aggregation_alias.clone()), alias.clone())); + } + pre_aggregation_schema.add_column(SchemaColumn::new(alias, Some(dim.full_name()))); + } + for meas in pre_aggregation.measures.iter() { + let alias = BaseMemberHelper::default_alias( + &meas.cube_name(), + &meas.name(), + &meas.alias_suffix(), + self.query_tools.clone(), + )?; + measure_references.insert(meas.full_name(), QualifiedColumnName::new(Some(pre_aggregation_alias.clone()), alias.clone())); + pre_aggregation_schema.add_column(SchemaColumn::new(alias, Some(meas.full_name()))); + } + let from = From::new_from_table_reference(pre_aggregation.table_name.clone(), Rc::new(pre_aggregation_schema), Some(pre_aggregation_alias)); + Ok(from) + } + fn build_full_key_aggregate_query( &self, logical_plan: &FullKeyAggregateQuery, diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs index 8957321274111..e2abb89ee52b8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/query_plan.rs @@ -3,6 +3,7 @@ use crate::planner::sql_templates::PlanSqlTemplates; use cubenativeutils::CubeError; use std::rc::Rc; +#[derive(Clone)] pub enum QueryPlan { Select(Rc