Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DEBUG] add integration test to reproduce missing column for multipli… #9260

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
59 changes: 50 additions & 9 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,7 @@ export class BaseQuery {
] : [])
.concat(
R.pipe(
// TODO for member expressions this can be misleading: it can be a name of view, which does not have PK
R.groupBy(m => m.cube().name),
R.toPairs,
R.map(
Expand Down Expand Up @@ -1054,18 +1055,29 @@ export class BaseQuery {
outerMeasuresJoinFullKeyQueryAggregate(innerMembers, outerMembers, toJoin) {
const renderedReferenceContext = {
renderedReference: R.pipe(
R.map(m => [m.measure || m.dimension, m.aliasName()]),
R.map(m => {
let memberPath;
if (m.measure) {
memberPath = typeof m.measure === 'string' ? m.measure : this.cubeEvaluator.pathFromArray([m.expressionCubeName, m.expressionName]);
} else {
memberPath = typeof m.dimension === 'string' ? m.dimension : this.cubeEvaluator.pathFromArray([m.expressionCubeName, m.expressionName]);
}
return [memberPath, m.aliasName()];
}),
R.fromPairs,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
)(innerMembers),
};

const join = R.drop(1, toJoin)
.map(
(q, i) => (this.dimensionAliasNames().length ?
`INNER JOIN ${this.wrapInParenthesis((q))} as q_${i + 1} ON ${this.dimensionsJoinCondition(`q_${i}`, `q_${i + 1}`)}` :
`, ${this.wrapInParenthesis(q)} as q_${i + 1}`),
).join('\n');
.map((q, i) => {
console.log("outerMeasuresJoinFullKeyQueryAggregate generating join, this.dimensionAliasNames()", this.dimensionAliasNames());
return this.dimensionAliasNames().length
? `INNER JOIN ${this.wrapInParenthesis(q)} as q_${
i + 1
} ON ${this.dimensionsJoinCondition(`q_${i}`, `q_${i + 1}`)}`
: `, ${this.wrapInParenthesis(q)} as q_${i + 1}`;
})
.join("\n");

const columnsToSelect = this.evaluateSymbolSqlWithContext(
() => this.dimensionColumns('q_0').concat(outerMembers.map(m => m.selectColumns())).join(', '),
Expand Down Expand Up @@ -1120,9 +1132,22 @@ export class BaseQuery {
return allMemberChildren[m]?.some(c => hasMultiStageMembers(c)) || false;
};

// This is a bit of a hack
// For now object can only come from dimension-only measure branch inside collectRootMeasureToHieararchy
// So just filters those out, and treat them as regular measures
// TODO teach rest of code here to work with member expressions
const dimensionOnlyMeasures = Object.values(measureToHierarchy)
.flat()
.map((m) => m.measure)
.filter((m) => typeof m === 'object');

const measuresToRender = (multiplied, cumulative) => R.pipe(
R.values,
R.flatten,
R.filter(
// To filter out member expressions
m => typeof m.measure === 'string'
),
R.filter(
m => m.multiplied === multiplied && this.newMeasure(m.measure).isCumulative() === cumulative && !hasMultiStageMembers(m.measure)
),
Expand All @@ -1131,8 +1156,11 @@ export class BaseQuery {
R.map(m => this.newMeasure(m))
);

const multipliedMeasures = measuresToRender(true, false)(measureToHierarchy);
const regularMeasures = measuresToRender(false, false)(measureToHierarchy);
// TODO dimensionOnlyMeasures should belong to proper subquery depending on join tree, not to regular measures
const multipliedMeasures = measuresToRender(true, false)(measureToHierarchy)
// .concat(dimensionOnlyMeasures);
const regularMeasures = measuresToRender(false, false)(measureToHierarchy)
.concat(dimensionOnlyMeasures);

const cumulativeMeasures =
R.pipe(
Expand Down Expand Up @@ -1709,6 +1737,19 @@ export class BaseQuery {
const cubeName = m.expressionCubeName ? `\`${m.expressionCubeName}\` ` : '';
throw new UserError(`The query contains \`COUNT(*)\` expression but cube/view ${cubeName}is missing \`count\` measure`);
}
if (collectedMeasures.length === 0 && m.isMemberExpression) {
// `m` is member expression measure, but does not reference any other measure
// Consider this dimensions-only measure. This can happen at least in 2 cases:
// 1. Ad-hoc aggregation over dimension: SELECT MAX(dim) FROM cube
// 2. Ungrouped query with SQL pushdown will render every column as measure: SELECT dim1 FROM cube WHERE LOWER(dim2) = 'foo';
// Measures like this considered regular: they depend only on dimensions and join tree
// This would return measure object in `measure`, not path
// TODO return measure object for every measure
return [`${m.measure.cubeName}.${m.measure.name}`, [{
multiplied: false,
measure: m,
}]];
}
return [typeof m.measure === 'string' ? m.measure : `${m.measure.cubeName}.${m.measure.name}`, collectedMeasures];
}));
}
Expand Down
Loading
Loading