Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Restore public functions and mark them as deprecated
  • Loading branch information
AdamGS committed Feb 10, 2026
commit 3b4dc057d17165fb4c0e700bd6b767f2a25ce515
85 changes: 83 additions & 2 deletions datafusion/physical-expr/src/simplifier/const_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,59 @@ use std::sync::Arc;
use arrow::array::new_null_array;
use arrow::datatypes::{DataType, Field, Schema};
use arrow::record_batch::RecordBatch;
use datafusion_common::tree_node::Transformed;
use datafusion_common::tree_node::{Transformed, TreeNode, TreeNodeRecursion};
use datafusion_common::{Result, ScalarValue};
use datafusion_expr_common::columnar_value::ColumnarValue;

use crate::PhysicalExpr;
use crate::expressions::{Column, Literal};

/// Simplify expressions that consist only of literals by evaluating them.
///
/// This function checks if all children of the given expression are literals.
/// If so, it evaluates the expression against a dummy RecordBatch and returns
/// the result as a new Literal.
///
/// # Example transformations
/// - `1 + 2` -> `3`
/// - `(1 + 2) * 3` -> `9` (with bottom-up traversal)
/// - `'hello' || ' world'` -> `'hello world'`
#[deprecated(
since = "53.0.0",
note = "This function will be removed in a future release in favor of a private implementation that depends on other implementation details. Please open an issue if you have a use case for keeping it."
)]
pub fn simplify_const_expr(
expr: Arc<dyn PhysicalExpr>,
) -> Result<Transformed<Arc<dyn PhysicalExpr>>> {
let batch = create_dummy_batch()?;
// If expr is already a const literal or can't be evaluated into one.
if expr.as_any().is::<Literal>() || (!can_evaluate_as_constant(&expr)) {
return Ok(Transformed::no(expr));
}

// Evaluate the expression
match expr.evaluate(&batch) {
Ok(ColumnarValue::Scalar(scalar)) => {
Ok(Transformed::yes(Arc::new(Literal::new(scalar))))
}
Ok(ColumnarValue::Array(arr)) if arr.len() == 1 => {
// Some operations return an array even for scalar inputs
let scalar = ScalarValue::try_from_array(&arr, 0)?;
Ok(Transformed::yes(Arc::new(Literal::new(scalar))))
}
Ok(_) => {
// Unexpected result - keep original expression
Ok(Transformed::no(expr))
}
Err(_) => {
// On error, keep original expression
// The expression might succeed at runtime due to short-circuit evaluation
// or other runtime conditions
Ok(Transformed::no(expr))
}
}
}

/// Simplify expressions whose immediate children are all literals.
///
/// This function only checks the direct children of the expression,
Expand All @@ -39,7 +85,7 @@ use crate::expressions::{Column, Literal};
/// - `1 + 2` -> `3`
/// - `(1 + 2) * 3` -> `9` (with bottom-up traversal, inner expr simplified first)
/// - `'hello' || ' world'` -> `'hello world'`
pub(crate) fn simplify_const_expr(
pub(crate) fn simplify_const_expr_immediate(
expr: Arc<dyn PhysicalExpr>,
batch: &RecordBatch,
) -> Result<Transformed<Arc<dyn PhysicalExpr>>> {
Expand Down Expand Up @@ -106,3 +152,38 @@ pub(crate) fn create_dummy_batch() -> Result<RecordBatch> {
let col = new_null_array(&DataType::Null, 1);
Ok(RecordBatch::try_new(dummy_schema, vec![col])?)
}

fn can_evaluate_as_constant(expr: &Arc<dyn PhysicalExpr>) -> bool {
let mut can_evaluate = true;

expr.apply(|e| {
if e.as_any().is::<Column>() || e.is_volatile_node() {
can_evaluate = false;
Ok(TreeNodeRecursion::Stop)
} else {
Ok(TreeNodeRecursion::Continue)
}
})
.expect("apply should not fail");

can_evaluate
}

/// Check if this expression has any column references.
#[deprecated(
since = "53.0.0",
note = "This function isn't used internally and is trivial to implement, therefore it will be removed in a future release."
)]
pub fn has_column_references(expr: &Arc<dyn PhysicalExpr>) -> bool {
let mut has_columns = false;
expr.apply(|expr| {
if expr.as_any().downcast_ref::<Column>().is_some() {
has_columns = true;
Ok(TreeNodeRecursion::Stop)
} else {
Ok(TreeNodeRecursion::Continue)
}
})
.expect("apply should not fail");
has_columns
}
17 changes: 9 additions & 8 deletions datafusion/physical-expr/src/simplifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ use std::sync::Arc;
use crate::{
PhysicalExpr,
simplifier::{
const_evaluator::{create_dummy_batch, simplify_const_expr},
not::simplify_not_expr,
unwrap_cast::unwrap_cast_in_comparison,
const_evaluator::create_dummy_batch, unwrap_cast::unwrap_cast_in_comparison,
},
};

mod const_evaluator;
mod not;
mod unwrap_cast;
pub mod const_evaluator;
pub mod not;
pub mod unwrap_cast;

const MAX_LOOP_COUNT: usize = 5;

Expand Down Expand Up @@ -67,9 +65,12 @@ impl<'a> PhysicalExprSimplifier<'a> {

// Apply NOT expression simplification first, then unwrap cast optimization,
// then constant expression evaluation
let rewritten = simplify_not_expr(node, schema)?
#[expect(deprecated, reason = "`simplify_not_expr` is marked as deprecated until it's made private.")]
let rewritten = not::simplify_not_expr(node, schema)?
.transform_data(|node| unwrap_cast_in_comparison(node, schema))?
.transform_data(|node| simplify_const_expr(node, &batch))?;
.transform_data(|node| {
const_evaluator::simplify_const_expr_immediate(node, &batch)
})?;

#[cfg(debug_assertions)]
assert_eq!(
Expand Down
6 changes: 5 additions & 1 deletion datafusion/physical-expr/src/simplifier/not.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ use crate::expressions::{BinaryExpr, InListExpr, Literal, NotExpr, in_list, lit}
/// This function applies a single simplification rule and returns. When used with
/// TreeNodeRewriter, multiple passes will automatically be applied until no more
/// transformations are possible.
pub(crate) fn simplify_not_expr(
#[deprecated(
since = "53.0.0",
note = "This function will be made private in a future release, please file an issue if you have a reason for keeping it public."
)]
pub fn simplify_not_expr(
expr: Arc<dyn PhysicalExpr>,
schema: &Schema,
) -> Result<Transformed<Arc<dyn PhysicalExpr>>> {
Expand Down