Skip to content

Conversation

EeshanBembi
Copy link

Summary

Adds an optimization rule to simplify CASE expressions where the first condition is always true (literal true), reducing them to just the THEN expression.

This fixes the issue where expressions like:

CASE WHEN true THEN 1 ELSE x END

were not being simplified to just 1.

Changes

  • Added new simplification rule in expr_simplifier.rs that detects CASE WHEN true THEN expr patterns
  • Added comprehensive tests covering various scenarios including:
    • Simple literal values
    • Column references
    • Multiple WHEN clauses (only first is simplified)
    • Cases with and without ELSE clauses

Test Plan

  • Added unit tests for the new simplification rule
  • Verified existing CASE tests still pass
  • Tested with the exact example from the issue
  • Confirmed logical and physical plans now show simplified expressions

Before/After

Before:

logical_plan  | Projection: CASE WHEN Boolean(true) THEN Int64(1) ELSE CAST(foo.x AS Int64) END
physical_plan | ProjectionExec: expr=[CASE WHEN true THEN 1 ELSE CAST(x@0 AS Int64) END as ...]

After:

logical_plan  | Projection: Int64(1) AS CASE WHEN Boolean(true) THEN Int64(1) ELSE foo.x END
physical_plan | ProjectionExec: expr=[1 as CASE WHEN Boolean(true) THEN Int64(1) ELSE foo.x END]

Fixes #17448

Add optimization rule to simplify CASE expressions where the first
condition is always true (literal true), reducing them to just the
THEN expression. This eliminates unnecessary branching and casting
for cases like "CASE WHEN true THEN 1 ELSE x END" which now
simplifies to "1".

Fixes apache#17448
@github-actions github-actions bot added the optimizer Optimizer rules label Sep 5, 2025
Copy link
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

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

Thank you for the quick turnaround @EeshanBembi

I left some comments / suggestions

@@ -3552,6 +3564,61 @@ mod tests {
);
}

#[test]
fn simplify_expr_case_when_true() {
// CASE WHEN true THEN 1 ELSE x END --> 1
Copy link
Contributor

Choose a reason for hiding this comment

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

can we please add some negative tests too -- like CASE WHEN a THEN 1 ELSE 2 END?

else_expr: _,
}) if !when_then_expr.is_empty()
&& matches!(when_then_expr[0].0.as_ref(),
Expr::Literal(ScalarValue::Boolean(Some(true)), _)) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

can we please use is_true to follow the pattern in the rest of this method?

pub fn is_true(expr: &Expr) -> bool {
match expr {
Expr::Literal(ScalarValue::Boolean(Some(v)), _) => *v,
_ => false,
}
}

&& matches!(when_then_expr[0].0.as_ref(),
Expr::Literal(ScalarValue::Boolean(Some(true)), _)) =>
{
Transformed::yes((*when_then_expr[0].1).clone())
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be good to avoid this clone too,

How about something like this:

Suggested change
Transformed::yes((*when_then_expr[0].1).clone())
let (when, then_) = when_then_expr.swap_remove(0);
Transformed::yes(*when)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
optimizer Optimizer rules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Simplify CASE WHEN true <xx> ...
2 participants