datafusion_functions/string/
btrim.rs1use crate::string::common::*;
19use crate::utils::make_scalar_function;
20use arrow::array::{ArrayRef, AsArray};
21use arrow::datatypes::DataType;
22use datafusion_common::types::logical_string;
23use datafusion_common::{Result, exec_err};
24use datafusion_expr::function::Hint;
25use datafusion_expr::{
26 Coercion, ColumnarValue, Documentation, EncodingPreservation, ScalarFunctionArgs,
27 ScalarUDFImpl, Signature, TypeSignature, TypeSignatureClass, Volatility,
28};
29use datafusion_macros::user_doc;
30use std::sync::Arc;
31
32fn btrim(args: &[ArrayRef]) -> Result<ArrayRef> {
35 let args = if args.len() > 1 {
36 let arg1 = arrow::compute::kernels::cast::cast(&args[1], args[0].data_type())?;
37 vec![Arc::clone(&args[0]), arg1]
38 } else {
39 args.to_owned()
40 };
41 match args[0].data_type() {
42 DataType::Utf8 => general_trim::<i32, TrimBoth>(&args, false),
43 DataType::LargeUtf8 => general_trim::<i64, TrimBoth>(&args, false),
44 DataType::Utf8View => general_trim::<i32, TrimBoth>(&args, true),
45 DataType::Dictionary(_, _) => {
46 let dictionary = args[0].as_any_dictionary();
47 let trimmed = btrim(&[Arc::clone(dictionary.values())])?;
48 Ok(dictionary.with_values(trimmed))
49 }
50 other => exec_err!(
51 "Unsupported data type {other:?} for function btrim, expected Utf8, LargeUtf8 or Utf8View."
52 ),
53 }
54}
55
56#[user_doc(
57 doc_section(label = "String Functions"),
58 description = "Trims the specified trim string from the start and end of a string. If no trim string is provided, all spaces are removed from the start and end of the input string.",
59 syntax_example = "btrim(str[, trim_str])",
60 sql_example = r#"```sql
61> select btrim('__datafusion____', '_');
62+-------------------------------------------+
63| btrim(Utf8("__datafusion____"),Utf8("_")) |
64+-------------------------------------------+
65| datafusion |
66+-------------------------------------------+
67```"#,
68 standard_argument(name = "str", prefix = "String"),
69 argument(
70 name = "trim_str",
71 description = r"String expression to operate on. Can be a constant, column, or function, and any combination of operators. _Default is a space._"
72 ),
73 alternative_syntax = "trim(BOTH trim_str FROM str)",
74 alternative_syntax = "trim(trim_str FROM str)",
75 related_udf(name = "ltrim"),
76 related_udf(name = "rtrim")
77)]
78#[derive(Debug, PartialEq, Eq, Hash)]
79pub struct BTrimFunc {
80 signature: Signature,
81 aliases: Vec<String>,
82}
83
84impl Default for BTrimFunc {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90impl BTrimFunc {
91 pub fn new() -> Self {
92 Self {
93 signature: Signature::one_of(
94 vec![
95 TypeSignature::Coercible(vec![
96 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
97 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
98 ]),
99 TypeSignature::Coercible(vec![
100 Coercion::new_exact(TypeSignatureClass::Native(logical_string()))
101 .with_encoding_preservation(
102 EncodingPreservation::dictionary(),
103 ),
104 ]),
105 ],
106 Volatility::Immutable,
107 ),
108 aliases: vec![String::from("trim")],
109 }
110 }
111}
112
113impl ScalarUDFImpl for BTrimFunc {
114 fn name(&self) -> &str {
115 "btrim"
116 }
117
118 fn signature(&self) -> &Signature {
119 &self.signature
120 }
121
122 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
123 Ok(arg_types[0].clone())
124 }
125
126 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
127 make_scalar_function(btrim, vec![Hint::Pad, Hint::AcceptsSingular])(&args.args)
128 }
129
130 fn aliases(&self) -> &[String] {
131 &self.aliases
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::btrim::BTrimFunc;
148 use crate::utils::test::test_function;
149
150 #[test]
151 fn test_functions() {
152 test_function!(
154 BTrimFunc::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 BTrimFunc::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 BTrimFunc::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("alphabe")),
182 &str,
183 Utf8View,
184 StringViewArray
185 );
186 test_function!(
187 BTrimFunc::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 BTrimFunc::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 BTrimFunc::new(),
217 vec![
218 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from(
219 "xxxalphabetalphabetxxx"
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 BTrimFunc::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 BTrimFunc::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 BTrimFunc::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("alphabe")),
256 &str,
257 Utf8,
258 StringArray
259 );
260 test_function!(
261 BTrimFunc::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 BTrimFunc::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}