1use std::cmp::{self, Ordering};
21use std::sync::Arc;
22use std::task::{Poll, ready};
23
24use super::metrics::{
25 self, BaselineMetrics, ExecutionPlanMetricsSet, MetricBuilder, MetricCategory,
26 MetricsSet, RecordOutput,
27};
28use super::{DisplayAs, ExecutionPlanProperties, PlanProperties};
29use crate::stream::EmptyRecordBatchStream;
30use crate::{
31 ChildrenPropertiesMode, DisplayFormatType, Distribution, ExecutionPlan,
32 RecordBatchStream, ReplaceChildrenOptions, SendableRecordBatchStream,
33 validate_child_count,
34};
35
36use arrow::array::{
37 Array, ArrayRef, AsArray, BooleanBufferBuilder, FixedSizeListArray, Int64Array,
38 LargeListArray, LargeListViewArray, ListArray, ListViewArray, PrimitiveArray, Scalar,
39 StructArray, new_null_array,
40};
41use arrow::compute::kernels::length::length;
42use arrow::compute::kernels::zip::zip;
43use arrow::compute::{cast, is_not_null, kernels, sum};
44use arrow::datatypes::{DataType, Int64Type, Schema, SchemaRef};
45use arrow::record_batch::RecordBatch;
46use arrow_ord::cmp::lt;
47use async_trait::async_trait;
48use datafusion_common::tree_node::TreeNodeRecursion;
49use datafusion_common::{
50 Constraints, HashMap, HashSet, Result, UnnestOptions, exec_datafusion_err, exec_err,
51 internal_err,
52};
53use datafusion_execution::TaskContext;
54use datafusion_physical_expr::PhysicalExpr;
55use datafusion_physical_expr::equivalence::ProjectionMapping;
56use datafusion_physical_expr::expressions::Column;
57use futures::{Stream, StreamExt};
58use log::trace;
59
60#[derive(Debug, Clone)]
67pub struct UnnestExec {
68 input: Arc<dyn ExecutionPlan>,
70 schema: SchemaRef,
72 list_column_indices: Vec<ListUnnest>,
74 struct_column_indices: Vec<usize>,
76 options: UnnestOptions,
78 metrics: ExecutionPlanMetricsSet,
80 cache: Arc<PlanProperties>,
82}
83
84impl UnnestExec {
85 pub fn new(
87 input: Arc<dyn ExecutionPlan>,
88 list_column_indices: Vec<ListUnnest>,
89 struct_column_indices: Vec<usize>,
90 schema: SchemaRef,
91 options: UnnestOptions,
92 ) -> Result<Self> {
93 let cache = Self::compute_properties(
94 &input,
95 &list_column_indices,
96 &struct_column_indices,
97 &schema,
98 )?;
99
100 Ok(UnnestExec {
101 input,
102 schema,
103 list_column_indices,
104 struct_column_indices,
105 options,
106 metrics: Default::default(),
107 cache: Arc::new(cache),
108 })
109 }
110
111 fn compute_properties(
113 input: &Arc<dyn ExecutionPlan>,
114 list_column_indices: &[ListUnnest],
115 struct_column_indices: &[usize],
116 schema: &SchemaRef,
117 ) -> Result<PlanProperties> {
118 let input_schema = input.schema();
120 let mut unnested_indices = BooleanBufferBuilder::new(input_schema.fields().len());
121 unnested_indices.append_n(input_schema.fields().len(), false);
122 for list_unnest in list_column_indices {
123 unnested_indices.set_bit(list_unnest.index_in_input_schema, true);
124 }
125 for struct_unnest in struct_column_indices {
126 unnested_indices.set_bit(*struct_unnest, true)
127 }
128 let unnested_indices = unnested_indices.finish();
129 let non_unnested_indices: Vec<usize> = (0..input_schema.fields().len())
130 .filter(|idx| !unnested_indices.value(*idx))
131 .collect();
132
133 let input_schema = input.schema();
135 let projection_mapping: ProjectionMapping = non_unnested_indices
136 .iter()
137 .map(|&input_idx| {
138 let input_field = input_schema.field(input_idx);
140 let output_idx = schema
141 .fields()
142 .iter()
143 .position(|output_field| output_field.name() == input_field.name())
144 .ok_or_else(|| {
145 exec_datafusion_err!(
146 "Non-unnested column '{}' must exist in output schema",
147 input_field.name()
148 )
149 })?;
150
151 let input_col = Arc::new(Column::new(input_field.name(), input_idx))
152 as Arc<dyn PhysicalExpr>;
153 let target_col = Arc::new(Column::new(input_field.name(), output_idx))
154 as Arc<dyn PhysicalExpr>;
155 let targets = vec![(target_col, output_idx)].into();
157 Ok((input_col, targets))
158 })
159 .collect::<Result<ProjectionMapping>>()?;
160
161 let input_eq_properties = input.equivalence_properties();
165 let eq_properties = input_eq_properties
166 .project(&projection_mapping, Arc::clone(schema))
167 .with_constraints(Constraints::default());
168
169 let output_partitioning = input
171 .output_partitioning()
172 .project(&projection_mapping, &eq_properties);
173
174 Ok(PlanProperties::new(
175 eq_properties,
176 output_partitioning,
177 input.pipeline_behavior(),
178 input.boundedness(),
179 ))
180 }
181
182 pub fn input(&self) -> &Arc<dyn ExecutionPlan> {
184 &self.input
185 }
186
187 pub fn list_column_indices(&self) -> &[ListUnnest] {
189 &self.list_column_indices
190 }
191
192 pub fn struct_column_indices(&self) -> &[usize] {
194 &self.struct_column_indices
195 }
196
197 pub fn options(&self) -> &UnnestOptions {
198 &self.options
199 }
200}
201
202impl DisplayAs for UnnestExec {
203 fn fmt_as(
204 &self,
205 t: DisplayFormatType,
206 f: &mut std::fmt::Formatter,
207 ) -> std::fmt::Result {
208 match t {
209 DisplayFormatType::Default | DisplayFormatType::Verbose => {
210 write!(f, "UnnestExec")
211 }
212 DisplayFormatType::TreeRender => {
213 write!(f, "")
214 }
215 }
216 }
217}
218
219impl ExecutionPlan for UnnestExec {
220 fn name(&self) -> &'static str {
221 "UnnestExec"
222 }
223
224 fn properties(&self) -> &Arc<PlanProperties> {
225 &self.cache
226 }
227
228 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
229 vec![&self.input]
230 }
231
232 fn apply_expressions(
233 &self,
234 _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
235 ) -> Result<TreeNodeRecursion> {
236 Ok(TreeNodeRecursion::Continue)
237 }
238
239 fn replace_children(
240 self: Arc<Self>,
241 mut children: Vec<Arc<dyn ExecutionPlan>>,
242 options: ReplaceChildrenOptions,
243 ) -> Result<Arc<dyn ExecutionPlan>> {
244 validate_child_count!(self, children);
245 match options.children_properties {
246 ChildrenPropertiesMode::Keep => Ok(Arc::new(Self {
247 input: children.swap_remove(0),
248 metrics: ExecutionPlanMetricsSet::new(),
249 ..Self::clone(&*self)
250 })),
251 ChildrenPropertiesMode::Recompute => Ok(Arc::new(UnnestExec::new(
252 children.swap_remove(0),
253 self.list_column_indices.clone(),
254 self.struct_column_indices.clone(),
255 Arc::clone(&self.schema),
256 self.options.clone(),
257 )?)),
258 }
259 }
260
261 fn with_new_children(
262 self: Arc<Self>,
263 children: Vec<Arc<dyn ExecutionPlan>>,
264 ) -> Result<Arc<dyn ExecutionPlan>> {
265 self.replace_children(
266 children,
267 ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
268 )
269 }
270
271 fn with_new_children_and_same_properties(
272 self: Arc<Self>,
273 children: Vec<Arc<dyn ExecutionPlan>>,
274 ) -> Result<Arc<dyn ExecutionPlan>> {
275 self.replace_children(
276 children,
277 ReplaceChildrenOptions::new(ChildrenPropertiesMode::Keep),
278 )
279 }
280
281 fn required_input_distribution(&self) -> Vec<Distribution> {
282 self.input_distribution_requirements().into_per_child()
283 }
284
285 fn input_distribution_requirements(&self) -> crate::InputDistributionRequirements {
286 crate::InputDistributionRequirements::new(vec![
287 Distribution::UnspecifiedDistribution,
288 ])
289 }
290
291 fn execute(
292 &self,
293 partition: usize,
294 context: Arc<TaskContext>,
295 ) -> Result<SendableRecordBatchStream> {
296 let input = self.input.execute(partition, context)?;
297 let metrics = UnnestMetrics::new(partition, &self.metrics);
298
299 Ok(Box::pin(UnnestStream {
300 input,
301 schema: Arc::clone(&self.schema),
302 list_type_columns: self.list_column_indices.clone(),
303 struct_column_indices: self.struct_column_indices.iter().copied().collect(),
304 options: self.options.clone(),
305 metrics,
306 }))
307 }
308
309 fn metrics(&self) -> Option<MetricsSet> {
310 Some(self.metrics.clone_inner())
311 }
312
313 #[cfg(feature = "proto")]
314 fn try_to_proto(
315 &self,
316 ctx: &crate::proto::ExecutionPlanEncodeCtx<'_>,
317 ) -> Result<Option<datafusion_proto_models::protobuf::PhysicalPlanNode>> {
318 use datafusion_proto_models::protobuf;
319
320 let Self {
324 input,
325 schema,
326 list_column_indices,
327 struct_column_indices,
328 options,
329 metrics: _,
331 cache: _,
333 } = self;
334
335 let input = ctx.encode_child(input)?;
336 let schema = schema.as_ref().try_into()?;
337 let list_type_columns = list_column_indices
338 .iter()
339 .map(|column| protobuf::ListUnnest {
340 index_in_input_schema: column.index_in_input_schema as _,
341 depth: column.depth as _,
342 })
343 .collect();
344 let struct_type_columns = struct_column_indices
345 .iter()
346 .map(|index| *index as _)
347 .collect();
348 let null_handling = {
349 use datafusion_common::NullHandling;
350 use protobuf::unnest_options::NullHandling as ProtoNullHandling;
351 match options.null_handling {
352 NullHandling::Preserve => ProtoNullHandling::Preserve,
353 NullHandling::Drop => ProtoNullHandling::Drop,
354 NullHandling::PreserveAndExpandEmpty => {
355 ProtoNullHandling::PreserveAndExpandEmpty
356 }
357 }
358 } as i32;
359 let options = protobuf::UnnestOptions {
360 null_handling,
361 recursions: options
362 .recursions
363 .iter()
364 .map(|recursion| protobuf::RecursionUnnestOption {
365 input_column: Some((&recursion.input_column).into()),
366 output_column: Some((&recursion.output_column).into()),
367 depth: recursion.depth as _,
368 })
369 .collect(),
370 };
371
372 Ok(Some(protobuf::PhysicalPlanNode {
373 physical_plan_type: Some(
374 protobuf::physical_plan_node::PhysicalPlanType::Unnest(Box::new(
375 protobuf::UnnestExecNode {
376 input: Some(Box::new(input)),
377 schema: Some(schema),
378 list_type_columns,
379 struct_type_columns,
380 options: Some(options),
381 },
382 )),
383 ),
384 }))
385 }
386}
387
388#[cfg(feature = "proto")]
389impl UnnestExec {
390 pub fn try_from_proto(
396 node: &datafusion_proto_models::protobuf::PhysicalPlanNode,
397 ctx: &crate::proto::ExecutionPlanDecodeCtx<'_>,
398 ) -> Result<Arc<dyn ExecutionPlan>> {
399 use datafusion_proto_models::protobuf;
400
401 let unnest = crate::expect_plan_variant!(
402 node,
403 protobuf::physical_plan_node::PhysicalPlanType::Unnest,
404 "UnnestExec",
405 );
406 let protobuf::UnnestExecNode {
409 input,
410 schema,
411 list_type_columns,
412 struct_type_columns,
413 options,
414 } = unnest.as_ref();
415
416 let input = ctx.decode_required_child(input.as_deref(), "UnnestExec", "input")?;
417 let schema: Schema = schema
418 .as_ref()
419 .ok_or_else(|| {
420 datafusion_common::internal_datafusion_err!(
421 "UnnestExec is missing required field 'schema'"
422 )
423 })?
424 .try_into()?;
425 let list_column_indices = list_type_columns
426 .iter()
427 .map(|column| ListUnnest {
428 index_in_input_schema: column.index_in_input_schema as _,
429 depth: column.depth as _,
430 })
431 .collect();
432 let struct_column_indices = struct_type_columns
433 .iter()
434 .map(|index| *index as _)
435 .collect();
436 let options = options.as_ref().ok_or_else(|| {
437 datafusion_common::internal_datafusion_err!(
438 "UnnestExec is missing required field 'options'"
439 )
440 })?;
441 let null_handling = {
442 use datafusion_common::NullHandling;
443 use protobuf::unnest_options::NullHandling as ProtoNullHandling;
444 match ProtoNullHandling::try_from(options.null_handling) {
445 Ok(ProtoNullHandling::Preserve) => NullHandling::Preserve,
446 Ok(ProtoNullHandling::Drop) => NullHandling::Drop,
447 Ok(ProtoNullHandling::PreserveAndExpandEmpty) => {
448 NullHandling::PreserveAndExpandEmpty
449 }
450 Err(_) => NullHandling::Preserve,
453 }
454 };
455 let options = UnnestOptions {
456 null_handling,
457 recursions: options
458 .recursions
459 .iter()
460 .map(|recursion| datafusion_common::RecursionUnnestOption {
461 input_column: recursion.input_column.as_ref().unwrap().into(),
462 output_column: recursion.output_column.as_ref().unwrap().into(),
463 depth: recursion.depth as _,
464 })
465 .collect(),
466 };
467
468 Ok(Arc::new(UnnestExec::new(
469 input,
470 list_column_indices,
471 struct_column_indices,
472 Arc::new(schema),
473 options,
474 )?))
475 }
476}
477
478#[derive(Clone, Debug)]
479struct UnnestMetrics {
480 baseline_metrics: BaselineMetrics,
482 input_batches: metrics::Count,
484 input_rows: metrics::Count,
486}
487
488impl UnnestMetrics {
489 fn new(partition: usize, metrics: &ExecutionPlanMetricsSet) -> Self {
490 let input_batches = MetricBuilder::new(metrics)
491 .with_category(MetricCategory::Rows)
492 .counter("input_batches", partition);
493
494 let input_rows = MetricBuilder::new(metrics)
495 .with_category(MetricCategory::Rows)
496 .counter("input_rows", partition);
497
498 Self {
499 baseline_metrics: BaselineMetrics::new(metrics, partition),
500 input_batches,
501 input_rows,
502 }
503 }
504}
505
506struct UnnestStream {
508 input: SendableRecordBatchStream,
510 schema: Arc<Schema>,
512 list_type_columns: Vec<ListUnnest>,
516 struct_column_indices: HashSet<usize>,
517 options: UnnestOptions,
519 metrics: UnnestMetrics,
521}
522
523impl RecordBatchStream for UnnestStream {
524 fn schema(&self) -> SchemaRef {
525 Arc::clone(&self.schema)
526 }
527}
528
529#[async_trait]
530impl Stream for UnnestStream {
531 type Item = Result<RecordBatch>;
532
533 fn poll_next(
534 mut self: std::pin::Pin<&mut Self>,
535 cx: &mut std::task::Context<'_>,
536 ) -> Poll<Option<Self::Item>> {
537 self.poll_next_impl(cx)
538 }
539}
540
541impl UnnestStream {
542 fn poll_next_impl(
545 &mut self,
546 cx: &mut std::task::Context<'_>,
547 ) -> Poll<Option<Result<RecordBatch>>> {
548 loop {
549 return Poll::Ready(match ready!(self.input.poll_next_unpin(cx)) {
550 Some(Ok(batch)) => {
551 let elapsed_compute =
552 self.metrics.baseline_metrics.elapsed_compute().clone();
553 let timer = elapsed_compute.timer();
554 self.metrics.input_batches.add(1);
555 self.metrics.input_rows.add(batch.num_rows());
556 let result = build_batch(
557 &batch,
558 &self.schema,
559 &self.list_type_columns,
560 &self.struct_column_indices,
561 &self.options,
562 )?;
563 timer.done();
564 let Some(result_batch) = result else {
565 continue;
566 };
567 (&result_batch).record_output(&self.metrics.baseline_metrics);
568
569 debug_assert!(result_batch.num_rows() > 0);
572 Some(Ok(result_batch))
573 }
574 other => {
576 trace!(
577 "Processed {} probe-side input batches containing {} rows and \
578 produced {} output batches containing {} rows in {}",
579 self.metrics.input_batches,
580 self.metrics.input_rows,
581 self.metrics.baseline_metrics.output_batches(),
582 self.metrics.baseline_metrics.output_rows(),
583 self.metrics.baseline_metrics.elapsed_compute(),
584 );
585
586 if other.is_none() {
588 let input_schema = self.input.schema();
590 self.input = Box::pin(EmptyRecordBatchStream::new(input_schema));
591 }
592
593 other
594 }
595 });
596 }
597 }
598}
599
600fn flatten_struct_cols(
611 input_batch: &[Arc<dyn Array>],
612 schema: &SchemaRef,
613 struct_column_indices: &HashSet<usize>,
614) -> Result<RecordBatch> {
615 let columns_expanded = input_batch
617 .iter()
618 .enumerate()
619 .map(|(idx, column_data)| match struct_column_indices.get(&idx) {
620 Some(_) => match column_data.data_type() {
621 DataType::Struct(_) => {
622 let struct_arr =
623 column_data.as_any().downcast_ref::<StructArray>().unwrap();
624 Ok(struct_arr.columns().to_vec())
625 }
626 data_type => internal_err!(
627 "expecting column {idx} from input plan to be a struct, got {data_type}"
628 ),
629 },
630 None => Ok(vec![Arc::clone(column_data)]),
631 })
632 .collect::<Result<Vec<_>>>()?
633 .into_iter()
634 .flatten()
635 .collect();
636 Ok(RecordBatch::try_new(Arc::clone(schema), columns_expanded)?)
637}
638
639#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
640pub struct ListUnnest {
641 pub index_in_input_schema: usize,
642 pub depth: usize,
643}
644
645fn list_unnest_at_level(
684 batch: &[ArrayRef],
685 list_type_unnests: &[ListUnnest],
686 temp_unnested_arrs: &mut HashMap<ListUnnest, ArrayRef>,
687 level_to_unnest: usize,
688 options: &UnnestOptions,
689) -> Result<Option<Vec<ArrayRef>>> {
690 let (arrs_to_unnest, list_unnest_specs): (Vec<Arc<dyn Array>>, Vec<_>) =
692 list_type_unnests
693 .iter()
694 .filter_map(|unnesting| {
695 if level_to_unnest == unnesting.depth {
696 return Some((
697 Arc::clone(&batch[unnesting.index_in_input_schema]),
698 *unnesting,
699 ));
700 }
701 if level_to_unnest < unnesting.depth {
704 return Some((
705 Arc::clone(temp_unnested_arrs.get(unnesting).unwrap()),
706 *unnesting,
707 ));
708 }
709 None
710 })
711 .unzip();
712
713 let longest_length = find_longest_length(&arrs_to_unnest, options)?;
716 let unnested_length = longest_length.as_primitive::<Int64Type>();
717 let total_length = if unnested_length.is_empty() {
718 0
719 } else {
720 sum(unnested_length).ok_or_else(|| {
721 exec_datafusion_err!("Failed to calculate the total unnested length")
722 })? as usize
723 };
724 if total_length == 0 {
725 return Ok(None);
726 }
727
728 let unnested_temp_arrays =
730 unnest_list_arrays(arrs_to_unnest.as_ref(), unnested_length, total_length)?;
731
732 let take_indices = create_take_indices(unnested_length, total_length);
734 unnested_temp_arrays
735 .into_iter()
736 .zip(list_unnest_specs.iter())
737 .for_each(|(flatten_arr, unnesting)| {
738 temp_unnested_arrs.insert(*unnesting, flatten_arr);
739 });
740
741 let repeat_mask: Vec<bool> = batch
742 .iter()
743 .enumerate()
744 .map(|(i, _)| {
745 let needed_in_future_levels = list_type_unnests.iter().any(|unnesting| {
747 unnesting.index_in_input_schema == i && unnesting.depth < level_to_unnest
748 });
749
750 let is_involved_in_unnesting = list_type_unnests
752 .iter()
753 .any(|unnesting| unnesting.index_in_input_schema == i);
754
755 needed_in_future_levels || !is_involved_in_unnesting
757 })
758 .collect();
759
760 let ret = repeat_arrs_from_indices(batch, &take_indices, &repeat_mask)?;
763
764 Ok(Some(ret))
765}
766struct UnnestingResult {
767 arr: ArrayRef,
768 depth: usize,
769}
770
771fn build_batch(
828 batch: &RecordBatch,
829 schema: &SchemaRef,
830 list_type_columns: &[ListUnnest],
831 struct_column_indices: &HashSet<usize>,
832 options: &UnnestOptions,
833) -> Result<Option<RecordBatch>> {
834 let transformed = match list_type_columns.len() {
835 0 => flatten_struct_cols(batch.columns(), schema, struct_column_indices),
836 _ => {
837 let mut temp_unnested_result = HashMap::new();
838 let max_recursion = list_type_columns
839 .iter()
840 .fold(0, |highest_depth, ListUnnest { depth, .. }| {
841 cmp::max(highest_depth, *depth)
842 });
843
844 let mut flatten_arrs = vec![];
846
847 for depth in (1..=max_recursion).rev() {
850 let input = match depth == max_recursion {
851 true => batch.columns(),
852 false => &flatten_arrs,
853 };
854 let Some(temp_result) = list_unnest_at_level(
855 input,
856 list_type_columns,
857 &mut temp_unnested_result,
858 depth,
859 options,
860 )?
861 else {
862 return Ok(None);
863 };
864 flatten_arrs = temp_result;
865 }
866 let unnested_array_map: HashMap<usize, Vec<UnnestingResult>> =
867 temp_unnested_result.into_iter().fold(
868 HashMap::new(),
869 |mut acc,
870 (
871 ListUnnest {
872 index_in_input_schema,
873 depth,
874 },
875 flattened_array,
876 )| {
877 acc.entry(index_in_input_schema).or_default().push(
878 UnnestingResult {
879 arr: flattened_array,
880 depth,
881 },
882 );
883 acc
884 },
885 );
886 let output_order: HashMap<ListUnnest, usize> = list_type_columns
887 .iter()
888 .enumerate()
889 .map(|(order, unnest_def)| (*unnest_def, order))
890 .collect();
891
892 let mut multi_unnested_per_original_index = unnested_array_map
894 .into_iter()
895 .map(
896 |(original_index, mut unnested_columns)| {
900 unnested_columns.sort_by(
901 |UnnestingResult { depth: depth1, .. },
902 UnnestingResult { depth: depth2, .. }|
903 -> Ordering {
904 output_order
905 .get(&ListUnnest {
906 depth: *depth1,
907 index_in_input_schema: original_index,
908 })
909 .unwrap()
910 .cmp(
911 output_order
912 .get(&ListUnnest {
913 depth: *depth2,
914 index_in_input_schema: original_index,
915 })
916 .unwrap(),
917 )
918 },
919 );
920 (
921 original_index,
922 unnested_columns
923 .into_iter()
924 .map(|result| result.arr)
925 .collect::<Vec<_>>(),
926 )
927 },
928 )
929 .collect::<HashMap<_, _>>();
930
931 let ret = flatten_arrs
932 .into_iter()
933 .enumerate()
934 .flat_map(|(col_idx, arr)| {
935 match multi_unnested_per_original_index.remove(&col_idx) {
939 Some(unnested_arrays) => unnested_arrays,
940 None => vec![arr],
941 }
942 })
943 .collect::<Vec<_>>();
944
945 flatten_struct_cols(&ret, schema, struct_column_indices)
946 }
947 }?;
948 Ok(Some(transformed))
949}
950
951fn find_longest_length(
980 list_arrays: &[ArrayRef],
981 options: &UnnestOptions,
982) -> Result<ArrayRef> {
983 let null_length = if options.preserve_nulls() {
985 Scalar::new(Int64Array::from_value(1, 1))
986 } else {
987 Scalar::new(Int64Array::from_value(0, 1))
988 };
989 let expand_empty = options.expand_empty_as_null();
990 let zero = Scalar::new(Int64Array::from_value(0, 1));
992 let one = Scalar::new(Int64Array::from_value(1, 1));
993 let list_lengths: Vec<ArrayRef> = list_arrays
994 .iter()
995 .map(|list_array| {
996 let mut length_array = length(list_array)?;
997 length_array = cast(&length_array, &DataType::Int64)?;
999 length_array =
1000 zip(&is_not_null(&length_array)?, &length_array, &null_length)?;
1001 if expand_empty {
1002 let is_zero = arrow_ord::cmp::eq(&length_array, &zero)?;
1005 length_array = zip(&is_zero, &one, &length_array)?;
1006 }
1007 Ok(length_array)
1008 })
1009 .collect::<Result<_>>()?;
1010
1011 let longest_length = list_lengths.iter().skip(1).try_fold(
1012 Arc::clone(&list_lengths[0]),
1013 |longest, current| {
1014 let is_lt = lt(&longest, ¤t)?;
1015 zip(&is_lt, ¤t, &longest)
1016 },
1017 )?;
1018 Ok(longest_length)
1019}
1020
1021trait ListArrayType: Array {
1023 fn values(&self) -> &ArrayRef;
1025
1026 fn value_offsets(&self, row: usize) -> (i64, i64);
1028}
1029
1030impl ListArrayType for ListArray {
1031 fn values(&self) -> &ArrayRef {
1032 self.values()
1033 }
1034
1035 fn value_offsets(&self, row: usize) -> (i64, i64) {
1036 let offsets = self.value_offsets();
1037 (offsets[row].into(), offsets[row + 1].into())
1038 }
1039}
1040
1041impl ListArrayType for LargeListArray {
1042 fn values(&self) -> &ArrayRef {
1043 self.values()
1044 }
1045
1046 fn value_offsets(&self, row: usize) -> (i64, i64) {
1047 let offsets = self.value_offsets();
1048 (offsets[row], offsets[row + 1])
1049 }
1050}
1051
1052impl ListArrayType for FixedSizeListArray {
1053 fn values(&self) -> &ArrayRef {
1054 self.values()
1055 }
1056
1057 fn value_offsets(&self, row: usize) -> (i64, i64) {
1058 let start = self.value_offset(row) as i64;
1059 (start, start + self.value_length() as i64)
1060 }
1061}
1062
1063impl ListArrayType for ListViewArray {
1064 fn values(&self) -> &ArrayRef {
1065 self.values()
1066 }
1067
1068 fn value_offsets(&self, row: usize) -> (i64, i64) {
1069 let offset = self.value_offsets()[row] as i64;
1070 let size = self.value_sizes()[row] as i64;
1071 (offset, offset + size)
1072 }
1073}
1074
1075impl ListArrayType for LargeListViewArray {
1076 fn values(&self) -> &ArrayRef {
1077 self.values()
1078 }
1079
1080 fn value_offsets(&self, row: usize) -> (i64, i64) {
1081 let offset = self.value_offsets()[row];
1082 let size = self.value_sizes()[row];
1083 (offset, offset + size)
1084 }
1085}
1086
1087fn unnest_list_arrays(
1089 list_arrays: &[ArrayRef],
1090 length_array: &PrimitiveArray<Int64Type>,
1091 capacity: usize,
1092) -> Result<Vec<ArrayRef>> {
1093 let typed_arrays = list_arrays
1094 .iter()
1095 .map(|list_array| match list_array.data_type() {
1096 DataType::List(_) => Ok(list_array.as_list::<i32>() as &dyn ListArrayType),
1097 DataType::LargeList(_) => {
1098 Ok(list_array.as_list::<i64>() as &dyn ListArrayType)
1099 }
1100 DataType::FixedSizeList(_, _) => {
1101 Ok(list_array.as_fixed_size_list() as &dyn ListArrayType)
1102 }
1103 DataType::ListView(_) => {
1104 Ok(list_array.as_list_view::<i32>() as &dyn ListArrayType)
1105 }
1106 DataType::LargeListView(_) => {
1107 Ok(list_array.as_list_view::<i64>() as &dyn ListArrayType)
1108 }
1109 other => exec_err!("Invalid unnest datatype {other }"),
1110 })
1111 .collect::<Result<Vec<_>>>()?;
1112
1113 typed_arrays
1114 .iter()
1115 .map(|list_array| unnest_list_array(*list_array, length_array, capacity))
1116 .collect::<Result<_>>()
1117}
1118
1119fn unnest_list_array(
1140 list_array: &dyn ListArrayType,
1141 length_array: &PrimitiveArray<Int64Type>,
1142 capacity: usize,
1143) -> Result<ArrayRef> {
1144 let values = list_array.values();
1145 let mut take_indices_builder = PrimitiveArray::<Int64Type>::builder(capacity);
1146 for row in 0..list_array.len() {
1147 let mut value_length = 0;
1148 if !list_array.is_null(row) {
1149 let (start, end) = list_array.value_offsets(row);
1150 value_length = end - start;
1151 for i in start..end {
1152 take_indices_builder.append_value(i)
1153 }
1154 }
1155 let target_length = length_array.value(row);
1156 debug_assert!(
1157 value_length <= target_length,
1158 "value length is beyond the longest length"
1159 );
1160 for _ in value_length..target_length {
1162 take_indices_builder.append_null();
1163 }
1164 }
1165 Ok(kernels::take::take(
1166 &values,
1167 &take_indices_builder.finish(),
1168 None,
1169 )?)
1170}
1171
1172fn create_take_indices(
1187 length_array: &PrimitiveArray<Int64Type>,
1188 capacity: usize,
1189) -> PrimitiveArray<Int64Type> {
1190 debug_assert!(
1192 length_array.null_count() == 0,
1193 "length array should not contain nulls"
1194 );
1195 let mut builder = PrimitiveArray::<Int64Type>::builder(capacity);
1196 for (index, repeat) in length_array.iter().enumerate() {
1197 let repeat = repeat.unwrap();
1199 (0..repeat).for_each(|_| builder.append_value(index as i64));
1200 }
1201 builder.finish()
1202}
1203
1204fn repeat_arrs_from_indices(
1251 batch: &[ArrayRef],
1252 indices: &PrimitiveArray<Int64Type>,
1253 repeat_mask: &[bool],
1254) -> Result<Vec<Arc<dyn Array>>> {
1255 batch
1256 .iter()
1257 .zip(repeat_mask.iter())
1258 .map(|(arr, &repeat)| {
1259 if repeat {
1260 Ok(kernels::take::take(arr, indices, None)?)
1261 } else {
1262 Ok(new_null_array(arr.data_type(), arr.len()))
1263 }
1264 })
1265 .collect()
1266}
1267
1268#[cfg(test)]
1269mod tests {
1270 use super::*;
1271 use arrow::array::{
1272 GenericListArray, NullBufferBuilder, OffsetSizeTrait, StringArray,
1273 };
1274 use arrow::buffer::{NullBuffer, OffsetBuffer};
1275 use arrow::datatypes::{Field, Int32Type};
1276 use datafusion_common::NullHandling;
1277 use datafusion_common::test_util::batches_to_string;
1278 use insta::assert_snapshot;
1279
1280 fn make_generic_array<OffsetSize>() -> GenericListArray<OffsetSize>
1283 where
1284 OffsetSize: OffsetSizeTrait,
1285 {
1286 let mut values = vec![];
1287 let mut offsets: Vec<OffsetSize> = vec![OffsetSize::zero()];
1288 let mut valid = NullBufferBuilder::new(6);
1289
1290 values.extend_from_slice(&[Some("A"), Some("B"), Some("C")]);
1292 offsets.push(OffsetSize::from_usize(values.len()).unwrap());
1293 valid.append_non_null();
1294
1295 offsets.push(OffsetSize::from_usize(values.len()).unwrap());
1297 valid.append_non_null();
1298
1299 values.push(Some("?"));
1302 offsets.push(OffsetSize::from_usize(values.len()).unwrap());
1303 valid.append_null();
1304
1305 values.push(Some("D"));
1307 offsets.push(OffsetSize::from_usize(values.len()).unwrap());
1308 valid.append_non_null();
1309
1310 offsets.push(OffsetSize::from_usize(values.len()).unwrap());
1312 valid.append_null();
1313
1314 values.extend_from_slice(&[None, Some("F")]);
1316 offsets.push(OffsetSize::from_usize(values.len()).unwrap());
1317 valid.append_non_null();
1318
1319 let field = Arc::new(Field::new_list_field(DataType::Utf8, true));
1320 GenericListArray::<OffsetSize>::new(
1321 field,
1322 OffsetBuffer::new(offsets.into()),
1323 Arc::new(StringArray::from(values)),
1324 valid.finish(),
1325 )
1326 }
1327
1328 fn make_fixed_list() -> FixedSizeListArray {
1331 let values = Arc::new(StringArray::from_iter([
1332 Some("A"),
1333 Some("B"),
1334 None,
1335 None,
1336 Some("C"),
1337 Some("D"),
1338 None,
1339 None,
1340 None,
1341 Some("F"),
1342 None,
1343 None,
1344 ]));
1345 let field = Arc::new(Field::new_list_field(DataType::Utf8, true));
1346 let valid = NullBuffer::from(vec![true, false, true, false, true, true]);
1347 FixedSizeListArray::new(field, 2, values, Some(valid))
1348 }
1349
1350 fn verify_unnest_list_array(
1351 list_array: &dyn ListArrayType,
1352 lengths: Vec<i64>,
1353 expected: Vec<Option<&str>>,
1354 ) -> Result<()> {
1355 let length_array = Int64Array::from(lengths);
1356 let unnested_array = unnest_list_array(list_array, &length_array, 3 * 6)?;
1357 let strs = unnested_array.as_string::<i32>().iter().collect::<Vec<_>>();
1358 assert_eq!(strs, expected);
1359 Ok(())
1360 }
1361
1362 #[test]
1363 fn test_build_batch_list_arr_recursive() -> Result<()> {
1364 let list_arr1 = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
1369 Some(vec![Some(1), Some(2), Some(3)]),
1370 None,
1371 Some(vec![Some(4), Some(5)]),
1372 Some(vec![Some(7), Some(8), Some(9), Some(10)]),
1373 None,
1374 Some(vec![Some(11), Some(12), Some(13)]),
1375 ]);
1376
1377 let list_arr1_ref = Arc::new(list_arr1) as ArrayRef;
1378 let offsets = OffsetBuffer::from_lengths([3, 3, 0]);
1379 let mut nulls = NullBufferBuilder::new(3);
1380 nulls.append_non_null();
1381 nulls.append_non_null();
1382 nulls.append_null();
1383 let col1_field = Field::new_list_field(
1385 DataType::List(Arc::new(Field::new_list_field(
1386 list_arr1_ref.data_type().to_owned(),
1387 true,
1388 ))),
1389 true,
1390 );
1391 let col1 = ListArray::new(
1392 Arc::new(Field::new_list_field(
1393 list_arr1_ref.data_type().to_owned(),
1394 true,
1395 )),
1396 offsets,
1397 list_arr1_ref,
1398 nulls.finish(),
1399 );
1400
1401 let list_arr2 = StringArray::from(vec![
1402 Some("a"),
1403 Some("b"),
1404 Some("c"),
1405 Some("d"),
1406 Some("e"),
1407 ]);
1408
1409 let offsets = OffsetBuffer::from_lengths([2, 2, 1]);
1410 let mut nulls = NullBufferBuilder::new(3);
1411 nulls.append_n_non_nulls(3);
1412 let col2_field = Field::new(
1413 "col2",
1414 DataType::List(Arc::new(Field::new_list_field(DataType::Utf8, true))),
1415 true,
1416 );
1417 let col2 = GenericListArray::<i32>::new(
1418 Arc::new(Field::new_list_field(DataType::Utf8, true)),
1419 OffsetBuffer::new(offsets.into()),
1420 Arc::new(list_arr2),
1421 nulls.finish(),
1422 );
1423 let schema = Arc::new(Schema::new(vec![col1_field, col2_field]));
1425 let out_schema = Arc::new(Schema::new(vec![
1426 Field::new(
1427 "col1_unnest_placeholder_depth_1",
1428 DataType::List(Arc::new(Field::new_list_field(DataType::Int32, true))),
1429 true,
1430 ),
1431 Field::new("col1_unnest_placeholder_depth_2", DataType::Int32, true),
1432 Field::new("col2_unnest_placeholder_depth_1", DataType::Utf8, true),
1433 ]));
1434 let batch = RecordBatch::try_new(
1435 Arc::clone(&schema),
1436 vec![Arc::new(col1) as ArrayRef, Arc::new(col2) as ArrayRef],
1437 )
1438 .unwrap();
1439 let list_type_columns = vec![
1440 ListUnnest {
1441 index_in_input_schema: 0,
1442 depth: 1,
1443 },
1444 ListUnnest {
1445 index_in_input_schema: 0,
1446 depth: 2,
1447 },
1448 ListUnnest {
1449 index_in_input_schema: 1,
1450 depth: 1,
1451 },
1452 ];
1453 let ret = build_batch(
1454 &batch,
1455 &out_schema,
1456 list_type_columns.as_ref(),
1457 &HashSet::default(),
1458 &UnnestOptions {
1459 null_handling: NullHandling::Preserve,
1460 recursions: vec![],
1461 },
1462 )?
1463 .unwrap();
1464
1465 assert_snapshot!(batches_to_string(&[ret]),
1466 @r"
1467 +---------------------------------+---------------------------------+---------------------------------+
1468 | col1_unnest_placeholder_depth_1 | col1_unnest_placeholder_depth_2 | col2_unnest_placeholder_depth_1 |
1469 +---------------------------------+---------------------------------+---------------------------------+
1470 | [1, 2, 3] | 1 | a |
1471 | | 2 | b |
1472 | [4, 5] | 3 | |
1473 | [1, 2, 3] | | a |
1474 | | | b |
1475 | [4, 5] | | |
1476 | [1, 2, 3] | 4 | a |
1477 | | 5 | b |
1478 | [4, 5] | | |
1479 | [7, 8, 9, 10] | 7 | c |
1480 | | 8 | d |
1481 | [11, 12, 13] | 9 | |
1482 | | 10 | |
1483 | [7, 8, 9, 10] | | c |
1484 | | | d |
1485 | [11, 12, 13] | | |
1486 | [7, 8, 9, 10] | 11 | c |
1487 | | 12 | d |
1488 | [11, 12, 13] | 13 | |
1489 | | | e |
1490 +---------------------------------+---------------------------------+---------------------------------+
1491 ");
1492 Ok(())
1493 }
1494
1495 #[test]
1496 fn test_build_batch_preserve_and_expand_empty() -> Result<()> {
1497 let list_array = Arc::new(make_generic_array::<i32>()) as ArrayRef;
1506 let other =
1507 Arc::new(arrow::array::Int32Array::from(vec![1, 2, 3, 4, 5, 6])) as ArrayRef;
1508 let in_schema = Arc::new(Schema::new(vec![
1509 Field::new(
1510 "c1",
1511 DataType::List(Arc::new(Field::new_list_field(DataType::Utf8, true))),
1512 true,
1513 ),
1514 Field::new("c2", DataType::Int32, true),
1515 ]));
1516 let out_schema = Arc::new(Schema::new(vec![
1517 Field::new("c1_unnested", DataType::Utf8, true),
1518 Field::new("c2", DataType::Int32, true),
1519 ]));
1520 let batch = RecordBatch::try_new(
1521 Arc::clone(&in_schema),
1522 vec![Arc::clone(&list_array), Arc::clone(&other)],
1523 )?;
1524 let list_type_columns = vec![ListUnnest {
1525 index_in_input_schema: 0,
1526 depth: 1,
1527 }];
1528
1529 let ret = build_batch(
1530 &batch,
1531 &out_schema,
1532 &list_type_columns,
1533 &HashSet::default(),
1534 &UnnestOptions {
1535 null_handling: NullHandling::PreserveAndExpandEmpty,
1536 recursions: vec![],
1537 },
1538 )?
1539 .unwrap();
1540
1541 assert_snapshot!(batches_to_string(&[ret]),
1542 @r"
1543 +-------------+----+
1544 | c1_unnested | c2 |
1545 +-------------+----+
1546 | A | 1 |
1547 | B | 1 |
1548 | C | 1 |
1549 | | 2 |
1550 | | 3 |
1551 | D | 4 |
1552 | | 5 |
1553 | | 6 |
1554 | F | 6 |
1555 +-------------+----+
1556 ");
1557 Ok(())
1558 }
1559
1560 #[test]
1563 fn test_build_batch_preserve_and_expand_empty_largelist() -> Result<()> {
1564 let list_array = Arc::new(make_generic_array::<i64>()) as ArrayRef;
1565 let other =
1566 Arc::new(arrow::array::Int32Array::from(vec![1, 2, 3, 4, 5, 6])) as ArrayRef;
1567 let in_schema = Arc::new(Schema::new(vec![
1568 Field::new(
1569 "c1",
1570 DataType::LargeList(Arc::new(Field::new_list_field(
1571 DataType::Utf8,
1572 true,
1573 ))),
1574 true,
1575 ),
1576 Field::new("c2", DataType::Int32, true),
1577 ]));
1578 let out_schema = Arc::new(Schema::new(vec![
1579 Field::new("c1_unnested", DataType::Utf8, true),
1580 Field::new("c2", DataType::Int32, true),
1581 ]));
1582 let batch = RecordBatch::try_new(
1583 Arc::clone(&in_schema),
1584 vec![Arc::clone(&list_array), Arc::clone(&other)],
1585 )?;
1586 let list_type_columns = vec![ListUnnest {
1587 index_in_input_schema: 0,
1588 depth: 1,
1589 }];
1590
1591 let ret = build_batch(
1592 &batch,
1593 &out_schema,
1594 &list_type_columns,
1595 &HashSet::default(),
1596 &UnnestOptions {
1597 null_handling: NullHandling::PreserveAndExpandEmpty,
1598 recursions: vec![],
1599 },
1600 )?
1601 .unwrap();
1602
1603 assert_snapshot!(batches_to_string(&[ret]),
1606 @r"
1607 +-------------+----+
1608 | c1_unnested | c2 |
1609 +-------------+----+
1610 | A | 1 |
1611 | B | 1 |
1612 | C | 1 |
1613 | | 2 |
1614 | | 3 |
1615 | D | 4 |
1616 | | 5 |
1617 | | 6 |
1618 | F | 6 |
1619 +-------------+----+
1620 ");
1621 Ok(())
1622 }
1623
1624 #[test]
1629 fn test_build_batch_preserve_and_expand_empty_multi_column() -> Result<()> {
1630 let col_a = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
1633 Some(vec![Some(1), Some(2)]),
1634 Some(vec![]),
1635 None,
1636 Some(vec![Some(3)]),
1637 ]);
1638 let col_b = {
1639 let mut b =
1640 arrow::array::ListBuilder::new(arrow::array::StringBuilder::new());
1641 b.values().append_value("x");
1642 b.append(true);
1643 b.values().append_value("y");
1644 b.append(true);
1645 b.values().append_value("z");
1646 b.append(true);
1647 b.append(false);
1648 b.finish()
1649 };
1650 let id =
1651 Arc::new(arrow::array::Int32Array::from(vec![10, 20, 30, 40])) as ArrayRef;
1652
1653 let in_schema = Arc::new(Schema::new(vec![
1654 Field::new(
1655 "a",
1656 DataType::List(Arc::new(Field::new_list_field(DataType::Int32, true))),
1657 true,
1658 ),
1659 Field::new(
1660 "b",
1661 DataType::List(Arc::new(Field::new_list_field(DataType::Utf8, true))),
1662 true,
1663 ),
1664 Field::new("id", DataType::Int32, true),
1665 ]));
1666 let out_schema = Arc::new(Schema::new(vec![
1667 Field::new("a_unnested", DataType::Int32, true),
1668 Field::new("b_unnested", DataType::Utf8, true),
1669 Field::new("id", DataType::Int32, true),
1670 ]));
1671 let batch = RecordBatch::try_new(
1672 Arc::clone(&in_schema),
1673 vec![
1674 Arc::new(col_a) as ArrayRef,
1675 Arc::new(col_b) as ArrayRef,
1676 Arc::clone(&id),
1677 ],
1678 )?;
1679 let list_type_columns = vec![
1680 ListUnnest {
1681 index_in_input_schema: 0,
1682 depth: 1,
1683 },
1684 ListUnnest {
1685 index_in_input_schema: 1,
1686 depth: 1,
1687 },
1688 ];
1689
1690 let ret = build_batch(
1691 &batch,
1692 &out_schema,
1693 &list_type_columns,
1694 &HashSet::default(),
1695 &UnnestOptions {
1696 null_handling: NullHandling::PreserveAndExpandEmpty,
1697 recursions: vec![],
1698 },
1699 )?
1700 .unwrap();
1701
1702 assert_snapshot!(batches_to_string(&[ret]),
1707 @r"
1708 +------------+------------+----+
1709 | a_unnested | b_unnested | id |
1710 +------------+------------+----+
1711 | 1 | x | 10 |
1712 | 2 | | 10 |
1713 | | y | 20 |
1714 | | z | 30 |
1715 | 3 | | 40 |
1716 +------------+------------+----+
1717 ");
1718 Ok(())
1719 }
1720
1721 #[test]
1725 fn test_build_batch_preserve_and_expand_empty_recursive() -> Result<()> {
1726 let list_arr1 = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
1731 Some(vec![Some(1), Some(2), Some(3)]),
1732 None,
1733 Some(vec![Some(4), Some(5)]),
1734 Some(vec![Some(7), Some(8), Some(9), Some(10)]),
1735 None,
1736 Some(vec![Some(11), Some(12), Some(13)]),
1737 ]);
1738 let list_arr1_ref = Arc::new(list_arr1) as ArrayRef;
1739 let offsets = OffsetBuffer::from_lengths([3, 3, 0]);
1740 let mut nulls = NullBufferBuilder::new(3);
1741 nulls.append_non_null();
1742 nulls.append_non_null();
1743 nulls.append_null();
1744 let col1_field = Field::new_list_field(
1745 DataType::List(Arc::new(Field::new_list_field(
1746 list_arr1_ref.data_type().to_owned(),
1747 true,
1748 ))),
1749 true,
1750 );
1751 let col1 = ListArray::new(
1752 Arc::new(Field::new_list_field(
1753 list_arr1_ref.data_type().to_owned(),
1754 true,
1755 )),
1756 offsets,
1757 list_arr1_ref,
1758 nulls.finish(),
1759 );
1760
1761 let list_arr2 = StringArray::from(vec![
1762 Some("a"),
1763 Some("b"),
1764 Some("c"),
1765 Some("d"),
1766 Some("e"),
1767 ]);
1768 let offsets = OffsetBuffer::from_lengths([2, 2, 1]);
1769 let mut nulls = NullBufferBuilder::new(3);
1770 nulls.append_n_non_nulls(3);
1771 let col2_field = Field::new(
1772 "col2",
1773 DataType::List(Arc::new(Field::new_list_field(DataType::Utf8, true))),
1774 true,
1775 );
1776 let col2 = GenericListArray::<i32>::new(
1777 Arc::new(Field::new_list_field(DataType::Utf8, true)),
1778 OffsetBuffer::new(offsets.into()),
1779 Arc::new(list_arr2),
1780 nulls.finish(),
1781 );
1782 let schema = Arc::new(Schema::new(vec![col1_field, col2_field]));
1783 let out_schema = Arc::new(Schema::new(vec![
1784 Field::new(
1785 "col1_unnest_placeholder_depth_1",
1786 DataType::List(Arc::new(Field::new_list_field(DataType::Int32, true))),
1787 true,
1788 ),
1789 Field::new("col1_unnest_placeholder_depth_2", DataType::Int32, true),
1790 Field::new("col2_unnest_placeholder_depth_1", DataType::Utf8, true),
1791 ]));
1792 let batch = RecordBatch::try_new(
1793 Arc::clone(&schema),
1794 vec![Arc::new(col1) as ArrayRef, Arc::new(col2) as ArrayRef],
1795 )?;
1796 let list_type_columns = vec![
1797 ListUnnest {
1798 index_in_input_schema: 0,
1799 depth: 1,
1800 },
1801 ListUnnest {
1802 index_in_input_schema: 0,
1803 depth: 2,
1804 },
1805 ListUnnest {
1806 index_in_input_schema: 1,
1807 depth: 1,
1808 },
1809 ];
1810
1811 let ret = build_batch(
1812 &batch,
1813 &out_schema,
1814 &list_type_columns,
1815 &HashSet::default(),
1816 &UnnestOptions {
1817 null_handling: NullHandling::PreserveAndExpandEmpty,
1818 recursions: vec![],
1819 },
1820 )?
1821 .unwrap();
1822
1823 assert_snapshot!(batches_to_string(&[ret]),
1829 @r"
1830 +---------------------------------+---------------------------------+---------------------------------+
1831 | col1_unnest_placeholder_depth_1 | col1_unnest_placeholder_depth_2 | col2_unnest_placeholder_depth_1 |
1832 +---------------------------------+---------------------------------+---------------------------------+
1833 | [1, 2, 3] | 1 | a |
1834 | | 2 | b |
1835 | [4, 5] | 3 | |
1836 | [1, 2, 3] | | a |
1837 | | | b |
1838 | [4, 5] | | |
1839 | [1, 2, 3] | 4 | a |
1840 | | 5 | b |
1841 | [4, 5] | | |
1842 | [7, 8, 9, 10] | 7 | c |
1843 | | 8 | d |
1844 | [11, 12, 13] | 9 | |
1845 | | 10 | |
1846 | [7, 8, 9, 10] | | c |
1847 | | | d |
1848 | [11, 12, 13] | | |
1849 | [7, 8, 9, 10] | 11 | c |
1850 | | 12 | d |
1851 | [11, 12, 13] | 13 | |
1852 | | | e |
1853 +---------------------------------+---------------------------------+---------------------------------+
1854 ");
1855 Ok(())
1856 }
1857
1858 #[test]
1859 fn test_unnest_list_array() -> Result<()> {
1860 let list_array = make_generic_array::<i32>();
1862 verify_unnest_list_array(
1863 &list_array,
1864 vec![3, 2, 1, 2, 0, 3],
1865 vec![
1866 Some("A"),
1867 Some("B"),
1868 Some("C"),
1869 None,
1870 None,
1871 None,
1872 Some("D"),
1873 None,
1874 None,
1875 Some("F"),
1876 None,
1877 ],
1878 )?;
1879
1880 let list_array = make_fixed_list();
1882 verify_unnest_list_array(
1883 &list_array,
1884 vec![3, 1, 2, 0, 2, 3],
1885 vec![
1886 Some("A"),
1887 Some("B"),
1888 None,
1889 None,
1890 Some("C"),
1891 Some("D"),
1892 None,
1893 Some("F"),
1894 None,
1895 None,
1896 None,
1897 ],
1898 )?;
1899
1900 Ok(())
1901 }
1902
1903 fn verify_longest_length(
1904 list_arrays: &[ArrayRef],
1905 null_handling: NullHandling,
1906 expected: Vec<i64>,
1907 ) -> Result<()> {
1908 let options = UnnestOptions {
1909 null_handling,
1910 recursions: vec![],
1911 };
1912 let longest_length = find_longest_length(list_arrays, &options)?;
1913 let expected_array = Int64Array::from(expected);
1914 assert_eq!(
1915 longest_length
1916 .as_any()
1917 .downcast_ref::<Int64Array>()
1918 .unwrap(),
1919 &expected_array
1920 );
1921 Ok(())
1922 }
1923
1924 #[test]
1925 fn test_longest_list_length() -> Result<()> {
1926 let list_array = Arc::new(make_generic_array::<i32>()) as ArrayRef;
1929 verify_longest_length(
1930 &[Arc::clone(&list_array)],
1931 NullHandling::Drop,
1932 vec![3, 0, 0, 1, 0, 2],
1933 )?;
1934 verify_longest_length(
1935 &[Arc::clone(&list_array)],
1936 NullHandling::Preserve,
1937 vec![3, 0, 1, 1, 1, 2],
1938 )?;
1939 verify_longest_length(
1941 &[Arc::clone(&list_array)],
1942 NullHandling::PreserveAndExpandEmpty,
1943 vec![3, 1, 1, 1, 1, 2],
1944 )?;
1945
1946 let list_array = Arc::new(make_generic_array::<i64>()) as ArrayRef;
1949 verify_longest_length(
1950 &[Arc::clone(&list_array)],
1951 NullHandling::Drop,
1952 vec![3, 0, 0, 1, 0, 2],
1953 )?;
1954 verify_longest_length(
1955 &[Arc::clone(&list_array)],
1956 NullHandling::Preserve,
1957 vec![3, 0, 1, 1, 1, 2],
1958 )?;
1959 verify_longest_length(
1960 &[Arc::clone(&list_array)],
1961 NullHandling::PreserveAndExpandEmpty,
1962 vec![3, 1, 1, 1, 1, 2],
1963 )?;
1964
1965 let list_array = Arc::new(make_fixed_list()) as ArrayRef;
1968 verify_longest_length(
1969 &[Arc::clone(&list_array)],
1970 NullHandling::Drop,
1971 vec![2, 0, 2, 0, 2, 2],
1972 )?;
1973 verify_longest_length(
1974 &[Arc::clone(&list_array)],
1975 NullHandling::Preserve,
1976 vec![2, 1, 2, 1, 2, 2],
1977 )?;
1978
1979 let list1 = Arc::new(make_generic_array::<i32>()) as ArrayRef;
1983 let list2 = Arc::new(make_fixed_list()) as ArrayRef;
1984 let list_arrays = vec![Arc::clone(&list1), Arc::clone(&list2)];
1985 verify_longest_length(&list_arrays, NullHandling::Drop, vec![3, 0, 2, 1, 2, 2])?;
1986 verify_longest_length(
1987 &list_arrays,
1988 NullHandling::Preserve,
1989 vec![3, 1, 2, 1, 2, 2],
1990 )?;
1991 verify_longest_length(
1992 &list_arrays,
1993 NullHandling::PreserveAndExpandEmpty,
1994 vec![3, 1, 2, 1, 2, 2],
1995 )?;
1996
1997 Ok(())
1998 }
1999
2000 #[test]
2001 fn test_create_take_indices() -> Result<()> {
2002 let length_array = Int64Array::from(vec![2, 3, 1]);
2003 let take_indices = create_take_indices(&length_array, 6);
2004 let expected = Int64Array::from(vec![0, 0, 1, 1, 1, 2]);
2005 assert_eq!(take_indices, expected);
2006 Ok(())
2007 }
2008}