Skip to content

fix(cubesql): render MSSQL booleans in scalar and predicate contexts - #11827

Open
davidda wants to merge 10 commits into
cube-js:masterfrom
davidda:feature/codex-mssql-boolean-contexts
Open

davidda wants to merge 10 commits into
cube-js:masterfrom
davidda:feature/codex-mssql-boolean-contexts

Conversation

@davidda

@davidda davidda commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Check List

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

Issue partially addressed by this PR
#11826

Scope and limitations
Boolean dimension metadata does not distinguish BIT-valued SQL from predicate SQL. This PR therefore preserves raw references without guessing their representation or adding model configuration:

  • Predicate-backed dimensions continue working in WHERE, NOT, and CASE WHEN.
  • Bare BIT-backed dimension conditions remain unsupported on this pushdown path; use explicit comparisons.
  • Raw predicate-backed dimensions are not automatically converted for scalar use.
  • Scalarization rejects visible volatile expressions and opaque subqueries that would otherwise be evaluated twice.
  • The existing DataFusion lowering issue for IS [NOT] TRUE/FALSE with NULL inputs remains outside scope and is recorded by an ignored test.

@davidda
davidda requested review from a team as code owners September 9, 2026 13:44
@github-actions github-actions Bot added rust Pull requests that update Rust code javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members. labels Sep 9, 2026
@MazterQyou

Copy link
Copy Markdown
Member

/bot-deep-review

1 similar comment
@ovr

ovr commented Sep 14, 2026

Copy link
Copy Markdown
Member

/bot-deep-review

@MazterQyou

Copy link
Copy Markdown
Member

@davidda could you please rebase this on latest master? Thanks

@davidda
davidda force-pushed the feature/codex-mssql-boolean-contexts branch from f998c76 to 3f6e271 Compare September 15, 2026 06:47
@davidda

davidda commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

@MazterQyou rebase done

@MazterQyou

Copy link
Copy Markdown
Member

/bot-deep-review

@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 12m 6s —— View job


Deep review — approach is sound, no correctness bug found. 0 high, 3 medium, 3 low (6 inline comments).

Full review

Todo

  • Read issue MSSQL SQL API pushdown emits boolean literals and predicates in invalid contexts #11826 and confirm the fix covers the reported symptom
  • Read all 8 changed files in full, plus surrounding code
  • Trace every predicate flag through to its call site
  • Audit is_sql_predicate against the Expr variants the wrapper actually renders
  • Check template consumers: cubesql vs Tesseract vs JS segments/pre-aggs
  • Check for parameter-placeholder breakage from the duplicated {{ expr }}
  • Review test coverage and wiring
  • Check existing review threads (none — no duplicates to skip, nothing stale to resolve)
  • Post inline comments

Verdict

The design holds up. is_sql_predicate classifies by emitted SQL shape rather than DataFusion type, and the conversion only fires on a mismatch — that's the right seam, and the doc comment at wrapper.rs:2411 says so in two lines.

I traced every context the flag reaches, looking specifically for a slot where predicate=true could be applied to a non-boolean expression (which would emit (<number> = CAST(1 AS BIT))). All five are boolean by DataFusion's own construction: filter_expr, And/Or operands, Expr::Not's inner, searched-CASE when, and the join condition. The reverse direction is likewise only reachable from boolean-typed expressions. No hole found.

Things I checked that turned out fine, worth recording so nobody re-derives them:

  • Parameter duplication. predicate_to_scalar renders {{ expr }} twice, so a $N$ placeholder inside is emitted twice. SqlQuery::finalize_query (wrapper.rs:3xx) pushes self.values[param_index] once per occurrence when reuse_params is false and dedupes when it's true — both correct. No arity mismatch.
  • Blast radius of expressions/true/false. Only cubesql reads them (transport/service.rs:999). Tesseract's sql_templates/plan.rs uses only wrap_segment_select/wrap_segment_filter, and the expressions/true entry in its mock fixture is unused. So changing TRUECAST(1 AS BIT) cannot reach the legacy or Tesseract JS planners.
  • Segment interaction. Push-to-Cube filters become segments: member defs. MssqlSegment.filterToWhere passes them through unchanged outside rollup context, and wrapSegmentForDimensionSelect wraps them as CASE WHEN <predicate> THEN 1 ELSE 0 END — which the new predicate-context output satisfies and the old bare-BIT output did not. This path gets fixed too, as the issue predicted.
  • Simple vs. searched CASE. expr.is_none() at wrapper.rs:3134 reads the shadowed Option<String>, not the original Option<Box<Expr>>. Subtle, but correct either way.
  • having_expr is never rendered to SQL, so it isn't a missed predicate context.
  • Nothing in docs-mintlify documents the SQL template set, so there's no doc surface to update for the two new template names.

Security: the templates are static and {{ expr }} is already-generated SQL, not user input. Nothing new.

Findings

Where
medium check-boolean-context-results.js Never executed by any runner — and it's the only place the emitted T-SQL meets a real SQL Server
medium test/mod.rs:1498 Three unsynchronised copies of the MSSQL templates; the Rust tests verify the copy, not the product
medium wrapper.rs:2462 Volatility guard misses Expr::InSubquery, which the duplicated {{ expr }} inlines and re-executes
low wrapper.rs:2492 Expr::Alias arm in _raw is now unreachable
low test_wrapper.rs:4696 MSSQL_BOOLEAN_PLAN output has no consumer
low MssqlQuery.ts:323 Object.assign inconsistent with the direct assignments beside it
low check-boolean-context-results.js:6 eslint-disable for a rule oxlint doesn't have, on a declared devDependency

The first one is the one I'd act on. The truth-table test round-trips the MSSQL SQL through DataFusion after a " AS BIT)"" AS BOOLEAN)" string swap — but DataFusion accepts both WHERE b and WHERE (b = CAST(1 AS BOOLEAN)), so it structurally cannot catch a regression in the context conversion itself. Only the six pinned strings in boolean_context_sql_boundaries can. The manual script that would catch the rest isn't wired into CI.

Not verified

I could not compile or run anything here: node_modules is not installed and cargo isn't available to this session. The Rust test module reads as though it should compile — within_group, ExpressionVisitor/Recursion/Volatility, sql_generator, with_custom_templates and find_cube_scan_wrapped_sql all exist as used, and the private-fn access from a child module is fine — but that's from reading, not from a build. Worth confirming CI is green on cargo test -p cubesql --lib boolean_context and on cubejs-schema-compiler's unit tests before merging.

· branch [`feature/codex-mssql-boolean-contexts`](https://github.com/cube-js/cube/tree/feature/codex-mssql-boolean-contexts)

Comment thread rust/cubesql/cubesql/src/compile/test/mod.rs
Comment thread rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs
Comment thread rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs
Comment thread rust/cubesql/cubesql/src/compile/test/test_wrapper.rs Outdated
Comment thread packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts Outdated
@davidda
davidda force-pushed the feature/codex-mssql-boolean-contexts branch from 3f6e271 to 045c34f Compare September 15, 2026 14:55
@MazterQyou

Copy link
Copy Markdown
Member

/bot-deep-review

@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 14m 38s —— View job


Deep review — approach is sound; 1 medium, 3 low, plus a red Lerna tsc on this head that needs attributing

Full review

Verdict

The core design holds up. I traced the four queries from #11826 through generate_sql_for_expr_context and each lands where the issue expects: the aggregate flag and the IS NULL projection go through predicate_to_scalar, WHERE completed = TRUE and WHERE NOT completed go through scalar_to_predicate, and GROUP BY completed IS NULL renders the same scalar text as the projection so MSSQL's expression-based group_by_exprs matches. Three-valued semantics survive in both directions — NOT (b = CAST(1 AS BIT)) on a NULL b is UNKNOWN, and the scalar CASE falls to its ELSE NULL.

Things I specifically checked and found clean:

  • Non-MSSQL dialects are untouched. boolean_context_expr (transport/service.rs:858) returns the expression unchanged when the template is absent, and the volatile/subquery guard is gated on contains_template("expressions/predicate_to_scalar"). Expr::Not switching to generate_sql_for_predicate and filter_expr switching to predicate=true are no-ops everywhere else.
  • expressions/true / false are SQL-API-only. Sole consumer is transport/service.rs:1012; Tesseract's sql_templates/plan.rs never renders them, so redefining them to CAST(1 AS BIT) can't reach the Tesseract pre-aggregation path. Previously MSSQL emitted bare TRUE/FALSE, which is invalid T-SQL in every position, so this is strictly a fix.
  • Duplicating {{ expr }} does not corrupt bind parameters. Placeholders are index-tagged $N$, and SqlQuery::finalize_query (wrapper.rs:291) handles repeats on both branches — reuse_params returns the same rendered param, otherwise it pushes a fresh value per occurrence. MssqlQuery's expressions.sort already duplicates {{ expr }}, so the pattern was proven.
  • The guard covers nested conversions, not just the top one. COALESCE(random() > 0.5, b) isn't a predicate at the root, but the argument is rendered through _context(predicate=false), where the guard fires on that subtree.
  • is_sql_predicate's classification matches the renderer. Every match arm in generate_sql_for_expr_raw that emits predicate-shaped SQL is in the list; the And/Or special case for BinaryExpr children, expr.is_none() for CASE when, and false for order_expr all line up with where the operands actually sit.

The truth-table test is the strongest part of the change: rendering the real MSSQL SQL, translating only the BIT type name back, and replaying it through DataFusion against hand-written three-valued expectations catches far more than a snapshot would.

Findings

# Severity Where
1 medium rust/cubesql/cubesql/src/compile/test/mod.rs:1501 — test fixture include_str!-ed into release builds from outside the Rust workspace
2 low packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts:328predicate_to_scalar doubles the SQL per nesting level, unbounded
3 low packages/cubejs-schema-compiler/test/integration/mssql/mssql-boolean-contexts.test.ts:13 — ~58 full fixture rebuilds for tables the suite never reads
4 low rust/cubesql/cubesql/src/compile/engine/df/wrapper/boolean_context_tests.rs:207 — permanently #[ignore]d test is the only record of the remaining IS TRUE gap

CI: Lerna tsc is failing on this head

Run 34984929543 (commit 045c34f) fails the Lerna tsc step in five jobs — unit (24.x, 3.13), Build & Test :dev for Debian, integration-cubestore, and both integration-smoke matrix legs. lint, build-cubestore and Build native linux pass, so it is a TypeScript compile error, not a lint or Rust failure.

I could not read the job log in this environment (log download and gh run view --log-failed are both blocked here), so I can't attribute it. Worth ruling out the two new .json-importing test files first — they are the package's only JSON imports, and booleanFixture.cases / booleanFixture.aggregates get heterogeneous inferred element types from resolveJsonModule, which is where union-of-array-types method calls like test.expected.filter(...) (mssql-boolean-contexts.test.ts:25) tend to break. If master is red for an unrelated reason, ignore this.

Not verified

cargo and node_modules are both unavailable here, so I did not compile the Rust crate, run cargo test -p cubesql, or run the Jest suites. Every claim above is from reading the code; the MSSQL integration test in particular is unexecuted on my side.

Prior threads

All 7 existing review threads are already resolved; nothing to resolve and no finding duplicates one.

· [`feature/codex-mssql-boolean-contexts`](https://github.com/cube-js/cube/tree/feature/codex-mssql-boolean-contexts)

Comment thread rust/cubesql/cubesql/src/compile/test/mod.rs
Comment thread packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts
@davidda
davidda requested a review from keydunov as a code owner September 18, 2026 08:59
@github-actions github-actions Bot added the docs Issues that require a documentation improvement label Sep 18, 2026
@MazterQyou

Copy link
Copy Markdown
Member

/bot-deep-review

@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 15m 46s —— View job


Deep review complete — 7 findings posted inline: 1 high, 3 medium, 3 low. Core rendering design is sound; the linked issue is only partly fixed.

Full review
  • Read full diff of all 12 changed files
  • Read issue MSSQL SQL API pushdown emits boolean literals and predicates in invalid contexts #11826 for the reported symptom
  • Review wrapper.rs boolean context conversion (segment/dimension lookup, NULL semantics, guard)
  • Review MssqlQuery.ts templates + cross-path interaction (wrap_segment_select, Tesseract)
  • Review tests (Rust unit, planner, MSSQL integration, fixture) and CI wiring
  • Review docs change
  • Reconcile with prior review threads (18 threads, all already resolved — nothing to resolve, nothing to skip)
  • Post inline findings

What holds up

The context-threading design is contained and correct where it applies. Specific things I checked and found sound:

  • No cross-dialect regression. Every new behaviour is gated on contains_template("expressions/scalar_to_predicate"), and boolean_context_expr is a no-op without templates. expressions/true/false are consumed only by literal_bool_expr (wrapper.rs:3355) on the SQL API pushdown path — Tesseract's expressions/true appears only in mock_sql_templates_render.rs with no caller, so the CAST(1 AS BIT) override cannot leak into the native planner or into filters/*.
  • No new error path from the member pre-check. The !subqueries.contains_key / known_join_subqueries guard at wrapper.rs:2484-2489 mirrors generate_sql_for_column's branches exactly, so find_member_in_ungrouped_scan is reached for the same set of columns in both places.
  • No double-wrapping with the existing segment path. MemberExpressionSymbol::is_segment is only set via with_is_segment(), which the SQL API pushdown never calls, so wrap_segment_select does not stack on top of predicate_to_scalar. (It does create a second, disagreeing scalarization — see the medium finding.)
  • Expr coverage. AnyExpr and SimilarTo are predicate-shaped and absent from is_sql_predicate, but both are commented out in generate_sql_for_expr_raw (wrapper.rs:2616, :2695) and fall to the unsupported-expression error, so the omission is unreachable. Between/InList/Like/ILike render unparenthesized but only ever land inside CASE WHEN … or NOT (…), so no precedence hazard.
  • Alias unwrapping. Replacing the single recursive Expr::Alias arm with the while loop in generate_sql_for_expr_context is behaviour-preserving: every internal recursion enters through generate_sql_for_expr*, never generate_sql_for_expr_raw directly.
  • tsconfig.json include. Adding the JSON path is not redundant — a composite project must list every input file, and directory globs skip .json even with resolveJsonModule.
  • CI. The rust-cubesql.yml path additions cover both push and pull_request, and the MSSQL integration suite does run (push.yml:424 matrix), so the fixture's expected values are executed against SQL Server rather than only against DataFusion.

Findings

Sev Where Finding
High wrapper.rs:2496 leave_raw_dimension leaves issue #11826's WHERE NOT completed repro emitting NOT ((completed)); the fixture asserts it errors ("expected": null)
Medium wrapper.rs:2516 RejectRepeatedBoolean cannot see inside ${Cube.segment}, so segment scalarization double-evaluates volatile/subquery model SQL
Medium MssqlQuery.ts:327 Two segment scalarizations now disagree on NULL — wrap_segment_select gives 0, predicate_to_scalar gives NULL
Medium service.rs:866 The template pair is gated in three places on two names and a missing one returns Ok(expr) — a partial dialect definition is silently wrong
Low mssql-boolean-contexts.test.ts:82 segmentSql clobbered per loop pass; unchecked dimensions[test.model] lets the negative cases pass for the wrong reason
Low mssql-boolean-contexts.test.ts:96 rejects.toThrow(/non-boolean|syntax/i) cannot distinguish an unsupported form from a malformed render
Low ms-sql.mdx:27-55 29 lines of SQL API semantics on a connection page, against docs-mintlify/CLAUDE.md

The high finding is the one that decides whether this PR closes #11826. The issue's model is a BIT column declared type: boolean, and its stated cause says explicitly that "replacing literals with 1/0 alone leaves invalid predicate projections and NOT(bit_column)". Three of the four repro queries are fixed; WHERE NOT completed is not, and fixtures/mssql-boolean-contexts.json:431-443 encodes that as expected. Round 4 reached this by backing off the raw-dimension conversion after a prior round showed it broke predicate-shaped boolean dimensions — a real conflict, but the resolution protects the undocumented shape at the cost of the reported one. Two concrete ways out are in the inline comment.

Re-affirmed, not re-posted

predicate_to_scalar duplicates {{ expr }}, so the rendered SQL doubles per nesting level — fixtures/mssql-boolean-contexts.json:172 already shows four copies of b = c for (b = c) = b, and a depth-30 boolean expression from a SQL API client would blow up the renderer. This is thread PRRT_kwDOCOH19M6ipLay on MssqlQuery.ts:328, which @davidda closed as "separate renderer hardening". Not reopening it, but flagging that the concern is an availability one, not a tidiness one, so "separate" should mean "tracked" rather than "dropped".

Not verified

I did not execute anything. No node_modules and no rust/cubesql/target in this checkout, and building the DataFusion fork plus a monorepo install was out of budget — so cargo test -p cubesql, yarn unit, yarn integration:mssql and yarn lint were all skipped, and every finding above is from reading the sources. The pre-aggregation half of the MssqlQuery.ts:327 finding is marked PLAUSIBLE for that reason; the rest are traced end to end through the renderer and, for the high finding, corroborated by the PR's own fixture.

· branch `feature/codex-mssql-boolean-contexts`

Comment thread rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs
Comment thread rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs
Comment thread packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts
Comment thread rust/cubesql/cubesql/src/transport/service.rs
Comment thread docs-mintlify/admin/connect-to-data/data-sources/ms-sql.mdx Outdated
@MazterQyou

Copy link
Copy Markdown
Member

/bot-deep-review

@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 15m 51s —— View job


Deep review — 1 medium (correctness, non-MSSQL dialects), 1 docs gap, 2 comment-hygiene. 4 inline comments posted.

Review details

Findings

# Severity Location Issue
1 Medium rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs:2476 Segment parenthesization gated behind the MSSQL-only scalar_to_predicate template, so WHERE seg1 AND seg2 still returns wrong rows on every other dialect
2 Low docs-mintlify/admin/connect-to-data/data-sources/ms-sql.mdx:40 Warning omits the scalar direction (SELECT big on a predicate-backed boolean dimension → SQL Server msg 102), which the PR's own fixture pins
3 Low rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs:2513 4-line comment, two lines restating RejectRepeatedBoolean
4 Low packages/cubejs-schema-compiler/test/integration/mssql/mssql-boolean-contexts.test.ts:6 Same cross-language note repeated 4× across two files

What holds up

The core design is sound. is_sql_predicate classifies by emitted SQL shape rather than DataFusion type, which is the right axis, and the context flag is threaded correctly through every call site I checked: filter_expr and patch-measure filters and join conditions get predicate=true; projection / group-by / aggregate args / window args / ORDER BY / CASE THEN/ELSE stay scalar; CASE WHEN uses expr.is_none() so the simple-CASE operand is correctly treated as a value. And/Or operands get predicate context while comparison operands do not.

Three-valued semantics survive both conversions — (x = CAST(1 AS BIT)) yields UNKNOWN on NULL, and the WHEN p … WHEN NOT (p) … ELSE NULL shape preserves it in the other direction. The DataFusion truth-table test is a genuinely independent oracle rather than a snapshot.

I checked one thing the duplication in predicate_to_scalar could have broken and it does not: string literals become $N$ placeholders, and duplicating them is safe because SqlQuery::finalize_query (wrapper.rs:292) re-pushes self.values[param_index] per occurrence when reuse_params is false, and reuses the rendered placeholder when it is true. No parameter/value mismatch either way.

Blast radius outside MSSQL is small: every new behaviour keys off contains_template, a HashMap lookup, and expressions/true/false are consumed only by SqlTemplates::literal_bool_expr in cubesql — Tesseract's sql_templates/plan.rs never reads them, so the legacy and native planners are untouched.

Raised before, not re-posted

predicate_to_scalar interpolating {{ expr }} twice grows nested predicate-as-scalar SQL as 2^depth — SELECT ((a=1)=(b=2))=((c=3)=(d=4)) already emits 8 copies of each leaf, and there is no size or depth bound on this path. This is thread PRRT_kwDOCOH19M6ipLay, which @davidda explicitly deferred to separate renderer hardening; noting it here rather than re-opening it. Worth a follow-up issue so it does not get lost.

I also dropped a candidate finding about MssqlQuery.ts in the rust-cubesql.yml trigger paths — an earlier round asked for exactly that line.

Not verified

Neither the Rust nor the JS suite was run: no target/ directory and cargo/yarn invocations are outside this job's permissions. Findings 1-4 come from reading the code and tracing the call graph, not from a failing run.

Review threads

Listing returned 25 threads authored by claude, all 25 already resolved with an explicit reply from @davidda. 0 resolved this round (nothing stale left open), 0 findings skipped as duplicates.

· branch `feature/codex-mssql-boolean-contexts`

Comment thread rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs
Comment thread docs-mintlify/admin/connect-to-data/data-sources/ms-sql.mdx
Comment thread rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs Outdated
@MazterQyou

Copy link
Copy Markdown
Member

/bot-deep-review

@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 14m 54s —— View job


Deep review — no blocking issues. 0 high, 0 medium, 4 low (2 efficiency, 2 test/CI hygiene), posted inline.

Full review
  • Gather context (diff vs. c2044b3, prior review rounds)
  • Review wrapper.rs boolean-context logic
  • Review MssqlQuery.ts templates + service.rs helper
  • Review tests & fixture
  • Review docs + CI workflow change
  • Reconcile with existing review threads, post findings

Verdict

The design holds up. I traced every one of the 48 generate_sql_for_expr* call sites in wrapper.rs and checked the context flag at each: projection/group-by/aggregate/window/order get scalar, filter_expr, Expr::Not's operand, And/Or operands, searched-CASE WHEN arms, join conditions and patch-measure addFilters get predicate. That set is right, and it maintains the invariant the whole change rests on — every projection an MSSQL wrapper emits is a BIT scalar, every filter is a predicate — so nested wrappers compose.

Cross-dialect safety is real, not assumed: has_boolean_context gates the member lookup on expressions/scalar_to_predicate, boolean_context_expr (rust/cubesql/cubesql/src/transport/service.rs:859) is an identity when the template is absent, and MSSQL is the only dialect defining either template. Every new predicate argument is therefore a no-op for Postgres/BigQuery/etc.

Two things I checked specifically because they would have been silent-wrong-answer bugs:

  • Parameter duplication. predicate_to_scalar interpolates {{ expr }} twice. Placeholders are indexed $N$ and SqlQuery::add_value dedupes by value (wrapper.rs:211), so duplicating the rendered string is safe — it does not desync the values vector.
  • Failure modes are loud. For a BIT-backed dimension, leave_raw_dimension emits (${dim}) into predicate position, which SQL Server rejects with 4145; a predicate-backed dimension in scalar position is a 102 syntax error. Neither produces wrong rows silently. The dimensionCases entries with expected: null pin both error numbers against a real server.

The documented workaround is backed by a planner-level test rather than just prose, which matters more than it looks: DataFusion's simplifier folds b = TRUEb (visible in cases at fixture line 76), so "use completed = TRUE" would be useless advice if the same folding happened in the CubeSQL pipeline. boolean_context_segment_members runs convert_sql_to_cube_query on WHERE has_subscription = TRUE and asserts (${…} = CAST(1 AS BIT)) survives — so the docs are pinned, not hoped for.

tsconfig.json's explicit JSON entry is required, not redundant: tsconfig.base.json sets composite: true, so an imported-but-unlisted .json would raise TS6307.

Findings (all low)

# Where Concern
1 rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs:2491 find_member_in_ungrouped_scan resolved twice per column on the MSSQL path — once here, once in generate_sql_for_column — under character-identical guards
2 .../wrapper/boolean_context_tests.rs:206 boolean_context_truth_tests_nulls is #[ignore]d and never calls the renderer; un-ignoring it would prove nothing about MSSQL IS TRUE rendering
3 packages/cubejs-schema-compiler/test/fixtures/mssql-boolean-contexts.json:331 3 of 15 dimensionCases are byte-identical in query+sql, so boolean_context_segment_members re-plans them
4 .github/workflows/rust-cubesql.yml:8 MssqlQuery.ts in the Rust path filter — Rust only include_str!s the JSON; adapter drift is caught by the JS unit test

Nothing here blocks merge.

Not re-raised

All 29 prior review threads on this PR are resolved, so nothing was skipped as a duplicate and nothing needed resolving. Two points remain open by the author's explicit, reasoned deferral and I did not relitigate them: the size growth of nested predicate_to_scalar expansions, and the scalar_to_predicate-vs-predicate_to_scalar split gating in boolean_context_expr.

Not verified

No build or test run. This checkout has no node_modules and no rust/cubesql/target, and installing the monorepo plus compiling the DataFusion fork was not worth the wall-clock here. Findings above are from reading the working tree; the correctness reasoning is static analysis, not execution.

• branch `feature/codex-mssql-boolean-contexts`

.is_some_and(|relation| context.known_join_subqueries.contains(relation))
{
if let MemberField::Member(member) =
Self::find_member_in_ungrouped_scan(context.ungrouped_scan_node, column)?

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.

find_member_in_ungrouped_scan now runs twice for every column on the MSSQL push-to-cube path: once here, and again inside generate_sql_for_column (rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs:3084) after generate_sql_for_expr_raw dispatches. It is a linear zip-scan over ungrouped_scan_node.schema.fields(), so a wide ungrouped scan projecting most of its members goes from O(n²) to 2·O(n²) field comparisons, and each column additionally pays find_cube_with_name (linear over cubes) plus lookup_segment and lookup_dimension (linear, eq_ignore_ascii_case per member).

The guard conditions here are character-for-character the ones generate_sql_for_column already applies (subqueries miss, then known_join_subqueries miss, then the lookup), so the resolved MemberField could be threaded through instead of resolved twice — e.g. resolve once here and pass the result into generate_sql_for_expr_raw/generate_sql_for_column, or cache it on the context.

Non-blocking, and MSSQL-only, but it is pure duplicated work on the hottest per-expression path.

Fix this →

Comment on lines +206 to +218
#[tokio::test]
#[ignore = "DataFusion truth-test lowering loses NULL semantics before SQL rendering"]
async fn boolean_context_truth_tests_nulls() {
let ctx = SessionContext::new();
let mut actual = vec![];
for test in ["IS TRUE", "IS FALSE", "IS NOT TRUE", "IS NOT FALSE"] {
actual.extend(values(&ctx, &format!("SELECT CAST(NULL AS BOOLEAN) {test}")).await);
}
assert_eq!(
actual,
vec![Some(false), Some(false), Some(true), Some(true)]
);
}

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.

boolean_context_truth_tests_nulls never touches the code this PR changes. It builds a bare SessionContext, runs SELECT CAST(NULL AS BOOLEAN) IS TRUE and friends, and asserts DataFusion's own results — no render(...), no generate_sql_for_expr_context, no MSSQL templates. Combined with #[ignore], it is inert in CI and, if someone ever un-ignores it after a DataFusion bump, a pass tells them the lowering bug is gone but nothing about whether IS [NOT] TRUE/FALSE renders correctly for MSSQL — which is the thing this file exists to pin.

Making it a real reproducer for this PR's boundary is a small change: plan the expression through ctx.sql, then assert on render(expr, true, true) / render(expr, false, true) alongside the DataFusion values, the way boolean_context_truth_tables does. Then un-ignoring it after an upgrade actually exercises the renderer.

Fix this →

"expected": 1
}
],
"dimensionCases": [

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.

dimensionCases carries the model/expected axis that only the MSSQL integration test reads, but boolean_context_segment_members (rust/cubesql/cubesql/src/compile/test/test_wrapper.rs) iterates the same array and asserts sql alone. Three pairs are byte-identical in both query and sql, so Rust plans the same query twice and asserts the same expectation twice:

  • WHERE has_subscription GROUP BY 1(${…has_subscription}) at lines 333 and 431
  • SUM(CASE WHEN has_subscription …)SUM(CASE WHEN (${…has_subscription}) …) at lines 347 and 354
  • COUNT(DISTINCT has_subscription)COUNT(DISTINCT ${…has_subscription}) at lines 417 and 439

That is 3 redundant full convert_sql_to_cube_query runs out of 15 in a test that already re-plans every case. Deduplicating on the Rust side — e.g. collect the distinct (query, sql) pairs before the loop — keeps the JS matrix intact while dropping a fifth of the planner work.

Fix this →

Comment on lines +8 to +9
- 'packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts'
- 'packages/cubejs-schema-compiler/test/fixtures/mssql-boolean-contexts.json'

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.

No Rust code reads MssqlQuery.ts. The only cross-tree dependency is the fixture, pulled in by include_str! at rust/cubesql/cubesql/src/compile/test/mod.rs:1502 — so line 9 is load-bearing and line 8 is not. Drift between the adapter and the fixture is caught on the JS side by packages/cubejs-schema-compiler/test/unit/mssql-query.test.ts:8, which compares MssqlQuery.prototype.sqlTemplates() against booleanFixture.templates.

Net effect of line 8 (and its pull_request twin at line 18): every edit to MssqlQuery.ts — null ordering, DATE_ADD, PERCENTILECONT, anything — now triggers a full native build plus the cubesql suite, for a file the suite cannot observe. Dropping the two MssqlQuery.ts entries keeps the fixture coupling covered.

Fix this →

This branch was successfully deployed

1 active (outdated) deployment
Preview f998c769 Deployed Sep 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Issues that require a documentation improvement javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members. rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants