Skip to content

Commit ab46752

Browse files
committed
feat(tesseract): Pre-aggregation planning fallback
1 parent 06ae94b commit ab46752

File tree

10 files changed

+107
-41
lines changed

10 files changed

+107
-41
lines changed

packages/cubejs-backend-native/Cargo.lock

Lines changed: 12 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cubejs-schema-compiler/src/adapter/BaseQuery.js

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,9 @@ export class BaseQuery {
278278
return dimension;
279279
}).filter(R.identity).map(this.newTimeDimension.bind(this));
280280
this.allFilters = this.timeDimensions.concat(this.segments).concat(this.filters);
281+
this.useNativeSqlPlanner = this.options.useNativeSqlPlanner ?? getEnv('nativeSqlPlanner');
282+
this.prebuildJoin();
281283

282-
if (!getEnv('nativeSqlPlanner')) {
283-
// Tesseract doesn't require join to be prebuilt and there's a case where single join can't be built for multi-fact query
284-
this.join = this.joinGraph.buildJoin(this.allJoinHints);
285-
}
286284
this.cubeAliasPrefix = this.options.cubeAliasPrefix;
287285
this.preAggregationsSchemaOption = this.options.preAggregationsSchema ?? DEFAULT_PREAGGREGATIONS_SCHEMA;
288286
this.externalQueryClass = this.options.externalQueryClass;
@@ -299,6 +297,15 @@ export class BaseQuery {
299297
this.initUngrouped();
300298
}
301299

300+
prebuildJoin() {
301+
/* if (!this.useNativeSqlPlanner) { We still need this join for the follback to preaggregation to work properly. This condition should be returned after the tesseract starts working with pre-aggregations
302+
// Tesseract doesn't require join to be prebuilt and there's a case where single join can't be built for multi-fact query
303+
this.join = this.joinGraph.buildJoin(this.allJoinHints);
304+
} */
305+
306+
this.join = this.joinGraph.buildJoin(this.allJoinHints);
307+
}
308+
302309
cacheValue(key, fn, { contextPropNames, inputProps, cache } = {}) {
303310
const currentContext = this.safeEvaluateSymbolContext();
304311
if (contextPropNames) {
@@ -375,8 +382,7 @@ export class BaseQuery {
375382
initUngrouped() {
376383
this.ungrouped = this.options.ungrouped;
377384
if (this.ungrouped) {
378-
// this.join is not defined for Tesseract
379-
if (!this.options.allowUngroupedWithoutPrimaryKey && !getEnv('nativeSqlPlanner')) {
385+
if (!this.options.allowUngroupedWithoutPrimaryKey) {
380386
const cubes = R.uniq([this.join.root].concat(this.join.joins.map(j => j.originalTo)));
381387
const primaryKeyNames = cubes.flatMap(c => this.primaryKeyNames(c));
382388
const missingPrimaryKeys = primaryKeyNames.filter(key => !this.dimensions.find(d => d.dimension === key));
@@ -603,33 +609,61 @@ export class BaseQuery {
603609
return false;
604610
}
605611

612+
newQueryNotUseNative() {
613+
const QueryClass = this.constructor;
614+
return new QueryClass(this.compilers, { ...this.options, useNativeSqlPlanner: false });
615+
}
616+
606617
/**
607618
* Returns a pair of SQL query string and parameter values for the query.
608619
* @param {boolean} [exportAnnotatedSql] - returns annotated sql with not rendered params if true
609620
* @returns {[string, Array<unknown>]}
610621
*/
611622
buildSqlAndParams(exportAnnotatedSql) {
612-
if (getEnv('nativeSqlPlanner')) {
613-
return this.buildSqlAndParamsRust(exportAnnotatedSql);
614-
} else {
615-
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) {
616-
if (this.externalPreAggregationQuery()) { // TODO performance
617-
return this.externalQuery().buildSqlAndParams(exportAnnotatedSql);
623+
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) {
624+
if (this.externalPreAggregationQuery()) { // TODO performance
625+
return this.externalQuery().buildSqlAndParams(exportAnnotatedSql);
626+
}
627+
}
628+
629+
if (this.useNativeSqlPlanner) {
630+
let isRelatedToPreAggregation = false;
631+
if (this.options.preAggregationQuery) {
632+
isRelatedToPreAggregation = true;
633+
} else if (!this.options.disableExternalPreAggregations && this.externalQueryClass) {
634+
if (this.externalPreAggregationQuery()) {
635+
isRelatedToPreAggregation = true;
636+
}
637+
} else {
638+
let preAggForQuery =
639+
this.preAggregations.findPreAggregationForQuery();
640+
if (this.options.disableExternalPreAggregations && preAggForQuery && preAggForQuery.preAggregation.external) {
641+
preAggForQuery = undefined;
642+
}
643+
if (preAggForQuery) {
644+
isRelatedToPreAggregation = true;
618645
}
619646
}
620-
return this.compilers.compiler.withQuery(
621-
this,
622-
() => this.cacheValue(
623-
['buildSqlAndParams', exportAnnotatedSql],
624-
() => this.paramAllocator.buildSqlAndParams(
625-
this.buildParamAnnotatedSql(),
626-
exportAnnotatedSql,
627-
this.shouldReuseParams
628-
),
629-
{ cache: this.queryCache }
630-
)
631-
);
647+
648+
if (isRelatedToPreAggregation) {
649+
return this.newQueryNotUseNative().buildSqlAndParams(exportAnnotatedSql);
650+
}
651+
652+
return this.buildSqlAndParamsRust(exportAnnotatedSql);
632653
}
654+
655+
return this.compilers.compiler.withQuery(
656+
this,
657+
() => this.cacheValue(
658+
['buildSqlAndParams', exportAnnotatedSql],
659+
() => this.paramAllocator.buildSqlAndParams(
660+
this.buildParamAnnotatedSql(),
661+
exportAnnotatedSql,
662+
this.shouldReuseParams
663+
),
664+
{ cache: this.queryCache }
665+
)
666+
);
633667
}
634668

635669
buildSqlAndParamsRust(exportAnnotatedSql) {
@@ -2960,6 +2994,7 @@ export class BaseQuery {
29602994
}
29612995

29622996
newSubQueryForCube(cube, options) {
2997+
options = { ...options, useNativeSqlPlanner: false }; // We not use tesseract for pre-aggregations generation yet
29632998
if (this.options.queryFactory) {
29642999
// When dealing with rollup joins, it's crucial to use the correct parameter allocator for the specific cube in use.
29653000
// By default, we'll use BaseQuery, but it's important to note that different databases (Oracle, PostgreSQL, MySQL, Druid, etc.)
@@ -2983,6 +3018,7 @@ export class BaseQuery {
29833018
historyQueries: this.options.historyQueries,
29843019
externalQueryClass: this.options.externalQueryClass,
29853020
queryFactory: this.options.queryFactory,
3021+
useNativeSqlPlanner: this.options.useNativeSqlPlanner,
29863022
...options,
29873023
};
29883024
}

packages/cubejs-schema-compiler/src/adapter/PreAggregations.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,6 @@ export class PreAggregations {
5757
}
5858

5959
preAggregationCubes() {
60-
if (getEnv('nativeSqlPlanner')) {
61-
// No join defined in Tesseract
62-
return [];
63-
}
6460
const { join } = this.query;
6561
return join.joins.map(j => j.originalTo).concat([join.root]);
6662
}

rust/cubenativeutils/src/wrappers/context.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ pub trait NativeContext<IT: InnerTypes>: Clone {
99
fn null(&self) -> Result<NativeObjectHandle<IT>, CubeError>;
1010
fn empty_array(&self) -> Result<IT::Array, CubeError>;
1111
fn empty_struct(&self) -> Result<IT::Struct, CubeError>;
12-
//fn boxed<T: 'static>(&self, value: T) -> impl NativeBox<IT, T>;
1312
fn to_string_fn(&self, result: String) -> Result<IT::Function, CubeError>;
1413
}
1514

rust/cubesqlplanner/Cargo.lock

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/cubesqlplanner/cubesqlplanner/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ serde = "1.0.115"
1616
serde_json = "1.0.56"
1717
cubenativeutils = { path = "../../cubenativeutils/" }
1818
minijinja = { version = "1", features = ["json", "loader"] }
19-
convert_case = "0.6"
19+
convert_case = "0.7.1"
2020
chrono = "0.4.15"
2121
chrono-tz = "0.8.2"
2222
lazy_static = "1.4.0"

rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ use lazy_static::lazy_static;
99
use regex::Regex;
1010
use std::rc::Rc;
1111

12+
const FROM_PARTITION_RANGE: &'static str = "__FROM_PARTITION_RANGE";
13+
14+
const TO_PARTITION_RANGE: &'static str = "__TO_PARTITION_RANGE";
15+
1216
#[derive(Debug, Clone, PartialEq, Eq)]
1317
pub enum FilterType {
1418
Dimension,
@@ -393,7 +397,7 @@ impl BaseFilter {
393397
let from = if let Some(from_str) = &self.values[0] {
394398
let from = self.format_from_date(&from_str)?;
395399

396-
if use_db_time_zone {
400+
if use_db_time_zone && &from != FROM_PARTITION_RANGE {
397401
self.query_tools.base_tools().in_db_time_zone(from)?
398402
} else {
399403
from
@@ -407,7 +411,7 @@ impl BaseFilter {
407411
let to = if let Some(to_str) = &self.values[1] {
408412
let to = self.format_to_date(&to_str)?;
409413

410-
if use_db_time_zone {
414+
if use_db_time_zone && &to != TO_PARTITION_RANGE {
411415
self.query_tools.base_tools().in_db_time_zone(to)?
412416
} else {
413417
to

rust/cubesqlplanner/cubesqlplanner/src/planner/planners/simple_query_planer.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ impl SimpleQueryPlanner {
6565
}
6666
let render_references = dimension_subquery_planner.dimensions_refs().clone();
6767
context_factory.set_render_references(render_references);
68+
context_factory.set_rendered_as_multiplied_measures(
69+
self.query_properties
70+
.full_key_aggregate_measures()?
71+
.rendered_as_multiplied_measures
72+
.clone(),
73+
);
6874
select_builder.set_filter(filter);
6975
select_builder.set_group_by(self.query_properties.group_by());
7076
select_builder.set_order_by(self.order_planner.default_order());

rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::plan::FilterItem;
1111
use crate::planner::sql_evaluator::collectors::collect_join_hints;
1212
use crate::planner::sql_templates::PlanSqlTemplates;
1313
use chrono_tz::Tz;
14-
use convert_case::{Case, Casing};
14+
use convert_case::{Boundary, Case, Casing};
1515
use cubenativeutils::CubeError;
1616
use itertools::Itertools;
1717
use lazy_static::lazy_static;
@@ -187,7 +187,9 @@ impl QueryTools {
187187
}
188188

189189
pub fn alias_name(&self, name: &str) -> String {
190-
name.to_case(Case::Snake).replace(".", "__")
190+
name.without_boundaries(&[Boundary::LOWER_DIGIT, Boundary::UPPER_DIGIT])
191+
.to_case(Case::Snake)
192+
.replace(".", "__")
191193
}
192194

193195
pub fn escaped_alias_name(&self, name: &str) -> String {

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{TemplateGroupByColumn, TemplateOrderByColumn, TemplateProjectionColumn};
22
use crate::cube_bridge::sql_templates_render::SqlTemplatesRender;
33
use crate::plan::join::JoinType;
4-
use convert_case::{Case, Casing};
4+
use convert_case::{Boundary, Case, Casing};
55
use cubenativeutils::CubeError;
66
use minijinja::context;
77
use std::rc::Rc;
@@ -17,7 +17,12 @@ impl PlanSqlTemplates {
1717
}
1818

1919
pub fn alias_name(name: &str) -> String {
20-
name.to_case(Case::Snake).replace(".", "__")
20+
let res = name
21+
.from_case(Case::Camel)
22+
.without_boundaries(&[Boundary::LOWER_DIGIT, Boundary::UPPER_DIGIT])
23+
.to_case(Case::Snake)
24+
.replace(".", "__");
25+
res
2126
}
2227

2328
pub fn memeber_alias_name(cube_name: &str, name: &str, suffix: &Option<String>) -> String {

0 commit comments

Comments
 (0)