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 };
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("Ⱦ"), 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}