Skip to content

fix(tesseract): Resolve join for hint-less member-expression measures#11136

Open
ovr wants to merge 3 commits into
masterfrom
fix/tesseract-join-hint-member-expression
Open

fix(tesseract): Resolve join for hint-less member-expression measures#11136
ovr wants to merge 3 commits into
masterfrom
fix/tesseract-join-hint-member-expression

Conversation

@ovr

@ovr ovr commented Jun 22, 2026

Copy link
Copy Markdown
Member

A dependency-free member-expression measure such as count(*), in a query with no dimensions or filters to seed the join, resolved an empty join-hint set. That path calls JoinGraph.buildJoin([]), which returns null, and the Rust bridge then fails to deserialize the missing JoinDefinitionStatic ("invalid type: unit value, expected struct JoinDefinitionStatic").

In the multi-fact join grouping, fall back to the measure's own cube when its hint set is empty — but only for real, joinable cubes. View members are left untouched: their join comes from the other query members, and the view itself is not a joinable cube.

A dependency-free member-expression measure such as `count(*)`, in a
query with no dimensions or filters to seed the join, resolved an empty
join-hint set. That path calls `JoinGraph.buildJoin([])`, which returns
null, and the Rust bridge then fails to deserialize the missing
`JoinDefinitionStatic` ("invalid type: unit value, expected struct
JoinDefinitionStatic").

In the multi-fact join grouping, fall back to the measure's own cube when
its hint set is empty — but only for real, joinable cubes. View members
are left untouched: their join comes from the other query members, and
the view itself is not a joinable cube.
@github-actions github-actions Bot added the rust Pull requests that update Rust code label Jun 22, 2026
@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ovr's task in 44s —— View job


Code Review

Verdict: LGTM — focused, well-tested fix. 0 high, 0 medium, 2 low.
  • Read changed files
  • Analyze fix logic in multi_fact_join_groups.rs
  • Review test coverage
  • Check edge cases (views, missing cubes, errors)

Summary

The fix in multi_fact_join_groups.rs:218-222 correctly addresses the root cause: when a measure-expression's hints are empty AND the query has no dimensions/filters to seed the join, falling back to the measure's own cube avoids the null-join path that the bridge can't deserialize. Guarding the fallback by is_view is the right call — views aren't joinable cubes and their join is resolved through other query members.

The new integration test (test_expr_measure_count_star_no_hints) is a clean regression test that exercises exactly the scenario described in the PR body, and the snapshot is minimal.

Low-severity notes

1. .ok() silently swallows lookup errors (multi_fact_join_groups.rs:254-259)

let is_view = query_tools
    .cube_evaluator()
    .cube_from_path(cube_name.clone())
    .ok()
    .and_then(|cube| cube.static_data().is_view)
    .unwrap_or(false);

If cube_from_path fails for any reason other than "cube doesn't exist" (e.g., a future evaluator error), it silently defaults to is_view = false, which then injects a JoinHintItem::Single(<bad name>) and the next join_for_hints call surfaces a less obvious error than the original lookup. Not a blocker since the current cube_from_path only fails on missing cubes, but propagating the error with ? would be a touch more defensive.

2. View detection happens per-measure (multi_fact_join_groups.rs:218-222)

Each empty-hint measure triggers an independent cube_from_path call. In practice multi-measure queries with all-empty hints are vanishingly rare so this is fine, but if it ever becomes hot it can be memoized on State.

Nits

  • The doc comment block on lines 211–217 is excellent context — keep that style.
  • fallback_hints_for_measure is a nicely named, easily-testable helper; consider adding a tiny unit test in the existing mod tests block (one case for view, one for non-view) so the policy is locked in without spinning up the integration harness.

@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ovr's task in 37s —— View job


Code Review

Verdict: LGTM — focused fix with a tight regression test. 0 high, 0 medium, 2 low.
  • Read changed files
  • Analyze fallback logic in multi_fact_join_groups.rs
  • Review test + snapshot coverage
  • Check edge cases (views, missing cubes, errors)

Summary

The fix in multi_fact_join_groups.rs:208-222 correctly addresses the root cause described in the PR body: a dependency-free measure expression (COUNT(*)) resolves to empty hints, and with no dimensions/filters to seed the join the planner used to call JoinGraph.buildJoin([]) and emit a null JoinDefinitionStatic that the bridge couldn't deserialize. Seeding the fallback from the measure's owning cube is the right correction, and gating it by is_view is exactly the subtlety that needs to be there — views are not joinable cubes and their join comes from the other query members.

The new integration test test_expr_measure_count_star_no_hints (member_expressions.rs:147-172) hits the exact scenario from the PR description: a single measure expression with no dimensions/filters and no member dependencies. The snapshot is minimal (SELECT count(*) FROM orders), which is the right output.

Low-severity notes

1. .ok() silently swallows lookup errors (multi_fact_join_groups.rs:246-258)

let is_view = query_tools
    .cube_evaluator()
    .cube_from_path(cube_name.clone())
    .ok()
    .and_then(|cube| cube.static_data().is_view)
    .unwrap_or(false);

If cube_from_path ever fails for a reason other than "cube doesn't exist", this silently defaults to is_view = false and emits a JoinHintItem::Single(<bad name>). The next join_for_hints call will then surface a much less obvious error than the original lookup. Not a blocker since current cube_from_path only fails on missing cubes, but propagating with ? would be more defensive — fallback_hints_for_measure already returns Result<JoinHints, CubeError>.

2. View detection happens per-measure (multi_fact_join_groups.rs:208-222)

Each empty-hint measure triggers an independent cube_from_path call. In practice multi-measure queries with all-empty hints are vanishingly rare so this is fine; if it ever becomes hot it can be memoized on State.

Nits

  • The doc comment on the helper (lines 239–241) clearly explains the policy — keep that style.
  • A tiny unit test on fallback_hints_for_measure (one view, one non-view case) would lock the policy in without spinning up the integration harness, but the integration coverage is sufficient.
    · fix/tesseract-join-hint-member-expression

@claude

claude Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ovr's task in 32s —— View job


Code Review

Verdict: LGTM — focused fix with a tight regression test. 0 high, 0 medium, 2 low.

Review details
  • Read changed files
  • Analyze fallback logic in multi_fact_join_groups.rs
  • Review test + snapshot coverage
  • Check edge cases (views, missing cubes, errors)

Summary

The fix in multi_fact_join_groups.rs:208-222 correctly addresses the root cause from the PR body: a dependency-free measure expression (COUNT(*)) resolves to empty hints, and with no dimensions/filters to seed the join the planner used to call JoinGraph.buildJoin([]) and emit a null JoinDefinitionStatic that the bridge couldn't deserialize. Seeding the fallback from the measure's owning cube is the right correction, and gating it by is_view is the subtlety that needs to be there — views are not joinable cubes and their join comes from other query members.

The new integration test test_expr_measure_count_star_no_hints (member_expressions.rs:147-172) targets exactly the scenario from the PR description, and the snapshot (SELECT count(*) FROM orders) is minimal.

Low-severity notes

1. .ok() silently swallows lookup errors (multi_fact_join_groups.rs:246-251)

let is_view = query_tools
    .cube_evaluator()
    .cube_from_path(cube_name.clone())
    .ok()
    .and_then(|cube| cube.static_data().is_view)
    .unwrap_or(false);

If cube_from_path ever fails for a reason other than "cube doesn't exist", this silently defaults to is_view = false and emits a JoinHintItem::Single(<bad name>). The next join_for_hints call then surfaces a much less obvious error than the original lookup. Not a blocker since current cube_from_path only fails on missing cubes, but propagating with ? would be more defensive — fallback_hints_for_measure already returns Result<JoinHints, CubeError>.

2. View detection happens per-measure (multi_fact_join_groups.rs:208-222)

Each empty-hint measure triggers an independent cube_from_path call. In practice multi-measure queries with all-empty hints are vanishingly rare so this is fine; if it ever becomes hot it can be memoized on State.

Nits

  • The doc comment on the helper (lines 239–241) clearly explains the policy — keep that style.
  • A tiny unit test on fallback_hints_for_measure (one view, one non-view case) would lock the policy in without spinning up the integration harness, but the integration coverage is sufficient.
· `fix/tesseract-join-hint-member-expression`

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.12%. Comparing base (bd8b6e6) to head (0aaf4ab).

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #11136      +/-   ##
==========================================
- Coverage   83.63%   79.12%   -4.51%     
==========================================
  Files         256      473     +217     
  Lines       79019    96514   +17495     
  Branches        0     3558    +3558     
==========================================
+ Hits        66085    76368   +10283     
- Misses      12934    19628    +6694     
- Partials        0      518     +518     
Flag Coverage Δ
cube-backend 58.74% <ø> (?)
cubesql 83.63% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants