datafusion_functions/string/
lower.rs1use arrow::datatypes::DataType;
19use std::any::Any;
20
21use crate::string::common::to_lower;
22use crate::utils::utf8_to_str_type;
23use datafusion_common::types::logical_string;
24use datafusion_common::Result;
25use datafusion_expr::{
26 Coercion, ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
27 TypeSignatureClass, Volatility,
28};
29use 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)]
48pub struct LowerFunc {
49 signature: Signature,
50}
51
52impl Default for LowerFunc {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl LowerFunc {
59 pub fn new() -> Self {
60 Self {
61 signature: Signature::coercible(
62 vec![Coercion::new_exact(TypeSignatureClass::Native(
63 logical_string(),
64 ))],
65 Volatility::Immutable,
66 ),
67 }
68 }
69}
70
71impl ScalarUDFImpl for LowerFunc {
72 fn as_any(&self) -> &dyn Any {
73 self
74 }
75
76 fn name(&self) -> &str {
77 "lower"
78 }
79
80 fn signature(&self) -> &Signature {
81 &self.signature
82 }
83
84 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
85 utf8_to_str_type(&arg_types[0], "lower")
86 }
87
88 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
89 to_lower(&args.args, "lower")
90 }
91
92 fn documentation(&self) -> Option<&Documentation> {
93 self.doc()
94 }
95}
96
97#[cfg(test)]
98mod 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 lambdas: None,
117 };
118
119 let result = match func.invoke_with_args(args)? {
120 ColumnarValue::Array(result) => result,
121 _ => unreachable!("lower"),
122 };
123 assert_eq!(&expected, &result);
124 Ok(())
125 }
126
127 #[test]
128 fn lower_maybe_optimization() -> Result<()> {
129 let input = Arc::new(StringArray::from(vec![
130 Some("农历新年"),
131 None,
132 Some("DATAFUSION"),
133 Some("0123456789"),
134 Some(""),
135 ])) as ArrayRef;
136
137 let expected = Arc::new(StringArray::from(vec![
138 Some("农历新年"),
139 None,
140 Some("datafusion"),
141 Some("0123456789"),
142 Some(""),
143 ])) as ArrayRef;
144
145 to_lower(input, expected)
146 }
147
148 #[test]
149 fn lower_full_optimization() -> Result<()> {
150 let input = Arc::new(StringArray::from(vec![
151 Some("ARROW"),
152 None,
153 Some("DATAFUSION"),
154 Some("0123456789"),
155 Some(""),
156 ])) as ArrayRef;
157
158 let expected = Arc::new(StringArray::from(vec![
159 Some("arrow"),
160 None,
161 Some("datafusion"),
162 Some("0123456789"),
163 Some(""),
164 ])) as ArrayRef;
165
166 to_lower(input, expected)
167 }
168
169 #[test]
170 fn lower_partial_optimization() -> Result<()> {
171 let input = Arc::new(StringArray::from(vec![
172 Some("ARROW"),
173 None,
174 Some("DATAFUSION"),
175 Some("@_"),
176 Some("0123456789"),
177 Some(""),
178 Some("\t\n"),
179 Some("ὈΔΥΣΣΕΎΣ"),
180 Some("TSCHÜSS"),
181 Some("Ⱦ"), Some("农历新年"),
183 ])) as ArrayRef;
184
185 let expected = Arc::new(StringArray::from(vec![
186 Some("arrow"),
187 None,
188 Some("datafusion"),
189 Some("@_"),
190 Some("0123456789"),
191 Some(""),
192 Some("\t\n"),
193 Some("ὀδυσσεύς"),
194 Some("tschüss"),
195 Some("ⱦ"),
196 Some("农历新年"),
197 ])) as ArrayRef;
198
199 to_lower(input, expected)
200 }
201}