## 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/functions/src/core/greatest.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
    use crate::core::greatest_least_utils::GreatestLeastOperator;
    19
    use arrow::array::{make_comparator, Array, BooleanArray};
    20
    use arrow::buffer::BooleanBuffer;
    21
    use arrow::compute::kernels::cmp;
    22
    use arrow::compute::SortOptions;
    23
    use arrow::datatypes::DataType;
    24
    use datafusion_common::{
    25
        assert_eq_or_internal_err, DataFusionError, Result, ScalarValue,
    26
    };
    27
    use datafusion_doc::Documentation;
    28
    use datafusion_expr::{ColumnarValue, ScalarFunctionArgs};
    29
    use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
    30
    use datafusion_macros::user_doc;
    31
    use std::any::Any;
    32
    33
    const SORT_OPTIONS: SortOptions = SortOptions {
    34
        // We want greatest first
    35
        descending: false,
    36
    37
        // NULL will be less than any other value
    38
        nulls_first: true,
    39
    };
    40
    41
    #[user_doc(
    42
        doc_section(label = "Conditional Functions"),
    43
        description = "Returns the greatest value in a list of expressions. Returns _null_ if all expressions are _null_.",
    44
        syntax_example = "greatest(expression1[, ..., expression_n])",
    45
        sql_example = r#"```sql
    46
    > select greatest(4, 7, 5);
    47
    +---------------------------+
    48
    | greatest(4,7,5)           |
    49
    +---------------------------+
    50
    | 7                         |
    51
    +---------------------------+
    52
    ```"#,
    53
        argument(
    54
            name = "expression1, expression_n",
    55
            description = "Expressions to compare and return the greatest value.. Can be a constant, column, or function, and any combination of arithmetic operators. Pass as many expression arguments as necessary."
    56
        )
    57
    )]
    58
    #[derive(Debug, PartialEq, Eq, Hash)]
    59
    pub struct GreatestFunc {
    60
        signature: Signature,
    61
    }
    62
    63
    impl Default for GreatestFunc {
    64
    0
        fn default() -> Self {
    65
    0
            GreatestFunc::new()
    66
    0
        }
    67
    }
    68
    69
    impl GreatestFunc {
    70
    0
        pub fn new() -> Self {
    71
    0
            Self {
    72
    0
                signature: Signature::user_defined(Volatility::Immutable),
    73
    0
            }
    74
    0
        }
    75
    }
    76
    77
    impl GreatestLeastOperator for GreatestFunc {
    78
        const NAME: &'static str = "greatest";
    79
    80
    0
        fn keep_scalar<'a>(
    81
    0
            lhs: &'a ScalarValue,
    82
    0
            rhs: &'a ScalarValue,
    83
    0
        ) -> Result<&'a ScalarValue> {
    84
    0
            if !lhs.data_type().is_nested() {
    85
    0
                return if lhs >= rhs { Ok(lhs) } else { Ok(rhs) };
    86
    0
            }
    87
    88
            // If complex type we can't compare directly as we want null values to be smaller
    89
    0
            let cmp = make_comparator(
    90
    0
                lhs.to_array()?.as_ref(),
    91
    0
                rhs.to_array()?.as_ref(),
    92
                SORT_OPTIONS,
    93
    0
            )?;
    94
    95
    0
            if cmp(0, 0).is_ge() {
    96
    0
                Ok(lhs)
    97
            } else {
    98
    0
                Ok(rhs)
    99
            }
    100
    0
        }
    101
    102
        /// Return boolean array where `arr[i] = lhs[i] >= rhs[i]` for all i, where `arr` is the result array
    103
        /// Nulls are always considered smaller than any other value
    104
    0
        fn get_indexes_to_keep(lhs: &dyn Array, rhs: &dyn Array) -> Result<BooleanArray> {
    105
            // Fast path:
    106
            // If both arrays are not nested, have the same length and no nulls, we can use the faster vectorized kernel
    107
            // - If both arrays are not nested: Nested types, such as lists, are not supported as the null semantics are not well-defined.
    108
            // - both array does not have any nulls: cmp::gt_eq will return null if any of the input is null while we want to return false in that case
    109
    0
            if !lhs.data_type().is_nested()
    110
    0
                && lhs.logical_null_count() == 0
    111
    0
                && rhs.logical_null_count() == 0
    112
            {
    113
    0
                return cmp::gt_eq(&lhs, &rhs).map_err(|e| e.into());
    114
    0
            }
    115
    116
    0
            let cmp = make_comparator(lhs, rhs, SORT_OPTIONS)?;
    117
    118
    0
            assert_eq_or_internal_err!(
    119
    0
                lhs.len(),
    120
    0
                rhs.len(),
    121
    0
                "All arrays should have the same length for greatest comparison"
    122
            );
    123
    124
    0
            let values = BooleanBuffer::collect_bool(lhs.len(), |i| cmp(i, i).is_ge());
    125
    126
            // No nulls as we only want to keep the values that are larger, its either true or false
    127
    0
            Ok(BooleanArray::new(values, None))
    128
    0
        }
    129
    }
    130
    131
    impl ScalarUDFImpl for GreatestFunc {
    132
    0
        fn as_any(&self) -> &dyn Any {
    133
    0
            self
    134
    0
        }
    135
    136
    0
        fn name(&self) -> &str {
    137
    0
            "greatest"
    138
    0
        }
    139
    140
    0
        fn signature(&self) -> &Signature {
    141
    0
            &self.signature
    142
    0
        }
    143
    144
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    145
    0
            Ok(arg_types[0].clone())
    146
    0
        }
    147
    148
    0
        fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
    149
    0
            super::greatest_least_utils::execute_conditional::<Self>(&args.args)
    150
    0
        }
    151
    152
    0
        fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
    153
    0
            let coerced_type =
    154
    0
                super::greatest_least_utils::find_coerced_type::<Self>(arg_types)?;
    155
    156
    0
            Ok(vec![coerced_type; arg_types.len()])
    157
    0
        }
    158
    159
    0
        fn documentation(&self) -> Option<&Documentation> {
    160
    0
            self.doc()
    161
    0
        }
    162
    }
    163
    164
    #[cfg(test)]
    165
    mod test {
    166
        use crate::core;
    167
        use arrow::datatypes::DataType;
    168
        use datafusion_expr::ScalarUDFImpl;
    169
    170
        #[test]
    171
        fn test_greatest_return_types_without_common_supertype_in_arg_type() {
    172
            let greatest = core::greatest::GreatestFunc::new();
    173
            let return_type = greatest
    174
                .coerce_types(&[DataType::Decimal128(10, 3), DataType::Decimal128(10, 4)])
    175
                .unwrap();
    176
            assert_eq!(
    177
                return_type,
    178
                vec![DataType::Decimal128(11, 4), DataType::Decimal128(11, 4)]
    179
            );
    180
        }
    181
    }