Skip to content

[SQL] Context-sensitive conditionals optimization#6335

Merged
mihaibudiu merged 1 commit into
feldera:mainfrom
mihaibudiu:issue5507
Jun 28, 2026
Merged

[SQL] Context-sensitive conditionals optimization#6335
mihaibudiu merged 1 commit into
feldera:mainfrom
mihaibudiu:issue5507

Conversation

@mihaibudiu

Copy link
Copy Markdown
Contributor

Fixes #5507

This implements a flow-sensitive dataflow analysis which tracks expressions that are constant within a conditional. For example, given: if x { ... x ... } else { ... x ... } in the then part of the if x can be substituted with true, and in the else part it can be substituted with false.

Checklist

  • Unit tests added/updated

Code generated before:

        let s4: Stream<RootCircuit, Tup2<bool, (Tup1<Timestamp>, )>> = s1.apply(
            move |p0: &Tup2<bool, Tup1<Timestamp>>, | -> 
            Tup2<bool, (Tup1<Timestamp>, )> {
                Tup2::new((*p0).0, (if (*p0).0 {
                    let p1: (&Tup1<Timestamp>, ) = (
                        &(if (*p0).0 {
                            Tup1::new(timestamp_trunc_week_Timestamp((*p0).1.0))
                        } else {
                            STATIC_0307dce7d0790fbf.clone()
                        }), 
                    );
                    (
                        (*p1.0), 
                    )
                } else {
                    STATIC_777571800a13a3a7.clone()
                }))
            }
        ).mark_distinct();

Code generated after:

        let s4: Stream<RootCircuit, Tup2<bool, (Tup1<Timestamp>, )>> = s1.apply(
            move |p0: &Tup2<bool, Tup1<Timestamp>>, | ->
            Tup2<bool, (Tup1<Timestamp>, )> {
                Tup2::new((*p0).0, (if (*p0).0 {
                    let p1: (&Tup1<Timestamp>, ) = (
                        &Tup1::new(timestamp_trunc_week_Timestamp((*p0).1.0)),
                    );
                    (
                        (*p1.0),
                    )
                } else {
                    STATIC_777571800a13a3a7.clone()
                }))
            }
        ).mark_distinct();

@mihaibudiu
mihaibudiu enabled auto-merge May 27, 2026 22:54

@mythical-fred mythical-fred 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.

Review

The optimization is well-targeted. The ContainsIf short-circuit avoids paying analysis cost on if-free expressions, and the EquivalenceContext-based comparison correctly handles alpha-renaming (testAliasedVariable proves it). The ResolveReferences.preorder(DBSPLetExpression) fix -- initializer in outer scope, body in inner scope -- is the right semantics that was missing, and dropping the let declaration's postorder cleanup into the inlined preorder is the right shape. Three IR-level unit tests cover the basic substitution, the equivalent-comparison case, and the alpha-renaming negative case.

Coverage gaps worth filling before merge

  1. No SQL-level regression test. The PR description shows the timestamp_trunc_week codegen win beautifully, but there is no Regression3Tests case that compiles that SQL and pins the post-optimization shape. The IR-level unit tests are good but a SQL fixture is what catches future regressions in optimizer ordering -- e.g. the new CircuitRewriter(new SimplifyConditionals(...), false) placement before Simplify in CircuitOptimizer.createOptimizer, or interactions with the joins/limiters that run after it. Per the workspace rule on behavior changes without tests, this would normally block; soft-blocking here because the IR tests do exist and the description shows the wins, but please add at least one SQL-level fixture before merge.

  2. No nullable-bool negative test. The optimization explicitly excludes nullable booleans (!expression.getType().mayBeNull in checkIfConstant). That is the right conservative choice today, but there should be a unit test that proves it: feed if (a: bool?) { a } and assert the inner a is not rewritten. Otherwise a future relaxation has no canary.

Inline notes

9 comments below covering: complexity bound, Scopes.clone() aliasing contract, enforce(subst != null) missing diagnostic, missing @Override, temp-file accumulation in QATests, and a coverage note on the MultiCrateTests.qaTests removal.

The big-picture design -- flow-sensitive dataflow over the IR using SymbolicInterpreter + EquivalenceContext for alpha-renamed equality -- is the right shape. Once the SQL-level test and the nullable-bool negative test land, this is good to go from my side.

@mythical-fred mythical-fred 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.

Re-APPROVE on 7796aa1. Two of my prior nits cleanly addressed: @Override added to postorder(DBSPIsNullExpression), and the Scopes.clone() lastClone/version memoization removed — eliminates the implicit aliasing contract and matches the explicit-copy semantics callers expect. The other three threads (constant-prop excludes nullable booleans; complexity bound on linear scan; enforce message in DBSPVariablePath) are non-blocking and can land as follow-ups. The replaceConnectors/deleteOnExit and MultiCrateTests doc nits also still apply but aren't blockers.


/** Simplifies expressions based on their context within a conditional;
* if (a) { ... a ... } simplifies the second occurrence of 'a' to 'true'. */
public class SimplifyConditionals implements IRTransform {

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.

You'd think calcite would have this optimization pass already

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These are expressions that are synthesized by our compiler, usually from inlining functions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This happens when we fuse multiple consecutive operators; this is something Calcite cannot do in general, it's set of operators is too weak to represent all we need.

.appendSupplier(expression::toString)
.append(" = ")
.append(value);
Utilities.putNew(this.constants, expression, value);

@gz gz Jun 24, 2026

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.

you need an Utilities method to put something in a Map?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

putNew checks that the value doesn't exist already

@gz gz left a comment

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.

I like it

@mihaibudiu
mihaibudiu added this pull request to the merge queue Jun 24, 2026
@mihaibudiu

Copy link
Copy Markdown
Contributor Author

good, I will let you rest a bit and send you more of these.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 25, 2026
Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
@mihaibudiu

Copy link
Copy Markdown
Contributor Author

I will merge this after the current release is finished, to minimize possible disruptions. We have enough changes for now.

@mythical-fred mythical-fred 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.

Squash-rebase of previously approved work onto main — no new substantive changes. Still LGTM.

@mihaibudiu
mihaibudiu added this pull request to the merge queue Jun 28, 2026
Merged via the queue into feldera:main with commit b734d02 Jun 28, 2026
1 check passed
@mihaibudiu
mihaibudiu deleted the issue5507 branch June 28, 2026 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SQL] Optimize nested if expressions with the same condition

3 participants