Skip to content

[pull] master from cube-js:master - #657

Merged
pull[bot] merged 2 commits into
code:masterfrom
cube-js:master
Aug 10, 2026
Merged

[pull] master from cube-js:master#657
pull[bot] merged 2 commits into
code:masterfrom
cube-js:master

Conversation

@pull

@pull pull Bot commented Aug 10, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? πŸ’– Please sponsor : )

waralexrom and others added 2 commits August 10, 2026 17:32
…11514)

* fix(tesseract): report multi-stage reads of out-of-grain dimensions

A multi-stage member whose SQL reads a dimension that is not part of the
grain it is computed at emitted SQL referencing the dimension's own cube
alias, which the CTE's FROM never brings into scope. Postgres rejects such
a query with "missing FROM-clause entry"; nothing reported it at plan time.

Report the member and the dimension where a multi-stage member's deps are
decomposed into children, naming the `grain.include` entry that makes the
model plan.

Reachability mirrors reference resolution: a member the source exposes as a
column stops the walk, anything else is reachable only if every member its
own SQL reads is, and a dimension that also reads a raw cube column is
unreachable regardless of what its member deps resolve to. Only the slots
that reach rendered SQL are considered β€” `drill_filters` are never emitted
and a `mask` only for masked members, so neither can put a column
requirement on a CTE.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(tesseract): apply the grain check to masks that are actually applied

A mask reaches the rendered SQL for the members it is applied to, so a mask
reading a dimension outside the stage grain emits the same dangling cube
alias β€” for masked tenants only, which is where it is hardest to notice.
Excluding `mask_sql` unconditionally left that case unreported.

Thread the masking predicate through the reachability walk so the mask slot
counts as rendered exactly when the member is masked.

Also pin two behaviours that were decisions rather than accidents: a
`grain.include` on a child widens that child's leaf and not the columns its
CTE projects, so it does not satisfy a parent reading the dimension; and the
CASE-SWITCH path plans branch dependencies as their own CTEs, which is why
it skips the check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(tesseract): execute the multi-stage grain plans against postgres

The plans the grain check lets through were asserted on substrings only, so
nothing established that they run. Execute the six of them and pin the result
sets: the declared-grain measure returns the first-half-of-month totals
(420 / 650 / 850 against the seed), and the derived-dimension, query-grain,
keys-side, drill-filter and mask shapes return their expected rows.

The keys-side result carries the whole month against the `completed` row
rather than the `completed` total. That follows from `reduce_by` collapsing
the measure below the status grain, so the test says so β€” the number reads
like a mistake otherwise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(tesseract): pin the mask render path and make the snapshots total

Row order: four of the executed snapshots pinned an order no ORDER BY
determines β€” the queries group by a dimension and the month while ordering by
the dimension alone, so the committed rows were hash-aggregate output. Name
every grouped dimension in `order:`, as the neighbouring suite does.

Mask coverage: nothing established the premise the mask branch rests on β€” that
an applied mask is rendered inside the multi-stage CTE. It is, and the new
test pins it against the CTE column.

Writing that test turned up a limitation worth recording: an unconditional
mask replaces the member's aggregate, so its dimension read sits outside any
aggregate and has to be in the stage's own GROUP BY. A declared leaf grain
puts the column in the source but not in that GROUP BY, and the database
rejects the result. The reachability check asks only whether a column exists,
so it does not tell the two apart β€” noted on the test and on the mask branch.

Also compare the masked-dimension measure against a literal the seed matches,
so its result set discriminates instead of being twelve zeros.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s sql (#11517)

* feat(tesseract): push a segment named in FILTER_PARAMS into the cube's sql

`FILTER_PARAMS.<cube>.<segment>.filter(...)` compiled without error but
rendered `1 = 1`, leaving the segment predicate in the outer WHERE after
the joins. On a cube wrapping a large union scan that predicate is what
makes the scan viable, so the query never completes.

A segment is compared to no value, so the `filter()` argument is the whole
predicate rather than the left side of one. It is stated there rather than
taken from the segment's own `sql`, which prefixes its columns with the
cube β€” not in scope inside the sql that builds that cube, the same reason a
dimension binding restates its column.

`find_subtree_for_members` now matches a `Segment` node when a target names
it, which also activates the binding through the existing activity visitor,
and `BaseSegment` renders the binding's column when the filters context
carries one. A segment absent from the query still renders `1 = 1`.

The name is matched along the reference chain, so a view re-exporting a
segment activates the underlying cube's binding. The walk stops at the
first symbol that is not a member expression: a segment whose sql is a bare
reference resolves on to that dimension, whose binding states a column and
not a predicate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(tesseract): degrade a value-taking segment FILTER_PARAMS column to always-true

A column that takes filter values cannot render for a segment, which supplies
none. Raising a user error there fails a query that a model already serving
`1 = 1` used to answer, and it is louder than what the same mismatch gets on
the dimension path, which drops only the restatement inside the SQL. An
argument the binding cannot resolve at all stays silent too, so the arity is
not the place to start reporting.

Both callback variants now degrade. A callback declaring no parameters is
compiled with none, so the raw-callback variant only ever holds a column that
wants values β€” a rest parameter, or a parameter list that could not be read β€”
and invoking it with none interpolated `undefined` into the cube's sql.

The binding is looked up by the path the query asked for before scanning the
chain, which is both what the model meant and what the dimension path does.

Covers the compiled column in the Postgres suite, which the mock DSL cannot
express: the renderer parenthesizes a binding it reached, telling an
activated-then-dropped column apart from an unselected segment's bare `1 = 1`.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Aug 10, 2026
@pull
pull Bot merged commit a0723ac into code:master Aug 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant