Skip to main content

datafusion_functions/unicode/
lpad.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::fmt::Write;
19use std::sync::Arc;
20
21use DataType::{LargeUtf8, Utf8, Utf8View};
22use arrow::array::{
23    Array, 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 left side of a string with another string to a specified string length.",
41    syntax_example = "lpad(str, n[, padding_str])",
42    sql_example = r#"```sql
43> select lpad('Dolly', 10, 'hello');
44+---------------------------------------------+
45| lpad(Utf8("Dolly"),Int64(10),Utf8("hello")) |
46+---------------------------------------------+
47| helloDolly                                  |
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 (on the right)."
54    ),
55    argument(
56        name = "padding_str",
57        description = "Optional 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 = "rpad")
60)]
61#[derive(Debug, PartialEq, Eq, Hash)]
62pub struct LPadFunc {
63    signature: Signature,
64}
65
66impl Default for LPadFunc {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl LPadFunc {
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 LPadFunc {
98    fn name(&self) -> &str {
99        "lpad"
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], "lpad")
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 target_len and fill (if specified) are constants, use the scalar
118        // fast path.
119        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                        "lpad requested length {n} too large, maximum allowed length is {}",
125                        i32::MAX
126                    );
127                }
128                Err(_) => 0, // negative → 0
129            };
130
131            let fill_str = if args.len() == 3 {
132                try_as_scalar_str(&args[2])
133            } else {
134                Some(" ")
135            };
136
137            // Skip the fast path for very large `target_len` values to avoid
138            // consuming too much memory. Such large padding values are uncommon
139            // in practice.
140            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 => lpad_scalar_args::<_, i32>(
146                        string_array.as_string_view(),
147                        target_len,
148                        fill,
149                    ),
150                    Utf8 => lpad_scalar_args::<_, i32>(
151                        string_array.as_string::<i32>(),
152                        target_len,
153                        fill,
154                    ),
155                    LargeUtf8 => lpad_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 lpad")
162                    }
163                }?;
164                return Ok(ColumnarValue::Array(result));
165            }
166        }
167
168        match args[0].data_type() {
169            Utf8 | Utf8View => make_scalar_function(lpad::<i32>, vec![])(&args),
170            LargeUtf8 => make_scalar_function(lpad::<i64>, vec![])(&args),
171            other => exec_err!("Unsupported data type {other:?} for function lpad"),
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
185/// Optimized lpad for constant target_len and fill arguments.
186fn lpad_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        lpad_scalar_ascii::<V, T>(string_array, target_len, fill)
193    } else {
194        lpad_scalar_unicode::<V, T>(string_array, target_len, fill)
195    }
196}
197
198fn lpad_scalar_ascii<'a, V: StringArrayType<'a> + Copy, T: OffsetSizeTrait>(
199    string_array: V,
200    target_len: usize,
201    fill: &str,
202) -> Result<ArrayRef> {
203    // With a scalar `target_len` and `fill`, we can precompute a padding
204    // buffer of `target_len` fill characters repeated cyclically.
205    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    // Each output row is exactly `target_len` ASCII bytes (padding + string).
221    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(&padding_buf[..pad_needed])?;
236                    builder.append_value(string);
237                }
238            }
239            None => builder.append_null(),
240        }
241    }
242
243    Ok(Arc::new(builder.finish()) as ArrayRef)
244}
245
246fn lpad_scalar_unicode<'a, V: StringArrayType<'a> + Copy, T: OffsetSizeTrait>(
247    string_array: V,
248    target_len: usize,
249    fill: &str,
250) -> Result<ArrayRef> {
251    let fill_chars: Vec<char> = fill.chars().collect();
252
253    // With a scalar `target_len` and `fill`, we can precompute a padding buffer
254    // of `target_len` fill characters repeated cyclically. Because Unicode
255    // characters are variable-width, we build a byte-offset table to map from
256    // character count to the corresponding byte position in the padding buffer.
257    let (padding_buf, char_byte_offsets) = if !fill_chars.is_empty() {
258        let mut buf = String::new();
259        let mut offsets = Vec::with_capacity(target_len + 1);
260        offsets.push(0usize);
261        for i in 0..target_len {
262            buf.push(fill_chars[i % fill_chars.len()]);
263            offsets.push(buf.len());
264        }
265        (buf, offsets)
266    } else {
267        (String::new(), vec![0])
268    };
269
270    // Each output row is `target_len` chars; multiply by 4 (max UTF-8 bytes
271    // per char) for an upper bound in bytes.
272    let data_capacity = string_array.len().saturating_mul(target_len * 4);
273    let mut builder =
274        GenericStringBuilder::<T>::with_capacity(string_array.len(), data_capacity);
275
276    for maybe_string in string_array.iter() {
277        match maybe_string {
278            Some(string) => match char_count_or_boundary(string, target_len) {
279                StringCharLen::ByteOffset(offset) => {
280                    builder.append_value(&string[..offset]);
281                }
282                StringCharLen::CharCount(char_count) => {
283                    if !fill_chars.is_empty() {
284                        let pad_chars = target_len - char_count;
285                        let pad_bytes = char_byte_offsets[pad_chars];
286                        builder.write_str(&padding_buf[..pad_bytes])?;
287                    }
288                    builder.append_value(string);
289                }
290            },
291            None => builder.append_null(),
292        }
293    }
294
295    Ok(Arc::new(builder.finish()) as ArrayRef)
296}
297
298/// Left-pads `string` to `target_len` using the fill string (default: space).
299/// Truncates from the right if `string` is already longer than `target_len`.
300/// lpad('hi', 5, 'xy') = 'xyxhi'
301fn lpad<T: OffsetSizeTrait>(args: &[ArrayRef]) -> Result<ArrayRef> {
302    if args.len() <= 1 || args.len() > 3 {
303        return exec_err!(
304            "lpad was called with {} arguments. It requires at least 2 and at most 3.",
305            args.len()
306        );
307    }
308
309    let length_array = as_int64_array(&args[1])?;
310
311    match (args.len(), args[0].data_type()) {
312        (2, Utf8View) => lpad_impl::<&StringViewArray, &GenericStringArray<i32>, T>(
313            &args[0].as_string_view(),
314            length_array,
315            None,
316        ),
317        (2, Utf8 | LargeUtf8) => lpad_impl::<
318            &GenericStringArray<T>,
319            &GenericStringArray<T>,
320            T,
321        >(&args[0].as_string::<T>(), length_array, None),
322        (3, Utf8View) => lpad_with_replace::<&StringViewArray, T>(
323            &args[0].as_string_view(),
324            length_array,
325            &args[2],
326        ),
327        (3, Utf8 | LargeUtf8) => lpad_with_replace::<&GenericStringArray<T>, T>(
328            &args[0].as_string::<T>(),
329            length_array,
330            &args[2],
331        ),
332        (len, dt) => unreachable!("lpad: unexpected arg count ({len}) or type ({dt})"),
333    }
334}
335
336fn lpad_with_replace<'a, V, T: OffsetSizeTrait>(
337    string_array: &V,
338    length_array: &Int64Array,
339    fill_array: &'a ArrayRef,
340) -> Result<ArrayRef>
341where
342    V: StringArrayType<'a>,
343{
344    match fill_array.data_type() {
345        Utf8View => lpad_impl::<V, &StringViewArray, T>(
346            string_array,
347            length_array,
348            Some(fill_array.as_string_view()),
349        ),
350        LargeUtf8 => lpad_impl::<V, &GenericStringArray<i64>, T>(
351            string_array,
352            length_array,
353            Some(fill_array.as_string::<i64>()),
354        ),
355        Utf8 => lpad_impl::<V, &GenericStringArray<i32>, T>(
356            string_array,
357            length_array,
358            Some(fill_array.as_string::<i32>()),
359        ),
360        other => {
361            exec_err!("Unsupported data type {other:?} for function lpad")
362        }
363    }
364}
365
366fn lpad_impl<'a, V, V2, T>(
367    string_array: &V,
368    length_array: &Int64Array,
369    fill_array: Option<V2>,
370) -> Result<ArrayRef>
371where
372    V: StringArrayType<'a>,
373    V2: StringArrayType<'a>,
374    T: OffsetSizeTrait,
375{
376    let array = if let Some(fill_array) = fill_array {
377        let mut builder: GenericStringBuilder<T> = GenericStringBuilder::with_capacity(
378            string_array.len(),
379            pad_data_capacity(length_array),
380        );
381        let mut fill_chars_buf = Vec::new();
382
383        for ((string, target_len), fill) in string_array
384            .iter()
385            .zip(length_array.iter())
386            .zip(fill_array.iter())
387        {
388            if let (Some(string), Some(target_len), Some(fill)) =
389                (string, target_len, fill)
390            {
391                if target_len > i32::MAX as i64 {
392                    return exec_err!(
393                        "lpad requested length {target_len} too large, maximum allowed length is {}",
394                        i32::MAX
395                    );
396                }
397
398                let target_len = if target_len < 0 {
399                    0
400                } else {
401                    target_len as usize
402                };
403                if target_len == 0 {
404                    builder.append_value("");
405                    continue;
406                }
407
408                if string.is_ascii() && fill.is_ascii() {
409                    // ASCII fast path: byte length == character length.
410                    let str_len = string.len();
411                    if target_len < str_len {
412                        builder.append_value(&string[..target_len]);
413                    } else if fill.is_empty() {
414                        builder.append_value(string);
415                    } else {
416                        let pad_len = target_len - str_len;
417                        let fill_len = fill.len();
418                        let full_reps = pad_len / fill_len;
419                        let remainder = pad_len % fill_len;
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(string);
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                            if !fill_chars_buf.is_empty() {
438                                for l in 0..target_len - char_count {
439                                    let c = *fill_chars_buf
440                                        .get(l % fill_chars_buf.len())
441                                        .unwrap();
442                                    builder.write_char(c)?;
443                                }
444                            }
445                            builder.append_value(string);
446                        }
447                    }
448                }
449            } else {
450                builder.append_null();
451            }
452        }
453
454        builder.finish()
455    } else {
456        let mut builder: GenericStringBuilder<T> = GenericStringBuilder::with_capacity(
457            string_array.len(),
458            pad_data_capacity(length_array),
459        );
460
461        for (string, target_len) in string_array.iter().zip(length_array.iter()) {
462            if let (Some(string), Some(target_len)) = (string, target_len) {
463                if target_len > i32::MAX as i64 {
464                    return exec_err!(
465                        "lpad requested length {target_len} too large, maximum allowed length is {}",
466                        i32::MAX
467                    );
468                }
469
470                let target_len = if target_len < 0 {
471                    0
472                } else {
473                    target_len as usize
474                };
475                if target_len == 0 {
476                    builder.append_value("");
477                    continue;
478                }
479
480                if string.is_ascii() {
481                    // ASCII fast path: byte length == character length
482                    let str_len = string.len();
483                    if target_len < str_len {
484                        builder.append_value(&string[..target_len]);
485                    } else {
486                        for _ in 0..(target_len - str_len) {
487                            builder.write_str(" ")?;
488                        }
489                        builder.append_value(string);
490                    }
491                } else {
492                    match char_count_or_boundary(string, target_len) {
493                        StringCharLen::ByteOffset(offset) => {
494                            builder.append_value(&string[..offset]);
495                        }
496                        StringCharLen::CharCount(char_count) => {
497                            for _ in 0..(target_len - char_count) {
498                                builder.write_str(" ")?;
499                            }
500                            builder.append_value(string);
501                        }
502                    }
503                }
504            } else {
505                builder.append_null();
506            }
507        }
508
509        builder.finish()
510    };
511
512    Ok(Arc::new(array) as ArrayRef)
513}
514
515#[cfg(test)]
516mod tests {
517    use crate::unicode::lpad::LPadFunc;
518    use crate::utils::test::test_function;
519
520    use arrow::array::{Array, LargeStringArray, StringArray};
521    use arrow::datatypes::DataType::{LargeUtf8, Utf8};
522
523    use datafusion_common::{Result, ScalarValue};
524    use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
525
526    macro_rules! test_lpad {
527        ($INPUT:expr, $LENGTH:expr, $EXPECTED:expr) => {
528            test_function!(
529                LPadFunc::new(),
530                vec![
531                    ColumnarValue::Scalar(ScalarValue::Utf8($INPUT)),
532                    ColumnarValue::Scalar($LENGTH)
533                ],
534                $EXPECTED,
535                &str,
536                Utf8,
537                StringArray
538            );
539
540            test_function!(
541                LPadFunc::new(),
542                vec![
543                    ColumnarValue::Scalar(ScalarValue::LargeUtf8($INPUT)),
544                    ColumnarValue::Scalar($LENGTH)
545                ],
546                $EXPECTED,
547                &str,
548                LargeUtf8,
549                LargeStringArray
550            );
551
552            test_function!(
553                LPadFunc::new(),
554                vec![
555                    ColumnarValue::Scalar(ScalarValue::Utf8View($INPUT)),
556                    ColumnarValue::Scalar($LENGTH)
557                ],
558                $EXPECTED,
559                &str,
560                Utf8,
561                StringArray
562            );
563        };
564
565        ($INPUT:expr, $LENGTH:expr, $REPLACE:expr, $EXPECTED:expr) => {
566            // utf8, utf8
567            test_function!(
568                LPadFunc::new(),
569                vec![
570                    ColumnarValue::Scalar(ScalarValue::Utf8($INPUT)),
571                    ColumnarValue::Scalar($LENGTH),
572                    ColumnarValue::Scalar(ScalarValue::Utf8($REPLACE))
573                ],
574                $EXPECTED,
575                &str,
576                Utf8,
577                StringArray
578            );
579            // utf8, largeutf8
580            test_function!(
581                LPadFunc::new(),
582                vec![
583                    ColumnarValue::Scalar(ScalarValue::Utf8($INPUT)),
584                    ColumnarValue::Scalar($LENGTH),
585                    ColumnarValue::Scalar(ScalarValue::LargeUtf8($REPLACE))
586                ],
587                $EXPECTED,
588                &str,
589                Utf8,
590                StringArray
591            );
592            // utf8, utf8view
593            test_function!(
594                LPadFunc::new(),
595                vec![
596                    ColumnarValue::Scalar(ScalarValue::Utf8($INPUT)),
597                    ColumnarValue::Scalar($LENGTH),
598                    ColumnarValue::Scalar(ScalarValue::Utf8View($REPLACE))
599                ],
600                $EXPECTED,
601                &str,
602                Utf8,
603                StringArray
604            );
605
606            // largeutf8, utf8
607            test_function!(
608                LPadFunc::new(),
609                vec![
610                    ColumnarValue::Scalar(ScalarValue::LargeUtf8($INPUT)),
611                    ColumnarValue::Scalar($LENGTH),
612                    ColumnarValue::Scalar(ScalarValue::Utf8($REPLACE))
613                ],
614                $EXPECTED,
615                &str,
616                LargeUtf8,
617                LargeStringArray
618            );
619            // largeutf8, largeutf8
620            test_function!(
621                LPadFunc::new(),
622                vec![
623                    ColumnarValue::Scalar(ScalarValue::LargeUtf8($INPUT)),
624                    ColumnarValue::Scalar($LENGTH),
625                    ColumnarValue::Scalar(ScalarValue::LargeUtf8($REPLACE))
626                ],
627                $EXPECTED,
628                &str,
629                LargeUtf8,
630                LargeStringArray
631            );
632            // largeutf8, utf8view
633            test_function!(
634                LPadFunc::new(),
635                vec![
636                    ColumnarValue::Scalar(ScalarValue::LargeUtf8($INPUT)),
637                    ColumnarValue::Scalar($LENGTH),
638                    ColumnarValue::Scalar(ScalarValue::Utf8View($REPLACE))
639                ],
640                $EXPECTED,
641                &str,
642                LargeUtf8,
643                LargeStringArray
644            );
645
646            // utf8view, utf8
647            test_function!(
648                LPadFunc::new(),
649                vec![
650                    ColumnarValue::Scalar(ScalarValue::Utf8View($INPUT)),
651                    ColumnarValue::Scalar($LENGTH),
652                    ColumnarValue::Scalar(ScalarValue::Utf8($REPLACE))
653                ],
654                $EXPECTED,
655                &str,
656                Utf8,
657                StringArray
658            );
659            // utf8view, largeutf8
660            test_function!(
661                LPadFunc::new(),
662                vec![
663                    ColumnarValue::Scalar(ScalarValue::Utf8View($INPUT)),
664                    ColumnarValue::Scalar($LENGTH),
665                    ColumnarValue::Scalar(ScalarValue::LargeUtf8($REPLACE))
666                ],
667                $EXPECTED,
668                &str,
669                Utf8,
670                StringArray
671            );
672            // utf8view, utf8view
673            test_function!(
674                LPadFunc::new(),
675                vec![
676                    ColumnarValue::Scalar(ScalarValue::Utf8View($INPUT)),
677                    ColumnarValue::Scalar($LENGTH),
678                    ColumnarValue::Scalar(ScalarValue::Utf8View($REPLACE))
679                ],
680                $EXPECTED,
681                &str,
682                Utf8,
683                StringArray
684            );
685        };
686    }
687
688    #[test]
689    fn test_functions() -> Result<()> {
690        test_lpad!(
691            Some("josé".into()),
692            ScalarValue::Int64(Some(5i64)),
693            Ok(Some(" josé"))
694        );
695        test_lpad!(
696            Some("hi".into()),
697            ScalarValue::Int64(Some(5i64)),
698            Ok(Some("   hi"))
699        );
700        test_lpad!(
701            Some("hi".into()),
702            ScalarValue::Int64(Some(0i64)),
703            Ok(Some(""))
704        );
705        test_lpad!(Some("hi".into()), ScalarValue::Int64(None), Ok(None));
706        test_lpad!(None, ScalarValue::Int64(Some(5i64)), Ok(None));
707        test_lpad!(
708            Some("hi".into()),
709            ScalarValue::Int64(Some(5i64)),
710            Some("xy".into()),
711            Ok(Some("xyxhi"))
712        );
713        test_lpad!(
714            Some("hi".into()),
715            ScalarValue::Int64(Some(21i64)),
716            Some("abcdef".into()),
717            Ok(Some("abcdefabcdefabcdefahi"))
718        );
719        test_lpad!(
720            Some("hi".into()),
721            ScalarValue::Int64(Some(5i64)),
722            Some(" ".into()),
723            Ok(Some("   hi"))
724        );
725        test_lpad!(
726            Some("hi".into()),
727            ScalarValue::Int64(Some(5i64)),
728            Some("".into()),
729            Ok(Some("hi"))
730        );
731        test_lpad!(
732            None,
733            ScalarValue::Int64(Some(5i64)),
734            Some("xy".into()),
735            Ok(None)
736        );
737        test_lpad!(
738            Some("hi".into()),
739            ScalarValue::Int64(None),
740            Some("xy".into()),
741            Ok(None)
742        );
743        test_lpad!(
744            Some("hi".into()),
745            ScalarValue::Int64(Some(5i64)),
746            None,
747            Ok(None)
748        );
749        test_lpad!(
750            Some("hello".into()),
751            ScalarValue::Int64(Some(2i64)),
752            Ok(Some("he"))
753        );
754        test_lpad!(
755            Some("hi".into()),
756            ScalarValue::Int64(Some(6i64)),
757            Some("xy".into()),
758            Ok(Some("xyxyhi"))
759        );
760        test_lpad!(
761            Some("josé".into()),
762            ScalarValue::Int64(Some(10i64)),
763            Some("xy".into()),
764            Ok(Some("xyxyxyjosé"))
765        );
766        test_lpad!(
767            Some("josé".into()),
768            ScalarValue::Int64(Some(10i64)),
769            Some("éñ".into()),
770            Ok(Some("éñéñéñjosé"))
771        );
772
773        #[cfg(not(feature = "unicode_expressions"))]
774        test_lpad!(
775            Some("josé".into()),
776            ScalarValue::Int64(Some(5i64)),
777            internal_err!(
778                "function lpad requires compilation with feature flag: unicode_expressions."
779            )
780        );
781
782        Ok(())
783    }
784}