Skip to content

Commit dbba94a

Browse files
committed
Preserve native CASE function fields without a depth cutoff
1 parent 42a0be8 commit dbba94a

3 files changed

Lines changed: 53 additions & 39 deletions

File tree

‎datafusion/physical-expr/src/expressions/case.rs‎

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::expressions::{
99
BinaryExpr, CastExpr, LambdaVariable, NegativeExpr, NotExpr, TryCastExpr, lit,
1010
try_cast,
1111
};
12-
use crate::{HigherOrderFunctionExpr, PhysicalExpr, ScalarFunctionExpr};
12+
use crate::{PhysicalExpr, ScalarFunctionExpr};
1313
use arrow::array::*;
1414
use arrow::compute::kernels::zip::zip;
1515
use arrow::compute::{
@@ -1337,21 +1337,6 @@ fn case_result_field_metadata(
13371337
case: &CaseExpr,
13381338
input_schema: &Schema,
13391339
) -> Result<(DataType, Metadata)> {
1340-
const MAX_EXACT_BRANCH_DEPTH: usize = 8;
1341-
1342-
fn can_infer_exact_function_branch(expr: &dyn PhysicalExpr) -> bool {
1343-
let mut work = vec![(expr, 1)];
1344-
while let Some((node, depth)) = work.pop() {
1345-
if depth > MAX_EXACT_BRANCH_DEPTH {
1346-
return false;
1347-
}
1348-
for child in node.children() {
1349-
work.push((child.as_ref(), depth + 1));
1350-
}
1351-
}
1352-
true
1353-
}
1354-
13551340
struct BranchField {
13561341
field: FieldRef,
13571342
certainly_null: bool,
@@ -1421,21 +1406,6 @@ fn case_result_field_metadata(
14211406
field: Arc::new(Field::new("", DataType::Boolean, true)),
14221407
certainly_null: false,
14231408
});
1424-
} else if expr.is::<ScalarFunctionExpr>()
1425-
|| expr.is::<HigherOrderFunctionExpr>()
1426-
{
1427-
let field = expr.return_field(input_schema)?;
1428-
let field = if field.metadata().is_empty()
1429-
|| can_infer_exact_function_branch(expr)
1430-
{
1431-
field
1432-
} else {
1433-
Arc::new(Field::new("", field.data_type().clone(), true))
1434-
};
1435-
fields.push(BranchField {
1436-
field,
1437-
certainly_null: false,
1438-
});
14391409
} else {
14401410
let field = expr.return_field(input_schema)?;
14411411
fields.push(BranchField {
@@ -2504,7 +2474,7 @@ mod tests {
25042474
}
25052475

25062476
#[test]
2507-
fn case_function_metadata_respects_logical_depth_limit() -> Result<()> {
2477+
fn case_function_metadata_preserves_cached_fields_at_any_depth() -> Result<()> {
25082478
let metadata = HashMap::from([("logical".to_string(), "binary".to_string())]);
25092479
let schema = Schema::new(vec![
25102480
Field::new("a", DataType::Binary, false).with_metadata(metadata.clone()),
@@ -2522,7 +2492,7 @@ mod tests {
25222492
.with_metadata(metadata.clone()),
25232493
);
25242494
let options = Arc::new(ConfigOptions::default());
2525-
for (depth, expected_marked) in [(2, true), (9, false)] {
2495+
for depth in [2, 9, 32, 128] {
25262496
let mut branch = col("a", &schema)?;
25272497
for _ in 0..depth {
25282498
branch = Arc::new(ScalarFunctionExpr::new(
@@ -2535,11 +2505,7 @@ mod tests {
25352505
}
25362506
let expr = case(None, vec![(lit(true), branch)], Some(col("b", &schema)?))?;
25372507
let field = expr.return_field(&schema)?;
2538-
if expected_marked {
2539-
assert_eq!(field.metadata(), &metadata);
2540-
} else {
2541-
assert!(field.metadata().is_empty());
2542-
}
2508+
assert_eq!(field.metadata(), &metadata);
25432509
}
25442510
Ok(())
25452511
}

‎datafusion/physical-expr/src/planner.rs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,10 @@ mod tests {
813813
};
814814
let logical = when(lit(true), marked(aliased)).otherwise(marked(col("a")))?;
815815
let logical_field = logical.to_field(&df_schema)?.1;
816-
assert!(logical_field.metadata().is_empty());
816+
assert_eq!(
817+
logical_field.metadata(),
818+
&Metadata::new().with("source", "test")
819+
);
817820

818821
let physical = create_physical_expr(
819822
&logical,

‎datafusion/sqllogictest/test_files/metadata.slt‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,51 @@ NULL the l_name field
652652
BAR the l_name field
653653
l_baz the l_name field
654654

655+
# Native binary/NOT fields do not erase a marked sibling result.
656+
query IT rowsort
657+
select
658+
case when id is null then id + 1 else with_metadata(id, 'case_result', 'marked') end,
659+
arrow_metadata(
660+
case when id is null then id + 1 else with_metadata(id, 'case_result', 'marked') end,
661+
'case_result')
662+
from table_with_metadata;
663+
----
664+
1 marked
665+
3 marked
666+
NULL marked
667+
668+
query BT rowsort
669+
select
670+
case when id is null then not (id > 0)
671+
else with_metadata(id > 0, 'case_result', 'marked') end,
672+
arrow_metadata(
673+
case when id is null then not (id > 0)
674+
else with_metadata(id > 0, 'case_result', 'marked') end,
675+
'case_result')
676+
from table_with_metadata;
677+
----
678+
NULL marked
679+
true marked
680+
true marked
681+
682+
# A depth greater than eight must not erase function result metadata.
683+
query T
684+
select arrow_metadata(
685+
case when id is null then
686+
with_metadata(with_metadata(with_metadata(
687+
with_metadata(with_metadata(with_metadata(
688+
with_metadata(with_metadata(with_metadata(name, 'deep', 'marked'),
689+
'deep', 'marked'), 'deep', 'marked'),
690+
'deep', 'marked'), 'deep', 'marked'), 'deep', 'marked'),
691+
'deep', 'marked'), 'deep', 'marked'), 'deep', 'marked')
692+
else name end,
693+
'deep')
694+
from table_with_metadata;
695+
----
696+
marked
697+
marked
698+
marked
699+
655700
# A LIMIT keeps an unmarked derived column at the physical operator boundary.
656701
query TT rowsort
657702
select value, arrow_metadata(value, 'metadata_key') from (

0 commit comments

Comments
 (0)