@@ -1482,22 +1482,15 @@ impl TreeNodeRewriter for Simplifier<'_> {
14821482 . iter ( )
14831483 . any ( |( when, _) | is_true ( when. as_ref ( ) ) || is_false ( when. as_ref ( ) ) ) =>
14841484 {
1485- // CASE WHEN false THEN A END --> NULL
1486- // The NULL keeps the CASE's field metadata, such as an extension type
1487- if else_expr. is_none ( )
1488- && when_then_expr
1489- . iter ( )
1490- . all ( |( when, _) | is_false ( when. as_ref ( ) ) )
1491- {
1492- let ( _, field) = Expr :: Case ( Case :: new ( None , when_then_expr, None ) )
1493- . to_field ( info. schema ( ) . as_ref ( ) ) ?;
1494- let metadata = FieldMetadata :: new_from_field ( & field) ;
1495- let null = Expr :: Literal (
1496- ScalarValue :: try_new_null ( field. data_type ( ) ) ?,
1497- ( !metadata. is_empty ( ) ) . then_some ( metadata) ,
1498- ) ;
1499- return Ok ( Transformed :: yes ( null) ) ;
1500- }
1485+ // Dropping a branch must not change the field metadata of the CASE,
1486+ // which merges the metadata of all its value branches
1487+ let original = Expr :: Case ( Case :: new (
1488+ None ,
1489+ when_then_expr. clone ( ) ,
1490+ else_expr. clone ( ) ,
1491+ ) ) ;
1492+ let schema = info. schema ( ) . as_ref ( ) ;
1493+ let ( _, field) = original. to_field ( schema) ?;
15011494
15021495 let mut new_when_then_expr = Vec :: with_capacity ( when_then_expr. len ( ) ) ;
15031496
@@ -1514,18 +1507,37 @@ impl TreeNodeRewriter for Simplifier<'_> {
15141507 }
15151508
15161509 // Exclude CASE statement altogether if there are no when-then expressions left
1517- // CASE WHEN false THEN A ELSE B END --> B
1518- if new_when_then_expr. is_empty ( )
1519- && let Some ( else_expr) = else_expr
1520- {
1521- return Ok ( Transformed :: yes ( * else_expr) ) ;
1522- }
1510+ let simplified = match ( new_when_then_expr. is_empty ( ) , else_expr) {
1511+ // CASE WHEN false THEN A END --> NULL
1512+ ( true , None ) => {
1513+ Expr :: Literal ( ScalarValue :: try_new_null ( field. data_type ( ) ) ?, None )
1514+ }
1515+ // CASE WHEN false THEN A ELSE B END --> B
1516+ ( true , Some ( else_expr) ) => * else_expr,
1517+ ( false , else_expr) => Expr :: Case ( Case {
1518+ expr : None ,
1519+ when_then_expr : new_when_then_expr,
1520+ else_expr,
1521+ } ) ,
1522+ } ;
15231523
1524- Transformed :: yes ( Expr :: Case ( Case {
1525- expr : None ,
1526- when_then_expr : new_when_then_expr,
1527- else_expr,
1528- } ) )
1524+ match simplified {
1525+ // A literal takes the CASE's field metadata, such as an extension type
1526+ Expr :: Literal ( value, _) => {
1527+ let metadata = FieldMetadata :: new_from_field ( & field) ;
1528+ Transformed :: yes ( Expr :: Literal (
1529+ value,
1530+ ( !metadata. is_empty ( ) ) . then_some ( metadata) ,
1531+ ) )
1532+ }
1533+ simplified
1534+ if simplified. to_field ( schema) ?. 1 . metadata ( )
1535+ == field. metadata ( ) =>
1536+ {
1537+ Transformed :: yes ( simplified)
1538+ }
1539+ _ => Transformed :: no ( original) ,
1540+ }
15291541 }
15301542
15311543 // CASE
@@ -1552,7 +1564,8 @@ impl TreeNodeRewriter for Simplifier<'_> {
15521564 || ( when_then_expr. iter ( ) . all ( |( _, then) | is_bool_lit ( then) )
15531565 && when_then_expr. iter ( ) . filter ( |( _, then) | is_true ( then) ) . count ( ) < 3 ) )
15541566 && info. is_boolean_type ( & when_then_expr[ 0 ] . 1 ) ?
1555- && can_lower_case_to_boolean ( & when_then_expr, else_expr. as_deref ( ) ) =>
1567+ && can_lower_case_to_boolean ( & when_then_expr, else_expr. as_deref ( ) )
1568+ && !case_branch_has_metadata ( & when_then_expr, else_expr. as_deref ( ) , info) ? =>
15561569 {
15571570 // String disjunction of all the when predicates encountered so far. Not nullable.
15581571 let mut filter_expr = lit ( false ) ;
@@ -1610,7 +1623,8 @@ impl TreeNodeRewriter for Simplifier<'_> {
16101623 . count ( )
16111624 < 3
16121625 && else_expr. as_deref ( ) . is_none_or ( is_bool_lit)
1613- && can_lower_case_to_boolean ( & when_then_expr, else_expr. as_deref ( ) ) =>
1626+ && can_lower_case_to_boolean ( & when_then_expr, else_expr. as_deref ( ) )
1627+ && !case_branch_has_metadata ( & when_then_expr, else_expr. as_deref ( ) , info) ? =>
16141628 {
16151629 Transformed :: yes (
16161630 Expr :: Case ( Case {
@@ -2408,6 +2422,25 @@ fn can_lower_case_to_boolean(
24082422 && else_expr. is_none_or ( is_leaf)
24092423}
24102424
2425+ /// Whether a value branch of a CASE has field metadata, which AND/OR cannot keep
2426+ fn case_branch_has_metadata (
2427+ when_then_expr : & [ ( Box < Expr > , Box < Expr > ) ] ,
2428+ else_expr : Option < & Expr > ,
2429+ info : & SimplifyContext ,
2430+ ) -> Result < bool > {
2431+ let branches = when_then_expr
2432+ . iter ( )
2433+ . map ( |( _, then) | then. as_ref ( ) )
2434+ . chain ( else_expr) ;
2435+ for expr in branches {
2436+ let ( _, field) = expr. to_field ( info. schema ( ) . as_ref ( ) ) ?;
2437+ if !field. metadata ( ) . is_empty ( ) {
2438+ return Ok ( true ) ;
2439+ }
2440+ }
2441+ Ok ( false )
2442+ }
2443+
24112444/// Returns expression testing a boolean `expr` for being exactly `true` (not `false` or NULL).
24122445fn is_exactly_true ( expr : Expr , info : & SimplifyContext ) -> Result < Expr > {
24132446 if !info. nullable ( & expr) ? {
@@ -4601,28 +4634,114 @@ mod tests {
46014634 }
46024635
46034636 #[ test]
4604- fn simplify_expr_case_when_false_keeps_field_metadata ( ) -> Result < ( ) > {
4637+ fn simplify_expr_case_keeps_field_metadata ( ) -> Result < ( ) > {
46054638 let metadata = FieldMetadata :: from ( HashMap :: from ( [ (
46064639 "ARROW:extension:name" . to_string ( ) ,
46074640 "a.type" . to_string ( ) ,
46084641 ) ] ) ) ;
46094642 let schema = Arc :: new ( DFSchema :: from_unqualified_fields (
4610- vec ! [ metadata. add_to_field( Field :: new( "ext" , DataType :: Utf8 , true ) ) ] . into ( ) ,
4643+ vec ! [
4644+ metadata. add_to_field( Field :: new( "ext" , DataType :: Utf8 , true ) ) ,
4645+ Field :: new( "plain" , DataType :: Utf8 , true ) ,
4646+ metadata. add_to_field( Field :: new( "flag" , DataType :: Boolean , true ) ) ,
4647+ Field :: new( "b1" , DataType :: Boolean , true ) ,
4648+ Field :: new( "b2" , DataType :: Boolean , true ) ,
4649+ Field :: new( "b3" , DataType :: Boolean , true ) ,
4650+ ]
4651+ . into ( ) ,
46114652 HashMap :: new ( ) ,
46124653 ) ?) ;
46134654 let simplifier =
46144655 ExprSimplifier :: new ( SimplifyContext :: builder ( ) . with_schema ( schema) . build ( ) ) ;
46154656
4616- // CASE WHEN false THEN ext END --> NULL with ext's extension type
4617- let expr = Expr :: Case ( Case :: new (
4618- None ,
4619- vec ! [ ( Box :: new( lit( false ) ) , Box :: new( col( "ext" ) ) ) ] ,
4620- None ,
4621- ) ) ;
4622- assert_eq ! (
4623- simplifier. simplify( expr) ?,
4624- Expr :: Literal ( ScalarValue :: Utf8 ( None ) , Some ( metadata) )
4625- ) ;
4657+ // A CASE that simplifies to a literal gives it the CASE's metadata
4658+ let to_literal = [
4659+ // CASE WHEN false THEN ext END --> NULL
4660+ (
4661+ Case :: new (
4662+ None ,
4663+ vec ! [ ( Box :: new( lit( false ) ) , Box :: new( col( "ext" ) ) ) ] ,
4664+ None ,
4665+ ) ,
4666+ ScalarValue :: Utf8 ( None ) ,
4667+ ) ,
4668+ // CASE WHEN false THEN ext ELSE NULL END --> NULL
4669+ (
4670+ Case :: new (
4671+ None ,
4672+ vec ! [ ( Box :: new( lit( false ) ) , Box :: new( col( "ext" ) ) ) ] ,
4673+ Some ( Box :: new ( Expr :: Literal ( ScalarValue :: Utf8 ( None ) , None ) ) ) ,
4674+ ) ,
4675+ ScalarValue :: Utf8 ( None ) ,
4676+ ) ,
4677+ // CASE WHEN true THEN 'x' ELSE ext END --> 'x'
4678+ (
4679+ Case :: new (
4680+ None ,
4681+ vec ! [ ( Box :: new( lit( true ) ) , Box :: new( lit( "x" ) ) ) ] ,
4682+ Some ( Box :: new ( col ( "ext" ) ) ) ,
4683+ ) ,
4684+ ScalarValue :: from ( "x" ) ,
4685+ ) ,
4686+ ] ;
4687+ for ( case, expected) in to_literal {
4688+ assert_eq ! (
4689+ simplifier. simplify( Expr :: Case ( case) ) ?,
4690+ Expr :: Literal ( expected, Some ( metadata. clone( ) ) )
4691+ ) ;
4692+ }
4693+
4694+ // Rewrites that would drop the metadata of a branch leave the CASE as is
4695+ let unchanged = [
4696+ // CASE WHEN false THEN ext ELSE plain END
4697+ Case :: new (
4698+ None ,
4699+ vec ! [ ( Box :: new( lit( false ) ) , Box :: new( col( "ext" ) ) ) ] ,
4700+ Some ( Box :: new ( col ( "plain" ) ) ) ,
4701+ ) ,
4702+ // CASE WHEN true THEN plain ELSE ext END
4703+ Case :: new (
4704+ None ,
4705+ vec ! [ ( Box :: new( lit( true ) ) , Box :: new( col( "plain" ) ) ) ] ,
4706+ Some ( Box :: new ( col ( "ext" ) ) ) ,
4707+ ) ,
4708+ // CASE WHEN b1 THEN plain WHEN false THEN ext END
4709+ Case :: new (
4710+ None ,
4711+ vec ! [
4712+ ( Box :: new( col( "b1" ) ) , Box :: new( col( "plain" ) ) ) ,
4713+ ( Box :: new( lit( false ) ) , Box :: new( col( "ext" ) ) ) ,
4714+ ] ,
4715+ None ,
4716+ ) ,
4717+ // CASE WHEN b1 THEN flag END, which AND/OR cannot express with metadata
4718+ Case :: new (
4719+ None ,
4720+ vec ! [ ( Box :: new( col( "b1" ) ) , Box :: new( col( "flag" ) ) ) ] ,
4721+ None ,
4722+ ) ,
4723+ // CASE WHEN b1 THEN true WHEN b2 THEN true WHEN b3 THEN true ELSE false END,
4724+ // with metadata on the first THEN, which NOT(CASE ...) cannot keep
4725+ Case :: new (
4726+ None ,
4727+ vec ! [
4728+ (
4729+ Box :: new( col( "b1" ) ) ,
4730+ Box :: new( Expr :: Literal (
4731+ ScalarValue :: Boolean ( Some ( true ) ) ,
4732+ Some ( metadata. clone( ) ) ,
4733+ ) ) ,
4734+ ) ,
4735+ ( Box :: new( col( "b2" ) ) , Box :: new( lit( true ) ) ) ,
4736+ ( Box :: new( col( "b3" ) ) , Box :: new( lit( true ) ) ) ,
4737+ ] ,
4738+ Some ( Box :: new ( lit ( false ) ) ) ,
4739+ ) ,
4740+ ] ;
4741+ for case in unchanged {
4742+ let expr = Expr :: Case ( case) ;
4743+ assert_eq ! ( simplifier. simplify( expr. clone( ) ) ?, expr) ;
4744+ }
46264745 Ok ( ( ) )
46274746 }
46284747
0 commit comments