1use crate::strings::{
19 BulkNullStringArrayBuilder, GenericStringArrayBuilder, StringViewArrayBuilder,
20};
21use crate::utils::utf8_to_str_type;
22use arrow::array::{Array, ArrayRef, AsArray, Int64Array, StringArrayType};
23use arrow::buffer::NullBuffer;
24use arrow::datatypes::DataType;
25use arrow::datatypes::DataType::{LargeUtf8, Utf8, Utf8View};
26use datafusion_common::cast::as_int64_array;
27use datafusion_common::types::{NativeType, logical_int64, logical_string};
28use datafusion_common::utils::take_function_args;
29use datafusion_common::{
30 DataFusionError, Result, ScalarValue, exec_datafusion_err, exec_err, internal_err,
31};
32use datafusion_expr::{ColumnarValue, Documentation, Volatility};
33use datafusion_expr::{ScalarFunctionArgs, ScalarUDFImpl, Signature};
34use datafusion_expr_common::signature::{Coercion, TypeSignatureClass};
35use datafusion_macros::user_doc;
36
37#[user_doc(
38 doc_section(label = "String Functions"),
39 description = "Returns a string with an input string repeated a specified number.",
40 syntax_example = "repeat(str, n)",
41 sql_example = r#"```sql
42> select repeat('data', 3);
43+-------------------------------+
44| repeat(Utf8("data"),Int64(3)) |
45+-------------------------------+
46| datadatadata |
47+-------------------------------+
48```"#,
49 standard_argument(name = "str", prefix = "String"),
50 argument(
51 name = "n",
52 description = "Number of times to repeat the input string."
53 )
54)]
55#[derive(Debug, PartialEq, Eq, Hash)]
56pub struct RepeatFunc {
57 signature: Signature,
58}
59
60impl Default for RepeatFunc {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl RepeatFunc {
67 pub fn new() -> Self {
68 Self {
69 signature: Signature::coercible(
70 vec![
71 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
72 Coercion::new_implicit(
74 TypeSignatureClass::Native(logical_int64()),
75 vec![TypeSignatureClass::Integer],
76 NativeType::Int64,
77 ),
78 ],
79 Volatility::Immutable,
80 ),
81 }
82 }
83}
84
85impl ScalarUDFImpl for RepeatFunc {
86 fn name(&self) -> &str {
87 "repeat"
88 }
89
90 fn signature(&self) -> &Signature {
91 &self.signature
92 }
93
94 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
95 if arg_types[0] == Utf8View {
96 return Ok(Utf8View);
97 }
98 utf8_to_str_type(&arg_types[0], "repeat")
99 }
100
101 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
102 let return_type = args.return_field.data_type().clone();
103 let [string_arg, count_arg] = take_function_args(self.name(), args.args)?;
104
105 if let ColumnarValue::Scalar(s) = &string_arg
107 && s.is_null()
108 {
109 return Ok(ColumnarValue::Scalar(ScalarValue::try_from(&return_type)?));
110 }
111 if let ColumnarValue::Scalar(c) = &count_arg
112 && c.is_null()
113 {
114 return Ok(ColumnarValue::Scalar(ScalarValue::try_from(&return_type)?));
115 }
116
117 match (&string_arg, &count_arg) {
118 (
119 ColumnarValue::Scalar(string_scalar),
120 ColumnarValue::Scalar(count_scalar),
121 ) => {
122 let count = match count_scalar {
123 ScalarValue::Int64(Some(n)) => *n,
124 _ => {
125 return internal_err!(
126 "Unexpected data type {:?} for repeat count",
127 count_scalar.data_type()
128 );
129 }
130 };
131
132 let result = match string_scalar {
133 ScalarValue::Utf8View(Some(s)) => ScalarValue::Utf8View(Some(
134 compute_repeat(s, count, i32::MAX as usize)?,
135 )),
136 ScalarValue::Utf8(Some(s)) => ScalarValue::Utf8(Some(
137 compute_repeat(s, count, i32::MAX as usize)?,
138 )),
139 ScalarValue::LargeUtf8(Some(s)) => ScalarValue::LargeUtf8(Some(
140 compute_repeat(s, count, i64::MAX as usize)?,
141 )),
142 _ => {
143 return internal_err!(
144 "Unexpected data type {:?} for function repeat",
145 string_scalar.data_type()
146 );
147 }
148 };
149
150 Ok(ColumnarValue::Scalar(result))
151 }
152 _ => {
153 let string_array = string_arg.to_array(args.number_rows)?;
154 let count_array = count_arg.to_array(args.number_rows)?;
155 Ok(ColumnarValue::Array(repeat(&string_array, &count_array)?))
156 }
157 }
158 }
159
160 fn documentation(&self) -> Option<&Documentation> {
161 self.doc()
162 }
163}
164
165#[inline]
167fn compute_repeat(s: &str, count: i64, max_size: usize) -> Result<String> {
168 if count <= 0 {
169 return Ok(String::new());
170 }
171 let result_len = repeat_len(s.len(), count, max_size)?;
172 debug_assert!(result_len <= max_size);
173 let count = repeat_count(count, max_size)?;
174 Ok(s.repeat(count))
175}
176
177fn repeat_len(string_len: usize, count: i64, max_size: usize) -> Result<usize> {
178 let count = repeat_count(count, max_size)?;
179 let result_len = string_len.checked_mul(count).ok_or_else(|| {
180 exec_datafusion_err!(
181 "string size overflow on repeat, max size is {}, but got {}",
182 max_size,
183 usize::MAX
184 )
185 })?;
186 if result_len > max_size {
187 return exec_err!(
188 "string size overflow on repeat, max size is {}, but got {}",
189 max_size,
190 result_len
191 );
192 }
193 Ok(result_len)
194}
195
196fn repeat_count(count: i64, max_size: usize) -> Result<usize> {
197 match usize::try_from(count) {
198 Ok(count) => Ok(count),
199 Err(_) => exec_err!(
200 "string size overflow on repeat, max size is {}, but got {}",
201 max_size,
202 usize::MAX
203 ),
204 }
205}
206
207fn repeat(string_array: &ArrayRef, count_array: &ArrayRef) -> Result<ArrayRef> {
210 let number_array = as_int64_array(count_array)?;
211 match string_array.data_type() {
212 Utf8View => {
213 let string_view_array = string_array.as_string_view();
214 let (_, max_item_capacity) = calculate_capacities(
215 &string_view_array,
216 number_array,
217 i32::MAX as usize,
218 )?;
219 let builder = StringViewArrayBuilder::with_capacity(string_array.len());
220 repeat_impl(&string_view_array, number_array, max_item_capacity, builder)
221 }
222 Utf8 => {
223 let string_arr = string_array.as_string::<i32>();
224 let (total_capacity, max_item_capacity) =
225 calculate_capacities(&string_arr, number_array, i32::MAX as usize)?;
226 let builder = GenericStringArrayBuilder::<i32>::with_capacity(
227 string_array.len(),
228 total_capacity,
229 );
230 repeat_impl(&string_arr, number_array, max_item_capacity, builder)
231 }
232 LargeUtf8 => {
233 let string_arr = string_array.as_string::<i64>();
234 let (total_capacity, max_item_capacity) =
235 calculate_capacities(&string_arr, number_array, i64::MAX as usize)?;
236 let builder = GenericStringArrayBuilder::<i64>::with_capacity(
237 string_array.len(),
238 total_capacity,
239 );
240 repeat_impl(&string_arr, number_array, max_item_capacity, builder)
241 }
242 other => exec_err!(
243 "Unsupported data type {other:?} for function repeat. \
244 Expected Utf8, Utf8View or LargeUtf8."
245 ),
246 }
247}
248
249fn calculate_capacities<'a, S>(
250 string_array: &S,
251 number_array: &Int64Array,
252 max_str_len: usize,
253) -> Result<(usize, usize)>
254where
255 S: StringArrayType<'a>,
256{
257 let mut total_capacity = 0usize;
258 let mut max_item_capacity = 0usize;
259
260 string_array.iter().zip(number_array.iter()).try_for_each(
261 |(string, number)| -> Result<(), DataFusionError> {
262 match (string, number) {
263 (Some(string), Some(number)) if number >= 0 => {
264 let item_capacity = repeat_len(string.len(), number, max_str_len)?;
265 total_capacity =
266 total_capacity.checked_add(item_capacity).ok_or_else(|| {
267 exec_datafusion_err!(
268 "string size overflow on repeat, max size is {}, but got {}",
269 max_str_len,
270 usize::MAX
271 )
272 })?;
273 max_item_capacity = max_item_capacity.max(item_capacity);
274 }
275 _ => (),
276 }
277 Ok(())
278 },
279 )?;
280
281 Ok((total_capacity, max_item_capacity))
282}
283
284fn repeat_impl<'a, S, B>(
285 string_array: &S,
286 number_array: &Int64Array,
287 max_item_capacity: usize,
288 mut builder: B,
289) -> Result<ArrayRef>
290where
291 S: StringArrayType<'a> + 'a,
292 B: BulkNullStringArrayBuilder,
293{
294 let mut buffer = Vec::<u8>::with_capacity(max_item_capacity);
296
297 #[inline]
300 fn repeat_to_buffer(buffer: &mut Vec<u8>, string: &str, count: usize) {
301 buffer.clear();
302 if !string.is_empty() {
303 let src = string.as_bytes();
304 buffer.extend_from_slice(src);
306 while buffer.len() < src.len() * count {
308 let copy_len = buffer.len().min(src.len() * count - buffer.len());
309 buffer.extend_from_within(..copy_len);
311 }
312 }
313 }
314
315 let nulls = NullBuffer::union(string_array.nulls(), number_array.nulls());
317
318 if let Some(ref n) = nulls {
319 for i in 0..string_array.len() {
320 if n.is_null(i) {
321 builder.append_placeholder();
322 continue;
323 }
324 let string = unsafe { string_array.value_unchecked(i) };
326 let count = unsafe { number_array.value_unchecked(i) };
327 if count > 0 {
328 repeat_to_buffer(&mut buffer, string, count as usize);
329 builder.append_value(unsafe { std::str::from_utf8_unchecked(&buffer) });
331 } else {
332 builder.append_value("");
333 }
334 }
335 } else {
336 for i in 0..string_array.len() {
337 let string = unsafe { string_array.value_unchecked(i) };
339 let count = unsafe { number_array.value_unchecked(i) };
340 if count > 0 {
341 repeat_to_buffer(&mut buffer, string, count as usize);
342 builder.append_value(unsafe { std::str::from_utf8_unchecked(&buffer) });
344 } else {
345 builder.append_value("");
346 }
347 }
348 }
349
350 builder.finish(nulls)
351}
352
353#[cfg(test)]
354mod tests {
355 use std::sync::Arc;
356
357 use arrow::array::{
358 Array, ArrayRef, Int64Array, LargeStringArray, StringArray, StringViewArray,
359 };
360 use arrow::datatypes::DataType::{LargeUtf8, Utf8, Utf8View};
361
362 use datafusion_common::ScalarValue;
363 use datafusion_common::{Result, exec_err};
364 use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
365
366 use crate::string::repeat::RepeatFunc;
367 use crate::utils::test::test_function;
368
369 #[test]
370 fn test_functions() -> Result<()> {
371 test_function!(
372 RepeatFunc::new(),
373 vec![
374 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("Pg")))),
375 ColumnarValue::Scalar(ScalarValue::Int64(Some(4))),
376 ],
377 Ok(Some("PgPgPgPg")),
378 &str,
379 Utf8,
380 StringArray
381 );
382 test_function!(
383 RepeatFunc::new(),
384 vec![
385 ColumnarValue::Scalar(ScalarValue::Utf8(None)),
386 ColumnarValue::Scalar(ScalarValue::Int64(Some(4))),
387 ],
388 Ok(None),
389 &str,
390 Utf8,
391 StringArray
392 );
393 test_function!(
394 RepeatFunc::new(),
395 vec![
396 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("Pg")))),
397 ColumnarValue::Scalar(ScalarValue::Int64(None)),
398 ],
399 Ok(None),
400 &str,
401 Utf8,
402 StringArray
403 );
404
405 test_function!(
406 RepeatFunc::new(),
407 vec![
408 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from("Pg")))),
409 ColumnarValue::Scalar(ScalarValue::Int64(Some(4))),
410 ],
411 Ok(Some("PgPgPgPg")),
412 &str,
413 Utf8View,
414 StringViewArray
415 );
416 test_function!(
417 RepeatFunc::new(),
418 vec![
419 ColumnarValue::Scalar(ScalarValue::Utf8View(None)),
420 ColumnarValue::Scalar(ScalarValue::Int64(Some(4))),
421 ],
422 Ok(None),
423 &str,
424 Utf8View,
425 StringViewArray
426 );
427 test_function!(
428 RepeatFunc::new(),
429 vec![
430 ColumnarValue::Scalar(ScalarValue::LargeUtf8(Some(String::from("Pg")))),
431 ColumnarValue::Scalar(ScalarValue::Int64(None)),
432 ],
433 Ok(None),
434 &str,
435 LargeUtf8,
436 LargeStringArray
437 );
438 test_function!(
439 RepeatFunc::new(),
440 vec![
441 ColumnarValue::Scalar(ScalarValue::Utf8View(Some(String::from("Pg")))),
442 ColumnarValue::Scalar(ScalarValue::Int64(None)),
443 ],
444 Ok(None),
445 &str,
446 Utf8View,
447 StringViewArray
448 );
449 test_function!(
450 RepeatFunc::new(),
451 vec![
452 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("Pg")))),
453 ColumnarValue::Scalar(ScalarValue::Int64(Some(1073741824))),
454 ],
455 exec_err!(
456 "string size overflow on repeat, max size is {}, but got {}",
457 i32::MAX,
458 2usize * 1073741824
459 ),
460 &str,
461 Utf8,
462 StringArray
463 );
464
465 Ok(())
466 }
467
468 fn sliced_offset_inputs<F>(make_strings: F) -> (ArrayRef, ArrayRef)
475 where
476 F: FnOnce(Vec<Option<&'static str>>) -> ArrayRef,
477 {
478 let strings = make_strings(vec![
479 None,
480 Some("a"),
481 Some("bb"),
482 Some("c"),
483 None,
484 Some("d"),
485 ]);
486 let counts: ArrayRef = Arc::new(Int64Array::from(vec![
487 Some(2),
488 Some(3),
489 Some(2),
490 None,
491 Some(1),
492 Some(2),
493 ]));
494 (strings.slice(1, 4), counts.slice(1, 4))
495 }
496
497 fn assert_sliced_offset_output<A: Array + 'static>(result: ArrayRef)
498 where
499 for<'a> &'a A: arrow::array::ArrayAccessor<Item = &'a str>,
500 {
501 let result = result.as_any().downcast_ref::<A>().unwrap();
502 assert_eq!(result.len(), 4);
503 assert_eq!(arrow::array::ArrayAccessor::value(&result, 0), "aaa");
504 assert_eq!(arrow::array::ArrayAccessor::value(&result, 1), "bbbb");
505 assert!(result.is_null(2));
506 assert!(result.is_null(3));
507 assert_eq!(result.null_count(), 2);
508 }
509
510 #[test]
511 fn test_repeat_sliced_string_with_null_offset() {
512 let (strings, counts) = sliced_offset_inputs(|v| Arc::new(StringArray::from(v)));
513 let result = super::repeat(&strings, &counts).unwrap();
514 assert_sliced_offset_output::<StringArray>(result);
515 }
516
517 #[test]
518 fn test_repeat_string_array_overflow() {
519 let strings: ArrayRef = Arc::new(StringArray::from(vec![Some("abc")]));
520 let counts: ArrayRef = Arc::new(Int64Array::from(vec![Some(i64::MAX)]));
521
522 let err = super::repeat(&strings, &counts).unwrap_err().to_string();
523 assert!(
524 err.contains("string size overflow on repeat"),
525 "unexpected error: {err}"
526 );
527 }
528
529 #[test]
530 fn test_repeat_sliced_large_string_with_null_offset() {
531 let (strings, counts) =
532 sliced_offset_inputs(|v| Arc::new(LargeStringArray::from(v)));
533 let result = super::repeat(&strings, &counts).unwrap();
534 assert_sliced_offset_output::<LargeStringArray>(result);
535 }
536
537 #[test]
538 fn test_repeat_sliced_string_view_with_null_offset() {
539 let (strings, counts) =
540 sliced_offset_inputs(|v| Arc::new(StringViewArray::from(v)));
541 let result = super::repeat(&strings, &counts).unwrap();
542 assert_sliced_offset_output::<StringViewArray>(result);
543 }
544}