Skip to content

Commit f6f5731

Browse files
committed
add support for transitive joins in tesseract
1 parent e75e3db commit f6f5731

File tree

3 files changed

+56
-3
lines changed

3 files changed

+56
-3
lines changed

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

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,53 @@ export class BaseQuery {
386386
}
387387
}
388388

389+
/**
390+
* Is used by native
391+
* This function follows the same logic as in this.collectJoinHints()
392+
* @private
393+
* @param {Array<(Array<string> | string)>} hints
394+
* @return {import('../compiler/JoinGraph').FinishedJoinTree}
395+
*/
396+
joinTreeForHints(hints) {
397+
const explicitJoinHintMembers = new Set(hints.filter(j => Array.isArray(j)).flat());
398+
const queryJoinMaps = this.queryJoinMap();
399+
const newCollectedHints = [];
400+
401+
const constructJH = () => R.uniq(this.enrichHintsWithJoinMap([
402+
...newCollectedHints,
403+
...hints,
404+
],
405+
queryJoinMaps));
406+
407+
let prevJoin = null;
408+
let newJoin = null;
409+
410+
// Safeguard against infinite loop in case of cyclic joins somehow managed to slip through
411+
let cnt = 0;
412+
let newJoinHintsCollectedCnt;
413+
414+
do {
415+
const allJoinHints = constructJH();
416+
prevJoin = newJoin;
417+
newJoin = this.joinGraph.buildJoin(allJoinHints);
418+
const allJoinHintsFlatten = new Set(allJoinHints.flat());
419+
const joinMembersJoinHints = this.collectJoinHintsFromMembers(this.joinMembersFromJoin(newJoin));
420+
421+
const iterationCollectedHints = joinMembersJoinHints.filter(j => !allJoinHintsFlatten.has(j));
422+
newJoinHintsCollectedCnt = iterationCollectedHints.length;
423+
cnt++;
424+
if (newJoin) {
425+
newCollectedHints.push(...joinMembersJoinHints.filter(j => !explicitJoinHintMembers.has(j)));
426+
}
427+
} while (newJoin?.joins.length > 0 && !this.isJoinTreesEqual(prevJoin, newJoin) && cnt < 10000 && newJoinHintsCollectedCnt > 0);
428+
429+
if (cnt >= 10000) {
430+
throw new UserError('Can not construct joins for the query, potential loop detected');
431+
}
432+
433+
return newJoin;
434+
}
435+
389436
cacheValue(key, fn, { contextPropNames, inputProps, cache } = {}) {
390437
const currentContext = this.safeEvaluateSymbolContext();
391438
if (contextPropNames) {
@@ -450,7 +497,7 @@ export class BaseQuery {
450497

451498
for (const member of queryMembers) {
452499
const memberCube = member.cube?.();
453-
if (memberCube?.isView && !joinMaps[memberCube.name]) {
500+
if (memberCube?.isView && !joinMaps[memberCube.name] && memberCube.joinMap) {
454501
joinMaps[memberCube.name] = memberCube.joinMap;
455502
}
456503
}

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::base_query_options::FilterItem;
2+
use super::join_definition::{JoinDefinition, NativeJoinDefinition};
23
use super::driver_tools::{DriverTools, NativeDriverTools};
34
use super::filter_group::{FilterGroup, NativeFilterGroup};
45
use super::filter_params::{FilterParams, NativeFilterParams};
@@ -14,6 +15,7 @@ use cubenativeutils::wrappers::NativeObjectHandle;
1415
use cubenativeutils::CubeError;
1516
use std::any::Any;
1617
use std::rc::Rc;
18+
use crate::cube_bridge::join_hints::JoinHintItem;
1719

1820
#[nativebridge::native_bridge]
1921
pub trait BaseTools {
@@ -53,4 +55,8 @@ pub trait BaseTools {
5355
cube_name: String,
5456
name: String,
5557
) -> Result<String, CubeError>; //TODO move to rust
58+
59+
fn join_tree_for_hints(
60+
&self,
61+
hints: Vec<JoinHintItem>) -> Result<Rc<dyn JoinDefinition>, CubeError>;
5662
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ impl QueryProperties {
613613
let join = query_tools
614614
.cached_data_mut()
615615
.join_by_hints(dimension_and_filter_join_hints_concat.clone(), |hints| {
616-
query_tools.join_graph().build_join(hints)
616+
query_tools.base_tools().join_tree_for_hints(hints)
617617
})?;
618618
vec![(Vec::new(), join)]
619619
}
@@ -628,7 +628,7 @@ impl QueryProperties {
628628
.into_iter()
629629
.chain(dimension_and_filter_join_hints_concat.clone().into_iter())
630630
.collect::<Vec<_>>(),
631-
|hints| query_tools.join_graph().build_join(hints),
631+
|hints| query_tools.base_tools().join_tree_for_hints(hints),
632632
)?;
633633
Ok((vec![m.clone()], join))
634634
})

0 commit comments

Comments
 (0)