## 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/lower.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 arrow::datatypes::DataType;
    19
    use std::any::Any;
    20
    21
    use crate::string::common::to_lower;
    22
    use crate::utils::utf8_to_str_type;
    23
    use datafusion_common::types::logical_string;
    24
    use datafusion_common::Result;
    25
    use datafusion_expr::{
    26
        Coercion, ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
    27
        TypeSignatureClass, Volatility,
    28
    };
    29
    use datafusion_macros::user_doc;
    30
    31
    #[user_doc(
    32
        doc_section(label = "String Functions"),
    33
        description = "Converts a string to lower-case.",
    34
        syntax_example = "lower(str)",
    35
        sql_example = r#"```sql
    36
    > select lower('Ångström');
    37
    +-------------------------+
    38
    | lower(Utf8("Ångström")) |
    39
    +-------------------------+
    40
    | ångström                |
    41
    +-------------------------+
    42
    ```"#,
    43
        standard_argument(name = "str", prefix = "String"),
    44
        related_udf(name = "initcap"),
    45
        related_udf(name = "upper")
    46
    )]
    47
    #[derive(Debug, PartialEq, Eq, Hash)]
    48
    pub struct LowerFunc {
    49
        signature: Signature,
    50
    }
    51
    52
    impl Default for LowerFunc {
    53
    0
        fn default() -> Self {
    54
    0
            Self::new()
    55
    0
        }
    56
    }
    57
    58
    impl LowerFunc {
    59
    0
        pub fn new() -> Self {
    60
    0
            Self {
    61
    0
                signature: Signature::coercible(
    62
    0
                    vec![Coercion::new_exact(TypeSignatureClass::Native(
    63
    0
                        logical_string(),
    64
    0
                    ))],
    65
    0
                    Volatility::Immutable,
    66
    0
                ),
    67
    0
            }
    68
    0
        }
    69
    }
    70
    71
    impl ScalarUDFImpl for LowerFunc {
    72
    0
        fn as_any(&self) -> &dyn Any {
    73
    0
            self
    74
    0
        }
    75
    76
    0
        fn name(&self) -> &str {
    77
    0
            "lower"
    78
    0
        }
    79
    80
    0
        fn signature(&self) -> &Signature {
    81
    0
            &self.signature
    82
    0
        }
    83
    84
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    85
    0
            utf8_to_str_type(&arg_types[0], "lower")
    86
    0
        }
    87
    88
    0
        fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
    89
    0
            to_lower(&args.args, "lower")
    90
    0
        }
    91
    92
    0
        fn documentation(&self) -> Option<&Documentation> {
    93
    0
            self.doc()
    94
    0
        }
    95
    }
    96
    97
    #[cfg(test)]
    98
    mod tests {
    99
        use super::*;
    100
        use arrow::array::{Array, ArrayRef, StringArray};
    101
        use arrow::datatypes::DataType::Utf8;
    102
        use arrow::datatypes::Field;
    103
        use datafusion_common::config::ConfigOptions;
    104
        use std::sync::Arc;
    105
    106
        fn to_lower(input: ArrayRef, expected: ArrayRef) -> Result<()> {
    107
            let func = LowerFunc::new();
    108
            let arg_fields = vec![Field::new("a", input.data_type().clone(), true).into()];
    109
    110
            let args = ScalarFunctionArgs {
    111
                number_rows: input.len(),
    112
                args: vec![ColumnarValue::Array(input)],
    113
                arg_fields,
    114
                return_field: Field::new("f", Utf8, true).into(),
    115
                config_options: Arc::new(ConfigOptions::default()),
    116
            };
    117
    118
            let result = match func.invoke_with_args(args)? {
    119
                ColumnarValue::Array(result) => result,
    120
                _ => unreachable!("lower"),
    121
            };
    122
            assert_eq!(&expected, &result);
    123
            Ok(())
    124
        }
    125
    126
        #[test]
    127
        fn lower_maybe_optimization() -> Result<()> {
    128
            let input = Arc::new(StringArray::from(vec![
    129
                Some("农历新年"),
    130
                None,
    131
                Some("DATAFUSION"),
    132
                Some("0123456789"),
    133
                Some(""),
    134
            ])) as ArrayRef;
    135
    136
            let expected = Arc::new(StringArray::from(vec![
    137
                Some("农历新年"),
    138
                None,
    139
                Some("datafusion"),
    140
                Some("0123456789"),
    141
                Some(""),
    142
            ])) as ArrayRef;
    143
    144
            to_lower(input, expected)
    145
        }
    146
    147
        #[test]
    148
        fn lower_full_optimization() -> Result<()> {
    149
            let input = Arc::new(StringArray::from(vec![
    150
                Some("ARROW"),
    151
                None,
    152
                Some("DATAFUSION"),
    153
                Some("0123456789"),
    154
                Some(""),
    155
            ])) as ArrayRef;
    156
    157
            let expected = Arc::new(StringArray::from(vec![
    158
                Some("arrow"),
    159
                None,
    160
                Some("datafusion"),
    161
                Some("0123456789"),
    162
                Some(""),
    163
            ])) as ArrayRef;
    164
    165
            to_lower(input, expected)
    166
        }
    167
    168
        #[test]
    169
        fn lower_partial_optimization() -> Result<()> {
    170
            let input = Arc::new(StringArray::from(vec![
    171
                Some("ARROW"),
    172
                None,
    173
                Some("DATAFUSION"),
    174
                Some("@_"),
    175
                Some("0123456789"),
    176
                Some(""),
    177
                Some("\t\n"),
    178
                Some("ὈΔΥΣΣΕΎΣ"),
    179
                Some("TSCHÜSS"),
    180
                Some("Ⱦ"), // ⱦ: length change
    181
                Some("农历新年"),
    182
            ])) as ArrayRef;
    183
    184
            let expected = Arc::new(StringArray::from(vec![
    185
                Some("arrow"),
    186
                None,
    187
                Some("datafusion"),
    188
                Some("@_"),
    189
                Some("0123456789"),
    190
                Some(""),
    191
                Some("\t\n"),
    192
                Some("ὀδυσσεύς"),
    193
                Some("tschüss"),
    194
                Some("ⱦ"),
    195
                Some("农历新年"),
    196
            ])) as ArrayRef;
    197
    198
            to_lower(input, expected)
    199
        }
    200
    }