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)]
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 std::sync::Arc;
102
103 fn to_lower(input: ArrayRef, expected: ArrayRef) -> Result<()> {
104 let func = LowerFunc::new();
105
106 let args = ScalarFunctionArgs {
107 number_rows: input.len(),
108 args: vec![ColumnarValue::Array(input)],
109 return_type: &DataType::Utf8,
110 };
111
112 let result = match func.invoke_with_args(args)? {
113 ColumnarValue::Array(result) => result,
114 _ => unreachable!("lower"),
115 };
116 assert_eq!(&expected, &result);
117 Ok(())
118 }
119
120 #[test]
121 fn lower_maybe_optimization() -> Result<()> {
122 let input = Arc::new(StringArray::from(vec![
123 Some("农历新年"),
124 None,
125 Some("DATAFUSION"),
126 Some("0123456789"),
127 Some(""),
128 ])) as ArrayRef;
129
130 let expected = Arc::new(StringArray::from(vec![
131 Some("农历新年"),
132 None,
133 Some("datafusion"),
134 Some("0123456789"),
135 Some(""),
136 ])) as ArrayRef;
137
138 to_lower(input, expected)
139 }
140
141 #[test]
142 fn lower_full_optimization() -> Result<()> {
143 let input = Arc::new(StringArray::from(vec![
144 Some("ARROW"),
145 None,
146 Some("DATAFUSION"),
147 Some("0123456789"),
148 Some(""),
149 ])) as ArrayRef;
150
151 let expected = Arc::new(StringArray::from(vec![
152 Some("arrow"),
153 None,
154 Some("datafusion"),
155 Some("0123456789"),
156 Some(""),
157 ])) as ArrayRef;
158
159 to_lower(input, expected)
160 }
161
162 #[test]
163 fn lower_partial_optimization() -> Result<()> {
164 let input = Arc::new(StringArray::from(vec![
165 Some("ARROW"),
166 None,
167 Some("DATAFUSION"),
168 Some("@_"),
169 Some("0123456789"),
170 Some(""),
171 Some("\t\n"),
172 Some("ὈΔΥΣΣΕΎΣ"),
173 Some("TSCHÜSS"),
174 Some("Ⱦ"), Some("农历新年"),
176 ])) as ArrayRef;
177
178 let expected = Arc::new(StringArray::from(vec![
179 Some("arrow"),
180 None,
181 Some("datafusion"),
182 Some("@_"),
183 Some("0123456789"),
184 Some(""),
185 Some("\t\n"),
186 Some("ὀδυσσεύς"),
187 Some("tschüss"),
188 Some("ⱦ"),
189 Some("农历新年"),
190 ])) as ArrayRef;
191
192 to_lower(input, expected)
193 }
194}