Skip to content

Commit 28aae9d

Browse files
committed
feat(tesseract): Pre-aggregation planning fallback
1 parent 405a604 commit 28aae9d

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
@@ -286,11 +286,9 @@ export class BaseQuery {
286286
return dimension;
287287
}).filter(R.identity).map(this.newTimeDimension.bind(this));
288288
this.allFilters = this.timeDimensions.concat(this.segments).concat(this.filters);
289+
this.useNativeSqlPlanner = this.options.useNativeSqlPlanner ?? getEnv('nativeSqlPlanner');
290+
this.prebuildJoin();
289291

290-
if (!getEnv('nativeSqlPlanner')) {
291-
// Tesseract doesn't require join to be prebuilt and there's a case where single join can't be built for multi-fact query
292-
this.join = this.joinGraph.buildJoin(this.allJoinHints);
293-
}
294292
this.cubeAliasPrefix = this.options.cubeAliasPrefix;
295293
this.preAggregationsSchemaOption = this.options.preAggregationsSchema ?? DEFAULT_PREAGGREGATIONS_SCHEMA;
296294
this.externalQueryClass = this.options.externalQueryClass;
@@ -307,6 +305,15 @@ export class BaseQuery {
307305
this.initUngrouped();
308306
}
309307

308+
prebuildJoin() {
309+
/* 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
310+
// Tesseract doesn't require join to be prebuilt and there's a case where single join can't be built for multi-fact query
311+
this.join = this.joinGraph.buildJoin(this.allJoinHints);
312+
} */
313+
314+
this.join = this.joinGraph.buildJoin(this.allJoinHints);
315+
}
316+
310317
cacheValue(key, fn, { contextPropNames, inputProps, cache } = {}) {
311318
const currentContext = this.safeEvaluateSymbolContext();
312319
if (contextPropNames) {
@@ -383,8 +390,7 @@ export class BaseQuery {
383390
initUngrouped() {
384391
this.ungrouped = this.options.ungrouped;
385392
if (this.ungrouped) {
386-
// this.join is not defined for Tesseract
387-
if (!this.options.allowUngroupedWithoutPrimaryKey && !getEnv('nativeSqlPlanner')) {
393+
if (!this.options.allowUngroupedWithoutPrimaryKey) {
388394
const cubes = R.uniq([this.join.root].concat(this.join.joins.map(j => j.originalTo)));
389395
const primaryKeyNames = cubes.flatMap(c => this.primaryKeyNames(c));
390396
const missingPrimaryKeys = primaryKeyNames.filter(key => !this.dimensions.find(d => d.dimension === key));
@@ -611,33 +617,61 @@ export class BaseQuery {
611617
return false;
612618
}
613619

620+
newQueryNotUseNative() {
621+
const QueryClass = this.constructor;
622+
return new QueryClass(this.compilers, { ...this.options, useNativeSqlPlanner: false });
623+
}
624+
614625
/**
615626
* Returns a pair of SQL query string and parameter values for the query.
616627
* @param {boolean} [exportAnnotatedSql] - returns annotated sql with not rendered params if true
617628
* @returns {[string, Array<unknown>]}
618629
*/
619630
buildSqlAndParams(exportAnnotatedSql) {
620-
if (getEnv('nativeSqlPlanner')) {
621-
return this.buildSqlAndParamsRust(exportAnnotatedSql);
622-
} else {
623-
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) {
624-
if (this.externalPreAggregationQuery()) { // TODO performance
625-
return this.externalQuery().buildSqlAndParams(exportAnnotatedSql);
631+
if (!this.options.preAggregationQuery && !this.options.disableExternalPreAggregations && this.externalQueryClass) {
632+
if (this.externalPreAggregationQuery()) { // TODO performance
633+
return this.externalQuery().buildSqlAndParams(exportAnnotatedSql);
634+
}
635+
}
636+
637+
if (this.useNativeSqlPlanner) {
638+
let isRelatedToPreAggregation = false;
639+
if (this.options.preAggregationQuery) {
640+
isRelatedToPreAggregation = true;
641+
} else if (!this.options.disableExternalPreAggregations && this.externalQueryClass) {
642+
if (this.externalPreAggregationQuery()) {
643+
isRelatedToPreAggregation = true;
644+
}
645+
} else {
646+
let preAggForQuery =
647+
this.preAggregations.findPreAggregationForQuery();
648+
if (this.options.disableExternalPreAggregations && preAggForQuery && preAggForQuery.preAggregation.external) {
649+
preAggForQuery = undefined;
650+
}
651+
if (preAggForQuery) {
652+
isRelatedToPreAggregation = true;
626653
}
627654
}
628-
return this.compilers.compiler.withQuery(
629-
this,
630-
() => this.cacheValue(
631-
['buildSqlAndParams', exportAnnotatedSql],
632-
() => this.paramAllocator.buildSqlAndParams(
633-
this.buildParamAnnotatedSql(),
634-
exportAnnotatedSql,
635-
this.shouldReuseParams
636-
),
637-
{ cache: this.queryCache }
638-
)
639-
);
655+
656+
if (isRelatedToPreAggregation) {
657+
return this.newQueryNotUseNative().buildSqlAndParams(exportAnnotatedSql);
658+
}
659+
660+
return this.buildSqlAndParamsRust(exportAnnotatedSql);
640661
}
662+
663+
return this.compilers.compiler.withQuery(
664+
this,
665+
() => this.cacheValue(
666+
['buildSqlAndParams', exportAnnotatedSql],
667+
() => this.paramAllocator.buildSqlAndParams(
668+
this.buildParamAnnotatedSql(),
669+
exportAnnotatedSql,
670+
this.shouldReuseParams
671+
),
672+
{ cache: this.queryCache }
673+
)
674+
);
641675
}
642676

643677
buildSqlAndParamsRust(exportAnnotatedSql) {
@@ -3052,6 +3086,7 @@ export class BaseQuery {
30523086
}
30533087

30543088
newSubQueryForCube(cube, options) {
3089+
options = { ...options, useNativeSqlPlanner: false }; // We not use tesseract for pre-aggregations generation yet
30553090
if (this.options.queryFactory) {
30563091
// When dealing with rollup joins, it's crucial to use the correct parameter allocator for the specific cube in use.
30573092
// By default, we'll use BaseQuery, but it's important to note that different databases (Oracle, PostgreSQL, MySQL, Druid, etc.)
@@ -3075,6 +3110,7 @@ export class BaseQuery {
30753110
historyQueries: this.options.historyQueries,
30763111
externalQueryClass: this.options.externalQueryClass,
30773112
queryFactory: this.options.queryFactory,
3113+
useNativeSqlPlanner: this.options.useNativeSqlPlanner,
30783114
...options,
30793115
};
30803116
}

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)