1use std::{collections::VecDeque, fmt::Debug, pin::Pin, sync::Arc, task::Poll};
19
20use crate::{FlightData, FlightDescriptor, SchemaAsIpc, error::Result};
21
22use arrow_array::{Array, ArrayRef, RecordBatch, RecordBatchOptions, UnionArray};
23use arrow_ipc::writer::{DictionaryTracker, IpcDataGenerator, IpcWriteContext, IpcWriteOptions};
24
25use arrow_schema::{DataType, Field, FieldRef, Fields, Schema, SchemaRef, UnionMode};
26use bytes::Bytes;
27use futures::{Stream, StreamExt, ready, stream::BoxStream};
28
29#[derive(Debug)]
145pub struct FlightDataEncoderBuilder {
146 max_flight_data_size: usize,
149 options: IpcWriteOptions,
151 app_metadata: Bytes,
153 schema: Option<SchemaRef>,
155 descriptor: Option<FlightDescriptor>,
157 dictionary_handling: DictionaryHandling,
160}
161
162pub const GRPC_TARGET_MAX_FLIGHT_SIZE_BYTES: usize = 2097152;
167
168impl Default for FlightDataEncoderBuilder {
169 fn default() -> Self {
170 Self {
171 max_flight_data_size: GRPC_TARGET_MAX_FLIGHT_SIZE_BYTES,
172 options: IpcWriteOptions::default(),
173 app_metadata: Bytes::new(),
174 schema: None,
175 descriptor: None,
176 dictionary_handling: DictionaryHandling::Hydrate,
177 }
178 }
179}
180
181impl FlightDataEncoderBuilder {
182 pub fn new() -> Self {
184 Self::default()
185 }
186
187 pub fn with_max_flight_data_size(mut self, max_flight_data_size: usize) -> Self {
198 self.max_flight_data_size = max_flight_data_size;
199 self
200 }
201
202 pub fn with_dictionary_handling(mut self, dictionary_handling: DictionaryHandling) -> Self {
204 self.dictionary_handling = dictionary_handling;
205 self
206 }
207
208 pub fn with_metadata(mut self, app_metadata: Bytes) -> Self {
212 self.app_metadata = app_metadata;
213 self
214 }
215
216 pub fn with_options(mut self, options: IpcWriteOptions) -> Self {
218 self.options = options;
219 self
220 }
221
222 pub fn with_schema(mut self, schema: SchemaRef) -> Self {
227 self.schema = Some(schema);
228 self
229 }
230
231 pub fn with_flight_descriptor(mut self, descriptor: Option<FlightDescriptor>) -> Self {
233 self.descriptor = descriptor;
234 self
235 }
236
237 pub fn build<S>(self, input: S) -> FlightDataEncoder
242 where
243 S: Stream<Item = Result<RecordBatch>> + Send + 'static,
244 {
245 let Self {
246 max_flight_data_size,
247 options,
248 app_metadata,
249 schema,
250 descriptor,
251 dictionary_handling,
252 } = self;
253
254 FlightDataEncoder::new(
255 input.boxed(),
256 schema,
257 max_flight_data_size,
258 options,
259 app_metadata,
260 descriptor,
261 dictionary_handling,
262 )
263 }
264}
265
266pub struct FlightDataEncoder {
270 inner: BoxStream<'static, Result<RecordBatch>>,
272 schema: Option<SchemaRef>,
274 max_flight_data_size: usize,
277 encoder: FlightIpcEncoder,
279 app_metadata: Option<Bytes>,
281 queue: VecDeque<FlightData>,
283 done: bool,
285 descriptor: Option<FlightDescriptor>,
287 dictionary_handling: DictionaryHandling,
290}
291
292impl FlightDataEncoder {
293 fn new(
294 inner: BoxStream<'static, Result<RecordBatch>>,
295 schema: Option<SchemaRef>,
296 max_flight_data_size: usize,
297 options: IpcWriteOptions,
298 app_metadata: Bytes,
299 descriptor: Option<FlightDescriptor>,
300 dictionary_handling: DictionaryHandling,
301 ) -> Self {
302 let mut encoder = Self {
303 inner,
304 schema: None,
305 max_flight_data_size,
306 encoder: FlightIpcEncoder::new(
307 options,
308 dictionary_handling != DictionaryHandling::Resend,
309 ),
310 app_metadata: Some(app_metadata),
311 queue: VecDeque::new(),
312 done: false,
313 descriptor,
314 dictionary_handling,
315 };
316
317 if let Some(schema) = schema {
319 encoder.encode_schema(&schema);
320 }
321
322 encoder
323 }
324
325 pub fn known_schema(&self) -> Option<SchemaRef> {
328 self.schema.clone()
329 }
330
331 #[inline]
333 fn queue_message(&mut self, mut data: FlightData) {
334 if let Some(descriptor) = self.descriptor.take() {
335 data.flight_descriptor = Some(descriptor);
336 }
337 self.queue.push_back(data);
338 }
339
340 fn encode_schema(&mut self, schema: &SchemaRef) -> SchemaRef {
343 let send_dictionaries = self.dictionary_handling == DictionaryHandling::Resend;
346 let schema = Arc::new(prepare_schema_for_flight(
347 schema,
348 &mut self.encoder.dictionary_tracker,
349 send_dictionaries,
350 ));
351 let mut schema_flight_data = self.encoder.encode_schema(&schema);
352
353 if let Some(app_metadata) = self.app_metadata.take() {
355 schema_flight_data.app_metadata = app_metadata;
356 }
357 self.queue_message(schema_flight_data);
358 self.schema = Some(schema.clone());
360 schema
361 }
362
363 fn encode_batch(&mut self, batch: RecordBatch) -> Result<()> {
365 let schema = match &self.schema {
366 Some(schema) => schema.clone(),
367 None => self.encode_schema(batch.schema_ref()),
369 };
370
371 let batch = match self.dictionary_handling {
372 DictionaryHandling::Resend => batch,
373 DictionaryHandling::Hydrate => hydrate_dictionaries(&batch, schema)?,
374 };
375
376 for batch in split_batch_for_grpc_response(batch, self.max_flight_data_size) {
377 let (flight_dictionaries, flight_batch) = self.encoder.encode_batch(&batch)?;
378 for dict in flight_dictionaries {
379 self.queue_message(dict);
380 }
381 self.queue_message(flight_batch);
382 }
383
384 Ok(())
385 }
386}
387
388impl Stream for FlightDataEncoder {
389 type Item = Result<FlightData>;
390
391 fn poll_next(
392 mut self: Pin<&mut Self>,
393 cx: &mut std::task::Context<'_>,
394 ) -> Poll<Option<Self::Item>> {
395 loop {
396 if self.done && self.queue.is_empty() {
397 return Poll::Ready(None);
398 }
399
400 if let Some(data) = self.queue.pop_front() {
402 return Poll::Ready(Some(Ok(data)));
403 }
404
405 let batch = ready!(self.inner.poll_next_unpin(cx));
407
408 match batch {
409 None => {
410 self.done = true;
412 assert!(self.queue.is_empty());
414 return Poll::Ready(None);
415 }
416 Some(Err(e)) => {
417 self.done = true;
419 self.queue.clear();
420 return Poll::Ready(Some(Err(e)));
421 }
422 Some(Ok(batch)) => {
423 if let Err(e) = self.encode_batch(batch) {
425 self.done = true;
426 self.queue.clear();
427 return Poll::Ready(Some(Err(e)));
428 }
429 }
430 }
431 }
432 }
433}
434
435#[derive(Debug, PartialEq)]
464pub enum DictionaryHandling {
465 Hydrate,
473 Resend,
483}
484
485fn prepare_field_for_flight(
486 field: &FieldRef,
487 dictionary_tracker: &mut DictionaryTracker,
488 send_dictionaries: bool,
489) -> Field {
490 match field.data_type() {
491 DataType::List(inner) => Field::new_list(
492 field.name(),
493 prepare_field_for_flight(inner, dictionary_tracker, send_dictionaries),
494 field.is_nullable(),
495 )
496 .with_metadata(field.metadata().clone()),
497 DataType::LargeList(inner) => Field::new_list(
498 field.name(),
499 prepare_field_for_flight(inner, dictionary_tracker, send_dictionaries),
500 field.is_nullable(),
501 )
502 .with_metadata(field.metadata().clone()),
503 DataType::Struct(fields) => {
504 let new_fields: Vec<Field> = fields
505 .iter()
506 .map(|f| prepare_field_for_flight(f, dictionary_tracker, send_dictionaries))
507 .collect();
508 Field::new_struct(field.name(), new_fields, field.is_nullable())
509 .with_metadata(field.metadata().clone())
510 }
511 DataType::Union(fields, mode) => {
512 let (type_ids, new_fields): (Vec<i8>, Vec<Field>) = fields
513 .iter()
514 .map(|(type_id, f)| {
515 (
516 type_id,
517 prepare_field_for_flight(f, dictionary_tracker, send_dictionaries),
518 )
519 })
520 .unzip();
521
522 Field::new_union(field.name(), type_ids, new_fields, *mode)
523 }
524 DataType::Dictionary(_, value_type) => {
525 if !send_dictionaries {
526 let value_field = Field::new(
528 field.name(),
529 value_type.as_ref().clone(),
530 field.is_nullable(),
531 );
532 prepare_field_for_flight(
533 &Arc::new(value_field),
534 dictionary_tracker,
535 send_dictionaries,
536 )
537 .with_metadata(field.metadata().clone())
538 } else {
539 let value_field = Field::new("values", value_type.as_ref().clone(), true);
543 prepare_field_for_flight(
544 &Arc::new(value_field),
545 dictionary_tracker,
546 send_dictionaries,
547 );
548 dictionary_tracker.next_dict_id();
549 #[allow(deprecated)]
550 Field::new_dict(
551 field.name(),
552 field.data_type().clone(),
553 field.is_nullable(),
554 0,
555 field.dict_is_ordered().unwrap_or_default(),
556 )
557 .with_metadata(field.metadata().clone())
558 }
559 }
560 DataType::ListView(inner) | DataType::LargeListView(inner) => {
561 let prepared = prepare_field_for_flight(inner, dictionary_tracker, send_dictionaries);
562 Field::new(
563 field.name(),
564 match field.data_type() {
565 DataType::ListView(_) => DataType::ListView(Arc::new(prepared)),
566 _ => DataType::LargeListView(Arc::new(prepared)),
567 },
568 field.is_nullable(),
569 )
570 .with_metadata(field.metadata().clone())
571 }
572 DataType::FixedSizeList(inner, size) => Field::new(
573 field.name(),
574 DataType::FixedSizeList(
575 Arc::new(prepare_field_for_flight(
576 inner,
577 dictionary_tracker,
578 send_dictionaries,
579 )),
580 *size,
581 ),
582 field.is_nullable(),
583 )
584 .with_metadata(field.metadata().clone()),
585 DataType::RunEndEncoded(run_ends, values) => Field::new(
586 field.name(),
587 DataType::RunEndEncoded(
588 run_ends.clone(),
589 Arc::new(prepare_field_for_flight(
590 values,
591 dictionary_tracker,
592 send_dictionaries,
593 )),
594 ),
595 field.is_nullable(),
596 )
597 .with_metadata(field.metadata().clone()),
598 DataType::Map(inner, sorted) => Field::new(
599 field.name(),
600 DataType::Map(
601 prepare_field_for_flight(inner, dictionary_tracker, send_dictionaries).into(),
602 *sorted,
603 ),
604 field.is_nullable(),
605 )
606 .with_metadata(field.metadata().clone()),
607 DataType::Null
608 | DataType::Boolean
609 | DataType::Int8
610 | DataType::Int16
611 | DataType::Int32
612 | DataType::Int64
613 | DataType::UInt8
614 | DataType::UInt16
615 | DataType::UInt32
616 | DataType::UInt64
617 | DataType::Float16
618 | DataType::Float32
619 | DataType::Float64
620 | DataType::Timestamp(_, _)
621 | DataType::Date32
622 | DataType::Date64
623 | DataType::Time32(_)
624 | DataType::Time64(_)
625 | DataType::Duration(_)
626 | DataType::Interval(_)
627 | DataType::Binary
628 | DataType::FixedSizeBinary(_)
629 | DataType::LargeBinary
630 | DataType::BinaryView
631 | DataType::Utf8
632 | DataType::LargeUtf8
633 | DataType::Utf8View
634 | DataType::Decimal32(_, _)
635 | DataType::Decimal64(_, _)
636 | DataType::Decimal128(_, _)
637 | DataType::Decimal256(_, _) => field.as_ref().clone(),
638 }
639}
640
641fn prepare_schema_for_flight(
647 schema: &Schema,
648 dictionary_tracker: &mut DictionaryTracker,
649 send_dictionaries: bool,
650) -> Schema {
651 let fields: Fields = schema
652 .fields()
653 .iter()
654 .map(|field| prepare_field_for_flight(field, dictionary_tracker, send_dictionaries))
655 .collect();
656
657 Schema::new(fields).with_metadata(schema.metadata().clone())
658}
659
660fn split_batch_for_grpc_response(
667 batch: RecordBatch,
668 max_flight_data_size: usize,
669) -> impl Iterator<Item = RecordBatch> {
670 let size = batch
671 .columns()
672 .iter()
673 .map(|col| col.get_buffer_memory_size())
674 .sum::<usize>();
675
676 let n_batches =
677 (size / max_flight_data_size + usize::from(size % max_flight_data_size != 0)).max(1);
678 let num_rows = batch.num_rows();
679 let rows_per_batch = (num_rows / n_batches).max(1);
680 let mut offset = 0;
681
682 std::iter::from_fn(move || {
683 if offset < num_rows {
684 let length = rows_per_batch.min(num_rows - offset);
685 let slice = batch.slice(offset, length);
686 offset += length;
687 Some(slice)
688 } else {
689 None
690 }
691 })
692}
693
694struct FlightIpcEncoder {
701 options: IpcWriteOptions,
702 data_gen: IpcDataGenerator,
703 dictionary_tracker: DictionaryTracker,
704 ipc_write_context: IpcWriteContext,
705}
706
707impl FlightIpcEncoder {
708 fn new(options: IpcWriteOptions, error_on_replacement: bool) -> Self {
709 Self {
710 options,
711 data_gen: IpcDataGenerator::default(),
712 dictionary_tracker: DictionaryTracker::new(error_on_replacement),
713 ipc_write_context: IpcWriteContext::default(),
714 }
715 }
716
717 fn encode_schema(&self, schema: &Schema) -> FlightData {
719 SchemaAsIpc::new(schema, &self.options).into()
720 }
721
722 fn encode_batch(
725 &mut self,
726 batch: &RecordBatch,
727 ) -> Result<(impl Iterator<Item = FlightData> + use<>, FlightData)> {
728 let (encoded_dictionaries, encoded_batch) = self.data_gen.encode(
729 batch,
730 &mut self.dictionary_tracker,
731 &self.options,
732 &mut self.ipc_write_context,
733 )?;
734
735 let flight_dictionaries = encoded_dictionaries.into_iter().map(|e| e.into());
736 let flight_batch = encoded_batch.into();
737
738 Ok((flight_dictionaries, flight_batch))
739 }
740}
741
742fn hydrate_dictionaries(batch: &RecordBatch, schema: SchemaRef) -> Result<RecordBatch> {
745 let columns = schema
746 .fields()
747 .iter()
748 .zip(batch.columns())
749 .map(|(field, c)| hydrate_dictionary(c, field.data_type()))
750 .collect::<Result<Vec<_>>>()?;
751
752 let options = RecordBatchOptions::new().with_row_count(Some(batch.num_rows()));
753
754 Ok(RecordBatch::try_new_with_options(
755 schema, columns, &options,
756 )?)
757}
758
759fn hydrate_dictionary(array: &ArrayRef, data_type: &DataType) -> Result<ArrayRef> {
761 let arr = match (array.data_type(), data_type) {
762 (DataType::Union(_, UnionMode::Sparse), DataType::Union(fields, UnionMode::Sparse)) => {
763 let union_arr = array.as_any().downcast_ref::<UnionArray>().unwrap();
764
765 Arc::new(UnionArray::try_new(
766 fields.clone(),
767 union_arr.type_ids().clone(),
768 None,
769 fields
770 .iter()
771 .map(|(type_id, field)| {
772 Ok(arrow_cast::cast(
773 union_arr.child(type_id),
774 field.data_type(),
775 )?)
776 })
777 .collect::<Result<Vec<_>>>()?,
778 )?)
779 }
780 (_, data_type) => arrow_cast::cast(array, data_type)?,
781 };
782 Ok(arr)
783}
784
785#[cfg(test)]
786mod tests {
787 use crate::decode::{DecodedPayload, FlightDataDecoder};
788 use arrow_array::builder::{
789 FixedSizeListBuilder, GenericByteDictionaryBuilder, GenericListViewBuilder, ListBuilder,
790 StringDictionaryBuilder, StructBuilder,
791 };
792 use arrow_array::*;
793 use arrow_array::{cast::downcast_array, types::*};
794 use arrow_buffer::ScalarBuffer;
795 use arrow_cast::pretty::pretty_format_batches;
796 use arrow_ipc::{CompressionType, MetadataVersion};
797 use arrow_schema::{UnionFields, UnionMode};
798 use builder::MapBuilder;
799 use std::collections::HashMap;
800
801 use super::*;
802
803 #[test]
804 fn test_encode_flight_data() {
807 let options = IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap();
809 let c1 = UInt32Array::from(vec![1, 2, 3, 4, 5, 6]);
810
811 let batch = RecordBatch::try_from_iter(vec![("a", Arc::new(c1) as ArrayRef)])
812 .expect("cannot create record batch");
813 let schema = batch.schema_ref();
814
815 let (_, baseline_flight_batch) = make_flight_data(&batch, &options);
816
817 let big_batch = batch.slice(0, batch.num_rows() - 1);
818 let optimized_big_batch =
819 hydrate_dictionaries(&big_batch, Arc::clone(schema)).expect("failed to optimize");
820 let (_, optimized_big_flight_batch) = make_flight_data(&optimized_big_batch, &options);
821
822 assert_eq!(
823 baseline_flight_batch.data_body.len(),
824 optimized_big_flight_batch.data_body.len()
825 );
826
827 let small_batch = batch.slice(0, 1);
828 let optimized_small_batch =
829 hydrate_dictionaries(&small_batch, Arc::clone(schema)).expect("failed to optimize");
830 let (_, optimized_small_flight_batch) = make_flight_data(&optimized_small_batch, &options);
831
832 assert!(
833 baseline_flight_batch.data_body.len() > optimized_small_flight_batch.data_body.len()
834 );
835 }
836
837 #[tokio::test]
838 async fn test_dictionary_hydration() {
839 let arr1: DictionaryArray<UInt16Type> = vec!["a", "a", "b"].into_iter().collect();
840 let arr2: DictionaryArray<UInt16Type> = vec!["c", "c", "d"].into_iter().collect();
841
842 let schema = Arc::new(Schema::new(vec![Field::new_dictionary(
843 "dict",
844 DataType::UInt16,
845 DataType::Utf8,
846 false,
847 )]));
848 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
849 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
850
851 let stream = futures::stream::iter(vec![Ok(batch1), Ok(batch2)]);
852
853 let encoder = FlightDataEncoderBuilder::default().build(stream);
854 let mut decoder = FlightDataDecoder::new(encoder);
855 let expected_schema = Schema::new(vec![Field::new("dict", DataType::Utf8, false)]);
856 let expected_schema = Arc::new(expected_schema);
857 let mut expected_arrays = vec![
858 StringArray::from(vec!["a", "a", "b"]),
859 StringArray::from(vec!["c", "c", "d"]),
860 ]
861 .into_iter();
862 while let Some(decoded) = decoder.next().await {
863 let decoded = decoded.unwrap();
864 match decoded.payload {
865 DecodedPayload::None => {}
866 DecodedPayload::Schema(s) => assert_eq!(s, expected_schema),
867 DecodedPayload::RecordBatch(b) => {
868 assert_eq!(b.schema(), expected_schema);
869 let expected_array = expected_arrays.next().unwrap();
870 let actual_array = b.column_by_name("dict").unwrap();
871 let actual_array = downcast_array::<StringArray>(actual_array);
872
873 assert_eq!(actual_array, expected_array);
874 }
875 }
876 }
877 }
878
879 #[tokio::test]
880 async fn test_dictionary_resend() {
881 let arr1: DictionaryArray<UInt16Type> = vec!["a", "a", "b"].into_iter().collect();
882 let arr2: DictionaryArray<UInt16Type> = vec!["c", "c", "d"].into_iter().collect();
883
884 let schema = Arc::new(Schema::new(vec![Field::new_dictionary(
885 "dict",
886 DataType::UInt16,
887 DataType::Utf8,
888 false,
889 )]));
890 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
891 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
892
893 verify_flight_round_trip(vec![batch1, batch2]).await;
894 }
895
896 #[tokio::test]
897 async fn test_compression_round_trip() {
898 let ints = Int32Array::from_iter_values((0..1024).map(|i| i % 8));
902 let strings = StringArray::from_iter_values((0..1024).map(|i| format!("value-{}", i % 8)));
903 let batch = RecordBatch::try_from_iter(vec![
904 ("ints", Arc::new(ints) as ArrayRef),
905 ("strings", Arc::new(strings) as ArrayRef),
906 ])
907 .unwrap();
908
909 for compression in [CompressionType::LZ4_FRAME, CompressionType::ZSTD] {
910 let options = IpcWriteOptions::default()
911 .try_with_compression(Some(compression))
912 .unwrap();
913 verify_flight_round_trip_with_options(vec![batch.clone()], options).await;
914 }
915 }
916
917 #[tokio::test]
918 async fn test_dictionary_hydration_known_schema() {
919 let arr1: DictionaryArray<UInt16Type> = vec!["a", "a", "b"].into_iter().collect();
920 let arr2: DictionaryArray<UInt16Type> = vec!["c", "c", "d"].into_iter().collect();
921
922 let schema = Arc::new(Schema::new(vec![Field::new_dictionary(
923 "dict",
924 DataType::UInt16,
925 DataType::Utf8,
926 false,
927 )]));
928 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
929 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
930
931 let stream = futures::stream::iter(vec![Ok(batch1), Ok(batch2)]);
932
933 let encoder = FlightDataEncoderBuilder::default()
934 .with_schema(schema)
935 .build(stream);
936 let expected_schema =
937 Arc::new(Schema::new(vec![Field::new("dict", DataType::Utf8, false)]));
938 assert_eq!(Some(expected_schema), encoder.known_schema())
939 }
940
941 #[tokio::test]
942 async fn test_dictionary_resend_known_schema() {
943 let arr1: DictionaryArray<UInt16Type> = vec!["a", "a", "b"].into_iter().collect();
944 let arr2: DictionaryArray<UInt16Type> = vec!["c", "c", "d"].into_iter().collect();
945
946 let schema = Arc::new(Schema::new(vec![Field::new_dictionary(
947 "dict",
948 DataType::UInt16,
949 DataType::Utf8,
950 false,
951 )]));
952 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
953 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
954
955 let stream = futures::stream::iter(vec![Ok(batch1), Ok(batch2)]);
956
957 let encoder = FlightDataEncoderBuilder::default()
958 .with_dictionary_handling(DictionaryHandling::Resend)
959 .with_schema(schema.clone())
960 .build(stream);
961 assert_eq!(Some(schema), encoder.known_schema())
962 }
963
964 #[tokio::test]
965 async fn test_multiple_dictionaries_resend() {
966 let schema = Arc::new(Schema::new(vec![
968 Field::new_dictionary("dict_1", DataType::UInt16, DataType::Utf8, false),
969 Field::new_dictionary("dict_2", DataType::UInt16, DataType::Utf8, false),
970 ]));
971
972 let arr_one_1: Arc<DictionaryArray<UInt16Type>> =
973 Arc::new(vec!["a", "a", "b"].into_iter().collect());
974 let arr_one_2: Arc<DictionaryArray<UInt16Type>> =
975 Arc::new(vec!["c", "c", "d"].into_iter().collect());
976 let arr_two_1: Arc<DictionaryArray<UInt16Type>> =
977 Arc::new(vec!["b", "a", "c"].into_iter().collect());
978 let arr_two_2: Arc<DictionaryArray<UInt16Type>> =
979 Arc::new(vec!["k", "d", "e"].into_iter().collect());
980 let batch1 =
981 RecordBatch::try_new(schema.clone(), vec![arr_one_1.clone(), arr_one_2.clone()])
982 .unwrap();
983 let batch2 =
984 RecordBatch::try_new(schema.clone(), vec![arr_two_1.clone(), arr_two_2.clone()])
985 .unwrap();
986
987 verify_flight_round_trip(vec![batch1, batch2]).await;
988 }
989
990 #[tokio::test]
991 async fn test_dictionary_list_hydration() {
992 let mut builder = ListBuilder::new(StringDictionaryBuilder::<UInt16Type>::new());
993
994 builder.append_value(vec![Some("a"), None, Some("b")]);
995
996 let arr1 = builder.finish();
997
998 builder.append_value(vec![Some("c"), None, Some("d")]);
999
1000 let arr2 = builder.finish();
1001
1002 let schema = Arc::new(Schema::new(vec![Field::new_list(
1003 "dict_list",
1004 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1005 true,
1006 )]));
1007
1008 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1009 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
1010
1011 let stream = futures::stream::iter(vec![Ok(batch1), Ok(batch2)]);
1012
1013 let encoder = FlightDataEncoderBuilder::default().build(stream);
1014
1015 let mut decoder = FlightDataDecoder::new(encoder);
1016 let expected_schema = Schema::new(vec![Field::new_list(
1017 "dict_list",
1018 Field::new_list_field(DataType::Utf8, true),
1019 true,
1020 )]);
1021
1022 let expected_schema = Arc::new(expected_schema);
1023
1024 let mut expected_arrays = vec![
1025 StringArray::from_iter(vec![Some("a"), None, Some("b")]),
1026 StringArray::from_iter(vec![Some("c"), None, Some("d")]),
1027 ]
1028 .into_iter();
1029
1030 while let Some(decoded) = decoder.next().await {
1031 let decoded = decoded.unwrap();
1032 match decoded.payload {
1033 DecodedPayload::None => {}
1034 DecodedPayload::Schema(s) => assert_eq!(s, expected_schema),
1035 DecodedPayload::RecordBatch(b) => {
1036 assert_eq!(b.schema(), expected_schema);
1037 let expected_array = expected_arrays.next().unwrap();
1038 let list_array =
1039 downcast_array::<ListArray>(b.column_by_name("dict_list").unwrap());
1040 let elem_array = downcast_array::<StringArray>(list_array.value(0).as_ref());
1041
1042 assert_eq!(elem_array, expected_array);
1043 }
1044 }
1045 }
1046 }
1047
1048 #[tokio::test]
1049 async fn test_dictionary_list_resend() {
1050 let mut builder = ListBuilder::new(StringDictionaryBuilder::<UInt16Type>::new());
1051
1052 builder.append_value(vec![Some("a"), None, Some("b")]);
1053
1054 let arr1 = builder.finish();
1055
1056 builder.append_value(vec![Some("c"), None, Some("d")]);
1057
1058 let arr2 = builder.finish();
1059
1060 let schema = Arc::new(Schema::new(vec![Field::new_list(
1061 "dict_list",
1062 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1063 true,
1064 )]));
1065
1066 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1067 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
1068
1069 verify_flight_round_trip(vec![batch1, batch2]).await;
1070 }
1071
1072 #[tokio::test]
1073 async fn test_dictionary_struct_hydration() {
1074 let struct_fields = vec![Field::new_list(
1075 "dict_list",
1076 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1077 true,
1078 )];
1079
1080 let mut struct_builder = StructBuilder::new(
1081 struct_fields.clone(),
1082 vec![Box::new(builder::ListBuilder::new(
1083 StringDictionaryBuilder::<UInt16Type>::new(),
1084 ))],
1085 );
1086
1087 struct_builder
1088 .field_builder::<ListBuilder<GenericByteDictionaryBuilder<UInt16Type,GenericStringType<i32>>>>(0)
1089 .unwrap()
1090 .append_value(vec![Some("a"), None, Some("b")]);
1091
1092 struct_builder.append(true);
1093
1094 let arr1 = struct_builder.finish();
1095
1096 struct_builder
1097 .field_builder::<ListBuilder<GenericByteDictionaryBuilder<UInt16Type,GenericStringType<i32>>>>(0)
1098 .unwrap()
1099 .append_value(vec![Some("c"), None, Some("d")]);
1100 struct_builder.append(true);
1101
1102 let arr2 = struct_builder.finish();
1103
1104 let schema = Arc::new(Schema::new(vec![Field::new_struct(
1105 "struct",
1106 struct_fields,
1107 true,
1108 )]));
1109
1110 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1111 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
1112
1113 let stream = futures::stream::iter(vec![Ok(batch1), Ok(batch2)]);
1114
1115 let encoder = FlightDataEncoderBuilder::default().build(stream);
1116
1117 let mut decoder = FlightDataDecoder::new(encoder);
1118 let expected_schema = Schema::new(vec![Field::new_struct(
1119 "struct",
1120 vec![Field::new_list(
1121 "dict_list",
1122 Field::new_list_field(DataType::Utf8, true),
1123 true,
1124 )],
1125 true,
1126 )]);
1127
1128 let expected_schema = Arc::new(expected_schema);
1129
1130 let mut expected_arrays = vec![
1131 StringArray::from_iter(vec![Some("a"), None, Some("b")]),
1132 StringArray::from_iter(vec![Some("c"), None, Some("d")]),
1133 ]
1134 .into_iter();
1135
1136 while let Some(decoded) = decoder.next().await {
1137 let decoded = decoded.unwrap();
1138 match decoded.payload {
1139 DecodedPayload::None => {}
1140 DecodedPayload::Schema(s) => assert_eq!(s, expected_schema),
1141 DecodedPayload::RecordBatch(b) => {
1142 assert_eq!(b.schema(), expected_schema);
1143 let expected_array = expected_arrays.next().unwrap();
1144 let struct_array =
1145 downcast_array::<StructArray>(b.column_by_name("struct").unwrap());
1146 let list_array = downcast_array::<ListArray>(struct_array.column(0));
1147
1148 let elem_array = downcast_array::<StringArray>(list_array.value(0).as_ref());
1149
1150 assert_eq!(elem_array, expected_array);
1151 }
1152 }
1153 }
1154 }
1155
1156 #[tokio::test]
1157 async fn test_dictionary_struct_resend() {
1158 let struct_fields = vec![Field::new_list(
1159 "dict_list",
1160 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1161 true,
1162 )];
1163
1164 let mut struct_builder = StructBuilder::new(
1165 struct_fields.clone(),
1166 vec![Box::new(builder::ListBuilder::new(
1167 StringDictionaryBuilder::<UInt16Type>::new(),
1168 ))],
1169 );
1170
1171 struct_builder.field_builder::<ListBuilder<GenericByteDictionaryBuilder<UInt16Type,GenericStringType<i32>>>>(0)
1172 .unwrap()
1173 .append_value(vec![Some("a"), None, Some("b")]);
1174 struct_builder.append(true);
1175
1176 let arr1 = struct_builder.finish();
1177
1178 struct_builder.field_builder::<ListBuilder<GenericByteDictionaryBuilder<UInt16Type,GenericStringType<i32>>>>(0)
1179 .unwrap()
1180 .append_value(vec![Some("c"), None, Some("d")]);
1181 struct_builder.append(true);
1182
1183 let arr2 = struct_builder.finish();
1184
1185 let schema = Arc::new(Schema::new(vec![Field::new_struct(
1186 "struct",
1187 struct_fields,
1188 true,
1189 )]));
1190
1191 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1192 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
1193
1194 verify_flight_round_trip(vec![batch1, batch2]).await;
1195 }
1196
1197 #[tokio::test]
1198 async fn test_dictionary_union_hydration() {
1199 let struct_fields = vec![Field::new_list(
1200 "dict_list",
1201 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1202 true,
1203 )];
1204
1205 let union_fields = [
1206 (
1207 0,
1208 Arc::new(Field::new_list(
1209 "dict_list",
1210 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1211 true,
1212 )),
1213 ),
1214 (
1215 1,
1216 Arc::new(Field::new_struct("struct", struct_fields.clone(), true)),
1217 ),
1218 (2, Arc::new(Field::new("string", DataType::Utf8, true))),
1219 ]
1220 .into_iter()
1221 .collect::<UnionFields>();
1222
1223 let struct_fields = vec![Field::new_list(
1224 "dict_list",
1225 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1226 true,
1227 )];
1228
1229 let mut builder = builder::ListBuilder::new(StringDictionaryBuilder::<UInt16Type>::new());
1230
1231 builder.append_value(vec![Some("a"), None, Some("b")]);
1232
1233 let arr1 = builder.finish();
1234
1235 let type_id_buffer = [0].into_iter().collect::<ScalarBuffer<i8>>();
1236 let arr1 = UnionArray::try_new(
1237 union_fields.clone(),
1238 type_id_buffer,
1239 None,
1240 vec![
1241 Arc::new(arr1) as Arc<dyn Array>,
1242 new_null_array(union_fields.iter().nth(1).unwrap().1.data_type(), 1),
1243 new_null_array(union_fields.iter().nth(2).unwrap().1.data_type(), 1),
1244 ],
1245 )
1246 .unwrap();
1247
1248 builder.append_value(vec![Some("c"), None, Some("d")]);
1249
1250 let arr2 = Arc::new(builder.finish());
1251 let arr2 = StructArray::new(struct_fields.clone().into(), vec![arr2], None);
1252
1253 let type_id_buffer = [1].into_iter().collect::<ScalarBuffer<i8>>();
1254 let arr2 = UnionArray::try_new(
1255 union_fields.clone(),
1256 type_id_buffer,
1257 None,
1258 vec![
1259 new_null_array(union_fields.iter().next().unwrap().1.data_type(), 1),
1260 Arc::new(arr2),
1261 new_null_array(union_fields.iter().nth(2).unwrap().1.data_type(), 1),
1262 ],
1263 )
1264 .unwrap();
1265
1266 let type_id_buffer = [2].into_iter().collect::<ScalarBuffer<i8>>();
1267 let arr3 = UnionArray::try_new(
1268 union_fields.clone(),
1269 type_id_buffer,
1270 None,
1271 vec![
1272 new_null_array(union_fields.iter().next().unwrap().1.data_type(), 1),
1273 new_null_array(union_fields.iter().nth(1).unwrap().1.data_type(), 1),
1274 Arc::new(StringArray::from(vec!["e"])),
1275 ],
1276 )
1277 .unwrap();
1278
1279 let (type_ids, union_fields): (Vec<_>, Vec<_>) = union_fields
1280 .iter()
1281 .map(|(type_id, field_ref)| (type_id, (*Arc::clone(field_ref)).clone()))
1282 .unzip();
1283 let schema = Arc::new(Schema::new(vec![Field::new_union(
1284 "union",
1285 type_ids.clone(),
1286 union_fields.clone(),
1287 UnionMode::Sparse,
1288 )]));
1289
1290 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1291 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
1292 let batch3 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr3)]).unwrap();
1293
1294 let stream = futures::stream::iter(vec![Ok(batch1), Ok(batch2), Ok(batch3)]);
1295
1296 let encoder = FlightDataEncoderBuilder::default().build(stream);
1297
1298 let mut decoder = FlightDataDecoder::new(encoder);
1299
1300 let hydrated_struct_fields = vec![Field::new_list(
1301 "dict_list",
1302 Field::new_list_field(DataType::Utf8, true),
1303 true,
1304 )];
1305
1306 let hydrated_union_fields = vec![
1307 Field::new_list(
1308 "dict_list",
1309 Field::new_list_field(DataType::Utf8, true),
1310 true,
1311 ),
1312 Field::new_struct("struct", hydrated_struct_fields.clone(), true),
1313 Field::new("string", DataType::Utf8, true),
1314 ];
1315
1316 let expected_schema = Schema::new(vec![Field::new_union(
1317 "union",
1318 type_ids.clone(),
1319 hydrated_union_fields,
1320 UnionMode::Sparse,
1321 )]);
1322
1323 let expected_schema = Arc::new(expected_schema);
1324
1325 let mut expected_arrays = vec![
1326 StringArray::from_iter(vec![Some("a"), None, Some("b")]),
1327 StringArray::from_iter(vec![Some("c"), None, Some("d")]),
1328 StringArray::from(vec!["e"]),
1329 ]
1330 .into_iter();
1331
1332 let mut batch = 0;
1333 while let Some(decoded) = decoder.next().await {
1334 let decoded = decoded.unwrap();
1335 match decoded.payload {
1336 DecodedPayload::None => {}
1337 DecodedPayload::Schema(s) => assert_eq!(s, expected_schema),
1338 DecodedPayload::RecordBatch(b) => {
1339 assert_eq!(b.schema(), expected_schema);
1340 let expected_array = expected_arrays.next().unwrap();
1341 let union_arr =
1342 downcast_array::<UnionArray>(b.column_by_name("union").unwrap());
1343
1344 let elem_array = match batch {
1345 0 => {
1346 let list_array = downcast_array::<ListArray>(union_arr.child(0));
1347 downcast_array::<StringArray>(list_array.value(0).as_ref())
1348 }
1349 1 => {
1350 let struct_array = downcast_array::<StructArray>(union_arr.child(1));
1351 let list_array = downcast_array::<ListArray>(struct_array.column(0));
1352
1353 downcast_array::<StringArray>(list_array.value(0).as_ref())
1354 }
1355 _ => downcast_array::<StringArray>(union_arr.child(2)),
1356 };
1357
1358 batch += 1;
1359
1360 assert_eq!(elem_array, expected_array);
1361 }
1362 }
1363 }
1364 }
1365
1366 #[tokio::test]
1367 async fn test_dictionary_union_resend() {
1368 let struct_fields = vec![Field::new_list(
1369 "dict_list",
1370 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1371 true,
1372 )];
1373
1374 let union_fields = [
1375 (
1376 0,
1377 Arc::new(Field::new_list(
1378 "dict_list",
1379 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1380 true,
1381 )),
1382 ),
1383 (
1384 1,
1385 Arc::new(Field::new_struct("struct", struct_fields.clone(), true)),
1386 ),
1387 (2, Arc::new(Field::new("string", DataType::Utf8, true))),
1388 ]
1389 .into_iter()
1390 .collect::<UnionFields>();
1391
1392 let mut field_types = union_fields.iter().map(|(_, field)| field.data_type());
1393 let dict_list_ty = field_types.next().unwrap();
1394 let struct_ty = field_types.next().unwrap();
1395 let string_ty = field_types.next().unwrap();
1396
1397 let struct_fields = vec![Field::new_list(
1398 "dict_list",
1399 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1400 true,
1401 )];
1402
1403 let mut builder = builder::ListBuilder::new(StringDictionaryBuilder::<UInt16Type>::new());
1404
1405 builder.append_value(vec![Some("a"), None, Some("b")]);
1406
1407 let arr1 = builder.finish();
1408
1409 let type_id_buffer = [0].into_iter().collect::<ScalarBuffer<i8>>();
1410 let arr1 = UnionArray::try_new(
1411 union_fields.clone(),
1412 type_id_buffer,
1413 None,
1414 vec![
1415 Arc::new(arr1),
1416 new_null_array(struct_ty, 1),
1417 new_null_array(string_ty, 1),
1418 ],
1419 )
1420 .unwrap();
1421
1422 builder.append_value(vec![Some("c"), None, Some("d")]);
1423
1424 let arr2 = Arc::new(builder.finish());
1425 let arr2 = StructArray::new(struct_fields.clone().into(), vec![arr2], None);
1426
1427 let type_id_buffer = [1].into_iter().collect::<ScalarBuffer<i8>>();
1428 let arr2 = UnionArray::try_new(
1429 union_fields.clone(),
1430 type_id_buffer,
1431 None,
1432 vec![
1433 new_null_array(dict_list_ty, 1),
1434 Arc::new(arr2),
1435 new_null_array(string_ty, 1),
1436 ],
1437 )
1438 .unwrap();
1439
1440 let type_id_buffer = [2].into_iter().collect::<ScalarBuffer<i8>>();
1441 let arr3 = UnionArray::try_new(
1442 union_fields.clone(),
1443 type_id_buffer,
1444 None,
1445 vec![
1446 new_null_array(dict_list_ty, 1),
1447 new_null_array(struct_ty, 1),
1448 Arc::new(StringArray::from(vec!["e"])),
1449 ],
1450 )
1451 .unwrap();
1452
1453 let (type_ids, union_fields): (Vec<_>, Vec<_>) = union_fields
1454 .iter()
1455 .map(|(type_id, field_ref)| (type_id, (*Arc::clone(field_ref)).clone()))
1456 .unzip();
1457 let schema = Arc::new(Schema::new(vec![Field::new_union(
1458 "union",
1459 type_ids.clone(),
1460 union_fields.clone(),
1461 UnionMode::Sparse,
1462 )]));
1463
1464 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1465 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
1466 let batch3 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr3)]).unwrap();
1467
1468 verify_flight_round_trip(vec![batch1, batch2, batch3]).await;
1469 }
1470
1471 #[tokio::test]
1472 async fn test_dictionary_map_hydration() {
1473 let mut builder = MapBuilder::new(
1474 None,
1475 StringDictionaryBuilder::<UInt16Type>::new(),
1476 StringDictionaryBuilder::<UInt16Type>::new(),
1477 );
1478
1479 builder.keys().append_value("k1");
1481 builder.values().append_value("a");
1482 builder.keys().append_value("k2");
1483 builder.values().append_null();
1484 builder.keys().append_value("k3");
1485 builder.values().append_value("b");
1486 builder.append(true).unwrap();
1487
1488 let arr1 = builder.finish();
1489
1490 builder.keys().append_value("k1");
1492 builder.values().append_value("c");
1493 builder.keys().append_value("k2");
1494 builder.values().append_null();
1495 builder.keys().append_value("k3");
1496 builder.values().append_value("d");
1497 builder.append(true).unwrap();
1498
1499 let arr2 = builder.finish();
1500
1501 let schema = Arc::new(Schema::new(vec![Field::new_map(
1502 "dict_map",
1503 "entries",
1504 Field::new_dictionary("keys", DataType::UInt16, DataType::Utf8, false),
1505 Field::new_dictionary("values", DataType::UInt16, DataType::Utf8, true),
1506 false,
1507 false,
1508 )]));
1509
1510 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1511 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
1512
1513 let stream = futures::stream::iter(vec![Ok(batch1), Ok(batch2)]);
1514
1515 let encoder = FlightDataEncoderBuilder::default().build(stream);
1516
1517 let mut decoder = FlightDataDecoder::new(encoder);
1518 let expected_schema = Schema::new(vec![Field::new_map(
1519 "dict_map",
1520 "entries",
1521 Field::new("keys", DataType::Utf8, false),
1522 Field::new("values", DataType::Utf8, true),
1523 false,
1524 false,
1525 )]);
1526
1527 let expected_schema = Arc::new(expected_schema);
1528
1529 let arr1 = MapArray::from_vec_of_maps::<StringArray, StringArray, _, _>(
1531 vec![Some(vec![
1532 ("k1", Some("a")),
1533 ("k2", None),
1534 ("k3", Some("b")),
1535 ])],
1536 false,
1537 );
1538
1539 let arr2 = MapArray::from_vec_of_maps::<StringArray, StringArray, _, _>(
1540 vec![Some(vec![
1541 ("k1", Some("c")),
1542 ("k2", None),
1543 ("k3", Some("d")),
1544 ])],
1545 false,
1546 );
1547
1548 let mut expected_arrays = vec![arr1, arr2].into_iter();
1549
1550 while let Some(decoded) = decoder.next().await {
1551 let decoded = decoded.unwrap();
1552 match decoded.payload {
1553 DecodedPayload::None => {}
1554 DecodedPayload::Schema(s) => assert_eq!(s, expected_schema),
1555 DecodedPayload::RecordBatch(b) => {
1556 assert_eq!(b.schema(), expected_schema);
1557 let expected_array = expected_arrays.next().unwrap();
1558 let map_array =
1559 downcast_array::<MapArray>(b.column_by_name("dict_map").unwrap());
1560
1561 assert_eq!(map_array, expected_array);
1562 }
1563 }
1564 }
1565 }
1566
1567 #[tokio::test]
1568 async fn test_dictionary_map_resend() {
1569 let mut builder = MapBuilder::new(
1570 None,
1571 StringDictionaryBuilder::<UInt16Type>::new(),
1572 StringDictionaryBuilder::<UInt16Type>::new(),
1573 );
1574
1575 builder.keys().append_value("k1");
1577 builder.values().append_value("a");
1578 builder.keys().append_value("k2");
1579 builder.values().append_null();
1580 builder.keys().append_value("k3");
1581 builder.values().append_value("b");
1582 builder.append(true).unwrap();
1583
1584 let arr1 = builder.finish();
1585
1586 builder.keys().append_value("k1");
1588 builder.values().append_value("c");
1589 builder.keys().append_value("k2");
1590 builder.values().append_null();
1591 builder.keys().append_value("k3");
1592 builder.values().append_value("d");
1593 builder.append(true).unwrap();
1594
1595 let arr2 = builder.finish();
1596
1597 let schema = Arc::new(Schema::new(vec![Field::new_map(
1598 "dict_map",
1599 "entries",
1600 Field::new_dictionary("keys", DataType::UInt16, DataType::Utf8, false),
1601 Field::new_dictionary("values", DataType::UInt16, DataType::Utf8, true),
1602 false,
1603 false,
1604 )]));
1605
1606 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1607 let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr2)]).unwrap();
1608
1609 verify_flight_round_trip(vec![batch1, batch2]).await;
1610 }
1611
1612 #[tokio::test]
1613 async fn test_dictionary_ree_resend() {
1614 let dict_values1 = vec![Some("a"), None, Some("b")]
1615 .into_iter()
1616 .collect::<DictionaryArray<Int32Type>>();
1617 let run_ends1 = Int32Array::from(vec![1, 2, 3]);
1618 let arr1 = RunArray::try_new(&run_ends1, &dict_values1).unwrap();
1619
1620 let dict_values2 = vec![Some("c"), Some("a")]
1621 .into_iter()
1622 .collect::<DictionaryArray<Int32Type>>();
1623 let run_ends2 = Int32Array::from(vec![1, 2]);
1624 let arr2 = RunArray::try_new(&run_ends2, &dict_values2).unwrap();
1625
1626 let schema = Arc::new(Schema::new(vec![Field::new(
1627 "ree",
1628 arr1.data_type().clone(),
1629 true,
1630 )]));
1631
1632 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1633 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
1634
1635 verify_flight_round_trip(vec![batch1, batch2]).await;
1636 }
1637
1638 #[tokio::test]
1639 async fn test_dictionary_of_struct_of_dict_resend() {
1640 let struct_fields: Vec<Field> = vec![
1644 Field::new_dictionary("dict", DataType::Int32, DataType::Utf8, true),
1645 Field::new("int", DataType::Int32, false),
1646 ];
1647
1648 let inner_values =
1649 StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
1650 let inner_keys = Int32Array::from_iter_values([0, 1, 2, 3, 0]);
1651 let inner_dict = DictionaryArray::new(inner_keys, Arc::new(inner_values));
1652 let int_array = Int32Array::from(vec![10, 20, 30, 40, 50]);
1653
1654 let struct_array = StructArray::from(vec![
1655 (
1656 Arc::new(struct_fields[0].clone()),
1657 Arc::new(inner_dict) as ArrayRef,
1658 ),
1659 (
1660 Arc::new(struct_fields[1].clone()),
1661 Arc::new(int_array) as ArrayRef,
1662 ),
1663 ]);
1664
1665 let outer_keys = Int8Array::from_iter_values([0, 0, 1, 2]);
1666 let arr1 = DictionaryArray::new(outer_keys, Arc::new(struct_array));
1667
1668 let inner_values2 = StringArray::from(vec![Some("x"), Some("y")]);
1669 let inner_keys2 = Int32Array::from_iter_values([0, 1, 0]);
1670 let inner_dict2 = DictionaryArray::new(inner_keys2, Arc::new(inner_values2));
1671 let int_array2 = Int32Array::from(vec![100, 200, 300]);
1672
1673 let struct_array2 = StructArray::from(vec![
1674 (
1675 Arc::new(struct_fields[0].clone()),
1676 Arc::new(inner_dict2) as ArrayRef,
1677 ),
1678 (
1679 Arc::new(struct_fields[1].clone()),
1680 Arc::new(int_array2) as ArrayRef,
1681 ),
1682 ]);
1683
1684 let outer_keys2 = Int8Array::from_iter_values([0, 1]);
1685 let arr2 = DictionaryArray::new(outer_keys2, Arc::new(struct_array2));
1686
1687 let schema = Arc::new(Schema::new(vec![Field::new(
1688 "dict_struct",
1689 arr1.data_type().clone(),
1690 false,
1691 )]));
1692
1693 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1694 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
1695
1696 verify_flight_round_trip(vec![batch1, batch2]).await;
1697 }
1698
1699 async fn verify_dictionary_list_view_resend<O: OffsetSizeTrait>() {
1700 let mut builder =
1701 GenericListViewBuilder::<O, _>::new(StringDictionaryBuilder::<UInt16Type>::new());
1702
1703 builder.append_value(vec![Some("a"), None, Some("b")]);
1704 let arr1 = builder.finish();
1705
1706 builder.append_value(vec![Some("c"), None, Some("d")]);
1707 let arr2 = builder.finish();
1708
1709 let inner = Arc::new(Field::new_dictionary(
1710 "item",
1711 DataType::UInt16,
1712 DataType::Utf8,
1713 true,
1714 ));
1715 let dt = if O::IS_LARGE {
1716 DataType::LargeListView(inner)
1717 } else {
1718 DataType::ListView(inner)
1719 };
1720 let schema = Arc::new(Schema::new(vec![Field::new("dict_list_view", dt, true)]));
1721
1722 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1723 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
1724
1725 verify_flight_round_trip(vec![batch1, batch2]).await;
1726 }
1727
1728 #[tokio::test]
1729 async fn test_dictionary_list_view_resend() {
1730 verify_dictionary_list_view_resend::<i32>().await;
1731 }
1732
1733 #[tokio::test]
1734 async fn test_dictionary_large_list_view_resend() {
1735 verify_dictionary_list_view_resend::<i64>().await;
1736 }
1737
1738 #[tokio::test]
1739 async fn test_dictionary_fixed_size_list_resend() {
1740 let mut builder =
1741 FixedSizeListBuilder::new(StringDictionaryBuilder::<UInt16Type>::new(), 2);
1742
1743 builder.values().append_value("a");
1744 builder.values().append_value("b");
1745 builder.append(true);
1746 let arr1 = builder.finish();
1747
1748 builder.values().append_value("c");
1749 builder.values().append_value("d");
1750 builder.append(true);
1751 let arr2 = builder.finish();
1752
1753 let schema = Arc::new(Schema::new(vec![Field::new_fixed_size_list(
1754 "dict_fsl",
1755 Field::new_dictionary("item", DataType::UInt16, DataType::Utf8, true),
1756 2,
1757 true,
1758 )]));
1759
1760 let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(arr1)]).unwrap();
1761 let batch2 = RecordBatch::try_new(schema, vec![Arc::new(arr2)]).unwrap();
1762
1763 verify_flight_round_trip(vec![batch1, batch2]).await;
1764 }
1765
1766 async fn verify_flight_round_trip(batches: Vec<RecordBatch>) {
1767 verify_flight_round_trip_with_options(batches, IpcWriteOptions::default()).await;
1768 }
1769
1770 async fn verify_flight_round_trip_with_options(
1773 mut batches: Vec<RecordBatch>,
1774 options: IpcWriteOptions,
1775 ) {
1776 let expected_schema = batches.first().unwrap().schema();
1777
1778 let encoder = FlightDataEncoderBuilder::default()
1779 .with_options(options)
1780 .with_dictionary_handling(DictionaryHandling::Resend)
1781 .build(futures::stream::iter(batches.clone().into_iter().map(Ok)));
1782
1783 let mut expected_batches = batches.drain(..);
1784
1785 let mut decoder = FlightDataDecoder::new(encoder);
1786 while let Some(decoded) = decoder.next().await {
1787 let decoded = decoded.unwrap();
1788 match decoded.payload {
1789 DecodedPayload::None => {}
1790 DecodedPayload::Schema(s) => assert_eq!(s, expected_schema),
1791 DecodedPayload::RecordBatch(b) => {
1792 let expected_batch = expected_batches.next().unwrap();
1793 assert_eq!(b, expected_batch);
1794 }
1795 }
1796 }
1797 }
1798
1799 #[test]
1800 fn test_schema_metadata_encoded() {
1801 let schema = Schema::new(vec![Field::new("data", DataType::Int32, false)]).with_metadata(
1802 HashMap::from([("some_key".to_owned(), "some_value".to_owned())]),
1803 );
1804
1805 let mut dictionary_tracker = DictionaryTracker::new(false);
1806
1807 let got = prepare_schema_for_flight(&schema, &mut dictionary_tracker, false);
1808 assert!(got.metadata().contains_key("some_key"));
1809 }
1810
1811 #[test]
1812 fn test_encode_no_column_batch() {
1813 let batch = RecordBatch::try_new_with_options(
1814 Arc::new(Schema::empty()),
1815 vec![],
1816 &RecordBatchOptions::new().with_row_count(Some(10)),
1817 )
1818 .expect("cannot create record batch");
1819
1820 hydrate_dictionaries(&batch, batch.schema()).expect("failed to optimize");
1821 }
1822
1823 fn make_flight_data(
1824 batch: &RecordBatch,
1825 options: &IpcWriteOptions,
1826 ) -> (Vec<FlightData>, FlightData) {
1827 flight_data_from_arrow_batch(batch, options)
1828 }
1829
1830 fn flight_data_from_arrow_batch(
1831 batch: &RecordBatch,
1832 options: &IpcWriteOptions,
1833 ) -> (Vec<FlightData>, FlightData) {
1834 let data_gen = IpcDataGenerator::default();
1835 let mut dictionary_tracker = DictionaryTracker::new(false);
1836 let mut ipc_write_context = IpcWriteContext::default();
1837
1838 let (encoded_dictionaries, encoded_batch) = data_gen
1839 .encode(
1840 batch,
1841 &mut dictionary_tracker,
1842 options,
1843 &mut ipc_write_context,
1844 )
1845 .expect("DictionaryTracker configured above to not error on replacement");
1846
1847 let flight_dictionaries = encoded_dictionaries.into_iter().map(Into::into).collect();
1848 let flight_batch = encoded_batch.into();
1849
1850 (flight_dictionaries, flight_batch)
1851 }
1852
1853 #[test]
1854 fn test_split_batch_for_grpc_response() {
1855 let max_flight_data_size = 1024;
1856
1857 let c = UInt32Array::from(vec![1, 2, 3, 4, 5, 6]);
1859 let batch = RecordBatch::try_from_iter(vec![("a", Arc::new(c) as ArrayRef)])
1860 .expect("cannot create record batch");
1861 let split: Vec<_> =
1862 split_batch_for_grpc_response(batch.clone(), max_flight_data_size).collect();
1863 assert_eq!(split.len(), 1);
1864 assert_eq!(batch, split[0]);
1865
1866 let n_rows = max_flight_data_size + 1;
1868 assert!(n_rows % 2 == 1, "should be an odd number");
1869 let c = UInt8Array::from((0..n_rows).map(|i| (i % 256) as u8).collect::<Vec<_>>());
1870 let batch = RecordBatch::try_from_iter(vec![("a", Arc::new(c) as ArrayRef)])
1871 .expect("cannot create record batch");
1872 let split: Vec<_> =
1873 split_batch_for_grpc_response(batch.clone(), max_flight_data_size).collect();
1874 assert_eq!(split.len(), 3);
1875 assert_eq!(
1876 split.iter().map(|batch| batch.num_rows()).sum::<usize>(),
1877 n_rows
1878 );
1879 let a = pretty_format_batches(&split).unwrap().to_string();
1880 let b = pretty_format_batches(&[batch]).unwrap().to_string();
1881 assert_eq!(a, b);
1882 }
1883
1884 #[test]
1885 fn test_split_batch_for_grpc_response_sizes() {
1886 verify_split(2000, 2 * 1024, vec![250, 250, 250, 250, 250, 250, 250, 250]);
1888
1889 verify_split(2000, 4 * 1024, vec![500, 500, 500, 500]);
1891
1892 verify_split(2023, 3 * 1024, vec![337, 337, 337, 337, 337, 337, 1]);
1894
1895 verify_split(10, 1, vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
1897
1898 verify_split(10, 1024, vec![10]);
1900 }
1901
1902 fn verify_split(
1906 num_input_rows: u64,
1907 max_flight_data_size_bytes: usize,
1908 expected_sizes: Vec<usize>,
1909 ) {
1910 let array: UInt64Array = (0..num_input_rows).collect();
1911
1912 let batch = RecordBatch::try_from_iter(vec![("a", Arc::new(array) as ArrayRef)])
1913 .expect("cannot create record batch");
1914
1915 let input_rows = batch.num_rows();
1916
1917 let split: Vec<_> =
1918 split_batch_for_grpc_response(batch.clone(), max_flight_data_size_bytes).collect();
1919 let sizes: Vec<_> = split.iter().map(RecordBatch::num_rows).collect();
1920 let output_rows: usize = sizes.iter().sum();
1921
1922 assert_eq!(sizes, expected_sizes, "mismatch for {batch:?}");
1923 assert_eq!(input_rows, output_rows, "mismatch for {batch:?}");
1924 }
1925
1926 #[tokio::test]
1930 async fn flight_data_size_even() {
1931 let s1 = StringArray::from_iter_values(std::iter::repeat_n(".10 bytes.", 1024));
1932 let i1 = Int16Array::from_iter_values(0..1024);
1933 let s2 = StringArray::from_iter_values(std::iter::repeat_n("6bytes", 1024));
1934 let i2 = Int64Array::from_iter_values(0..1024);
1935
1936 let batch = RecordBatch::try_from_iter(vec![
1937 ("s1", Arc::new(s1) as _),
1938 ("i1", Arc::new(i1) as _),
1939 ("s2", Arc::new(s2) as _),
1940 ("i2", Arc::new(i2) as _),
1941 ])
1942 .unwrap();
1943
1944 verify_encoded_split(batch, 120).await;
1945 }
1946
1947 #[tokio::test]
1948 async fn flight_data_size_uneven_variable_lengths() {
1949 let array = StringArray::from_iter_values((0..1024).map(|i| "*".repeat(i)));
1951 let batch = RecordBatch::try_from_iter(vec![("data", Arc::new(array) as _)]).unwrap();
1952
1953 verify_encoded_split(batch, 4312).await;
1956 }
1957
1958 #[tokio::test]
1959 async fn flight_data_size_large_row() {
1960 let array1 = StringArray::from_iter_values(vec![
1962 "*".repeat(500),
1963 "*".repeat(500),
1964 "*".repeat(500),
1965 "*".repeat(500),
1966 ]);
1967 let array2 = StringArray::from_iter_values(vec![
1968 "*".to_string(),
1969 "*".repeat(1000),
1970 "*".repeat(2000),
1971 "*".repeat(4000),
1972 ]);
1973
1974 let array3 = StringArray::from_iter_values(vec![
1975 "*".to_string(),
1976 "*".to_string(),
1977 "*".repeat(1000),
1978 "*".repeat(2000),
1979 ]);
1980
1981 let batch = RecordBatch::try_from_iter(vec![
1982 ("a1", Arc::new(array1) as _),
1983 ("a2", Arc::new(array2) as _),
1984 ("a3", Arc::new(array3) as _),
1985 ])
1986 .unwrap();
1987
1988 verify_encoded_split(batch, 5808).await;
1992 }
1993
1994 #[tokio::test]
1995 async fn flight_data_size_string_dictionary() {
1996 let array: DictionaryArray<Int32Type> = (1..1024)
1998 .map(|i| match i % 3 {
1999 0 => Some("value0"),
2000 1 => Some("value1"),
2001 _ => None,
2002 })
2003 .collect();
2004
2005 let batch = RecordBatch::try_from_iter(vec![("a1", Arc::new(array) as _)]).unwrap();
2006
2007 verify_encoded_split(batch, 56).await;
2008 }
2009
2010 #[tokio::test]
2011 async fn flight_data_size_large_dictionary() {
2012 let values: Vec<_> = (1..1024).map(|i| "**".repeat(i)).collect();
2014
2015 let array: DictionaryArray<Int32Type> = values.iter().map(|s| Some(s.as_str())).collect();
2016
2017 let batch = RecordBatch::try_from_iter(vec![("a1", Arc::new(array) as _)]).unwrap();
2018
2019 verify_encoded_split(batch, 3336).await;
2022 }
2023
2024 #[tokio::test]
2025 async fn flight_data_size_large_dictionary_repeated_non_uniform() {
2026 let values = StringArray::from_iter_values((0..1024).map(|i| "******".repeat(i)));
2028 let keys = Int32Array::from_iter_values((0..3000).map(|i| (3000 - i) % 1024));
2029 let array = DictionaryArray::new(keys, Arc::new(values));
2030
2031 let batch = RecordBatch::try_from_iter(vec![("a1", Arc::new(array) as _)]).unwrap();
2032
2033 verify_encoded_split(batch, 5288).await;
2036 }
2037
2038 #[tokio::test]
2039 async fn flight_data_size_multiple_dictionaries() {
2040 let values1: Vec<_> = (1..1024).map(|i| "**".repeat(i)).collect();
2042 let values2: Vec<_> = (1..1024).map(|i| "**".repeat(i % 10)).collect();
2044 let values3: Vec<_> = (1..1024).map(|i| "**".repeat(i % 100)).collect();
2046
2047 let array1: DictionaryArray<Int32Type> = values1.iter().map(|s| Some(s.as_str())).collect();
2048 let array2: DictionaryArray<Int32Type> = values2.iter().map(|s| Some(s.as_str())).collect();
2049 let array3: DictionaryArray<Int32Type> = values3.iter().map(|s| Some(s.as_str())).collect();
2050
2051 let batch = RecordBatch::try_from_iter(vec![
2052 ("a1", Arc::new(array1) as _),
2053 ("a2", Arc::new(array2) as _),
2054 ("a3", Arc::new(array3) as _),
2055 ])
2056 .unwrap();
2057
2058 verify_encoded_split(batch, 4136).await;
2061 }
2062
2063 fn flight_data_size(d: &FlightData) -> usize {
2065 let flight_descriptor_size = d
2066 .flight_descriptor
2067 .as_ref()
2068 .map(|descriptor| {
2069 let path_len: usize = descriptor.path.iter().map(|p| p.len()).sum();
2070
2071 std::mem::size_of_val(descriptor) + descriptor.cmd.len() + path_len
2072 })
2073 .unwrap_or(0);
2074
2075 flight_descriptor_size + d.app_metadata.len() + d.data_body.len() + d.data_header.len()
2076 }
2077
2078 async fn verify_encoded_split(batch: RecordBatch, allowed_overage: usize) {
2094 let num_rows = batch.num_rows();
2095
2096 let mut max_overage_seen = 0;
2098
2099 for max_flight_data_size in [1024, 2021, 5000] {
2100 println!("Encoding {num_rows} with a maximum size of {max_flight_data_size}");
2101
2102 let mut stream = FlightDataEncoderBuilder::new()
2103 .with_max_flight_data_size(max_flight_data_size)
2104 .with_options(IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap())
2106 .build(futures::stream::iter([Ok(batch.clone())]));
2107
2108 let mut i = 0;
2109 while let Some(data) = stream.next().await.transpose().unwrap() {
2110 let actual_data_size = flight_data_size(&data);
2111
2112 let actual_overage = actual_data_size.saturating_sub(max_flight_data_size);
2113
2114 assert!(
2115 actual_overage <= allowed_overage,
2116 "encoded data[{i}]: actual size {actual_data_size}, \
2117 actual_overage: {actual_overage} \
2118 allowed_overage: {allowed_overage}"
2119 );
2120
2121 i += 1;
2122
2123 max_overage_seen = max_overage_seen.max(actual_overage)
2124 }
2125 }
2126
2127 assert_eq!(
2131 allowed_overage, max_overage_seen,
2132 "Specified overage was too high"
2133 );
2134 }
2135}