1use std::fmt::Write;
19use std::sync::Arc;
20
21use DataType::{LargeUtf8, Utf8, Utf8View};
22use arrow::array::{
23 ArrayRef, AsArray, GenericStringArray, GenericStringBuilder, Int64Array,
24 OffsetSizeTrait, StringArrayType, StringViewArray,
25};
26use arrow::datatypes::DataType;
27
28use crate::utils::{make_scalar_function, utf8_to_str_type};
29use datafusion_common::cast::as_int64_array;
30use datafusion_common::{Result, exec_err};
31use datafusion_expr::TypeSignature::Exact;
32use datafusion_expr::{
33 ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
34 Volatility,
35};
36use datafusion_macros::user_doc;
37
38#[user_doc(
39 doc_section(label = "String Functions"),
40 description = "Pads the right side of a string with another string to a specified string length.",
41 syntax_example = "rpad(str, n[, padding_str])",
42 sql_example = r#"```sql
43> select rpad('datafusion', 20, '_-');
44+-----------------------------------------------+
45| rpad(Utf8("datafusion"),Int64(20),Utf8("_-")) |
46+-----------------------------------------------+
47| datafusion_-_-_-_-_- |
48+-----------------------------------------------+
49```"#,
50 standard_argument(name = "str", prefix = "String"),
51 argument(
52 name = "n",
53 description = "String length to pad to. If the input string is longer than this length, it is truncated."
54 ),
55 argument(
56 name = "padding_str",
57 description = "String expression to pad with. Can be a constant, column, or function, and any combination of string operators. _Default is a space._"
58 ),
59 related_udf(name = "lpad")
60)]
61#[derive(Debug, PartialEq, Eq, Hash)]
62pub struct RPadFunc {
63 signature: Signature,
64}
65
66impl Default for RPadFunc {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl RPadFunc {
73 pub fn new() -> Self {
74 use DataType::*;
75 Self {
76 signature: Signature::one_of(
77 vec![
78 Exact(vec![Utf8View, Int64]),
79 Exact(vec![Utf8View, Int64, Utf8View]),
80 Exact(vec![Utf8View, Int64, Utf8]),
81 Exact(vec![Utf8View, Int64, LargeUtf8]),
82 Exact(vec![Utf8, Int64]),
83 Exact(vec![Utf8, Int64, Utf8View]),
84 Exact(vec![Utf8, Int64, Utf8]),
85 Exact(vec![Utf8, Int64, LargeUtf8]),
86 Exact(vec![LargeUtf8, Int64]),
87 Exact(vec![LargeUtf8, Int64, Utf8View]),
88 Exact(vec![LargeUtf8, Int64, Utf8]),
89 Exact(vec![LargeUtf8, Int64, LargeUtf8]),
90 ],
91 Volatility::Immutable,
92 ),
93 }
94 }
95}
96
97impl ScalarUDFImpl for RPadFunc {
98 fn name(&self) -> &str {
99 "rpad"
100 }
101
102 fn signature(&self) -> &Signature {
103 &self.signature
104 }
105
106 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
107 utf8_to_str_type(&arg_types[0], "rpad")
108 }
109
110 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
111 let ScalarFunctionArgs {
112 args, number_rows, ..
113 } = args;
114
115 const MAX_SCALAR_TARGET_LEN: usize = 16384;
116
117 if let Some(target_len) = try_as_scalar_i64(&args[1]) {
120 let target_len: usize = match usize::try_from(target_len) {
121 Ok(n) if n <= i32::MAX as usize => n,
122 Ok(n) => {
123 return exec_err!(
124 "rpad requested length {n} too large, maximum allowed length is {}",
125 i32::MAX
126 );
127 }
128 Err(_) => 0, };
130
131 let fill_str = if args.len() == 3 {
132 try_as_scalar_str(&args[2])
133 } else {
134 Some(" ")
135 };
136
137 if target_len <= MAX_SCALAR_TARGET_LEN
141 && let Some(fill) = fill_str
142 {
143 let string_array = args[0].to_array_of_size(number_rows)?;
144 let result = match string_array.data_type() {
145 Utf8View => rpad_scalar_args::<_, i32>(
146 string_array.as_string_view(),
147 target_len,
148 fill,
149 ),
150 Utf8 => rpad_scalar_args::<_, i32>(
151 string_array.as_string::<i32>(),
152 target_len,
153 fill,
154 ),
155 LargeUtf8 => rpad_scalar_args::<_, i64>(
156 string_array.as_string::<i64>(),
157 target_len,
158 fill,
159 ),
160 other => {
161 exec_err!("Unsupported data type {other:?} for function rpad")
162 }
163 }?;
164 return Ok(ColumnarValue::Array(result));
165 }
166 }
167
168 match args[0].data_type() {
169 Utf8 | Utf8View => make_scalar_function(rpad::<i32>, vec![])(&args),
170 LargeUtf8 => make_scalar_function(rpad::<i64>, vec![])(&args),
171 other => exec_err!("Unsupported data type {other:?} for function rpad"),
172 }
173 }
174
175 fn documentation(&self) -> Option<&Documentation> {
176 self.doc()
177 }
178}
179
180use super::common::{
181 StringCharLen, char_count_or_boundary, pad_data_capacity, try_as_scalar_i64,
182 try_as_scalar_str,
183};
184
185fn rpad_scalar_args<'a, V: StringArrayType<'a> + Copy, T: OffsetSizeTrait>(
187 string_array: V,
188 target_len: usize,
189 fill: &str,
190) -> Result<ArrayRef> {
191 if string_array.is_ascii() && fill.is_ascii() {
192 rpad_scalar_ascii::<V, T>(string_array, target_len, fill)
193 } else {
194 rpad_scalar_unicode::<V, T>(string_array, target_len, fill)
195 }
196}
197
198fn rpad_scalar_ascii<'a, V: StringArrayType<'a> + Copy, T: OffsetSizeTrait>(
199 string_array: V,
200 target_len: usize,
201 fill: &str,
202) -> Result<ArrayRef> {
203 let padding_buf = if !fill.is_empty() {
206 let mut buf = String::with_capacity(target_len);
207 while buf.len() < target_len {
208 let remaining = target_len - buf.len();
209 if remaining >= fill.len() {
210 buf.push_str(fill);
211 } else {
212 buf.push_str(&fill[..remaining]);
213 }
214 }
215 buf
216 } else {
217 String::new()
218 };
219
220 let data_capacity = string_array.len().saturating_mul(target_len);
222 let mut builder =
223 GenericStringBuilder::<T>::with_capacity(string_array.len(), data_capacity);
224
225 for maybe_string in string_array.iter() {
226 match maybe_string {
227 Some(string) => {
228 let str_len = string.len();
229 if target_len <= str_len {
230 builder.append_value(&string[..target_len]);
231 } else if fill.is_empty() {
232 builder.append_value(string);
233 } else {
234 let pad_needed = target_len - str_len;
235 builder.write_str(string)?;
236 builder.write_str(&padding_buf[..pad_needed])?;
237 builder.append_value("");
238 }
239 }
240 None => builder.append_null(),
241 }
242 }
243
244 Ok(Arc::new(builder.finish()) as ArrayRef)
245}
246
247fn rpad_scalar_unicode<'a, V: StringArrayType<'a> + Copy, T: OffsetSizeTrait>(
248 string_array: V,
249 target_len: usize,
250 fill: &str,
251) -> Result<ArrayRef> {
252 let fill_chars: Vec<char> = fill.chars().collect();
253
254 let (padding_buf, char_byte_offsets) = if !fill_chars.is_empty() {
259 let mut buf = String::new();
260 let mut offsets = Vec::with_capacity(target_len + 1);
261 offsets.push(0usize);
262 for i in 0..target_len {
263 buf.push(fill_chars[i % fill_chars.len()]);
264 offsets.push(buf.len());
265 }
266 (buf, offsets)
267 } else {
268 (String::new(), vec![0])
269 };
270
271 let data_capacity = string_array.len().saturating_mul(target_len * 4);
274 let mut builder =
275 GenericStringBuilder::<T>::with_capacity(string_array.len(), data_capacity);
276
277 for maybe_string in string_array.iter() {
278 match maybe_string {
279 Some(string) => match char_count_or_boundary(string, target_len) {
280 StringCharLen::ByteOffset(offset) => {
281 builder.append_value(&string[..offset]);
282 }
283 StringCharLen::CharCount(char_count) => {
284 builder.write_str(string)?;
285 if !fill_chars.is_empty() {
286 let pad_chars = target_len - char_count;
287 let pad_bytes = char_byte_offsets[pad_chars];
288 builder.write_str(&padding_buf[..pad_bytes])?;
289 }
290 builder.append_value("");
291 }
292 },
293 None => builder.append_null(),
294 }
295 }
296
297 Ok(Arc::new(builder.finish()) as ArrayRef)
298}
299
300fn rpad<T: OffsetSizeTrait>(args: &[ArrayRef]) -> Result<ArrayRef> {
301 if args.len() <= 1 || args.len() > 3 {
302 return exec_err!(
303 "rpad was called with {} arguments. It requires at least 2 and at most 3.",
304 args.len()
305 );
306 }
307
308 let length_array = as_int64_array(&args[1])?;
309
310 match (args.len(), args[0].data_type()) {
311 (2, Utf8View) => rpad_impl::<&StringViewArray, &GenericStringArray<i32>, T>(
312 &args[0].as_string_view(),
313 length_array,
314 None,
315 ),
316 (2, Utf8 | LargeUtf8) => rpad_impl::<
317 &GenericStringArray<T>,
318 &GenericStringArray<T>,
319 T,
320 >(&args[0].as_string::<T>(), length_array, None),
321 (3, Utf8View) => rpad_with_replace::<&StringViewArray, T>(
322 &args[0].as_string_view(),
323 length_array,
324 &args[2],
325 ),
326 (3, Utf8 | LargeUtf8) => rpad_with_replace::<&GenericStringArray<T>, T>(
327 &args[0].as_string::<T>(),
328 length_array,
329 &args[2],
330 ),
331 (len, dt) => unreachable!("rpad: unexpected arg count ({len}) or type ({dt})"),
332 }
333}
334
335fn rpad_with_replace<'a, V, T: OffsetSizeTrait>(
336 string_array: &V,
337 length_array: &Int64Array,
338 fill_array: &'a ArrayRef,
339) -> Result<ArrayRef>
340where
341 V: StringArrayType<'a>,
342{
343 match fill_array.data_type() {
344 Utf8View => rpad_impl::<V, &StringViewArray, T>(
345 string_array,
346 length_array,
347 Some(fill_array.as_string_view()),
348 ),
349 LargeUtf8 => rpad_impl::<V, &GenericStringArray<i64>, T>(
350 string_array,
351 length_array,
352 Some(fill_array.as_string::<i64>()),
353 ),
354 Utf8 => rpad_impl::<V, &GenericStringArray<i32>, T>(
355 string_array,
356 length_array,
357 Some(fill_array.as_string::<i32>()),
358 ),
359 other => {
360 exec_err!("Unsupported data type {other:?} for function rpad")
361 }
362 }
363}
364
365fn rpad_impl<'a, V, V2, T>(
366 string_array: &V,
367 length_array: &Int64Array,
368 fill_array: Option<V2>,
369) -> Result<ArrayRef>
370where
371 V: StringArrayType<'a>,
372 V2: StringArrayType<'a>,
373 T: OffsetSizeTrait,
374{
375 let array = if let Some(fill_array) = fill_array {
376 let mut builder: GenericStringBuilder<T> = GenericStringBuilder::with_capacity(
377 string_array.len(),
378 pad_data_capacity(length_array),
379 );
380 let mut fill_chars_buf = Vec::new();
381
382 for ((string, target_len), fill) in string_array
383 .iter()
384 .zip(length_array.iter())
385 .zip(fill_array.iter())
386 {
387 if let (Some(string), Some(target_len), Some(fill)) =
388 (string, target_len, fill)
389 {
390 if target_len > i32::MAX as i64 {
391 return exec_err!(
392 "rpad requested length {target_len} too large, maximum allowed length is {}",
393 i32::MAX
394 );
395 }
396
397 let target_len = if target_len < 0 {
398 0
399 } else {
400 target_len as usize
401 };
402 if target_len == 0 {
403 builder.append_value("");
404 continue;
405 }
406
407 if string.is_ascii() && fill.is_ascii() {
408 let str_len = string.len();
410 if target_len < str_len {
411 builder.append_value(&string[..target_len]);
412 } else if fill.is_empty() {
413 builder.append_value(string);
414 } else {
415 let pad_len = target_len - str_len;
416 let fill_len = fill.len();
417 let full_reps = pad_len / fill_len;
418 let remainder = pad_len % fill_len;
419 builder.write_str(string)?;
420 for _ in 0..full_reps {
421 builder.write_str(fill)?;
422 }
423 if remainder > 0 {
424 builder.write_str(&fill[..remainder])?;
425 }
426 builder.append_value("");
427 }
428 } else {
429 fill_chars_buf.clear();
430 fill_chars_buf.extend(fill.chars());
431
432 match char_count_or_boundary(string, target_len) {
433 StringCharLen::ByteOffset(offset) => {
434 builder.append_value(&string[..offset]);
435 }
436 StringCharLen::CharCount(char_count) => {
437 builder.write_str(string)?;
438 if !fill_chars_buf.is_empty() {
439 for l in 0..target_len - char_count {
440 let c = *fill_chars_buf
441 .get(l % fill_chars_buf.len())
442 .unwrap();
443 builder.write_char(c)?;
444 }
445 }
446 builder.append_value("");
447 }
448 }
449 }
450 } else {
451 builder.append_null();
452 }
453 }
454
455 builder.finish()
456 } else {
457 let mut builder: GenericStringBuilder<T> = GenericStringBuilder::with_capacity(
458 string_array.len(),
459 pad_data_capacity(length_array),
460 );
461
462 for (string, target_len) in string_array.iter().zip(length_array.iter()) {
463 if let (Some(string), Some(target_len)) = (string, target_len) {
464 if target_len > i32::MAX as i64 {
465 return exec_err!(
466 "rpad requested length {target_len} too large, maximum allowed length is {}",
467 i32::MAX
468 );
469 }
470
471 let target_len = if target_len < 0 {
472 0
473 } else {
474 target_len as usize
475 };
476 if target_len == 0 {
477 builder.append_value("");
478 continue;
479 }
480
481 if string.is_ascii() {
482 let str_len = string.len();
484 if target_len < str_len {
485 builder.append_value(&string[..target_len]);
486 } else {
487 builder.write_str(string)?;
488 for _ in 0..(target_len - str_len) {
489 builder.write_str(" ")?;
490 }
491 builder.append_value("");
492 }
493 } else {
494 match char_count_or_boundary(string, target_len) {
495 StringCharLen::ByteOffset(offset) => {
496 builder.append_value(&string[..offset]);
497 }
498 StringCharLen::CharCount(char_count) => {
499 builder.write_str(string)?;
500 for _ in 0..(target_len - char_count) {
501 builder.write_str(" ")?;
502 }
503 builder.append_value("");
504 }
505 }
506 }
507 } else {
508 builder.append_null();
509 }
510 }
511
512 builder.finish()
513 };
514
515 Ok(Arc::new(array) as ArrayRef)
516}
517
518#[cfg(test)]
519mod tests {
520 use arrow::array::{Array, StringArray};
521 use arrow::datatypes::DataType::Utf8;
522
523 use datafusion_common::{Result, ScalarValue};
524 use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
525
526 use crate::unicode::rpad::RPadFunc;
527 use crate::utils::test::test_function;
528
529 #[test]
530 fn test_functions() -> Result<()> {
531 test_function!(
532 RPadFunc::new(),
533 vec![
534 ColumnarValue::Scalar(ScalarValue::from("josé")),
535 ColumnarValue::Scalar(ScalarValue::from(5i64)),
536 ],
537 Ok(Some("josé ")),
538 &str,
539 Utf8,
540 StringArray
541 );
542 test_function!(
543 RPadFunc::new(),
544 vec![
545 ColumnarValue::Scalar(ScalarValue::from("hi")),
546 ColumnarValue::Scalar(ScalarValue::from(5i64)),
547 ],
548 Ok(Some("hi ")),
549 &str,
550 Utf8,
551 StringArray
552 );
553 test_function!(
554 RPadFunc::new(),
555 vec![
556 ColumnarValue::Scalar(ScalarValue::from("hi")),
557 ColumnarValue::Scalar(ScalarValue::from(0i64)),
558 ],
559 Ok(Some("")),
560 &str,
561 Utf8,
562 StringArray
563 );
564 test_function!(
565 RPadFunc::new(),
566 vec![
567 ColumnarValue::Scalar(ScalarValue::from("hi")),
568 ColumnarValue::Scalar(ScalarValue::Int64(None)),
569 ],
570 Ok(None),
571 &str,
572 Utf8,
573 StringArray
574 );
575 test_function!(
576 RPadFunc::new(),
577 vec![
578 ColumnarValue::Scalar(ScalarValue::Utf8(None)),
579 ColumnarValue::Scalar(ScalarValue::from(5i64)),
580 ],
581 Ok(None),
582 &str,
583 Utf8,
584 StringArray
585 );
586 test_function!(
587 RPadFunc::new(),
588 vec![
589 ColumnarValue::Scalar(ScalarValue::from("hi")),
590 ColumnarValue::Scalar(ScalarValue::from(5i64)),
591 ColumnarValue::Scalar(ScalarValue::from("xy")),
592 ],
593 Ok(Some("hixyx")),
594 &str,
595 Utf8,
596 StringArray
597 );
598 test_function!(
599 RPadFunc::new(),
600 vec![
601 ColumnarValue::Scalar(ScalarValue::from("hi")),
602 ColumnarValue::Scalar(ScalarValue::from(21i64)),
603 ColumnarValue::Scalar(ScalarValue::from("abcdef")),
604 ],
605 Ok(Some("hiabcdefabcdefabcdefa")),
606 &str,
607 Utf8,
608 StringArray
609 );
610 test_function!(
611 RPadFunc::new(),
612 vec![
613 ColumnarValue::Scalar(ScalarValue::from("hi")),
614 ColumnarValue::Scalar(ScalarValue::from(5i64)),
615 ColumnarValue::Scalar(ScalarValue::from(" ")),
616 ],
617 Ok(Some("hi ")),
618 &str,
619 Utf8,
620 StringArray
621 );
622 test_function!(
623 RPadFunc::new(),
624 vec![
625 ColumnarValue::Scalar(ScalarValue::from("hi")),
626 ColumnarValue::Scalar(ScalarValue::from(5i64)),
627 ColumnarValue::Scalar(ScalarValue::from("")),
628 ],
629 Ok(Some("hi")),
630 &str,
631 Utf8,
632 StringArray
633 );
634 test_function!(
635 RPadFunc::new(),
636 vec![
637 ColumnarValue::Scalar(ScalarValue::Utf8(None)),
638 ColumnarValue::Scalar(ScalarValue::from(5i64)),
639 ColumnarValue::Scalar(ScalarValue::from("xy")),
640 ],
641 Ok(None),
642 &str,
643 Utf8,
644 StringArray
645 );
646 test_function!(
647 RPadFunc::new(),
648 vec![
649 ColumnarValue::Scalar(ScalarValue::from("hi")),
650 ColumnarValue::Scalar(ScalarValue::Int64(None)),
651 ColumnarValue::Scalar(ScalarValue::from("xy")),
652 ],
653 Ok(None),
654 &str,
655 Utf8,
656 StringArray
657 );
658 test_function!(
659 RPadFunc::new(),
660 vec![
661 ColumnarValue::Scalar(ScalarValue::from("hi")),
662 ColumnarValue::Scalar(ScalarValue::from(5i64)),
663 ColumnarValue::Scalar(ScalarValue::Utf8(None)),
664 ],
665 Ok(None),
666 &str,
667 Utf8,
668 StringArray
669 );
670 test_function!(
671 RPadFunc::new(),
672 vec![
673 ColumnarValue::Scalar(ScalarValue::from("hello")),
674 ColumnarValue::Scalar(ScalarValue::from(2i64)),
675 ],
676 Ok(Some("he")),
677 &str,
678 Utf8,
679 StringArray
680 );
681 test_function!(
682 RPadFunc::new(),
683 vec![
684 ColumnarValue::Scalar(ScalarValue::from("hi")),
685 ColumnarValue::Scalar(ScalarValue::from(6i64)),
686 ColumnarValue::Scalar(ScalarValue::from("xy")),
687 ],
688 Ok(Some("hixyxy")),
689 &str,
690 Utf8,
691 StringArray
692 );
693 test_function!(
694 RPadFunc::new(),
695 vec![
696 ColumnarValue::Scalar(ScalarValue::from("josé")),
697 ColumnarValue::Scalar(ScalarValue::from(10i64)),
698 ColumnarValue::Scalar(ScalarValue::from("xy")),
699 ],
700 Ok(Some("joséxyxyxy")),
701 &str,
702 Utf8,
703 StringArray
704 );
705 test_function!(
706 RPadFunc::new(),
707 vec![
708 ColumnarValue::Scalar(ScalarValue::from("josé")),
709 ColumnarValue::Scalar(ScalarValue::from(10i64)),
710 ColumnarValue::Scalar(ScalarValue::from("éñ")),
711 ],
712 Ok(Some("josééñéñéñ")),
713 &str,
714 Utf8,
715 StringArray
716 );
717 #[cfg(not(feature = "unicode_expressions"))]
718 test_function!(
719 RPadFunc::new(),
720 &[
721 ColumnarValue::Scalar(ScalarValue::from("josé")),
722 ColumnarValue::Scalar(ScalarValue::from(5i64)),
723 ],
724 internal_err!(
725 "function rpad requires compilation with feature flag: unicode_expressions."
726 ),
727 &str,
728 Utf8,
729 StringArray
730 );
731
732 Ok(())
733 }
734}