blob: 81846b4f8060845a781e2c4f58d9e217c74665dd [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! Tree node implementation for Logical Expressions
use crate::expr::{
AggregateFunction, AggregateFunctionParams, Alias, Between, BinaryExpr, Case, Cast,
GroupingSet, InList, InSubquery, Like, Placeholder, ScalarFunction, TryCast, Unnest,
WindowFunction, WindowFunctionParams,
};
use crate::Expr;
use datafusion_common::tree_node::{
Transformed, TreeNode, TreeNodeContainer, TreeNodeRecursion, TreeNodeRefContainer,
};
use datafusion_common::Result;
/// Implementation of the [`TreeNode`] trait
///
/// This allows logical expressions (`Expr`) to be traversed and transformed
/// Facilitates tasks such as optimization and rewriting during query
/// planning.
impl TreeNode for Expr {
/// Applies a function `f` to each child expression of `self`.
///
/// The function `f` determines whether to continue traversing the tree or to stop.
/// This method collects all child expressions and applies `f` to each.
fn apply_children<'n, F: FnMut(&'n Self) -> Result<TreeNodeRecursion>>(
&'n self,
f: F,
) -> Result<TreeNodeRecursion> {
match self {
Expr::Alias(Alias { expr, .. })
| Expr::Unnest(Unnest { expr })
| Expr::Not(expr)
| Expr::IsNotNull(expr)
| Expr::IsTrue(expr)
| Expr::IsFalse(expr)
| Expr::IsUnknown(expr)
| Expr::IsNotTrue(expr)
| Expr::IsNotFalse(expr)
| Expr::IsNotUnknown(expr)
| Expr::IsNull(expr)
| Expr::Negative(expr)
| Expr::Cast(Cast { expr, .. })
| Expr::TryCast(TryCast { expr, .. })
| Expr::InSubquery(InSubquery { expr, .. }) => expr.apply_elements(f),
Expr::GroupingSet(GroupingSet::Rollup(exprs))
| Expr::GroupingSet(GroupingSet::Cube(exprs)) => exprs.apply_elements(f),
Expr::ScalarFunction(ScalarFunction { args, .. }) => {
args.apply_elements(f)
}
Expr::GroupingSet(GroupingSet::GroupingSets(lists_of_exprs)) => {
lists_of_exprs.apply_elements(f)
}
// TODO: remove the next line after `Expr::Wildcard` is removed
#[expect(deprecated)]
Expr::Column(_)
// Treat OuterReferenceColumn as a leaf expression
| Expr::OuterReferenceColumn(_, _)
| Expr::ScalarVariable(_, _)
| Expr::Literal(_, _)
| Expr::Exists { .. }
| Expr::ScalarSubquery(_)
| Expr::Wildcard { .. }
| Expr::Placeholder(_) => Ok(TreeNodeRecursion::Continue),
Expr::BinaryExpr(BinaryExpr { left, right, .. }) => {
(left, right).apply_ref_elements(f)
}
Expr::Like(Like { expr, pattern, .. })
| Expr::SimilarTo(Like { expr, pattern, .. }) => {
(expr, pattern).apply_ref_elements(f)
}
Expr::Between(Between {
expr, low, high, ..
}) => (expr, low, high).apply_ref_elements(f),
Expr::Case(Case { expr, when_then_expr, else_expr }) =>
(expr, when_then_expr, else_expr).apply_ref_elements(f),
Expr::AggregateFunction(AggregateFunction { params: AggregateFunctionParams { args, filter, order_by, ..}, .. }) =>
(args, filter, order_by).apply_ref_elements(f),
Expr::WindowFunction(window_fun) => {
let WindowFunctionParams {
args,
partition_by,
order_by,
filter,
..
} = &window_fun.as_ref().params;
(args, partition_by, order_by, filter).apply_ref_elements(f)
}
Expr::InList(InList { expr, list, .. }) => {
(expr, list).apply_ref_elements(f)
}
}
}
/// Maps each child of `self` using the provided closure `f`.
///
/// The closure `f` takes ownership of an expression and returns a `Transformed` result,
/// indicating whether the expression was transformed or left unchanged.
fn map_children<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
mut f: F,
) -> Result<Transformed<Self>> {
Ok(match self {
// TODO: remove the next line after `Expr::Wildcard` is removed
#[expect(deprecated)]
Expr::Column(_)
| Expr::Wildcard { .. }
| Expr::Placeholder(Placeholder { .. })
| Expr::OuterReferenceColumn(_, _)
| Expr::Exists { .. }
| Expr::ScalarSubquery(_)
| Expr::ScalarVariable(_, _)
| Expr::Literal(_, _) => Transformed::no(self),
Expr::Unnest(Unnest { expr, .. }) => expr
.map_elements(f)?
.update_data(|expr| Expr::Unnest(Unnest { expr })),
Expr::Alias(Alias {
expr,
relation,
name,
metadata,
}) => f(*expr)?.update_data(|e| {
e.alias_qualified_with_metadata(relation, name, metadata)
}),
Expr::InSubquery(InSubquery {
expr,
subquery,
negated,
}) => expr.map_elements(f)?.update_data(|be| {
Expr::InSubquery(InSubquery::new(be, subquery, negated))
}),
Expr::BinaryExpr(BinaryExpr { left, op, right }) => (left, right)
.map_elements(f)?
.update_data(|(new_left, new_right)| {
Expr::BinaryExpr(BinaryExpr::new(new_left, op, new_right))
}),
Expr::Like(Like {
negated,
expr,
pattern,
escape_char,
case_insensitive,
}) => {
(expr, pattern)
.map_elements(f)?
.update_data(|(new_expr, new_pattern)| {
Expr::Like(Like::new(
negated,
new_expr,
new_pattern,
escape_char,
case_insensitive,
))
})
}
Expr::SimilarTo(Like {
negated,
expr,
pattern,
escape_char,
case_insensitive,
}) => {
(expr, pattern)
.map_elements(f)?
.update_data(|(new_expr, new_pattern)| {
Expr::SimilarTo(Like::new(
negated,
new_expr,
new_pattern,
escape_char,
case_insensitive,
))
})
}
Expr::Not(expr) => expr.map_elements(f)?.update_data(Expr::Not),
Expr::IsNotNull(expr) => expr.map_elements(f)?.update_data(Expr::IsNotNull),
Expr::IsNull(expr) => expr.map_elements(f)?.update_data(Expr::IsNull),
Expr::IsTrue(expr) => expr.map_elements(f)?.update_data(Expr::IsTrue),
Expr::IsFalse(expr) => expr.map_elements(f)?.update_data(Expr::IsFalse),
Expr::IsUnknown(expr) => expr.map_elements(f)?.update_data(Expr::IsUnknown),
Expr::IsNotTrue(expr) => expr.map_elements(f)?.update_data(Expr::IsNotTrue),
Expr::IsNotFalse(expr) => expr.map_elements(f)?.update_data(Expr::IsNotFalse),
Expr::IsNotUnknown(expr) => {
expr.map_elements(f)?.update_data(Expr::IsNotUnknown)
}
Expr::Negative(expr) => expr.map_elements(f)?.update_data(Expr::Negative),
Expr::Between(Between {
expr,
negated,
low,
high,
}) => (expr, low, high).map_elements(f)?.update_data(
|(new_expr, new_low, new_high)| {
Expr::Between(Between::new(new_expr, negated, new_low, new_high))
},
),
Expr::Case(Case {
expr,
when_then_expr,
else_expr,
}) => (expr, when_then_expr, else_expr)
.map_elements(f)?
.update_data(|(new_expr, new_when_then_expr, new_else_expr)| {
Expr::Case(Case::new(new_expr, new_when_then_expr, new_else_expr))
}),
Expr::Cast(Cast { expr, data_type }) => expr
.map_elements(f)?
.update_data(|be| Expr::Cast(Cast::new(be, data_type))),
Expr::TryCast(TryCast { expr, data_type }) => expr
.map_elements(f)?
.update_data(|be| Expr::TryCast(TryCast::new(be, data_type))),
Expr::ScalarFunction(ScalarFunction { func, args }) => {
args.map_elements(f)?.map_data(|new_args| {
Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
func, new_args,
)))
})?
}
Expr::WindowFunction(window_fun) => {
let WindowFunction {
fun,
params:
WindowFunctionParams {
args,
partition_by,
order_by,
window_frame,
filter,
null_treatment,
distinct,
},
} = *window_fun;
(args, partition_by, order_by, filter)
.map_elements(f)?
.map_data(
|(new_args, new_partition_by, new_order_by, new_filter)| {
Ok(Expr::from(WindowFunction {
fun,
params: WindowFunctionParams {
args: new_args,
partition_by: new_partition_by,
order_by: new_order_by,
window_frame,
filter: new_filter,
null_treatment,
distinct,
},
}))
},
)?
}
Expr::AggregateFunction(AggregateFunction {
func,
params:
AggregateFunctionParams {
args,
distinct,
filter,
order_by,
null_treatment,
},
}) => (args, filter, order_by).map_elements(f)?.map_data(
|(new_args, new_filter, new_order_by)| {
Ok(Expr::AggregateFunction(AggregateFunction::new_udf(
func,
new_args,
distinct,
new_filter,
new_order_by,
null_treatment,
)))
},
)?,
Expr::GroupingSet(grouping_set) => match grouping_set {
GroupingSet::Rollup(exprs) => exprs
.map_elements(f)?
.update_data(|ve| Expr::GroupingSet(GroupingSet::Rollup(ve))),
GroupingSet::Cube(exprs) => exprs
.map_elements(f)?
.update_data(|ve| Expr::GroupingSet(GroupingSet::Cube(ve))),
GroupingSet::GroupingSets(lists_of_exprs) => lists_of_exprs
.map_elements(f)?
.update_data(|new_lists_of_exprs| {
Expr::GroupingSet(GroupingSet::GroupingSets(new_lists_of_exprs))
}),
},
Expr::InList(InList {
expr,
list,
negated,
}) => (expr, list)
.map_elements(f)?
.update_data(|(new_expr, new_list)| {
Expr::InList(InList::new(new_expr, new_list, negated))
}),
})
}
}