## 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/string/ends_with.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 std::any::Any;
    19
    use std::sync::Arc;
    20
    21
    use arrow::array::ArrayRef;
    22
    use arrow::datatypes::DataType;
    23
    24
    use crate::utils::make_scalar_function;
    25
    use datafusion_common::types::logical_string;
    26
    use datafusion_common::{internal_err, Result};
    27
    use datafusion_expr::binary::{binary_to_string_coercion, string_coercion};
    28
    use datafusion_expr::{
    29
        Coercion, ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
    30
        TypeSignatureClass, Volatility,
    31
    };
    32
    use datafusion_macros::user_doc;
    33
    34
    #[user_doc(
    35
        doc_section(label = "String Functions"),
    36
        description = "Tests if a string ends with a substring.",
    37
        syntax_example = "ends_with(str, substr)",
    38
        sql_example = r#"```sql
    39
    >  select ends_with('datafusion', 'soin');
    40
    +--------------------------------------------+
    41
    | ends_with(Utf8("datafusion"),Utf8("soin")) |
    42
    +--------------------------------------------+
    43
    | false                                      |
    44
    +--------------------------------------------+
    45
    > select ends_with('datafusion', 'sion');
    46
    +--------------------------------------------+
    47
    | ends_with(Utf8("datafusion"),Utf8("sion")) |
    48
    +--------------------------------------------+
    49
    | true                                       |
    50
    +--------------------------------------------+
    51
    ```"#,
    52
        standard_argument(name = "str", prefix = "String"),
    53
        argument(name = "substr", description = "Substring to test for.")
    54
    )]
    55
    #[derive(Debug, PartialEq, Eq, Hash)]
    56
    pub struct EndsWithFunc {
    57
        signature: Signature,
    58
    }
    59
    60
    impl Default for EndsWithFunc {
    61
    0
        fn default() -> Self {
    62
    0
            EndsWithFunc::new()
    63
    0
        }
    64
    }
    65
    66
    impl EndsWithFunc {
    67
    0
        pub fn new() -> Self {
    68
    0
            Self {
    69
    0
                signature: Signature::coercible(
    70
    0
                    vec![
    71
    0
                        Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
    72
    0
                        Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
    73
    0
                    ],
    74
    0
                    Volatility::Immutable,
    75
    0
                ),
    76
    0
            }
    77
    0
        }
    78
    }
    79
    80
    impl ScalarUDFImpl for EndsWithFunc {
    81
    0
        fn as_any(&self) -> &dyn Any {
    82
    0
            self
    83
    0
        }
    84
    85
    0
        fn name(&self) -> &str {
    86
    0
            "ends_with"
    87
    0
        }
    88
    89
    0
        fn signature(&self) -> &Signature {
    90
    0
            &self.signature
    91
    0
        }
    92
    93
    0
        fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
    94
    0
            Ok(DataType::Boolean)
    95
    0
        }
    96
    97
    0
        fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
    98
    0
            match args.args[0].data_type() {
    99
                DataType::Utf8View | DataType::Utf8 | DataType::LargeUtf8 => {
    100
    0
                    make_scalar_function(ends_with, vec![])(&args.args)
    101
                }
    102
    0
                other => {
    103
    0
                    internal_err!("Unsupported data type {other:?} for function ends_with. Expected Utf8, LargeUtf8 or Utf8View")?
    104
                }
    105
            }
    106
    0
        }
    107
    108
    0
        fn documentation(&self) -> Option<&Documentation> {
    109
    0
            self.doc()
    110
    0
        }
    111
    }
    112
    113
    /// Returns true if string ends with suffix.
    114
    /// ends_with('alphabet', 'abet') = 't'
    115
    0
    fn ends_with(args: &[ArrayRef]) -> Result<ArrayRef> {
    116
    0
        if let Some(coercion_data_type) =
    117
    0
            string_coercion(args[0].data_type(), args[1].data_type()).or_else(|| {
    118
    0
                binary_to_string_coercion(args[0].data_type(), args[1].data_type())
    119
    0
            })
    120
        {
    121
    0
            let arg0 = if args[0].data_type() == &coercion_data_type {
    122
    0
                Arc::clone(&args[0])
    123
            } else {
    124
    0
                arrow::compute::kernels::cast::cast(&args[0], &coercion_data_type)?
    125
            };
    126
    0
            let arg1 = if args[1].data_type() == &coercion_data_type {
    127
    0
                Arc::clone(&args[1])
    128
            } else {
    129
    0
                arrow::compute::kernels::cast::cast(&args[1], &coercion_data_type)?
    130
            };
    131
    0
            let result = arrow::compute::kernels::comparison::ends_with(&arg0, &arg1)?;
    132
    0
            Ok(Arc::new(result) as ArrayRef)
    133
        } else {
    134
    0
            internal_err!(
    135
    0
                "Unsupported data types for ends_with. Expected Utf8, LargeUtf8 or Utf8View"
    136
            )
    137
        }
    138
    0
    }
    139
    140
    #[cfg(test)]
    141
    mod tests {
    142
        use arrow::array::{Array, BooleanArray};
    143
        use arrow::datatypes::DataType::Boolean;
    144
    145
        use datafusion_common::Result;
    146
        use datafusion_common::ScalarValue;
    147
        use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
    148
    149
        use crate::string::ends_with::EndsWithFunc;
    150
        use crate::utils::test::test_function;
    151
    152
        #[test]
    153
        fn test_functions() -> Result<()> {
    154
            test_function!(
    155
                EndsWithFunc::new(),
    156
                vec![
    157
                    ColumnarValue::Scalar(ScalarValue::from("alphabet")),
    158
                    ColumnarValue::Scalar(ScalarValue::from("alph")),
    159
                ],
    160
                Ok(Some(false)),
    161
                bool,
    162
                Boolean,
    163
                BooleanArray
    164
            );
    165
            test_function!(
    166
                EndsWithFunc::new(),
    167
                vec![
    168
                    ColumnarValue::Scalar(ScalarValue::from("alphabet")),
    169
                    ColumnarValue::Scalar(ScalarValue::from("bet")),
    170
                ],
    171
                Ok(Some(true)),
    172
                bool,
    173
                Boolean,
    174
                BooleanArray
    175
            );
    176
            test_function!(
    177
                EndsWithFunc::new(),
    178
                vec![
    179
                    ColumnarValue::Scalar(ScalarValue::Utf8(None)),
    180
                    ColumnarValue::Scalar(ScalarValue::from("alph")),
    181
                ],
    182
                Ok(None),
    183
                bool,
    184
                Boolean,
    185
                BooleanArray
    186
            );
    187
            test_function!(
    188
                EndsWithFunc::new(),
    189
                vec![
    190
                    ColumnarValue::Scalar(ScalarValue::from("alphabet")),
    191
                    ColumnarValue::Scalar(ScalarValue::Utf8(None)),
    192
                ],
    193
                Ok(None),
    194
                bool,
    195
                Boolean,
    196
                BooleanArray
    197
            );
    198
    199
            Ok(())
    200
        }
    201
    }