Skip to content

Commit 4c92291

Browse files
committed
feat(datafusion): support abs argument and fold literal helper
Re-add `abs(x)` as a NaN-preserving argument to `isnan`, and merge the literal f64 extraction into a single `literal_as_f64` helper. No other behavior change.
1 parent b33d08d commit 4c92291

1 file changed

Lines changed: 17 additions & 17 deletions

File tree

crates/integrations/datafusion/src/physical_plan/expr_to_predicate.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,14 @@ fn scalar_function_to_iceberg_predicate(func_name: &str, args: &[Expr]) -> Trans
249249
/// wrapped column is NaN — so both `isnan(arg)` and `NOT isnan(arg)` are sound:
250250
///
251251
/// * negation: `-x` is NaN iff `x` is NaN
252+
/// * `abs(x)`: `abs(x)` is NaN iff `x` is NaN
252253
/// * casts between numeric types preserve NaN
253254
/// * `x + c`, `c + x`, `x - c`, `c - x` for a finite literal `c`
254255
/// * `x * c`, `c * x`, `x / c` for a finite, non-zero literal `c`
255256
///
256257
/// Multiplication/division by zero and `c / x` are intentionally rejected: e.g.
257258
/// `x * 0` is NaN when `x` is `±inf`, so it does not imply `x` is NaN.
258259
///
259-
/// Scalar-function arguments (e.g. `abs(x)`) are intentionally left for a
260-
/// follow-up; only references, negation, casts and arithmetic are resolved here.
261-
///
262260
/// [`IcebergTableProvider::supports_filters_pushdown`]: crate::table::IcebergTableProvider
263261
fn resolve_nan_preserving_reference(expr: &Expr) -> Option<Reference> {
264262
match expr {
@@ -272,9 +270,12 @@ fn resolve_nan_preserving_reference(expr: &Expr) -> Option<Reference> {
272270
}
273271
resolve_nan_preserving_reference(&cast.expr)
274272
}
273+
Expr::ScalarFunction(ScalarFunction { func, args })
274+
if func.name() == "abs" && args.len() == 1 =>
275+
{
276+
resolve_nan_preserving_reference(&args[0])
277+
}
275278
Expr::BinaryExpr(binary) => resolve_nan_preserving_binary(binary),
276-
// TODO(#2154): resolve NaN-preserving scalar-function arguments such as
277-
// `abs(x)` (and similar, e.g. `signum`).
278279
_ => None,
279280
}
280281
}
@@ -338,14 +339,9 @@ fn is_finite_nonzero_literal(expr: &Expr) -> bool {
338339
/// Returns the value of `expr` as an `f64` if it is a numeric literal. Used only
339340
/// to inspect the finiteness and sign of literals (precision loss is irrelevant).
340341
fn literal_as_f64(expr: &Expr) -> Option<f64> {
341-
match expr {
342-
Expr::Literal(value, _) => scalar_value_as_f64(value),
343-
_ => None,
344-
}
345-
}
346-
347-
/// Extracts a numeric [`ScalarValue`] as an `f64`.
348-
fn scalar_value_as_f64(value: &ScalarValue) -> Option<f64> {
342+
let Expr::Literal(value, _) = expr else {
343+
return None;
344+
};
349345
match value {
350346
ScalarValue::Int8(Some(v)) => Some(*v as f64),
351347
ScalarValue::Int16(Some(v)) => Some(*v as f64),
@@ -864,6 +860,13 @@ mod tests {
864860
assert_eq!(predicate, !Reference::new("qux").is_nan());
865861
}
866862

863+
#[test]
864+
fn test_predicate_conversion_with_isnan_abs() {
865+
// abs(x) is NaN iff x is NaN
866+
let predicate = convert_to_iceberg_predicate("isnan(abs(qux))").unwrap();
867+
assert_eq!(predicate, Reference::new("qux").is_nan());
868+
}
869+
867870
#[test]
868871
fn test_predicate_conversion_with_isnan_additive() {
869872
// x + c, c + x, x - c, c - x are NaN iff x is NaN (for finite c)
@@ -891,7 +894,7 @@ mod tests {
891894
#[test]
892895
fn test_predicate_conversion_with_isnan_nested_expr() {
893896
// Nested NaN-preserving transformations resolve to the inner column
894-
let predicate = convert_to_iceberg_predicate("isnan(-(qux + 1) * 3)").unwrap();
897+
let predicate = convert_to_iceberg_predicate("isnan(-(abs(qux) + 1) * 3)").unwrap();
895898
assert_eq!(predicate, Reference::new("qux").is_nan());
896899
}
897900

@@ -920,9 +923,6 @@ mod tests {
920923
// single column reference.
921924
assert_eq!(convert_to_iceberg_predicate("isnan(qux + foo)"), None);
922925

923-
// Scalar-function arguments (e.g. abs) are left for a follow-up.
924-
assert_eq!(convert_to_iceberg_predicate("isnan(abs(qux))"), None);
925-
926926
// Unknown scalar functions are not pushed down.
927927
assert_eq!(convert_to_iceberg_predicate("isnan(sqrt(qux))"), None);
928928
}

0 commit comments

Comments
 (0)