Skip to content

Commit bb08799

Browse files
committed
implement smart join tree comparator and loop detector
1 parent 209f995 commit bb08799

File tree

1 file changed

+36
-1
lines changed

1 file changed

+36
-1
lines changed

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,12 +441,47 @@ export class BaseQuery {
441441
};
442442

443443
let prevJoins = this.join;
444+
let prevJoinMembersJoinHints = joinMembersJoinHints;
444445
let newJoin = this.joinGraph.buildJoin(constructJH());
445446

446-
while (newJoin?.joins.length > 0 && !R.equals(prevJoins, newJoin)) {
447+
const isOrderPreserved = (base, updated) => {
448+
const common = base.filter(value => updated.includes(value));
449+
const bFiltered = updated.filter(value => common.includes(value));
450+
451+
return common.every((x, i) => x === bFiltered[i]);
452+
};
453+
454+
const isJoinTreesEqual = (a, b) => {
455+
if (!a || !b || a.root !== b.root || a.joins.length !== b.joins.length) {
456+
return false;
457+
}
458+
459+
// We don't care about the order of joins on the same level, so
460+
// we can compare them as sets.
461+
const aJoinsSet = new Set(a.joins.map(j => `${j.originalFrom}->${j.originalTo}`));
462+
const bJoinsSet = new Set(b.joins.map(j => `${j.originalFrom}->${j.originalTo}`));
463+
464+
if (aJoinsSet.size !== bJoinsSet.size) {
465+
return false;
466+
}
467+
468+
for (const val of aJoinsSet) {
469+
if (!bJoinsSet.has(val)) {
470+
return false;
471+
}
472+
}
473+
474+
return true;
475+
};
476+
477+
while (newJoin?.joins.length > 0 && !isJoinTreesEqual(prevJoins, newJoin)) {
447478
prevJoins = newJoin;
448479
joinMembersJoinHints = this.collectJoinHintsFromMembers(this.joinMembersFromJoin(newJoin));
480+
if (!isOrderPreserved(prevJoinMembersJoinHints, joinMembersJoinHints)) {
481+
throw new UserError(`Can not construct joins for the query, potential loop detected: ${prevJoinMembersJoinHints.join('->')} vs ${joinMembersJoinHints.join('->')}`);
482+
}
449483
newJoin = this.joinGraph.buildJoin(constructJH());
484+
prevJoinMembersJoinHints = joinMembersJoinHints;
450485
}
451486

452487
this.collectedJoinHints = R.uniq(constructJH());

0 commit comments

Comments
 (0)