## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/expr/src/tree_node.rs

    Line
    Count
    Source
    1
    // Licensed to the Apache Software Foundation (ASF) under one
    2
    // or more contributor license agreements.  See the NOTICE file
    3
    // distributed with this work for additional information
    4
    // regarding copyright ownership.  The ASF licenses this file
    5
    // to you under the Apache License, Version 2.0 (the
    6
    // "License"); you may not use this file except in compliance
    7
    // with the License.  You may obtain a copy of the License at
    8
    //
    9
    //   http://www.apache.org/licenses/LICENSE-2.0
    10
    //
    11
    // Unless required by applicable law or agreed to in writing,
    12
    // software distributed under the License is distributed on an
    13
    // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14
    // KIND, either express or implied.  See the License for the
    15
    // specific language governing permissions and limitations
    16
    // under the License.
    17
    18
    //! Tree node implementation for Logical Expressions
    19
    20
    use crate::expr::{
    21
        AggregateFunction, AggregateFunctionParams, Alias, Between, BinaryExpr, Case, Cast,
    22
        GroupingSet, InList, InSubquery, Like, Placeholder, ScalarFunction, TryCast, Unnest,
    23
        WindowFunction, WindowFunctionParams,
    24
    };
    25
    use crate::Expr;
    26
    27
    use datafusion_common::tree_node::{
    28
        Transformed, TreeNode, TreeNodeContainer, TreeNodeRecursion, TreeNodeRefContainer,
    29
    };
    30
    use datafusion_common::Result;
    31
    32
    /// Implementation of the [`TreeNode`] trait
    33
    ///
    34
    /// This allows logical expressions (`Expr`) to be traversed and transformed
    35
    /// Facilitates tasks such as optimization and rewriting during query
    36
    /// planning.
    37
    impl TreeNode for Expr {
    38
        /// Applies a function `f` to each child expression of `self`.
    39
        ///
    40
        /// The function `f` determines whether to continue traversing the tree or to stop.
    41
        /// This method collects all child expressions and applies `f` to each.
    42
    0
        fn apply_children<'n, F: FnMut(&'n Self) -> Result<TreeNodeRecursion>>(
    43
    0
            &'n self,
    44
    0
            f: F,
    45
    0
        ) -> Result<TreeNodeRecursion> {
    46
    0
            match self {
    47
    0
                Expr::Alias(Alias { expr, .. })
    48
    0
                | Expr::Unnest(Unnest { expr })
    49
    0
                | Expr::Not(expr)
    50
    0
                | Expr::IsNotNull(expr)
    51
    0
                | Expr::IsTrue(expr)
    52
    0
                | Expr::IsFalse(expr)
    53
    0
                | Expr::IsUnknown(expr)
    54
    0
                | Expr::IsNotTrue(expr)
    55
    0
                | Expr::IsNotFalse(expr)
    56
    0
                | Expr::IsNotUnknown(expr)
    57
    0
                | Expr::IsNull(expr)
    58
    0
                | Expr::Negative(expr)
    59
    0
                | Expr::Cast(Cast { expr, .. })
    60
    0
                | Expr::TryCast(TryCast { expr, .. })
    61
    0
                | Expr::InSubquery(InSubquery { expr, .. }) => expr.apply_elements(f),
    62
    0
                Expr::GroupingSet(GroupingSet::Rollup(exprs))
    63
    0
                | Expr::GroupingSet(GroupingSet::Cube(exprs)) => exprs.apply_elements(f),
    64
    0
                Expr::ScalarFunction(ScalarFunction { args, .. }) => {
    65
    0
                    args.apply_elements(f)
    66
                }
    67
    0
                Expr::GroupingSet(GroupingSet::GroupingSets(lists_of_exprs)) => {
    68
    0
                    lists_of_exprs.apply_elements(f)
    69
                }
    70
                // TODO: remove the next line after `Expr::Wildcard` is removed
    71
                #[expect(deprecated)]
    72
                Expr::Column(_)
    73
                // Treat OuterReferenceColumn as a leaf expression
    74
                | Expr::OuterReferenceColumn(_, _)
    75
                | Expr::ScalarVariable(_, _)
    76
                | Expr::Literal(_, _)
    77
                | Expr::Exists { .. }
    78
                | Expr::ScalarSubquery(_)
    79
                | Expr::Wildcard { .. }
    80
    0
                | Expr::Placeholder(_) => Ok(TreeNodeRecursion::Continue),
    81
    0
                Expr::BinaryExpr(BinaryExpr { left, right, .. }) => {
    82
    0
                    (left, right).apply_ref_elements(f)
    83
                }
    84
    0
                Expr::Like(Like { expr, pattern, .. })
    85
    0
                | Expr::SimilarTo(Like { expr, pattern, .. }) => {
    86
    0
                    (expr, pattern).apply_ref_elements(f)
    87
                }
    88
                Expr::Between(Between {
    89
    0
                                  expr, low, high, ..
    90
    0
                              }) => (expr, low, high).apply_ref_elements(f),
    91
    0
                Expr::Case(Case { expr, when_then_expr, else_expr }) =>
    92
    0
                    (expr, when_then_expr, else_expr).apply_ref_elements(f),
    93
    0
                Expr::AggregateFunction(AggregateFunction { params: AggregateFunctionParams { args, filter, order_by, ..}, .. }) =>
    94
    0
                    (args, filter, order_by).apply_ref_elements(f),
    95
    0
                Expr::WindowFunction(window_fun) => {
    96
                    let WindowFunctionParams {
    97
    0
                        args,
    98
    0
                        partition_by,
    99
    0
                        order_by,
    100
    0
                        filter,
    101
                        ..
    102
    0
                    } = &window_fun.as_ref().params;
    103
    0
                    (args, partition_by, order_by, filter).apply_ref_elements(f)
    104
                }
    105
    106
    0
                Expr::InList(InList { expr, list, .. }) => {
    107
    0
                    (expr, list).apply_ref_elements(f)
    108
                }
    109
            }
    110
    0
        }
    111
    112
        /// Maps each child of `self` using the provided closure `f`.
    113
        ///
    114
        /// The closure `f` takes ownership of an expression and returns a `Transformed` result,
    115
        /// indicating whether the expression was transformed or left unchanged.
    116
    0
        fn map_children<F: FnMut(Self) -> Result<Transformed<Self>>>(
    117
    0
            self,
    118
    0
            mut f: F,
    119
    0
        ) -> Result<Transformed<Self>> {
    120
    0
            Ok(match self {
    121
                // TODO: remove the next line after `Expr::Wildcard` is removed
    122
                #[expect(deprecated)]
    123
                Expr::Column(_)
    124
                | Expr::Wildcard { .. }
    125
                | Expr::Placeholder(Placeholder { .. })
    126
                | Expr::OuterReferenceColumn(_, _)
    127
                | Expr::Exists { .. }
    128
                | Expr::ScalarSubquery(_)
    129
                | Expr::ScalarVariable(_, _)
    130
    0
                | Expr::Literal(_, _) => Transformed::no(self),
    131
    0
                Expr::Unnest(Unnest { expr, .. }) => expr
    132
    0
                    .map_elements(f)?
    133
    0
                    .update_data(|expr| Expr::Unnest(Unnest { expr })),
    134
                Expr::Alias(Alias {
    135
    0
                    expr,
    136
    0
                    relation,
    137
    0
                    name,
    138
    0
                    metadata,
    139
    0
                }) => f(*expr)?.update_data(|e| {
    140
    0
                    e.alias_qualified_with_metadata(relation, name, metadata)
    141
    0
                }),
    142
                Expr::InSubquery(InSubquery {
    143
    0
                    expr,
    144
    0
                    subquery,
    145
    0
                    negated,
    146
    0
                }) => expr.map_elements(f)?.update_data(|be| {
    147
    0
                    Expr::InSubquery(InSubquery::new(be, subquery, negated))
    148
    0
                }),
    149
    0
                Expr::BinaryExpr(BinaryExpr { left, op, right }) => (left, right)
    150
    0
                    .map_elements(f)?
    151
    0
                    .update_data(|(new_left, new_right)| {
    152
    0
                        Expr::BinaryExpr(BinaryExpr::new(new_left, op, new_right))
    153
    0
                    }),
    154
                Expr::Like(Like {
    155
    0
                    negated,
    156
    0
                    expr,
    157
    0
                    pattern,
    158
    0
                    escape_char,
    159
    0
                    case_insensitive,
    160
                }) => {
    161
    0
                    (expr, pattern)
    162
    0
                        .map_elements(f)?
    163
    0
                        .update_data(|(new_expr, new_pattern)| {
    164
    0
                            Expr::Like(Like::new(
    165
    0
                                negated,
    166
    0
                                new_expr,
    167
    0
                                new_pattern,
    168
    0
                                escape_char,
    169
    0
                                case_insensitive,
    170
    0
                            ))
    171
    0
                        })
    172
                }
    173
                Expr::SimilarTo(Like {
    174
    0
                    negated,
    175
    0
                    expr,
    176
    0
                    pattern,
    177
    0
                    escape_char,
    178
    0
                    case_insensitive,
    179
                }) => {
    180
    0
                    (expr, pattern)
    181
    0
                        .map_elements(f)?
    182
    0
                        .update_data(|(new_expr, new_pattern)| {
    183
    0
                            Expr::SimilarTo(Like::new(
    184
    0
                                negated,
    185
    0
                                new_expr,
    186
    0
                                new_pattern,
    187
    0
                                escape_char,
    188
    0
                                case_insensitive,
    189
    0
                            ))
    190
    0
                        })
    191
                }
    192
    0
                Expr::Not(expr) => expr.map_elements(f)?.update_data(Expr::Not),
    193
    0
                Expr::IsNotNull(expr) => expr.map_elements(f)?.update_data(Expr::IsNotNull),
    194
    0
                Expr::IsNull(expr) => expr.map_elements(f)?.update_data(Expr::IsNull),
    195
    0
                Expr::IsTrue(expr) => expr.map_elements(f)?.update_data(Expr::IsTrue),
    196
    0
                Expr::IsFalse(expr) => expr.map_elements(f)?.update_data(Expr::IsFalse),
    197
    0
                Expr::IsUnknown(expr) => expr.map_elements(f)?.update_data(Expr::IsUnknown),
    198
    0
                Expr::IsNotTrue(expr) => expr.map_elements(f)?.update_data(Expr::IsNotTrue),
    199
    0
                Expr::IsNotFalse(expr) => expr.map_elements(f)?.update_data(Expr::IsNotFalse),
    200
    0
                Expr::IsNotUnknown(expr) => {
    201
    0
                    expr.map_elements(f)?.update_data(Expr::IsNotUnknown)
    202
                }
    203
    0
                Expr::Negative(expr) => expr.map_elements(f)?.update_data(Expr::Negative),
    204
                Expr::Between(Between {
    205
    0
                    expr,
    206
    0
                    negated,
    207
    0
                    low,
    208
    0
                    high,
    209
    0
                }) => (expr, low, high).map_elements(f)?.update_data(
    210
    0
                    |(new_expr, new_low, new_high)| {
    211
    0
                        Expr::Between(Between::new(new_expr, negated, new_low, new_high))
    212
    0
                    },
    213
                ),
    214
                Expr::Case(Case {
    215
    0
                    expr,
    216
    0
                    when_then_expr,
    217
    0
                    else_expr,
    218
    0
                }) => (expr, when_then_expr, else_expr)
    219
    0
                    .map_elements(f)?
    220
    0
                    .update_data(|(new_expr, new_when_then_expr, new_else_expr)| {
    221
    0
                        Expr::Case(Case::new(new_expr, new_when_then_expr, new_else_expr))
    222
    0
                    }),
    223
    0
                Expr::Cast(Cast { expr, data_type }) => expr
    224
    0
                    .map_elements(f)?
    225
    0
                    .update_data(|be| Expr::Cast(Cast::new(be, data_type))),
    226
    0
                Expr::TryCast(TryCast { expr, data_type }) => expr
    227
    0
                    .map_elements(f)?
    228
    0
                    .update_data(|be| Expr::TryCast(TryCast::new(be, data_type))),
    229
    0
                Expr::ScalarFunction(ScalarFunction { func, args }) => {
    230
    0
                    args.map_elements(f)?.map_data(|new_args| {
    231
    0
                        Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
    232
    0
                            func, new_args,
    233
    0
                        )))
    234
    0
                    })?
    235
                }
    236
    0
                Expr::WindowFunction(window_fun) => {
    237
                    let WindowFunction {
    238
    0
                        fun,
    239
                        params:
    240
                            WindowFunctionParams {
    241
    0
                                args,
    242
    0
                                partition_by,
    243
    0
                                order_by,
    244
    0
                                window_frame,
    245
    0
                                filter,
    246
    0
                                null_treatment,
    247
    0
                                distinct,
    248
                            },
    249
    0
                    } = *window_fun;
    250
    251
    0
                    (args, partition_by, order_by, filter)
    252
    0
                        .map_elements(f)?
    253
    0
                        .map_data(
    254
    0
                            |(new_args, new_partition_by, new_order_by, new_filter)| {
    255
    0
                                Ok(Expr::from(WindowFunction {
    256
    0
                                    fun,
    257
    0
                                    params: WindowFunctionParams {
    258
    0
                                        args: new_args,
    259
    0
                                        partition_by: new_partition_by,
    260
    0
                                        order_by: new_order_by,
    261
    0
                                        window_frame,
    262
    0
                                        filter: new_filter,
    263
    0
                                        null_treatment,
    264
    0
                                        distinct,
    265
    0
                                    },
    266
    0
                                }))
    267
    0
                            },
    268
    0
                        )?
    269
                }
    270
                Expr::AggregateFunction(AggregateFunction {
    271
    0
                    func,
    272
                    params:
    273
                        AggregateFunctionParams {
    274
    0
                            args,
    275
    0
                            distinct,
    276
    0
                            filter,
    277
    0
                            order_by,
    278
    0
                            null_treatment,
    279
                        },
    280
    0
                }) => (args, filter, order_by).map_elements(f)?.map_data(
    281
    0
                    |(new_args, new_filter, new_order_by)| {
    282
    0
                        Ok(Expr::AggregateFunction(AggregateFunction::new_udf(
    283
    0
                            func,
    284
    0
                            new_args,
    285
    0
                            distinct,
    286
    0
                            new_filter,
    287
    0
                            new_order_by,
    288
    0
                            null_treatment,
    289
    0
                        )))
    290
    0
                    },
    291
    0
                )?,
    292
    0
                Expr::GroupingSet(grouping_set) => match grouping_set {
    293
    0
                    GroupingSet::Rollup(exprs) => exprs
    294
    0
                        .map_elements(f)?
    295
    0
                        .update_data(|ve| Expr::GroupingSet(GroupingSet::Rollup(ve))),
    296
    0
                    GroupingSet::Cube(exprs) => exprs
    297
    0
                        .map_elements(f)?
    298
    0
                        .update_data(|ve| Expr::GroupingSet(GroupingSet::Cube(ve))),
    299
    0
                    GroupingSet::GroupingSets(lists_of_exprs) => lists_of_exprs
    300
    0
                        .map_elements(f)?
    301
    0
                        .update_data(|new_lists_of_exprs| {
    302
    0
                            Expr::GroupingSet(GroupingSet::GroupingSets(new_lists_of_exprs))
    303
    0
                        }),
    304
                },
    305
                Expr::InList(InList {
    306
    0
                    expr,
    307
    0
                    list,
    308
    0
                    negated,
    309
    0
                }) => (expr, list)
    310
    0
                    .map_elements(f)?
    311
    0
                    .update_data(|(new_expr, new_list)| {
    312
    0
                        Expr::InList(InList::new(new_expr, new_list, negated))
    313
    0
                    }),
    314
            })
    315
    0
        }
    316
    }