1use crate::column::chunker::ContentDefinedChunker;
21
22use bytes::Bytes;
23use std::io::Write;
24use std::slice::Iter;
25use std::sync::{Arc, Mutex};
26use std::vec::IntoIter;
27
28use arrow_array::cast::AsArray;
29use arrow_array::{ArrayRef, Int32Array, RecordBatch, RecordBatchWriter};
30use arrow_array::{PrimitiveArray, types::*};
31use arrow_schema::{
32 ArrowError, DataType as ArrowDataType, Field, IntervalUnit, SchemaRef, TimeUnit,
33};
34
35use super::schema::{add_encoded_arrow_schema_to_metadata, decimal_length_from_precision};
36
37use crate::arrow::ArrowSchemaConverter;
38use crate::arrow::arrow_writer::byte_array::ByteArrayEncoder;
39use crate::basic::PageType;
40use crate::column::page::{CompressedPage, PageWriteSpec, PageWriter};
41use crate::column::page_encryption::PageEncryptor;
42use crate::column::writer::encoder::ColumnValueEncoder;
43use crate::column::writer::{
44 ColumnCloseResult, ColumnWriter, GenericColumnWriter, get_column_writer,
45};
46use crate::data_type::{ByteArray, FixedLenByteArray};
47#[cfg(feature = "encryption")]
48use crate::encryption::encrypt::FileEncryptor;
49use crate::errors::{ParquetError, Result};
50use crate::file::metadata::{KeyValue, ParquetMetaData, RowGroupMetaData};
51use crate::file::properties::{WriterProperties, WriterPropertiesPtr};
52use crate::file::writer::{SerializedFileWriter, SerializedRowGroupWriter};
53use crate::parquet_thrift::{ThriftCompactOutputProtocol, WriteThrift};
54use crate::schema::types::{ColumnDescPtr, SchemaDescPtr, SchemaDescriptor};
55use levels::{ArrayLevels, calculate_array_levels};
56
57mod byte_array;
58mod levels;
59
60#[doc(inline)]
61pub use crate::column::page_store::{
62 InMemoryPageStore, InMemoryPageStoreFactory, PageKey, PageStore, PageStoreArgs,
63 PageStoreFactory,
64};
65
66pub struct ArrowWriter<W: Write> {
183 writer: SerializedFileWriter<W>,
185
186 in_progress: Option<ArrowRowGroupWriter>,
188
189 arrow_schema: SchemaRef,
193
194 row_group_writer_factory: ArrowRowGroupWriterFactory,
196
197 max_row_group_row_count: Option<usize>,
199
200 max_row_group_bytes: Option<usize>,
202
203 cdc_chunkers: Option<Vec<ContentDefinedChunker>>,
205}
206
207impl<W: Write + Send> std::fmt::Debug for ArrowWriter<W> {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 let buffered_memory = self.in_progress_size();
210 f.debug_struct("ArrowWriter")
211 .field("writer", &self.writer)
212 .field("in_progress_size", &format_args!("{buffered_memory} bytes"))
213 .field("in_progress_rows", &self.in_progress_rows())
214 .field("arrow_schema", &self.arrow_schema)
215 .field("max_row_group_row_count", &self.max_row_group_row_count)
216 .field("max_row_group_bytes", &self.max_row_group_bytes)
217 .finish()
218 }
219}
220
221impl<W: Write + Send> ArrowWriter<W> {
222 pub fn try_new(
228 writer: W,
229 arrow_schema: SchemaRef,
230 props: Option<WriterProperties>,
231 ) -> Result<Self> {
232 let options = ArrowWriterOptions::new().with_properties(props.unwrap_or_default());
233 Self::try_new_with_options(writer, arrow_schema, options)
234 }
235
236 pub fn try_new_with_options(
242 writer: W,
243 arrow_schema: SchemaRef,
244 options: ArrowWriterOptions,
245 ) -> Result<Self> {
246 let mut props = options.properties;
247
248 let schema = if let Some(parquet_schema) = options.schema_descr {
249 parquet_schema.clone()
250 } else {
251 let mut converter = ArrowSchemaConverter::new().with_coerce_types(props.coerce_types());
252 if let Some(schema_root) = &options.schema_root {
253 converter = converter.schema_root(schema_root);
254 }
255
256 converter.convert(&arrow_schema)?
257 };
258
259 if !options.skip_arrow_metadata {
260 add_encoded_arrow_schema_to_metadata(&arrow_schema, &mut props);
262 }
263
264 let max_row_group_row_count = props.max_row_group_row_count();
265 let max_row_group_bytes = props.max_row_group_bytes();
266
267 let props_ptr = Arc::new(props);
268 let file_writer =
269 SerializedFileWriter::new(writer, schema.root_schema_ptr(), Arc::clone(&props_ptr))?;
270
271 let mut row_group_writer_factory =
272 ArrowRowGroupWriterFactory::new(&file_writer, arrow_schema.clone());
273 if let Some(page_store_factory) = options.page_store_factory {
274 row_group_writer_factory =
275 row_group_writer_factory.with_page_store_factory(page_store_factory);
276 }
277
278 let cdc_chunkers = props_ptr
279 .content_defined_chunking()
280 .map(|opts| {
281 file_writer
282 .schema_descr()
283 .columns()
284 .iter()
285 .map(|desc| ContentDefinedChunker::new(desc, opts))
286 .collect::<Result<Vec<_>>>()
287 })
288 .transpose()?;
289
290 Ok(Self {
291 writer: file_writer,
292 in_progress: None,
293 arrow_schema,
294 row_group_writer_factory,
295 max_row_group_row_count,
296 max_row_group_bytes,
297 cdc_chunkers,
298 })
299 }
300
301 pub fn flushed_row_groups(&self) -> &[RowGroupMetaData] {
303 self.writer.flushed_row_groups()
304 }
305
306 pub fn memory_size(&self) -> usize {
311 match &self.in_progress {
312 Some(in_progress) => in_progress.writers.iter().map(|x| x.memory_size()).sum(),
313 None => 0,
314 }
315 }
316
317 pub fn in_progress_size(&self) -> usize {
324 match &self.in_progress {
325 Some(in_progress) => in_progress
326 .writers
327 .iter()
328 .map(|x| x.get_estimated_total_bytes())
329 .sum(),
330 None => 0,
331 }
332 }
333
334 pub fn in_progress_rows(&self) -> usize {
336 self.in_progress
337 .as_ref()
338 .map(|x| x.buffered_rows)
339 .unwrap_or_default()
340 }
341
342 pub fn bytes_written(&self) -> usize {
344 self.writer.bytes_written()
345 }
346
347 pub fn write(&mut self, batch: &RecordBatch) -> Result<()> {
359 if batch.num_rows() == 0 {
360 return Ok(());
361 }
362
363 let in_progress = match &mut self.in_progress {
364 Some(in_progress) => in_progress,
365 x => x.insert(
366 self.row_group_writer_factory
367 .create_row_group_writer(self.writer.flushed_row_groups().len())?,
368 ),
369 };
370
371 if let Some(max_rows) = self.max_row_group_row_count
372 && in_progress.buffered_rows + batch.num_rows() > max_rows
373 {
374 let to_write = max_rows - in_progress.buffered_rows;
375 let a = batch.slice(0, to_write);
376 let b = batch.slice(to_write, batch.num_rows() - to_write);
377 self.write(&a)?;
378 return self.write(&b);
379 }
380
381 if let Some(max_bytes) = self.max_row_group_bytes
384 && in_progress.buffered_rows > 0
385 {
386 let current_bytes = in_progress.get_estimated_total_bytes();
387
388 if current_bytes >= max_bytes {
389 self.flush()?;
390 return self.write(batch);
391 }
392
393 if let Some(avg_row_bytes) = current_bytes
394 .checked_div(in_progress.buffered_rows)
395 .filter(|avg_row_bytes| *avg_row_bytes > 0)
396 {
397 let remaining_bytes = max_bytes - current_bytes;
399 let rows_that_fit = remaining_bytes.checked_div(avg_row_bytes).unwrap_or(0);
400
401 if batch.num_rows() > rows_that_fit {
402 if rows_that_fit > 0 {
403 let a = batch.slice(0, rows_that_fit);
404 let b = batch.slice(rows_that_fit, batch.num_rows() - rows_that_fit);
405 self.write(&a)?;
406 return self.write(&b);
407 } else {
408 self.flush()?;
409 return self.write(batch);
410 }
411 }
412 }
413 }
414
415 match self.cdc_chunkers.as_mut() {
416 Some(chunkers) => in_progress.write_with_chunkers(batch, chunkers)?,
417 None => in_progress.write(batch)?,
418 }
419
420 let should_flush = self
421 .max_row_group_row_count
422 .is_some_and(|max| in_progress.buffered_rows >= max)
423 || self
424 .max_row_group_bytes
425 .is_some_and(|max| in_progress.get_estimated_total_bytes() >= max);
426
427 if should_flush {
428 self.flush()?
429 }
430 Ok(())
431 }
432
433 pub fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
438 self.writer.write_all(buf)
439 }
440
441 pub fn sync(&mut self) -> std::io::Result<()> {
443 self.writer.flush()
444 }
445
446 pub fn flush(&mut self) -> Result<()> {
451 let Some(in_progress) = self.in_progress.take() else {
452 return Ok(());
453 };
454
455 let mut row_group_writer = self.writer.next_row_group()?;
456 for chunk in in_progress.close()? {
457 chunk.append_to_row_group(&mut row_group_writer)?;
458 }
459 row_group_writer.close()?;
460 Ok(())
461 }
462
463 pub fn append_key_value_metadata(&mut self, kv_metadata: KeyValue) {
467 self.writer.append_key_value_metadata(kv_metadata)
468 }
469
470 pub fn inner(&self) -> &W {
472 self.writer.inner()
473 }
474
475 pub fn inner_mut(&mut self) -> &mut W {
484 self.writer.inner_mut()
485 }
486
487 pub fn into_inner(mut self) -> Result<W> {
489 self.flush()?;
490 self.writer.into_inner()
491 }
492
493 pub fn finish(&mut self) -> Result<ParquetMetaData> {
499 self.flush()?;
500 self.writer.finish()
501 }
502
503 pub fn close(mut self) -> Result<ParquetMetaData> {
505 self.finish()
506 }
507
508 pub fn into_serialized_writer(
515 mut self,
516 ) -> Result<(SerializedFileWriter<W>, ArrowRowGroupWriterFactory)> {
517 self.flush()?;
518 Ok((self.writer, self.row_group_writer_factory))
519 }
520}
521
522impl<W: Write + Send> RecordBatchWriter for ArrowWriter<W> {
523 fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
524 self.write(batch).map_err(|e| e.into())
525 }
526
527 fn close(self) -> std::result::Result<(), ArrowError> {
528 self.close()?;
529 Ok(())
530 }
531}
532
533#[derive(Debug, Clone, Default)]
537pub struct ArrowWriterOptions {
538 properties: WriterProperties,
539 skip_arrow_metadata: bool,
540 schema_root: Option<String>,
541 schema_descr: Option<SchemaDescriptor>,
542 page_store_factory: Option<Arc<dyn PageStoreFactory>>,
543}
544
545impl ArrowWriterOptions {
546 pub fn new() -> Self {
548 Self::default()
549 }
550
551 pub fn with_properties(self, properties: WriterProperties) -> Self {
553 Self { properties, ..self }
554 }
555
556 pub fn with_page_store_factory(self, page_store_factory: Arc<dyn PageStoreFactory>) -> Self {
642 Self {
643 page_store_factory: Some(page_store_factory),
644 ..self
645 }
646 }
647
648 pub fn with_skip_arrow_metadata(self, skip_arrow_metadata: bool) -> Self {
655 Self {
656 skip_arrow_metadata,
657 ..self
658 }
659 }
660
661 pub fn with_schema_root(self, schema_root: String) -> Self {
663 Self {
664 schema_root: Some(schema_root),
665 ..self
666 }
667 }
668
669 pub fn with_parquet_schema(self, schema_descr: SchemaDescriptor) -> Self {
675 Self {
676 schema_descr: Some(schema_descr),
677 ..self
678 }
679 }
680}
681
682struct ArrowColumnChunkData {
688 length: usize,
689 store: Box<dyn PageStore>,
690 keys: Vec<PageKey>,
691 dictionary_keys: Vec<PageKey>,
702 dictionary_len: usize,
706}
707
708impl ArrowColumnChunkData {
709 fn new(store: Box<dyn PageStore>) -> Self {
710 Self {
711 length: 0,
712 store,
713 keys: Vec::new(),
714 dictionary_keys: Vec::new(),
715 dictionary_len: 0,
716 }
717 }
718
719 fn push(&mut self, value: Bytes) -> Result<()> {
722 let key = self.store.put(value)?;
723 self.keys.push(key);
724 Ok(())
725 }
726
727 fn push_dictionary(&mut self, value: Bytes) -> Result<()> {
731 self.dictionary_len += value.len();
732 let key = self.store.put(value)?;
733 self.dictionary_keys.push(key);
734 Ok(())
735 }
736
737 fn memory_size(&self) -> usize {
740 self.store.memory_size()
741 }
742}
743
744struct StreamingColumnChunkPages {
753 store: Box<dyn PageStore>,
754 keys: IntoIter<PageKey>,
757}
758
759impl StreamingColumnChunkPages {
760 fn new(data: ArrowColumnChunkData) -> Self {
761 let keys = if data.dictionary_keys.is_empty() {
764 data.keys
765 } else {
766 let mut keys = Vec::with_capacity(data.dictionary_keys.len() + data.keys.len());
767 keys.extend(data.dictionary_keys);
768 keys.extend(data.keys);
769 keys
770 };
771 Self {
772 store: data.store,
773 keys: keys.into_iter(),
774 }
775 }
776}
777
778impl Iterator for StreamingColumnChunkPages {
779 type Item = Result<Bytes>;
780
781 fn next(&mut self) -> Option<Self::Item> {
782 let key = self.keys.next()?;
783 Some(self.store.take(key))
784 }
785}
786
787type SharedColumnChunk = Arc<Mutex<ArrowColumnChunkData>>;
792
793struct ArrowPageWriter {
794 buffer: SharedColumnChunk,
795 #[cfg(feature = "encryption")]
796 page_encryptor: Option<PageEncryptor>,
797}
798
799impl ArrowPageWriter {
800 fn new(store: Box<dyn PageStore>) -> Self {
802 Self {
803 buffer: Arc::new(Mutex::new(ArrowColumnChunkData::new(store))),
804 #[cfg(feature = "encryption")]
805 page_encryptor: None,
806 }
807 }
808
809 #[cfg(feature = "encryption")]
810 pub fn with_encryptor(mut self, page_encryptor: Option<PageEncryptor>) -> Self {
811 self.page_encryptor = page_encryptor;
812 self
813 }
814
815 #[cfg(feature = "encryption")]
816 fn page_encryptor_mut(&mut self) -> Option<&mut PageEncryptor> {
817 self.page_encryptor.as_mut()
818 }
819
820 #[cfg(not(feature = "encryption"))]
823 #[expect(
824 clippy::needless_pass_by_ref_mut,
825 reason = "mirrors the encryption-enabled signature"
826 )]
827 fn page_encryptor_mut(&mut self) -> Option<&mut PageEncryptor> {
828 None
829 }
830}
831
832impl PageWriter for ArrowPageWriter {
833 fn write_page(&mut self, page: CompressedPage) -> Result<PageWriteSpec> {
834 let page = match self.page_encryptor_mut() {
835 Some(page_encryptor) => page_encryptor.encrypt_compressed_page(page)?,
836 None => page,
837 };
838
839 let page_header = page.to_thrift_header()?;
840 let header = {
841 let mut header = Vec::with_capacity(1024);
842
843 match self.page_encryptor_mut() {
844 Some(page_encryptor) => {
845 page_encryptor.encrypt_page_header(&page_header, &mut header)?;
846 if page.compressed_page().is_data_page() {
847 page_encryptor.increment_page();
848 }
849 }
850 None => {
851 let mut protocol = ThriftCompactOutputProtocol::new(&mut header);
852 page_header.write_thrift(&mut protocol)?;
853 }
854 }
855
856 Bytes::from(header)
857 };
858
859 let mut buf = self.buffer.try_lock().unwrap();
860
861 let data = page.compressed_page().buffer().clone();
862 let compressed_size = data.len() + header.len();
863
864 let mut spec = PageWriteSpec::new();
865 spec.page_type = page.page_type();
866 spec.num_values = page.num_values();
867 spec.uncompressed_size = page.uncompressed_size() + header.len();
868 spec.offset = buf.length as u64;
869 spec.compressed_size = compressed_size;
870 spec.bytes_written = compressed_size as u64;
871
872 buf.length += compressed_size;
873 if spec.page_type == PageType::DICTIONARY_PAGE {
874 buf.push_dictionary(header)?;
877 buf.push_dictionary(data)?;
878 } else {
879 buf.push(header)?;
880 buf.push(data)?;
881 }
882
883 Ok(spec)
884 }
885
886 fn defers_dictionary_ordering(&self) -> bool {
887 true
892 }
893
894 fn buffered_memory_size(&self) -> usize {
895 self.buffer.try_lock().unwrap().memory_size()
898 }
899
900 fn close(&mut self) -> Result<()> {
901 Ok(())
902 }
903}
904
905#[derive(Debug)]
907pub struct ArrowLeafColumn(ArrayLevels);
908
909pub fn compute_leaves(field: &Field, array: &ArrayRef) -> Result<Vec<ArrowLeafColumn>> {
914 let levels = calculate_array_levels(array, field)?;
915 Ok(levels.into_iter().map(ArrowLeafColumn).collect())
916}
917
918pub struct ArrowColumnChunk {
920 data: ArrowColumnChunkData,
921 close: ColumnCloseResult,
922}
923
924impl std::fmt::Debug for ArrowColumnChunk {
925 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
926 f.debug_struct("ArrowColumnChunk")
927 .field("length", &self.data.length)
928 .finish_non_exhaustive()
929 }
930}
931
932impl ArrowColumnChunk {
933 pub fn close(&self) -> &ColumnCloseResult {
940 &self.close
941 }
942
943 pub fn close_mut(&mut self) -> &mut ColumnCloseResult {
950 &mut self.close
951 }
952
953 pub fn append_to_row_group<W: Write + Send>(
956 self,
957 writer: &mut SerializedRowGroupWriter<'_, W>,
958 ) -> Result<()> {
959 let ArrowColumnChunk { data, close } = self;
960
961 let close = close.update_dictionary_location(data.dictionary_len)?;
965
966 let pages = StreamingColumnChunkPages::new(data);
967 writer.append_column_from_pages(pages, close)
968 }
969}
970
971pub struct ArrowColumnWriter {
1069 writer: ArrowColumnWriterImpl,
1070 chunk: SharedColumnChunk,
1071}
1072
1073impl std::fmt::Debug for ArrowColumnWriter {
1074 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1075 f.debug_struct("ArrowColumnWriter").finish_non_exhaustive()
1076 }
1077}
1078
1079enum ArrowColumnWriterImpl {
1080 ByteArray(GenericColumnWriter<'static, ByteArrayEncoder>),
1081 Column(ColumnWriter<'static>),
1082}
1083
1084impl ArrowColumnWriter {
1085 pub fn write(&mut self, col: &ArrowLeafColumn) -> Result<()> {
1087 self.write_internal(&col.0)
1088 }
1089
1090 fn write_with_chunker(
1092 &mut self,
1093 col: &ArrowLeafColumn,
1094 chunker: &mut ContentDefinedChunker,
1095 ) -> Result<()> {
1096 let levels = &col.0;
1097 let chunks = chunker.get_arrow_chunks(
1098 levels.def_level_data().as_ref(),
1099 levels.rep_level_data().as_ref(),
1100 levels.array(),
1101 )?;
1102
1103 let num_chunks = chunks.len();
1104 for (i, chunk) in chunks.iter().enumerate() {
1105 let chunk_levels = levels.slice_for_chunk(chunk);
1106 self.write_internal(&chunk_levels)?;
1107
1108 if i + 1 < num_chunks {
1110 match &mut self.writer {
1111 ArrowColumnWriterImpl::Column(c) => c.add_data_page()?,
1112 ArrowColumnWriterImpl::ByteArray(c) => c.add_data_page()?,
1113 }
1114 }
1115 }
1116 Ok(())
1117 }
1118
1119 fn write_internal(&mut self, levels: &ArrayLevels) -> Result<()> {
1120 match &mut self.writer {
1121 ArrowColumnWriterImpl::Column(c) => {
1122 let leaf = levels.array();
1123 match leaf.as_any_dictionary_opt() {
1124 Some(dictionary) => {
1125 let materialized =
1126 arrow_select::take::take(dictionary.values(), dictionary.keys(), None)?;
1127 write_leaf(c, &materialized, levels)?
1128 }
1129 None => write_leaf(c, leaf, levels)?,
1130 };
1131 }
1132 ArrowColumnWriterImpl::ByteArray(c) => {
1133 write_primitive(c, levels.array().as_ref(), levels)?;
1134 }
1135 }
1136 Ok(())
1137 }
1138
1139 pub fn close(self) -> Result<ArrowColumnChunk> {
1141 let close = match self.writer {
1142 ArrowColumnWriterImpl::ByteArray(c) => c.close()?,
1143 ArrowColumnWriterImpl::Column(c) => c.close()?,
1144 };
1145 let chunk = Arc::try_unwrap(self.chunk).ok().unwrap();
1146 let data = chunk.into_inner().unwrap();
1147 Ok(ArrowColumnChunk { data, close })
1148 }
1149
1150 pub fn memory_size(&self) -> usize {
1161 match &self.writer {
1162 ArrowColumnWriterImpl::ByteArray(c) => c.memory_size(),
1163 ArrowColumnWriterImpl::Column(c) => c.memory_size(),
1164 }
1165 }
1166
1167 pub fn get_estimated_total_bytes(&self) -> usize {
1175 match &self.writer {
1176 ArrowColumnWriterImpl::ByteArray(c) => c.get_estimated_total_bytes() as _,
1177 ArrowColumnWriterImpl::Column(c) => c.get_estimated_total_bytes() as _,
1178 }
1179 }
1180}
1181
1182#[derive(Debug)]
1189struct ArrowRowGroupWriter {
1190 writers: Vec<ArrowColumnWriter>,
1191 schema: SchemaRef,
1192 buffered_rows: usize,
1193}
1194
1195impl ArrowRowGroupWriter {
1196 fn new(writers: Vec<ArrowColumnWriter>, arrow: &SchemaRef) -> Self {
1197 Self {
1198 writers,
1199 schema: arrow.clone(),
1200 buffered_rows: 0,
1201 }
1202 }
1203
1204 fn write(&mut self, batch: &RecordBatch) -> Result<()> {
1205 self.buffered_rows += batch.num_rows();
1206 let mut writers = self.writers.iter_mut();
1207 for (field, column) in self.schema.fields().iter().zip(batch.columns()) {
1208 for leaf in compute_leaves(field.as_ref(), column)? {
1209 writers.next().unwrap().write(&leaf)?;
1210 }
1211 }
1212 Ok(())
1213 }
1214
1215 fn write_with_chunkers(
1216 &mut self,
1217 batch: &RecordBatch,
1218 chunkers: &mut [ContentDefinedChunker],
1219 ) -> Result<()> {
1220 self.buffered_rows += batch.num_rows();
1221 let mut writers = self.writers.iter_mut();
1222 let mut chunkers = chunkers.iter_mut();
1223 for (field, column) in self.schema.fields().iter().zip(batch.columns()) {
1224 for leaf in compute_leaves(field.as_ref(), column)? {
1225 writers
1226 .next()
1227 .unwrap()
1228 .write_with_chunker(&leaf, chunkers.next().unwrap())?;
1229 }
1230 }
1231 Ok(())
1232 }
1233
1234 fn get_estimated_total_bytes(&self) -> usize {
1236 self.writers
1237 .iter()
1238 .map(|x| x.get_estimated_total_bytes())
1239 .sum()
1240 }
1241
1242 fn close(self) -> Result<Vec<ArrowColumnChunk>> {
1243 self.writers
1244 .into_iter()
1245 .map(|writer| writer.close())
1246 .collect()
1247 }
1248}
1249
1250#[derive(Debug)]
1255pub struct ArrowRowGroupWriterFactory {
1256 schema: SchemaDescPtr,
1257 arrow_schema: SchemaRef,
1258 props: WriterPropertiesPtr,
1259 page_store_factory: Arc<dyn PageStoreFactory>,
1260 #[cfg(feature = "encryption")]
1261 file_encryptor: Option<Arc<FileEncryptor>>,
1262}
1263
1264impl ArrowRowGroupWriterFactory {
1265 pub fn new<W: Write + Send>(
1267 file_writer: &SerializedFileWriter<W>,
1268 arrow_schema: SchemaRef,
1269 ) -> Self {
1270 let schema = Arc::clone(file_writer.schema_descr_ptr());
1271 let props = Arc::clone(file_writer.properties());
1272 Self {
1273 schema,
1274 arrow_schema,
1275 props,
1276 page_store_factory: Arc::new(InMemoryPageStoreFactory),
1277 #[cfg(feature = "encryption")]
1278 file_encryptor: file_writer.file_encryptor(),
1279 }
1280 }
1281
1282 pub fn with_page_store_factory(
1286 mut self,
1287 page_store_factory: Arc<dyn PageStoreFactory>,
1288 ) -> Self {
1289 self.page_store_factory = page_store_factory;
1290 self
1291 }
1292
1293 fn create_row_group_writer(&self, row_group_index: usize) -> Result<ArrowRowGroupWriter> {
1294 let writers = self.create_column_writers(row_group_index)?;
1295 Ok(ArrowRowGroupWriter::new(writers, &self.arrow_schema))
1296 }
1297
1298 pub fn create_column_writers(&self, row_group_index: usize) -> Result<Vec<ArrowColumnWriter>> {
1300 let mut writers = Vec::with_capacity(self.arrow_schema.fields.len());
1301 let mut leaves = self.schema.columns().iter();
1302 let column_factory = self.column_writer_factory(row_group_index);
1303 for field in &self.arrow_schema.fields {
1304 column_factory.get_arrow_column_writer(
1305 field.data_type(),
1306 &self.props,
1307 &mut leaves,
1308 &mut writers,
1309 )?;
1310 }
1311 Ok(writers)
1312 }
1313
1314 #[cfg(feature = "encryption")]
1315 fn column_writer_factory(&self, row_group_idx: usize) -> ArrowColumnWriterFactory {
1316 ArrowColumnWriterFactory::new()
1317 .with_page_store_factory(self.page_store_factory.clone())
1318 .with_file_encryptor(row_group_idx, self.file_encryptor.clone())
1319 }
1320
1321 #[cfg(not(feature = "encryption"))]
1322 fn column_writer_factory(&self, _row_group_idx: usize) -> ArrowColumnWriterFactory {
1323 ArrowColumnWriterFactory::new().with_page_store_factory(self.page_store_factory.clone())
1324 }
1325}
1326
1327struct ArrowColumnWriterFactory {
1329 page_store_factory: Arc<dyn PageStoreFactory>,
1331 #[cfg(feature = "encryption")]
1332 row_group_index: usize,
1333 #[cfg(feature = "encryption")]
1334 file_encryptor: Option<Arc<FileEncryptor>>,
1335}
1336
1337impl ArrowColumnWriterFactory {
1338 pub fn new() -> Self {
1339 Self {
1340 page_store_factory: Arc::new(InMemoryPageStoreFactory),
1341 #[cfg(feature = "encryption")]
1342 row_group_index: 0,
1343 #[cfg(feature = "encryption")]
1344 file_encryptor: None,
1345 }
1346 }
1347
1348 pub fn with_page_store_factory(
1350 mut self,
1351 page_store_factory: Arc<dyn PageStoreFactory>,
1352 ) -> Self {
1353 self.page_store_factory = page_store_factory;
1354 self
1355 }
1356
1357 #[cfg(feature = "encryption")]
1358 pub fn with_file_encryptor(
1359 mut self,
1360 row_group_index: usize,
1361 file_encryptor: Option<Arc<FileEncryptor>>,
1362 ) -> Self {
1363 self.row_group_index = row_group_index;
1364 self.file_encryptor = file_encryptor;
1365 self
1366 }
1367
1368 #[cfg(feature = "encryption")]
1369 fn create_page_writer(
1370 &self,
1371 column_descriptor: &ColumnDescPtr,
1372 column_index: usize,
1373 ) -> Result<Box<ArrowPageWriter>> {
1374 let column_path = column_descriptor.path().string();
1375 let page_encryptor = PageEncryptor::create_if_column_encrypted(
1376 self.file_encryptor.as_ref(),
1377 self.row_group_index,
1378 column_index,
1379 &column_path,
1380 )?;
1381 let args = PageStoreArgs::new(column_index, column_descriptor);
1382 let store = self.page_store_factory.create(&args)?;
1383 Ok(Box::new(
1384 ArrowPageWriter::new(store).with_encryptor(page_encryptor),
1385 ))
1386 }
1387
1388 #[cfg(not(feature = "encryption"))]
1389 fn create_page_writer(
1390 &self,
1391 column_descriptor: &ColumnDescPtr,
1392 column_index: usize,
1393 ) -> Result<Box<ArrowPageWriter>> {
1394 let args = PageStoreArgs::new(column_index, column_descriptor);
1395 let store = self.page_store_factory.create(&args)?;
1396 Ok(Box::new(ArrowPageWriter::new(store)))
1397 }
1398
1399 fn get_arrow_column_writer(
1402 &self,
1403 data_type: &ArrowDataType,
1404 props: &WriterPropertiesPtr,
1405 leaves: &mut Iter<'_, ColumnDescPtr>,
1406 out: &mut Vec<ArrowColumnWriter>,
1407 ) -> Result<()> {
1408 let col = |desc: &ColumnDescPtr| -> Result<ArrowColumnWriter> {
1410 let page_writer = self.create_page_writer(desc, out.len())?;
1411 let chunk = page_writer.buffer.clone();
1412 let writer = get_column_writer(desc.clone(), props.clone(), page_writer);
1413 Ok(ArrowColumnWriter {
1414 chunk,
1415 writer: ArrowColumnWriterImpl::Column(writer),
1416 })
1417 };
1418
1419 let bytes = |desc: &ColumnDescPtr| -> Result<ArrowColumnWriter> {
1421 let page_writer = self.create_page_writer(desc, out.len())?;
1422 let chunk = page_writer.buffer.clone();
1423 let writer = GenericColumnWriter::new(desc.clone(), props.clone(), page_writer);
1424 Ok(ArrowColumnWriter {
1425 chunk,
1426 writer: ArrowColumnWriterImpl::ByteArray(writer),
1427 })
1428 };
1429
1430 match data_type {
1431 _ if data_type.is_primitive() => out.push(col(leaves.next().unwrap())?),
1432 ArrowDataType::FixedSizeBinary(_) | ArrowDataType::Boolean | ArrowDataType::Null => {
1433 out.push(col(leaves.next().unwrap())?)
1434 }
1435 ArrowDataType::LargeBinary
1436 | ArrowDataType::Binary
1437 | ArrowDataType::Utf8
1438 | ArrowDataType::LargeUtf8
1439 | ArrowDataType::BinaryView
1440 | ArrowDataType::Utf8View => out.push(bytes(leaves.next().unwrap())?),
1441 ArrowDataType::List(f)
1442 | ArrowDataType::LargeList(f)
1443 | ArrowDataType::FixedSizeList(f, _)
1444 | ArrowDataType::ListView(f)
1445 | ArrowDataType::LargeListView(f) => {
1446 self.get_arrow_column_writer(f.data_type(), props, leaves, out)?
1447 }
1448 ArrowDataType::Struct(fields) => {
1449 for field in fields {
1450 self.get_arrow_column_writer(field.data_type(), props, leaves, out)?
1451 }
1452 }
1453 ArrowDataType::Map(f, _) => match f.data_type() {
1454 ArrowDataType::Struct(f) => {
1455 self.get_arrow_column_writer(f[0].data_type(), props, leaves, out)?;
1456 self.get_arrow_column_writer(f[1].data_type(), props, leaves, out)?
1457 }
1458 _ => unreachable!("invalid map type"),
1459 },
1460 ArrowDataType::Dictionary(_, value_type) => match value_type.as_ref() {
1461 ArrowDataType::Utf8
1462 | ArrowDataType::LargeUtf8
1463 | ArrowDataType::Binary
1464 | ArrowDataType::LargeBinary => out.push(bytes(leaves.next().unwrap())?),
1465 ArrowDataType::Utf8View | ArrowDataType::BinaryView => {
1466 out.push(bytes(leaves.next().unwrap())?)
1467 }
1468 ArrowDataType::FixedSizeBinary(_) => out.push(bytes(leaves.next().unwrap())?),
1469 _ => out.push(col(leaves.next().unwrap())?),
1470 },
1471 ArrowDataType::RunEndEncoded(_, value_field) => {
1472 self.get_arrow_column_writer(value_field.data_type(), props, leaves, out)?
1473 }
1474 _ => {
1475 return Err(ParquetError::NYI(format!(
1476 "Attempting to write an Arrow type {data_type} to parquet that is not yet implemented"
1477 )));
1478 }
1479 }
1480 Ok(())
1481 }
1482}
1483
1484fn write_leaf(
1485 writer: &mut ColumnWriter<'_>,
1486 column: &dyn arrow_array::Array,
1487 levels: &ArrayLevels,
1488) -> Result<usize> {
1489 let indices = levels.non_null_indices();
1490
1491 match writer {
1492 ColumnWriter::Int32ColumnWriter(typed) => {
1494 match column.data_type() {
1495 ArrowDataType::Null => {
1496 let array = Int32Array::new_null(column.len());
1497 write_primitive(typed, array.values(), levels)
1498 }
1499 ArrowDataType::Int8 => {
1500 let array: Int32Array = column.as_primitive::<Int8Type>().unary(|x| x as i32);
1501 write_primitive(typed, array.values(), levels)
1502 }
1503 ArrowDataType::Int16 => {
1504 let array: Int32Array = column.as_primitive::<Int16Type>().unary(|x| x as i32);
1505 write_primitive(typed, array.values(), levels)
1506 }
1507 ArrowDataType::Int32 => {
1508 write_primitive(typed, column.as_primitive::<Int32Type>().values(), levels)
1509 }
1510 ArrowDataType::UInt8 => {
1511 let array: Int32Array = column.as_primitive::<UInt8Type>().unary(|x| x as i32);
1512 write_primitive(typed, array.values(), levels)
1513 }
1514 ArrowDataType::UInt16 => {
1515 let array: Int32Array = column.as_primitive::<UInt16Type>().unary(|x| x as i32);
1516 write_primitive(typed, array.values(), levels)
1517 }
1518 ArrowDataType::UInt32 => {
1519 let array = column.as_primitive::<UInt32Type>();
1522 write_primitive(typed, array.values().inner().typed_data(), levels)
1523 }
1524 ArrowDataType::Date32 => {
1525 let array = column.as_primitive::<Date32Type>();
1526 write_primitive(typed, array.values(), levels)
1527 }
1528 ArrowDataType::Time32(TimeUnit::Second) => {
1529 let array = column.as_primitive::<Time32SecondType>();
1530 write_primitive(typed, array.values(), levels)
1531 }
1532 ArrowDataType::Time32(TimeUnit::Millisecond) => {
1533 let array = column.as_primitive::<Time32MillisecondType>();
1534 write_primitive(typed, array.values(), levels)
1535 }
1536 ArrowDataType::Date64 => {
1537 let array: Int32Array = column
1539 .as_primitive::<Date64Type>()
1540 .unary(|x| (x / 86_400_000) as _);
1541
1542 write_primitive(typed, array.values(), levels)
1543 }
1544 ArrowDataType::Decimal32(_, _) => {
1545 let array = column
1546 .as_primitive::<Decimal32Type>()
1547 .unary::<_, Int32Type>(|v| v);
1548 write_primitive(typed, array.values(), levels)
1549 }
1550 ArrowDataType::Decimal64(_, _) => {
1551 let array = column
1553 .as_primitive::<Decimal64Type>()
1554 .unary::<_, Int32Type>(|v| v as i32);
1555 write_primitive(typed, array.values(), levels)
1556 }
1557 ArrowDataType::Decimal128(_, _) => {
1558 let array = column
1560 .as_primitive::<Decimal128Type>()
1561 .unary::<_, Int32Type>(|v| v as i32);
1562 write_primitive(typed, array.values(), levels)
1563 }
1564 ArrowDataType::Decimal256(_, _) => {
1565 let array = column
1567 .as_primitive::<Decimal256Type>()
1568 .unary::<_, Int32Type>(|v| v.as_i128() as i32);
1569 write_primitive(typed, array.values(), levels)
1570 }
1571 d => Err(ParquetError::General(format!("Cannot coerce {d} to I32"))),
1572 }
1573 }
1574 ColumnWriter::BoolColumnWriter(typed) => {
1575 let array = column.as_boolean();
1576 let values = get_bool_array_slice(array, indices.iter().copied());
1577 typed.write_batch_internal(
1578 values.as_slice(),
1579 None,
1580 levels.def_level_data().as_ref(),
1581 levels.rep_level_data().as_ref(),
1582 None,
1583 None,
1584 None,
1585 )
1586 }
1587 ColumnWriter::Int64ColumnWriter(typed) => {
1588 match column.data_type() {
1589 ArrowDataType::Date64 => {
1590 let array = column
1591 .as_primitive::<Date64Type>()
1592 .reinterpret_cast::<Int64Type>();
1593
1594 write_primitive(typed, array.values(), levels)
1595 }
1596 ArrowDataType::Int64 => {
1597 let array = column.as_primitive::<Int64Type>();
1598 write_primitive(typed, array.values(), levels)
1599 }
1600 ArrowDataType::UInt64 => {
1601 let values = column.as_primitive::<UInt64Type>().values();
1602 let array = values.inner().typed_data::<i64>();
1605 write_primitive(typed, array, levels)
1606 }
1607 ArrowDataType::Time64(TimeUnit::Microsecond) => {
1608 let array = column.as_primitive::<Time64MicrosecondType>();
1609 write_primitive(typed, array.values(), levels)
1610 }
1611 ArrowDataType::Time64(TimeUnit::Nanosecond) => {
1612 let array = column.as_primitive::<Time64NanosecondType>();
1613 write_primitive(typed, array.values(), levels)
1614 }
1615 ArrowDataType::Timestamp(unit, _) => match unit {
1616 TimeUnit::Second => {
1617 let array = column.as_primitive::<TimestampSecondType>();
1618 write_primitive(typed, array.values(), levels)
1619 }
1620 TimeUnit::Millisecond => {
1621 let array = column.as_primitive::<TimestampMillisecondType>();
1622 write_primitive(typed, array.values(), levels)
1623 }
1624 TimeUnit::Microsecond => {
1625 let array = column.as_primitive::<TimestampMicrosecondType>();
1626 write_primitive(typed, array.values(), levels)
1627 }
1628 TimeUnit::Nanosecond => {
1629 let array = column.as_primitive::<TimestampNanosecondType>();
1630 write_primitive(typed, array.values(), levels)
1631 }
1632 },
1633 ArrowDataType::Duration(unit) => match unit {
1634 TimeUnit::Second => {
1635 let array = column.as_primitive::<DurationSecondType>();
1636 write_primitive(typed, array.values(), levels)
1637 }
1638 TimeUnit::Millisecond => {
1639 let array = column.as_primitive::<DurationMillisecondType>();
1640 write_primitive(typed, array.values(), levels)
1641 }
1642 TimeUnit::Microsecond => {
1643 let array = column.as_primitive::<DurationMicrosecondType>();
1644 write_primitive(typed, array.values(), levels)
1645 }
1646 TimeUnit::Nanosecond => {
1647 let array = column.as_primitive::<DurationNanosecondType>();
1648 write_primitive(typed, array.values(), levels)
1649 }
1650 },
1651 ArrowDataType::Decimal64(_, _) => {
1652 let array = column
1653 .as_primitive::<Decimal64Type>()
1654 .reinterpret_cast::<Int64Type>();
1655 write_primitive(typed, array.values(), levels)
1656 }
1657 ArrowDataType::Decimal128(_, _) => {
1658 let array = column
1660 .as_primitive::<Decimal128Type>()
1661 .unary::<_, Int64Type>(|v| v as i64);
1662 write_primitive(typed, array.values(), levels)
1663 }
1664 ArrowDataType::Decimal256(_, _) => {
1665 let array = column
1667 .as_primitive::<Decimal256Type>()
1668 .unary::<_, Int64Type>(|v| v.as_i128() as i64);
1669 write_primitive(typed, array.values(), levels)
1670 }
1671 d => Err(ParquetError::General(format!("Cannot coerce {d} to I64"))),
1672 }
1673 }
1674 ColumnWriter::Int96ColumnWriter(_typed) => {
1675 unreachable!("Currently unreachable because data type not supported")
1676 }
1677 ColumnWriter::FloatColumnWriter(typed) => {
1678 let array = column.as_primitive::<Float32Type>();
1679 write_primitive(typed, array.values(), levels)
1680 }
1681 ColumnWriter::DoubleColumnWriter(typed) => {
1682 let array = column.as_primitive::<Float64Type>();
1683 write_primitive(typed, array.values(), levels)
1684 }
1685 ColumnWriter::ByteArrayColumnWriter(_) => {
1686 unreachable!("should use ByteArrayWriter")
1687 }
1688 ColumnWriter::FixedLenByteArrayColumnWriter(typed) => {
1689 let bytes = match column.data_type() {
1690 ArrowDataType::Interval(interval_unit) => match interval_unit {
1691 IntervalUnit::YearMonth => {
1692 let array = column.as_primitive::<IntervalYearMonthType>();
1693 get_interval_ym_array_slice(array, indices.iter().copied())
1694 }
1695 IntervalUnit::DayTime => {
1696 let array = column.as_primitive::<IntervalDayTimeType>();
1697 get_interval_dt_array_slice(array, indices.iter().copied())
1698 }
1699 IntervalUnit::MonthDayNano => {
1700 return Err(ParquetError::NYI(format!(
1701 "Attempting to write an Arrow interval type {interval_unit:?} to parquet that is not yet implemented"
1702 )));
1703 }
1704 },
1705 ArrowDataType::FixedSizeBinary(_) => {
1706 let array = column.as_fixed_size_binary();
1707 get_fsb_array_slice(array, indices.iter().copied())
1708 }
1709 ArrowDataType::Decimal32(_, _) => {
1710 let array = column.as_primitive::<Decimal32Type>();
1711 get_decimal_array_slice(array, indices.iter().copied())
1712 }
1713 ArrowDataType::Decimal64(_, _) => {
1714 let array = column.as_primitive::<Decimal64Type>();
1715 get_decimal_array_slice(array, indices.iter().copied())
1716 }
1717 ArrowDataType::Decimal128(_, _) => {
1718 let array = column.as_primitive::<Decimal128Type>();
1719 get_decimal_array_slice(array, indices.iter().copied())
1720 }
1721 ArrowDataType::Decimal256(_, _) => {
1722 let array = column.as_primitive::<Decimal256Type>();
1723 get_decimal_array_slice(array, indices.iter().copied())
1724 }
1725 ArrowDataType::Float16 => {
1726 let array = column.as_primitive::<Float16Type>();
1727 get_float_16_array_slice(array, indices.iter().copied())
1728 }
1729 _ => {
1730 return Err(ParquetError::NYI(
1731 "Attempting to write an Arrow type that is not yet implemented".to_string(),
1732 ));
1733 }
1734 };
1735 typed.write_batch_internal(
1736 bytes.as_slice(),
1737 None,
1738 levels.def_level_data().as_ref(),
1739 levels.rep_level_data().as_ref(),
1740 None,
1741 None,
1742 None,
1743 )
1744 }
1745 }
1746}
1747
1748fn write_primitive<E: ColumnValueEncoder>(
1749 writer: &mut GenericColumnWriter<E>,
1750 values: &E::Values,
1751 levels: &ArrayLevels,
1752) -> Result<usize> {
1753 writer.write_batch_internal(
1754 values,
1755 Some(levels.non_null_indices()),
1756 levels.def_level_data().as_ref(),
1757 levels.rep_level_data().as_ref(),
1758 None,
1759 None,
1760 None,
1761 )
1762}
1763
1764fn get_bool_array_slice(
1765 array: &arrow_array::BooleanArray,
1766 indices: impl ExactSizeIterator<Item = usize>,
1767) -> Vec<bool> {
1768 let mut values = Vec::with_capacity(indices.len());
1769 for i in indices {
1770 values.push(array.value(i))
1771 }
1772 values
1773}
1774
1775fn get_interval_ym_array_slice(
1778 array: &arrow_array::IntervalYearMonthArray,
1779 indices: impl ExactSizeIterator<Item = usize>,
1780) -> Vec<FixedLenByteArray> {
1781 chunk_array_slice(12, indices, move |i, chunk| {
1782 let value = array.value(i);
1783 chunk[0..4].copy_from_slice(&value.to_le_bytes());
1784 })
1785}
1786
1787fn get_interval_dt_array_slice(
1790 array: &arrow_array::IntervalDayTimeArray,
1791 indices: impl ExactSizeIterator<Item = usize>,
1792) -> Vec<FixedLenByteArray> {
1793 chunk_array_slice(12, indices, move |i, chunk| {
1794 let value = array.value(i);
1795 chunk[4..8].copy_from_slice(&value.days.to_le_bytes());
1796 chunk[8..12].copy_from_slice(&value.milliseconds.to_le_bytes());
1797 })
1798}
1799
1800trait NativeDecimalType: DecimalType {
1801 type NativeBytes: AsRef<[u8]>;
1802
1803 fn to_be_bytes(value: Self::Native) -> Self::NativeBytes;
1804}
1805impl NativeDecimalType for Decimal32Type {
1806 type NativeBytes = [u8; Self::BYTE_LENGTH];
1807
1808 fn to_be_bytes(value: Self::Native) -> Self::NativeBytes {
1809 value.to_be_bytes()
1810 }
1811}
1812impl NativeDecimalType for Decimal64Type {
1813 type NativeBytes = [u8; Self::BYTE_LENGTH];
1814
1815 fn to_be_bytes(value: Self::Native) -> Self::NativeBytes {
1816 value.to_be_bytes()
1817 }
1818}
1819impl NativeDecimalType for Decimal128Type {
1820 type NativeBytes = [u8; Self::BYTE_LENGTH];
1821
1822 fn to_be_bytes(value: Self::Native) -> Self::NativeBytes {
1823 value.to_be_bytes()
1824 }
1825}
1826impl NativeDecimalType for Decimal256Type {
1827 type NativeBytes = [u8; Self::BYTE_LENGTH];
1828
1829 fn to_be_bytes(value: Self::Native) -> Self::NativeBytes {
1830 value.to_be_bytes()
1831 }
1832}
1833
1834fn get_decimal_array_slice<T: NativeDecimalType>(
1835 array: &PrimitiveArray<T>,
1836 indices: impl ExactSizeIterator<Item = usize>,
1837) -> Vec<FixedLenByteArray> {
1838 let chunk_size = decimal_length_from_precision(array.precision());
1839 assert!(chunk_size <= T::BYTE_LENGTH);
1840
1841 if chunk_size == T::BYTE_LENGTH {
1842 chunk_array_slice(chunk_size, indices, move |i, chunk| {
1844 let as_be_bytes = T::to_be_bytes(array.value(i));
1845 chunk.copy_from_slice(as_be_bytes.as_ref());
1846 })
1847 } else {
1848 chunk_array_slice(chunk_size, indices, move |i, chunk| {
1849 let as_be_bytes = T::to_be_bytes(array.value(i));
1850 let resized_value = &as_be_bytes.as_ref()[(T::BYTE_LENGTH - chunk.len())..];
1851 chunk.copy_from_slice(resized_value);
1852 })
1853 }
1854}
1855
1856fn get_float_16_array_slice(
1857 array: &arrow_array::Float16Array,
1858 indices: impl ExactSizeIterator<Item = usize>,
1859) -> Vec<FixedLenByteArray> {
1860 chunk_array_slice(2, indices, move |i, chunk| {
1861 let value = array.value(i).to_le_bytes();
1862 chunk.copy_from_slice(&value);
1863 })
1864}
1865
1866fn get_fsb_array_slice(
1867 array: &arrow_array::FixedSizeBinaryArray,
1868 indices: impl ExactSizeIterator<Item = usize>,
1869) -> Vec<FixedLenByteArray> {
1870 chunk_array_slice(array.value_size(), indices, move |i, chunk| {
1871 let value = array.value(i);
1872 chunk.copy_from_slice(value);
1873 })
1874}
1875
1876#[inline]
1877fn chunk_array_slice(
1878 chunk_size: usize,
1879 indices: impl ExactSizeIterator<Item = usize>,
1880 writer: impl Fn(usize, &mut [u8]),
1881) -> Vec<FixedLenByteArray> {
1882 let capacity = indices.len() * chunk_size;
1883 let mut arena = vec![0; capacity];
1886 for (i, chunk) in indices.zip(arena.chunks_exact_mut(chunk_size)) {
1887 writer(i, chunk);
1888 }
1889 chunk_contiguous_vec(arena, chunk_size)
1890}
1891
1892fn chunk_contiguous_vec(arena: Vec<u8>, chunk_size: usize) -> Vec<FixedLenByteArray> {
1893 let mut values = Vec::with_capacity(arena.len() / chunk_size);
1894 let mut arena = Bytes::from(arena);
1895 while arena.len() >= chunk_size {
1896 let slice = arena.split_to(chunk_size);
1897 values.push(FixedLenByteArray::from(ByteArray::from(slice)));
1898 }
1899 values
1900}
1901
1902#[cfg(test)]
1903mod tests {
1904 use super::*;
1905 use std::cmp::Ordering;
1906 use std::collections::HashMap;
1907
1908 use std::fs::File;
1909
1910 use crate::arrow::arrow_reader::{ParquetRecordBatchReader, ParquetRecordBatchReaderBuilder};
1911 use crate::arrow::{ARROW_SCHEMA_META_KEY, PARQUET_FIELD_ID_META_KEY};
1912 use crate::column::page::{Page, PageReader};
1913 use crate::file::metadata::thrift::PageHeader;
1914 use crate::file::page_index::column_index::ColumnIndexMetaData;
1915 use crate::file::reader::SerializedPageReader;
1916 use crate::parquet_thrift::{ReadThrift, ThriftSliceInputProtocol};
1917 use crate::schema::types::ColumnPath;
1918 use arrow::datatypes::ToByteSlice;
1919 use arrow::datatypes::{DataType, Schema};
1920 use arrow::error::Result as ArrowResult;
1921 use arrow::util::data_gen::create_random_array;
1922 use arrow::util::pretty::pretty_format_batches;
1923 use arrow::{array::*, buffer::Buffer};
1924 use arrow_buffer::{IntervalDayTime, IntervalMonthDayNano, NullBuffer, OffsetBuffer, i256};
1925 use arrow_schema::Fields;
1926 use half::f16;
1927 use num_traits::{FromPrimitive, ToPrimitive};
1928 use tempfile::tempfile;
1929
1930 use crate::basic::Encoding;
1931 use crate::data_type::AsBytes;
1932 use crate::file::metadata::{ColumnChunkMetaData, ParquetMetaData, ParquetMetaDataReader};
1933 use crate::file::properties::{
1934 BloomFilterPosition, EnabledStatistics, ReaderProperties, WriterVersion,
1935 };
1936 use crate::file::serialized_reader::ReadOptionsBuilder;
1937 use crate::file::{
1938 reader::{FileReader, SerializedFileReader},
1939 statistics::Statistics,
1940 };
1941
1942 #[derive(Debug, Default)]
1947 struct RecordingPageStore {
1948 next: u64,
1949 blobs: HashMap<u64, Bytes>,
1950 puts: Arc<std::sync::atomic::AtomicUsize>,
1951 }
1952
1953 impl PageStore for RecordingPageStore {
1954 fn put(&mut self, value: Bytes) -> Result<PageKey> {
1955 let id = 100 + self.next * 7;
1957 self.next += 1;
1958 self.puts.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1959 self.blobs.insert(id, value);
1960 Ok(PageKey::new(id))
1961 }
1962
1963 fn take(&mut self, key: PageKey) -> Result<Bytes> {
1964 self.blobs
1965 .remove(&key.get())
1966 .ok_or_else(|| ParquetError::General(format!("missing key {}", key.get())))
1967 }
1968 }
1969
1970 #[derive(Debug)]
1971 struct RecordingPageStoreFactory {
1972 puts: Arc<std::sync::atomic::AtomicUsize>,
1973 }
1974
1975 impl PageStoreFactory for RecordingPageStoreFactory {
1976 fn create(&self, _args: &PageStoreArgs<'_>) -> Result<Box<dyn PageStore>> {
1977 Ok(Box::new(RecordingPageStore {
1978 puts: self.puts.clone(),
1979 ..Default::default()
1980 }))
1981 }
1982 }
1983
1984 #[test]
1988 fn custom_page_store_is_byte_identical_to_default() {
1989 let schema = Arc::new(Schema::new(vec![
1990 Field::new("i", DataType::Int32, true),
1991 Field::new("s", DataType::Utf8, true),
1993 ]));
1994 let i = Int32Array::from(vec![Some(1), None, Some(3), Some(4), Some(5), Some(6)]);
1995 let s = StringArray::from(vec![
1996 Some("a"),
1997 Some("bb"),
1998 Some("a"),
1999 None,
2000 Some("bb"),
2001 Some("ccc"),
2002 ]);
2003 let batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(i), Arc::new(s)]).unwrap();
2004
2005 let props = WriterProperties::builder()
2008 .set_max_row_group_row_count(Some(3))
2009 .build();
2010
2011 let write = |factory: Option<Arc<dyn PageStoreFactory>>| {
2012 let mut buffer = Vec::new();
2013 let mut opts = ArrowWriterOptions::new().with_properties(props.clone());
2014 if let Some(factory) = factory {
2015 opts = opts.with_page_store_factory(factory);
2016 }
2017 let mut writer =
2018 ArrowWriter::try_new_with_options(&mut buffer, schema.clone(), opts).unwrap();
2019 writer.write(&batch).unwrap();
2020 writer.close().unwrap();
2021 buffer
2022 };
2023
2024 let default_bytes = write(None);
2025
2026 let puts = Arc::new(std::sync::atomic::AtomicUsize::new(0));
2027 let custom_bytes = write(Some(Arc::new(RecordingPageStoreFactory {
2028 puts: puts.clone(),
2029 })));
2030
2031 assert!(
2032 puts.load(std::sync::atomic::Ordering::Relaxed) > 0,
2033 "custom PageStore was never written to"
2034 );
2035 assert_eq!(
2036 default_bytes, custom_bytes,
2037 "a custom PageStore must produce byte-identical output to the default"
2038 );
2039 }
2040
2041 #[test]
2047 fn dictionary_column_round_trips_with_offset_index_disabled() {
2048 let schema = Arc::new(Schema::new(vec![Field::new("k", DataType::Int32, true)]));
2049
2050 let values: Vec<Option<i32>> = (0..50_000).map(|i| Some(i % 8)).collect();
2053 let array = Int32Array::from(values.clone());
2054 let batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(array)]).unwrap();
2055
2056 let props = WriterProperties::builder()
2057 .set_offset_index_disabled(true)
2058 .set_data_page_row_count_limit(4096)
2059 .build();
2060 let opts = ArrowWriterOptions::new().with_properties(props);
2061
2062 let mut buffer = Vec::new();
2063 let mut writer =
2064 ArrowWriter::try_new_with_options(&mut buffer, schema.clone(), opts).unwrap();
2065 writer.write(&batch).unwrap();
2066 writer.close().unwrap();
2067
2068 let reader = ParquetRecordBatchReader::try_new(Bytes::from(buffer), values.len()).unwrap();
2069 let read: Vec<RecordBatch> = reader.collect::<ArrowResult<_>>().unwrap();
2070 let read_values: Vec<Option<i32>> = read
2071 .iter()
2072 .flat_map(|b| b.column(0).as_primitive::<Int32Type>().iter())
2073 .collect();
2074 assert_eq!(read_values, values);
2075 }
2076
2077 #[test]
2082 fn dictionary_page_is_routed_through_the_store() {
2083 #[derive(Debug, Default)]
2085 struct SizeRecordingPageStore {
2086 blobs: Vec<Bytes>,
2087 bytes_put: Arc<std::sync::atomic::AtomicUsize>,
2088 }
2089 impl PageStore for SizeRecordingPageStore {
2090 fn put(&mut self, value: Bytes) -> Result<PageKey> {
2091 self.bytes_put
2092 .fetch_add(value.len(), std::sync::atomic::Ordering::Relaxed);
2093 let key = PageKey::new(self.blobs.len() as u64);
2094 self.blobs.push(value);
2095 Ok(key)
2096 }
2097 fn take(&mut self, key: PageKey) -> Result<Bytes> {
2098 Ok(std::mem::take(&mut self.blobs[key.get() as usize]))
2099 }
2100 }
2101 #[derive(Debug)]
2102 struct Factory {
2103 bytes_put: Arc<std::sync::atomic::AtomicUsize>,
2104 }
2105 impl PageStoreFactory for Factory {
2106 fn create(&self, _args: &PageStoreArgs<'_>) -> Result<Box<dyn PageStore>> {
2107 Ok(Box::new(SizeRecordingPageStore {
2108 bytes_put: self.bytes_put.clone(),
2109 ..Default::default()
2110 }))
2111 }
2112 }
2113
2114 let schema = Arc::new(Schema::new(vec![Field::new("s", DataType::Utf8, false)]));
2115 let values: Vec<&str> = (0..2048)
2118 .map(|i| ["alpha", "beta", "gamma", "delta"][i % 4])
2119 .collect();
2120 let batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(StringArray::from(values))])
2121 .unwrap();
2122
2123 let bytes_put = Arc::new(std::sync::atomic::AtomicUsize::new(0));
2124 let opts = ArrowWriterOptions::new().with_page_store_factory(Arc::new(Factory {
2125 bytes_put: bytes_put.clone(),
2126 }));
2127
2128 let mut buffer = Vec::new();
2131 let mut writer =
2132 ArrowWriter::try_new_with_options(&mut buffer, schema.clone(), opts).unwrap();
2133 writer.write(&batch).unwrap();
2134 writer.close().unwrap();
2135
2136 let reader = SerializedFileReader::new(Bytes::from(buffer)).unwrap();
2137 let column = reader.metadata().row_group(0).column(0);
2138 assert!(
2139 column.dictionary_page_offset().is_some(),
2140 "expected the column to be dictionary-encoded"
2141 );
2142
2143 assert_eq!(
2147 bytes_put.load(std::sync::atomic::Ordering::Relaxed) as i64,
2148 column.compressed_size(),
2149 "the dictionary page must pass through the store like any other page"
2150 );
2151 }
2152
2153 #[test]
2154 fn arrow_writer() {
2155 let schema = Schema::new(vec![
2157 Field::new("a", DataType::Int32, false),
2158 Field::new("b", DataType::Int32, true),
2159 ]);
2160
2161 let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
2163 let b = Int32Array::from(vec![Some(1), None, None, Some(4), Some(5)]);
2164
2165 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]).unwrap();
2167
2168 roundtrip(batch, Some(SMALL_SIZE / 2));
2169 }
2170
2171 fn get_bytes_after_close(schema: SchemaRef, expected_batch: &RecordBatch) -> Vec<u8> {
2172 let mut buffer = vec![];
2173
2174 let mut writer = ArrowWriter::try_new(&mut buffer, schema, None).unwrap();
2175 writer.write(expected_batch).unwrap();
2176 writer.close().unwrap();
2177
2178 buffer
2179 }
2180
2181 fn get_bytes_by_into_inner(schema: SchemaRef, expected_batch: &RecordBatch) -> Vec<u8> {
2182 let mut writer = ArrowWriter::try_new(Vec::new(), schema, None).unwrap();
2183 writer.write(expected_batch).unwrap();
2184 writer.into_inner().unwrap()
2185 }
2186
2187 #[test]
2188 fn roundtrip_bytes() {
2189 let schema = Arc::new(Schema::new(vec![
2191 Field::new("a", DataType::Int32, false),
2192 Field::new("b", DataType::Int32, true),
2193 ]));
2194
2195 let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
2197 let b = Int32Array::from(vec![Some(1), None, None, Some(4), Some(5)]);
2198
2199 let expected_batch =
2201 RecordBatch::try_new(schema.clone(), vec![Arc::new(a), Arc::new(b)]).unwrap();
2202
2203 for buffer in [
2204 get_bytes_after_close(schema.clone(), &expected_batch),
2205 get_bytes_by_into_inner(schema, &expected_batch),
2206 ] {
2207 let cursor = Bytes::from(buffer);
2208 let mut record_batch_reader = ParquetRecordBatchReader::try_new(cursor, 1024).unwrap();
2209
2210 let actual_batch = record_batch_reader
2211 .next()
2212 .expect("No batch found")
2213 .expect("Unable to get batch");
2214
2215 assert_eq!(expected_batch.schema(), actual_batch.schema());
2216 assert_eq!(expected_batch.num_columns(), actual_batch.num_columns());
2217 assert_eq!(expected_batch.num_rows(), actual_batch.num_rows());
2218 for i in 0..expected_batch.num_columns() {
2219 let expected_data = expected_batch.column(i).to_data();
2220 let actual_data = actual_batch.column(i).to_data();
2221
2222 assert_eq!(expected_data, actual_data);
2223 }
2224 }
2225 }
2226
2227 #[test]
2228 fn arrow_writer_non_null() {
2229 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
2230 let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
2231
2232 RoundTripTest::new(Arc::new(a))
2233 .with_schema(Arc::new(schema))
2234 .run();
2235 }
2236
2237 #[test]
2238 fn arrow_writer_list() {
2239 let schema = Schema::new(vec![Field::new(
2241 "a",
2242 DataType::List(Arc::new(Field::new_list_field(DataType::Int32, false))),
2243 true,
2244 )]);
2245
2246 let a_values = Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2248
2249 let a_value_offsets = arrow::buffer::Buffer::from([0, 1, 3, 3, 6, 10].to_byte_slice());
2252
2253 let a_list_data = ArrayData::builder(DataType::List(Arc::new(Field::new_list_field(
2255 DataType::Int32,
2256 false,
2257 ))))
2258 .len(5)
2259 .add_buffer(a_value_offsets)
2260 .add_child_data(a_values.into_data())
2261 .null_bit_buffer(Some(Buffer::from([0b00011011])))
2262 .build()
2263 .unwrap();
2264 let a = ListArray::from(a_list_data);
2265 assert_eq!(a.null_count(), 1);
2266
2267 RoundTripTest::new(Arc::new(a))
2268 .with_schema(Arc::new(schema))
2269 .run();
2270 }
2271
2272 #[test]
2273 fn arrow_writer_list_non_null() {
2274 let schema = Schema::new(vec![Field::new(
2276 "a",
2277 DataType::List(Arc::new(Field::new_list_field(DataType::Int32, false))),
2278 false,
2279 )]);
2280
2281 let a_values = Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2283
2284 let a_value_offsets = arrow::buffer::Buffer::from([0, 1, 3, 3, 6, 10].to_byte_slice());
2287
2288 let a_list_data = ArrayData::builder(DataType::List(Arc::new(Field::new_list_field(
2290 DataType::Int32,
2291 false,
2292 ))))
2293 .len(5)
2294 .add_buffer(a_value_offsets)
2295 .add_child_data(a_values.into_data())
2296 .build()
2297 .unwrap();
2298 let a = ListArray::from(a_list_data);
2299 assert_eq!(a.null_count(), 0);
2300
2301 RoundTripTest::new(Arc::new(a))
2302 .with_schema(Arc::new(schema))
2303 .run();
2304 }
2305
2306 #[test]
2307 fn arrow_writer_list_view() {
2308 let list_field = Arc::new(Field::new_list_field(DataType::Int32, false));
2309 let schema = Schema::new(vec![Field::new(
2310 "a",
2311 DataType::ListView(list_field.clone()),
2312 true,
2313 )]);
2314
2315 let a = ListViewArray::new(
2317 list_field,
2318 vec![0, 1, 0, 3, 6].into(),
2319 vec![1, 2, 0, 3, 4].into(),
2320 Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
2321 Some(vec![true, true, false, true, true].into()),
2322 );
2323 assert_eq!(a.null_count(), 1);
2324
2325 RoundTripTest::new(Arc::new(a))
2326 .with_schema(Arc::new(schema))
2327 .run();
2328 }
2329
2330 #[test]
2331 fn arrow_writer_list_view_non_null() {
2332 let list_field = Arc::new(Field::new_list_field(DataType::Int32, false));
2333 let schema = Schema::new(vec![Field::new(
2334 "a",
2335 DataType::ListView(list_field.clone()),
2336 false,
2337 )]);
2338
2339 let a = ListViewArray::new(
2341 list_field,
2342 vec![0, 1, 0, 3, 6].into(),
2343 vec![1, 2, 0, 3, 4].into(),
2344 Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
2345 None,
2346 );
2347 assert_eq!(a.null_count(), 0);
2348
2349 RoundTripTest::new(Arc::new(a))
2350 .with_schema(Arc::new(schema))
2351 .run();
2352 }
2353
2354 #[test]
2355 fn arrow_writer_list_view_out_of_order() {
2356 let list_field = Arc::new(Field::new_list_field(DataType::Int32, false));
2357 let schema = Schema::new(vec![Field::new(
2358 "a",
2359 DataType::ListView(list_field.clone()),
2360 false,
2361 )]);
2362
2363 let a = ListViewArray::new(
2365 list_field,
2366 vec![0, 1, 0, 6, 3].into(),
2367 vec![1, 2, 0, 4, 3].into(),
2368 Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
2369 None,
2370 );
2371 assert_eq!(a.null_count(), 0);
2372
2373 RoundTripTest::new(Arc::new(a))
2374 .with_schema(Arc::new(schema))
2375 .run();
2376 }
2377
2378 #[test]
2379 fn arrow_writer_large_list_view() {
2380 let list_field = Arc::new(Field::new_list_field(DataType::Int32, false));
2381 let schema = Schema::new(vec![Field::new(
2382 "a",
2383 DataType::LargeListView(list_field.clone()),
2384 true,
2385 )]);
2386
2387 let a = LargeListViewArray::new(
2389 list_field,
2390 vec![0i64, 1, 0, 3, 6].into(),
2391 vec![1i64, 2, 0, 3, 4].into(),
2392 Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])),
2393 Some(vec![true, true, false, true, true].into()),
2394 );
2395 assert_eq!(a.null_count(), 1);
2396
2397 RoundTripTest::new(Arc::new(a))
2398 .with_schema(Arc::new(schema))
2399 .run();
2400 }
2401
2402 #[test]
2403 fn arrow_writer_list_view_with_struct() {
2404 let struct_fields = Fields::from(vec![
2406 Field::new("id", DataType::Int32, false),
2407 Field::new("name", DataType::Utf8, false),
2408 ]);
2409 let struct_type = DataType::Struct(struct_fields.clone());
2410 let list_field = Arc::new(Field::new("item", struct_type.clone(), false));
2411
2412 let schema = Schema::new(vec![Field::new(
2413 "a",
2414 DataType::ListView(list_field.clone()),
2415 true,
2416 )]);
2417
2418 let id_array = Int32Array::from(vec![1, 2, 3, 4, 5]);
2420 let name_array = StringArray::from(vec!["a", "b", "c", "d", "e"]);
2421 let struct_array = StructArray::new(
2422 struct_fields,
2423 vec![Arc::new(id_array), Arc::new(name_array)],
2424 None,
2425 );
2426
2427 let list_view = ListViewArray::new(
2429 list_field,
2430 vec![0, 2, 2].into(), vec![2, 0, 3].into(), Arc::new(struct_array),
2433 Some(vec![true, false, true].into()),
2434 );
2435 assert_eq!(list_view.null_count(), 1);
2436
2437 RoundTripTest::new(Arc::new(list_view))
2438 .with_schema(Arc::new(schema))
2439 .run();
2440 }
2441
2442 #[test]
2443 fn arrow_writer_binary() {
2444 let raw_string_values = vec!["foo", "bar", "baz", "quux"];
2445 let raw_binary_values = [
2446 b"foo".to_vec(),
2447 b"bar".to_vec(),
2448 b"baz".to_vec(),
2449 b"quux".to_vec(),
2450 ];
2451 let raw_binary_value_refs = raw_binary_values
2452 .iter()
2453 .map(|x| x.as_slice())
2454 .collect::<Vec<_>>();
2455
2456 let string_values = StringArray::from(raw_string_values.clone());
2457 let binary_values = BinaryArray::from(raw_binary_value_refs);
2458 assert_eq!(string_values.null_count(), 0);
2459 assert_eq!(binary_values.null_count(), 0);
2460
2461 RoundTripTest::new(Arc::new(string_values)).run();
2462 RoundTripTest::new(Arc::new(binary_values)).run();
2463 }
2464
2465 #[test]
2466 fn arrow_writer_binary_view() {
2467 let raw_string_values = vec!["foo", "bar", "large payload over 12 bytes", "lulu"];
2468 let raw_binary_values = vec![
2469 b"foo".to_vec(),
2470 b"bar".to_vec(),
2471 b"large payload over 12 bytes".to_vec(),
2472 b"lulu".to_vec(),
2473 ];
2474 let nullable_string_values =
2475 vec![Some("foo"), None, Some("large payload over 12 bytes"), None];
2476
2477 let string_view_values = StringViewArray::from(raw_string_values);
2478 let binary_view_values = BinaryViewArray::from_iter_values(raw_binary_values);
2479 let nullable_string_view_values = StringViewArray::from(nullable_string_values);
2480
2481 RoundTripTest::new(Arc::new(string_view_values)).run();
2482 RoundTripTest::new(Arc::new(binary_view_values)).run();
2483 RoundTripTest::new(Arc::new(nullable_string_view_values)).run();
2484 }
2485
2486 #[test]
2487 fn arrow_writer_binary_view_long_value() {
2488 let long = "a".repeat(128);
2492 let raw_string_values = vec!["foo", long.as_str(), "bar"];
2493 let raw_binary_values = vec![b"foo".to_vec(), long.as_bytes().to_vec(), b"bar".to_vec()];
2494
2495 let string_view_values: ArrayRef = Arc::new(StringViewArray::from(raw_string_values));
2496 let binary_view_values: ArrayRef =
2497 Arc::new(BinaryViewArray::from_iter_values(raw_binary_values));
2498
2499 RoundTripTest::new(Arc::clone(&string_view_values))
2500 .with_nullable(false)
2501 .run();
2502 RoundTripTest::new(Arc::clone(&binary_view_values))
2503 .with_nullable(false)
2504 .run();
2505 }
2506
2507 fn get_decimal_batch(precision: u8, scale: i8) -> RecordBatch {
2508 let decimal_field = Field::new("a", DataType::Decimal128(precision, scale), false);
2509 let schema = Schema::new(vec![decimal_field]);
2510
2511 let decimal_values = vec![10_000, 50_000, 0, -100]
2512 .into_iter()
2513 .map(Some)
2514 .collect::<Decimal128Array>()
2515 .with_precision_and_scale(precision, scale)
2516 .unwrap();
2517
2518 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(decimal_values)]).unwrap()
2519 }
2520
2521 #[test]
2522 fn arrow_writer_decimal() {
2523 let batch_int32_decimal = get_decimal_batch(5, 2);
2525 roundtrip(batch_int32_decimal, Some(SMALL_SIZE / 2));
2526 let batch_int64_decimal = get_decimal_batch(12, 2);
2528 roundtrip(batch_int64_decimal, Some(SMALL_SIZE / 2));
2529 let batch_fixed_len_byte_array_decimal = get_decimal_batch(30, 2);
2531 roundtrip(batch_fixed_len_byte_array_decimal, Some(SMALL_SIZE / 2));
2532 }
2533
2534 #[test]
2535 fn arrow_writer_complex() {
2536 let struct_field_d = Arc::new(Field::new("d", DataType::Float64, true));
2538 let struct_field_f = Arc::new(Field::new("f", DataType::Float32, true));
2539 let struct_field_g = Arc::new(Field::new_list(
2540 "g",
2541 Field::new_list_field(DataType::Int16, true),
2542 false,
2543 ));
2544 let struct_field_h = Arc::new(Field::new_list(
2545 "h",
2546 Field::new_list_field(DataType::Int16, false),
2547 true,
2548 ));
2549 let struct_field_e = Arc::new(Field::new_struct(
2550 "e",
2551 vec![
2552 struct_field_f.clone(),
2553 struct_field_g.clone(),
2554 struct_field_h.clone(),
2555 ],
2556 false,
2557 ));
2558 let schema = Schema::new(vec![
2559 Field::new("a", DataType::Int32, false),
2560 Field::new("b", DataType::Int32, true),
2561 Field::new_struct(
2562 "c",
2563 vec![struct_field_d.clone(), struct_field_e.clone()],
2564 false,
2565 ),
2566 ]);
2567
2568 let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
2570 let b = Int32Array::from(vec![Some(1), None, None, Some(4), Some(5)]);
2571 let d = Float64Array::from(vec![None, None, None, Some(1.0), None]);
2572 let f = Float32Array::from(vec![Some(0.0), None, Some(333.3), None, Some(5.25)]);
2573
2574 let g_value = Int16Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2575
2576 let g_value_offsets = arrow::buffer::Buffer::from([0, 1, 3, 3, 6, 10].to_byte_slice());
2579
2580 let g_list_data = ArrayData::builder(struct_field_g.data_type().clone())
2582 .len(5)
2583 .add_buffer(g_value_offsets.clone())
2584 .add_child_data(g_value.to_data())
2585 .build()
2586 .unwrap();
2587 let g = ListArray::from(g_list_data);
2588 let h_list_data = ArrayData::builder(struct_field_h.data_type().clone())
2590 .len(5)
2591 .add_buffer(g_value_offsets)
2592 .add_child_data(g_value.to_data())
2593 .null_bit_buffer(Some(Buffer::from([0b00011011])))
2594 .build()
2595 .unwrap();
2596 let h = ListArray::from(h_list_data);
2597
2598 let e = StructArray::from(vec![
2599 (struct_field_f, Arc::new(f) as ArrayRef),
2600 (struct_field_g, Arc::new(g) as ArrayRef),
2601 (struct_field_h, Arc::new(h) as ArrayRef),
2602 ]);
2603
2604 let c = StructArray::from(vec![
2605 (struct_field_d, Arc::new(d) as ArrayRef),
2606 (struct_field_e, Arc::new(e) as ArrayRef),
2607 ]);
2608
2609 let batch = RecordBatch::try_new(
2611 Arc::new(schema),
2612 vec![Arc::new(a), Arc::new(b), Arc::new(c)],
2613 )
2614 .unwrap();
2615
2616 roundtrip(batch.clone(), Some(SMALL_SIZE / 2));
2617 roundtrip(batch, Some(SMALL_SIZE / 3));
2618 }
2619
2620 #[test]
2621 fn arrow_writer_complex_mixed() {
2622 let offset_field = Arc::new(Field::new("offset", DataType::Int32, false));
2627 let partition_field = Arc::new(Field::new("partition", DataType::Int64, true));
2628 let topic_field = Arc::new(Field::new("topic", DataType::Utf8, true));
2629 let schema = Schema::new(vec![Field::new(
2630 "some_nested_object",
2631 DataType::Struct(Fields::from(vec![
2632 offset_field.clone(),
2633 partition_field.clone(),
2634 topic_field.clone(),
2635 ])),
2636 false,
2637 )]);
2638
2639 let offset = Int32Array::from(vec![1, 2, 3, 4, 5]);
2641 let partition = Int64Array::from(vec![Some(1), None, None, Some(4), Some(5)]);
2642 let topic = StringArray::from(vec![Some("A"), None, Some("A"), Some(""), None]);
2643
2644 let some_nested_object = StructArray::from(vec![
2645 (offset_field, Arc::new(offset) as ArrayRef),
2646 (partition_field, Arc::new(partition) as ArrayRef),
2647 (topic_field, Arc::new(topic) as ArrayRef),
2648 ]);
2649
2650 let batch =
2652 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(some_nested_object)]).unwrap();
2653
2654 roundtrip(batch, Some(SMALL_SIZE / 2));
2655 }
2656
2657 #[test]
2658 fn arrow_writer_map() {
2659 let json_content = r#"
2661 {"stocks":{"long": "$AAA", "short": "$BBB"}}
2662 {"stocks":{"long": null, "long": "$CCC", "short": null}}
2663 {"stocks":{"hedged": "$YYY", "long": null, "short": "$D"}}
2664 "#;
2665 let entries_struct_type = DataType::Struct(Fields::from(vec![
2666 Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Utf8, false),
2667 Field::new(Field::MAP_VALUE_FIELD_DEFAULT_NAME, DataType::Utf8, true),
2668 ]));
2669 let stocks_field = Field::new(
2670 "stocks",
2671 DataType::Map(
2672 Arc::new(Field::new(
2673 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
2674 entries_struct_type,
2675 false,
2676 )),
2677 false,
2678 ),
2679 true,
2680 );
2681 let schema = Arc::new(Schema::new(vec![stocks_field]));
2682 let builder = arrow::json::ReaderBuilder::new(schema).with_batch_size(64);
2683 let mut reader = builder.build(std::io::Cursor::new(json_content)).unwrap();
2684
2685 let batch = reader.next().unwrap().unwrap();
2686 roundtrip(batch, None);
2687 }
2688
2689 #[test]
2690 fn arrow_writer_2_level_struct() {
2691 let field_c = Field::new("c", DataType::Int32, true);
2693 let field_b = Field::new("b", DataType::Struct(vec![field_c].into()), true);
2694 let type_a = DataType::Struct(vec![field_b.clone()].into());
2695 let field_a = Field::new("a", type_a, true);
2696 let schema = Schema::new(vec![field_a.clone()]);
2697
2698 let c = Int32Array::from(vec![Some(1), None, Some(3), None, None, Some(6)]);
2700 let b_data = ArrayDataBuilder::new(field_b.data_type().clone())
2701 .len(6)
2702 .null_bit_buffer(Some(Buffer::from([0b00100111])))
2703 .add_child_data(c.into_data())
2704 .build()
2705 .unwrap();
2706 let b = StructArray::from(b_data);
2707 let a_data = ArrayDataBuilder::new(field_a.data_type().clone())
2708 .len(6)
2709 .null_bit_buffer(Some(Buffer::from([0b00101111])))
2710 .add_child_data(b.into_data())
2711 .build()
2712 .unwrap();
2713 let a = StructArray::from(a_data);
2714
2715 assert_eq!(a.null_count(), 1);
2716 assert_eq!(a.column(0).null_count(), 2);
2717
2718 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
2720
2721 roundtrip(batch, Some(SMALL_SIZE / 2));
2722 }
2723
2724 #[test]
2725 fn arrow_writer_2_level_struct_non_null() {
2726 let field_c = Field::new("c", DataType::Int32, false);
2728 let type_b = DataType::Struct(vec![field_c].into());
2729 let field_b = Field::new("b", type_b.clone(), false);
2730 let type_a = DataType::Struct(vec![field_b].into());
2731 let field_a = Field::new("a", type_a.clone(), false);
2732 let schema = Schema::new(vec![field_a]);
2733
2734 let c = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
2736 let b_data = ArrayDataBuilder::new(type_b)
2737 .len(6)
2738 .add_child_data(c.into_data())
2739 .build()
2740 .unwrap();
2741 let b = StructArray::from(b_data);
2742 let a_data = ArrayDataBuilder::new(type_a)
2743 .len(6)
2744 .add_child_data(b.into_data())
2745 .build()
2746 .unwrap();
2747 let a = StructArray::from(a_data);
2748
2749 assert_eq!(a.null_count(), 0);
2750 assert_eq!(a.column(0).null_count(), 0);
2751
2752 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
2754
2755 roundtrip(batch, Some(SMALL_SIZE / 2));
2756 }
2757
2758 #[test]
2759 fn arrow_writer_2_level_struct_mixed_null() {
2760 let field_c = Field::new("c", DataType::Int32, false);
2762 let type_b = DataType::Struct(vec![field_c].into());
2763 let field_b = Field::new("b", type_b.clone(), true);
2764 let type_a = DataType::Struct(vec![field_b].into());
2765 let field_a = Field::new("a", type_a.clone(), false);
2766 let schema = Schema::new(vec![field_a]);
2767
2768 let c = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
2770 let b_data = ArrayDataBuilder::new(type_b)
2771 .len(6)
2772 .null_bit_buffer(Some(Buffer::from([0b00100111])))
2773 .add_child_data(c.into_data())
2774 .build()
2775 .unwrap();
2776 let b = StructArray::from(b_data);
2777 let a_data = ArrayDataBuilder::new(type_a)
2779 .len(6)
2780 .add_child_data(b.into_data())
2781 .build()
2782 .unwrap();
2783 let a = StructArray::from(a_data);
2784
2785 assert_eq!(a.null_count(), 0);
2786 assert_eq!(a.column(0).null_count(), 2);
2787
2788 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
2790
2791 roundtrip(batch, Some(SMALL_SIZE / 2));
2792 }
2793
2794 #[test]
2795 fn arrow_writer_2_level_struct_mixed_null_2() {
2796 let field_c = Field::new("c", DataType::Int32, false);
2798 let field_d = Field::new("d", DataType::FixedSizeBinary(4), false);
2799 let field_e = Field::new(
2800 "e",
2801 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
2802 false,
2803 );
2804
2805 let field_b = Field::new(
2806 "b",
2807 DataType::Struct(vec![field_c, field_d, field_e].into()),
2808 false,
2809 );
2810 let type_a = DataType::Struct(vec![field_b.clone()].into());
2811 let field_a = Field::new("a", type_a, true);
2812 let schema = Schema::new(vec![field_a.clone()]);
2813
2814 let c = Int32Array::from_iter_values(0..6);
2816 let d = FixedSizeBinaryArray::try_from_iter(
2817 ["aaaa", "bbbb", "cccc", "dddd", "eeee", "ffff"].into_iter(),
2818 )
2819 .expect("four byte values");
2820 let e = Int32DictionaryArray::from_iter(["one", "two", "three", "four", "five", "one"]);
2821 let b_data = ArrayDataBuilder::new(field_b.data_type().clone())
2822 .len(6)
2823 .add_child_data(c.into_data())
2824 .add_child_data(d.into_data())
2825 .add_child_data(e.into_data())
2826 .build()
2827 .unwrap();
2828 let b = StructArray::from(b_data);
2829 let a_data = ArrayDataBuilder::new(field_a.data_type().clone())
2830 .len(6)
2831 .null_bit_buffer(Some(Buffer::from([0b00100101])))
2832 .add_child_data(b.into_data())
2833 .build()
2834 .unwrap();
2835 let a = StructArray::from(a_data);
2836
2837 assert_eq!(a.null_count(), 3);
2838 assert_eq!(a.column(0).null_count(), 0);
2839
2840 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
2842
2843 roundtrip(batch, Some(SMALL_SIZE / 2));
2844 }
2845
2846 #[test]
2847 fn test_fixed_size_binary_in_dict() {
2848 fn test_fixed_size_binary_in_dict_inner<K>()
2849 where
2850 K: ArrowDictionaryKeyType,
2851 K::Native: FromPrimitive + ToPrimitive + TryFrom<u8>,
2852 <<K as arrow_array::ArrowPrimitiveType>::Native as TryFrom<u8>>::Error: std::fmt::Debug,
2853 {
2854 let field = Field::new(
2855 "a",
2856 DataType::Dictionary(
2857 Box::new(K::DATA_TYPE),
2858 Box::new(DataType::FixedSizeBinary(4)),
2859 ),
2860 false,
2861 );
2862 let schema = Schema::new(vec![field]);
2863
2864 let keys: Vec<K::Native> = vec![
2865 K::Native::try_from(0u8).unwrap(),
2866 K::Native::try_from(0u8).unwrap(),
2867 K::Native::try_from(1u8).unwrap(),
2868 ];
2869 let keys = PrimitiveArray::<K>::from_iter_values(keys);
2870 let values = FixedSizeBinaryArray::try_from_iter(
2871 vec![vec![0, 0, 0, 0], vec![1, 1, 1, 1]].into_iter(),
2872 )
2873 .unwrap();
2874
2875 let data = DictionaryArray::<K>::new(keys, Arc::new(values));
2876 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(data)]).unwrap();
2877 roundtrip(batch, None);
2878 }
2879
2880 test_fixed_size_binary_in_dict_inner::<UInt8Type>();
2881 test_fixed_size_binary_in_dict_inner::<UInt16Type>();
2882 test_fixed_size_binary_in_dict_inner::<UInt32Type>();
2883 test_fixed_size_binary_in_dict_inner::<UInt16Type>();
2884 test_fixed_size_binary_in_dict_inner::<Int8Type>();
2885 test_fixed_size_binary_in_dict_inner::<Int16Type>();
2886 test_fixed_size_binary_in_dict_inner::<Int32Type>();
2887 test_fixed_size_binary_in_dict_inner::<Int64Type>();
2888 }
2889
2890 #[test]
2891 fn test_empty_dict() {
2892 let struct_fields = Fields::from(vec![Field::new(
2893 "dict",
2894 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
2895 false,
2896 )]);
2897
2898 let schema = Schema::new(vec![Field::new_struct(
2899 "struct",
2900 struct_fields.clone(),
2901 true,
2902 )]);
2903 let dictionary = Arc::new(DictionaryArray::new(
2904 Int32Array::new_null(5),
2905 Arc::new(StringArray::new_null(0)),
2906 ));
2907
2908 let s = StructArray::new(
2909 struct_fields,
2910 vec![dictionary],
2911 Some(NullBuffer::new_null(5)),
2912 );
2913
2914 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(s)]).unwrap();
2915 roundtrip(batch, None);
2916 }
2917 #[test]
2918 fn arrow_writer_page_size() {
2919 let schema = Arc::new(Schema::new(vec![Field::new("col", DataType::Utf8, false)]));
2920
2921 let mut builder = StringBuilder::with_capacity(100, 329 * 10_000);
2922
2923 for i in 0..10 {
2925 let value = i
2926 .to_string()
2927 .repeat(10)
2928 .chars()
2929 .take(10)
2930 .collect::<String>();
2931
2932 builder.append_value(value);
2933 }
2934
2935 let array = Arc::new(builder.finish());
2936
2937 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
2938
2939 let file = tempfile::tempfile().unwrap();
2940
2941 let props = WriterProperties::builder()
2943 .set_data_page_size_limit(1)
2944 .set_dictionary_page_size_limit(1)
2945 .set_write_batch_size(1)
2946 .build();
2947
2948 let mut writer =
2949 ArrowWriter::try_new(file.try_clone().unwrap(), batch.schema(), Some(props))
2950 .expect("Unable to write file");
2951 writer.write(&batch).unwrap();
2952 writer.close().unwrap();
2953
2954 let options = ReadOptionsBuilder::new().with_page_index().build();
2955 let reader =
2956 SerializedFileReader::new_with_options(file.try_clone().unwrap(), options).unwrap();
2957
2958 let column = reader.metadata().row_group(0).columns();
2959
2960 assert_eq!(column.len(), 1);
2961
2962 assert!(
2965 column[0].dictionary_page_offset().is_some(),
2966 "Expected a dictionary page"
2967 );
2968
2969 assert!(reader.metadata().offset_index().is_some());
2970 let offset_indexes = &reader.metadata().offset_index().unwrap()[0];
2971
2972 let page_locations = offset_indexes[0].page_locations.clone();
2973
2974 assert_eq!(
2977 page_locations.len(),
2978 10,
2979 "Expected 10 pages but got {page_locations:#?}"
2980 );
2981 }
2982
2983 #[test]
2984 fn arrow_writer_float_nans() {
2985 let f16_field = Field::new("a", DataType::Float16, false);
2986 let f32_field = Field::new("b", DataType::Float32, false);
2987 let f64_field = Field::new("c", DataType::Float64, false);
2988 let schema = Schema::new(vec![f16_field, f32_field, f64_field]);
2989
2990 let f16_values = (0..MEDIUM_SIZE)
2991 .map(|i| {
2992 Some(if i % 2 == 0 {
2993 f16::NAN
2994 } else {
2995 f16::from_f32(i as f32)
2996 })
2997 })
2998 .collect::<Float16Array>();
2999
3000 let f32_values = (0..MEDIUM_SIZE)
3001 .map(|i| Some(if i % 2 == 0 { f32::NAN } else { i as f32 }))
3002 .collect::<Float32Array>();
3003
3004 let f64_values = (0..MEDIUM_SIZE)
3005 .map(|i| Some(if i % 2 == 0 { f64::NAN } else { i as f64 }))
3006 .collect::<Float64Array>();
3007
3008 let batch = RecordBatch::try_new(
3009 Arc::new(schema),
3010 vec![
3011 Arc::new(f16_values),
3012 Arc::new(f32_values),
3013 Arc::new(f64_values),
3014 ],
3015 )
3016 .unwrap();
3017
3018 roundtrip(batch, None);
3019 }
3020
3021 const SMALL_SIZE: usize = 7;
3022 const MEDIUM_SIZE: usize = 63;
3023
3024 fn roundtrip(expected_batch: RecordBatch, max_row_group_size: Option<usize>) -> Vec<Bytes> {
3027 let mut files = vec![];
3028 for version in [WriterVersion::PARQUET_1_0, WriterVersion::PARQUET_2_0] {
3029 let mut props = WriterProperties::builder().set_writer_version(version);
3030
3031 if let Some(size) = max_row_group_size {
3032 props = props.set_max_row_group_row_count(Some(size))
3033 }
3034
3035 let props = props.build();
3036 files.push(roundtrip_opts(&expected_batch, props))
3037 }
3038 files
3039 }
3040
3041 fn roundtrip_opts_with_array_validation<F>(
3045 expected_batch: &RecordBatch,
3046 props: WriterProperties,
3047 validate: F,
3048 ) -> Bytes
3049 where
3050 F: Fn(&ArrayData, &ArrayData),
3051 {
3052 let mut file = vec![];
3053
3054 let mut writer = ArrowWriter::try_new(&mut file, expected_batch.schema(), Some(props))
3055 .expect("Unable to write file");
3056 writer.write(expected_batch).unwrap();
3057 writer.close().unwrap();
3058
3059 let file = Bytes::from(file);
3060 let mut record_batch_reader =
3061 ParquetRecordBatchReader::try_new(file.clone(), 1024).unwrap();
3062
3063 let actual_batch = record_batch_reader
3064 .next()
3065 .expect("No batch found")
3066 .expect("Unable to get batch");
3067
3068 assert_eq!(expected_batch.schema(), actual_batch.schema());
3069 assert_eq!(expected_batch.num_columns(), actual_batch.num_columns());
3070 assert_eq!(expected_batch.num_rows(), actual_batch.num_rows());
3071 for i in 0..expected_batch.num_columns() {
3072 let expected_data = expected_batch.column(i).to_data();
3073 let actual_data = actual_batch.column(i).to_data();
3074 validate(&expected_data, &actual_data);
3075 }
3076
3077 file
3078 }
3079
3080 fn roundtrip_opts(expected_batch: &RecordBatch, props: WriterProperties) -> Bytes {
3081 roundtrip_opts_with_array_validation(expected_batch, props, |a, b| {
3082 a.validate_full().expect("valid expected data");
3083 b.validate_full().expect("valid actual data");
3084 assert_eq!(a, b)
3085 })
3086 }
3087
3088 struct RoundTripTest {
3092 values: ArrayRef,
3093 schema: Option<SchemaRef>,
3095 nullable: bool,
3098 bloom_filter: bool,
3099 bloom_filter_ndv: Option<u64>,
3100 bloom_filter_position: BloomFilterPosition,
3101 }
3102
3103 impl RoundTripTest {
3104 fn new(values: ArrayRef) -> Self {
3106 Self {
3107 values,
3108 schema: None,
3109 nullable: true,
3110 bloom_filter: false,
3111 bloom_filter_ndv: None,
3112 bloom_filter_position: BloomFilterPosition::AfterRowGroup,
3113 }
3114 }
3115
3116 fn with_schema(mut self, schema: SchemaRef) -> Self {
3118 self.schema = Some(schema);
3119 self
3120 }
3121
3122 fn with_nullable(mut self, nullable: bool) -> Self {
3124 self.nullable = nullable;
3125 self
3126 }
3127
3128 fn with_bloom_filter(mut self, bloom_filter: bool) -> Self {
3130 self.bloom_filter = bloom_filter;
3131 self
3132 }
3133
3134 fn with_bloom_filter_ndv(mut self, bloom_filter_ndv: u64) -> Self {
3136 self.bloom_filter_ndv = Some(bloom_filter_ndv);
3137 self
3138 }
3139
3140 fn with_bloom_filter_position(
3142 mut self,
3143 bloom_filter_position: BloomFilterPosition,
3144 ) -> Self {
3145 self.bloom_filter_position = bloom_filter_position;
3146 self
3147 }
3148
3149 fn run(self) -> Vec<Bytes> {
3151 let RoundTripTest {
3152 values,
3153 schema,
3154 nullable,
3155 bloom_filter,
3156 bloom_filter_ndv,
3157 bloom_filter_position,
3158 } = self;
3159
3160 let schema = schema.unwrap_or_else(|| {
3161 let data_type = values.data_type().clone();
3162 Arc::new(Schema::new(vec![Field::new("col", data_type, nullable)]))
3163 });
3164
3165 let encodings = match values.data_type() {
3166 DataType::Utf8 | DataType::LargeUtf8 | DataType::Binary | DataType::LargeBinary => {
3167 vec![
3168 Encoding::PLAIN,
3169 Encoding::DELTA_BYTE_ARRAY,
3170 Encoding::DELTA_LENGTH_BYTE_ARRAY,
3171 ]
3172 }
3173 DataType::Int64
3174 | DataType::Int32
3175 | DataType::Int16
3176 | DataType::Int8
3177 | DataType::UInt64
3178 | DataType::UInt32
3179 | DataType::UInt16
3180 | DataType::UInt8 => vec![
3181 Encoding::PLAIN,
3182 Encoding::DELTA_BINARY_PACKED,
3183 Encoding::BYTE_STREAM_SPLIT,
3184 ],
3185 DataType::Float32 | DataType::Float64 => {
3186 vec![Encoding::PLAIN, Encoding::BYTE_STREAM_SPLIT]
3187 }
3188 _ => vec![Encoding::PLAIN],
3189 };
3190
3191 let expected_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3192
3193 let row_group_sizes = [1024, SMALL_SIZE, SMALL_SIZE / 2, SMALL_SIZE / 2 + 1, 10];
3194
3195 let mut files = vec![];
3196 for dictionary_size in [0, 1, 1024] {
3197 for encoding in &encodings {
3198 for version in [WriterVersion::PARQUET_1_0, WriterVersion::PARQUET_2_0] {
3199 for row_group_size in row_group_sizes {
3200 let mut builder = WriterProperties::builder()
3201 .set_writer_version(version)
3202 .set_max_row_group_row_count(Some(row_group_size))
3203 .set_dictionary_enabled(dictionary_size != 0)
3204 .set_dictionary_page_size_limit(dictionary_size.max(1))
3205 .set_encoding(*encoding)
3206 .set_bloom_filter_enabled(bloom_filter)
3207 .set_bloom_filter_position(bloom_filter_position);
3208 if let Some(ndv) = bloom_filter_ndv {
3209 builder = builder.set_bloom_filter_max_ndv(ndv);
3210 }
3211 let props = builder.build();
3212
3213 files.push(roundtrip_opts(&expected_batch, props))
3214 }
3215 }
3216 }
3217 }
3218 files
3219 }
3220 }
3221
3222 fn values_required<A, I>(iter: I) -> Vec<Bytes>
3223 where
3224 A: From<Vec<I::Item>> + Array + 'static,
3225 I: IntoIterator,
3226 {
3227 let raw_values: Vec<_> = iter.into_iter().collect();
3228 let values = Arc::new(A::from(raw_values));
3229 RoundTripTest::new(values).with_nullable(false).run()
3230 }
3231
3232 fn values_optional<A, I>(iter: I) -> Vec<Bytes>
3233 where
3234 A: From<Vec<Option<I::Item>>> + Array + 'static,
3235 I: IntoIterator,
3236 {
3237 let optional_raw_values: Vec<_> = iter
3238 .into_iter()
3239 .enumerate()
3240 .map(|(i, v)| if i % 2 == 0 { None } else { Some(v) })
3241 .collect();
3242 let optional_values = Arc::new(A::from(optional_raw_values));
3243 RoundTripTest::new(optional_values).run()
3244 }
3245
3246 fn required_and_optional<A, I>(iter: I)
3247 where
3248 A: From<Vec<I::Item>> + From<Vec<Option<I::Item>>> + Array + 'static,
3249 I: IntoIterator + Clone,
3250 {
3251 values_required::<A, I>(iter.clone());
3252 values_optional::<A, I>(iter);
3253 }
3254
3255 fn check_bloom_filter<T: AsBytes>(
3256 files: Vec<Bytes>,
3257 file_column: String,
3258 positive_values: Vec<T>,
3259 negative_values: Vec<T>,
3260 ) {
3261 files.into_iter().take(1).for_each(|file| {
3262 let file_reader = SerializedFileReader::new_with_options(
3263 file,
3264 ReadOptionsBuilder::new()
3265 .with_reader_properties(
3266 ReaderProperties::builder()
3267 .set_read_bloom_filter(true)
3268 .build(),
3269 )
3270 .build(),
3271 )
3272 .expect("Unable to open file as Parquet");
3273 let metadata = file_reader.metadata();
3274
3275 let mut bloom_filters: Vec<_> = vec![];
3277 for (ri, row_group) in metadata.row_groups().iter().enumerate() {
3278 if let Some((column_index, _)) = row_group
3279 .columns()
3280 .iter()
3281 .enumerate()
3282 .find(|(_, column)| column.column_path().string() == file_column)
3283 {
3284 let row_group_reader = file_reader
3285 .get_row_group(ri)
3286 .expect("Unable to read row group");
3287 if let Some(sbbf) = row_group_reader.get_column_bloom_filter(column_index) {
3288 bloom_filters.push(sbbf.clone());
3289 } else {
3290 panic!("No bloom filter for column named {file_column} found");
3291 }
3292 } else {
3293 panic!("No column named {file_column} found");
3294 }
3295 }
3296
3297 positive_values.iter().for_each(|value| {
3298 let found = bloom_filters.iter().find(|sbbf| sbbf.check(value));
3299 assert!(
3300 found.is_some(),
3301 "{}",
3302 format!("Value {:?} should be in bloom filter", value.as_bytes())
3303 );
3304 });
3305
3306 negative_values.iter().for_each(|value| {
3307 let found = bloom_filters.iter().find(|sbbf| sbbf.check(value));
3308 assert!(
3309 found.is_none(),
3310 "{}",
3311 format!("Value {:?} should not be in bloom filter", value.as_bytes())
3312 );
3313 });
3314 });
3315 }
3316
3317 #[test]
3318 fn all_null_primitive_single_column() {
3319 let values = Arc::new(Int32Array::from(vec![None; SMALL_SIZE]));
3320 RoundTripTest::new(values).run();
3321 }
3322 #[test]
3323 fn null_single_column() {
3324 let values = Arc::new(NullArray::new(SMALL_SIZE));
3325 RoundTripTest::new(values).run();
3326 }
3328
3329 #[test]
3330 fn bool_single_column() {
3331 required_and_optional::<BooleanArray, _>(
3332 [true, false].iter().cycle().copied().take(SMALL_SIZE),
3333 );
3334 }
3335
3336 #[test]
3337 fn bool_large_single_column() {
3338 let values = Arc::new(
3339 [None, Some(true), Some(false)]
3340 .iter()
3341 .cycle()
3342 .copied()
3343 .take(200_000)
3344 .collect::<BooleanArray>(),
3345 );
3346 let schema = Schema::new(vec![Field::new("col", values.data_type().clone(), true)]);
3347 let expected_batch = RecordBatch::try_new(Arc::new(schema), vec![values]).unwrap();
3348 let file = tempfile::tempfile().unwrap();
3349
3350 let mut writer =
3351 ArrowWriter::try_new(file.try_clone().unwrap(), expected_batch.schema(), None)
3352 .expect("Unable to write file");
3353 writer.write(&expected_batch).unwrap();
3354 writer.close().unwrap();
3355 }
3356
3357 #[test]
3358 fn check_page_offset_index_with_nan() {
3359 let values = Arc::new(Float64Array::from(vec![f64::NAN; 10]));
3360 let schema = Schema::new(vec![Field::new("col", DataType::Float64, true)]);
3361 let batch = RecordBatch::try_new(Arc::new(schema), vec![values]).unwrap();
3362
3363 let mut out = Vec::with_capacity(1024);
3364 let mut writer =
3365 ArrowWriter::try_new(&mut out, batch.schema(), None).expect("Unable to write file");
3366 writer.write(&batch).unwrap();
3367 let file_meta_data = writer.close().unwrap();
3368 for row_group in file_meta_data.row_groups() {
3369 for column in row_group.columns() {
3370 assert!(column.offset_index_offset().is_some());
3371 assert!(column.offset_index_length().is_some());
3372 assert!(column.column_index_offset().is_some());
3373 assert!(column.column_index_length().is_some());
3374 }
3375 }
3376 assert!(file_meta_data.column_index().is_some());
3377 if let Some(col_indexes) = file_meta_data.column_index() {
3378 for rg_idx in col_indexes {
3379 for idx in rg_idx {
3380 assert!(idx.nan_counts().is_some());
3381 let ColumnIndexMetaData::DOUBLE(float_idx) = idx else {
3382 panic!("expected double statistics")
3383 };
3384 for i in 0..idx.num_pages() as usize {
3385 assert_eq!(float_idx.nan_count(i), Some(10));
3386 assert_eq!(
3387 f64::NAN.total_cmp(float_idx.min_value(i).unwrap()),
3388 Ordering::Equal
3389 );
3390 assert_eq!(
3391 f64::NAN.total_cmp(float_idx.max_value(i).unwrap()),
3392 Ordering::Equal
3393 );
3394 }
3395 }
3396 }
3397 }
3398 }
3399
3400 #[test]
3401 fn check_page_offset_index_with_mixed_nan() {
3402 let schema = Arc::new(Schema::new(vec![Field::new(
3403 "col",
3404 DataType::Float64,
3405 true,
3406 )]));
3407
3408 let mut out = Vec::with_capacity(1024);
3409 let props = WriterProperties::builder()
3410 .set_data_page_row_count_limit(10)
3411 .build();
3412 let mut writer = ArrowWriter::try_new(&mut out, schema.clone(), Some(props))
3413 .expect("Unable to write file");
3414
3415 let values = Arc::new(Float64Array::from(vec![f64::NAN; 10]));
3417 let batch = RecordBatch::try_new(schema.clone(), vec![values]).unwrap();
3418 writer.write(&batch).unwrap();
3419
3420 let values = Arc::new(Float64Array::from(vec![-f64::NAN; 10]));
3422 let batch = RecordBatch::try_new(schema.clone(), vec![values]).unwrap();
3423 writer.write(&batch).unwrap();
3424
3425 let values = Arc::new(Float64Array::from(vec![0_f64; 10]));
3427 let batch = RecordBatch::try_new(schema.clone(), vec![values]).unwrap();
3428 writer.write(&batch).unwrap();
3429
3430 let values = Arc::new(Float64Array::from(vec![
3432 -1.0,
3433 0.0,
3434 f64::NAN,
3435 -f64::NAN,
3436 1.0,
3437 ]));
3438 let batch = RecordBatch::try_new(schema.clone(), vec![values]).unwrap();
3439 writer.write(&batch).unwrap();
3440
3441 let file_meta_data = writer.close().unwrap();
3442
3443 let col_stats = file_meta_data
3445 .row_group(0)
3446 .column(0)
3447 .statistics()
3448 .expect("missing column chunk statistics");
3449
3450 assert_eq!(col_stats.nan_count_opt(), Some(22));
3451 assert_eq!(col_stats.min_bytes_opt(), Some((-1.0f64).as_bytes()));
3452 assert_eq!(col_stats.max_bytes_opt(), Some(1.0f64.as_bytes()));
3453
3454 assert!(file_meta_data.column_index().is_some());
3455 let col_idx = &file_meta_data.column_index().as_ref().unwrap()[0][0];
3456 assert_eq!(col_idx.num_pages(), 4);
3457
3458 let ColumnIndexMetaData::DOUBLE(float_idx) = col_idx else {
3460 panic!("expected double statistics")
3461 };
3462
3463 assert_eq!(float_idx.nan_counts, Some(vec![10, 10, 0, 2]));
3464 assert_eq!(
3465 f64::NAN.total_cmp(float_idx.min_value(0).unwrap()),
3466 Ordering::Equal
3467 );
3468 assert_eq!(
3469 f64::NAN.total_cmp(float_idx.max_value(0).unwrap()),
3470 Ordering::Equal
3471 );
3472 assert_eq!(
3473 (-f64::NAN).total_cmp(float_idx.min_value(1).unwrap()),
3474 Ordering::Equal
3475 );
3476 assert_eq!(
3477 (-f64::NAN).total_cmp(float_idx.max_value(1).unwrap()),
3478 Ordering::Equal
3479 );
3480 assert_eq!(float_idx.min_value(2), Some(&0.0));
3481 assert_eq!(float_idx.max_value(2), Some(&0.0));
3482 assert_eq!(float_idx.min_value(3), Some(&-1.0));
3483 assert_eq!(float_idx.max_value(3), Some(&1.0));
3484 }
3485
3486 #[test]
3487 fn i8_single_column() {
3488 required_and_optional::<Int8Array, _>(0..SMALL_SIZE as i8);
3489 }
3490
3491 #[test]
3492 fn i16_single_column() {
3493 required_and_optional::<Int16Array, _>(0..SMALL_SIZE as i16);
3494 }
3495
3496 #[test]
3497 fn i32_single_column() {
3498 required_and_optional::<Int32Array, _>(0..SMALL_SIZE as i32);
3499 }
3500
3501 #[test]
3502 fn i64_single_column() {
3503 required_and_optional::<Int64Array, _>(0..SMALL_SIZE as i64);
3504 }
3505
3506 #[test]
3507 fn u8_single_column() {
3508 required_and_optional::<UInt8Array, _>(0..SMALL_SIZE as u8);
3509 }
3510
3511 #[test]
3512 fn u16_single_column() {
3513 required_and_optional::<UInt16Array, _>(0..SMALL_SIZE as u16);
3514 }
3515
3516 #[test]
3517 fn u32_single_column() {
3518 required_and_optional::<UInt32Array, _>(0..SMALL_SIZE as u32);
3519 }
3520
3521 #[test]
3522 fn u64_single_column() {
3523 required_and_optional::<UInt64Array, _>(0..SMALL_SIZE as u64);
3524 }
3525
3526 #[test]
3527 fn f32_single_column() {
3528 required_and_optional::<Float32Array, _>((0..SMALL_SIZE).map(|i| i as f32));
3529 }
3530
3531 #[test]
3532 fn f64_single_column() {
3533 required_and_optional::<Float64Array, _>((0..SMALL_SIZE).map(|i| i as f64));
3534 }
3535
3536 #[test]
3541 fn timestamp_second_single_column() {
3542 let raw_values: Vec<_> = (0..SMALL_SIZE as i64).collect();
3543 let values = Arc::new(TimestampSecondArray::from(raw_values));
3544
3545 RoundTripTest::new(values).with_nullable(false).run();
3546 }
3547
3548 #[test]
3549 fn timestamp_millisecond_single_column() {
3550 let raw_values: Vec<_> = (0..SMALL_SIZE as i64).collect();
3551 let values = Arc::new(TimestampMillisecondArray::from(raw_values));
3552
3553 RoundTripTest::new(values).with_nullable(false).run();
3554 }
3555
3556 #[test]
3557 fn timestamp_microsecond_single_column() {
3558 let raw_values: Vec<_> = (0..SMALL_SIZE as i64).collect();
3559 let values = Arc::new(TimestampMicrosecondArray::from(raw_values));
3560
3561 RoundTripTest::new(values).with_nullable(false).run();
3562 }
3563
3564 #[test]
3565 fn timestamp_nanosecond_single_column() {
3566 let raw_values: Vec<_> = (0..SMALL_SIZE as i64).collect();
3567 let values = Arc::new(TimestampNanosecondArray::from(raw_values));
3568
3569 RoundTripTest::new(values).with_nullable(false).run();
3570 }
3571
3572 #[test]
3573 fn date32_single_column() {
3574 required_and_optional::<Date32Array, _>(0..SMALL_SIZE as i32);
3575 }
3576
3577 #[test]
3578 fn date64_single_column() {
3579 required_and_optional::<Date64Array, _>(
3581 (0..(SMALL_SIZE as i64 * 86400000)).step_by(86400000),
3582 );
3583 }
3584
3585 #[test]
3586 fn time32_second_single_column() {
3587 required_and_optional::<Time32SecondArray, _>(0..SMALL_SIZE as i32);
3588 }
3589
3590 #[test]
3591 fn time32_millisecond_single_column() {
3592 required_and_optional::<Time32MillisecondArray, _>(0..SMALL_SIZE as i32);
3593 }
3594
3595 #[test]
3596 fn time64_microsecond_single_column() {
3597 required_and_optional::<Time64MicrosecondArray, _>(0..SMALL_SIZE as i64);
3598 }
3599
3600 #[test]
3601 fn time64_nanosecond_single_column() {
3602 required_and_optional::<Time64NanosecondArray, _>(0..SMALL_SIZE as i64);
3603 }
3604
3605 #[test]
3606 fn duration_second_single_column() {
3607 required_and_optional::<DurationSecondArray, _>(0..SMALL_SIZE as i64);
3608 }
3609
3610 #[test]
3611 fn duration_millisecond_single_column() {
3612 required_and_optional::<DurationMillisecondArray, _>(0..SMALL_SIZE as i64);
3613 }
3614
3615 #[test]
3616 fn duration_microsecond_single_column() {
3617 required_and_optional::<DurationMicrosecondArray, _>(0..SMALL_SIZE as i64);
3618 }
3619
3620 #[test]
3621 fn duration_nanosecond_single_column() {
3622 required_and_optional::<DurationNanosecondArray, _>(0..SMALL_SIZE as i64);
3623 }
3624
3625 #[test]
3626 fn interval_year_month_single_column() {
3627 required_and_optional::<IntervalYearMonthArray, _>(0..SMALL_SIZE as i32);
3628 }
3629
3630 #[test]
3631 fn interval_day_time_single_column() {
3632 required_and_optional::<IntervalDayTimeArray, _>(vec![
3633 IntervalDayTime::new(0, 1),
3634 IntervalDayTime::new(0, 3),
3635 IntervalDayTime::new(3, -2),
3636 IntervalDayTime::new(-200, 4),
3637 ]);
3638 }
3639
3640 #[test]
3641 #[should_panic(
3642 expected = "Attempting to write an Arrow interval type MonthDayNano to parquet that is not yet implemented"
3643 )]
3644 fn interval_month_day_nano_single_column() {
3645 required_and_optional::<IntervalMonthDayNanoArray, _>(vec![
3646 IntervalMonthDayNano::new(0, 1, 5),
3647 IntervalMonthDayNano::new(0, 3, 2),
3648 IntervalMonthDayNano::new(3, -2, -5),
3649 IntervalMonthDayNano::new(-200, 4, -1),
3650 ]);
3651 }
3652
3653 #[test]
3654 fn binary_single_column() {
3655 let one_vec: Vec<u8> = (0..SMALL_SIZE as u8).collect();
3656 let many_vecs: Vec<_> = std::iter::repeat_n(one_vec, SMALL_SIZE).collect();
3657 let many_vecs_iter = many_vecs.iter().map(|v| v.as_slice());
3658
3659 values_required::<BinaryArray, _>(many_vecs_iter);
3661 }
3662
3663 #[test]
3664 fn binary_view_single_column() {
3665 let one_vec: Vec<u8> = (0..SMALL_SIZE as u8).collect();
3666 let many_vecs: Vec<_> = std::iter::repeat_n(one_vec, SMALL_SIZE).collect();
3667 let many_vecs_iter = many_vecs.iter().map(|v| v.as_slice());
3668
3669 values_required::<BinaryViewArray, _>(many_vecs_iter);
3671 }
3672
3673 #[test]
3674 fn i32_column_bloom_filter_at_end() {
3675 let array = Arc::new(Int32Array::from_iter(0..SMALL_SIZE as i32));
3676 let files = RoundTripTest::new(array)
3677 .with_nullable(false)
3678 .with_bloom_filter(true)
3679 .with_bloom_filter_position(BloomFilterPosition::End)
3680 .run();
3681
3682 check_bloom_filter(
3683 files,
3684 "col".to_string(),
3685 (0..SMALL_SIZE as i32).collect(),
3686 (SMALL_SIZE as i32 + 1..SMALL_SIZE as i32 + 10).collect(),
3687 );
3688 }
3689
3690 #[test]
3691 fn i32_column_bloom_filter() {
3692 let array = Arc::new(Int32Array::from_iter(0..SMALL_SIZE as i32));
3693 let files = RoundTripTest::new(array)
3694 .with_nullable(false)
3695 .with_bloom_filter(true)
3696 .run();
3697
3698 check_bloom_filter(
3699 files,
3700 "col".to_string(),
3701 (0..SMALL_SIZE as i32).collect(),
3702 (SMALL_SIZE as i32 + 1..SMALL_SIZE as i32 + 10).collect(),
3703 );
3704 }
3705
3706 #[test]
3711 fn i32_column_bloom_filter_fixed_ndv() {
3712 let array = Arc::new(Int32Array::from_iter(0..SMALL_SIZE as i32));
3713
3714 let files = RoundTripTest::new(array.clone())
3716 .with_nullable(false)
3717 .with_bloom_filter(true)
3718 .with_bloom_filter_ndv(1_000_000)
3719 .run();
3720
3721 check_bloom_filter(
3722 files,
3723 "col".to_string(),
3724 (0..SMALL_SIZE as i32).collect(),
3725 (SMALL_SIZE as i32 + 1..SMALL_SIZE as i32 + 10).collect(),
3726 );
3727
3728 let files = RoundTripTest::new(array)
3730 .with_nullable(false)
3731 .with_bloom_filter(true)
3732 .with_bloom_filter_ndv(3)
3733 .run();
3734
3735 check_bloom_filter(
3736 files,
3737 "col".to_string(),
3738 (0..SMALL_SIZE as i32).collect(),
3739 (SMALL_SIZE as i32 + 1..SMALL_SIZE as i32 + 10).collect(),
3740 );
3741 }
3742
3743 #[test]
3744 fn binary_column_bloom_filter() {
3745 let one_vec: Vec<u8> = (0..SMALL_SIZE as u8).collect();
3746 let many_vecs: Vec<_> = std::iter::repeat_n(one_vec, SMALL_SIZE).collect();
3747 let many_vecs_iter = many_vecs.iter().map(|v| v.as_slice());
3748
3749 let array = Arc::new(BinaryArray::from_iter_values(many_vecs_iter));
3750 let files = RoundTripTest::new(array)
3751 .with_nullable(false)
3752 .with_bloom_filter(true)
3753 .run();
3754
3755 check_bloom_filter(
3756 files,
3757 "col".to_string(),
3758 many_vecs,
3759 vec![vec![(SMALL_SIZE + 1) as u8]],
3760 );
3761 }
3762
3763 #[test]
3764 fn empty_string_null_column_bloom_filter() {
3765 let raw_values: Vec<_> = (0..SMALL_SIZE).map(|i| i.to_string()).collect();
3766 let raw_strs = raw_values.iter().map(|s| s.as_str());
3767
3768 let array = Arc::new(StringArray::from_iter_values(raw_strs));
3769 let files = RoundTripTest::new(array)
3770 .with_nullable(false)
3771 .with_bloom_filter(true)
3772 .run();
3773
3774 let optional_raw_values: Vec<_> = raw_values
3775 .iter()
3776 .enumerate()
3777 .filter_map(|(i, v)| if i % 2 == 0 { None } else { Some(v.as_str()) })
3778 .collect();
3779 check_bloom_filter(files, "col".to_string(), optional_raw_values, vec![""]);
3781 }
3782
3783 #[test]
3784 fn large_binary_single_column() {
3785 let one_vec: Vec<u8> = (0..SMALL_SIZE as u8).collect();
3786 let many_vecs: Vec<_> = std::iter::repeat_n(one_vec, SMALL_SIZE).collect();
3787 let many_vecs_iter = many_vecs.iter().map(|v| v.as_slice());
3788
3789 values_required::<LargeBinaryArray, _>(many_vecs_iter);
3791 }
3792
3793 #[test]
3794 fn fixed_size_binary_single_column() {
3795 let mut builder = FixedSizeBinaryBuilder::new(4);
3796 builder.append_value(b"0123").unwrap();
3797 builder.append_null();
3798 builder.append_value(b"8910").unwrap();
3799 builder.append_value(b"1112").unwrap();
3800 let array = Arc::new(builder.finish());
3801
3802 RoundTripTest::new(array).run();
3803 }
3804
3805 #[test]
3806 fn string_single_column() {
3807 let raw_values: Vec<_> = (0..SMALL_SIZE).map(|i| i.to_string()).collect();
3808 let raw_strs = raw_values.iter().map(|s| s.as_str());
3809
3810 required_and_optional::<StringArray, _>(raw_strs);
3811 }
3812
3813 #[test]
3814 fn large_string_single_column() {
3815 let raw_values: Vec<_> = (0..SMALL_SIZE).map(|i| i.to_string()).collect();
3816 let raw_strs = raw_values.iter().map(|s| s.as_str());
3817
3818 required_and_optional::<LargeStringArray, _>(raw_strs);
3819 }
3820
3821 #[test]
3822 fn string_view_single_column() {
3823 let raw_values: Vec<_> = (0..SMALL_SIZE).map(|i| i.to_string()).collect();
3824 let raw_strs = raw_values.iter().map(|s| s.as_str());
3825
3826 required_and_optional::<StringViewArray, _>(raw_strs);
3827 }
3828
3829 #[test]
3830 fn null_list_single_column() {
3831 let null_field = Field::new_list_field(DataType::Null, true);
3832 let list_field = Field::new("emptylist", DataType::List(Arc::new(null_field)), true);
3833
3834 let schema = Schema::new(vec![list_field]);
3835
3836 let a_values = NullArray::new(2);
3838 let a_value_offsets = arrow::buffer::Buffer::from([0, 0, 0, 2].to_byte_slice());
3839 let a_list_data = ArrayData::builder(DataType::List(Arc::new(Field::new_list_field(
3840 DataType::Null,
3841 true,
3842 ))))
3843 .len(3)
3844 .add_buffer(a_value_offsets)
3845 .null_bit_buffer(Some(Buffer::from([0b00000101])))
3846 .add_child_data(a_values.into_data())
3847 .build()
3848 .unwrap();
3849
3850 let a = ListArray::from(a_list_data);
3851
3852 assert!(a.is_valid(0));
3853 assert!(!a.is_valid(1));
3854 assert!(a.is_valid(2));
3855
3856 assert_eq!(a.value(0).len(), 0);
3857 assert_eq!(a.value(2).len(), 2);
3858 assert_eq!(a.value(2).logical_nulls().unwrap().null_count(), 2);
3859
3860 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
3861 roundtrip(batch, None);
3862 }
3863
3864 #[test]
3865 fn list_single_column() {
3866 let a_values = Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
3867 let a_value_offsets = arrow::buffer::Buffer::from([0, 1, 3, 3, 6, 10].to_byte_slice());
3868 let a_list_data = ArrayData::builder(DataType::List(Arc::new(Field::new_list_field(
3869 DataType::Int32,
3870 false,
3871 ))))
3872 .len(5)
3873 .add_buffer(a_value_offsets)
3874 .null_bit_buffer(Some(Buffer::from([0b00011011])))
3875 .add_child_data(a_values.into_data())
3876 .build()
3877 .unwrap();
3878
3879 assert_eq!(a_list_data.null_count(), 1);
3880
3881 let a = ListArray::from(a_list_data);
3882 let values = Arc::new(a);
3883
3884 RoundTripTest::new(values).run();
3885 }
3886
3887 #[test]
3888 fn large_list_single_column() {
3889 let a_values = Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
3890 let a_value_offsets = arrow::buffer::Buffer::from([0i64, 1, 3, 3, 6, 10].to_byte_slice());
3891 let a_list_data = ArrayData::builder(DataType::LargeList(Arc::new(Field::new(
3892 "large_item",
3893 DataType::Int32,
3894 true,
3895 ))))
3896 .len(5)
3897 .add_buffer(a_value_offsets)
3898 .add_child_data(a_values.into_data())
3899 .null_bit_buffer(Some(Buffer::from([0b00011011])))
3900 .build()
3901 .unwrap();
3902
3903 assert_eq!(a_list_data.null_count(), 1);
3905
3906 let a = LargeListArray::from(a_list_data);
3907 let values = Arc::new(a);
3908
3909 RoundTripTest::new(values).run();
3910 }
3911
3912 #[test]
3913 fn list_nested_nulls() {
3914 use arrow::datatypes::Int32Type;
3915 let data = vec![
3916 Some(vec![Some(1)]),
3917 Some(vec![Some(2), Some(3)]),
3918 None,
3919 Some(vec![Some(4), Some(5), None]),
3920 Some(vec![None]),
3921 Some(vec![Some(6), Some(7)]),
3922 ];
3923
3924 let list = ListArray::from_iter_primitive::<Int32Type, _, _>(data.clone());
3925 RoundTripTest::new(Arc::new(list)).run();
3926
3927 let list = LargeListArray::from_iter_primitive::<Int32Type, _, _>(data);
3928 RoundTripTest::new(Arc::new(list)).run();
3929 }
3930
3931 #[test]
3932 fn list_utf8_view_selective_padding_roundtrip() {
3933 let item = Arc::new(Field::new_list_field(DataType::Utf8View, true));
3934 let mut builder = ListBuilder::new(StringViewBuilder::new()).with_field(item);
3935 builder.values().append_value("a");
3936 builder.values().append_null();
3937 builder.append(true);
3938 builder.append(false);
3941 builder.values().append_value("large payload over 12 bytes");
3943 builder.append(true);
3944
3945 RoundTripTest::new(Arc::new(builder.finish())).run();
3946 }
3947
3948 #[test]
3949 fn struct_single_column() {
3950 let a_values = Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
3951 let struct_field_a = Arc::new(Field::new("f", DataType::Int32, false));
3952 let s = StructArray::from(vec![(struct_field_a, Arc::new(a_values) as ArrayRef)]);
3953
3954 let values = Arc::new(s);
3955 RoundTripTest::new(values).with_nullable(false).run();
3956 }
3957
3958 #[test]
3959 fn list_and_map_coerced_names() {
3960 let list_field =
3962 Field::new_list("my_list", Field::new("item", DataType::Int32, false), false);
3963 let map_field = Field::new_map(
3964 "my_map",
3965 "my_entries",
3966 Field::new("my_keys", DataType::Int32, false),
3967 Field::new("my_values", DataType::Int32, true),
3968 false,
3969 true,
3970 );
3971
3972 let list_array = create_random_array(&list_field, 100, 0.0, 0.0).unwrap();
3973 let map_array = create_random_array(&map_field, 100, 0.0, 0.0).unwrap();
3974
3975 let arrow_schema = Arc::new(Schema::new(vec![list_field, map_field]));
3976
3977 let props = Some(WriterProperties::builder().set_coerce_types(true).build());
3979 let file = tempfile::tempfile().unwrap();
3980 let mut writer =
3981 ArrowWriter::try_new(file.try_clone().unwrap(), arrow_schema.clone(), props).unwrap();
3982
3983 let batch = RecordBatch::try_new(arrow_schema, vec![list_array, map_array]).unwrap();
3984 writer.write(&batch).unwrap();
3985 let file_metadata = writer.close().unwrap();
3986
3987 let schema = file_metadata.file_metadata().schema();
3988 let list_field = &schema.get_fields()[0].get_fields()[0];
3990 assert_eq!(list_field.get_fields()[0].name(), "element");
3991
3992 let map_field = &schema.get_fields()[1].get_fields()[0];
3993 assert_eq!(map_field.name(), "key_value");
3995 assert_eq!(map_field.get_fields()[0].name(), "key");
3997 assert_eq!(map_field.get_fields()[1].name(), "value");
3999
4000 let reader = SerializedFileReader::new(file).unwrap();
4002 let file_schema = reader.metadata().file_metadata().schema();
4003 let fields = file_schema.get_fields();
4004 let list_field = &fields[0].get_fields()[0];
4005 assert_eq!(list_field.get_fields()[0].name(), "element");
4006 let map_field = &fields[1].get_fields()[0];
4007 assert_eq!(map_field.name(), "key_value");
4008 assert_eq!(map_field.get_fields()[0].name(), "key");
4009 assert_eq!(map_field.get_fields()[1].name(), "value");
4010 }
4011
4012 #[test]
4013 fn fallback_flush_data_page() {
4014 let raw_values: Vec<_> = (0..MEDIUM_SIZE).map(|i| i.to_string()).collect();
4016 let values = Arc::new(StringArray::from(raw_values));
4017 let encodings = vec![
4018 Encoding::DELTA_BYTE_ARRAY,
4019 Encoding::DELTA_LENGTH_BYTE_ARRAY,
4020 ];
4021 let data_type = values.data_type().clone();
4022 let schema = Arc::new(Schema::new(vec![Field::new("col", data_type, false)]));
4023 let expected_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4024
4025 let row_group_sizes = [1024, SMALL_SIZE, SMALL_SIZE / 2, SMALL_SIZE / 2 + 1, 10];
4026 let data_page_size_limit: usize = 32;
4027 let write_batch_size: usize = 16;
4028
4029 for encoding in &encodings {
4030 for row_group_size in row_group_sizes {
4031 let props = WriterProperties::builder()
4032 .set_writer_version(WriterVersion::PARQUET_2_0)
4033 .set_max_row_group_row_count(Some(row_group_size))
4034 .set_dictionary_enabled(false)
4035 .set_encoding(*encoding)
4036 .set_data_page_size_limit(data_page_size_limit)
4037 .set_write_batch_size(write_batch_size)
4038 .build();
4039
4040 roundtrip_opts_with_array_validation(&expected_batch, props, |a, b| {
4041 let string_array_a = StringArray::from(a.clone());
4042 let string_array_b = StringArray::from(b.clone());
4043 let vec_a: Vec<&str> = string_array_a.iter().map(|v| v.unwrap()).collect();
4044 let vec_b: Vec<&str> = string_array_b.iter().map(|v| v.unwrap()).collect();
4045 assert_eq!(
4046 vec_a, vec_b,
4047 "failed for encoder: {encoding:?} and row_group_size: {row_group_size:?}"
4048 );
4049 });
4050 }
4051 }
4052 }
4053
4054 #[test]
4055 fn arrow_writer_string_dictionary() {
4056 #[expect(deprecated)]
4058 let schema = Arc::new(Schema::new(vec![Field::new_dict(
4059 "dictionary",
4060 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4061 true,
4062 42,
4063 true,
4064 )]));
4065
4066 let d: Int32DictionaryArray = [Some("alpha"), None, Some("beta"), Some("alpha")]
4068 .iter()
4069 .copied()
4070 .collect();
4071
4072 RoundTripTest::new(Arc::new(d)).with_schema(schema).run();
4074 }
4075
4076 #[test]
4077 fn arrow_writer_test_type_compatibility() {
4078 fn ensure_compatible_write<T1, T2>(array1: T1, array2: T2, expected_result: T1)
4079 where
4080 T1: Array + 'static,
4081 T2: Array + 'static,
4082 {
4083 let schema1 = Arc::new(Schema::new(vec![Field::new(
4084 "a",
4085 array1.data_type().clone(),
4086 false,
4087 )]));
4088
4089 let file = tempfile().unwrap();
4090 let mut writer =
4091 ArrowWriter::try_new(file.try_clone().unwrap(), schema1.clone(), None).unwrap();
4092
4093 let rb1 = RecordBatch::try_new(schema1.clone(), vec![Arc::new(array1)]).unwrap();
4094 writer.write(&rb1).unwrap();
4095
4096 let schema2 = Arc::new(Schema::new(vec![Field::new(
4097 "a",
4098 array2.data_type().clone(),
4099 false,
4100 )]));
4101 let rb2 = RecordBatch::try_new(schema2, vec![Arc::new(array2)]).unwrap();
4102 writer.write(&rb2).unwrap();
4103
4104 writer.close().unwrap();
4105
4106 let mut record_batch_reader =
4107 ParquetRecordBatchReader::try_new(file.try_clone().unwrap(), 1024).unwrap();
4108 let actual_batch = record_batch_reader.next().unwrap().unwrap();
4109
4110 let expected_batch =
4111 RecordBatch::try_new(schema1, vec![Arc::new(expected_result)]).unwrap();
4112 assert_eq!(actual_batch, expected_batch);
4113 }
4114
4115 ensure_compatible_write(
4118 DictionaryArray::new(
4119 UInt8Array::from_iter_values(vec![0]),
4120 Arc::new(StringArray::from_iter_values(vec!["parquet"])),
4121 ),
4122 StringArray::from_iter_values(vec!["barquet"]),
4123 DictionaryArray::new(
4124 UInt8Array::from_iter_values(vec![0, 1]),
4125 Arc::new(StringArray::from_iter_values(vec!["parquet", "barquet"])),
4126 ),
4127 );
4128
4129 ensure_compatible_write(
4130 StringArray::from_iter_values(vec!["parquet"]),
4131 DictionaryArray::new(
4132 UInt8Array::from_iter_values(vec![0]),
4133 Arc::new(StringArray::from_iter_values(vec!["barquet"])),
4134 ),
4135 StringArray::from_iter_values(vec!["parquet", "barquet"]),
4136 );
4137
4138 ensure_compatible_write(
4141 DictionaryArray::new(
4142 UInt8Array::from_iter_values(vec![0]),
4143 Arc::new(StringArray::from_iter_values(vec!["parquet"])),
4144 ),
4145 DictionaryArray::new(
4146 UInt16Array::from_iter_values(vec![0]),
4147 Arc::new(StringArray::from_iter_values(vec!["barquet"])),
4148 ),
4149 DictionaryArray::new(
4150 UInt8Array::from_iter_values(vec![0, 1]),
4151 Arc::new(StringArray::from_iter_values(vec!["parquet", "barquet"])),
4152 ),
4153 );
4154
4155 ensure_compatible_write(
4157 DictionaryArray::new(
4158 UInt8Array::from_iter_values(vec![0]),
4159 Arc::new(StringArray::from_iter_values(vec!["parquet"])),
4160 ),
4161 DictionaryArray::new(
4162 UInt8Array::from_iter_values(vec![0]),
4163 Arc::new(LargeStringArray::from_iter_values(vec!["barquet"])),
4164 ),
4165 DictionaryArray::new(
4166 UInt8Array::from_iter_values(vec![0, 1]),
4167 Arc::new(StringArray::from_iter_values(vec!["parquet", "barquet"])),
4168 ),
4169 );
4170
4171 ensure_compatible_write(
4173 DictionaryArray::new(
4174 UInt8Array::from_iter_values(vec![0]),
4175 Arc::new(StringArray::from_iter_values(vec!["parquet"])),
4176 ),
4177 LargeStringArray::from_iter_values(vec!["barquet"]),
4178 DictionaryArray::new(
4179 UInt8Array::from_iter_values(vec![0, 1]),
4180 Arc::new(StringArray::from_iter_values(vec!["parquet", "barquet"])),
4181 ),
4182 );
4183
4184 ensure_compatible_write(
4187 StringArray::from_iter_values(vec!["parquet"]),
4188 LargeStringArray::from_iter_values(vec!["barquet"]),
4189 StringArray::from_iter_values(vec!["parquet", "barquet"]),
4190 );
4191
4192 ensure_compatible_write(
4193 LargeStringArray::from_iter_values(vec!["parquet"]),
4194 StringArray::from_iter_values(vec!["barquet"]),
4195 LargeStringArray::from_iter_values(vec!["parquet", "barquet"]),
4196 );
4197
4198 ensure_compatible_write(
4199 StringArray::from_iter_values(vec!["parquet"]),
4200 StringViewArray::from_iter_values(vec!["barquet"]),
4201 StringArray::from_iter_values(vec!["parquet", "barquet"]),
4202 );
4203
4204 ensure_compatible_write(
4205 StringViewArray::from_iter_values(vec!["parquet"]),
4206 StringArray::from_iter_values(vec!["barquet"]),
4207 StringViewArray::from_iter_values(vec!["parquet", "barquet"]),
4208 );
4209
4210 ensure_compatible_write(
4211 LargeStringArray::from_iter_values(vec!["parquet"]),
4212 StringViewArray::from_iter_values(vec!["barquet"]),
4213 LargeStringArray::from_iter_values(vec!["parquet", "barquet"]),
4214 );
4215
4216 ensure_compatible_write(
4217 StringViewArray::from_iter_values(vec!["parquet"]),
4218 LargeStringArray::from_iter_values(vec!["barquet"]),
4219 StringViewArray::from_iter_values(vec!["parquet", "barquet"]),
4220 );
4221
4222 ensure_compatible_write(
4225 BinaryArray::from_iter_values(vec![b"parquet"]),
4226 LargeBinaryArray::from_iter_values(vec![b"barquet"]),
4227 BinaryArray::from_iter_values(vec![b"parquet", b"barquet"]),
4228 );
4229
4230 ensure_compatible_write(
4231 LargeBinaryArray::from_iter_values(vec![b"parquet"]),
4232 BinaryArray::from_iter_values(vec![b"barquet"]),
4233 LargeBinaryArray::from_iter_values(vec![b"parquet", b"barquet"]),
4234 );
4235
4236 ensure_compatible_write(
4237 BinaryArray::from_iter_values(vec![b"parquet"]),
4238 BinaryViewArray::from_iter_values(vec![b"barquet"]),
4239 BinaryArray::from_iter_values(vec![b"parquet", b"barquet"]),
4240 );
4241
4242 ensure_compatible_write(
4243 BinaryViewArray::from_iter_values(vec![b"parquet"]),
4244 BinaryArray::from_iter_values(vec![b"barquet"]),
4245 BinaryViewArray::from_iter_values(vec![b"parquet", b"barquet"]),
4246 );
4247
4248 ensure_compatible_write(
4249 BinaryViewArray::from_iter_values(vec![b"parquet"]),
4250 LargeBinaryArray::from_iter_values(vec![b"barquet"]),
4251 BinaryViewArray::from_iter_values(vec![b"parquet", b"barquet"]),
4252 );
4253
4254 ensure_compatible_write(
4255 LargeBinaryArray::from_iter_values(vec![b"parquet"]),
4256 BinaryViewArray::from_iter_values(vec![b"barquet"]),
4257 LargeBinaryArray::from_iter_values(vec![b"parquet", b"barquet"]),
4258 );
4259
4260 let list_field_metadata = HashMap::from_iter(vec![(
4263 PARQUET_FIELD_ID_META_KEY.to_string(),
4264 "1".to_string(),
4265 )]);
4266 let list_field = Field::new_list_field(DataType::Int32, false);
4267
4268 let values1 = Arc::new(Int32Array::from(vec![0, 1, 2, 3, 4]));
4269 let offsets1 = OffsetBuffer::new(vec![0, 2, 5].into());
4270
4271 let values2 = Arc::new(Int32Array::from(vec![5, 6, 7, 8, 9]));
4272 let offsets2 = OffsetBuffer::new(vec![0, 3, 5].into());
4273
4274 let values_expected = Arc::new(Int32Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
4275 let offsets_expected = OffsetBuffer::new(vec![0, 2, 5, 8, 10].into());
4276
4277 ensure_compatible_write(
4278 ListArray::try_new(
4280 Arc::new(
4281 list_field
4282 .clone()
4283 .with_metadata(list_field_metadata.clone()),
4284 ),
4285 offsets1,
4286 values1,
4287 None,
4288 )
4289 .unwrap(),
4290 ListArray::try_new(Arc::new(list_field.clone()), offsets2, values2, None).unwrap(),
4292 ListArray::try_new(
4294 Arc::new(
4295 list_field
4296 .clone()
4297 .with_metadata(list_field_metadata.clone()),
4298 ),
4299 offsets_expected,
4300 values_expected,
4301 None,
4302 )
4303 .unwrap(),
4304 );
4305 }
4306
4307 #[test]
4308 fn arrow_writer_primitive_dictionary() {
4309 #[expect(deprecated)]
4311 let schema = Arc::new(Schema::new(vec![Field::new_dict(
4312 "dictionary",
4313 DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::UInt32)),
4314 true,
4315 42,
4316 true,
4317 )]));
4318
4319 let mut builder = PrimitiveDictionaryBuilder::<UInt8Type, UInt32Type>::new();
4321 builder.append(12345678).unwrap();
4322 builder.append_null();
4323 builder.append(22345678).unwrap();
4324 builder.append(12345678).unwrap();
4325 let d = builder.finish();
4326
4327 RoundTripTest::new(Arc::new(d)).with_schema(schema).run();
4328 }
4329
4330 #[test]
4331 fn arrow_writer_decimal32_dictionary() {
4332 let integers = vec![12345, 56789, 34567];
4333
4334 let keys = UInt8Array::from(vec![Some(0), None, Some(1), Some(2), Some(1)]);
4335
4336 let values = Decimal32Array::from(integers.clone())
4337 .with_precision_and_scale(5, 2)
4338 .unwrap();
4339
4340 let array = DictionaryArray::new(keys, Arc::new(values));
4341 RoundTripTest::new(Arc::new(array.clone())).run();
4342
4343 let values = Decimal32Array::from(integers)
4344 .with_precision_and_scale(9, 2)
4345 .unwrap();
4346
4347 let array = array.with_values(Arc::new(values));
4348 RoundTripTest::new(Arc::new(array)).run();
4349 }
4350
4351 #[test]
4352 fn arrow_writer_decimal64_dictionary() {
4353 let integers = vec![12345, 56789, 34567];
4354
4355 let keys = UInt8Array::from(vec![Some(0), None, Some(1), Some(2), Some(1)]);
4356
4357 let values = Decimal64Array::from(integers.clone())
4358 .with_precision_and_scale(5, 2)
4359 .unwrap();
4360
4361 let array = DictionaryArray::new(keys, Arc::new(values));
4362 RoundTripTest::new(Arc::new(array.clone())).run();
4363
4364 let values = Decimal64Array::from(integers)
4365 .with_precision_and_scale(12, 2)
4366 .unwrap();
4367
4368 let array = array.with_values(Arc::new(values));
4369 RoundTripTest::new(Arc::new(array)).run();
4370 }
4371
4372 #[test]
4373 fn arrow_writer_decimal128_dictionary() {
4374 let integers = vec![12345, 56789, 34567];
4375
4376 let keys = UInt8Array::from(vec![Some(0), None, Some(1), Some(2), Some(1)]);
4377
4378 let values = Decimal128Array::from(integers.clone())
4379 .with_precision_and_scale(5, 2)
4380 .unwrap();
4381
4382 let array = DictionaryArray::new(keys, Arc::new(values));
4383 RoundTripTest::new(Arc::new(array.clone())).run();
4384
4385 let values = Decimal128Array::from(integers)
4386 .with_precision_and_scale(12, 2)
4387 .unwrap();
4388
4389 let array = array.with_values(Arc::new(values));
4390 RoundTripTest::new(Arc::new(array)).run();
4391 }
4392
4393 #[test]
4394 fn arrow_writer_decimal256_dictionary() {
4395 let integers = vec![
4396 i256::from_i128(12345),
4397 i256::from_i128(56789),
4398 i256::from_i128(34567),
4399 ];
4400
4401 let keys = UInt8Array::from(vec![Some(0), None, Some(1), Some(2), Some(1)]);
4402
4403 let values = Decimal256Array::from(integers.clone())
4404 .with_precision_and_scale(5, 2)
4405 .unwrap();
4406
4407 let array = DictionaryArray::new(keys, Arc::new(values));
4408 RoundTripTest::new(Arc::new(array.clone())).run();
4409
4410 let values = Decimal256Array::from(integers)
4411 .with_precision_and_scale(12, 2)
4412 .unwrap();
4413
4414 let array = array.with_values(Arc::new(values));
4415 RoundTripTest::new(Arc::new(array)).run();
4416 }
4417
4418 #[test]
4419 fn arrow_writer_string_dictionary_unsigned_index() {
4420 #[expect(deprecated)]
4422 let schema = Arc::new(Schema::new(vec![Field::new_dict(
4423 "dictionary",
4424 DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::Utf8)),
4425 true,
4426 42,
4427 true,
4428 )]));
4429
4430 let d: UInt8DictionaryArray = [Some("alpha"), None, Some("beta"), Some("alpha")]
4432 .iter()
4433 .copied()
4434 .collect();
4435
4436 RoundTripTest::new(Arc::new(d)).with_schema(schema).run();
4437 }
4438
4439 #[test]
4440 fn u32_min_max() {
4441 let src = [
4443 u32::MIN,
4444 1,
4445 (i32::MAX as u32) - 1,
4446 i32::MAX as u32,
4447 (i32::MAX as u32) + 1,
4448 u32::MAX - 1,
4449 u32::MAX,
4450 ];
4451 let values = Arc::new(UInt32Array::from_iter_values(src.iter().copied()));
4452 let files = RoundTripTest::new(values).with_nullable(false).run();
4453
4454 for file in files {
4455 let reader = SerializedFileReader::new(file).unwrap();
4457 let metadata = reader.metadata();
4458
4459 let mut row_offset = 0;
4460 for row_group in metadata.row_groups() {
4461 assert_eq!(row_group.num_columns(), 1);
4462 let column = row_group.column(0);
4463
4464 let num_values = column.num_values() as usize;
4465 let src_slice = &src[row_offset..row_offset + num_values];
4466 row_offset += column.num_values() as usize;
4467
4468 let stats = column.statistics().unwrap();
4469 if let Statistics::Int32(stats) = stats {
4470 assert_eq!(
4471 *stats.min_opt().unwrap() as u32,
4472 *src_slice.iter().min().unwrap()
4473 );
4474 assert_eq!(
4475 *stats.max_opt().unwrap() as u32,
4476 *src_slice.iter().max().unwrap()
4477 );
4478 } else {
4479 panic!("Statistics::Int32 missing")
4480 }
4481 }
4482 }
4483 }
4484
4485 #[test]
4486 fn u64_min_max() {
4487 let src = [
4489 u64::MIN,
4490 1,
4491 (i64::MAX as u64) - 1,
4492 i64::MAX as u64,
4493 (i64::MAX as u64) + 1,
4494 u64::MAX - 1,
4495 u64::MAX,
4496 ];
4497 let values = Arc::new(UInt64Array::from_iter_values(src.iter().copied()));
4498 let files = RoundTripTest::new(values).with_nullable(false).run();
4499
4500 for file in files {
4501 let reader = SerializedFileReader::new(file).unwrap();
4503 let metadata = reader.metadata();
4504
4505 let mut row_offset = 0;
4506 for row_group in metadata.row_groups() {
4507 assert_eq!(row_group.num_columns(), 1);
4508 let column = row_group.column(0);
4509
4510 let num_values = column.num_values() as usize;
4511 let src_slice = &src[row_offset..row_offset + num_values];
4512 row_offset += column.num_values() as usize;
4513
4514 let stats = column.statistics().unwrap();
4515 if let Statistics::Int64(stats) = stats {
4516 assert_eq!(
4517 *stats.min_opt().unwrap() as u64,
4518 *src_slice.iter().min().unwrap()
4519 );
4520 assert_eq!(
4521 *stats.max_opt().unwrap() as u64,
4522 *src_slice.iter().max().unwrap()
4523 );
4524 } else {
4525 panic!("Statistics::Int64 missing")
4526 }
4527 }
4528 }
4529 }
4530
4531 #[test]
4532 fn statistics_null_counts_only_nulls() {
4533 let values = Arc::new(UInt64Array::from(vec![None, None]));
4535 let files = RoundTripTest::new(values).run();
4536
4537 for file in files {
4538 let reader = SerializedFileReader::new(file).unwrap();
4540 let metadata = reader.metadata();
4541 assert_eq!(metadata.num_row_groups(), 1);
4542 let row_group = metadata.row_group(0);
4543 assert_eq!(row_group.num_columns(), 1);
4544 let column = row_group.column(0);
4545 let stats = column.statistics().unwrap();
4546 assert_eq!(stats.null_count_opt(), Some(2));
4547 }
4548 }
4549
4550 #[test]
4551 fn test_list_of_struct_roundtrip() {
4552 let int_field = Field::new("a", DataType::Int32, true);
4554 let int_field2 = Field::new("b", DataType::Int32, true);
4555
4556 let int_builder = Int32Builder::with_capacity(10);
4557 let int_builder2 = Int32Builder::with_capacity(10);
4558
4559 let struct_builder = StructBuilder::new(
4560 vec![int_field, int_field2],
4561 vec![Box::new(int_builder), Box::new(int_builder2)],
4562 );
4563 let mut list_builder = ListBuilder::new(struct_builder);
4564
4565 let values = list_builder.values();
4570 values
4571 .field_builder::<Int32Builder>(0)
4572 .unwrap()
4573 .append_value(1);
4574 values
4575 .field_builder::<Int32Builder>(1)
4576 .unwrap()
4577 .append_value(2);
4578 values.append(true);
4579 list_builder.append(true);
4580
4581 list_builder.append(true);
4583
4584 list_builder.append(false);
4586
4587 let values = list_builder.values();
4589 values
4590 .field_builder::<Int32Builder>(0)
4591 .unwrap()
4592 .append_null();
4593 values
4594 .field_builder::<Int32Builder>(1)
4595 .unwrap()
4596 .append_null();
4597 values.append(false);
4598 values
4599 .field_builder::<Int32Builder>(0)
4600 .unwrap()
4601 .append_null();
4602 values
4603 .field_builder::<Int32Builder>(1)
4604 .unwrap()
4605 .append_null();
4606 values.append(false);
4607 list_builder.append(true);
4608
4609 let values = list_builder.values();
4611 values
4612 .field_builder::<Int32Builder>(0)
4613 .unwrap()
4614 .append_null();
4615 values
4616 .field_builder::<Int32Builder>(1)
4617 .unwrap()
4618 .append_value(3);
4619 values.append(true);
4620 list_builder.append(true);
4621
4622 let values = list_builder.values();
4624 values
4625 .field_builder::<Int32Builder>(0)
4626 .unwrap()
4627 .append_value(2);
4628 values
4629 .field_builder::<Int32Builder>(1)
4630 .unwrap()
4631 .append_null();
4632 values.append(true);
4633 list_builder.append(true);
4634
4635 let array = Arc::new(list_builder.finish());
4636
4637 RoundTripTest::new(array).run();
4638 }
4639
4640 fn row_group_sizes(metadata: &ParquetMetaData) -> Vec<i64> {
4641 metadata.row_groups().iter().map(|x| x.num_rows()).collect()
4642 }
4643
4644 #[test]
4645 fn test_aggregates_records() {
4646 let arrays = [
4647 Int32Array::from((0..100).collect::<Vec<_>>()),
4648 Int32Array::from((0..50).collect::<Vec<_>>()),
4649 Int32Array::from((200..500).collect::<Vec<_>>()),
4650 ];
4651
4652 let schema = Arc::new(Schema::new(vec![Field::new(
4653 "int",
4654 ArrowDataType::Int32,
4655 false,
4656 )]));
4657
4658 let file = tempfile::tempfile().unwrap();
4659
4660 let props = WriterProperties::builder()
4661 .set_max_row_group_row_count(Some(200))
4662 .build();
4663
4664 let mut writer =
4665 ArrowWriter::try_new(file.try_clone().unwrap(), schema.clone(), Some(props)).unwrap();
4666
4667 for array in arrays {
4668 let batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(array)]).unwrap();
4669 writer.write(&batch).unwrap();
4670 }
4671
4672 writer.close().unwrap();
4673
4674 let builder = ParquetRecordBatchReaderBuilder::try_new(file).unwrap();
4675 assert_eq!(&row_group_sizes(builder.metadata()), &[200, 200, 50]);
4676
4677 let batches = builder
4678 .with_batch_size(100)
4679 .build()
4680 .unwrap()
4681 .collect::<ArrowResult<Vec<_>>>()
4682 .unwrap();
4683
4684 assert_eq!(batches.len(), 5);
4685 assert!(batches.iter().all(|x| x.num_columns() == 1));
4686
4687 let batch_sizes: Vec<_> = batches.iter().map(|x| x.num_rows()).collect();
4688
4689 assert_eq!(&batch_sizes, &[100, 100, 100, 100, 50]);
4690
4691 let values: Vec<_> = batches
4692 .iter()
4693 .flat_map(|x| {
4694 x.column(0)
4695 .as_any()
4696 .downcast_ref::<Int32Array>()
4697 .unwrap()
4698 .values()
4699 .iter()
4700 .copied()
4701 })
4702 .collect();
4703
4704 let expected_values: Vec<_> = [0..100, 0..50, 200..500].into_iter().flatten().collect();
4705 assert_eq!(&values, &expected_values)
4706 }
4707
4708 #[test]
4709 fn complex_aggregate() {
4710 let field_a = Arc::new(Field::new("leaf_a", DataType::Int32, false));
4712 let field_b = Arc::new(Field::new("leaf_b", DataType::Int32, true));
4713 let struct_a = Arc::new(Field::new(
4714 "struct_a",
4715 DataType::Struct(vec![field_a.clone(), field_b.clone()].into()),
4716 true,
4717 ));
4718
4719 let list_a = Arc::new(Field::new("list", DataType::List(struct_a), true));
4720 let struct_b = Arc::new(Field::new(
4721 "struct_b",
4722 DataType::Struct(vec![list_a.clone()].into()),
4723 false,
4724 ));
4725
4726 let schema = Arc::new(Schema::new(vec![struct_b]));
4727
4728 let field_a_array = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
4730 let field_b_array =
4731 Int32Array::from_iter(vec![Some(1), None, Some(2), None, None, Some(6)]);
4732
4733 let struct_a_array = StructArray::from(vec![
4734 (field_a.clone(), Arc::new(field_a_array) as ArrayRef),
4735 (field_b.clone(), Arc::new(field_b_array) as ArrayRef),
4736 ]);
4737
4738 let list_data = ArrayDataBuilder::new(list_a.data_type().clone())
4739 .len(5)
4740 .add_buffer(Buffer::from_iter(vec![
4741 0_i32, 1_i32, 1_i32, 3_i32, 3_i32, 5_i32,
4742 ]))
4743 .null_bit_buffer(Some(Buffer::from_iter(vec![
4744 true, false, true, false, true,
4745 ])))
4746 .child_data(vec![struct_a_array.into_data()])
4747 .build()
4748 .unwrap();
4749
4750 let list_a_array = Arc::new(ListArray::from(list_data)) as ArrayRef;
4751 let struct_b_array = StructArray::from(vec![(list_a.clone(), list_a_array)]);
4752
4753 let batch1 =
4754 RecordBatch::try_from_iter(vec![("struct_b", Arc::new(struct_b_array) as ArrayRef)])
4755 .unwrap();
4756
4757 let field_a_array = Int32Array::from(vec![6, 7, 8, 9, 10]);
4758 let field_b_array = Int32Array::from_iter(vec![None, None, None, Some(1), None]);
4759
4760 let struct_a_array = StructArray::from(vec![
4761 (field_a, Arc::new(field_a_array) as ArrayRef),
4762 (field_b, Arc::new(field_b_array) as ArrayRef),
4763 ]);
4764
4765 let list_data = ArrayDataBuilder::new(list_a.data_type().clone())
4766 .len(2)
4767 .add_buffer(Buffer::from_iter(vec![0_i32, 4_i32, 5_i32]))
4768 .child_data(vec![struct_a_array.into_data()])
4769 .build()
4770 .unwrap();
4771
4772 let list_a_array = Arc::new(ListArray::from(list_data)) as ArrayRef;
4773 let struct_b_array = StructArray::from(vec![(list_a, list_a_array)]);
4774
4775 let batch2 =
4776 RecordBatch::try_from_iter(vec![("struct_b", Arc::new(struct_b_array) as ArrayRef)])
4777 .unwrap();
4778
4779 let batches = &[batch1, batch2];
4780
4781 let expected = r"
4784 +-------------------------------------------------------------------------------------------------------+
4785 | struct_b |
4786 +-------------------------------------------------------------------------------------------------------+
4787 | {list: [{leaf_a: 1, leaf_b: 1}]} |
4788 | {list: } |
4789 | {list: [{leaf_a: 2, leaf_b: }, {leaf_a: 3, leaf_b: 2}]} |
4790 | {list: } |
4791 | {list: [{leaf_a: 4, leaf_b: }, {leaf_a: 5, leaf_b: }]} |
4792 | {list: [{leaf_a: 6, leaf_b: }, {leaf_a: 7, leaf_b: }, {leaf_a: 8, leaf_b: }, {leaf_a: 9, leaf_b: 1}]} |
4793 | {list: [{leaf_a: 10, leaf_b: }]} |
4794 +-------------------------------------------------------------------------------------------------------+
4795 ".trim().split('\n').map(|x| x.trim()).collect::<Vec<_>>().join("\n");
4796
4797 let actual = pretty_format_batches(batches).unwrap().to_string();
4798 assert_eq!(actual, expected);
4799
4800 let file = tempfile::tempfile().unwrap();
4802 let props = WriterProperties::builder()
4803 .set_max_row_group_row_count(Some(6))
4804 .build();
4805
4806 let mut writer =
4807 ArrowWriter::try_new(file.try_clone().unwrap(), schema, Some(props)).unwrap();
4808
4809 for batch in batches {
4810 writer.write(batch).unwrap();
4811 }
4812 writer.close().unwrap();
4813
4814 let builder = ParquetRecordBatchReaderBuilder::try_new(file).unwrap();
4819 assert_eq!(&row_group_sizes(builder.metadata()), &[6, 1]);
4820
4821 let batches = builder
4822 .with_batch_size(2)
4823 .build()
4824 .unwrap()
4825 .collect::<ArrowResult<Vec<_>>>()
4826 .unwrap();
4827
4828 assert_eq!(batches.len(), 4);
4829 let batch_counts: Vec<_> = batches.iter().map(|x| x.num_rows()).collect();
4830 assert_eq!(&batch_counts, &[2, 2, 2, 1]);
4831
4832 let actual = pretty_format_batches(&batches).unwrap().to_string();
4833 assert_eq!(actual, expected);
4834 }
4835
4836 #[test]
4837 fn test_arrow_writer_metadata() {
4838 let batch_schema = Schema::new(vec![Field::new("int32", DataType::Int32, false)]);
4839 let file_schema = batch_schema.clone().with_metadata([("foo", "bar")]);
4840
4841 let batch = RecordBatch::try_new(
4842 Arc::new(batch_schema),
4843 vec![Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _],
4844 )
4845 .unwrap();
4846
4847 let mut buf = Vec::with_capacity(1024);
4848 let mut writer = ArrowWriter::try_new(&mut buf, Arc::new(file_schema), None).unwrap();
4849 writer.write(&batch).unwrap();
4850 writer.close().unwrap();
4851 }
4852
4853 #[test]
4854 fn test_arrow_writer_nullable() {
4855 let batch_schema = Schema::new(vec![Field::new("int32", DataType::Int32, false)]);
4856 let file_schema = Schema::new(vec![Field::new("int32", DataType::Int32, true)]);
4857 let file_schema = Arc::new(file_schema);
4858
4859 let batch = RecordBatch::try_new(
4860 Arc::new(batch_schema),
4861 vec![Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _],
4862 )
4863 .unwrap();
4864
4865 let mut buf = Vec::with_capacity(1024);
4866 let mut writer = ArrowWriter::try_new(&mut buf, file_schema.clone(), None).unwrap();
4867 writer.write(&batch).unwrap();
4868 writer.close().unwrap();
4869
4870 let mut read = ParquetRecordBatchReader::try_new(Bytes::from(buf), 1024).unwrap();
4871 let back = read.next().unwrap().unwrap();
4872 assert_eq!(back.schema(), file_schema);
4873 assert_ne!(back.schema(), batch.schema());
4874 assert_eq!(back.column(0).as_ref(), batch.column(0).as_ref());
4875 }
4876
4877 #[test]
4878 fn in_progress_accounting() {
4879 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
4881
4882 let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
4884
4885 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap();
4887
4888 let mut writer = ArrowWriter::try_new(vec![], batch.schema(), None).unwrap();
4889
4890 assert_eq!(writer.in_progress_size(), 0);
4892 assert_eq!(writer.in_progress_rows(), 0);
4893 assert_eq!(writer.memory_size(), 0);
4894 assert_eq!(writer.bytes_written(), 4); writer.write(&batch).unwrap();
4896
4897 let initial_size = writer.in_progress_size();
4899 assert!(initial_size > 0);
4900 assert_eq!(writer.in_progress_rows(), 5);
4901 let initial_memory = writer.memory_size();
4902 assert!(initial_memory > 0);
4903 assert!(
4905 initial_size <= initial_memory,
4906 "{initial_size} <= {initial_memory}"
4907 );
4908
4909 writer.write(&batch).unwrap();
4911 assert!(writer.in_progress_size() > initial_size);
4912 assert_eq!(writer.in_progress_rows(), 10);
4913 assert!(writer.memory_size() > initial_memory);
4914 assert!(
4915 writer.in_progress_size() <= writer.memory_size(),
4916 "in_progress_size {} <= memory_size {}",
4917 writer.in_progress_size(),
4918 writer.memory_size()
4919 );
4920
4921 let pre_flush_bytes_written = writer.bytes_written();
4923 writer.flush().unwrap();
4924 assert_eq!(writer.in_progress_size(), 0);
4925 assert_eq!(writer.memory_size(), 0);
4926 assert!(writer.bytes_written() > pre_flush_bytes_written);
4927
4928 writer.close().unwrap();
4929 }
4930
4931 #[test]
4932 fn test_writer_all_null() {
4933 let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
4934 let b = Int32Array::new(vec![0; 5].into(), Some(NullBuffer::new_null(5)));
4935 let batch = RecordBatch::try_from_iter(vec![
4936 ("a", Arc::new(a) as ArrayRef),
4937 ("b", Arc::new(b) as ArrayRef),
4938 ])
4939 .unwrap();
4940
4941 let mut buf = Vec::with_capacity(1024);
4942 let mut writer = ArrowWriter::try_new(&mut buf, batch.schema(), None).unwrap();
4943 writer.write(&batch).unwrap();
4944 writer.close().unwrap();
4945
4946 let bytes = Bytes::from(buf);
4947 let options = ReadOptionsBuilder::new().with_page_index().build();
4948 let reader = SerializedFileReader::new_with_options(bytes, options).unwrap();
4949 let index = reader.metadata().offset_index().unwrap();
4950
4951 assert_eq!(index.len(), 1);
4952 assert_eq!(index[0].len(), 2); assert_eq!(index[0][0].page_locations().len(), 1); assert_eq!(index[0][1].page_locations().len(), 1); }
4956
4957 #[test]
4958 fn test_disabled_statistics_with_page() {
4959 let file_schema = Schema::new(vec![
4960 Field::new("a", DataType::Utf8, true),
4961 Field::new("b", DataType::Utf8, true),
4962 ]);
4963 let file_schema = Arc::new(file_schema);
4964
4965 let batch = RecordBatch::try_new(
4966 file_schema.clone(),
4967 vec![
4968 Arc::new(StringArray::from(vec!["a", "b", "c", "d"])) as _,
4969 Arc::new(StringArray::from(vec!["w", "x", "y", "z"])) as _,
4970 ],
4971 )
4972 .unwrap();
4973
4974 let props = WriterProperties::builder()
4975 .set_statistics_enabled(EnabledStatistics::None)
4976 .set_column_statistics_enabled("a".into(), EnabledStatistics::Page)
4977 .build();
4978
4979 let mut buf = Vec::with_capacity(1024);
4980 let mut writer = ArrowWriter::try_new(&mut buf, file_schema.clone(), Some(props)).unwrap();
4981 writer.write(&batch).unwrap();
4982
4983 let metadata = writer.close().unwrap();
4984 assert_eq!(metadata.num_row_groups(), 1);
4985 let row_group = metadata.row_group(0);
4986 assert_eq!(row_group.num_columns(), 2);
4987 assert!(row_group.column(0).offset_index_offset().is_some());
4989 assert!(row_group.column(0).column_index_offset().is_some());
4990 assert!(row_group.column(1).offset_index_offset().is_some());
4992 assert!(row_group.column(1).column_index_offset().is_none());
4993
4994 let options = ReadOptionsBuilder::new().with_page_index().build();
4995 let reader = SerializedFileReader::new_with_options(Bytes::from(buf), options).unwrap();
4996
4997 let row_group = reader.get_row_group(0).unwrap();
4998 let a_col = row_group.metadata().column(0);
4999 let b_col = row_group.metadata().column(1);
5000
5001 if let Statistics::ByteArray(byte_array_stats) = a_col.statistics().unwrap() {
5003 let min = byte_array_stats.min_opt().unwrap();
5004 let max = byte_array_stats.max_opt().unwrap();
5005
5006 assert_eq!(min.as_bytes(), b"a");
5007 assert_eq!(max.as_bytes(), b"d");
5008 } else {
5009 panic!("expecting Statistics::ByteArray");
5010 }
5011
5012 assert!(b_col.statistics().is_none());
5014
5015 let offset_index = reader.metadata().offset_index().unwrap();
5016 assert_eq!(offset_index.len(), 1); assert_eq!(offset_index[0].len(), 2); let column_index = reader.metadata().column_index().unwrap();
5020 assert_eq!(column_index.len(), 1); assert_eq!(column_index[0].len(), 2); let a_idx = &column_index[0][0];
5024 assert!(
5025 matches!(a_idx, ColumnIndexMetaData::BYTE_ARRAY(_)),
5026 "{a_idx:?}"
5027 );
5028 let b_idx = &column_index[0][1];
5029 assert!(matches!(b_idx, ColumnIndexMetaData::NONE), "{b_idx:?}");
5030 }
5031
5032 #[test]
5033 fn test_disabled_statistics_with_chunk() {
5034 let file_schema = Schema::new(vec![
5035 Field::new("a", DataType::Utf8, true),
5036 Field::new("b", DataType::Utf8, true),
5037 ]);
5038 let file_schema = Arc::new(file_schema);
5039
5040 let batch = RecordBatch::try_new(
5041 file_schema.clone(),
5042 vec![
5043 Arc::new(StringArray::from(vec!["a", "b", "c", "d"])) as _,
5044 Arc::new(StringArray::from(vec!["w", "x", "y", "z"])) as _,
5045 ],
5046 )
5047 .unwrap();
5048
5049 let props = WriterProperties::builder()
5050 .set_statistics_enabled(EnabledStatistics::None)
5051 .set_column_statistics_enabled("a".into(), EnabledStatistics::Chunk)
5052 .build();
5053
5054 let mut buf = Vec::with_capacity(1024);
5055 let mut writer = ArrowWriter::try_new(&mut buf, file_schema.clone(), Some(props)).unwrap();
5056 writer.write(&batch).unwrap();
5057
5058 let metadata = writer.close().unwrap();
5059 assert_eq!(metadata.num_row_groups(), 1);
5060 let row_group = metadata.row_group(0);
5061 assert_eq!(row_group.num_columns(), 2);
5062 assert!(row_group.column(0).offset_index_offset().is_some());
5064 assert!(row_group.column(0).column_index_offset().is_none());
5065 assert!(row_group.column(1).offset_index_offset().is_some());
5067 assert!(row_group.column(1).column_index_offset().is_none());
5068
5069 let options = ReadOptionsBuilder::new().with_page_index().build();
5070 let reader = SerializedFileReader::new_with_options(Bytes::from(buf), options).unwrap();
5071
5072 let row_group = reader.get_row_group(0).unwrap();
5073 let a_col = row_group.metadata().column(0);
5074 let b_col = row_group.metadata().column(1);
5075
5076 if let Statistics::ByteArray(byte_array_stats) = a_col.statistics().unwrap() {
5078 let min = byte_array_stats.min_opt().unwrap();
5079 let max = byte_array_stats.max_opt().unwrap();
5080
5081 assert_eq!(min.as_bytes(), b"a");
5082 assert_eq!(max.as_bytes(), b"d");
5083 } else {
5084 panic!("expecting Statistics::ByteArray");
5085 }
5086
5087 assert!(b_col.statistics().is_none());
5089
5090 let column_index = reader.metadata().column_index().unwrap();
5091 assert_eq!(column_index.len(), 1); assert_eq!(column_index[0].len(), 2); let a_idx = &column_index[0][0];
5095 assert!(matches!(a_idx, ColumnIndexMetaData::NONE), "{a_idx:?}");
5096 let b_idx = &column_index[0][1];
5097 assert!(matches!(b_idx, ColumnIndexMetaData::NONE), "{b_idx:?}");
5098 }
5099
5100 #[test]
5101 fn test_arrow_writer_skip_metadata() {
5102 let batch_schema = Schema::new(vec![Field::new("int32", DataType::Int32, false)]);
5103 let file_schema = Arc::new(batch_schema.clone());
5104
5105 let batch = RecordBatch::try_new(
5106 Arc::new(batch_schema),
5107 vec![Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _],
5108 )
5109 .unwrap();
5110 let skip_options = ArrowWriterOptions::new().with_skip_arrow_metadata(true);
5111
5112 let mut buf = Vec::with_capacity(1024);
5113 let mut writer =
5114 ArrowWriter::try_new_with_options(&mut buf, file_schema.clone(), skip_options).unwrap();
5115 writer.write(&batch).unwrap();
5116 writer.close().unwrap();
5117
5118 let bytes = Bytes::from(buf);
5119 let reader_builder = ParquetRecordBatchReaderBuilder::try_new(bytes).unwrap();
5120 assert_eq!(file_schema, *reader_builder.schema());
5121 if let Some(key_value_metadata) = reader_builder
5122 .metadata()
5123 .file_metadata()
5124 .key_value_metadata()
5125 {
5126 assert!(
5127 !key_value_metadata
5128 .iter()
5129 .any(|kv| kv.key.as_str() == ARROW_SCHEMA_META_KEY)
5130 );
5131 }
5132 }
5133
5134 #[test]
5135 fn test_arrow_writer_skip_path_in_schema() {
5136 let batch_schema = Schema::new(vec![Field::new("int32", DataType::Int32, false)]);
5137 let file_schema = Arc::new(batch_schema.clone());
5138
5139 let batch = RecordBatch::try_new(
5140 Arc::new(batch_schema),
5141 vec![Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _],
5142 )
5143 .unwrap();
5144
5145 let skip_options = ArrowWriterOptions::new();
5147
5148 let mut buf = Vec::with_capacity(1024);
5149 let mut writer =
5150 ArrowWriter::try_new_with_options(&mut buf, file_schema.clone(), skip_options).unwrap();
5151 writer.write(&batch).unwrap();
5152 writer.close().unwrap();
5153
5154 let skip_options = ArrowWriterOptions::new().with_properties(
5156 WriterProperties::builder()
5157 .set_write_path_in_schema(false)
5158 .build(),
5159 );
5160
5161 let mut buf2 = Vec::with_capacity(1024);
5162 let mut writer =
5163 ArrowWriter::try_new_with_options(&mut buf2, file_schema.clone(), skip_options)
5164 .unwrap();
5165 writer.write(&batch).unwrap();
5166 writer.close().unwrap();
5167
5168 assert!(buf.len() > buf2.len());
5170 }
5171
5172 #[test]
5173 fn mismatched_schemas() {
5174 let batch_schema = Schema::new(vec![Field::new("count", DataType::Int32, false)]);
5175 let file_schema = Arc::new(Schema::new(vec![Field::new(
5176 "temperature",
5177 DataType::Float64,
5178 false,
5179 )]));
5180
5181 let batch = RecordBatch::try_new(
5182 Arc::new(batch_schema),
5183 vec![Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _],
5184 )
5185 .unwrap();
5186
5187 let mut buf = Vec::with_capacity(1024);
5188 let mut writer = ArrowWriter::try_new(&mut buf, file_schema.clone(), None).unwrap();
5189
5190 let err = writer.write(&batch).unwrap_err().to_string();
5191 assert_eq!(
5192 err,
5193 "Arrow: Incompatible type. Field 'temperature' has type Float64, array has type Int32"
5194 );
5195 }
5196
5197 #[test]
5198 fn test_roundtrip_empty_schema() {
5200 let empty_batch = RecordBatch::try_new_with_options(
5202 Arc::new(Schema::empty()),
5203 vec![],
5204 &RecordBatchOptions::default().with_row_count(Some(0)),
5205 )
5206 .unwrap();
5207
5208 let mut parquet_bytes: Vec<u8> = Vec::new();
5210 let mut writer =
5211 ArrowWriter::try_new(&mut parquet_bytes, empty_batch.schema(), None).unwrap();
5212 writer.write(&empty_batch).unwrap();
5213 writer.close().unwrap();
5214
5215 let bytes = Bytes::from(parquet_bytes);
5217 let reader = ParquetRecordBatchReaderBuilder::try_new(bytes).unwrap();
5218 assert_eq!(reader.schema(), &empty_batch.schema());
5219 let batches: Vec<_> = reader
5220 .build()
5221 .unwrap()
5222 .collect::<ArrowResult<Vec<_>>>()
5223 .unwrap();
5224 assert_eq!(batches.len(), 0);
5225 }
5226
5227 #[test]
5228 fn test_page_stats_not_written_by_default() {
5229 let string_field = Field::new("a", DataType::Utf8, false);
5230 let schema = Schema::new(vec![string_field]);
5231 let raw_string_values = vec!["Blart Versenwald III"];
5232 let string_values = StringArray::from(raw_string_values.clone());
5233 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(string_values)]).unwrap();
5234
5235 let props = WriterProperties::builder()
5236 .set_statistics_enabled(EnabledStatistics::Page)
5237 .set_dictionary_enabled(false)
5238 .set_encoding(Encoding::PLAIN)
5239 .set_compression(crate::basic::Compression::UNCOMPRESSED)
5240 .build();
5241
5242 let file = roundtrip_opts(&batch, props);
5243
5244 let first_page = &file[4..];
5249 let mut prot = ThriftSliceInputProtocol::new(first_page);
5250 let hdr = PageHeader::read_thrift(&mut prot).unwrap();
5251 let stats = hdr.data_page_header.unwrap().statistics;
5252
5253 assert!(stats.is_none());
5254 }
5255
5256 #[test]
5257 fn test_page_stats_when_enabled() {
5258 let string_field = Field::new("a", DataType::Utf8, false);
5259 let schema = Schema::new(vec![string_field]);
5260 let raw_string_values = vec!["Blart Versenwald III", "Andrew Lamb"];
5261 let string_values = StringArray::from(raw_string_values.clone());
5262 let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(string_values)]).unwrap();
5263
5264 let props = WriterProperties::builder()
5265 .set_statistics_enabled(EnabledStatistics::Page)
5266 .set_dictionary_enabled(false)
5267 .set_encoding(Encoding::PLAIN)
5268 .set_write_page_header_statistics(true)
5269 .set_compression(crate::basic::Compression::UNCOMPRESSED)
5270 .build();
5271
5272 let file = roundtrip_opts(&batch, props);
5273
5274 let first_page = &file[4..];
5279 let mut prot = ThriftSliceInputProtocol::new(first_page);
5280 let hdr = PageHeader::read_thrift(&mut prot).unwrap();
5281 let stats = hdr.data_page_header.unwrap().statistics;
5282
5283 let stats = stats.unwrap();
5284 assert!(stats.is_max_value_exact.unwrap());
5286 assert!(stats.is_min_value_exact.unwrap());
5287 assert_eq!(stats.max_value.unwrap(), b"Blart Versenwald III");
5288 assert_eq!(stats.min_value.unwrap(), b"Andrew Lamb");
5289 }
5290
5291 #[test]
5292 fn test_page_stats_truncation() {
5293 let string_field = Field::new("a", DataType::Utf8, false);
5294 let binary_field = Field::new("b", DataType::Binary, false);
5295 let schema = Schema::new(vec![string_field, binary_field]);
5296
5297 let raw_string_values = vec!["Blart Versenwald III"];
5298 let raw_binary_values = [b"Blart Versenwald III".to_vec()];
5299 let raw_binary_value_refs = raw_binary_values
5300 .iter()
5301 .map(|x| x.as_slice())
5302 .collect::<Vec<_>>();
5303
5304 let string_values = StringArray::from(raw_string_values.clone());
5305 let binary_values = BinaryArray::from(raw_binary_value_refs);
5306 let batch = RecordBatch::try_new(
5307 Arc::new(schema),
5308 vec![Arc::new(string_values), Arc::new(binary_values)],
5309 )
5310 .unwrap();
5311
5312 let props = WriterProperties::builder()
5313 .set_statistics_truncate_length(Some(2))
5314 .set_dictionary_enabled(false)
5315 .set_encoding(Encoding::PLAIN)
5316 .set_write_page_header_statistics(true)
5317 .set_compression(crate::basic::Compression::UNCOMPRESSED)
5318 .build();
5319
5320 let file = roundtrip_opts(&batch, props);
5321
5322 let first_page = &file[4..];
5327 let mut prot = ThriftSliceInputProtocol::new(first_page);
5328 let hdr = PageHeader::read_thrift(&mut prot).unwrap();
5329 let stats = hdr.data_page_header.unwrap().statistics;
5330 assert!(stats.is_some());
5331 let stats = stats.unwrap();
5332 assert!(!stats.is_max_value_exact.unwrap());
5334 assert!(!stats.is_min_value_exact.unwrap());
5335 assert_eq!(stats.max_value.unwrap(), b"Bm");
5336 assert_eq!(stats.min_value.unwrap(), b"Bl");
5337
5338 let second_page = &prot.as_slice()[hdr.compressed_page_size as usize..];
5340 let mut prot = ThriftSliceInputProtocol::new(second_page);
5341 let hdr = PageHeader::read_thrift(&mut prot).unwrap();
5342 let stats = hdr.data_page_header.unwrap().statistics;
5343 assert!(stats.is_some());
5344 let stats = stats.unwrap();
5345 assert!(!stats.is_max_value_exact.unwrap());
5347 assert!(!stats.is_min_value_exact.unwrap());
5348 assert_eq!(stats.max_value.unwrap(), b"Bm");
5349 assert_eq!(stats.min_value.unwrap(), b"Bl");
5350 }
5351
5352 #[test]
5353 fn test_page_encoding_statistics_roundtrip() {
5354 let batch_schema = Schema::new(vec![Field::new(
5355 "int32",
5356 arrow_schema::DataType::Int32,
5357 false,
5358 )]);
5359
5360 let batch = RecordBatch::try_new(
5361 Arc::new(batch_schema.clone()),
5362 vec![Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as _],
5363 )
5364 .unwrap();
5365
5366 let mut file: File = tempfile::tempfile().unwrap();
5367 let mut writer = ArrowWriter::try_new(&mut file, Arc::new(batch_schema), None).unwrap();
5368 writer.write(&batch).unwrap();
5369 let file_metadata = writer.close().unwrap();
5370
5371 assert_eq!(file_metadata.num_row_groups(), 1);
5372 assert_eq!(file_metadata.row_group(0).num_columns(), 1);
5373 assert!(
5374 file_metadata
5375 .row_group(0)
5376 .column(0)
5377 .page_encoding_stats()
5378 .is_some()
5379 );
5380 let chunk_page_stats = file_metadata
5381 .row_group(0)
5382 .column(0)
5383 .page_encoding_stats()
5384 .unwrap();
5385
5386 let options = ReadOptionsBuilder::new()
5388 .with_page_index()
5389 .with_encoding_stats_as_mask(false)
5390 .build();
5391 let reader = SerializedFileReader::new_with_options(file, options).unwrap();
5392
5393 let rowgroup = reader.get_row_group(0).expect("row group missing");
5394 assert_eq!(rowgroup.num_columns(), 1);
5395 let column = rowgroup.metadata().column(0);
5396 assert!(column.page_encoding_stats().is_some());
5397 let file_page_stats = column.page_encoding_stats().unwrap();
5398 assert_eq!(chunk_page_stats, file_page_stats);
5399 }
5400
5401 #[test]
5402 fn test_different_dict_page_size_limit() {
5403 let array = Arc::new(Int64Array::from_iter(0..1024 * 1024));
5404 let schema = Arc::new(Schema::new(vec![
5405 Field::new("col0", arrow_schema::DataType::Int64, false),
5406 Field::new("col1", arrow_schema::DataType::Int64, false),
5407 ]));
5408 let batch =
5409 arrow_array::RecordBatch::try_new(schema.clone(), vec![array.clone(), array]).unwrap();
5410
5411 let props = WriterProperties::builder()
5412 .set_dictionary_page_size_limit(1024 * 1024)
5413 .set_column_dictionary_page_size_limit(ColumnPath::from("col1"), 1024 * 1024 * 4)
5414 .build();
5415 let mut writer = ArrowWriter::try_new(Vec::new(), schema, Some(props)).unwrap();
5416 writer.write(&batch).unwrap();
5417 let data = Bytes::from(writer.into_inner().unwrap());
5418
5419 let mut metadata = ParquetMetaDataReader::new();
5420 metadata.try_parse(&data).unwrap();
5421 let metadata = metadata.finish().unwrap();
5422 let col0_meta = metadata.row_group(0).column(0);
5423 let col1_meta = metadata.row_group(0).column(1);
5424
5425 let get_dict_page_size = move |meta: &ColumnChunkMetaData| {
5426 let mut reader =
5427 SerializedPageReader::new(Arc::new(data.clone()), meta, 0, None).unwrap();
5428 let page = reader.get_next_page().unwrap().unwrap();
5429 match page {
5430 Page::DictionaryPage { buf, .. } => buf.len(),
5431 _ => panic!("expected DictionaryPage"),
5432 }
5433 };
5434
5435 assert_eq!(get_dict_page_size(col0_meta), 1024 * 1024);
5436 assert_eq!(get_dict_page_size(col1_meta), 1024 * 1024 * 4);
5437 }
5438
5439 #[test]
5440 fn test_arrow_writer_granular_mode_roundtrip() {
5441 let small = "tiny".to_string();
5450 let big = "x".repeat(64 * 1024);
5451 let strings: Vec<String> = (0..256)
5452 .map(|i| {
5453 if i % 16 == 0 {
5454 big.clone()
5455 } else {
5456 small.clone()
5457 }
5458 })
5459 .collect();
5460
5461 let schema = Arc::new(Schema::new(vec![Field::new(
5462 "col",
5463 ArrowDataType::Utf8,
5464 false,
5465 )]));
5466 let batch = RecordBatch::try_new(
5467 schema.clone(),
5468 vec![Arc::new(StringArray::from(strings.clone())) as _],
5469 )
5470 .unwrap();
5471
5472 let props = WriterProperties::builder()
5473 .set_dictionary_enabled(false)
5474 .set_data_page_size_limit(16 * 1024)
5475 .build();
5476 let mut writer = ArrowWriter::try_new(Vec::new(), schema, Some(props)).unwrap();
5477 writer.write(&batch).unwrap();
5478 let data = Bytes::from(writer.into_inner().unwrap());
5479
5480 let mut reader = ParquetRecordBatchReader::try_new(data, 1024).unwrap();
5481 let read = reader.next().unwrap().unwrap();
5482 assert!(reader.next().is_none(), "expected one batch");
5483 let col = read
5484 .column(0)
5485 .as_any()
5486 .downcast_ref::<StringArray>()
5487 .unwrap();
5488 assert_eq!(col.len(), strings.len());
5489 for (i, expected) in strings.iter().enumerate() {
5490 assert_eq!(
5491 col.value(i),
5492 expected.as_str(),
5493 "value mismatch at index {i}"
5494 );
5495 }
5496 }
5497
5498 #[test]
5499 fn test_arrow_writer_all_null_string_column() {
5500 let num_rows = 1024;
5505 let schema = Arc::new(Schema::new(vec![Field::new(
5506 "col",
5507 ArrowDataType::Utf8,
5508 true,
5509 )]));
5510 let nulls: Vec<Option<&str>> = vec![None; num_rows];
5511 let batch = RecordBatch::try_new(
5512 schema.clone(),
5513 vec![Arc::new(StringArray::from(nulls)) as _],
5514 )
5515 .unwrap();
5516
5517 let props = WriterProperties::builder()
5518 .set_dictionary_enabled(false)
5519 .set_data_page_size_limit(16 * 1024)
5520 .build();
5521 let mut writer = ArrowWriter::try_new(Vec::new(), schema, Some(props)).unwrap();
5522 writer.write(&batch).unwrap();
5523 let data = Bytes::from(writer.into_inner().unwrap());
5524
5525 let mut metadata = ParquetMetaDataReader::new();
5528 metadata.try_parse(&data).unwrap();
5529 let metadata = metadata.finish().unwrap();
5530 let row_group = metadata.row_group(0);
5531 let col_meta = row_group.column(0);
5532 assert_eq!(row_group.num_rows() as usize, num_rows);
5533 if let Some(stats) = col_meta.statistics() {
5536 assert_eq!(
5537 stats.null_count_opt().unwrap_or(0) as usize,
5538 num_rows,
5539 "expected all-null column to report null_count = num_rows"
5540 );
5541 }
5542
5543 let mut reader =
5544 SerializedPageReader::new(Arc::new(data.clone()), col_meta, num_rows, None).unwrap();
5545 let mut total_values = 0u32;
5546 while let Some(page) = reader.get_next_page().unwrap() {
5547 if matches!(page, Page::DataPage { .. } | Page::DataPageV2 { .. }) {
5548 total_values += page.num_values();
5549 }
5550 }
5551 assert_eq!(
5552 total_values as usize, num_rows,
5553 "expected every level position to be represented in some page"
5554 );
5555 }
5556
5557 struct WriteBatchesShape {
5558 num_batches: usize,
5559 rows_per_batch: usize,
5560 row_size: usize,
5561 }
5562
5563 fn write_batches(
5565 WriteBatchesShape {
5566 num_batches,
5567 rows_per_batch,
5568 row_size,
5569 }: WriteBatchesShape,
5570 props: WriterProperties,
5571 ) -> ParquetRecordBatchReaderBuilder<File> {
5572 let schema = Arc::new(Schema::new(vec![Field::new(
5573 "str",
5574 ArrowDataType::Utf8,
5575 false,
5576 )]));
5577 let file = tempfile::tempfile().unwrap();
5578 let mut writer =
5579 ArrowWriter::try_new(file.try_clone().unwrap(), schema.clone(), Some(props)).unwrap();
5580
5581 for batch_idx in 0..num_batches {
5582 let strings: Vec<String> = (0..rows_per_batch)
5583 .map(|i| format!("{:0>width$}", batch_idx * 10 + i, width = row_size))
5584 .collect();
5585 let array = StringArray::from(strings);
5586 let batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(array)]).unwrap();
5587 writer.write(&batch).unwrap();
5588 }
5589 writer.close().unwrap();
5590 ParquetRecordBatchReaderBuilder::try_new(file).unwrap()
5591 }
5592
5593 #[test]
5594 fn test_row_group_limit_none_writes_single_row_group() {
5596 let props = WriterProperties::builder()
5597 .set_max_row_group_row_count(None)
5598 .set_max_row_group_bytes(None)
5599 .build();
5600
5601 let builder = write_batches(
5602 WriteBatchesShape {
5603 num_batches: 1,
5604 rows_per_batch: 1000,
5605 row_size: 4,
5606 },
5607 props,
5608 );
5609
5610 assert_eq!(
5611 &row_group_sizes(builder.metadata()),
5612 &[1000],
5613 "With no limits, all rows should be in a single row group"
5614 );
5615 }
5616
5617 #[test]
5618 fn test_row_group_limit_rows_only() {
5620 let props = WriterProperties::builder()
5621 .set_max_row_group_row_count(Some(300))
5622 .set_max_row_group_bytes(None)
5623 .build();
5624
5625 let builder = write_batches(
5626 WriteBatchesShape {
5627 num_batches: 1,
5628 rows_per_batch: 1000,
5629 row_size: 4,
5630 },
5631 props,
5632 );
5633
5634 assert_eq!(
5635 &row_group_sizes(builder.metadata()),
5636 &[300, 300, 300, 100],
5637 "Row groups should be split by row count"
5638 );
5639 }
5640
5641 #[test]
5642 fn test_row_group_limit_bytes_only() {
5644 let props = WriterProperties::builder()
5645 .set_max_row_group_row_count(None)
5646 .set_max_row_group_bytes(Some(3500))
5648 .build();
5649
5650 let builder = write_batches(
5651 WriteBatchesShape {
5652 num_batches: 10,
5653 rows_per_batch: 10,
5654 row_size: 100,
5655 },
5656 props,
5657 );
5658
5659 let sizes = row_group_sizes(builder.metadata());
5660
5661 assert!(
5662 sizes.len() > 1,
5663 "Should have multiple row groups due to byte limit, got {sizes:?}",
5664 );
5665
5666 let total_rows: i64 = sizes.iter().sum();
5667 assert_eq!(total_rows, 100, "Total rows should be preserved");
5668 }
5669
5670 #[test]
5671 fn test_row_group_limit_bytes_flushes_when_current_group_already_too_large() {
5673 let schema = Arc::new(Schema::new(vec![Field::new(
5674 "str",
5675 ArrowDataType::Utf8,
5676 false,
5677 )]));
5678 let file = tempfile::tempfile().unwrap();
5679
5680 let props = WriterProperties::builder()
5682 .set_max_row_group_row_count(None)
5683 .set_max_row_group_bytes(None)
5684 .build();
5685 let mut writer =
5686 ArrowWriter::try_new(file.try_clone().unwrap(), schema.clone(), Some(props)).unwrap();
5687
5688 let first_array = StringArray::from(
5689 (0..10)
5690 .map(|i| format!("{i:0>100}"))
5691 .collect::<Vec<String>>(),
5692 );
5693 let first_batch =
5694 RecordBatch::try_new(schema.clone(), vec![Arc::new(first_array)]).unwrap();
5695 writer.write(&first_batch).unwrap();
5696 assert_eq!(writer.in_progress_rows(), 10);
5697
5698 writer.max_row_group_bytes = Some(1);
5701
5702 let second_array = StringArray::from(vec!["x".to_string()]);
5703 let second_batch =
5704 RecordBatch::try_new(schema.clone(), vec![Arc::new(second_array)]).unwrap();
5705 writer.write(&second_batch).unwrap();
5706 writer.close().unwrap();
5707 let builder = ParquetRecordBatchReaderBuilder::try_new(file).unwrap();
5708
5709 assert_eq!(
5710 &row_group_sizes(builder.metadata()),
5711 &[10, 1],
5712 "The second write should flush an oversized in-progress row group first",
5713 );
5714 }
5715
5716 #[test]
5717 fn test_row_group_limit_both_row_wins_single_batch() {
5719 let props = WriterProperties::builder()
5720 .set_max_row_group_row_count(Some(200)) .set_max_row_group_bytes(Some(1024 * 1024)) .build();
5723
5724 let builder = write_batches(
5725 WriteBatchesShape {
5726 num_batches: 1,
5727 row_size: 4,
5728 rows_per_batch: 1000,
5729 },
5730 props,
5731 );
5732
5733 assert_eq!(
5734 &row_group_sizes(builder.metadata()),
5735 &[200, 200, 200, 200, 200],
5736 "Row limit should trigger before byte limit"
5737 );
5738 }
5739
5740 #[test]
5741 fn test_row_group_limit_both_row_wins_multiple_batches() {
5743 let props = WriterProperties::builder()
5744 .set_max_row_group_row_count(Some(5)) .set_max_row_group_bytes(Some(9999)) .build();
5747
5748 let builder = write_batches(
5749 WriteBatchesShape {
5750 num_batches: 10,
5751 rows_per_batch: 10,
5752 row_size: 100,
5753 },
5754 props,
5755 );
5756
5757 assert_eq!(
5758 &row_group_sizes(builder.metadata()),
5759 &[5; 20],
5760 "Row limit should trigger before byte limit"
5761 );
5762 }
5763
5764 #[test]
5765 fn test_row_group_limit_both_bytes_wins() {
5767 let props = WriterProperties::builder()
5768 .set_max_row_group_row_count(Some(1000)) .set_max_row_group_bytes(Some(3500)) .build();
5771
5772 let builder = write_batches(
5773 WriteBatchesShape {
5774 num_batches: 10,
5775 rows_per_batch: 10,
5776 row_size: 100,
5777 },
5778 props,
5779 );
5780
5781 let sizes = row_group_sizes(builder.metadata());
5782
5783 assert!(
5784 sizes.len() > 1,
5785 "Byte limit should trigger before row limit, got {sizes:?}",
5786 );
5787
5788 assert!(
5789 sizes.iter().all(|&s| s < 1000),
5790 "No row group should hit the row limit"
5791 );
5792
5793 let total_rows: i64 = sizes.iter().sum();
5794 assert_eq!(total_rows, 100, "Total rows should be preserved");
5795 }
5796
5797 #[test]
5798 fn arrow_column_chunk_close_mut_drops_column_index() {
5799 use crate::arrow::ArrowSchemaConverter;
5800 use crate::file::writer::SerializedFileWriter;
5801
5802 let schema = Arc::new(Schema::new(vec![Field::new("i", DataType::Int32, false)]));
5803 let props = Arc::new(
5804 WriterProperties::builder()
5805 .set_statistics_enabled(EnabledStatistics::Page)
5806 .build(),
5807 );
5808 let parquet_schema = ArrowSchemaConverter::new()
5809 .with_coerce_types(props.coerce_types())
5810 .convert(&schema)
5811 .unwrap();
5812
5813 let mut buf = Vec::with_capacity(1024);
5814 let mut writer =
5815 SerializedFileWriter::new(&mut buf, parquet_schema.root_schema_ptr(), props.clone())
5816 .unwrap();
5817
5818 let factory = ArrowRowGroupWriterFactory::new(&writer, Arc::clone(&schema));
5819 let mut col_writers = factory.create_column_writers(0).unwrap();
5820 let arr: ArrayRef = Arc::new(Int32Array::from_iter_values(0..64));
5821 for leaves in compute_leaves(schema.field(0), &arr).unwrap() {
5822 col_writers[0].write(&leaves).unwrap();
5823 }
5824 let mut chunk = col_writers.pop().unwrap().close().unwrap();
5825
5826 assert!(
5828 chunk.close().column_index.is_some(),
5829 "EnabledStatistics::Page should produce a column_index"
5830 );
5831
5832 chunk.close_mut().column_index = None;
5834 assert!(chunk.close().column_index.is_none());
5835
5836 let mut rg = writer.next_row_group().unwrap();
5837 chunk.append_to_row_group(&mut rg).unwrap();
5838 rg.close().unwrap();
5839 let file_meta = writer.close().unwrap();
5840
5841 let cc = file_meta.row_group(0).column(0);
5844 assert!(cc.column_index_range().is_none());
5845 }
5846
5847 fn write_column_to_bytes(array: ArrayRef) -> Bytes {
5849 let schema = Arc::new(Schema::new(vec![Field::new(
5850 "col",
5851 array.data_type().clone(),
5852 true,
5853 )]));
5854 let buf = get_bytes_after_close(
5855 schema.clone(),
5856 &RecordBatch::try_new(schema, vec![array]).unwrap(),
5857 );
5858 Bytes::from(buf)
5859 }
5860
5861 fn read_column_with_schema(bytes: Bytes, schema: SchemaRef) -> ArrayRef {
5865 let opts = crate::arrow::arrow_reader::ArrowReaderOptions::new().with_schema(schema);
5866 ParquetRecordBatchReaderBuilder::try_new_with_options(bytes, opts)
5867 .unwrap()
5868 .build()
5869 .unwrap()
5870 .next()
5871 .unwrap()
5872 .unwrap()
5873 .column(0)
5874 .clone()
5875 }
5876
5877 fn ree_write_read_roundtrip(ree: ArrayRef, flat: ArrayRef) {
5878 let flat_schema = Arc::new(Schema::new(vec![Field::new(
5879 "col",
5880 flat.data_type().clone(),
5881 true,
5882 )]));
5883 let ree_bytes = write_column_to_bytes(ree);
5884 let flat_bytes = write_column_to_bytes(flat.clone());
5885 assert_eq!(
5886 ree_bytes, flat_bytes,
5887 "REE and flat bytes should be identical"
5888 );
5889
5890 let decoded_ree = read_column_with_schema(ree_bytes, flat_schema.clone());
5891 let decoded_flat = read_column_with_schema(flat_bytes, flat_schema);
5892
5893 assert_eq!(decoded_ree.as_ref(), flat.as_ref());
5894 assert_eq!(decoded_ree.as_ref(), decoded_flat.as_ref());
5895 }
5896
5897 #[test]
5898 fn ree_string() {
5899 let ree: ArrayRef = Arc::new(
5900 [Some("a"), Some("a"), None, Some("b"), Some("b")]
5901 .into_iter()
5902 .collect::<Int32RunArray>(),
5903 );
5904 let flat: ArrayRef = Arc::new(StringArray::from(vec![
5905 Some("a"),
5906 Some("a"),
5907 None,
5908 Some("b"),
5909 Some("b"),
5910 ]));
5911 ree_write_read_roundtrip(ree, flat);
5912 }
5913
5914 #[test]
5915 fn ree_int32() {
5916 let mut b = PrimitiveRunBuilder::<Int32Type, Int32Type>::new();
5917 for v in [Some(1), Some(1), None, Some(2), Some(2)] {
5918 b.append_option(v);
5919 }
5920 let ree: ArrayRef = Arc::new(b.finish());
5921 let flat: ArrayRef = Arc::new(Int32Array::from(vec![
5922 Some(1),
5923 Some(1),
5924 None,
5925 Some(2),
5926 Some(2),
5927 ]));
5928 ree_write_read_roundtrip(ree, flat);
5929 }
5930
5931 #[test]
5932 fn ree_bool() {
5933 let ree: ArrayRef = Arc::new(
5935 RunArray::try_new(
5936 &Int32Array::from(vec![3, 5, 7]),
5937 &BooleanArray::from(vec![Some(true), None, Some(false)]),
5938 )
5939 .unwrap(),
5940 );
5941 let flat: ArrayRef = Arc::new(BooleanArray::from(vec![
5942 Some(true),
5943 Some(true),
5944 Some(true),
5945 None,
5946 None,
5947 Some(false),
5948 Some(false),
5949 ]));
5950 ree_write_read_roundtrip(ree, flat);
5951 }
5952
5953 #[test]
5954 fn ree_fixed_size_binary() {
5955 let mk = |vals: &[Option<&[u8]>]| -> FixedSizeBinaryArray {
5956 let mut b = FixedSizeBinaryBuilder::new(2);
5957 for v in vals {
5958 match v {
5959 Some(x) => b.append_value(x).unwrap(),
5960 None => b.append_null(),
5961 }
5962 }
5963 b.finish()
5964 };
5965 let ree: ArrayRef = Arc::new(
5967 RunArray::try_new(
5968 &Int32Array::from(vec![2, 4, 6]),
5969 &mk(&[Some(b"aa"), None, Some(b"bb")]),
5970 )
5971 .unwrap(),
5972 );
5973 let flat: ArrayRef = Arc::new(mk(&[
5974 Some(b"aa"),
5975 Some(b"aa"),
5976 None,
5977 None,
5978 Some(b"bb"),
5979 Some(b"bb"),
5980 ]));
5981 ree_write_read_roundtrip(ree, flat);
5982 }
5983
5984 #[test]
5985 fn ree_single_run() {
5986 let ree: ArrayRef = Arc::new(["x", "x", "x"].into_iter().collect::<Int32RunArray>());
5987 let flat: ArrayRef = Arc::new(StringArray::from(vec!["x", "x", "x"]));
5988 ree_write_read_roundtrip(ree, flat);
5989 }
5990
5991 #[test]
5992 fn ree_float32() {
5993 let ree: ArrayRef = Arc::new(
5995 RunArray::try_new(
5996 &Int32Array::from(vec![2, 4, 5]),
5997 &Float32Array::from(vec![Some(1.0_f32), None, Some(2.5_f32)]),
5998 )
5999 .unwrap(),
6000 );
6001 let flat: ArrayRef = Arc::new(Float32Array::from(vec![
6002 Some(1.0_f32),
6003 Some(1.0_f32),
6004 None,
6005 None,
6006 Some(2.5_f32),
6007 ]));
6008 ree_write_read_roundtrip(ree, flat);
6009 }
6010
6011 #[test]
6012 fn ree_sliced() {
6013 let full: ArrayRef = Arc::new(
6018 RunArray::try_new(
6019 &Int32Array::from(vec![3, 5, 7]),
6020 &StringArray::from(vec!["a", "b", "c"]),
6021 )
6022 .unwrap(),
6023 );
6024 let sliced = full.slice(2, 5);
6025 let flat: ArrayRef = Arc::new(StringArray::from(vec!["a", "b", "b", "c", "c"]));
6026 ree_write_read_roundtrip(sliced, flat);
6027 }
6028
6029 #[test]
6030 fn ree_struct_with_ree_child() {
6031 let run_ends = Int32Array::from(vec![2i32, 3, 5]);
6034
6035 let col_a: ArrayRef = Arc::new(
6036 RunArray::try_new(
6037 &run_ends,
6038 &StringArray::from(vec![Some("foo"), None, Some("bar")]),
6039 )
6040 .unwrap(),
6041 );
6042 let col_b: ArrayRef = Arc::new(
6043 RunArray::try_new(&run_ends, &Int32Array::from(vec![Some(1), None, Some(2)])).unwrap(),
6044 );
6045
6046 let struct_array: ArrayRef = Arc::new(StructArray::new(
6047 Fields::from(vec![
6048 Field::new("a", col_a.data_type().clone(), true),
6049 Field::new("b", col_b.data_type().clone(), true),
6050 ]),
6051 vec![col_a, col_b],
6052 None,
6053 ));
6054
6055 let schema = Arc::new(Schema::new(vec![Field::new(
6056 "row",
6057 struct_array.data_type().clone(),
6058 true,
6059 )]));
6060 let batch = RecordBatch::try_new(schema.clone(), vec![struct_array]).unwrap();
6061
6062 let mut buf = Vec::new();
6063 let mut writer = ArrowWriter::try_new(&mut buf, schema, None).unwrap();
6064 writer.write(&batch).unwrap();
6065 let metadata = writer.close().unwrap();
6066
6067 let parquet_schema = metadata.file_metadata().schema_descr();
6068 assert_eq!(parquet_schema.num_columns(), 2);
6069 assert_eq!(
6070 parquet_schema.column(0).physical_type(),
6071 crate::basic::Type::BYTE_ARRAY
6072 );
6073 assert_eq!(parquet_schema.column(0).path().string(), "row.a");
6074 assert_eq!(
6075 parquet_schema.column(1).physical_type(),
6076 crate::basic::Type::INT32
6077 );
6078 assert_eq!(parquet_schema.column(1).path().string(), "row.b");
6079 }
6080}