Skip to content

fix(schema-compiler): Allow dimension-only measure expressions over multiple cubes - #11330

Merged
MazterQyou merged 1 commit into
masterfrom
schema-compiler/dimension-only-measure-multiple-cube
Jul 22, 2026
Merged

fix(schema-compiler): Allow dimension-only measure expressions over multiple cubes#11330
MazterQyou merged 1 commit into
masterfrom
schema-compiler/dimension-only-measure-multiple-cube

Conversation

@MazterQyou

Copy link
Copy Markdown
Member

Check List

  • Tests have been run in packages where changes have been made if available
  • Linter has been run for changed code
  • Tests for the changes have been added if not covered yet
  • Docs have been added / updated if required

Description of Changes Made

This PR allows dimension-only member-expression measures referencing dimensions from multiple joined cubes when none of the referenced cubes is on the multiplied side of a join (both tesseract and legacy planner), keeping the error with an actionable message for the row-multiplication case. Related test is included.

…ultiple cubes

Signed-off-by: Alex Qyoun-ae <4062971+MazterQyou@users.noreply.github.com>
@MazterQyou
MazterQyou requested a review from a team as a code owner July 22, 2026 15:22
@github-actions github-actions Bot added rust Pull requests that update Rust code javascript Pull requests that update Javascript code labels Jul 22, 2026
@claude

claude Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 47s —— View job


Code Review

Verdict: LGTM overall — 0 high, 0 medium, 1 low (style nit)

Details

Todos

  • Read the diff and both modified sources
  • Cross-check JS and Rust behavior for parity
  • Review new test coverage
  • Post inline notes

Summary

The fix is small, symmetric across the legacy JS planner (BaseQuery.js) and Tesseract's Rust collector (multiplied_measures_collector.rs), and correctly narrows the previous blanket error to only the case where at least one referenced cube sits on the multiplied side of a join. When none do, the expression is evaluated as a plain non-multiplied measure on top of the join tree — consistent with the existing zero-cube branch. Error message is actionable and matches between planners.

Test coverage

Good coverage of the three interesting cases:

  1. Multi-cube dimension-only expression with no multiplication → runs and returns expected value.
  2. Same expression + a dimension that puts a referenced cube on the multiplied side → throws with row multiplication message.
  3. Same expression + a one_to_many sibling measure → Tesseract handles it, legacy planner still errors (documented via if (getEnv('nativeSqlPlanner'))).

Nice touch keeping the legacy-error assertion as a separate it block rather than silently skipping — it documents the divergence.

Findings

  • [low] Collapsible else { if ... } in Rustmultiplied_measures_collector.rs:141-160. Clippy's collapsible_else_if lint will fire on the } else { if ... } shape; collapse to } else if ... { to keep CI clean. Left as an inline comment.

Security / performance

No new user input paths, no query surface changes beyond loosening a previously-thrown error. .some(...) / .iter().any(...) over the small cubeNamesForMeasure slice is negligible.

Docs

No user-facing API changes; error message is self-explanatory. Docs update not required.

• Branch: `schema-compiler/dimension-only-measure-multiple-cube`

Comment on lines 141 to 160
} else {
return Err(CubeError::user(format!(
"Expected single cube for dimension-only measure {}, got {:?}",
node.full_name(),
cube_names
)));
if cube_names
.iter()
.any(|cube_name| join.is_multiplied(cube_name))
{
return Err(CubeError::user(format!(
"Dimension-only measure {} references cubes {:?} that lead to row multiplication. Please rewrite it using sub query.",
node.full_name(),
cube_names
)));
}
// Dimensions from several cubes, but none of them is on the
// multiplied side of a join - safe to evaluate the expression
// on top of the join tree as a regular measure.
vec![MeasureResult {
cube_name: node.cube_name().clone(),
measure: node.clone(),
multiplied: false,
}]
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this } else { if ... { ... } ... } will trigger clippy's collapsible_else_if lint. Consider collapsing to } else if cube_names.iter().any(...) { ... } else { ... } to keep the block flat and appease clippy in CI.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Cube’s schema compiler/planners to allow dimension-only member-expression measures to reference dimensions from multiple joined cubes when those references do not introduce row-multiplication risk, while still failing with an actionable error when row multiplication would occur.

Changes:

  • Updated the Tesseract (Rust) multiplied-measures collector to allow multi-cube dimension-only expressions when none of the referenced cubes are multiplied.
  • Updated the legacy planner (BaseQuery.js) to apply the same rule and preserve a clear row-multiplication error when unsafe.
  • Added Postgres integration tests covering the safe multi-cube case, the unsafe case triggered by query dimensions, and planner-specific behavior when combined with one-to-many measures.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
rust/cube/cubesqlplanner/cubesqlplanner/src/planner/collectors/multiplied_measures_collector.rs Allows multi-cube dimension-only member expressions when referenced cubes are not multiplied; keeps an error for row multiplication.
packages/cubejs-schema-compiler/src/adapter/BaseQuery.js Mirrors the same multi-cube allowance/row-multiplication error behavior for the legacy JS planner.
packages/cubejs-schema-compiler/test/integration/postgres/member-expression.test.ts Adds integration coverage for safe/unsafe multi-cube dimension-only expressions across planner modes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +146 to +150
return Err(CubeError::user(format!(
"Dimension-only measure {} references cubes {:?} that lead to row multiplication. Please rewrite it using sub query.",
node.full_name(),
cube_names
)));
Comment on lines +2192 to +2194
if (cubeNamesForMeasure.some(cubeName => this.multipliedJoinRowResult(cubeName))) {
throw new Error(`Dimension-only measure ${measureName} references cubes (${cubeNamesForMeasure}) that lead to row multiplication. Please rewrite it using sub query.`);
}
@MazterQyou
MazterQyou merged commit 69e8728 into master Jul 22, 2026
164 of 167 checks passed
@MazterQyou
MazterQyou deleted the schema-compiler/dimension-only-measure-multiple-cube branch July 22, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update Javascript code rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants