1mod stream;
15pub use stream::*;
16
17use arrow_select::concat;
18
19use flatbuffers::{VectorIter, VerifierOptions};
20use std::collections::{HashMap, VecDeque};
21use std::fmt;
22use std::io::{BufReader, Read, Seek, SeekFrom};
23use std::sync::Arc;
24
25use arrow_array::*;
26use arrow_buffer::{
27 ArrowNativeType, BooleanBuffer, Buffer, MutableBuffer, NullBuffer, ScalarBuffer,
28};
29use arrow_data::{ArrayData, ArrayDataBuilder, UnsafeFlag};
30use arrow_schema::*;
31
32use crate::compression::{CompressionCodec, DecompressionContext};
33use crate::r#gen::Message;
34use crate::{Block, CONTINUATION_MARKER, FieldNode, MetadataVersion};
35use DataType::*;
36
37fn read_buffer(
47 buf: &crate::Buffer,
48 a_data: &Buffer,
49 compression_codec: Option<CompressionCodec>,
50 decompression_context: &mut DecompressionContext,
51) -> Result<Buffer, ArrowError> {
52 let start_offset = buf.offset() as usize;
53 let buf_data = a_data
54 .try_slice_with_length(start_offset, buf.length() as usize)
55 .map_err(|e| ArrowError::IpcError(e.to_string()))?;
56 match (buf_data.is_empty(), compression_codec) {
58 (true, _) | (_, None) => Ok(buf_data),
59 (false, Some(decompressor)) => {
60 decompressor.decompress_to_buffer(&buf_data, decompression_context)
61 }
62 }
63}
64
65impl RecordBatchDecoder<'_> {
66 fn create_array(
79 &mut self,
80 field: &Field,
81 variadic_counts: &mut VecDeque<i64>,
82 ) -> Result<ArrayRef, ArrowError> {
83 let data_type = field.data_type();
84 match data_type {
85 Utf8 | Binary | LargeBinary | LargeUtf8 => {
86 let field_node = self.next_node(field)?;
87 let buffers = [
88 self.next_buffer()?,
89 self.next_buffer()?,
90 self.next_buffer()?,
91 ];
92 self.create_primitive_array(field_node, data_type, &buffers)
93 }
94 BinaryView | Utf8View => {
95 let count = self.next_variadic_buffer_count(variadic_counts, data_type)?;
96 let buffers = (0..count)
97 .map(|_| self.next_buffer())
98 .collect::<Result<Vec<_>, _>>()?;
99 let field_node = self.next_node(field)?;
100 self.create_primitive_array(field_node, data_type, &buffers)
101 }
102 FixedSizeBinary(_) => {
103 let field_node = self.next_node(field)?;
104 let buffers = [self.next_buffer()?, self.next_buffer()?];
105 self.create_primitive_array(field_node, data_type, &buffers)
106 }
107 List(list_field) | LargeList(list_field) | Map(list_field, _) => {
108 let list_node = self.next_node(field)?;
109 let list_buffers = [self.next_buffer()?, self.next_buffer()?];
110 let values = self.create_array(list_field, variadic_counts)?;
111 self.create_list_array(list_node, data_type, &list_buffers, values)
112 }
113 ListView(list_field) | LargeListView(list_field) => {
114 let list_node = self.next_node(field)?;
115 let list_buffers = [
116 self.next_buffer()?, self.next_buffer()?, self.next_buffer()?, ];
120 let values = self.create_array(list_field, variadic_counts)?;
121 self.create_list_view_array(list_node, data_type, &list_buffers, values)
122 }
123 FixedSizeList(list_field, _) => {
124 let list_node = self.next_node(field)?;
125 let list_buffers = [self.next_buffer()?];
126 let values = self.create_array(list_field, variadic_counts)?;
127 self.create_list_array(list_node, data_type, &list_buffers, values)
128 }
129 Struct(struct_fields) => {
130 let struct_node = self.next_node(field)?;
131 let null_buffer = self.next_buffer()?;
132
133 let mut struct_arrays = Vec::with_capacity(struct_fields.len());
135 for struct_field in struct_fields {
138 let child = self.create_array(struct_field, variadic_counts)?;
139 struct_arrays.push(child);
140 }
141 self.create_struct_array(struct_node, null_buffer, struct_fields, struct_arrays)
142 }
143 RunEndEncoded(run_ends_field, values_field) => {
144 let run_node = self.next_node(field)?;
145 let run_ends = self.create_array(run_ends_field, variadic_counts)?;
146 let values = self.create_array(values_field, variadic_counts)?;
147
148 let run_array_length = run_node.length() as usize;
149 let builder = ArrayData::builder(data_type.clone())
150 .len(run_array_length)
151 .offset(0)
152 .add_child_data(run_ends.into_data())
153 .add_child_data(values.into_data())
154 .null_count(run_node.null_count() as usize);
155
156 self.create_array_from_builder(builder)
157 }
158 Dictionary(_, _) => {
160 let index_node = self.next_node(field)?;
161 let index_buffers = [self.next_buffer()?, self.next_buffer()?];
162
163 #[expect(deprecated)]
164 let dict_id = field.dict_id().ok_or_else(|| {
165 ArrowError::ParseError(format!("Field {field} does not have dict id"))
166 })?;
167
168 let value_array = match self.dictionaries_by_id.get(&dict_id) {
169 Some(array) => array.clone(),
170 None => {
171 if let Dictionary(_, value_type) = data_type {
175 arrow_array::new_empty_array(value_type.as_ref())
176 } else {
177 unreachable!()
178 }
179 }
180 };
181
182 self.create_dictionary_array(index_node, data_type, &index_buffers, value_array)
183 }
184 Union(fields, mode) => {
185 let union_node = self.next_node(field)?;
186 let len = union_node.length() as usize;
187
188 if self.version < MetadataVersion::V5 {
191 self.next_buffer()?;
192 }
193
194 let type_ids: ScalarBuffer<i8> = self
195 .next_buffer()?
196 .try_slice_with_length(0, len)
197 .map_err(|e| ArrowError::IpcError(e.to_string()))?
198 .into();
199
200 let value_offsets = match mode {
201 UnionMode::Dense => {
202 let offsets: ScalarBuffer<i32> = self
203 .next_buffer()?
204 .try_slice_with_length(0, len * 4)
205 .map_err(|e| ArrowError::IpcError(e.to_string()))?
206 .into();
207 Some(offsets)
208 }
209 UnionMode::Sparse => None,
210 };
211
212 let mut children = Vec::with_capacity(fields.len());
213
214 for (_id, field) in fields.iter() {
215 let child = self.create_array(field, variadic_counts)?;
216 children.push(child);
217 }
218
219 let array = if self.skip_validation.get() {
220 unsafe {
222 UnionArray::new_unchecked(fields.clone(), type_ids, value_offsets, children)
223 }
224 } else {
225 UnionArray::try_new(fields.clone(), type_ids, value_offsets, children)?
226 };
227 Ok(Arc::new(array))
228 }
229 Null => {
230 let node = self.next_node(field)?;
231 let length = node.length();
232 let null_count = node.null_count();
233
234 if length != null_count {
235 return Err(ArrowError::SchemaError(format!(
236 "Field {field} of NullArray has unequal null_count {null_count} and len {length}"
237 )));
238 }
239
240 let builder = ArrayData::builder(data_type.clone())
241 .len(length as usize)
242 .offset(0);
243 self.create_array_from_builder(builder)
244 }
245 _ => {
246 let field_node = self.next_node(field)?;
247 let buffers = [self.next_buffer()?, self.next_buffer()?];
248 self.create_primitive_array(field_node, data_type, &buffers)
249 }
250 }
251 }
252
253 fn create_primitive_array(
256 &self,
257 field_node: &FieldNode,
258 data_type: &DataType,
259 buffers: &[Buffer],
260 ) -> Result<ArrayRef, ArrowError> {
261 let length = field_node.length() as usize;
262 let null_buffer = (field_node.null_count() > 0).then_some(buffers[0].clone());
263 let mut builder = match data_type {
264 Utf8 | Binary | LargeBinary | LargeUtf8 => {
265 ArrayData::builder(data_type.clone())
267 .len(length)
268 .buffers(buffers[1..3].to_vec())
269 .null_bit_buffer(null_buffer)
270 }
271 BinaryView | Utf8View => ArrayData::builder(data_type.clone())
272 .len(length)
273 .buffers(buffers[1..].to_vec())
274 .null_bit_buffer(null_buffer),
275 _ if data_type.is_primitive() || matches!(data_type, Boolean | FixedSizeBinary(_)) => {
276 ArrayData::builder(data_type.clone())
278 .len(length)
279 .add_buffer(buffers[1].clone())
280 .null_bit_buffer(null_buffer)
281 }
282 t => unreachable!("Data type {:?} either unsupported or not primitive", t),
283 };
284
285 builder = builder.null_count(field_node.null_count() as usize);
286
287 self.create_array_from_builder(builder)
288 }
289
290 fn create_array_from_builder(&self, builder: ArrayDataBuilder) -> Result<ArrayRef, ArrowError> {
292 let mut builder = builder.align_buffers(!self.require_alignment);
293 if self.skip_validation.get() {
294 unsafe { builder = builder.skip_validation(true) }
296 }
297 Ok(make_array(builder.build()?))
298 }
299
300 fn create_list_array(
303 &self,
304 field_node: &FieldNode,
305 data_type: &DataType,
306 buffers: &[Buffer],
307 child_array: ArrayRef,
308 ) -> Result<ArrayRef, ArrowError> {
309 let null_buffer = (field_node.null_count() > 0).then_some(buffers[0].clone());
310 let length = field_node.length() as usize;
311 let child_data = child_array.into_data();
312 let mut builder = match data_type {
313 List(_) | LargeList(_) | Map(_, _) => ArrayData::builder(data_type.clone())
314 .len(length)
315 .add_buffer(buffers[1].clone())
316 .add_child_data(child_data)
317 .null_bit_buffer(null_buffer),
318
319 FixedSizeList(_, _) => ArrayData::builder(data_type.clone())
320 .len(length)
321 .add_child_data(child_data)
322 .null_bit_buffer(null_buffer),
323
324 _ => unreachable!("Cannot create list or map array from {:?}", data_type),
325 };
326
327 builder = builder.null_count(field_node.null_count() as usize);
328
329 self.create_array_from_builder(builder)
330 }
331
332 fn create_list_view_array(
333 &self,
334 field_node: &FieldNode,
335 data_type: &DataType,
336 buffers: &[Buffer],
337 child_array: ArrayRef,
338 ) -> Result<ArrayRef, ArrowError> {
339 assert!(matches!(data_type, ListView(_) | LargeListView(_)));
340
341 let null_buffer = (field_node.null_count() > 0).then_some(buffers[0].clone());
342 let length = field_node.length() as usize;
343 let child_data = child_array.into_data();
344
345 self.create_array_from_builder(
346 ArrayData::builder(data_type.clone())
347 .len(length)
348 .add_buffer(buffers[1].clone()) .add_buffer(buffers[2].clone()) .add_child_data(child_data)
351 .null_bit_buffer(null_buffer)
352 .null_count(field_node.null_count() as usize),
353 )
354 }
355
356 fn create_struct_array(
357 &self,
358 struct_node: &FieldNode,
359 null_buffer: Buffer,
360 struct_fields: &Fields,
361 struct_arrays: Vec<ArrayRef>,
362 ) -> Result<ArrayRef, ArrowError> {
363 let null_count = struct_node.null_count() as usize;
364 let len = struct_node.length() as usize;
365 let skip_validation = self.skip_validation.get();
366
367 let nulls = if null_count > 0 {
368 if !skip_validation {
369 let needed_len = len.div_ceil(8);
371 if null_buffer.len() < needed_len {
372 return Err(ArrowError::InvalidArgumentError(format!(
373 "null_bit_buffer size too small. got {} needed {}",
374 null_buffer.len(),
375 needed_len
376 )));
377 }
378 }
379 let validity_buffer = BooleanBuffer::new(null_buffer, 0, len);
380 let null_buffer = if skip_validation {
381 unsafe { NullBuffer::new_unchecked(validity_buffer, null_count) }
383 } else {
384 let null_buffer = NullBuffer::new(validity_buffer);
385
386 if null_buffer.null_count() != null_count {
387 return Err(ArrowError::InvalidArgumentError(format!(
388 "null_count value ({}) doesn't match actual number of nulls in array ({})",
389 null_count,
390 null_buffer.null_count()
391 )));
392 }
393
394 null_buffer
395 };
396
397 Some(null_buffer)
398 } else {
399 None
400 };
401 if struct_arrays.is_empty() {
402 return Ok(Arc::new(StructArray::new_empty_fields(len, nulls)));
405 }
406
407 let struct_array = if skip_validation {
408 unsafe { StructArray::new_unchecked(struct_fields.clone(), struct_arrays, nulls) }
410 } else {
411 StructArray::try_new(struct_fields.clone(), struct_arrays, nulls)?
412 };
413
414 Ok(Arc::new(struct_array))
415 }
416
417 fn create_dictionary_array(
420 &self,
421 field_node: &FieldNode,
422 data_type: &DataType,
423 buffers: &[Buffer],
424 value_array: ArrayRef,
425 ) -> Result<ArrayRef, ArrowError> {
426 if let Dictionary(_, _) = *data_type {
427 let null_buffer = (field_node.null_count() > 0).then_some(buffers[0].clone());
428 let builder = ArrayData::builder(data_type.clone())
429 .len(field_node.length() as usize)
430 .add_buffer(buffers[1].clone())
431 .add_child_data(value_array.into_data())
432 .null_bit_buffer(null_buffer)
433 .null_count(field_node.null_count() as usize);
434 self.create_array_from_builder(builder)
435 } else {
436 unreachable!("Cannot create dictionary array from {:?}", data_type)
437 }
438 }
439}
440
441pub struct RecordBatchDecoder<'a> {
447 batch: crate::RecordBatch<'a>,
449 schema: SchemaRef,
451 dictionaries_by_id: &'a HashMap<i64, ArrayRef>,
453 compression: Option<CompressionCodec>,
455 decompression_context: DecompressionContext,
457 version: MetadataVersion,
459 data: &'a Buffer,
461 nodes: VectorIter<'a, FieldNode>,
463 buffers: VectorIter<'a, crate::Buffer>,
465 projection: Option<&'a [usize]>,
468 require_alignment: bool,
471 skip_validation: UnsafeFlag,
475}
476
477impl<'a> RecordBatchDecoder<'a> {
478 pub fn try_new(
480 buf: &'a Buffer,
481 batch: crate::RecordBatch<'a>,
482 schema: SchemaRef,
483 dictionaries_by_id: &'a HashMap<i64, ArrayRef>,
484 metadata: &'a MetadataVersion,
485 ) -> Result<Self, ArrowError> {
486 let buffers = batch.buffers().ok_or_else(|| {
487 ArrowError::IpcError("Unable to get buffers from IPC RecordBatch".to_string())
488 })?;
489 let field_nodes = batch.nodes().ok_or_else(|| {
490 ArrowError::IpcError("Unable to get field nodes from IPC RecordBatch".to_string())
491 })?;
492
493 let batch_compression = batch.compression();
494 let compression = batch_compression
495 .map(|batch_compression| batch_compression.codec().try_into())
496 .transpose()?;
497
498 Ok(Self {
499 batch,
500 schema,
501 dictionaries_by_id,
502 compression,
503 decompression_context: DecompressionContext::new(),
504 version: *metadata,
505 data: buf,
506 nodes: field_nodes.iter(),
507 buffers: buffers.iter(),
508 projection: None,
509 require_alignment: false,
510 skip_validation: UnsafeFlag::new(),
511 })
512 }
513
514 pub fn with_projection(mut self, projection: Option<&'a [usize]>) -> Self {
519 self.projection = projection;
520 self
521 }
522
523 pub fn with_require_alignment(mut self, require_alignment: bool) -> Self {
529 self.require_alignment = require_alignment;
530 self
531 }
532
533 pub fn with_skip_validation(mut self, skip_validation: UnsafeFlag) -> Self {
546 self.skip_validation = skip_validation;
547 self
548 }
549
550 pub fn read_record_batch(mut self) -> Result<RecordBatch, ArrowError> {
557 let mut variadic_counts: VecDeque<i64> = self
558 .batch
559 .variadicBufferCounts()
560 .into_iter()
561 .flatten()
562 .collect();
563
564 let options = RecordBatchOptions::new().with_row_count(Some(self.batch.length() as usize));
565
566 let schema = Arc::clone(&self.schema);
567 if let Some(projection) = self.projection {
568 let mut arrays = Vec::with_capacity(projection.len());
569 for (idx, field) in schema.fields().iter().enumerate() {
571 let mut decoded = None;
573 for (proj_idx, projected_idx) in projection.iter().enumerate() {
574 if *projected_idx == idx {
575 let child = match decoded.clone() {
577 Some(child) => child,
578 None => {
579 let child = self.create_array(field, &mut variadic_counts)?;
580 decoded = Some(Arc::clone(&child));
581 child
582 }
583 };
584 arrays.push((proj_idx, child));
585 }
586 }
587
588 if decoded.is_none() {
589 self.skip_field(field, &mut variadic_counts)?;
590 }
591 }
592
593 arrays.sort_by_key(|t| t.0);
594
595 let schema = Arc::new(schema.project(projection)?);
596 let columns = arrays.into_iter().map(|t| t.1).collect::<Vec<_>>();
597
598 if self.skip_validation.get() {
599 unsafe {
601 Ok(RecordBatch::new_unchecked(
602 schema,
603 columns,
604 self.batch.length() as usize,
605 ))
606 }
607 } else {
608 check_variadic_counts_consumed(&variadic_counts)?;
609 RecordBatch::try_new_with_options(schema, columns, &options)
610 }
611 } else {
612 let mut children = Vec::with_capacity(schema.fields().len());
613 for field in schema.fields() {
615 let child = self.create_array(field, &mut variadic_counts)?;
616 children.push(child);
617 }
618
619 if self.skip_validation.get() {
620 unsafe {
622 Ok(RecordBatch::new_unchecked(
623 schema,
624 children,
625 self.batch.length() as usize,
626 ))
627 }
628 } else {
629 check_variadic_counts_consumed(&variadic_counts)?;
630 RecordBatch::try_new_with_options(schema, children, &options)
631 }
632 }
633 }
634
635 fn next_buffer(&mut self) -> Result<Buffer, ArrowError> {
636 let buffer = self.buffers.next().ok_or_else(|| {
637 ArrowError::IpcError("Buffer count mismatched with metadata".to_string())
638 })?;
639 read_buffer(
640 buffer,
641 self.data,
642 self.compression,
643 &mut self.decompression_context,
644 )
645 }
646
647 fn skip_buffer(&mut self) -> Result<(), ArrowError> {
648 self.buffers.next().ok_or_else(|| {
649 ArrowError::IpcError("Buffer count mismatched with metadata".to_string())
650 })?;
651 Ok(())
652 }
653
654 fn next_node(&mut self, field: &Field) -> Result<&'a FieldNode, ArrowError> {
655 self.nodes.next().ok_or_else(|| {
656 ArrowError::SchemaError(format!(
657 "Invalid data for schema. {field} refers to node not found in schema",
658 ))
659 })
660 }
661
662 fn skip_field(
663 &mut self,
664 field: &Field,
665 variadic_count: &mut VecDeque<i64>,
666 ) -> Result<(), ArrowError> {
667 self.next_node(field)?;
668
669 match field.data_type() {
670 Utf8 | Binary | LargeBinary | LargeUtf8 => {
671 for _ in 0..3 {
672 self.skip_buffer()?;
673 }
674 }
675 Utf8View | BinaryView => {
676 let count = self.next_variadic_buffer_count(variadic_count, field.data_type())?;
677 for _ in 0..count {
678 self.skip_buffer()?;
679 }
680 }
681 FixedSizeBinary(_) => {
682 self.skip_buffer()?;
683 self.skip_buffer()?;
684 }
685 List(list_field) | LargeList(list_field) | Map(list_field, _) => {
686 self.skip_buffer()?;
687 self.skip_buffer()?;
688 self.skip_field(list_field, variadic_count)?;
689 }
690 ListView(list_field) | LargeListView(list_field) => {
691 self.skip_buffer()?; self.skip_buffer()?; self.skip_buffer()?; self.skip_field(list_field, variadic_count)?;
695 }
696 FixedSizeList(list_field, _) => {
697 self.skip_buffer()?;
698 self.skip_field(list_field, variadic_count)?;
699 }
700 Struct(struct_fields) => {
701 self.skip_buffer()?;
702
703 for struct_field in struct_fields {
705 self.skip_field(struct_field, variadic_count)?
706 }
707 }
708 RunEndEncoded(run_ends_field, values_field) => {
709 self.skip_field(run_ends_field, variadic_count)?;
710 self.skip_field(values_field, variadic_count)?;
711 }
712 Dictionary(_, _) => {
713 self.skip_buffer()?; self.skip_buffer()?; }
716 Union(fields, mode) => {
717 if self.version < MetadataVersion::V5 {
718 self.skip_buffer()?; }
720 self.skip_buffer()?; match mode {
723 UnionMode::Dense => self.skip_buffer()?, UnionMode::Sparse => {}
725 }
726
727 for (_, field) in fields.iter() {
728 self.skip_field(field, variadic_count)?
729 }
730 }
731 Null => {}
733
734 Boolean
736 | Int8
737 | Int16
738 | Int32
739 | Int64
740 | UInt8
741 | UInt16
742 | UInt32
743 | UInt64
744 | Float16
745 | Float32
746 | Float64
747 | Timestamp(_, _)
748 | Date32
749 | Date64
750 | Time32(_)
751 | Time64(_)
752 | Duration(_)
753 | Interval(_)
754 | Decimal32(_, _)
755 | Decimal64(_, _)
756 | Decimal128(_, _)
757 | Decimal256(_, _) => {
758 self.skip_buffer()?;
759 self.skip_buffer()?;
760 }
761 }
762 Ok(())
763 }
764}
765
766impl RecordBatchDecoder<'_> {
767 fn next_variadic_buffer_count(
773 &self,
774 variadic_counts: &mut VecDeque<i64>,
775 data_type: &DataType,
776 ) -> Result<usize, ArrowError> {
777 let count = variadic_counts.pop_front().ok_or_else(|| {
778 ArrowError::IpcError(format!("Missing variadic count for {data_type} column"))
779 })?;
780
781 let remaining = self.buffers.len();
782
783 usize::try_from(count)
785 .ok()
786 .and_then(|count| count.checked_add(2))
787 .filter(|total| *total <= remaining)
788 .ok_or_else(|| {
789 ArrowError::IpcError(format!(
790 "Invalid variadic count {count} for {data_type} column, \
791 with {remaining} buffer(s) left in the message"
792 ))
793 })
794 }
795}
796
797pub fn read_record_batch(
808 buf: &Buffer,
809 batch: crate::RecordBatch,
810 schema: SchemaRef,
811 dictionaries_by_id: &HashMap<i64, ArrayRef>,
812 projection: Option<&[usize]>,
813 metadata: &MetadataVersion,
814) -> Result<RecordBatch, ArrowError> {
815 RecordBatchDecoder::try_new(buf, batch, schema, dictionaries_by_id, metadata)?
816 .with_projection(projection)
817 .with_require_alignment(false)
818 .read_record_batch()
819}
820
821pub fn read_dictionary(
824 buf: &Buffer,
825 batch: crate::DictionaryBatch,
826 schema: &Schema,
827 dictionaries_by_id: &mut HashMap<i64, ArrayRef>,
828 metadata: &MetadataVersion,
829) -> Result<(), ArrowError> {
830 read_dictionary_impl(
831 buf,
832 batch,
833 schema,
834 dictionaries_by_id,
835 metadata,
836 false,
837 UnsafeFlag::new(),
838 )
839}
840
841pub fn read_dictionary_impl(
843 buf: &Buffer,
844 batch: crate::DictionaryBatch,
845 schema: &Schema,
846 dictionaries_by_id: &mut HashMap<i64, ArrayRef>,
847 metadata: &MetadataVersion,
848 require_alignment: bool,
849 skip_validation: UnsafeFlag,
850) -> Result<(), ArrowError> {
851 let id = batch.id();
852
853 let dictionary_values = get_dictionary_values(
854 buf,
855 batch,
856 schema,
857 dictionaries_by_id,
858 metadata,
859 require_alignment,
860 skip_validation,
861 )?;
862
863 update_dictionaries(dictionaries_by_id, batch.isDelta(), id, dictionary_values)?;
864
865 Ok(())
866}
867
868fn update_dictionaries(
877 dictionaries_by_id: &mut HashMap<i64, ArrayRef>,
878 is_delta: bool,
879 dict_id: i64,
880 dict_values: ArrayRef,
881) -> Result<(), ArrowError> {
882 if !is_delta {
883 dictionaries_by_id.insert(dict_id, dict_values.clone());
887 return Ok(());
888 }
889
890 let existing = dictionaries_by_id.get(&dict_id).ok_or_else(|| {
891 ArrowError::InvalidArgumentError(format!(
892 "No existing dictionary for delta dictionary with id '{dict_id}'"
893 ))
894 })?;
895
896 let combined = concat::concat(&[existing, &dict_values]).map_err(|e| {
897 ArrowError::InvalidArgumentError(format!("Failed to concat delta dictionary: {e}"))
898 })?;
899
900 dictionaries_by_id.insert(dict_id, combined);
901
902 Ok(())
903}
904
905fn first_field_with_dict_id(fields: &Fields, id: i64) -> Option<&Field> {
910 fields
911 .iter()
912 .find_map(|field| field_with_dict_id(field, id))
913}
914
915fn field_with_dict_id(field: &Field, id: i64) -> Option<&Field> {
916 #[expect(deprecated)]
917 let dict_id = field.dict_id();
918 if dict_id == Some(id) {
919 return Some(field);
920 }
921 child_field_with_dict_id(field.data_type(), id)
922}
923
924fn child_field_with_dict_id(data_type: &DataType, id: i64) -> Option<&Field> {
925 match data_type {
926 DataType::Struct(fields) => first_field_with_dict_id(fields, id),
927 DataType::Union(fields, _) => fields.iter().find_map(|(_, f)| field_with_dict_id(f, id)),
928 DataType::List(field)
929 | DataType::LargeList(field)
930 | DataType::ListView(field)
931 | DataType::LargeListView(field)
932 | DataType::FixedSizeList(field, _)
933 | DataType::Map(field, _)
934 | DataType::RunEndEncoded(_, field) => field_with_dict_id(field, id),
935 DataType::Dictionary(_, value_type) => child_field_with_dict_id(value_type, id),
936 _ => None,
937 }
938}
939
940fn get_dictionary_values(
944 buf: &Buffer,
945 batch: crate::DictionaryBatch,
946 schema: &Schema,
947 dictionaries_by_id: &HashMap<i64, ArrayRef>,
948 metadata: &MetadataVersion,
949 require_alignment: bool,
950 skip_validation: UnsafeFlag,
951) -> Result<ArrayRef, ArrowError> {
952 let id = batch.id();
953 let first_field = first_field_with_dict_id(schema.fields(), id).ok_or_else(|| {
954 ArrowError::InvalidArgumentError(format!("dictionary id {id} not found in schema"))
955 })?;
956
957 let dictionary_values: ArrayRef = match first_field.data_type() {
961 DataType::Dictionary(_, value_type) => {
962 let value = value_type.as_ref().clone();
964 let schema = Schema::new(vec![Field::new("", value, true)]);
965 let Some(data) = batch.data() else {
967 return Err(ArrowError::ParseError(
968 "Dictionary batch is missing its data".to_string(),
969 ));
970 };
971 let record_batch = RecordBatchDecoder::try_new(
972 buf,
973 data,
974 Arc::new(schema),
975 dictionaries_by_id,
976 metadata,
977 )?
978 .with_require_alignment(require_alignment)
979 .with_skip_validation(skip_validation)
980 .read_record_batch()?;
981
982 Some(record_batch.column(0).clone())
983 }
984 _ => None,
985 }
986 .ok_or_else(|| {
987 ArrowError::InvalidArgumentError(format!("dictionary id {id} not found in schema"))
988 })?;
989
990 Ok(dictionary_values)
991}
992
993fn read_block<R: Read + Seek>(mut reader: R, block: &Block) -> Result<Buffer, ArrowError> {
995 reader.seek(SeekFrom::Start(block.offset() as u64))?;
996 let body_len = block.bodyLength().to_usize().unwrap();
997 let metadata_len = block.metaDataLength().to_usize().unwrap();
998 let total_len = body_len.checked_add(metadata_len).unwrap();
999
1000 let mut buf = MutableBuffer::try_from_len_zeroed(total_len)
1001 .map_err(|e| ArrowError::MemoryError(e.to_string()))?;
1002 reader.read_exact(&mut buf)?;
1003 Ok(buf.into())
1004}
1005
1006fn check_variadic_counts_consumed(variadic_counts: &VecDeque<i64>) -> Result<(), ArrowError> {
1011 if variadic_counts.is_empty() {
1012 Ok(())
1013 } else {
1014 Err(ArrowError::IpcError(format!(
1015 "Mismatch between schema and data: the IPC message declares {} more variadic \
1016 buffer count(s) than the schema has BinaryView or Utf8View columns",
1017 variadic_counts.len()
1018 )))
1019 }
1020}
1021
1022fn parse_message(buf: &[u8]) -> Result<Message::Message<'_>, ArrowError> {
1026 let buf = match buf[..4] == CONTINUATION_MARKER {
1027 true => &buf[8..],
1028 false => &buf[4..],
1029 };
1030 crate::root_as_message(buf)
1031 .map_err(|err| ArrowError::ParseError(format!("Unable to get root as message: {err:?}")))
1032}
1033
1034pub fn read_footer_length(buf: [u8; 10]) -> Result<usize, ArrowError> {
1038 if buf[4..] != super::ARROW_MAGIC {
1039 return Err(ArrowError::ParseError(
1040 "Arrow file does not contain correct footer".to_string(),
1041 ));
1042 }
1043
1044 let footer_len = i32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
1046 footer_len
1047 .try_into()
1048 .map_err(|_| ArrowError::ParseError(format!("Invalid footer length: {footer_len}")))
1049}
1050
1051#[derive(Debug)]
1116pub struct FileDecoder {
1117 schema: SchemaRef,
1118 dictionaries: HashMap<i64, ArrayRef>,
1119 version: MetadataVersion,
1120 projection: Option<Vec<usize>>,
1121 require_alignment: bool,
1122 skip_validation: UnsafeFlag,
1123}
1124
1125impl FileDecoder {
1126 pub fn new(schema: SchemaRef, version: MetadataVersion) -> Self {
1128 Self {
1129 schema,
1130 version,
1131 dictionaries: Default::default(),
1132 projection: None,
1133 require_alignment: false,
1134 skip_validation: UnsafeFlag::new(),
1135 }
1136 }
1137
1138 pub fn with_projection(mut self, projection: Vec<usize>) -> Self {
1140 self.projection = Some(projection);
1141 self
1142 }
1143
1144 pub fn with_require_alignment(mut self, require_alignment: bool) -> Self {
1157 self.require_alignment = require_alignment;
1158 self
1159 }
1160
1161 pub unsafe fn with_skip_validation(mut self, skip_validation: bool) -> Self {
1172 unsafe { self.skip_validation.set(skip_validation) };
1173 self
1174 }
1175
1176 fn read_message<'a>(&self, buf: &'a [u8]) -> Result<Message::Message<'a>, ArrowError> {
1177 let message = parse_message(buf)?;
1178
1179 if self.version != MetadataVersion::V1 && message.version() != self.version {
1181 return Err(ArrowError::IpcError(
1182 "Could not read IPC message as metadata versions mismatch".to_string(),
1183 ));
1184 }
1185 Ok(message)
1186 }
1187
1188 pub fn read_dictionary(&mut self, block: &Block, buf: &Buffer) -> Result<(), ArrowError> {
1190 let message = self.read_message(buf)?;
1191 match message.header_type() {
1192 crate::MessageHeader::DictionaryBatch => {
1193 let batch = message.header_as_dictionary_batch().unwrap();
1194 read_dictionary_impl(
1195 &buf.slice(block.metaDataLength() as _),
1196 batch,
1197 &self.schema,
1198 &mut self.dictionaries,
1199 &message.version(),
1200 self.require_alignment,
1201 self.skip_validation.clone(),
1202 )
1203 }
1204 t => Err(ArrowError::ParseError(format!(
1205 "Expecting DictionaryBatch in dictionary blocks, found {t:?}."
1206 ))),
1207 }
1208 }
1209
1210 pub fn read_record_batch(
1212 &self,
1213 block: &Block,
1214 buf: &Buffer,
1215 ) -> Result<Option<RecordBatch>, ArrowError> {
1216 let message = self.read_message(buf)?;
1217 match message.header_type() {
1218 crate::MessageHeader::Schema => Err(ArrowError::IpcError(
1219 "Not expecting a schema when messages are read".to_string(),
1220 )),
1221 crate::MessageHeader::RecordBatch => {
1222 let batch = message.header_as_record_batch().ok_or_else(|| {
1223 ArrowError::IpcError("Unable to read IPC message as record batch".to_string())
1224 })?;
1225 RecordBatchDecoder::try_new(
1227 &buf.slice(block.metaDataLength() as _),
1228 batch,
1229 self.schema.clone(),
1230 &self.dictionaries,
1231 &message.version(),
1232 )?
1233 .with_projection(self.projection.as_deref())
1234 .with_require_alignment(self.require_alignment)
1235 .with_skip_validation(self.skip_validation.clone())
1236 .read_record_batch()
1237 .map(Some)
1238 }
1239 crate::MessageHeader::NONE => Ok(None),
1240 t => Err(ArrowError::InvalidArgumentError(format!(
1241 "Reading types other than record batches not yet supported, unable to read {t:?}"
1242 ))),
1243 }
1244 }
1245}
1246
1247#[derive(Debug)]
1249pub struct FileReaderBuilder {
1250 projection: Option<Vec<usize>>,
1252 max_footer_fb_tables: usize,
1254 max_footer_fb_depth: usize,
1256}
1257
1258impl Default for FileReaderBuilder {
1259 fn default() -> Self {
1260 let verifier_options = VerifierOptions::default();
1261 Self {
1262 max_footer_fb_tables: verifier_options.max_tables,
1263 max_footer_fb_depth: verifier_options.max_depth,
1264 projection: None,
1265 }
1266 }
1267}
1268
1269impl FileReaderBuilder {
1270 pub fn new() -> Self {
1274 Self::default()
1275 }
1276
1277 pub fn with_projection(mut self, projection: Vec<usize>) -> Self {
1279 self.projection = Some(projection);
1280 self
1281 }
1282
1283 pub fn with_max_footer_fb_tables(mut self, max_footer_fb_tables: usize) -> Self {
1296 self.max_footer_fb_tables = max_footer_fb_tables;
1297 self
1298 }
1299
1300 pub fn with_max_footer_fb_depth(mut self, max_footer_fb_depth: usize) -> Self {
1313 self.max_footer_fb_depth = max_footer_fb_depth;
1314 self
1315 }
1316
1317 pub fn build<R: Read + Seek>(self, mut reader: R) -> Result<FileReader<R>, ArrowError> {
1319 let mut buffer = [0; 10];
1321 reader.seek(SeekFrom::End(-10))?;
1322 reader.read_exact(&mut buffer)?;
1323
1324 let footer_len = read_footer_length(buffer)?;
1325
1326 let mut footer_data = vec![0; footer_len];
1328 reader.seek(SeekFrom::End(-10 - footer_len as i64))?;
1329 reader.read_exact(&mut footer_data)?;
1330
1331 let verifier_options = VerifierOptions {
1332 max_tables: self.max_footer_fb_tables,
1333 max_depth: self.max_footer_fb_depth,
1334 ..Default::default()
1335 };
1336 let footer = crate::root_as_footer_with_opts(&verifier_options, &footer_data[..]).map_err(
1337 |err| ArrowError::ParseError(format!("Unable to get root as footer: {err:?}")),
1338 )?;
1339
1340 let blocks = footer.recordBatches().ok_or_else(|| {
1341 ArrowError::ParseError("Unable to get record batches from IPC Footer".to_string())
1342 })?;
1343
1344 let total_blocks = blocks.len();
1345
1346 let ipc_schema = footer.schema().ok_or_else(|| {
1347 ArrowError::ParseError("Unable to get schema from IPC Footer".to_string())
1348 })?;
1349 if !ipc_schema.endianness().equals_to_target_endianness() {
1350 return Err(ArrowError::IpcError(
1351 "the endianness of the source system does not match the endianness of the target system.".to_owned()
1352 ));
1353 }
1354
1355 let schema = Arc::new(crate::convert::try_fb_to_schema(ipc_schema)?);
1356
1357 let projected_schema = match &self.projection {
1358 Some(projection) => Arc::new(schema.project(projection)?),
1359 None => schema.clone(),
1360 };
1361
1362 let mut custom_metadata = HashMap::new();
1363 if let Some(fb_custom_metadata) = footer.custom_metadata() {
1364 for kv in fb_custom_metadata {
1365 let (Some(key), Some(value)) = (kv.key(), kv.value()) else {
1366 return Err(ArrowError::ParseError(
1367 "Custom metadata in the IPC footer is missing a key or a value".to_string(),
1368 ));
1369 };
1370 custom_metadata.insert(key.to_string(), value.to_string());
1371 }
1372 }
1373
1374 let mut decoder = FileDecoder::new(schema, footer.version());
1375 if let Some(projection) = self.projection {
1376 decoder = decoder.with_projection(projection)
1377 }
1378
1379 if let Some(dictionaries) = footer.dictionaries() {
1381 for block in dictionaries {
1382 let buf = read_block(&mut reader, block)?;
1383 decoder.read_dictionary(block, &buf)?;
1384 }
1385 }
1386
1387 Ok(FileReader {
1388 reader,
1389 blocks: blocks.iter().copied().collect(),
1390 current_block: 0,
1391 total_blocks,
1392 decoder,
1393 schema: projected_schema,
1394 custom_metadata,
1395 })
1396 }
1397}
1398
1399pub struct FileReader<R> {
1444 reader: R,
1446
1447 decoder: FileDecoder,
1449
1450 schema: SchemaRef,
1452
1453 blocks: Vec<Block>,
1457
1458 current_block: usize,
1460
1461 total_blocks: usize,
1463
1464 custom_metadata: HashMap<String, String>,
1466}
1467
1468impl<R> fmt::Debug for FileReader<R> {
1469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1470 f.debug_struct("FileReader<R>")
1471 .field("decoder", &self.decoder)
1472 .field("blocks", &self.blocks)
1473 .field("current_block", &self.current_block)
1474 .field("total_blocks", &self.total_blocks)
1475 .finish_non_exhaustive()
1476 }
1477}
1478
1479impl<R: Read + Seek> FileReader<BufReader<R>> {
1480 pub fn try_new_buffered(reader: R, projection: Option<Vec<usize>>) -> Result<Self, ArrowError> {
1484 Self::try_new(BufReader::new(reader), projection)
1485 }
1486}
1487
1488impl<R: Read + Seek> FileReader<R> {
1489 pub fn try_new(reader: R, projection: Option<Vec<usize>>) -> Result<Self, ArrowError> {
1501 let builder = FileReaderBuilder {
1502 projection,
1503 ..Default::default()
1504 };
1505 builder.build(reader)
1506 }
1507
1508 pub fn custom_metadata(&self) -> &HashMap<String, String> {
1510 &self.custom_metadata
1511 }
1512
1513 pub fn num_batches(&self) -> usize {
1515 self.total_blocks
1516 }
1517
1518 pub fn schema(&self) -> SchemaRef {
1520 self.schema.clone()
1521 }
1522
1523 pub fn set_index(&mut self, index: usize) -> Result<(), ArrowError> {
1527 if index >= self.total_blocks {
1528 Err(ArrowError::InvalidArgumentError(format!(
1529 "Cannot set batch to index {} from {} total batches",
1530 index, self.total_blocks
1531 )))
1532 } else {
1533 self.current_block = index;
1534 Ok(())
1535 }
1536 }
1537
1538 fn maybe_next(&mut self) -> Result<Option<RecordBatch>, ArrowError> {
1539 let block = &self.blocks[self.current_block];
1540 self.current_block += 1;
1541
1542 let buffer = read_block(&mut self.reader, block)?;
1544 self.decoder.read_record_batch(block, &buffer)
1545 }
1546
1547 pub fn get_ref(&self) -> &R {
1551 &self.reader
1552 }
1553
1554 pub fn get_mut(&mut self) -> &mut R {
1558 &mut self.reader
1559 }
1560
1561 pub unsafe fn with_skip_validation(mut self, skip_validation: bool) -> Self {
1567 self.decoder = unsafe { self.decoder.with_skip_validation(skip_validation) };
1568 self
1569 }
1570}
1571
1572impl<R: Read + Seek> Iterator for FileReader<R> {
1573 type Item = Result<RecordBatch, ArrowError>;
1574
1575 fn next(&mut self) -> Option<Self::Item> {
1576 if self.current_block < self.total_blocks {
1578 self.maybe_next().transpose()
1579 } else {
1580 None
1581 }
1582 }
1583}
1584
1585impl<R: Read + Seek> RecordBatchReader for FileReader<R> {
1586 fn schema(&self) -> SchemaRef {
1587 self.schema()
1588 }
1589}
1590
1591pub struct StreamReader<R> {
1625 reader: MessageReader<R>,
1627
1628 schema: SchemaRef,
1630
1631 dictionaries_by_id: HashMap<i64, ArrayRef>,
1635
1636 finished: bool,
1640
1641 projection: Option<(Vec<usize>, SchemaRef)>,
1643
1644 skip_validation: UnsafeFlag,
1648}
1649
1650impl<R> fmt::Debug for StreamReader<R> {
1651 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
1652 f.debug_struct("StreamReader<R>")
1653 .field("reader", &"R")
1654 .field("schema", &self.schema)
1655 .field("dictionaries_by_id", &self.dictionaries_by_id)
1656 .field("finished", &self.finished)
1657 .field("projection", &self.projection)
1658 .finish()
1659 }
1660}
1661
1662impl<R: Read> StreamReader<BufReader<R>> {
1663 pub fn try_new_buffered(reader: R, projection: Option<Vec<usize>>) -> Result<Self, ArrowError> {
1667 Self::try_new(BufReader::new(reader), projection)
1668 }
1669}
1670
1671impl<R: Read> StreamReader<R> {
1672 pub fn try_new(
1684 reader: R,
1685 projection: Option<Vec<usize>>,
1686 ) -> Result<StreamReader<R>, ArrowError> {
1687 let mut msg_reader = MessageReader::new(reader);
1688 let message = msg_reader.maybe_next()?;
1689 let Some((message, _)) = message else {
1690 return Err(ArrowError::IpcError(
1691 "Expected schema message, found empty stream.".to_string(),
1692 ));
1693 };
1694
1695 if message.header_type() != Message::MessageHeader::Schema {
1696 return Err(ArrowError::IpcError(format!(
1697 "Expected a schema as the first message in the stream, got: {:?}",
1698 message.header_type()
1699 )));
1700 }
1701
1702 let schema = message.header_as_schema().ok_or_else(|| {
1703 ArrowError::ParseError("Failed to parse schema from message header".to_string())
1704 })?;
1705 let schema = crate::convert::try_fb_to_schema(schema)?;
1706
1707 let dictionaries_by_id = HashMap::new();
1709
1710 let projection = match projection {
1711 Some(projection_indices) => {
1712 let schema = Arc::new(schema.project(&projection_indices)?);
1713 Some((projection_indices, schema))
1714 }
1715 _ => None,
1716 };
1717
1718 Ok(Self {
1719 reader: msg_reader,
1720 schema: Arc::new(schema),
1721 finished: false,
1722 dictionaries_by_id,
1723 projection,
1724 skip_validation: UnsafeFlag::new(),
1725 })
1726 }
1727
1728 pub fn schema(&self) -> SchemaRef {
1730 match &self.projection {
1731 Some((_, projected_schema)) => projected_schema.clone(),
1732 None => self.schema.clone(),
1733 }
1734 }
1735
1736 pub fn is_finished(&self) -> bool {
1738 self.finished
1739 }
1740
1741 fn maybe_next(&mut self) -> Result<Option<RecordBatch>, ArrowError> {
1742 if self.finished {
1743 return Ok(None);
1744 }
1745
1746 loop {
1748 let message = self.next_ipc_message()?;
1749 let Some(message) = message else {
1750 self.finished = true;
1752 return Ok(None);
1753 };
1754
1755 match message {
1756 IpcMessage::Schema(_) => {
1757 return Err(ArrowError::IpcError(
1758 "Expected a record batch, but found a schema".to_string(),
1759 ));
1760 }
1761 IpcMessage::RecordBatch(record_batch) => {
1762 return Ok(Some(record_batch));
1763 }
1764 IpcMessage::DictionaryBatch { .. } => {}
1765 }
1766 }
1767 }
1768
1769 pub(crate) fn next_ipc_message(&mut self) -> Result<Option<IpcMessage>, ArrowError> {
1777 let message = self.reader.maybe_next()?;
1778 let Some((message, body)) = message else {
1779 return Ok(None);
1781 };
1782
1783 let ipc_message = match message.header_type() {
1784 Message::MessageHeader::Schema => {
1785 let schema = message.header_as_schema().ok_or_else(|| {
1786 ArrowError::ParseError("Failed to parse schema from message header".to_string())
1787 })?;
1788 let arrow_schema = crate::convert::try_fb_to_schema(schema)?;
1789 IpcMessage::Schema(arrow_schema)
1790 }
1791 Message::MessageHeader::RecordBatch => {
1792 let batch = message.header_as_record_batch().ok_or_else(|| {
1793 ArrowError::IpcError("Unable to read IPC message as record batch".to_string())
1794 })?;
1795
1796 let version = message.version();
1797 let schema = self.schema.clone();
1798 let record_batch = RecordBatchDecoder::try_new(
1799 &body.into(),
1800 batch,
1801 schema,
1802 &self.dictionaries_by_id,
1803 &version,
1804 )?
1805 .with_projection(self.projection.as_ref().map(|x| x.0.as_ref()))
1806 .with_require_alignment(false)
1807 .with_skip_validation(self.skip_validation.clone())
1808 .read_record_batch()?;
1809 IpcMessage::RecordBatch(record_batch)
1810 }
1811 Message::MessageHeader::DictionaryBatch => {
1812 let dict = message.header_as_dictionary_batch().ok_or_else(|| {
1813 ArrowError::ParseError(
1814 "Failed to parse dictionary batch from message header".to_string(),
1815 )
1816 })?;
1817
1818 let version = message.version();
1819 let dict_values = get_dictionary_values(
1820 &body.into(),
1821 dict,
1822 &self.schema,
1823 &self.dictionaries_by_id,
1824 &version,
1825 false,
1826 self.skip_validation.clone(),
1827 )?;
1828
1829 update_dictionaries(
1830 &mut self.dictionaries_by_id,
1831 dict.isDelta(),
1832 dict.id(),
1833 dict_values.clone(),
1834 )?;
1835
1836 IpcMessage::DictionaryBatch {
1837 id: dict.id(),
1838 is_delta: (dict.isDelta()),
1839 values: (dict_values),
1840 }
1841 }
1842 x => {
1843 return Err(ArrowError::ParseError(format!(
1844 "Unsupported message header type in IPC stream: '{x:?}'"
1845 )));
1846 }
1847 };
1848
1849 Ok(Some(ipc_message))
1850 }
1851
1852 pub fn get_ref(&self) -> &R {
1856 self.reader.inner()
1857 }
1858
1859 pub fn get_mut(&mut self) -> &mut R {
1863 self.reader.inner_mut()
1864 }
1865
1866 pub unsafe fn with_skip_validation(mut self, skip_validation: bool) -> Self {
1872 unsafe { self.skip_validation.set(skip_validation) };
1873 self
1874 }
1875}
1876
1877impl<R: Read> Iterator for StreamReader<R> {
1878 type Item = Result<RecordBatch, ArrowError>;
1879
1880 fn next(&mut self) -> Option<Self::Item> {
1881 self.maybe_next().transpose()
1882 }
1883}
1884
1885impl<R: Read> RecordBatchReader for StreamReader<R> {
1886 fn schema(&self) -> SchemaRef {
1887 self.schema()
1888 }
1889}
1890
1891#[derive(Debug)]
1897#[expect(dead_code)]
1898pub(crate) enum IpcMessage {
1899 Schema(arrow_schema::Schema),
1900 RecordBatch(RecordBatch),
1901 DictionaryBatch {
1902 id: i64,
1903 is_delta: bool,
1904 values: ArrayRef,
1905 },
1906}
1907
1908pub(crate) const MAX_PREALLOC_BYTES: usize = 64 * 1024 * 1024;
1924
1925fn read_body_bounded<R: Read>(reader: &mut R, len: usize) -> Result<MutableBuffer, ArrowError> {
1927 let mut buf = MutableBuffer::try_from_len_zeroed(len.min(MAX_PREALLOC_BYTES))
1928 .map_err(|e| ArrowError::MemoryError(e.to_string()))?;
1929 let mut filled = 0;
1930 while filled < len {
1931 let target = buf.len();
1932 reader.read_exact(&mut buf.as_slice_mut()[filled..target])?;
1933 filled = target;
1934 if filled < len {
1935 buf.try_resize(len.min(target.saturating_mul(2)), 0)
1936 .map_err(|e| ArrowError::MemoryError(e.to_string()))?;
1937 }
1938 }
1939 Ok(buf)
1940}
1941
1942struct MessageReader<R> {
1945 reader: R,
1946 buf: Vec<u8>,
1947}
1948
1949impl<R: Read> MessageReader<R> {
1950 fn new(reader: R) -> Self {
1951 Self {
1952 reader,
1953 buf: Vec::new(),
1954 }
1955 }
1956
1957 fn maybe_next(&mut self) -> Result<Option<(Message::Message<'_>, MutableBuffer)>, ArrowError> {
1968 let meta_len = self.read_meta_len()?;
1969 let Some(meta_len) = meta_len else {
1970 return Ok(None);
1971 };
1972
1973 self.buf.clear();
1976 let read = (&mut self.reader)
1977 .take(meta_len as u64)
1978 .read_to_end(&mut self.buf)?;
1979 if read != meta_len {
1980 return Err(ArrowError::ParseError(format!(
1981 "Unexpected end of stream: expected {meta_len} metadata bytes, got {read}"
1982 )));
1983 }
1984
1985 let message = crate::root_as_message(self.buf.as_slice()).map_err(|err| {
1986 ArrowError::ParseError(format!("Unable to get root as message: {err:?}"))
1987 })?;
1988
1989 let body_len = usize::try_from(message.bodyLength()).map_err(|_| {
1990 ArrowError::ParseError(format!(
1991 "Invalid IPC message body length: {}",
1992 message.bodyLength()
1993 ))
1994 })?;
1995 let buf = read_body_bounded(&mut self.reader, body_len)?;
1996
1997 Ok(Some((message, buf)))
1998 }
1999
2000 fn inner_mut(&mut self) -> &mut R {
2002 &mut self.reader
2003 }
2004
2005 fn inner(&self) -> &R {
2007 &self.reader
2008 }
2009
2010 pub fn read_meta_len(&mut self) -> Result<Option<usize>, ArrowError> {
2019 let mut meta_len: [u8; 4] = [0; 4];
2020 match self.reader.read_exact(&mut meta_len) {
2021 Ok(()) => {}
2022 Err(e) => {
2023 return if e.kind() == std::io::ErrorKind::UnexpectedEof {
2024 Ok(None)
2028 } else {
2029 Err(ArrowError::from(e))
2030 };
2031 }
2032 }
2033
2034 let meta_len = {
2035 if meta_len == CONTINUATION_MARKER {
2038 self.reader.read_exact(&mut meta_len)?;
2039 }
2040
2041 i32::from_le_bytes(meta_len)
2042 };
2043
2044 if meta_len == 0 {
2045 return Ok(None);
2046 }
2047
2048 let meta_len = usize::try_from(meta_len)
2049 .map_err(|_| ArrowError::ParseError(format!("Invalid metadata length: {meta_len}")))?;
2050
2051 Ok(Some(meta_len))
2052 }
2053}
2054
2055#[cfg(test)]
2056mod tests {
2057 use std::io::Cursor;
2058
2059 use crate::convert::try_fb_to_schema;
2060 use crate::writer::{
2061 DictionaryTracker, IpcDataGenerator, IpcWriteOptions, unslice_run_array, write_message,
2062 };
2063
2064 use super::*;
2065
2066 use crate::{root_as_footer, root_as_message, size_prefixed_root_as_message};
2067 use arrow_array::builder::{PrimitiveRunBuilder, UnionBuilder};
2068 use arrow_array::types::*;
2069 use arrow_buffer::{NullBuffer, OffsetBuffer};
2070 use arrow_data::ArrayDataBuilder;
2071
2072 fn create_test_projection_schema() -> Schema {
2073 let list_data_type = DataType::List(Arc::new(Field::new_list_field(DataType::Int32, true)));
2075
2076 let fixed_size_list_data_type =
2077 DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 3);
2078
2079 let union_fields = UnionFields::from_fields(vec![
2080 Field::new("a", DataType::Int32, false),
2081 Field::new("b", DataType::Float64, false),
2082 ]);
2083
2084 let union_data_type = DataType::Union(union_fields, UnionMode::Dense);
2085
2086 let struct_fields = Fields::from(vec![
2087 Field::new("id", DataType::Int32, false),
2088 Field::new_list("list", Field::new_list_field(DataType::Int8, true), false),
2089 ]);
2090 let struct_data_type = DataType::Struct(struct_fields);
2091
2092 let run_encoded_data_type = DataType::RunEndEncoded(
2093 Arc::new(Field::new(
2094 Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
2095 DataType::Int16,
2096 false,
2097 )),
2098 Arc::new(Field::new(
2099 Field::REE_VALUES_FIELD_DEFAULT_NAME,
2100 DataType::Int32,
2101 true,
2102 )),
2103 );
2104
2105 Schema::new(vec![
2107 Field::new("f0", DataType::UInt32, false),
2108 Field::new("f1", DataType::Utf8, false),
2109 Field::new("f2", DataType::Boolean, false),
2110 Field::new("f3", union_data_type, true),
2111 Field::new("f4", DataType::Null, true),
2112 Field::new("f5", DataType::Float64, true),
2113 Field::new("f6", list_data_type, false),
2114 Field::new("f7", DataType::FixedSizeBinary(3), true),
2115 Field::new("f8", fixed_size_list_data_type, false),
2116 Field::new("f9", struct_data_type, false),
2117 Field::new("f10", run_encoded_data_type, false),
2118 Field::new("f11", DataType::Boolean, false),
2119 Field::new_dictionary("f12", DataType::Int8, DataType::Utf8, false),
2120 Field::new("f13", DataType::Utf8, false),
2121 ])
2122 }
2123
2124 fn create_test_projection_batch_data(schema: &Schema) -> RecordBatch {
2125 let array0 = UInt32Array::from(vec![1, 2, 3]);
2127 let array1 = StringArray::from(vec!["foo", "bar", "baz"]);
2128 let array2 = BooleanArray::from(vec![true, false, true]);
2129
2130 let mut union_builder = UnionBuilder::new_dense();
2131 union_builder.append::<Int32Type>("a", 1).unwrap();
2132 union_builder.append::<Float64Type>("b", 10.1).unwrap();
2133 union_builder.append_null::<Float64Type>("b").unwrap();
2134 let array3 = union_builder.build().unwrap();
2135
2136 let array4 = NullArray::new(3);
2137 let array5 = Float64Array::from(vec![Some(1.1), None, Some(3.3)]);
2138 let array6_values = vec![
2139 Some(vec![Some(10), Some(10), Some(10)]),
2140 Some(vec![Some(20), Some(20), Some(20)]),
2141 Some(vec![Some(30), Some(30)]),
2142 ];
2143 let array6 = ListArray::from_iter_primitive::<Int32Type, _, _>(array6_values);
2144 let array7_values = vec![vec![11, 12, 13], vec![22, 23, 24], vec![33, 34, 35]];
2145 let array7 = FixedSizeBinaryArray::try_from_iter(array7_values.into_iter()).unwrap();
2146
2147 let array8_values = ArrayData::builder(DataType::Int32)
2148 .len(9)
2149 .add_buffer(Buffer::from_slice_ref([40, 41, 42, 43, 44, 45, 46, 47, 48]))
2150 .build()
2151 .unwrap();
2152 let array8_data = ArrayData::builder(schema.field(8).data_type().clone())
2153 .len(3)
2154 .add_child_data(array8_values)
2155 .build()
2156 .unwrap();
2157 let array8 = FixedSizeListArray::from(array8_data);
2158
2159 let array9_id: ArrayRef = Arc::new(Int32Array::from(vec![1001, 1002, 1003]));
2160 let array9_list: ArrayRef =
2161 Arc::new(ListArray::from_iter_primitive::<Int8Type, _, _>(vec![
2162 Some(vec![Some(-10)]),
2163 Some(vec![Some(-20), Some(-20), Some(-20)]),
2164 Some(vec![Some(-30)]),
2165 ]));
2166 let array9 = ArrayDataBuilder::new(schema.field(9).data_type().clone())
2167 .add_child_data(array9_id.into_data())
2168 .add_child_data(array9_list.into_data())
2169 .len(3)
2170 .build()
2171 .unwrap();
2172 let array9 = StructArray::from(array9);
2173
2174 let array10_input = vec![Some(1_i32), None, None];
2175 let mut array10_builder = PrimitiveRunBuilder::<Int16Type, Int32Type>::new();
2176 array10_builder.extend(array10_input);
2177 let array10 = array10_builder.finish();
2178
2179 let array11 = BooleanArray::from(vec![false, false, true]);
2180
2181 let array12_values = StringArray::from(vec!["x", "yy", "zzz"]);
2182 let array12_keys = Int8Array::from_iter_values([1, 1, 2]);
2183 let array12 = DictionaryArray::new(array12_keys, Arc::new(array12_values));
2184
2185 let array13 = StringArray::from(vec!["a", "bb", "ccc"]);
2186
2187 RecordBatch::try_new(
2189 Arc::new(schema.clone()),
2190 vec![
2191 Arc::new(array0),
2192 Arc::new(array1),
2193 Arc::new(array2),
2194 Arc::new(array3),
2195 Arc::new(array4),
2196 Arc::new(array5),
2197 Arc::new(array6),
2198 Arc::new(array7),
2199 Arc::new(array8),
2200 Arc::new(array9),
2201 Arc::new(array10),
2202 Arc::new(array11),
2203 Arc::new(array12),
2204 Arc::new(array13),
2205 ],
2206 )
2207 .unwrap()
2208 }
2209
2210 #[test]
2211 fn test_negative_meta_len_start_stream() {
2212 let bytes = i32::to_le_bytes(-1);
2213 let mut buf = vec![];
2214 buf.extend(CONTINUATION_MARKER);
2215 buf.extend(bytes);
2216
2217 let reader_err = StreamReader::try_new(Cursor::new(buf), None).err();
2218 assert!(reader_err.is_some());
2219 assert_eq!(
2220 reader_err.unwrap().to_string(),
2221 "Parser error: Invalid metadata length: -1"
2222 );
2223 }
2224
2225 #[test]
2226 fn test_negative_meta_len_mid_stream() {
2227 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
2228 let mut buf = Vec::new();
2229 {
2230 let mut writer = crate::writer::StreamWriter::try_new(&mut buf, &schema).unwrap();
2231 let batch =
2232 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(Int32Array::from(vec![1]))])
2233 .unwrap();
2234 writer.write(&batch).unwrap();
2235 }
2236
2237 let bytes = i32::to_le_bytes(-1);
2238 buf.extend(CONTINUATION_MARKER);
2239 buf.extend(bytes);
2240
2241 let mut reader = StreamReader::try_new(Cursor::new(buf), None).unwrap();
2242 assert!(reader.maybe_next().is_ok());
2244 let batch_err = reader.maybe_next().err();
2246 assert!(batch_err.is_some());
2247 assert_eq!(
2248 batch_err.unwrap().to_string(),
2249 "Parser error: Invalid metadata length: -1"
2250 );
2251 }
2252
2253 #[test]
2254 fn test_invalid_dictionary_batch_without_data() {
2255 use crate::r#gen::Message::*;
2256 use flatbuffers::FlatBufferBuilder;
2257
2258 let schema = Schema::new(vec![Field::new(
2259 "col",
2260 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8)),
2261 true,
2262 )]);
2263
2264 let mut fbb = FlatBufferBuilder::new();
2267 let batch_offset = DictionaryBatch::create(
2268 &mut fbb,
2269 &DictionaryBatchArgs {
2270 id: 0,
2271 data: None,
2272 isDelta: false,
2273 },
2274 );
2275 fbb.finish_minimal(batch_offset);
2276 let batch_bytes = fbb.finished_data().to_vec();
2277 let batch = flatbuffers::root::<DictionaryBatch>(&batch_bytes).unwrap();
2278
2279 let data_buffer = Buffer::from(vec![0u8; 0]);
2280 let mut dictionaries: HashMap<i64, ArrayRef> = HashMap::new();
2281
2282 let err = read_dictionary(
2283 &data_buffer,
2284 batch,
2285 &schema,
2286 &mut dictionaries,
2287 &MetadataVersion::V5,
2288 )
2289 .unwrap_err();
2290
2291 assert_eq!(
2292 err.to_string(),
2293 "Parser error: Dictionary batch is missing its data"
2294 );
2295 }
2296
2297 #[test]
2298 fn test_missing_buffer_metadata_error() {
2299 use crate::r#gen::Message::*;
2300 use flatbuffers::FlatBufferBuilder;
2301
2302 let schema = Arc::new(Schema::new(vec![Field::new("col", DataType::Int32, true)]));
2303
2304 let mut fbb = FlatBufferBuilder::new();
2307 let nodes = fbb.create_vector(&[FieldNode::new(2, 0)]);
2308 let buffers = fbb.create_vector(&[crate::Buffer::new(0, 8)]);
2309 let batch_offset = RecordBatch::create(
2310 &mut fbb,
2311 &RecordBatchArgs {
2312 length: 2,
2313 nodes: Some(nodes),
2314 buffers: Some(buffers),
2315 compression: None,
2316 variadicBufferCounts: None,
2317 },
2318 );
2319 fbb.finish_minimal(batch_offset);
2320 let batch_bytes = fbb.finished_data().to_vec();
2321 let batch = flatbuffers::root::<RecordBatch>(&batch_bytes).unwrap();
2322
2323 let data_buffer = Buffer::from(vec![0u8; 8]);
2324 let dictionaries: HashMap<i64, ArrayRef> = HashMap::new();
2325 let metadata = MetadataVersion::V5;
2326
2327 let decoder = RecordBatchDecoder::try_new(
2328 &data_buffer,
2329 batch,
2330 schema.clone(),
2331 &dictionaries,
2332 &metadata,
2333 )
2334 .unwrap();
2335
2336 let result = decoder.read_record_batch();
2337
2338 match result {
2339 Err(ArrowError::IpcError(msg)) => {
2340 assert_eq!(msg, "Buffer count mismatched with metadata");
2341 }
2342 other => panic!("unexpected error: {other:?}"),
2343 }
2344 }
2345
2346 #[test]
2347 fn test_read_record_batch_buffer_out_of_bounds() {
2348 use crate::r#gen::Message::*;
2349 use flatbuffers::FlatBufferBuilder;
2350
2351 let schema = Arc::new(Schema::new(vec![Field::new("col", DataType::Int32, false)]));
2352 let body = Buffer::from(vec![0u8; 8]);
2353 let mut fbb = FlatBufferBuilder::new();
2354 let nodes = fbb.create_vector(&[FieldNode::new(2, 0)]);
2355 let buffers = fbb.create_vector(&[crate::Buffer::new(0, 0), crate::Buffer::new(4, 8)]);
2357 let batch = RecordBatch::create(
2358 &mut fbb,
2359 &RecordBatchArgs {
2360 length: 2,
2361 nodes: Some(nodes),
2362 buffers: Some(buffers),
2363 ..Default::default()
2364 },
2365 );
2366 fbb.finish_minimal(batch);
2367 let batch = flatbuffers::root::<RecordBatch>(fbb.finished_data()).unwrap();
2368
2369 read_record_batch(
2370 &body,
2371 batch,
2372 schema,
2373 &HashMap::new(),
2374 None,
2375 &MetadataVersion::V5,
2376 )
2377 .expect_err("invalid IPC buffer range must return an error");
2378 }
2379
2380 #[test]
2381 fn test_union_short_buffers() {
2382 use crate::r#gen::Message::*;
2383
2384 let fields = UnionFields::from_fields([Field::new("child", DataType::Null, true)]);
2385 let cases = [
2388 (UnionMode::Sparse, vec![crate::Buffer::new(0, 1)]),
2389 (
2390 UnionMode::Dense,
2391 vec![crate::Buffer::new(0, 2), crate::Buffer::new(8, 4)],
2392 ),
2393 ];
2394 for (mode, buffers) in cases {
2395 let schema = Arc::new(Schema::new(vec![Field::new(
2396 "union",
2397 DataType::Union(fields.clone(), mode),
2398 false,
2399 )]));
2400
2401 let mut fbb = flatbuffers::FlatBufferBuilder::new();
2402 let nodes = fbb.create_vector(&[FieldNode::new(2, 0), FieldNode::new(2, 2)]);
2403 let buffers = fbb.create_vector(&buffers);
2404 let batch = RecordBatch::create(
2405 &mut fbb,
2406 &RecordBatchArgs {
2407 length: 2,
2408 nodes: Some(nodes),
2409 buffers: Some(buffers),
2410 ..Default::default()
2411 },
2412 );
2413 fbb.finish_minimal(batch);
2414 let batch = flatbuffers::root::<RecordBatch>(fbb.finished_data()).unwrap();
2415
2416 read_record_batch(
2417 &Buffer::from(vec![0u8; 16]),
2418 batch,
2419 schema,
2420 &HashMap::new(),
2421 None,
2422 &MetadataVersion::V5,
2423 )
2424 .expect_err("a short union buffer should return an error");
2425 }
2426 }
2427
2428 #[test]
2429 fn test_offsets_buffer_with_trailing_bytes() {
2430 use crate::r#gen::Message::*;
2431 use flatbuffers::FlatBufferBuilder;
2432
2433 let schema = Arc::new(Schema::new(vec![Field::new("col", DataType::Utf8, false)]));
2434
2435 let mut fbb = FlatBufferBuilder::new();
2439 let nodes = fbb.create_vector(&[FieldNode::new(2, 0)]);
2440 let buffers = fbb.create_vector(&[
2441 crate::Buffer::new(0, 0),
2442 crate::Buffer::new(0, 14),
2443 crate::Buffer::new(16, 5),
2444 ]);
2445 let batch_offset = RecordBatch::create(
2446 &mut fbb,
2447 &RecordBatchArgs {
2448 length: 2,
2449 nodes: Some(nodes),
2450 buffers: Some(buffers),
2451 compression: None,
2452 variadicBufferCounts: None,
2453 },
2454 );
2455 fbb.finish_minimal(batch_offset);
2456 let batch_bytes = fbb.finished_data().to_vec();
2457 let batch = flatbuffers::root::<RecordBatch>(&batch_bytes).unwrap();
2458
2459 let mut body = Buffer::from_slice_ref([0_i32, 2, 5]).as_slice().to_vec();
2460 body.extend_from_slice(&[0; 4]);
2461 body.extend_from_slice(b"hello\0\0\0");
2462 let data_buffer = Buffer::from_slice_ref(&body);
2463 let dictionaries: HashMap<i64, ArrayRef> = HashMap::new();
2464 let metadata = MetadataVersion::V5;
2465
2466 let decoder =
2467 RecordBatchDecoder::try_new(&data_buffer, batch, schema, &dictionaries, &metadata)
2468 .unwrap();
2469 let batch = decoder.read_record_batch().unwrap();
2470 let col = batch.column(0).as_any().downcast_ref::<StringArray>();
2471 assert_eq!(col.unwrap(), &StringArray::from(vec!["he", "llo"]));
2472 }
2473
2474 fn read_batch_with_variadic_count(count: i64, projection: Option<&[usize]>) -> ArrowError {
2477 use crate::r#gen::Message::*;
2478 use flatbuffers::FlatBufferBuilder;
2479
2480 let schema = Arc::new(Schema::new(vec![Field::new(
2481 "col",
2482 DataType::Utf8View,
2483 true,
2484 )]));
2485
2486 let mut fbb = FlatBufferBuilder::new();
2487 let nodes = fbb.create_vector(&[FieldNode::new(1, 0)]);
2488 let buffers = fbb.create_vector(&[crate::Buffer::new(0, 8), crate::Buffer::new(8, 8)]);
2489 let variadic_buffer_counts = fbb.create_vector(&[count]);
2490 let batch_offset = RecordBatch::create(
2491 &mut fbb,
2492 &RecordBatchArgs {
2493 length: 1,
2494 nodes: Some(nodes),
2495 buffers: Some(buffers),
2496 compression: None,
2497 variadicBufferCounts: Some(variadic_buffer_counts),
2498 },
2499 );
2500 fbb.finish_minimal(batch_offset);
2501 let batch_bytes = fbb.finished_data().to_vec();
2502 let batch = flatbuffers::root::<RecordBatch>(&batch_bytes).unwrap();
2503
2504 let data_buffer = Buffer::from(vec![0u8; 16]);
2505 let dictionaries: HashMap<i64, ArrayRef> = HashMap::new();
2506
2507 RecordBatchDecoder::try_new(
2508 &data_buffer,
2509 batch,
2510 schema,
2511 &dictionaries,
2512 &MetadataVersion::V5,
2513 )
2514 .unwrap()
2515 .with_projection(projection)
2516 .read_record_batch()
2517 .expect_err("should get error")
2518 }
2519
2520 #[test]
2523 fn test_invalid_variadic_buffer_count_error() {
2524 for count in [-2, -1, 1, i64::MAX] {
2527 for projection in [None, Some([].as_slice())] {
2528 let err = read_batch_with_variadic_count(count, projection);
2529 assert_eq!(
2530 err.to_string(),
2531 format!(
2532 "Ipc error: Invalid variadic count {count} for Utf8View column, \
2533 with 2 buffer(s) left in the message"
2534 ),
2535 "count {count}, projection {projection:?}"
2536 );
2537 }
2538 }
2539 }
2540
2541 #[test]
2543 fn test_valid_variadic_buffer_count_is_accepted() {
2544 let err = read_batch_with_variadic_count(0, None);
2545 assert!(!err.to_string().contains("Invalid variadic count"), "{err}");
2546 }
2547
2548 #[test]
2549 fn test_missing_footer_schema_error() {
2550 use crate::r#gen::File::{Footer, FooterArgs};
2551 use flatbuffers::FlatBufferBuilder;
2552
2553 let mut fbb = FlatBufferBuilder::new();
2556 let record_batches = fbb.create_vector::<Block>(&[]);
2557 let footer = Footer::create(
2558 &mut fbb,
2559 &FooterArgs {
2560 version: MetadataVersion::V5,
2561 schema: None,
2562 dictionaries: None,
2563 recordBatches: Some(record_batches),
2564 custom_metadata: None,
2565 },
2566 );
2567 fbb.finish(footer, None);
2568 let footer_data = fbb.finished_data();
2569
2570 let mut buf = Vec::new();
2572 buf.extend_from_slice(&crate::ARROW_MAGIC);
2573 buf.extend_from_slice(footer_data);
2574 buf.extend_from_slice(&(footer_data.len() as i32).to_le_bytes());
2575 buf.extend_from_slice(&crate::ARROW_MAGIC);
2576
2577 let err = FileReader::try_new(Cursor::new(buf), None)
2578 .expect_err("expected an error, not a panic");
2579 assert!(
2580 matches!(err, ArrowError::ParseError(_)),
2581 "expected ParseError, got {err:?}"
2582 );
2583 }
2584
2585 #[test]
2587 fn test_read_legacy_empty_list_without_offsets_buffer() {
2588 use crate::r#gen::Message::*;
2589 use flatbuffers::FlatBufferBuilder;
2590
2591 let schema = Arc::new(Schema::new(vec![Field::new_list(
2592 "items",
2593 Field::new_list_field(DataType::Int32, true),
2594 true,
2595 )]));
2596
2597 let mut fbb = FlatBufferBuilder::new();
2600 let nodes = fbb.create_vector(&[
2601 FieldNode::new(0, 0), FieldNode::new(0, 0), ]);
2604 let buffers = fbb.create_vector(&[
2605 crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), ]);
2610 let batch_offset = RecordBatch::create(
2611 &mut fbb,
2612 &RecordBatchArgs {
2613 length: 0,
2614 nodes: Some(nodes),
2615 buffers: Some(buffers),
2616 compression: None,
2617 variadicBufferCounts: None,
2618 },
2619 );
2620 fbb.finish_minimal(batch_offset);
2621 let batch_bytes = fbb.finished_data().to_vec();
2622 let batch = flatbuffers::root::<RecordBatch>(&batch_bytes).unwrap();
2623
2624 let body = Buffer::from(Vec::<u8>::new());
2625 let dictionaries: HashMap<i64, ArrayRef> = HashMap::new();
2626 let metadata = MetadataVersion::V5;
2627
2628 let decoder =
2629 RecordBatchDecoder::try_new(&body, batch, schema.clone(), &dictionaries, &metadata)
2630 .unwrap();
2631
2632 let read_batch = decoder.read_record_batch().unwrap();
2633 assert_eq!(read_batch.num_rows(), 0);
2634
2635 let list = read_batch
2636 .column(0)
2637 .as_any()
2638 .downcast_ref::<ListArray>()
2639 .unwrap();
2640 assert_eq!(list.len(), 0);
2641 assert_eq!(list.values().len(), 0);
2642 }
2643
2644 #[test]
2646 fn test_read_legacy_empty_utf8_and_binary_without_offsets_buffer() {
2647 use crate::r#gen::Message::*;
2648 use flatbuffers::FlatBufferBuilder;
2649
2650 let schema = Arc::new(Schema::new(vec![
2651 Field::new("name", DataType::Utf8, true),
2652 Field::new("payload", DataType::Binary, true),
2653 ]));
2654
2655 let mut fbb = FlatBufferBuilder::new();
2658 let nodes = fbb.create_vector(&[
2659 FieldNode::new(0, 0), FieldNode::new(0, 0), ]);
2662 let buffers = fbb.create_vector(&[
2663 crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), crate::Buffer::new(0, 0), ]);
2670 let batch_offset = RecordBatch::create(
2671 &mut fbb,
2672 &RecordBatchArgs {
2673 length: 0,
2674 nodes: Some(nodes),
2675 buffers: Some(buffers),
2676 compression: None,
2677 variadicBufferCounts: None,
2678 },
2679 );
2680 fbb.finish_minimal(batch_offset);
2681 let batch_bytes = fbb.finished_data().to_vec();
2682 let batch = flatbuffers::root::<RecordBatch>(&batch_bytes).unwrap();
2683
2684 let body = Buffer::from(Vec::<u8>::new());
2685 let dictionaries: HashMap<i64, ArrayRef> = HashMap::new();
2686 let metadata = MetadataVersion::V5;
2687
2688 let decoder =
2689 RecordBatchDecoder::try_new(&body, batch, schema.clone(), &dictionaries, &metadata)
2690 .unwrap();
2691
2692 let read_batch = decoder.read_record_batch().unwrap();
2693 assert_eq!(read_batch.num_rows(), 0);
2694
2695 let utf8 = read_batch
2696 .column(0)
2697 .as_any()
2698 .downcast_ref::<StringArray>()
2699 .unwrap();
2700 assert_eq!(utf8.len(), 0);
2701 assert_eq!(utf8.value_offsets(), [0]);
2702
2703 let binary = read_batch
2704 .column(1)
2705 .as_any()
2706 .downcast_ref::<BinaryArray>()
2707 .unwrap();
2708 assert_eq!(binary.len(), 0);
2709 assert_eq!(binary.value_offsets(), [0]);
2710 }
2711
2712 #[test]
2713 fn test_projection_array_values() {
2714 let schema = create_test_projection_schema();
2716
2717 let batch = create_test_projection_batch_data(&schema);
2719
2720 let mut buf = Vec::new();
2722 {
2723 let mut writer = crate::writer::FileWriter::try_new(&mut buf, &schema).unwrap();
2724 writer.write(&batch).unwrap();
2725 writer.finish().unwrap();
2726 }
2727
2728 for index in 0..12 {
2730 let projection = vec![index];
2731 let reader = FileReader::try_new(std::io::Cursor::new(buf.clone()), Some(projection));
2732 let read_batch = reader.unwrap().next().unwrap().unwrap();
2733 let projected_column = read_batch.column(0);
2734 let expected_column = batch.column(index);
2735
2736 assert_eq!(projected_column.as_ref(), expected_column.as_ref());
2738 }
2739
2740 {
2741 let reader =
2743 FileReader::try_new(std::io::Cursor::new(buf.clone()), Some(vec![3, 2, 1]));
2744 let read_batch = reader.unwrap().next().unwrap().unwrap();
2745 let expected_batch = batch.project(&[3, 2, 1]).unwrap();
2746 assert_eq!(read_batch, expected_batch);
2747 }
2748 }
2749
2750 #[test]
2751 fn test_file_reader_projected_schema_matches_batch_schema() {
2752 let schema = create_test_projection_schema();
2753 let batch = create_test_projection_batch_data(&schema);
2754
2755 let mut buf = Vec::new();
2756 {
2757 let mut writer = crate::writer::FileWriter::try_new(&mut buf, &schema).unwrap();
2758 writer.write(&batch).unwrap();
2759 writer.finish().unwrap();
2760 }
2761
2762 let projection = vec![3, 2, 1];
2763 let mut reader = FileReader::try_new(Cursor::new(buf), Some(projection)).unwrap();
2764 let reader_schema = RecordBatchReader::schema(&reader);
2765 let read_batch = reader.next().unwrap().unwrap();
2766
2767 assert_eq!(reader_schema, read_batch.schema());
2768 }
2769
2770 #[test]
2771 fn test_stream_reader_projected_schema_matches_batch_schema() {
2772 let schema = create_test_projection_schema();
2773 let batch = create_test_projection_batch_data(&schema);
2774
2775 let mut buf = Vec::new();
2776 {
2777 let mut writer = crate::writer::StreamWriter::try_new(&mut buf, &schema).unwrap();
2778 writer.write(&batch).unwrap();
2779 writer.finish().unwrap();
2780 }
2781
2782 let projection = vec![3, 2, 1];
2783 let mut reader = StreamReader::try_new(Cursor::new(buf), Some(projection)).unwrap();
2784 let reader_schema = RecordBatchReader::schema(&reader);
2785 let read_batch = reader.next().unwrap().unwrap();
2786
2787 assert_eq!(reader_schema, read_batch.schema());
2788 }
2789
2790 #[test]
2791 fn test_file_reader_rejects_invalid_projection() {
2792 let schema = create_test_projection_schema();
2793 let batch = create_test_projection_batch_data(&schema);
2794
2795 let mut buf = Vec::new();
2796 {
2797 let mut writer = crate::writer::FileWriter::try_new(&mut buf, &schema).unwrap();
2798 writer.write(&batch).unwrap();
2799 writer.finish().unwrap();
2800 }
2801
2802 let result = FileReader::try_new(Cursor::new(buf), Some(vec![schema.fields().len()]));
2803
2804 assert!(matches!(result, Err(ArrowError::SchemaError(_))));
2805 }
2806
2807 #[test]
2808 fn test_projection_duplicate_indices() {
2809 let schema = create_test_projection_schema();
2810 let batch = create_test_projection_batch_data(&schema);
2811
2812 let mut buf = Vec::new();
2814 {
2815 let mut writer = crate::writer::FileWriter::try_new(&mut buf, &schema).unwrap();
2816 writer.write(&batch).unwrap();
2817 writer.finish().unwrap();
2818 }
2819
2820 for projection in [vec![1, 1], vec![2, 0, 2]] {
2822 let reader =
2823 FileReader::try_new(std::io::Cursor::new(buf.clone()), Some(projection.clone()));
2824 let read_batch = reader.unwrap().next().unwrap().unwrap();
2825
2826 let expected_batch = batch.project(&projection).unwrap();
2827 assert_eq!(read_batch, expected_batch);
2828 }
2829 }
2830
2831 #[test]
2832 fn test_arrow_single_float_row() {
2833 let schema = Schema::new(vec![
2834 Field::new("a", DataType::Float32, false),
2835 Field::new("b", DataType::Float32, false),
2836 Field::new("c", DataType::Int32, false),
2837 Field::new("d", DataType::Int32, false),
2838 ]);
2839 let arrays = vec![
2840 Arc::new(Float32Array::from(vec![1.23])) as ArrayRef,
2841 Arc::new(Float32Array::from(vec![-6.50])) as ArrayRef,
2842 Arc::new(Int32Array::from(vec![2])) as ArrayRef,
2843 Arc::new(Int32Array::from(vec![1])) as ArrayRef,
2844 ];
2845 let batch = RecordBatch::try_new(Arc::new(schema.clone()), arrays).unwrap();
2846 let mut file = tempfile::tempfile().unwrap();
2848 let mut stream_writer = crate::writer::StreamWriter::try_new(&mut file, &schema).unwrap();
2849 stream_writer.write(&batch).unwrap();
2850 stream_writer.finish().unwrap();
2851
2852 drop(stream_writer);
2853
2854 file.rewind().unwrap();
2855
2856 let reader = StreamReader::try_new(&mut file, None).unwrap();
2858
2859 reader.for_each(|batch| {
2860 let batch = batch.unwrap();
2861 assert!(
2862 batch
2863 .column(0)
2864 .as_any()
2865 .downcast_ref::<Float32Array>()
2866 .unwrap()
2867 .value(0)
2868 != 0.0
2869 );
2870 assert!(
2871 batch
2872 .column(1)
2873 .as_any()
2874 .downcast_ref::<Float32Array>()
2875 .unwrap()
2876 .value(0)
2877 != 0.0
2878 );
2879 });
2880
2881 file.rewind().unwrap();
2882
2883 let reader = StreamReader::try_new(file, Some(vec![0, 3])).unwrap();
2885
2886 reader.for_each(|batch| {
2887 let batch = batch.unwrap();
2888 assert_eq!(batch.schema().fields().len(), 2);
2889 assert_eq!(batch.schema().fields()[0].data_type(), &DataType::Float32);
2890 assert_eq!(batch.schema().fields()[1].data_type(), &DataType::Int32);
2891 });
2892 }
2893
2894 fn write_ipc(rb: &RecordBatch) -> Vec<u8> {
2896 let mut buf = Vec::new();
2897 let mut writer = crate::writer::FileWriter::try_new(&mut buf, rb.schema_ref()).unwrap();
2898 writer.write(rb).unwrap();
2899 writer.finish().unwrap();
2900 buf
2901 }
2902
2903 fn read_ipc(buf: &[u8]) -> Result<RecordBatch, ArrowError> {
2905 let mut reader = FileReader::try_new(std::io::Cursor::new(buf), None)?;
2906 reader.next().unwrap()
2907 }
2908
2909 fn read_ipc_skip_validation(buf: &[u8]) -> Result<RecordBatch, ArrowError> {
2912 let mut reader = unsafe {
2913 FileReader::try_new(std::io::Cursor::new(buf), None)?.with_skip_validation(true)
2914 };
2915 reader.next().unwrap()
2916 }
2917
2918 fn roundtrip_ipc(rb: &RecordBatch) -> RecordBatch {
2919 let buf = write_ipc(rb);
2920 read_ipc(&buf).unwrap()
2921 }
2922
2923 fn read_ipc_with_decoder(buf: Vec<u8>) -> Result<RecordBatch, ArrowError> {
2926 read_ipc_with_decoder_inner(buf, false)
2927 }
2928
2929 fn read_ipc_with_decoder_skip_validation(buf: Vec<u8>) -> Result<RecordBatch, ArrowError> {
2932 read_ipc_with_decoder_inner(buf, true)
2933 }
2934
2935 fn read_ipc_with_decoder_inner(
2936 buf: Vec<u8>,
2937 skip_validation: bool,
2938 ) -> Result<RecordBatch, ArrowError> {
2939 let buffer = Buffer::from_vec(buf);
2940 let trailer_start = buffer.len() - 10;
2941 let footer_len = read_footer_length(buffer[trailer_start..].try_into().unwrap())?;
2942 let footer = root_as_footer(&buffer[trailer_start - footer_len..trailer_start])
2943 .map_err(|e| ArrowError::InvalidArgumentError(format!("Invalid footer: {e}")))?;
2944
2945 let schema = try_fb_to_schema(footer.schema().unwrap()).unwrap();
2946
2947 let mut decoder = unsafe {
2948 FileDecoder::new(Arc::new(schema), footer.version())
2949 .with_skip_validation(skip_validation)
2950 };
2951 for block in footer.dictionaries().iter().flatten() {
2953 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
2954 let data = buffer.slice_with_length(block.offset() as _, block_len);
2955 decoder.read_dictionary(block, &data)?
2956 }
2957
2958 let batches = footer.recordBatches().unwrap();
2960 assert_eq!(batches.len(), 1); let block = batches.get(0);
2963 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
2964 let data = buffer.slice_with_length(block.offset() as _, block_len);
2965 Ok(decoder.read_record_batch(block, &data)?.unwrap())
2966 }
2967
2968 fn write_stream(rb: &RecordBatch) -> Vec<u8> {
2970 let mut buf = Vec::new();
2971 let mut writer = crate::writer::StreamWriter::try_new(&mut buf, rb.schema_ref()).unwrap();
2972 writer.write(rb).unwrap();
2973 writer.finish().unwrap();
2974 buf
2975 }
2976
2977 fn read_stream(buf: &[u8]) -> Result<RecordBatch, ArrowError> {
2979 let mut reader = StreamReader::try_new(std::io::Cursor::new(buf), None)?;
2980 reader.next().unwrap()
2981 }
2982
2983 fn read_stream_skip_validation(buf: &[u8]) -> Result<RecordBatch, ArrowError> {
2986 let mut reader = unsafe {
2987 StreamReader::try_new(std::io::Cursor::new(buf), None)?.with_skip_validation(true)
2988 };
2989 reader.next().unwrap()
2990 }
2991
2992 fn read_stream_with_decoder(
2995 buf: &[u8],
2996 skip_validation: bool,
2997 ) -> Result<RecordBatch, ArrowError> {
2998 let mut decoder = unsafe { StreamDecoder::new().with_skip_validation(skip_validation) };
2999 Ok(decoder.decode(&mut Buffer::from(buf))?.unwrap())
3000 }
3001
3002 fn roundtrip_ipc_stream(rb: &RecordBatch) -> RecordBatch {
3003 let buf = write_stream(rb);
3004 read_stream(&buf).unwrap()
3005 }
3006
3007 #[test]
3008 fn test_roundtrip_with_custom_metadata() {
3009 let schema = Schema::new(vec![Field::new("dummy", DataType::Float64, false)]);
3010 let mut buf = Vec::new();
3011 let mut writer = crate::writer::FileWriter::try_new(&mut buf, &schema).unwrap();
3012 let mut test_metadata = HashMap::new();
3013 test_metadata.insert("abc".to_string(), "abc".to_string());
3014 test_metadata.insert("def".to_string(), "def".to_string());
3015 for (k, v) in &test_metadata {
3016 writer.write_metadata(k, v);
3017 }
3018 writer.finish().unwrap();
3019 drop(writer);
3020
3021 let reader = crate::reader::FileReader::try_new(std::io::Cursor::new(buf), None).unwrap();
3022 assert_eq!(reader.custom_metadata(), &test_metadata);
3023 }
3024
3025 #[test]
3026 fn test_roundtrip_nested_dict() {
3027 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
3028
3029 let array = Arc::new(inner) as ArrayRef;
3030
3031 let dctfield = Arc::new(Field::new("dict", array.data_type().clone(), false));
3032
3033 let s = StructArray::from(vec![(dctfield, array)]);
3034 let struct_array = Arc::new(s) as ArrayRef;
3035
3036 let schema = Arc::new(Schema::new(vec![Field::new(
3037 "struct",
3038 struct_array.data_type().clone(),
3039 false,
3040 )]));
3041
3042 let batch = RecordBatch::try_new(schema, vec![struct_array]).unwrap();
3043
3044 assert_eq!(batch, roundtrip_ipc(&batch));
3045 }
3046
3047 #[test]
3048 fn test_roundtrip_nested_dict_no_preserve_dict_id() {
3049 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
3050
3051 let array = Arc::new(inner) as ArrayRef;
3052
3053 let dctfield = Arc::new(Field::new("dict", array.data_type().clone(), false));
3054
3055 let s = StructArray::from(vec![(dctfield, array)]);
3056 let struct_array = Arc::new(s) as ArrayRef;
3057
3058 let schema = Arc::new(Schema::new(vec![Field::new(
3059 "struct",
3060 struct_array.data_type().clone(),
3061 false,
3062 )]));
3063
3064 let batch = RecordBatch::try_new(schema, vec![struct_array]).unwrap();
3065
3066 let mut buf = Vec::new();
3067 let mut writer = crate::writer::FileWriter::try_new_with_options(
3068 &mut buf,
3069 batch.schema_ref(),
3070 IpcWriteOptions::default(),
3071 )
3072 .unwrap();
3073 writer.write(&batch).unwrap();
3074 writer.finish().unwrap();
3075 drop(writer);
3076
3077 let mut reader = FileReader::try_new(std::io::Cursor::new(buf), None).unwrap();
3078
3079 assert_eq!(batch, reader.next().unwrap().unwrap());
3080 }
3081
3082 fn check_union_with_builder(mut builder: UnionBuilder) {
3083 builder.append::<Int32Type>("a", 1).unwrap();
3084 builder.append_null::<Int32Type>("a").unwrap();
3085 builder.append::<Float64Type>("c", 3.0).unwrap();
3086 builder.append::<Int32Type>("a", 4).unwrap();
3087 builder.append::<Int64Type>("d", 11).unwrap();
3088 let union = builder.build().unwrap();
3089
3090 let schema = Arc::new(Schema::new(vec![Field::new(
3091 "union",
3092 union.data_type().clone(),
3093 false,
3094 )]));
3095
3096 let union_array = Arc::new(union) as ArrayRef;
3097
3098 let rb = RecordBatch::try_new(schema, vec![union_array]).unwrap();
3099 let rb2 = roundtrip_ipc(&rb);
3100 assert_eq!(rb.schema(), rb2.schema());
3103 assert_eq!(rb.num_columns(), rb2.num_columns());
3104 assert_eq!(rb.num_rows(), rb2.num_rows());
3105 let union1 = rb.column(0);
3106 let union2 = rb2.column(0);
3107
3108 assert_eq!(union1, union2);
3109 }
3110
3111 #[test]
3112 fn test_roundtrip_dense_union() {
3113 check_union_with_builder(UnionBuilder::new_dense());
3114 }
3115
3116 #[test]
3117 fn test_roundtrip_sparse_union() {
3118 check_union_with_builder(UnionBuilder::new_sparse());
3119 }
3120
3121 #[test]
3122 fn test_roundtrip_struct_empty_fields() {
3123 let nulls = NullBuffer::from(&[true, true, false]);
3124 let rb = RecordBatch::try_from_iter([(
3125 "",
3126 Arc::new(StructArray::new_empty_fields(nulls.len(), Some(nulls))) as _,
3127 )])
3128 .unwrap();
3129 let rb2 = roundtrip_ipc(&rb);
3130 assert_eq!(rb, rb2);
3131 }
3132
3133 #[test]
3134 fn test_roundtrip_stream_run_array_sliced() {
3135 let run_array_1: Int32RunArray = vec!["a", "a", "a", "b", "b", "c", "c", "c"]
3136 .into_iter()
3137 .collect();
3138 let run_array_1_sliced = run_array_1.slice(2, 5);
3139
3140 let run_array_2_inupt = vec![Some(1_i32), None, None, Some(2), Some(2)];
3141 let mut run_array_2_builder = PrimitiveRunBuilder::<Int16Type, Int32Type>::new();
3142 run_array_2_builder.extend(run_array_2_inupt);
3143 let run_array_2 = run_array_2_builder.finish();
3144
3145 let schema = Arc::new(Schema::new(vec![
3146 Field::new(
3147 "run_array_1_sliced",
3148 run_array_1_sliced.data_type().clone(),
3149 false,
3150 ),
3151 Field::new("run_array_2", run_array_2.data_type().clone(), false),
3152 ]));
3153 let input_batch = RecordBatch::try_new(
3154 schema,
3155 vec![Arc::new(run_array_1_sliced.clone()), Arc::new(run_array_2)],
3156 )
3157 .unwrap();
3158 let output_batch = roundtrip_ipc_stream(&input_batch);
3159
3160 assert_eq!(input_batch.column(1), output_batch.column(1));
3164
3165 let run_array_1_unsliced = unslice_run_array(run_array_1_sliced.into_data()).unwrap();
3166 assert_eq!(run_array_1_unsliced, output_batch.column(0).into_data());
3167 }
3168
3169 #[test]
3170 fn test_roundtrip_stream_nested_dict() {
3171 let xs = vec!["AA", "BB", "AA", "CC", "BB"];
3172 let dict = Arc::new(
3173 xs.clone()
3174 .into_iter()
3175 .collect::<DictionaryArray<Int8Type>>(),
3176 );
3177 let string_array: ArrayRef = Arc::new(StringArray::from(xs.clone()));
3178 let struct_array = StructArray::from(vec![
3179 (
3180 Arc::new(Field::new("f2.1", DataType::Utf8, false)),
3181 string_array,
3182 ),
3183 (
3184 Arc::new(Field::new("f2.2_struct", dict.data_type().clone(), false)),
3185 dict.clone() as ArrayRef,
3186 ),
3187 ]);
3188 let schema = Arc::new(Schema::new(vec![
3189 Field::new("f1_string", DataType::Utf8, false),
3190 Field::new("f2_struct", struct_array.data_type().clone(), false),
3191 ]));
3192 let input_batch = RecordBatch::try_new(
3193 schema,
3194 vec![
3195 Arc::new(StringArray::from(xs.clone())),
3196 Arc::new(struct_array),
3197 ],
3198 )
3199 .unwrap();
3200 let output_batch = roundtrip_ipc_stream(&input_batch);
3201 assert_eq!(input_batch, output_batch);
3202 }
3203
3204 #[test]
3205 fn test_ipc_writers_reject_dictionary_of_dictionary_schema() {
3206 let values = Arc::new(StringArray::from(vec![Some("a"), Some("b")])) as ArrayRef;
3207 let inner = Arc::new(DictionaryArray::new(
3208 UInt32Array::from_iter_values([0, 1]),
3209 values,
3210 )) as ArrayRef;
3211 let outer = Arc::new(DictionaryArray::new(
3212 UInt32Array::from_iter_values([0, 1, 0]),
3213 inner,
3214 )) as ArrayRef;
3215
3216 let schema = Arc::new(Schema::new(vec![Field::new(
3217 "f1",
3218 outer.data_type().clone(),
3219 false,
3220 )]));
3221 let batch = RecordBatch::try_new(schema, vec![outer]).unwrap();
3222
3223 let mut stream = Vec::new();
3224 let Err(err) = crate::writer::StreamWriter::try_new(&mut stream, batch.schema_ref()) else {
3225 panic!("IPC stream writer should reject dictionary-of-dictionary schemas");
3226 };
3227 assert!(stream.is_empty());
3228
3229 assert!(
3230 err.to_string().contains("dictionary-of-dictionary values"),
3231 "unexpected error: {err}"
3232 );
3233
3234 let mut file = Vec::new();
3235 let Err(err) = crate::writer::FileWriter::try_new(&mut file, batch.schema_ref()) else {
3236 panic!("IPC file writer should reject dictionary-of-dictionary schemas");
3237 };
3238 assert!(file.is_empty());
3239
3240 assert!(
3241 err.to_string().contains("dictionary-of-dictionary values"),
3242 "unexpected error: {err}"
3243 );
3244 }
3245
3246 #[test]
3247 fn test_roundtrip_stream_nested_dict_of_map_of_dict() {
3248 let values = StringArray::from(vec![Some("a"), None, Some("b"), Some("c")]);
3249 let values = Arc::new(values) as ArrayRef;
3250 let value_dict_keys = Int8Array::from_iter_values([0, 1, 1, 2, 3, 1]);
3251 let value_dict_array = DictionaryArray::new(value_dict_keys, values.clone());
3252
3253 let key_dict_keys = Int8Array::from_iter_values([0, 0, 2, 2, 2, 3]);
3254 let key_dict_array = DictionaryArray::new(key_dict_keys, values);
3255
3256 #[expect(deprecated)]
3257 let keys_field = Arc::new(Field::new_dict(
3258 Field::MAP_KEY_FIELD_DEFAULT_NAME,
3259 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8)),
3260 false,
3261 1,
3262 false,
3263 ));
3264 #[expect(deprecated)]
3265 let values_field = Arc::new(Field::new_dict(
3266 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
3267 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8)),
3268 true,
3269 2,
3270 false,
3271 ));
3272 let entry_struct = StructArray::from(vec![
3273 (keys_field, make_array(key_dict_array.into_data())),
3274 (values_field, make_array(value_dict_array.into_data())),
3275 ]);
3276 let map_data_type = DataType::Map(
3277 Arc::new(Field::new(
3278 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
3279 entry_struct.data_type().clone(),
3280 false,
3281 )),
3282 false,
3283 );
3284
3285 let entry_offsets = Buffer::from_slice_ref([0, 2, 4, 6]);
3286 let map_data = ArrayData::builder(map_data_type)
3287 .len(3)
3288 .add_buffer(entry_offsets)
3289 .add_child_data(entry_struct.into_data())
3290 .build()
3291 .unwrap();
3292 let map_array = MapArray::from(map_data);
3293
3294 let dict_keys = Int8Array::from_iter_values([0, 1, 1, 2, 2, 1]);
3295 let dict_dict_array = DictionaryArray::new(dict_keys, Arc::new(map_array));
3296
3297 let schema = Arc::new(Schema::new(vec![Field::new(
3298 "f1",
3299 dict_dict_array.data_type().clone(),
3300 false,
3301 )]));
3302 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(dict_dict_array)]).unwrap();
3303 let output_batch = roundtrip_ipc_stream(&input_batch);
3304 assert_eq!(input_batch, output_batch);
3305 }
3306
3307 fn test_roundtrip_stream_dict_of_list_of_dict_impl<
3308 OffsetSize: OffsetSizeTrait,
3309 U: ArrowNativeType,
3310 >(
3311 list_data_type: DataType,
3312 offsets: &[U; 5],
3313 ) {
3314 let values = StringArray::from(vec![Some("a"), None, Some("c"), None]);
3315 let keys = Int8Array::from_iter_values([0, 0, 1, 2, 0, 1, 3]);
3316 let dict_array = DictionaryArray::new(keys, Arc::new(values));
3317 let dict_data = dict_array.to_data();
3318
3319 let value_offsets = Buffer::from_slice_ref(offsets);
3320
3321 let list_data = ArrayData::builder(list_data_type)
3322 .len(4)
3323 .add_buffer(value_offsets)
3324 .add_child_data(dict_data)
3325 .build()
3326 .unwrap();
3327 let list_array = GenericListArray::<OffsetSize>::from(list_data);
3328
3329 let keys_for_dict = Int8Array::from_iter_values([0, 3, 0, 1, 1, 2, 0, 1, 3]);
3330 let dict_dict_array = DictionaryArray::new(keys_for_dict, Arc::new(list_array));
3331
3332 let schema = Arc::new(Schema::new(vec![Field::new(
3333 "f1",
3334 dict_dict_array.data_type().clone(),
3335 false,
3336 )]));
3337 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(dict_dict_array)]).unwrap();
3338 let output_batch = roundtrip_ipc_stream(&input_batch);
3339 assert_eq!(input_batch, output_batch);
3340 }
3341
3342 #[test]
3343 fn test_roundtrip_stream_dict_of_list_of_dict() {
3344 #[expect(deprecated)]
3346 let list_data_type = DataType::List(Arc::new(Field::new_dict(
3347 "item",
3348 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8)),
3349 true,
3350 1,
3351 false,
3352 )));
3353 let offsets: &[i32; 5] = &[0, 2, 4, 4, 6];
3354 test_roundtrip_stream_dict_of_list_of_dict_impl::<i32, i32>(list_data_type, offsets);
3355
3356 #[expect(deprecated)]
3358 let list_data_type = DataType::LargeList(Arc::new(Field::new_dict(
3359 "item",
3360 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8)),
3361 true,
3362 1,
3363 false,
3364 )));
3365 let offsets: &[i64; 5] = &[0, 2, 4, 4, 7];
3366 test_roundtrip_stream_dict_of_list_of_dict_impl::<i64, i64>(list_data_type, offsets);
3367 }
3368
3369 #[test]
3370 fn test_roundtrip_stream_dict_of_fixed_size_list_of_dict() {
3371 let values = StringArray::from(vec![Some("a"), None, Some("c"), None]);
3372 let keys = Int8Array::from_iter_values([0, 0, 1, 2, 0, 1, 3, 1, 2]);
3373 let dict_array = DictionaryArray::new(keys, Arc::new(values));
3374 let dict_data = dict_array.into_data();
3375
3376 #[expect(deprecated)]
3377 let list_data_type = DataType::FixedSizeList(
3378 Arc::new(Field::new_dict(
3379 "item",
3380 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8)),
3381 true,
3382 1,
3383 false,
3384 )),
3385 3,
3386 );
3387 let list_data = ArrayData::builder(list_data_type)
3388 .len(3)
3389 .add_child_data(dict_data)
3390 .build()
3391 .unwrap();
3392 let list_array = FixedSizeListArray::from(list_data);
3393
3394 let keys_for_dict = Int8Array::from_iter_values([0, 1, 0, 1, 1, 2, 0, 1, 2]);
3395 let dict_dict_array = DictionaryArray::new(keys_for_dict, Arc::new(list_array));
3396
3397 let schema = Arc::new(Schema::new(vec![Field::new(
3398 "f1",
3399 dict_dict_array.data_type().clone(),
3400 false,
3401 )]));
3402 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(dict_dict_array)]).unwrap();
3403 let output_batch = roundtrip_ipc_stream(&input_batch);
3404 assert_eq!(input_batch, output_batch);
3405 }
3406
3407 const LONG_TEST_STRING: &str =
3408 "This is a long string to make sure binary view array handles it";
3409
3410 #[test]
3411 fn test_roundtrip_view_types() {
3412 let schema = Schema::new(vec![
3413 Field::new("field_1", DataType::BinaryView, true),
3414 Field::new("field_2", DataType::Utf8, true),
3415 Field::new("field_3", DataType::Utf8View, true),
3416 ]);
3417 let bin_values: Vec<Option<&[u8]>> = vec![
3418 Some(b"foo"),
3419 None,
3420 Some(b"bar"),
3421 Some(LONG_TEST_STRING.as_bytes()),
3422 ];
3423 let utf8_values: Vec<Option<&str>> =
3424 vec![Some("foo"), None, Some("bar"), Some(LONG_TEST_STRING)];
3425 let bin_view_array = BinaryViewArray::from_iter(bin_values);
3426 let utf8_array = StringArray::from_iter(utf8_values.iter());
3427 let utf8_view_array = StringViewArray::from_iter(utf8_values);
3428 let record_batch = RecordBatch::try_new(
3429 Arc::new(schema.clone()),
3430 vec![
3431 Arc::new(bin_view_array),
3432 Arc::new(utf8_array),
3433 Arc::new(utf8_view_array),
3434 ],
3435 )
3436 .unwrap();
3437
3438 assert_eq!(record_batch, roundtrip_ipc(&record_batch));
3439 assert_eq!(record_batch, roundtrip_ipc_stream(&record_batch));
3440
3441 let sliced_batch = record_batch.slice(1, 2);
3442 assert_eq!(sliced_batch, roundtrip_ipc(&sliced_batch));
3443 assert_eq!(sliced_batch, roundtrip_ipc_stream(&sliced_batch));
3444 }
3445
3446 #[test]
3447 fn test_roundtrip_view_types_nested_dict() {
3448 let bin_values: Vec<Option<&[u8]>> = vec![
3449 Some(b"foo"),
3450 None,
3451 Some(b"bar"),
3452 Some(LONG_TEST_STRING.as_bytes()),
3453 Some(b"field"),
3454 ];
3455 let utf8_values: Vec<Option<&str>> = vec![
3456 Some("foo"),
3457 None,
3458 Some("bar"),
3459 Some(LONG_TEST_STRING),
3460 Some("field"),
3461 ];
3462 let bin_view_array = Arc::new(BinaryViewArray::from_iter(bin_values));
3463 let utf8_view_array = Arc::new(StringViewArray::from_iter(utf8_values));
3464
3465 let key_dict_keys = Int8Array::from_iter_values([0, 0, 2, 2, 0, 2, 3]);
3466 let key_dict_array = DictionaryArray::new(key_dict_keys, utf8_view_array.clone());
3467 #[expect(deprecated)]
3468 let keys_field = Arc::new(Field::new_dict(
3469 Field::MAP_KEY_FIELD_DEFAULT_NAME,
3470 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8View)),
3471 false,
3472 1,
3473 false,
3474 ));
3475
3476 let value_dict_keys = Int8Array::from_iter_values([0, 3, 0, 1, 2, 0, 1]);
3477 let value_dict_array = DictionaryArray::new(value_dict_keys, bin_view_array);
3478 #[expect(deprecated)]
3479 let values_field = Arc::new(Field::new_dict(
3480 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
3481 DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::BinaryView)),
3482 true,
3483 2,
3484 false,
3485 ));
3486 let entry_struct = StructArray::from(vec![
3487 (keys_field, make_array(key_dict_array.into_data())),
3488 (values_field, make_array(value_dict_array.into_data())),
3489 ]);
3490
3491 let map_data_type = DataType::Map(
3492 Arc::new(Field::new(
3493 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
3494 entry_struct.data_type().clone(),
3495 false,
3496 )),
3497 false,
3498 );
3499 let entry_offsets = Buffer::from_slice_ref([0, 2, 4, 7]);
3500 let map_data = ArrayData::builder(map_data_type)
3501 .len(3)
3502 .add_buffer(entry_offsets)
3503 .add_child_data(entry_struct.into_data())
3504 .build()
3505 .unwrap();
3506 let map_array = MapArray::from(map_data);
3507
3508 let dict_keys = Int8Array::from_iter_values([0, 1, 0, 1, 1, 2, 0, 1, 2]);
3509 let dict_dict_array = DictionaryArray::new(dict_keys, Arc::new(map_array));
3510 let schema = Arc::new(Schema::new(vec![Field::new(
3511 "f1",
3512 dict_dict_array.data_type().clone(),
3513 false,
3514 )]));
3515 let batch = RecordBatch::try_new(schema, vec![Arc::new(dict_dict_array)]).unwrap();
3516 assert_eq!(batch, roundtrip_ipc(&batch));
3517 assert_eq!(batch, roundtrip_ipc_stream(&batch));
3518
3519 let sliced_batch = batch.slice(1, 2);
3520 assert_eq!(sliced_batch, roundtrip_ipc(&sliced_batch));
3521 assert_eq!(sliced_batch, roundtrip_ipc_stream(&sliced_batch));
3522 }
3523
3524 #[test]
3525 fn test_no_columns_batch() {
3526 let schema = Arc::new(Schema::empty());
3527 let options = RecordBatchOptions::new()
3528 .with_match_field_names(true)
3529 .with_row_count(Some(10));
3530 let input_batch = RecordBatch::try_new_with_options(schema, vec![], &options).unwrap();
3531 let output_batch = roundtrip_ipc_stream(&input_batch);
3532 assert_eq!(input_batch, output_batch);
3533 }
3534
3535 #[test]
3536 fn test_unaligned() {
3537 let batch = RecordBatch::try_from_iter(vec![(
3538 "i32",
3539 Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _,
3540 )])
3541 .unwrap();
3542
3543 let r#gen = IpcDataGenerator {};
3544 let mut dict_tracker = DictionaryTracker::new(false);
3545 let (_, encoded) = r#gen
3546 .encode(
3547 &batch,
3548 &mut dict_tracker,
3549 &Default::default(),
3550 &mut Default::default(),
3551 )
3552 .unwrap();
3553
3554 let message = root_as_message(&encoded.ipc_message).unwrap();
3555
3556 let mut buffer = MutableBuffer::with_capacity(encoded.arrow_data.len() + 1);
3558 buffer.push(0_u8);
3559 buffer.extend_from_slice(&encoded.arrow_data);
3560 let b = Buffer::from(buffer).slice(1);
3561 assert_ne!(b.as_ptr().align_offset(8), 0);
3562
3563 let ipc_batch = message.header_as_record_batch().unwrap();
3564 let roundtrip = RecordBatchDecoder::try_new(
3565 &b,
3566 ipc_batch,
3567 batch.schema(),
3568 &Default::default(),
3569 &message.version(),
3570 )
3571 .unwrap()
3572 .with_require_alignment(false)
3573 .read_record_batch()
3574 .unwrap();
3575 assert_eq!(batch, roundtrip);
3576 }
3577
3578 #[test]
3579 fn test_unaligned_throws_error_with_require_alignment() {
3580 let batch = RecordBatch::try_from_iter(vec![(
3581 "i32",
3582 Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _,
3583 )])
3584 .unwrap();
3585
3586 let r#gen = IpcDataGenerator {};
3587 let mut dict_tracker = DictionaryTracker::new(false);
3588 let (_, encoded) = r#gen
3589 .encode(
3590 &batch,
3591 &mut dict_tracker,
3592 &Default::default(),
3593 &mut Default::default(),
3594 )
3595 .unwrap();
3596
3597 let message = root_as_message(&encoded.ipc_message).unwrap();
3598
3599 let mut buffer = MutableBuffer::with_capacity(encoded.arrow_data.len() + 1);
3601 buffer.push(0_u8);
3602 buffer.extend_from_slice(&encoded.arrow_data);
3603 let b = Buffer::from(buffer).slice(1);
3604 assert_ne!(b.as_ptr().align_offset(8), 0);
3605
3606 let ipc_batch = message.header_as_record_batch().unwrap();
3607 let result = RecordBatchDecoder::try_new(
3608 &b,
3609 ipc_batch,
3610 batch.schema(),
3611 &Default::default(),
3612 &message.version(),
3613 )
3614 .unwrap()
3615 .with_require_alignment(true)
3616 .read_record_batch();
3617
3618 let error = result.unwrap_err();
3619 assert_eq!(
3620 error.to_string(),
3621 "Invalid argument error: Misaligned buffers[0] in array of type Int32, \
3622 offset from expected alignment of 4 by 1"
3623 );
3624 }
3625
3626 #[test]
3635 fn test_misaligned_buffers_error() {
3636 const OFFSET_RANGE: usize = 128;
3637
3638 let cases: Vec<(ArrayRef, usize)> = vec![
3642 (Arc::new(Int32Array::from_iter(0i32..100)) as _, 4),
3643 (Arc::new(Int64Array::from_iter(0i64..100)) as _, 8),
3644 (
3645 Arc::new(StringArray::from_iter_values(
3646 (0..100).map(|i| i.to_string()),
3647 )) as _,
3648 4,
3649 ),
3650 (
3651 Arc::new(LargeStringArray::from_iter_values(
3652 (0..100).map(|i| i.to_string()),
3653 )) as _,
3654 8,
3655 ),
3656 ];
3657
3658 for (array, alignment) in cases {
3659 let batch = RecordBatch::try_from_iter(vec![("col", Arc::clone(&array))]).unwrap();
3660 let encoder = IpcDataGenerator {};
3661 let mut dict_tracker = DictionaryTracker::new(false);
3662 let (_, encoded) = encoder
3663 .encode(
3664 &batch,
3665 &mut dict_tracker,
3666 &Default::default(),
3667 &mut Default::default(),
3668 )
3669 .unwrap();
3670 let message = root_as_message(&encoded.ipc_message).unwrap();
3671 let ipc_batch = message.header_as_record_batch().unwrap();
3672
3673 for offset in 0..OFFSET_RANGE {
3674 let mut storage = MutableBuffer::with_capacity(encoded.arrow_data.len() + offset);
3680 for _ in 0..offset {
3681 storage.push(0_u8);
3682 }
3683 storage.extend_from_slice(&encoded.arrow_data);
3684 let buf = Buffer::from(storage).slice(offset);
3685
3686 let result = RecordBatchDecoder::try_new(
3687 &buf,
3688 ipc_batch,
3689 batch.schema(),
3690 &Default::default(),
3691 &message.version(),
3692 )
3693 .unwrap()
3694 .with_require_alignment(true)
3695 .read_record_batch();
3696
3697 if offset % alignment == 0 {
3698 assert!(
3699 result.is_ok(),
3700 "type={} offset={offset}: expected Ok but got {:?}",
3701 array.data_type(),
3702 result.unwrap_err(),
3703 );
3704 } else {
3705 let err = result
3706 .expect_err(&format!(
3707 "type={} offset={offset}: expected Err for misaligned buffer",
3708 array.data_type()
3709 ))
3710 .to_string();
3711 assert!(
3712 err.contains("Misaligned buffers"),
3713 "type={} offset={offset}: unexpected error: {err}",
3714 array.data_type(),
3715 );
3716 }
3717 }
3718 }
3719 }
3720
3721 #[test]
3722 #[cfg_attr(miri, ignore)] fn test_file_with_massive_column_count() {
3724 let limit = 600_000;
3726
3727 let fields = (0..limit)
3728 .map(|i| Field::new(format!("{i}"), DataType::Boolean, false))
3729 .collect::<Vec<_>>();
3730 let schema = Arc::new(Schema::new(fields));
3731 let batch = RecordBatch::new_empty(schema);
3732
3733 let mut buf = Vec::new();
3734 let mut writer = crate::writer::FileWriter::try_new(&mut buf, batch.schema_ref()).unwrap();
3735 writer.write(&batch).unwrap();
3736 writer.finish().unwrap();
3737 drop(writer);
3738
3739 let mut reader = FileReaderBuilder::new()
3740 .with_max_footer_fb_tables(1_500_000)
3741 .build(std::io::Cursor::new(buf))
3742 .unwrap();
3743 let roundtrip_batch = reader.next().unwrap().unwrap();
3744
3745 assert_eq!(batch, roundtrip_batch);
3746 }
3747
3748 #[test]
3749 fn test_file_with_deeply_nested_columns() {
3750 let limit = 61;
3752
3753 let fields = (0..limit).fold(
3754 vec![Field::new("leaf", DataType::Boolean, false)],
3755 |field, index| vec![Field::new_struct(format!("{index}"), field, false)],
3756 );
3757 let schema = Arc::new(Schema::new(fields));
3758 let batch = RecordBatch::new_empty(schema);
3759
3760 let mut buf = Vec::new();
3761 let mut writer = crate::writer::FileWriter::try_new(&mut buf, batch.schema_ref()).unwrap();
3762 writer.write(&batch).unwrap();
3763 writer.finish().unwrap();
3764 drop(writer);
3765
3766 let mut reader = FileReaderBuilder::new()
3767 .with_max_footer_fb_depth(65)
3768 .build(std::io::Cursor::new(buf))
3769 .unwrap();
3770 let roundtrip_batch = reader.next().unwrap().unwrap();
3771
3772 assert_eq!(batch, roundtrip_batch);
3773 }
3774
3775 #[test]
3776 fn test_stream_reader_rejects_short_validity_buffer() {
3777 let data = ArrayDataBuilder::new(DataType::Int32)
3779 .len(8000)
3780 .add_buffer(ScalarBuffer::<i32>::from_iter(0..8000).into())
3781 .nulls(Some(NullBuffer::from(&[true, false, true, false])));
3782 let array: ArrayRef = unsafe { Arc::new(Int32Array::from(data.build_unchecked())) };
3783 let batch = RecordBatch::try_from_iter([("a", array)]).unwrap();
3784
3785 let mut stream = Vec::new();
3786 let mut writer =
3787 crate::writer::StreamWriter::try_new(&mut stream, &batch.schema()).unwrap();
3788 writer.write(&batch).unwrap();
3789 writer.finish().unwrap();
3790
3791 let mut reader = StreamReader::try_new(Cursor::new(stream), None).unwrap();
3792 let err = reader.next().unwrap().unwrap_err();
3793 assert_eq!(
3794 err.to_string(),
3795 "Invalid argument error: null_bit_buffer size too small. got 1 needed 1000"
3796 );
3797 }
3798
3799 #[test]
3800 fn test_stream_reader_rejects_short_struct_validity_buffer() {
3801 let fields = Fields::from(vec![Field::new("b", DataType::Int32, false)]);
3803 let child: ArrayRef = Arc::new(Int32Array::from_iter_values(0..8000));
3804 let nulls = NullBuffer::from(&[true, false, true, false]);
3805 let array: ArrayRef =
3806 unsafe { Arc::new(StructArray::new_unchecked(fields, vec![child], Some(nulls))) };
3807 let batch = RecordBatch::try_from_iter([("a", array)]).unwrap();
3808
3809 let mut stream = Vec::new();
3810 let mut writer =
3811 crate::writer::StreamWriter::try_new(&mut stream, &batch.schema()).unwrap();
3812 writer.write(&batch).unwrap();
3813 writer.finish().unwrap();
3814
3815 let mut reader = StreamReader::try_new(Cursor::new(stream), None).unwrap();
3816 let err = reader.next().unwrap().unwrap_err();
3817 assert_eq!(
3818 err.to_string(),
3819 "Invalid argument error: null_bit_buffer size too small. got 1 needed 1000"
3820 );
3821 }
3822
3823 #[test]
3824 fn test_invalid_struct_array_ipc_read_errors() {
3825 let a_field = Field::new("a", DataType::Int32, false);
3826 let b_field = Field::new("b", DataType::Int32, false);
3827 let struct_fields = Fields::from(vec![a_field.clone(), b_field.clone()]);
3828
3829 let a_array_data = ArrayData::builder(a_field.data_type().clone())
3830 .len(4)
3831 .add_buffer(Buffer::from_slice_ref([1, 2, 3, 4]))
3832 .build()
3833 .unwrap();
3834 let b_array_data = ArrayData::builder(b_field.data_type().clone())
3835 .len(3)
3836 .add_buffer(Buffer::from_slice_ref([5, 6, 7]))
3837 .build()
3838 .unwrap();
3839
3840 let invalid_struct_arr = unsafe {
3841 StructArray::new_unchecked(
3842 struct_fields,
3843 vec![make_array(a_array_data), make_array(b_array_data)],
3844 None,
3845 )
3846 };
3847
3848 expect_ipc_validation_error(
3849 Arc::new(invalid_struct_arr),
3850 "Invalid argument error: Incorrect array length for StructArray field \"b\", expected 4 got 3",
3851 );
3852 }
3853
3854 #[test]
3855 fn test_invalid_nested_array_ipc_read_errors() {
3856 let a_field = Field::new("a", DataType::Int32, false);
3858 let b_field = Field::new("b", DataType::Utf8, false);
3859
3860 let schema = Arc::new(Schema::new(vec![Field::new_struct(
3861 "s",
3862 vec![a_field.clone(), b_field.clone()],
3863 false,
3864 )]));
3865
3866 let a_array_data = ArrayData::builder(a_field.data_type().clone())
3867 .len(4)
3868 .add_buffer(Buffer::from_slice_ref([1, 2, 3, 4]))
3869 .build()
3870 .unwrap();
3871 let b_array_data = {
3873 let valid: &[u8] = b" ";
3874 let mut invalid = vec![];
3875 invalid.extend_from_slice(b"ValidString");
3876 invalid.extend_from_slice(INVALID_UTF8_FIRST_CHAR);
3877 let binary_array =
3878 BinaryArray::from_iter(vec![None, Some(valid), None, Some(&invalid)]);
3879 let array = unsafe {
3880 StringArray::new_unchecked(
3881 binary_array.offsets().clone(),
3882 binary_array.values().clone(),
3883 binary_array.nulls().cloned(),
3884 )
3885 };
3886 array.into_data()
3887 };
3888 let struct_data_type = schema.field(0).data_type();
3889
3890 let invalid_struct_arr = unsafe {
3891 make_array(
3892 ArrayData::builder(struct_data_type.clone())
3893 .len(4)
3894 .add_child_data(a_array_data)
3895 .add_child_data(b_array_data)
3896 .build_unchecked(),
3897 )
3898 };
3899 expect_ipc_validation_error(
3900 invalid_struct_arr,
3901 "Invalid argument error: Invalid UTF8 sequence at string index 3 (3..18): invalid utf-8 sequence of 1 bytes from index 11",
3902 );
3903 }
3904
3905 #[test]
3906 #[expect(deprecated)]
3907 fn test_first_field_with_dict_id_matches_fields_with_dict_id() {
3908 let dict = |name: &str, id: i64| {
3909 let data_type =
3910 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8));
3911 Arc::new(Field::new_dict(name, data_type, true, id, false))
3912 };
3913 let dict_of_list_of_dict = |name: &str, outer_id: i64, inner_id: i64| {
3914 let inner = DataType::List(dict("inner", inner_id));
3915 let data_type = DataType::Dictionary(Box::new(DataType::Int32), Box::new(inner));
3916 Field::new_dict(name, data_type, true, outer_id, false)
3917 };
3918 let schema = Schema::new(vec![
3920 Field::new_dict("not_a_dictionary", DataType::Utf8, true, 3, false),
3922 Field::new_struct(
3923 "struct",
3924 vec![dict("struct.a", 1), dict("struct.b", 2)],
3925 true,
3926 ),
3927 Field::new(
3928 "union",
3929 DataType::Union(
3930 UnionFields::try_new(vec![0, 1], vec![dict("union.a", 3), dict("union.b", 1)])
3931 .unwrap(),
3932 UnionMode::Dense,
3933 ),
3934 true,
3935 ),
3936 Field::new("list", DataType::List(dict("list.item", 4)), true),
3937 Field::new_map(
3938 "map",
3939 "entries",
3940 dict("key", 5),
3941 dict("value", 2),
3942 false,
3943 true,
3944 ),
3945 Field::new(
3946 "ree",
3947 DataType::RunEndEncoded(
3948 Arc::new(Field::new("run_ends", DataType::Int32, false)),
3949 dict("ree.values", 6),
3950 ),
3951 true,
3952 ),
3953 dict_of_list_of_dict("dict_of_list_of_dict", 7, 8),
3954 dict_of_list_of_dict("same_outer_and_inner_id", 9, 9),
3955 ]);
3956
3957 for id in -1..=10 {
3958 let expected = schema.fields_with_dict_id(id).first().map(|f| f.name());
3959 let actual = first_field_with_dict_id(schema.fields(), id).map(|f| f.name());
3960 assert_eq!(actual, expected, "dictionary id {id}");
3961 }
3962 }
3963
3964 #[test]
3965 fn test_same_dict_id_without_preserve() {
3966 let batch = RecordBatch::try_new(
3967 Arc::new(Schema::new(
3968 ["a", "b"]
3969 .iter()
3970 .map(|name| {
3971 #[expect(deprecated)]
3972 Field::new_dict(
3973 name.to_string(),
3974 DataType::Dictionary(
3975 Box::new(DataType::Int32),
3976 Box::new(DataType::Utf8),
3977 ),
3978 true,
3979 0,
3980 false,
3981 )
3982 })
3983 .collect::<Vec<Field>>(),
3984 )),
3985 vec![
3986 Arc::new(
3987 vec![Some("c"), Some("d")]
3988 .into_iter()
3989 .collect::<DictionaryArray<Int32Type>>(),
3990 ) as ArrayRef,
3991 Arc::new(
3992 vec![Some("e"), Some("f")]
3993 .into_iter()
3994 .collect::<DictionaryArray<Int32Type>>(),
3995 ) as ArrayRef,
3996 ],
3997 )
3998 .expect("Failed to create RecordBatch");
3999
4000 let mut buf = vec![];
4002 {
4003 let mut writer = crate::writer::StreamWriter::try_new_with_options(
4004 &mut buf,
4005 batch.schema().as_ref(),
4006 crate::writer::IpcWriteOptions::default(),
4007 )
4008 .expect("Failed to create StreamWriter");
4009 writer.write(&batch).expect("Failed to write RecordBatch");
4010 writer.finish().expect("Failed to finish StreamWriter");
4011 }
4012
4013 StreamReader::try_new(std::io::Cursor::new(buf), None)
4014 .expect("Failed to create StreamReader")
4015 .for_each(|decoded_batch| {
4016 assert_eq!(decoded_batch.expect("Failed to read RecordBatch"), batch);
4017 });
4018 }
4019
4020 #[test]
4021 fn test_validation_of_invalid_list_array() {
4022 let array = unsafe {
4024 let values = Int32Array::from(vec![1, 2, 3]);
4025 let bad_offsets = ScalarBuffer::<i32>::from(vec![0, 2, 4, 2]); let offsets = OffsetBuffer::new_unchecked(bad_offsets); let field = Field::new_list_field(DataType::Int32, true);
4028 let nulls = None;
4029 ListArray::new(Arc::new(field), offsets, Arc::new(values), nulls)
4030 };
4031
4032 expect_ipc_validation_error(
4033 Arc::new(array),
4034 "Invalid argument error: Offset invariant failure: offset at position 2 out of bounds: 4 > 2",
4035 );
4036 }
4037
4038 #[test]
4039 fn test_validation_of_invalid_string_array() {
4040 let valid: &[u8] = b" ";
4041 let mut invalid = vec![];
4042 invalid.extend_from_slice(b"ThisStringIsCertainlyLongerThan12Bytes");
4043 invalid.extend_from_slice(INVALID_UTF8_FIRST_CHAR);
4044 let binary_array = BinaryArray::from_iter(vec![None, Some(valid), None, Some(&invalid)]);
4045 let array = unsafe {
4048 StringArray::new_unchecked(
4049 binary_array.offsets().clone(),
4050 binary_array.values().clone(),
4051 binary_array.nulls().cloned(),
4052 )
4053 };
4054 expect_ipc_validation_error(
4055 Arc::new(array),
4056 "Invalid argument error: Invalid UTF8 sequence at string index 3 (3..45): invalid utf-8 sequence of 1 bytes from index 38",
4057 );
4058 }
4059
4060 #[test]
4061 fn test_validation_of_invalid_string_view_array() {
4062 let valid: &[u8] = b" ";
4063 let mut invalid = vec![];
4064 invalid.extend_from_slice(b"ThisStringIsCertainlyLongerThan12Bytes");
4065 invalid.extend_from_slice(INVALID_UTF8_FIRST_CHAR);
4066 let binary_view_array =
4067 BinaryViewArray::from_iter(vec![None, Some(valid), None, Some(&invalid)]);
4068 let array = unsafe {
4071 StringViewArray::new_unchecked(
4072 binary_view_array.views().clone(),
4073 Arc::clone(binary_view_array.data_buffers()),
4074 binary_view_array.nulls().cloned(),
4075 )
4076 };
4077 expect_ipc_validation_error(
4078 Arc::new(array),
4079 "Invalid argument error: Encountered non-UTF-8 data at index 3: invalid utf-8 sequence of 1 bytes from index 38",
4080 );
4081 }
4082
4083 #[test]
4086 fn test_validation_of_invalid_dictionary_array() {
4087 let array = unsafe {
4088 let values = StringArray::from_iter_values(["a", "b", "c"]);
4089 let keys = Int32Array::from(vec![1, 200]); DictionaryArray::new_unchecked(keys, Arc::new(values))
4091 };
4092
4093 expect_ipc_validation_error(
4094 Arc::new(array),
4095 "Invalid argument error: Value at position 1 out of bounds: 200 (should be in [0, 2])",
4096 );
4097 }
4098
4099 #[test]
4100 fn test_validation_of_invalid_union_array() {
4101 let array = unsafe {
4102 let fields = UnionFields::try_new(
4103 vec![1, 3], vec![
4105 Field::new("a", DataType::Int32, false),
4106 Field::new("b", DataType::Utf8, false),
4107 ],
4108 )
4109 .unwrap();
4110 let type_ids = ScalarBuffer::from(vec![1i8, 2, 3]); let offsets = None;
4112 let children: Vec<ArrayRef> = vec![
4113 Arc::new(Int32Array::from(vec![10, 20, 30])),
4114 Arc::new(StringArray::from(vec![Some("a"), Some("b"), Some("c")])),
4115 ];
4116
4117 UnionArray::new_unchecked(fields, type_ids, offsets, children)
4118 };
4119
4120 expect_ipc_validation_error(
4121 Arc::new(array),
4122 "Invalid argument error: Type Ids values must match one of the field type ids",
4123 );
4124 }
4125
4126 const INVALID_UTF8_FIRST_CHAR: &[u8] = &[0xa0, 0xa1, 0x20, 0x20];
4129
4130 fn expect_ipc_validation_error(array: ArrayRef, expected_err: &str) {
4132 let rb = RecordBatch::try_from_iter([("a", array)]).unwrap();
4133
4134 let buf = write_stream(&rb); read_stream_skip_validation(&buf).unwrap();
4137 let err = read_stream(&buf).unwrap_err();
4138 assert_eq!(err.to_string(), expected_err);
4139
4140 read_stream_with_decoder(&buf, true).unwrap();
4142 let err = read_stream_with_decoder(&buf, false).unwrap_err();
4143 assert_eq!(err.to_string(), expected_err);
4144
4145 let buf = write_ipc(&rb); read_ipc_skip_validation(&buf).unwrap();
4148 let err = read_ipc(&buf).unwrap_err();
4149 assert_eq!(err.to_string(), expected_err);
4150
4151 read_ipc_with_decoder_skip_validation(buf.clone()).unwrap();
4153 let err = read_ipc_with_decoder(buf).unwrap_err();
4154 assert_eq!(err.to_string(), expected_err);
4155 }
4156
4157 #[test]
4158 fn test_roundtrip_schema() {
4159 let schema = Schema::new(vec![
4160 Field::new(
4161 "a",
4162 DataType::Dictionary(Box::new(DataType::UInt16), Box::new(DataType::Utf8)),
4163 false,
4164 ),
4165 Field::new(
4166 "b",
4167 DataType::Dictionary(Box::new(DataType::UInt16), Box::new(DataType::Utf8)),
4168 false,
4169 ),
4170 ]);
4171
4172 let options = IpcWriteOptions::default();
4173 let data_gen = IpcDataGenerator::default();
4174 let mut dict_tracker = DictionaryTracker::new(false);
4175 let encoded_data =
4176 data_gen.schema_to_bytes_with_dictionary_tracker(&schema, &mut dict_tracker, &options);
4177 let mut schema_bytes = vec![];
4178 write_message(&mut schema_bytes, encoded_data, &options).expect("write_message");
4179
4180 let begin_offset: usize = if schema_bytes[0..4].eq(&CONTINUATION_MARKER) {
4181 4
4182 } else {
4183 0
4184 };
4185
4186 size_prefixed_root_as_message(&schema_bytes[begin_offset..])
4187 .expect_err("size_prefixed_root_as_message");
4188
4189 let msg = parse_message(&schema_bytes).expect("parse_message");
4190 let ipc_schema = msg.header_as_schema().expect("header_as_schema");
4191 let new_schema = try_fb_to_schema(ipc_schema).unwrap();
4192
4193 assert_eq!(schema, new_schema);
4194 }
4195
4196 #[test]
4197 fn test_negative_meta_len() {
4198 let bytes = i32::to_le_bytes(-1);
4199 let mut buf = vec![];
4200 buf.extend(CONTINUATION_MARKER);
4201 buf.extend(bytes);
4202
4203 let reader = StreamReader::try_new(Cursor::new(buf), None);
4204 assert!(reader.is_err());
4205 }
4206
4207 #[test]
4213 fn test_read_null_dict_without_dictionary_batch() {
4214 let keys = Int32Array::new_null(4);
4216 let values: ArrayRef = new_empty_array(&DataType::Utf8);
4217 let dict_array = DictionaryArray::new(keys, values);
4218
4219 let schema = Arc::new(Schema::new(vec![Field::new(
4220 "d",
4221 dict_array.data_type().clone(),
4222 true,
4223 )]));
4224 let batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(dict_array)]).unwrap();
4225
4226 let full_stream = write_stream(&batch);
4228
4229 let mut stripped = Vec::new();
4233 let mut cursor = Cursor::new(&full_stream);
4234 loop {
4235 let mut header = [0u8; 4];
4238 if cursor.read_exact(&mut header).is_err() {
4239 break;
4240 }
4241 if header == CONTINUATION_MARKER && cursor.read_exact(&mut header).is_err() {
4242 break;
4243 }
4244 let meta_len = u32::from_le_bytes(header) as usize;
4245 if meta_len == 0 {
4246 stripped.extend_from_slice(&CONTINUATION_MARKER);
4248 stripped.extend_from_slice(&0u32.to_le_bytes());
4249 break;
4250 }
4251 let mut meta_buf = vec![0u8; meta_len];
4252 cursor.read_exact(&mut meta_buf).unwrap();
4253
4254 let message = root_as_message(&meta_buf).unwrap();
4255 let body_len = message.bodyLength() as usize;
4256 let mut body_buf = vec![0u8; body_len];
4257 cursor.read_exact(&mut body_buf).unwrap();
4258
4259 if message.header_type() == crate::MessageHeader::DictionaryBatch {
4260 continue;
4263 }
4264 stripped.extend_from_slice(&CONTINUATION_MARKER);
4265 stripped.extend_from_slice(&(meta_len as u32).to_le_bytes());
4266 stripped.extend_from_slice(&meta_buf);
4267 stripped.extend_from_slice(&body_buf);
4268 }
4269
4270 let result = read_stream(&stripped).unwrap();
4272 assert_eq!(result.num_rows(), 4);
4273 assert_eq!(result.num_columns(), 1);
4274
4275 let col = result.column(0);
4276 assert_eq!(col.null_count(), 4);
4277 assert_eq!(col.len(), 4);
4278 assert!(matches!(col.data_type(), DataType::Dictionary(_, _)));
4280 }
4281
4282 #[test]
4285 fn test_projection_skip_list_view() {
4286 use crate::reader::FileReader;
4287 use crate::writer::FileWriter;
4288 use arrow_array::{
4289 GenericListViewArray, Int32Array, RecordBatch,
4290 builder::{GenericListViewBuilder, UInt32Builder},
4291 };
4292 use arrow_schema::{DataType, Field, Schema};
4293 use std::sync::Arc;
4294
4295 let mut builder = GenericListViewBuilder::<i32, _>::new(UInt32Builder::new());
4297
4298 builder.values().append_value(1);
4299 builder.values().append_value(2);
4300 builder.append(true);
4301
4302 builder.append(false);
4303
4304 builder.values().append_value(3);
4305 builder.values().append_value(4);
4306 builder.append(true);
4307
4308 let list_view: GenericListViewArray<i32> = builder.finish();
4309
4310 let values = Int32Array::from(vec![10, 20, 30]);
4312
4313 let schema = Arc::new(Schema::new(vec![
4315 Field::new("a", list_view.data_type().clone(), true),
4316 Field::new("b", DataType::Int32, false),
4317 ]));
4318 let batch =
4320 RecordBatch::try_new(schema, vec![Arc::new(list_view), Arc::new(values.clone())])
4321 .unwrap();
4322
4323 let mut buf = Vec::new();
4325 {
4326 let mut writer = FileWriter::try_new(&mut buf, &batch.schema()).unwrap();
4327 writer.write(&batch).unwrap();
4328 writer.finish().unwrap();
4329 }
4330
4331 let mut reader = FileReader::try_new(std::io::Cursor::new(buf), Some(vec![1])).unwrap();
4333 let read_batch = reader.next().unwrap().unwrap();
4334
4335 assert_eq!(read_batch.num_columns(), 1);
4337 assert_eq!(read_batch.column(0).as_ref(), &values);
4338 }
4339
4340 #[test]
4343 fn test_projection_skip_union_v4() {
4344 use crate::MetadataVersion;
4345 use crate::reader::FileReader;
4346 use crate::writer::{FileWriter, IpcWriteOptions};
4347 use arrow_array::{
4348 ArrayRef, Int32Array, RecordBatch, builder::UnionBuilder, types::Int32Type,
4349 };
4350 use arrow_schema::{DataType, Field, Schema};
4351 use std::sync::Arc;
4352
4353 let mut builder = UnionBuilder::new_dense();
4355 builder.append::<Int32Type>("a", 1).unwrap();
4356 builder.append::<Int32Type>("a", 2).unwrap();
4357 builder.append::<Int32Type>("a", 3).unwrap();
4358 let union = builder.build().unwrap();
4359
4360 let values = Int32Array::from(vec![10, 20, 30]);
4362
4363 let schema = Arc::new(Schema::new(vec![
4365 Field::new("union", union.data_type().clone(), false),
4366 Field::new("values", DataType::Int32, false),
4367 ]));
4368
4369 let batch = RecordBatch::try_new(
4371 schema,
4372 vec![Arc::new(union) as ArrayRef, Arc::new(values.clone())],
4373 )
4374 .unwrap();
4375
4376 let mut buf = Vec::new();
4378 {
4379 let options = IpcWriteOptions::try_new(8, false, MetadataVersion::V4).unwrap();
4380 let mut writer =
4381 FileWriter::try_new_with_options(&mut buf, &batch.schema(), options).unwrap();
4382 writer.write(&batch).unwrap();
4383 writer.finish().unwrap();
4384 }
4385 let mut reader = FileReader::try_new(std::io::Cursor::new(buf), Some(vec![1])).unwrap();
4387 let read_batch = reader.next().unwrap().unwrap();
4388
4389 assert_eq!(read_batch.num_columns(), 1);
4391 assert_eq!(read_batch.column(0).as_ref(), &values);
4392 }
4393
4394 #[test]
4398 fn test_projection_skip_fixed_width_types() {
4399 use std::sync::Arc;
4400
4401 use arrow_array::{ArrayRef, BooleanArray, Int32Array, RecordBatch, make_array};
4402 use arrow_buffer::{Buffer, MutableBuffer};
4403 use arrow_data::ArrayData;
4404 use arrow_schema::{DataType, Field, IntervalUnit, Schema, TimeUnit};
4405
4406 use crate::reader::FileReader;
4407 use crate::writer::FileWriter;
4408
4409 fn make_array_for_type(data_type: DataType) -> ArrayRef {
4411 let len = 3;
4412
4413 if matches!(data_type, DataType::Boolean) {
4414 return Arc::new(BooleanArray::from(vec![true, false, true]));
4415 }
4416
4417 let width = data_type.primitive_width().unwrap();
4418 let data = ArrayData::builder(data_type)
4419 .len(len)
4420 .add_buffer(Buffer::from(MutableBuffer::from_len_zeroed(len * width)))
4421 .build()
4422 .unwrap();
4423
4424 make_array(data)
4425 }
4426
4427 let data_types = vec![
4429 DataType::Boolean,
4430 DataType::Int8,
4431 DataType::Int16,
4432 DataType::Int32,
4433 DataType::Int64,
4434 DataType::UInt8,
4435 DataType::UInt16,
4436 DataType::UInt32,
4437 DataType::UInt64,
4438 DataType::Float16,
4439 DataType::Float32,
4440 DataType::Float64,
4441 DataType::Timestamp(TimeUnit::Second, None),
4442 DataType::Date32,
4443 DataType::Date64,
4444 DataType::Time32(TimeUnit::Second),
4445 DataType::Time64(TimeUnit::Microsecond),
4446 DataType::Duration(TimeUnit::Second),
4447 DataType::Interval(IntervalUnit::YearMonth),
4448 DataType::Interval(IntervalUnit::DayTime),
4449 DataType::Interval(IntervalUnit::MonthDayNano),
4450 DataType::Decimal32(9, 2),
4451 DataType::Decimal64(18, 2),
4452 DataType::Decimal128(38, 2),
4453 DataType::Decimal256(76, 2),
4454 ];
4455
4456 for data_type in data_types {
4461 let skipped = make_array_for_type(data_type.clone());
4462 let values = Int32Array::from(vec![10, 20, 30]);
4463
4464 let schema = Arc::new(Schema::new(vec![
4465 Field::new("skipped", data_type, false),
4466 Field::new("values", DataType::Int32, false),
4467 ]));
4468
4469 let batch =
4470 RecordBatch::try_new(schema, vec![skipped, Arc::new(values.clone())]).unwrap();
4471
4472 let mut buf = Vec::new();
4474 {
4475 let mut writer = FileWriter::try_new(&mut buf, &batch.schema()).unwrap();
4476 writer.write(&batch).unwrap();
4477 writer.finish().unwrap();
4478 }
4479
4480 let mut reader = FileReader::try_new(std::io::Cursor::new(buf), Some(vec![1])).unwrap();
4482 let read_batch = reader.next().unwrap().unwrap();
4483
4484 assert_eq!(read_batch.num_columns(), 1);
4486 assert_eq!(read_batch.column(0).as_ref(), &values);
4487 }
4488 }
4489
4490 fn stream_with_declared_body(body_length: i64, body_bytes: usize) -> Vec<u8> {
4494 let mut fbb = flatbuffers::FlatBufferBuilder::new();
4495 let mut message = crate::MessageBuilder::new(&mut fbb);
4496 message.add_version(crate::MetadataVersion::V5);
4497 message.add_header_type(crate::MessageHeader::NONE);
4498 message.add_bodyLength(body_length);
4499 let root = message.finish();
4500 fbb.finish(root, None);
4501 let metadata = fbb.finished_data();
4502
4503 let mut stream = Vec::new();
4504 stream.extend_from_slice(&CONTINUATION_MARKER);
4505 stream.extend_from_slice(&(metadata.len() as i32).to_le_bytes());
4506 stream.extend_from_slice(metadata);
4507 stream.resize(stream.len() + body_bytes, 0xAB);
4508 stream
4509 }
4510
4511 #[test]
4516 fn test_stream_reader_rejects_implausible_body_length() {
4517 for body_length in [i64::MAX, 1 << 50, -1] {
4518 let stream = stream_with_declared_body(body_length, 0);
4519 let err = StreamReader::try_new(std::io::Cursor::new(stream), None).expect_err(
4520 &format!("a message declaring {body_length} body bytes must not be accepted"),
4521 );
4522 let err = err.to_string();
4523 assert!(
4524 err.contains("Invalid IPC message body length")
4525 || err.contains("Unexpected end of stream")
4526 || err.contains("failed to fill whole buffer"),
4527 "unexpected error for body_length {body_length}: {err}"
4528 );
4529 }
4530 }
4531
4532 #[test]
4536 #[cfg_attr(miri, ignore)] fn test_stream_reader_rejects_truncated_body() {
4538 let over_prealloc = MAX_PREALLOC_BYTES as i64 + 1;
4539 for (body_length, body_bytes) in [(1024, 10), (over_prealloc, MAX_PREALLOC_BYTES)] {
4540 let stream = stream_with_declared_body(body_length, body_bytes);
4541 let err = StreamReader::try_new(std::io::Cursor::new(stream), None)
4542 .expect_err("a body backed by fewer bytes than declared must not be accepted");
4543 assert!(
4544 err.to_string().contains("failed to fill whole buffer"),
4545 "unexpected error for body_length {body_length}: {err}"
4546 );
4547 }
4548 }
4549
4550 #[test]
4552 fn test_stream_reader_rejects_unbacked_metadata_length() {
4553 let mut stream = Vec::new();
4554 stream.extend_from_slice(&CONTINUATION_MARKER);
4555 stream.extend_from_slice(&i32::MAX.to_le_bytes());
4556 stream.extend_from_slice(b"not this many bytes");
4557
4558 let err = StreamReader::try_new(std::io::Cursor::new(stream), None)
4559 .expect_err("metadata length must be backed by the stream");
4560 assert!(
4561 err.to_string().contains("Unexpected end of stream"),
4562 "unexpected error: {err}"
4563 );
4564 }
4565}