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