Skip to content

Commit 6a640a6

Browse files
authored
feat(tesseract): Support use_original_sql_pre_aggregations in rollup (cube-js#11146)
1 parent c678c58 commit 6a640a6

10 files changed

Lines changed: 131 additions & 2 deletions

File tree

packages/cubejs-backend-native/test/bridge/object-bridges-coverage.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const BRIDGES: BridgeSpec[] = [
6262
'timezone',
6363
'total_query',
6464
'ungrouped',
65+
'use_original_sql_pre_aggregations_in_pre_aggregation',
6566
],
6667
},
6768
{

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,7 @@ export class BaseQuery {
954954
ungrouped: this.options.ungrouped,
955955
exportAnnotatedSql: exportAnnotatedSql === true,
956956
preAggregationQuery: this.options.preAggregationQuery,
957+
useOriginalSqlPreAggregationsInPreAggregation: this.options.useOriginalSqlPreAggregationsInPreAggregation,
957958
preAggregationId: this.options.preAggregationId || null,
958959
totalQuery: this.options.totalQuery,
959960
joinHints: this.options.joinHints,

rust/cube/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_query_options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ pub struct BaseQueryOptionsStatic {
206206
pub export_annotated_sql: bool,
207207
#[serde(rename = "preAggregationQuery")]
208208
pub pre_aggregation_query: Option<bool>,
209+
#[serde(rename = "useOriginalSqlPreAggregationsInPreAggregation")]
210+
pub use_original_sql_pre_aggregations_in_pre_aggregation: Option<bool>,
209211
#[serde(rename = "totalQuery")]
210212
pub total_query: Option<bool>,
211213
#[serde(rename = "cubestoreSupportMultistage")]

rust/cube/cubesqlplanner/cubesqlplanner/src/planner/query_properties.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ pub struct QueryProperties {
167167
ungrouped: bool,
168168
#[builder(default)]
169169
pre_aggregation_query: bool,
170+
/// When building a rollup pre-aggregation, source it from the cube's
171+
/// `originalSql` pre-aggregation table instead of the raw cube SQL.
172+
#[builder(default)]
173+
use_original_sql_pre_aggregations_in_pre_aggregation: bool,
170174
#[builder(default)]
171175
total_query: bool,
172176
#[builder(default = Rc::new(JoinHints::new()))]
@@ -354,6 +358,10 @@ impl QueryProperties {
354358
self.pre_aggregation_query
355359
}
356360

361+
pub fn use_original_sql_pre_aggregations_in_pre_aggregation(&self) -> bool {
362+
self.use_original_sql_pre_aggregations_in_pre_aggregation
363+
}
364+
357365
pub fn disable_external_pre_aggregations(&self) -> bool {
358366
self.disable_external_pre_aggregations
359367
}
@@ -1144,6 +1152,7 @@ impl PartialEq for QueryProperties {
11441152
ungrouped,
11451153
ignore_cumulative,
11461154
pre_aggregation_query,
1155+
use_original_sql_pre_aggregations_in_pre_aggregation,
11471156
total_query,
11481157
allow_multi_stage,
11491158
disable_external_pre_aggregations,
@@ -1169,6 +1178,8 @@ impl PartialEq for QueryProperties {
11691178
&& *ungrouped == other.ungrouped
11701179
&& *ignore_cumulative == other.ignore_cumulative
11711180
&& *pre_aggregation_query == other.pre_aggregation_query
1181+
&& *use_original_sql_pre_aggregations_in_pre_aggregation
1182+
== other.use_original_sql_pre_aggregations_in_pre_aggregation
11721183
&& *total_query == other.total_query
11731184
&& *allow_multi_stage == other.allow_multi_stage
11741185
&& *disable_external_pre_aggregations == other.disable_external_pre_aggregations

rust/cube/cubesqlplanner/cubesqlplanner/src/planner/query_properties_compiler.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ impl QueryPropertiesCompiler {
8484
.and_then(|v| v.parse::<usize>().ok());
8585
let ungrouped = options.static_data().ungrouped.unwrap_or(false);
8686
let pre_aggregation_query = options.static_data().pre_aggregation_query.unwrap_or(false);
87+
let use_original_sql_pre_aggregations_in_pre_aggregation = options
88+
.static_data()
89+
.use_original_sql_pre_aggregations_in_pre_aggregation
90+
.unwrap_or(false);
8791
let total_query = options.static_data().total_query.unwrap_or(false);
8892
let disable_external_pre_aggregations =
8993
options.static_data().disable_external_pre_aggregations;
@@ -109,6 +113,9 @@ impl QueryPropertiesCompiler {
109113
.offset(offset)
110114
.ungrouped(ungrouped)
111115
.pre_aggregation_query(pre_aggregation_query)
116+
.use_original_sql_pre_aggregations_in_pre_aggregation(
117+
use_original_sql_pre_aggregations_in_pre_aggregation,
118+
)
112119
.total_query(total_query)
113120
.query_join_hints(query_join_hints)
114121
.disable_external_pre_aggregations(disable_external_pre_aggregations)

rust/cube/cubesqlplanner/cubesqlplanner/src/planner/top_level_planner.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,17 @@ impl TopLevelPlanner {
5050
};
5151

5252
let templates = self.query_tools.plan_sql_templates(is_external)?;
53-
5453
let physical_plan_builder =
5554
PhysicalPlanBuilder::new(self.query_tools.query_tools().clone(), templates.clone());
56-
let original_sql_pre_aggregations = if !self.request.is_pre_aggregation_query() {
55+
56+
// Substitute a cube's base SQL with its `originalSql` pre-aggregation table when:
57+
// reading (regular query), or building a rollup that opted in via
58+
// `useOriginalSqlPreAggregationsInPreAggregation`.
59+
let original_sql_pre_aggregations = if !self.request.is_pre_aggregation_query()
60+
|| self
61+
.request
62+
.use_original_sql_pre_aggregations_in_pre_aggregation()
63+
{
5764
OriginalSqlCollector::new(self.query_tools.query_tools().clone())
5865
.collect(&optimized_plan)?
5966
} else {

rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/base_query_options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub struct MockBaseQueryOptions {
6565
#[builder(default)]
6666
pre_aggregation_query: Option<bool>,
6767
#[builder(default)]
68+
use_original_sql_pre_aggregations_in_pre_aggregation: Option<bool>,
69+
#[builder(default)]
6870
total_query: Option<bool>,
6971
#[builder(default)]
7072
cubestore_support_multistage: Option<bool>,
@@ -93,6 +95,7 @@ impl_static_data!(
9395
ungrouped,
9496
export_annotated_sql,
9597
pre_aggregation_query,
98+
use_original_sql_pre_aggregations_in_pre_aggregation,
9699
total_query,
97100
cubestore_support_multistage,
98101
disable_external_pre_aggregations,

rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/cube_bridge/yaml/base_query_options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub struct YamlBaseQueryOptions {
3030
#[serde(default)]
3131
pub pre_aggregation_query: Option<bool>,
3232
#[serde(default)]
33+
pub use_original_sql_pre_aggregations_in_pre_aggregation: Option<bool>,
34+
#[serde(default)]
3335
pub total_query: Option<bool>,
3436
#[serde(default)]
3537
pub cubestore_support_multistage: Option<bool>,

rust/cube/cubesqlplanner/cubesqlplanner/src/test_fixtures/test_utils/test_context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ impl TestContext {
443443
.ungrouped(yaml_options.ungrouped)
444444
.export_annotated_sql(yaml_options.export_annotated_sql.unwrap_or(false))
445445
.pre_aggregation_query(yaml_options.pre_aggregation_query)
446+
.use_original_sql_pre_aggregations_in_pre_aggregation(
447+
yaml_options.use_original_sql_pre_aggregations_in_pre_aggregation,
448+
)
446449
.total_query(yaml_options.total_query)
447450
.cubestore_support_multistage(yaml_options.cubestore_support_multistage)
448451
.disable_external_pre_aggregations(

rust/cube/cubesqlplanner/cubesqlplanner/src/tests/integration/pre_aggregations/sql_generation.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::test_fixtures::cube_bridge::{
1010
MockMemberExpressionDefinition, MockMemberSql, MockSchema,
1111
};
1212
use crate::test_fixtures::test_utils::TestContext;
13+
use cubenativeutils::CubeError;
1314
use indoc::indoc;
1415
use std::rc::Rc;
1516

@@ -1195,3 +1196,94 @@ fn test_count_distinct_approx_multistage_pre_agg_reads_cardinality() {
11951196
sql
11961197
);
11971198
}
1199+
1200+
// A cube `foo` whose `originalSql` pre-aggregation (`main`) materializes its base
1201+
// SQL, plus a `second` rollup. The mock renders the originalSql pre-agg table as
1202+
// `foo__main` and the raw cube SQL references `foo_table`.
1203+
fn use_original_sql_pre_aggregations_in_pre_aggregation_schema() -> Result<MockSchema, CubeError> {
1204+
MockSchema::from_yaml(indoc! {"
1205+
cubes:
1206+
- name: foo
1207+
sql: SELECT * FROM foo_table
1208+
dimensions:
1209+
- name: time
1210+
type: time
1211+
sql: timestamp
1212+
measures:
1213+
- name: total
1214+
type: sum
1215+
sql: amount
1216+
pre_aggregations:
1217+
- name: main
1218+
type: originalSql
1219+
- name: second
1220+
type: rollup
1221+
measures:
1222+
- total
1223+
time_dimension: time
1224+
granularity: day
1225+
"})
1226+
}
1227+
1228+
// Building a rollup with `useOriginalSqlPreAggregations` must source it from the cube's
1229+
// `originalSql` pre-aggregation table instead of the raw cube SQL
1230+
#[test]
1231+
fn test_rollup_build_with_use_original_sql_pre_aggregations_in_pre_aggregation_reads_original_sql_pre_agg(
1232+
) -> Result<(), CubeError> {
1233+
let ctx = TestContext::new(use_original_sql_pre_aggregations_in_pre_aggregation_schema()?)?;
1234+
1235+
let query_yaml = indoc! {"
1236+
measures:
1237+
- foo.total
1238+
time_dimensions:
1239+
- dimension: foo.time
1240+
granularity: day
1241+
pre_aggregation_query: true
1242+
use_original_sql_pre_aggregations_in_pre_aggregation: true
1243+
"};
1244+
1245+
let sql = ctx.build_sql(query_yaml)?;
1246+
1247+
assert!(
1248+
sql.contains("foo__main"),
1249+
"Build SQL should source from the originalSql pre-agg table, got:\n{}",
1250+
sql
1251+
);
1252+
assert!(
1253+
!sql.contains("foo_table"),
1254+
"Build SQL should not read the raw cube table when useOriginalSqlPreAggregations is set, got:\n{}",
1255+
sql
1256+
);
1257+
Ok(())
1258+
}
1259+
1260+
// Without the flag, a rollup build reads the raw cube SQL — the originalSql
1261+
// pre-agg table must not be substituted in.
1262+
#[test]
1263+
fn test_rollup_build_without_use_original_sql_pre_aggregations_in_pre_aggregation_reads_raw_table(
1264+
) -> Result<(), CubeError> {
1265+
let ctx = TestContext::new(use_original_sql_pre_aggregations_in_pre_aggregation_schema()?)?;
1266+
1267+
let query_yaml = indoc! {"
1268+
measures:
1269+
- foo.total
1270+
time_dimensions:
1271+
- dimension: foo.time
1272+
granularity: day
1273+
pre_aggregation_query: true
1274+
"};
1275+
1276+
let sql = ctx.build_sql(query_yaml)?;
1277+
1278+
assert!(
1279+
sql.contains("foo_table"),
1280+
"Build SQL should read the raw cube table without useOriginalSqlPreAggregations, got:\n{}",
1281+
sql
1282+
);
1283+
assert!(
1284+
!sql.contains("foo__main"),
1285+
"Build SQL should not source from the originalSql pre-agg table without the flag, got:\n{}",
1286+
sql
1287+
);
1288+
Ok(())
1289+
}

0 commit comments

Comments
 (0)