datafusion_functions/string/
ltrim.rs1use arrow::array::{ArrayRef, AsArray};
19use arrow::datatypes::DataType;
20use std::sync::Arc;
21
22use crate::string::common::*;
23use crate::utils::make_scalar_function;
24use datafusion_common::types::logical_string;
25use datafusion_common::{Result, exec_err};
26use datafusion_expr::function::Hint;
27use datafusion_expr::{
28 Coercion, ColumnarValue, Documentation, EncodingPreservation, ScalarFunctionArgs,
29 ScalarUDFImpl, Signature, TypeSignature, TypeSignatureClass, Volatility,
30};
31use datafusion_macros::user_doc;
32
33fn ltrim(args: &[ArrayRef]) -> Result<ArrayRef> {
36 let args = if args.len() > 1 {
37 let arg1 = arrow::compute::kernels::cast::cast(&args[1], args[0].data_type())?;
38 vec![Arc::clone(&args[0]), arg1]
39 } else {
40 args.to_owned()
41 };
42 match args[0].data_type() {
43 DataType::Utf8 => general_trim::<i32, TrimLeft>(&args, false),
44 DataType::LargeUtf8 => general_trim::<i64, TrimLeft>(&args, false),
45 DataType::Utf8View => general_trim::<i32, TrimLeft>(&args, true),
46 DataType::Dictionary(_, _) => {
47 let dictionary = args[0].as_any_dictionary();
48 let trimmed = ltrim(&[Arc::clone(dictionary.values())])?;
49 Ok(dictionary.with_values(trimmed))
50 }
51 other => exec_err!(
52 "Unsupported data type {other:?} for function ltrim, expected Utf8, LargeUtf8 or Utf8View."
53 ),
54 }
55}
56
57#[user_doc(
58 doc_section(label = "String Functions"),
59 description = "Trims the specified trim string from the beginning of a string. If no trim string is provided, spaces are removed from the start of the input string.",
60 syntax_example = "ltrim(str[, trim_str])",
61 sql_example = r#"```sql
62> select ltrim(' datafusion ');
63+-------------------------------+
64| ltrim(Utf8(" datafusion ")) |
65+-------------------------------+
66| datafusion |
67+-------------------------------+
68> select ltrim('___datafusion___', '_');
69+-------------------------------------------+
70| ltrim(Utf8("___datafusion___"),Utf8("_")) |
71+-------------------------------------------+
72| datafusion___ |
73+-------------------------------------------+
74```"#,
75 standard_argument(name = "str", prefix = "String"),
76 argument(
77 name = "trim_str",
78 description = r"String expression to trim from the beginning of the input string. Can be a constant, column, or function, and any combination of arithmetic operators. _Default is a space._"
79 ),
80 alternative_syntax = "trim(LEADING trim_str FROM str)",
81 related_udf(name = "btrim"),
82 related_udf(name = "rtrim")
83)]
84#[derive(Debug, PartialEq, Eq, Hash)]
85pub struct LtrimFunc {
86 signature: Signature,
87}
88
89impl Default for LtrimFunc {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl LtrimFunc {
96 pub fn new() -> Self {
97 Self {
98 signature: Signature::one_of(
99 vec![
100 TypeSignature::Coercible(vec![
101 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
102 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
103 ]),
104 TypeSignature::Coercible(vec![
105 Coercion::new_exact(TypeSignatureClass::Native(logical_string()))
106 .with_encoding_preservation(
107 EncodingPreservation::dictionary(),
108 ),
109 ]),
110 ],
111 Volatility::Immutable,
112 ),
113 }
114 }
115}
116
117impl ScalarUDFImpl for LtrimFunc {
118 fn name(&self) -> &str {
119 "ltrim"
120 }
121
122 fn signature(&self) -> &Signature {
123 &self.signature
124 }
125
126 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
127 Ok(arg_types[0].clone())
128 }
129
130 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
131 make_scalar_function(ltrim, vec![Hint::Pad, Hint::AcceptsSingular])(&args.args)
132 }
133
134 fn documentation(&self) -> Option<&Documentation> {
135 self.doc()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use arrow::array::{Array, StringArray, StringViewArray};
142 use arrow::datatypes::DataType::{Utf8, Utf8View};
143
144 use datafusion_common::{Result, ScalarValue};
145 use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
146
147 use crate::string::ltrim::LtrimFunc;
148 use crate::utils::test::test_function;
149
150 #[test]
151 fn test_functions() {
152 test_function!(
154 LtrimFunc::new(),
155 vec![ColumnarValue::Scalar(ScalarValue::Utf8View(Some(
156 String::from("alphabet ")
157 ))),],
158 Ok(Some("alphabet ")),
159 &str,
160 Utf8View,
161 StringViewArray
162 );
163 test_function!(
164 LtrimFunc::new(),
165 vec![ColumnarValue::Scalar(ScalarValue::Utf8View(Some(
166 String::from(" alphabet ")
167 ))),],
168 Ok(Some("alphabet ")),
169 &str,
170 Utf8View,
171 StringViewArray
172 );
173 test_function!(
174 LtrimFunc::new(),
175 vec![
176 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from(
177 "alphabet"
178 )))),
179 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from("t")))),
180 ],
181 Ok(Some("alphabet")),
182 &str,
183 Utf8View,
184 StringViewArray
185 );
186 test_function!(
187 LtrimFunc::new(),
188 vec![
189 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from(
190 "alphabet"
191 )))),
192 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from(
193 "alphabe"
194 )))),
195 ],
196 Ok(Some("t")),
197 &str,
198 Utf8View,
199 StringViewArray
200 );
201 test_function!(
202 LtrimFunc::new(),
203 vec![
204 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from(
205 "alphabet"
206 )))),
207 ColumnarValue::Scalar(ScalarValue::Utf8View(None)),
208 ],
209 Ok(None),
210 &str,
211 Utf8View,
212 StringViewArray
213 );
214 test_function!(
216 LtrimFunc::new(),
217 vec![
218 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from(
219 "xxxalphabetalphabet"
220 )))),
221 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from("x")))),
222 ],
223 Ok(Some("alphabetalphabet")),
224 &str,
225 Utf8View,
226 StringViewArray
227 );
228 test_function!(
230 LtrimFunc::new(),
231 vec![ColumnarValue::Scalar(ScalarValue::Utf8(Some(
232 String::from("alphabet ")
233 ))),],
234 Ok(Some("alphabet ")),
235 &str,
236 Utf8,
237 StringArray
238 );
239 test_function!(
240 LtrimFunc::new(),
241 vec![ColumnarValue::Scalar(ScalarValue::Utf8(Some(
242 String::from("alphabet ")
243 ))),],
244 Ok(Some("alphabet ")),
245 &str,
246 Utf8,
247 StringArray
248 );
249 test_function!(
250 LtrimFunc::new(),
251 vec![
252 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("alphabet")))),
253 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("t")))),
254 ],
255 Ok(Some("alphabet")),
256 &str,
257 Utf8,
258 StringArray
259 );
260 test_function!(
261 LtrimFunc::new(),
262 vec![
263 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("alphabet")))),
264 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("alphabe")))),
265 ],
266 Ok(Some("t")),
267 &str,
268 Utf8,
269 StringArray
270 );
271 test_function!(
272 LtrimFunc::new(),
273 vec![
274 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("alphabet")))),
275 ColumnarValue::Scalar(ScalarValue::Utf8(None)),
276 ],
277 Ok(None),
278 &str,
279 Utf8,
280 StringArray
281 );
282 }
283}