1use crate::strings::{
19 BulkNullStringArrayBuilder, GenericStringArrayBuilder, StringViewArrayBuilder,
20};
21use crate::utils::utf8_to_str_type;
22use arrow::array::{
23 Array, ArrayRef, AsArray, ByteView, Int64Array, StringArrayType, StringViewArray,
24 make_view, new_null_array,
25};
26use arrow::buffer::{NullBuffer, ScalarBuffer};
27use arrow::datatypes::DataType;
28use datafusion_common::ScalarValue;
29use datafusion_common::cast::as_int64_array;
30use datafusion_common::types::{NativeType, logical_int64, logical_string};
31use datafusion_common::{Result, exec_datafusion_err, exec_err};
32use datafusion_expr::{
33 Coercion, ColumnarValue, Documentation, TypeSignatureClass, Volatility,
34};
35use datafusion_expr::{ScalarFunctionArgs, ScalarUDFImpl, Signature};
36use datafusion_macros::user_doc;
37use memchr::memmem;
38use std::sync::Arc;
39
40#[user_doc(
41 doc_section(label = "String Functions"),
42 description = "Splits a string based on a specified delimiter and returns the substring in the specified position.",
43 syntax_example = "split_part(str, delimiter, pos)",
44 sql_example = r#"```sql
45> select split_part('1.2.3.4.5', '.', 3);
46+--------------------------------------------------+
47| split_part(Utf8("1.2.3.4.5"),Utf8("."),Int64(3)) |
48+--------------------------------------------------+
49| 3 |
50+--------------------------------------------------+
51```"#,
52 standard_argument(name = "str", prefix = "String"),
53 argument(name = "delimiter", description = "String or character to split on."),
54 argument(
55 name = "pos",
56 description = "Position of the part to return (counting from 1). Negative values count backward from the end of the string."
57 )
58)]
59#[derive(Debug, PartialEq, Eq, Hash)]
60pub struct SplitPartFunc {
61 signature: Signature,
62}
63
64impl Default for SplitPartFunc {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl SplitPartFunc {
71 pub fn new() -> Self {
72 Self {
73 signature: Signature::coercible(
74 vec![
75 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
76 Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
77 Coercion::new_implicit(
78 TypeSignatureClass::Native(logical_int64()),
79 vec![TypeSignatureClass::Integer],
80 NativeType::Int64,
81 ),
82 ],
83 Volatility::Immutable,
84 ),
85 }
86 }
87}
88
89impl ScalarUDFImpl for SplitPartFunc {
90 fn name(&self) -> &str {
91 "split_part"
92 }
93
94 fn signature(&self) -> &Signature {
95 &self.signature
96 }
97
98 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
99 if arg_types[0] == DataType::Utf8View {
100 Ok(DataType::Utf8View)
101 } else {
102 utf8_to_str_type(&arg_types[0], "split_part")
103 }
104 }
105
106 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
107 let ScalarFunctionArgs { args, .. } = args;
108
109 if let (
111 ColumnarValue::Array(string_array),
112 ColumnarValue::Scalar(delim_scalar),
113 ColumnarValue::Scalar(pos_scalar),
114 ) = (&args[0], &args[1], &args[2])
115 {
116 return split_part_scalar(string_array, delim_scalar, pos_scalar);
117 }
118
119 let len = args.iter().find_map(|arg| match arg {
121 ColumnarValue::Array(a) => Some(a.len()),
122 _ => None,
123 });
124
125 let inferred_length = len.unwrap_or(1);
126 let is_scalar = len.is_none();
127
128 let args = args
130 .iter()
131 .map(|arg| match arg {
132 ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(inferred_length),
133 ColumnarValue::Array(array) => Ok(Arc::clone(array)),
134 })
135 .collect::<Result<Vec<_>>>()?;
136
137 let n_array = as_int64_array(&args[2])?;
139
140 macro_rules! split_part_for_delimiter_type {
142 ($str_arr:expr, $builder:expr) => {
143 match args[1].data_type() {
144 DataType::Utf8View => split_part_impl(
145 $str_arr,
146 &args[1].as_string_view(),
147 n_array,
148 $builder,
149 ),
150 DataType::Utf8 => split_part_impl(
151 $str_arr,
152 &args[1].as_string::<i32>(),
153 n_array,
154 $builder,
155 ),
156 DataType::LargeUtf8 => split_part_impl(
157 $str_arr,
158 &args[1].as_string::<i64>(),
159 n_array,
160 $builder,
161 ),
162 other => {
163 exec_err!("Unsupported delimiter type {other:?} for split_part")
164 }
165 }
166 };
167 }
168
169 let result = match args[0].data_type() {
170 DataType::Utf8View => split_part_for_delimiter_type!(
171 &args[0].as_string_view(),
172 StringViewArrayBuilder::with_capacity(inferred_length)
173 ),
174 DataType::Utf8 => {
175 let str_arr = &args[0].as_string::<i32>();
176 split_part_for_delimiter_type!(
180 str_arr,
181 GenericStringArrayBuilder::<i32>::with_capacity(
182 inferred_length,
183 inferred_length,
184 )
185 )
186 }
187 DataType::LargeUtf8 => {
188 let str_arr = &args[0].as_string::<i64>();
189 split_part_for_delimiter_type!(
191 str_arr,
192 GenericStringArrayBuilder::<i64>::with_capacity(
193 inferred_length,
194 inferred_length,
195 )
196 )
197 }
198 other => exec_err!("Unsupported string type {other:?} for split_part"),
199 };
200 if is_scalar {
201 let result = result.and_then(|arr| ScalarValue::try_from_array(&arr, 0));
203 result.map(ColumnarValue::Scalar)
204 } else {
205 result.map(ColumnarValue::Array)
206 }
207 }
208
209 fn documentation(&self) -> Option<&Documentation> {
210 self.doc()
211 }
212}
213
214#[inline]
216fn split_nth<'a>(string: &'a str, delimiter: &str, n: usize) -> Option<&'a str> {
217 if delimiter.len() == 1 {
218 string.split(delimiter.as_bytes()[0] as char).nth(n)
223 } else {
224 string.split(delimiter).nth(n)
225 }
226}
227
228#[inline]
230fn rsplit_nth<'a>(string: &'a str, delimiter: &str, n: usize) -> Option<&'a str> {
231 if delimiter.len() == 1 {
232 string.rsplit(delimiter.as_bytes()[0] as char).nth(n)
237 } else {
238 string.rsplit(delimiter).nth(n)
239 }
240}
241
242fn split_part_scalar(
244 string_array: &ArrayRef,
245 delim_scalar: &ScalarValue,
246 pos_scalar: &ScalarValue,
247) -> Result<ColumnarValue> {
248 if string_array.is_empty() {
250 return Ok(ColumnarValue::Array(new_null_array(
251 string_array.data_type(),
252 0,
253 )));
254 }
255
256 let delimiter = delim_scalar.try_as_str().ok_or_else(|| {
257 exec_datafusion_err!(
258 "Unsupported delimiter type {:?} for split_part",
259 delim_scalar.data_type()
260 )
261 })?;
262
263 let position = match pos_scalar {
264 ScalarValue::Int64(v) => *v,
265 other => {
266 return exec_err!(
267 "Unsupported position type {:?} for split_part",
268 other.data_type()
269 );
270 }
271 };
272
273 let (Some(delimiter), Some(position)) = (delimiter, position) else {
275 return Ok(ColumnarValue::Array(new_null_array(
276 string_array.data_type(),
277 string_array.len(),
278 )));
279 };
280
281 if position == 0 {
282 return exec_err!("field position must not be zero");
283 }
284
285 let result = match string_array.data_type() {
286 DataType::Utf8View => {
287 split_part_scalar_view(string_array.as_string_view(), delimiter, position)
288 }
289 DataType::Utf8 => {
290 let arr = string_array.as_string::<i32>();
291 split_part_scalar_impl(
295 arr,
296 delimiter,
297 position,
298 GenericStringArrayBuilder::<i32>::with_capacity(arr.len(), arr.len()),
299 )
300 }
301 DataType::LargeUtf8 => {
302 let arr = string_array.as_string::<i64>();
303 split_part_scalar_impl(
305 arr,
306 delimiter,
307 position,
308 GenericStringArrayBuilder::<i64>::with_capacity(arr.len(), arr.len()),
309 )
310 }
311 other => exec_err!("Unsupported string type {other:?} for split_part"),
312 }?;
313
314 Ok(ColumnarValue::Array(result))
315}
316
317fn split_part_scalar_impl<'a, S, B>(
321 string_array: S,
322 delimiter: &str,
323 position: i64,
324 builder: B,
325) -> Result<ArrayRef>
326where
327 S: StringArrayType<'a> + Copy,
328 B: BulkNullStringArrayBuilder,
329{
330 if delimiter.is_empty() {
331 return if position == 1 || position == -1 {
334 map_strings(string_array, builder, Some)
335 } else {
336 map_strings(string_array, builder, |_| None)
337 };
338 }
339
340 let delim_bytes = delimiter.as_bytes();
341 let delim_len = delimiter.len();
342
343 if position > 0 {
344 let idx: usize = (position - 1).try_into().map_err(|_| {
345 exec_datafusion_err!(
346 "split_part index {position} exceeds maximum supported value"
347 )
348 })?;
349 let finder = memmem::Finder::new(delim_bytes);
350 map_strings(string_array, builder, |s| {
351 split_nth_finder(s, &finder, delim_len, idx)
352 })
353 } else {
354 let idx: usize = (position.unsigned_abs() - 1).try_into().map_err(|_| {
355 exec_datafusion_err!(
356 "split_part index {position} exceeds minimum supported value"
357 )
358 })?;
359 let finder_rev = memmem::FinderRev::new(delim_bytes);
360 map_strings(string_array, builder, |s| {
361 rsplit_nth_finder(s, &finder_rev, delim_len, idx)
362 })
363 }
364}
365
366#[inline]
369fn map_strings<'a, S, B, F>(string_array: S, mut builder: B, f: F) -> Result<ArrayRef>
370where
371 S: StringArrayType<'a> + Copy,
372 B: BulkNullStringArrayBuilder,
373 F: Fn(&'a str) -> Option<&'a str>,
374{
375 let item_len = string_array.len();
376 let nulls = string_array.nulls().cloned();
377
378 if let Some(ref n) = nulls {
379 for i in 0..item_len {
380 if n.is_null(i) {
381 builder.append_placeholder();
382 } else {
383 let s = unsafe { string_array.value_unchecked(i) };
385 builder.append_value(f(s).unwrap_or(""));
386 }
387 }
388 } else {
389 for i in 0..item_len {
390 let s = unsafe { string_array.value_unchecked(i) };
392 builder.append_value(f(s).unwrap_or(""));
393 }
394 }
395
396 builder.finish(nulls)
397}
398
399#[inline]
401fn split_nth_finder<'a>(
402 string: &'a str,
403 finder: &memmem::Finder,
404 delim_len: usize,
405 n: usize,
406) -> Option<&'a str> {
407 let bytes = string.as_bytes();
408 let mut start = 0;
409 for _ in 0..n {
410 let pos = finder.find(&bytes[start..])?;
411 start += pos + delim_len
412 }
413 match finder.find(&bytes[start..]) {
414 Some(pos) => Some(&string[start..start + pos]),
415 None => Some(&string[start..]),
416 }
417}
418
419#[inline]
422fn rsplit_nth_finder<'a>(
423 string: &'a str,
424 finder: &memmem::FinderRev,
425 delim_len: usize,
426 n: usize,
427) -> Option<&'a str> {
428 let bytes = string.as_bytes();
429 let mut end = bytes.len();
430 for _ in 0..n {
431 let pos = finder.rfind(&bytes[..end])?;
432 end = pos
433 }
434 match finder.rfind(&bytes[..end]) {
435 Some(pos) => Some(&string[pos + delim_len..end]),
436 None => Some(&string[..end]),
437 }
438}
439
440fn split_part_scalar_view(
446 string_view_array: &StringViewArray,
447 delimiter: &str,
448 position: i64,
449) -> Result<ArrayRef> {
450 let len = string_view_array.len();
451 let mut views_buf = Vec::with_capacity(len);
452 let views = string_view_array.views();
453
454 if delimiter.is_empty() {
455 let empty_view = make_view(b"", 0, 0);
457 let return_input = position == 1 || position == -1;
458 for i in 0..len {
459 if string_view_array.is_null(i) {
460 views_buf.push(0);
461 } else if return_input {
462 views_buf.push(views[i]);
463 } else {
464 views_buf.push(empty_view);
465 }
466 }
467 } else if position > 0 {
468 let idx: usize = (position - 1).try_into().map_err(|_| {
469 exec_datafusion_err!(
470 "split_part index {position} exceeds maximum supported value"
471 )
472 })?;
473 let finder = memmem::Finder::new(delimiter.as_bytes());
474 split_view_loop(string_view_array, views, &mut views_buf, |s| {
475 split_nth_finder(s, &finder, delimiter.len(), idx)
476 });
477 } else {
478 let idx: usize = (position.unsigned_abs() - 1).try_into().map_err(|_| {
479 exec_datafusion_err!(
480 "split_part index {position} exceeds minimum supported value"
481 )
482 })?;
483 let finder_rev = memmem::FinderRev::new(delimiter.as_bytes());
484 split_view_loop(string_view_array, views, &mut views_buf, |s| {
485 rsplit_nth_finder(s, &finder_rev, delimiter.len(), idx)
486 });
487 }
488
489 let views_buf = ScalarBuffer::from(views_buf);
490
491 let nulls = string_view_array.nulls().cloned();
493
494 unsafe {
498 Ok(Arc::new(StringViewArray::new_unchecked(
499 views_buf,
500 string_view_array.data_buffers().to_vec(),
501 nulls,
502 )) as ArrayRef)
503 }
504}
505
506#[inline]
509fn substr_view(original_view: &u128, substr: &str, start_offset: u32) -> u128 {
510 if substr.len() > 12 {
511 let view = ByteView::from(*original_view);
512 make_view(
513 substr.as_bytes(),
514 view.buffer_index,
515 view.offset + start_offset,
516 )
517 } else {
518 make_view(substr.as_bytes(), 0, 0)
519 }
520}
521
522#[inline(always)]
525fn split_view_loop<F>(
526 string_view_array: &StringViewArray,
527 views: &[u128],
528 views_buf: &mut Vec<u128>,
529 split_fn: F,
530) where
531 F: Fn(&str) -> Option<&str>,
532{
533 let empty_view = make_view(b"", 0, 0);
534 for (i, raw_view) in views.iter().enumerate() {
535 if string_view_array.is_null(i) {
536 views_buf.push(0);
537 continue;
538 }
539 let string = string_view_array.value(i);
540 match split_fn(string) {
541 Some(substr) => {
542 let start_offset = substr.as_ptr() as usize - string.as_ptr() as usize;
543 views_buf.push(substr_view(raw_view, substr, start_offset as u32));
544 }
545 None => views_buf.push(empty_view),
546 }
547 }
548}
549
550fn split_part_impl<'a, StringArrType, DelimiterArrType, B>(
551 string_array: &StringArrType,
552 delimiter_array: &DelimiterArrType,
553 n_array: &Int64Array,
554 mut builder: B,
555) -> Result<ArrayRef>
556where
557 StringArrType: StringArrayType<'a>,
558 DelimiterArrType: StringArrayType<'a>,
559 B: BulkNullStringArrayBuilder,
560{
561 let nulls = NullBuffer::union_many([
562 string_array.nulls(),
563 delimiter_array.nulls(),
564 n_array.nulls(),
565 ]);
566
567 if let Some(ref n) = nulls {
568 for i in 0..string_array.len() {
569 if n.is_null(i) {
570 builder.append_placeholder();
571 continue;
572 }
573
574 let string = unsafe { string_array.value_unchecked(i) };
576 let delimiter = unsafe { delimiter_array.value_unchecked(i) };
577 let position = unsafe { n_array.value_unchecked(i) };
578 append_split_part(string, delimiter, position, &mut builder)?;
579 }
580 } else {
581 for i in 0..string_array.len() {
582 let string = unsafe { string_array.value_unchecked(i) };
584 let delimiter = unsafe { delimiter_array.value_unchecked(i) };
585 let position = unsafe { n_array.value_unchecked(i) };
586 append_split_part(string, delimiter, position, &mut builder)?;
587 }
588 }
589
590 builder.finish(nulls)
591}
592
593#[inline]
594fn append_split_part<B: BulkNullStringArrayBuilder>(
595 string: &str,
596 delimiter: &str,
597 n: i64,
598 builder: &mut B,
599) -> Result<()> {
600 let result = match n.cmp(&0) {
601 std::cmp::Ordering::Greater => {
602 let idx: usize = (n - 1).try_into().map_err(|_| {
603 exec_datafusion_err!(
604 "split_part index {n} exceeds maximum supported value"
605 )
606 })?;
607 if delimiter.is_empty() {
608 (n == 1).then_some(string)
611 } else {
612 split_nth(string, delimiter, idx)
613 }
614 }
615 std::cmp::Ordering::Less => {
616 let idx: usize = (n.unsigned_abs() - 1).try_into().map_err(|_| {
617 exec_datafusion_err!(
618 "split_part index {n} exceeds minimum supported value"
619 )
620 })?;
621 if delimiter.is_empty() {
622 (n == -1).then_some(string)
625 } else {
626 rsplit_nth(string, delimiter, idx)
627 }
628 }
629 std::cmp::Ordering::Equal => {
630 return exec_err!("field position must not be zero");
631 }
632 };
633 builder.append_value(result.unwrap_or(""));
634 Ok(())
635}
636
637#[cfg(test)]
638mod tests {
639 use arrow::array::{Array, AsArray, StringArray, StringViewArray};
640 use arrow::datatypes::DataType::Utf8;
641
642 use datafusion_common::ScalarValue;
643 use datafusion_common::{Result, exec_err};
644 use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
645
646 use crate::string::split_part::SplitPartFunc;
647 use crate::utils::test::test_function;
648
649 #[test]
650 fn test_functions() -> Result<()> {
651 test_function!(
652 SplitPartFunc::new(),
653 vec![
654 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(
655 "abc~@~def~@~ghi"
656 )))),
657 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("~@~")))),
658 ColumnarValue::Scalar(ScalarValue::Int64(Some(2))),
659 ],
660 Ok(Some("def")),
661 &str,
662 Utf8,
663 StringArray
664 );
665 test_function!(
666 SplitPartFunc::new(),
667 vec![
668 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(
669 "abc~@~def~@~ghi"
670 )))),
671 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("~@~")))),
672 ColumnarValue::Scalar(ScalarValue::Int64(Some(20))),
673 ],
674 Ok(Some("")),
675 &str,
676 Utf8,
677 StringArray
678 );
679 test_function!(
680 SplitPartFunc::new(),
681 vec![
682 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(
683 "abc~@~def~@~ghi"
684 )))),
685 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("~@~")))),
686 ColumnarValue::Scalar(ScalarValue::Int64(Some(-1))),
687 ],
688 Ok(Some("ghi")),
689 &str,
690 Utf8,
691 StringArray
692 );
693 test_function!(
694 SplitPartFunc::new(),
695 vec![
696 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(
697 "abc~@~def~@~ghi"
698 )))),
699 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("~@~")))),
700 ColumnarValue::Scalar(ScalarValue::Int64(Some(0))),
701 ],
702 exec_err!("field position must not be zero"),
703 &str,
704 Utf8,
705 StringArray
706 );
707 test_function!(
708 SplitPartFunc::new(),
709 vec![
710 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(
711 "abc~@~def~@~ghi"
712 )))),
713 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("~@~")))),
714 ColumnarValue::Scalar(ScalarValue::Int64(Some(i64::MIN))),
715 ],
716 Ok(Some("")),
717 &str,
718 Utf8,
719 StringArray
720 );
721 test_function!(
723 SplitPartFunc::new(),
724 vec![
725 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
726 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(",")))),
727 ColumnarValue::Scalar(ScalarValue::Int64(Some(1))),
728 ],
729 Ok(Some("a")),
730 &str,
731 Utf8,
732 StringArray
733 );
734 test_function!(
735 SplitPartFunc::new(),
736 vec![
737 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
738 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(",")))),
739 ColumnarValue::Scalar(ScalarValue::Int64(Some(3))),
740 ],
741 Ok(Some("")),
742 &str,
743 Utf8,
744 StringArray
745 );
746 test_function!(
747 SplitPartFunc::new(),
748 vec![
749 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
750 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("")))),
751 ColumnarValue::Scalar(ScalarValue::Int64(Some(1))),
752 ],
753 Ok(Some("a,b")),
754 &str,
755 Utf8,
756 StringArray
757 );
758 test_function!(
759 SplitPartFunc::new(),
760 vec![
761 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
762 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("")))),
763 ColumnarValue::Scalar(ScalarValue::Int64(Some(2))),
764 ],
765 Ok(Some("")),
766 &str,
767 Utf8,
768 StringArray
769 );
770 test_function!(
771 SplitPartFunc::new(),
772 vec![
773 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
774 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(" ")))),
775 ColumnarValue::Scalar(ScalarValue::Int64(Some(1))),
776 ],
777 Ok(Some("a,b")),
778 &str,
779 Utf8,
780 StringArray
781 );
782 test_function!(
783 SplitPartFunc::new(),
784 vec![
785 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
786 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(" ")))),
787 ColumnarValue::Scalar(ScalarValue::Int64(Some(2))),
788 ],
789 Ok(Some("")),
790 &str,
791 Utf8,
792 StringArray
793 );
794
795 test_function!(
797 SplitPartFunc::new(),
798 vec![
799 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
800 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("")))),
801 ColumnarValue::Scalar(ScalarValue::Int64(Some(-1))),
802 ],
803 Ok(Some("a,b")),
804 &str,
805 Utf8,
806 StringArray
807 );
808 test_function!(
809 SplitPartFunc::new(),
810 vec![
811 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
812 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from(" ")))),
813 ColumnarValue::Scalar(ScalarValue::Int64(Some(-1))),
814 ],
815 Ok(Some("a,b")),
816 &str,
817 Utf8,
818 StringArray
819 );
820 test_function!(
821 SplitPartFunc::new(),
822 vec![
823 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("a,b")))),
824 ColumnarValue::Scalar(ScalarValue::Utf8(Some(String::from("")))),
825 ColumnarValue::Scalar(ScalarValue::Int64(Some(-2))),
826 ],
827 Ok(Some("")),
828 &str,
829 Utf8,
830 StringArray
831 );
832
833 Ok(())
834 }
835
836 #[test]
837 fn test_split_part_stringview_sliced() -> Result<()> {
838 use super::split_part_scalar_view;
839
840 let strings: StringViewArray = vec![
841 Some("skip_this.value"),
842 Some("this_is_a_long_prefix.suffix"),
843 Some("short.val"),
844 Some("another_long_result.rest"),
845 None,
846 ]
847 .into_iter()
848 .collect();
849
850 let sliced = strings.slice(1, 4);
852 let result = split_part_scalar_view(&sliced, ".", 1)?;
853 let result = result.as_string_view();
854 assert_eq!(result.len(), 4);
855 assert_eq!(result.value(0), "this_is_a_long_prefix");
856 assert_eq!(result.value(1), "short");
857 assert_eq!(result.value(2), "another_long_result");
858 assert!(result.is_null(3));
859
860 Ok(())
861 }
862}