1mod binary;
16mod binview;
17mod boolean;
18mod dictionary;
19mod file;
20mod fixed_size_binary;
21mod nested;
22mod pages;
23mod primitive;
24mod row_group;
25mod schema;
26mod utils;
27
28use arrow::array::*;
29use arrow::bitmap::Bitmap;
30use arrow::datatypes::*;
31use arrow::types::{NativeType, days_ms, i256};
32pub use nested::{num_values, write_rep_and_def};
33pub use pages::{to_leaves, to_nested, to_parquet_leaves};
34use polars_config::config;
35use polars_utils::float16::pf16;
36use polars_utils::pl_str::PlSmallStr;
37pub use utils::write_def_levels;
38
39pub use crate::parquet::compression::{BrotliLevel, CompressionOptions, GzipLevel, ZstdLevel};
40pub use crate::parquet::encoding::Encoding;
41pub use crate::parquet::metadata::{
42 Descriptor, FileMetadata, KeyValue, SchemaDescriptor, ThriftFileMetadata,
43};
44pub use crate::parquet::page::{CompressedDataPage, CompressedPage, Page};
45use crate::parquet::schema::Repetition;
46use crate::parquet::schema::types::PrimitiveType as ParquetPrimitiveType;
47pub use crate::parquet::schema::types::{
48 FieldInfo, ParquetType, PhysicalType as ParquetPhysicalType,
49};
50pub use crate::parquet::write::{
51 Compressor, DynIter, DynStreamingIterator, RowGroupIterColumns, Version, compress,
52 write_metadata_sidecar,
53};
54pub use crate::parquet::{FallibleStreamingIterator, fallible_streaming_iterator};
55use crate::write::fixed_size_binary::build_statistics_float16;
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
61pub struct StatisticsOptions {
62 pub min_value: bool,
63 pub max_value: bool,
64 pub distinct_count: bool,
65 pub null_count: bool,
66 pub binary_statistics_truncate_length: Option<u64>,
69}
70
71impl Default for StatisticsOptions {
72 fn default() -> Self {
73 Self {
74 min_value: true,
75 max_value: true,
76 distinct_count: false,
77 null_count: true,
78 binary_statistics_truncate_length: None,
79 }
80 }
81}
82
83#[derive(Clone, Copy)]
85pub enum EncodeNullability {
86 Required,
87 Optional,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct WriteOptions {
93 pub statistics: StatisticsOptions,
95 pub version: Version,
97 pub compression: CompressionOptions,
99 pub data_page_size: Option<usize>,
101}
102
103use arrow::compute::aggregate::estimated_bytes_size;
104use arrow::match_integer_type;
105pub use file::FileWriter;
106pub use pages::{Nested, array_to_columns, arrays_to_columns};
107use polars_error::{PolarsResult, polars_bail};
108pub use row_group::{RowGroupIterator, row_group_iter};
109pub use schema::{schema_to_metadata_key, to_parquet_type};
110
111use self::pages::{FixedSizeListNested, PrimitiveNested, StructNested};
112use crate::write::dictionary::encode_as_dictionary_optional;
113
114impl StatisticsOptions {
115 pub fn empty() -> Self {
116 Self {
117 min_value: false,
118 max_value: false,
119 distinct_count: false,
120 null_count: false,
121 binary_statistics_truncate_length: None,
122 }
123 }
124
125 pub fn full() -> Self {
126 Self {
127 min_value: true,
128 max_value: true,
129 distinct_count: true,
130 null_count: true,
131 binary_statistics_truncate_length: None,
132 }
133 }
134
135 pub fn is_empty(&self) -> bool {
136 !(self.min_value || self.max_value || self.distinct_count || self.null_count)
137 }
138
139 pub fn is_full(&self) -> bool {
140 self.min_value && self.max_value && self.distinct_count && self.null_count
141 }
142
143 pub fn binary_statistics_truncate_length(&self) -> Option<u64> {
145 let len = self
146 .binary_statistics_truncate_length
147 .unwrap_or_else(|| config().parquet_binary_statistics_truncate_length());
148 (len > 0).then_some(len)
149 }
150
151 pub fn binary_statistics_truncate_length_usize(&self) -> Option<usize> {
152 self.binary_statistics_truncate_length()
153 .and_then(|x| usize::try_from(x).ok())
154 }
155}
156
157impl WriteOptions {
158 pub fn has_statistics(&self) -> bool {
159 !self.statistics.is_empty()
160 }
161}
162
163impl EncodeNullability {
164 const fn new(is_optional: bool) -> Self {
165 if is_optional {
166 Self::Optional
167 } else {
168 Self::Required
169 }
170 }
171
172 fn is_optional(self) -> bool {
173 matches!(self, Self::Optional)
174 }
175}
176
177pub(crate) fn row_slice_ranges(
181 number_of_rows: usize,
182 byte_size: usize,
183 options: WriteOptions,
184) -> impl Iterator<Item = (usize, usize)> {
185 const DEFAULT_PAGE_SIZE: usize = 1024 * 1024; let max_page_size = options.data_page_size.unwrap_or(DEFAULT_PAGE_SIZE);
187 let max_page_size = max_page_size.min(2usize.pow(31) - 2usize.pow(25)); let bytes_per_row = if number_of_rows == 0 {
190 0
191 } else {
192 ((byte_size as f64) / (number_of_rows as f64)) as usize
193 };
194 let rows_per_page = (max_page_size / (bytes_per_row + 1)).max(1);
195
196 (0..number_of_rows)
197 .step_by(rows_per_page)
198 .map(move |offset| {
199 let length = (offset + rows_per_page).min(number_of_rows) - offset;
200 (offset, length)
201 })
202}
203
204pub fn slice_nested_leaf(nested: &[Nested]) -> (usize, usize) {
206 let mut out = (0, 0);
209 for nested in nested.iter().rev() {
210 match nested {
211 Nested::LargeList(l_nested) => {
212 let start = *l_nested.offsets.first();
213 let end = *l_nested.offsets.last();
214 return (start as usize, (end - start) as usize);
215 },
216 Nested::List(l_nested) => {
217 let start = *l_nested.offsets.first();
218 let end = *l_nested.offsets.last();
219 return (start as usize, (end - start) as usize);
220 },
221 Nested::FixedSizeList(nested) => return (0, nested.length * nested.width),
222 Nested::Primitive(nested) => out = (0, nested.length),
223 Nested::Struct(_) => {},
224 }
225 }
226 out
227}
228
229fn decimal_length_from_precision(precision: usize) -> usize {
230 (((10.0_f64.powi(precision as i32) + 1.0).log2() + 1.0) / 8.0).ceil() as usize
238}
239
240pub fn to_parquet_schema(schema: &ArrowSchema) -> PolarsResult<SchemaDescriptor> {
242 let parquet_types = schema
243 .iter_values()
244 .map(to_parquet_type)
245 .collect::<PolarsResult<Vec<_>>>()?;
246 Ok(SchemaDescriptor::new(
247 PlSmallStr::from_static("root"),
248 parquet_types,
249 ))
250}
251
252pub fn slice_parquet_array(
254 primitive_array: &mut dyn Array,
255 nested: &mut [Nested],
256 mut current_offset: usize,
257 mut current_length: usize,
258) {
259 for nested in nested.iter_mut() {
260 match nested {
261 Nested::LargeList(l_nested) => {
262 l_nested.offsets.slice(current_offset, current_length + 1);
263 if let Some(validity) = l_nested.validity.as_mut() {
264 validity.slice(current_offset, current_length)
265 };
266
267 current_length = l_nested.offsets.range() as usize;
269 current_offset = *l_nested.offsets.first() as usize;
270 },
271 Nested::List(l_nested) => {
272 l_nested.offsets.slice(current_offset, current_length + 1);
273 if let Some(validity) = l_nested.validity.as_mut() {
274 validity.slice(current_offset, current_length)
275 };
276
277 current_length = l_nested.offsets.range() as usize;
279 current_offset = *l_nested.offsets.first() as usize;
280 },
281 Nested::Struct(StructNested {
282 validity, length, ..
283 }) => {
284 *length = current_length;
285 if let Some(validity) = validity.as_mut() {
286 validity.slice(current_offset, current_length)
287 };
288 },
289 Nested::Primitive(PrimitiveNested {
290 validity, length, ..
291 }) => {
292 *length = current_length;
293 if let Some(validity) = validity.as_mut() {
294 validity.slice(current_offset, current_length)
295 };
296 primitive_array.slice(current_offset, current_length);
297 },
298 Nested::FixedSizeList(FixedSizeListNested {
299 validity,
300 length,
301 width,
302 ..
303 }) => {
304 if let Some(validity) = validity.as_mut() {
305 validity.slice(current_offset, current_length)
306 };
307 *length = current_length;
308 current_length *= *width;
310 current_offset *= *width;
311 },
312 }
313 }
314}
315
316pub fn get_max_length(nested: &[Nested]) -> usize {
318 let mut length = 0;
319 for nested in nested.iter() {
320 match nested {
321 Nested::LargeList(l_nested) => length += l_nested.offsets.range() as usize,
322 Nested::List(l_nested) => length += l_nested.offsets.range() as usize,
323 Nested::FixedSizeList(nested) => length += nested.length * nested.width,
324 _ => {},
325 }
326 }
327 length
328}
329
330pub fn array_to_pages(
332 primitive_array: &dyn Array,
333 type_: ParquetPrimitiveType,
334 nested: &[Nested],
335 options: WriteOptions,
336 mut encoding: Encoding,
337) -> PolarsResult<DynIter<'static, PolarsResult<Page>>> {
338 if let ArrowDataType::Dictionary(key_type, _, _) = primitive_array.dtype().to_storage() {
339 return match_integer_type!(key_type, |$T| {
340 dictionary::array_to_pages::<$T>(
341 primitive_array.as_any().downcast_ref().unwrap(),
342 type_,
343 &nested,
344 options,
345 encoding,
346 )
347 });
348 };
349 if let Encoding::RleDictionary = encoding {
350 if matches!(nested.first(), Some(Nested::Primitive(_))) {
352 if let Some(result) =
353 encode_as_dictionary_optional(primitive_array, nested, type_.clone(), options)
354 {
355 return result;
356 }
357 }
358
359 encoding = Encoding::Plain;
361 }
362
363 let nested = nested.to_vec();
364 let number_of_rows = nested[0].len();
365 let byte_size = estimated_bytes_size(primitive_array);
368 let primitive_array = primitive_array.to_boxed();
369
370 let pages =
371 row_slice_ranges(number_of_rows, byte_size, options).map(move |(offset, length)| {
372 let mut right_array = primitive_array.clone();
373 let mut right_nested = nested.clone();
374 slice_parquet_array(right_array.as_mut(), &mut right_nested, offset, length);
375
376 array_to_page(
377 right_array.as_ref(),
378 type_.clone(),
379 &right_nested,
380 options,
381 encoding,
382 )
383 });
384 Ok(DynIter::new(pages))
385}
386
387pub fn array_to_page(
389 array: &dyn Array,
390 type_: ParquetPrimitiveType,
391 nested: &[Nested],
392 options: WriteOptions,
393 encoding: Encoding,
394) -> PolarsResult<Page> {
395 if nested.len() == 1 {
396 return array_to_page_simple(array, type_, options, encoding);
398 }
399 array_to_page_nested(array, type_, nested, options, encoding)
400}
401
402pub fn array_to_page_simple(
404 array: &dyn Array,
405 type_: ParquetPrimitiveType,
406 options: WriteOptions,
407 encoding: Encoding,
408) -> PolarsResult<Page> {
409 let dtype = array.dtype();
410
411 if type_.field_info.repetition == Repetition::Required && array.null_count() > 0 {
412 polars_bail!(InvalidOperation: "writing a missing value to required parquet column '{}'", type_.field_info.name);
413 }
414
415 match dtype {
416 ArrowDataType::Struct(fs) if fs.is_empty() => boolean::array_to_page(
418 &BooleanArray::new(
419 ArrowDataType::Boolean,
420 Bitmap::new_zeroed(array.len()),
421 array.validity().cloned(),
422 ),
423 options,
424 type_,
425 encoding,
426 ),
427
428 ArrowDataType::Boolean => boolean::array_to_page(
429 array.as_any().downcast_ref().unwrap(),
430 options,
431 type_,
432 encoding,
433 ),
434 ArrowDataType::UInt8 => {
436 return primitive::array_to_page_integer::<u8, i32>(
437 array.as_any().downcast_ref().unwrap(),
438 options,
439 type_,
440 encoding,
441 );
442 },
443 ArrowDataType::UInt16 => {
444 return primitive::array_to_page_integer::<u16, i32>(
445 array.as_any().downcast_ref().unwrap(),
446 options,
447 type_,
448 encoding,
449 );
450 },
451 ArrowDataType::UInt32 => {
452 return primitive::array_to_page_integer::<u32, i32>(
453 array.as_any().downcast_ref().unwrap(),
454 options,
455 type_,
456 encoding,
457 );
458 },
459 ArrowDataType::UInt64 => {
460 return primitive::array_to_page_integer::<u64, i64>(
461 array.as_any().downcast_ref().unwrap(),
462 options,
463 type_,
464 encoding,
465 );
466 },
467 ArrowDataType::Int8 => {
468 return primitive::array_to_page_integer::<i8, i32>(
469 array.as_any().downcast_ref().unwrap(),
470 options,
471 type_,
472 encoding,
473 );
474 },
475 ArrowDataType::Int16 => {
476 return primitive::array_to_page_integer::<i16, i32>(
477 array.as_any().downcast_ref().unwrap(),
478 options,
479 type_,
480 encoding,
481 );
482 },
483 ArrowDataType::Int32 | ArrowDataType::Date32 | ArrowDataType::Time32(_) => {
484 return primitive::array_to_page_integer::<i32, i32>(
485 array.as_any().downcast_ref().unwrap(),
486 options,
487 type_,
488 encoding,
489 );
490 },
491 ArrowDataType::Int64
492 | ArrowDataType::Date64
493 | ArrowDataType::Time64(_)
494 | ArrowDataType::Timestamp(_, _)
495 | ArrowDataType::Duration(_) => {
496 return primitive::array_to_page_integer::<i64, i64>(
497 array.as_any().downcast_ref().unwrap(),
498 options,
499 type_,
500 encoding,
501 );
502 },
503 ArrowDataType::Float16 => {
504 let array: &PrimitiveArray<pf16> = array.as_any().downcast_ref().unwrap();
505 let statistics = options
506 .has_statistics()
507 .then(|| build_statistics_float16(array, type_.clone(), &options.statistics));
508 let array = FixedSizeBinaryArray::new(
509 ArrowDataType::FixedSizeBinary(2),
510 array.values().clone().try_transmute().unwrap(),
511 array.validity().cloned(),
512 );
513 fixed_size_binary::array_to_page(&array, options, type_, statistics)
514 },
515 ArrowDataType::Float32 => primitive::array_to_page_plain::<f32, f32>(
516 array.as_any().downcast_ref().unwrap(),
517 options,
518 type_,
519 ),
520 ArrowDataType::Float64 => primitive::array_to_page_plain::<f64, f64>(
521 array.as_any().downcast_ref().unwrap(),
522 options,
523 type_,
524 ),
525 ArrowDataType::LargeUtf8 => {
526 let array =
527 polars_compute::cast::cast(array, &ArrowDataType::LargeBinary, Default::default())
528 .unwrap();
529 return binary::array_to_page::<i64>(
530 array.as_any().downcast_ref().unwrap(),
531 options,
532 type_,
533 encoding,
534 );
535 },
536 ArrowDataType::LargeBinary => {
537 return binary::array_to_page::<i64>(
538 array.as_any().downcast_ref().unwrap(),
539 options,
540 type_,
541 encoding,
542 );
543 },
544 ArrowDataType::BinaryView => {
545 return binview::array_to_page(
546 array.as_any().downcast_ref().unwrap(),
547 options,
548 type_,
549 encoding,
550 );
551 },
552 ArrowDataType::Utf8View => {
553 let array =
554 polars_compute::cast::cast(array, &ArrowDataType::BinaryView, Default::default())
555 .unwrap();
556 return binview::array_to_page(
557 array.as_any().downcast_ref().unwrap(),
558 options,
559 type_,
560 encoding,
561 );
562 },
563 ArrowDataType::Null => {
564 let array = Int32Array::new_null(ArrowDataType::Int32, array.len());
565 primitive::array_to_page_plain::<i32, i32>(&array, options, type_)
566 },
567 ArrowDataType::Interval(IntervalUnit::YearMonth) => {
568 let array = array
569 .as_any()
570 .downcast_ref::<PrimitiveArray<i32>>()
571 .unwrap();
572 let mut values = Vec::<u8>::with_capacity(12 * array.len());
573 array.values().iter().for_each(|x| {
574 let bytes = &x.to_le_bytes();
575 values.extend_from_slice(bytes);
576 values.extend_from_slice(&[0; 8]);
577 });
578 let array = FixedSizeBinaryArray::new(
579 ArrowDataType::FixedSizeBinary(12),
580 values.into(),
581 array.validity().cloned(),
582 );
583 let statistics = if options.has_statistics() {
584 Some(fixed_size_binary::build_statistics(
585 &array,
586 type_.clone(),
587 &options.statistics,
588 ))
589 } else {
590 None
591 };
592 fixed_size_binary::array_to_page(&array, options, type_, statistics)
593 },
594 ArrowDataType::Interval(IntervalUnit::DayTime) => {
595 let array = array
596 .as_any()
597 .downcast_ref::<PrimitiveArray<days_ms>>()
598 .unwrap();
599 let mut values = Vec::<u8>::with_capacity(12 * array.len());
600 array.values().iter().for_each(|x| {
601 let bytes = &x.to_le_bytes();
602 values.extend_from_slice(&[0; 4]); values.extend_from_slice(bytes); });
605 let array = FixedSizeBinaryArray::new(
606 ArrowDataType::FixedSizeBinary(12),
607 values.into(),
608 array.validity().cloned(),
609 );
610 let statistics = if options.has_statistics() {
611 Some(fixed_size_binary::build_statistics(
612 &array,
613 type_.clone(),
614 &options.statistics,
615 ))
616 } else {
617 None
618 };
619 fixed_size_binary::array_to_page(&array, options, type_, statistics)
620 },
621 ArrowDataType::FixedSizeBinary(_) => {
622 let array = array.as_any().downcast_ref().unwrap();
623 let statistics = if options.has_statistics() {
624 Some(fixed_size_binary::build_statistics(
625 array,
626 type_.clone(),
627 &options.statistics,
628 ))
629 } else {
630 None
631 };
632
633 fixed_size_binary::array_to_page(array, options, type_, statistics)
634 },
635 ArrowDataType::Decimal256(precision, _) => {
636 let precision = *precision;
637 let array = array
638 .as_any()
639 .downcast_ref::<PrimitiveArray<i256>>()
640 .unwrap();
641 if precision <= 9 {
642 let values = array
643 .values()
644 .iter()
645 .map(|x| x.0.as_i32())
646 .collect::<Vec<_>>()
647 .into();
648
649 let array = PrimitiveArray::<i32>::new(
650 ArrowDataType::Int32,
651 values,
652 array.validity().cloned(),
653 );
654 return primitive::array_to_page_integer::<i32, i32>(
655 &array, options, type_, encoding,
656 );
657 } else if precision <= 18 {
658 let values = array
659 .values()
660 .iter()
661 .map(|x| x.0.as_i64())
662 .collect::<Vec<_>>()
663 .into();
664
665 let array = PrimitiveArray::<i64>::new(
666 ArrowDataType::Int64,
667 values,
668 array.validity().cloned(),
669 );
670 return primitive::array_to_page_integer::<i64, i64>(
671 &array, options, type_, encoding,
672 );
673 } else if precision <= 38 {
674 let size = decimal_length_from_precision(precision);
675 let statistics = if options.has_statistics() {
676 let stats = fixed_size_binary::build_statistics_decimal256_with_i128(
677 array,
678 type_.clone(),
679 size,
680 &options.statistics,
681 );
682 Some(stats)
683 } else {
684 None
685 };
686
687 let mut values = Vec::<u8>::with_capacity(size * array.len());
688 array.values().iter().for_each(|x| {
689 let bytes = &x.0.low().to_be_bytes()[16 - size..];
690 values.extend_from_slice(bytes)
691 });
692 let array = FixedSizeBinaryArray::new(
693 ArrowDataType::FixedSizeBinary(size),
694 values.into(),
695 array.validity().cloned(),
696 );
697 fixed_size_binary::array_to_page(&array, options, type_, statistics)
698 } else {
699 let size = 32;
700 let array = array
701 .as_any()
702 .downcast_ref::<PrimitiveArray<i256>>()
703 .unwrap();
704 let statistics = if options.has_statistics() {
705 let stats = fixed_size_binary::build_statistics_decimal256(
706 array,
707 type_.clone(),
708 size,
709 &options.statistics,
710 );
711 Some(stats)
712 } else {
713 None
714 };
715 let mut values = Vec::<u8>::with_capacity(size * array.len());
716 array.values().iter().for_each(|x| {
717 let bytes = &x.to_be_bytes();
718 values.extend_from_slice(bytes)
719 });
720 let array = FixedSizeBinaryArray::new(
721 ArrowDataType::FixedSizeBinary(size),
722 values.into(),
723 array.validity().cloned(),
724 );
725
726 fixed_size_binary::array_to_page(&array, options, type_, statistics)
727 }
728 },
729 ArrowDataType::Decimal(precision, _) => {
730 let precision = *precision;
731 let array = array
732 .as_any()
733 .downcast_ref::<PrimitiveArray<i128>>()
734 .unwrap();
735 if precision <= 9 {
736 let values = array
737 .values()
738 .iter()
739 .map(|x| *x as i32)
740 .collect::<Vec<_>>()
741 .into();
742
743 let array = PrimitiveArray::<i32>::new(
744 ArrowDataType::Int32,
745 values,
746 array.validity().cloned(),
747 );
748 return primitive::array_to_page_integer::<i32, i32>(
749 &array, options, type_, encoding,
750 );
751 } else if precision <= 18 {
752 let values = array
753 .values()
754 .iter()
755 .map(|x| *x as i64)
756 .collect::<Vec<_>>()
757 .into();
758
759 let array = PrimitiveArray::<i64>::new(
760 ArrowDataType::Int64,
761 values,
762 array.validity().cloned(),
763 );
764 return primitive::array_to_page_integer::<i64, i64>(
765 &array, options, type_, encoding,
766 );
767 } else {
768 let size = decimal_length_from_precision(precision);
769
770 let statistics = if options.has_statistics() {
771 let stats = fixed_size_binary::build_statistics_decimal(
772 array,
773 type_.clone(),
774 size,
775 &options.statistics,
776 );
777 Some(stats)
778 } else {
779 None
780 };
781
782 let mut values = Vec::<u8>::with_capacity(size * array.len());
783 array.values().iter().for_each(|x| {
784 let bytes = &x.to_be_bytes()[16 - size..];
785 values.extend_from_slice(bytes)
786 });
787 let array = FixedSizeBinaryArray::new(
788 ArrowDataType::FixedSizeBinary(size),
789 values.into(),
790 array.validity().cloned(),
791 );
792 fixed_size_binary::array_to_page(&array, options, type_, statistics)
793 }
794 },
795 ArrowDataType::UInt128 => {
796 let array: &PrimitiveArray<u128> = array.as_any().downcast_ref().unwrap();
797 let statistics = if options.has_statistics() {
798 let stats = fixed_size_binary::build_statistics_decimal(
799 array,
800 type_.clone(),
801 16,
802 &options.statistics,
803 );
804 Some(stats)
805 } else {
806 None
807 };
808 let array = FixedSizeBinaryArray::new(
809 ArrowDataType::FixedSizeBinary(16),
810 array.values().clone().try_transmute().unwrap(),
811 array.validity().cloned(),
812 );
813 fixed_size_binary::array_to_page(&array, options, type_, statistics)
814 },
815 ArrowDataType::Int128 => {
816 let array: &PrimitiveArray<i128> = array.as_any().downcast_ref().unwrap();
817 let statistics = if options.has_statistics() {
818 let stats = fixed_size_binary::build_statistics_decimal(
819 array,
820 type_.clone(),
821 16,
822 &options.statistics,
823 );
824 Some(stats)
825 } else {
826 None
827 };
828 let array = FixedSizeBinaryArray::new(
829 ArrowDataType::FixedSizeBinary(16),
830 array.values().clone().try_transmute().unwrap(),
831 array.validity().cloned(),
832 );
833 fixed_size_binary::array_to_page(&array, options, type_, statistics)
834 },
835 ArrowDataType::Extension(ext) => {
836 let mut boxed = array.to_boxed();
837 assert!(matches!(boxed.dtype(), ArrowDataType::Extension(ext2) if ext2 == ext));
838 *boxed.dtype_mut() = ext.inner.clone();
839 return array_to_page_simple(boxed.as_ref(), type_, options, encoding);
840 },
841 other => polars_bail!(nyi = "Writing parquet pages for data type {other:?}"),
842 }
843 .map(Page::Data)
844}
845
846fn array_to_page_nested(
847 array: &dyn Array,
848 type_: ParquetPrimitiveType,
849 nested: &[Nested],
850 options: WriteOptions,
851 _encoding: Encoding,
852) -> PolarsResult<Page> {
853 if type_.field_info.repetition == Repetition::Required
854 && array.validity().is_some_and(|v| v.unset_bits() > 0)
855 {
856 polars_bail!(InvalidOperation: "writing a missing value to required parquet column '{}'", type_.field_info.name);
857 }
858
859 use ArrowDataType::*;
860 match array.dtype().to_storage() {
861 Null => {
862 let array = Int32Array::new_null(ArrowDataType::Int32, array.len());
863 primitive::nested_array_to_page::<i32, i32>(&array, options, type_, nested)
864 },
865 Struct(fs) if fs.is_empty() => {
867 let array = BooleanArray::new(
868 ArrowDataType::Boolean,
869 Bitmap::new_zeroed(array.len()),
870 array.validity().cloned(),
871 );
872 boolean::nested_array_to_page(&array, options, type_, nested)
873 },
874 Boolean => {
875 let array = array.as_any().downcast_ref().unwrap();
876 boolean::nested_array_to_page(array, options, type_, nested)
877 },
878 LargeUtf8 => {
879 let array =
880 polars_compute::cast::cast(array, &LargeBinary, Default::default()).unwrap();
881 let array = array.as_any().downcast_ref().unwrap();
882 binary::nested_array_to_page::<i64>(array, options, type_, nested)
883 },
884 LargeBinary => {
885 let array = array.as_any().downcast_ref().unwrap();
886 binary::nested_array_to_page::<i64>(array, options, type_, nested)
887 },
888 BinaryView => {
889 let array = array.as_any().downcast_ref().unwrap();
890 binview::nested_array_to_page(array, options, type_, nested)
891 },
892 Utf8View => {
893 let array = polars_compute::cast::cast(array, &BinaryView, Default::default()).unwrap();
894 let array = array.as_any().downcast_ref().unwrap();
895 binview::nested_array_to_page(array, options, type_, nested)
896 },
897 UInt8 => {
898 let array = array.as_any().downcast_ref().unwrap();
899 primitive::nested_array_to_page::<u8, i32>(array, options, type_, nested)
900 },
901 UInt16 => {
902 let array = array.as_any().downcast_ref().unwrap();
903 primitive::nested_array_to_page::<u16, i32>(array, options, type_, nested)
904 },
905 UInt32 => {
906 let array = array.as_any().downcast_ref().unwrap();
907 primitive::nested_array_to_page::<u32, i32>(array, options, type_, nested)
908 },
909 UInt64 => {
910 let array = array.as_any().downcast_ref().unwrap();
911 primitive::nested_array_to_page::<u64, i64>(array, options, type_, nested)
912 },
913 Int8 => {
914 let array = array.as_any().downcast_ref().unwrap();
915 primitive::nested_array_to_page::<i8, i32>(array, options, type_, nested)
916 },
917 Int16 => {
918 let array = array.as_any().downcast_ref().unwrap();
919 primitive::nested_array_to_page::<i16, i32>(array, options, type_, nested)
920 },
921 Int32 | Date32 | Time32(_) => {
922 let array = array.as_any().downcast_ref().unwrap();
923 primitive::nested_array_to_page::<i32, i32>(array, options, type_, nested)
924 },
925 Int64 | Date64 | Time64(_) | Timestamp(_, _) | Duration(_) => {
926 let array = array.as_any().downcast_ref().unwrap();
927 primitive::nested_array_to_page::<i64, i64>(array, options, type_, nested)
928 },
929 Float16 => {
930 let array: &PrimitiveArray<pf16> = array.as_any().downcast_ref().unwrap();
931 let statistics = options
932 .has_statistics()
933 .then(|| build_statistics_float16(array, type_.clone(), &options.statistics));
934 let array = FixedSizeBinaryArray::new(
935 ArrowDataType::FixedSizeBinary(2),
936 array.values().clone().try_transmute().unwrap(),
937 array.validity().cloned(),
938 );
939 fixed_size_binary::nested_array_to_page(&array, options, type_, nested, statistics)
940 },
941 Float32 => {
942 let array = array.as_any().downcast_ref().unwrap();
943 primitive::nested_array_to_page::<f32, f32>(array, options, type_, nested)
944 },
945 Float64 => {
946 let array = array.as_any().downcast_ref().unwrap();
947 primitive::nested_array_to_page::<f64, f64>(array, options, type_, nested)
948 },
949 Decimal(precision, _) => {
950 let precision = *precision;
951 let array = array
952 .as_any()
953 .downcast_ref::<PrimitiveArray<i128>>()
954 .unwrap();
955 if precision <= 9 {
956 let values = array
957 .values()
958 .iter()
959 .map(|x| *x as i32)
960 .collect::<Vec<_>>()
961 .into();
962
963 let array = PrimitiveArray::<i32>::new(
964 ArrowDataType::Int32,
965 values,
966 array.validity().cloned(),
967 );
968 primitive::nested_array_to_page::<i32, i32>(&array, options, type_, nested)
969 } else if precision <= 18 {
970 let values = array
971 .values()
972 .iter()
973 .map(|x| *x as i64)
974 .collect::<Vec<_>>()
975 .into();
976
977 let array = PrimitiveArray::<i64>::new(
978 ArrowDataType::Int64,
979 values,
980 array.validity().cloned(),
981 );
982 primitive::nested_array_to_page::<i64, i64>(&array, options, type_, nested)
983 } else {
984 let size = decimal_length_from_precision(precision);
985
986 let statistics = if options.has_statistics() {
987 let stats = fixed_size_binary::build_statistics_decimal(
988 array,
989 type_.clone(),
990 size,
991 &options.statistics,
992 );
993 Some(stats)
994 } else {
995 None
996 };
997
998 let mut values = Vec::<u8>::with_capacity(size * array.len());
999 array.values().iter().for_each(|x| {
1000 let bytes = &x.to_be_bytes()[16 - size..];
1001 values.extend_from_slice(bytes)
1002 });
1003 let array = FixedSizeBinaryArray::new(
1004 ArrowDataType::FixedSizeBinary(size),
1005 values.into(),
1006 array.validity().cloned(),
1007 );
1008 fixed_size_binary::nested_array_to_page(&array, options, type_, nested, statistics)
1009 }
1010 },
1011 Decimal256(precision, _) => {
1012 let precision = *precision;
1013 let array = array
1014 .as_any()
1015 .downcast_ref::<PrimitiveArray<i256>>()
1016 .unwrap();
1017 if precision <= 9 {
1018 let values = array
1019 .values()
1020 .iter()
1021 .map(|x| x.0.as_i32())
1022 .collect::<Vec<_>>()
1023 .into();
1024
1025 let array = PrimitiveArray::<i32>::new(
1026 ArrowDataType::Int32,
1027 values,
1028 array.validity().cloned(),
1029 );
1030 primitive::nested_array_to_page::<i32, i32>(&array, options, type_, nested)
1031 } else if precision <= 18 {
1032 let values = array
1033 .values()
1034 .iter()
1035 .map(|x| x.0.as_i64())
1036 .collect::<Vec<_>>()
1037 .into();
1038
1039 let array = PrimitiveArray::<i64>::new(
1040 ArrowDataType::Int64,
1041 values,
1042 array.validity().cloned(),
1043 );
1044 primitive::nested_array_to_page::<i64, i64>(&array, options, type_, nested)
1045 } else if precision <= 38 {
1046 let size = decimal_length_from_precision(precision);
1047 let statistics = if options.has_statistics() {
1048 let stats = fixed_size_binary::build_statistics_decimal256_with_i128(
1049 array,
1050 type_.clone(),
1051 size,
1052 &options.statistics,
1053 );
1054 Some(stats)
1055 } else {
1056 None
1057 };
1058
1059 let mut values = Vec::<u8>::with_capacity(size * array.len());
1060 array.values().iter().for_each(|x| {
1061 let bytes = &x.0.low().to_be_bytes()[16 - size..];
1062 values.extend_from_slice(bytes)
1063 });
1064 let array = FixedSizeBinaryArray::new(
1065 ArrowDataType::FixedSizeBinary(size),
1066 values.into(),
1067 array.validity().cloned(),
1068 );
1069 fixed_size_binary::nested_array_to_page(&array, options, type_, nested, statistics)
1070 } else {
1071 let size = 32;
1072 let array = array
1073 .as_any()
1074 .downcast_ref::<PrimitiveArray<i256>>()
1075 .unwrap();
1076 let statistics = if options.has_statistics() {
1077 let stats = fixed_size_binary::build_statistics_decimal256(
1078 array,
1079 type_.clone(),
1080 size,
1081 &options.statistics,
1082 );
1083 Some(stats)
1084 } else {
1085 None
1086 };
1087 let mut values = Vec::<u8>::with_capacity(size * array.len());
1088 array.values().iter().for_each(|x| {
1089 let bytes = &x.to_be_bytes();
1090 values.extend_from_slice(bytes)
1091 });
1092 let array = FixedSizeBinaryArray::new(
1093 ArrowDataType::FixedSizeBinary(size),
1094 values.into(),
1095 array.validity().cloned(),
1096 );
1097
1098 fixed_size_binary::nested_array_to_page(&array, options, type_, nested, statistics)
1099 }
1100 },
1101 Int128 => {
1102 let array: &PrimitiveArray<i128> = array.as_any().downcast_ref().unwrap();
1103 let mut no_mm_options = options;
1105 no_mm_options.statistics.min_value = false;
1106 no_mm_options.statistics.max_value = false;
1107 let statistics = if no_mm_options.has_statistics() {
1108 let stats = fixed_size_binary::build_statistics_decimal(
1109 array,
1110 type_.clone(),
1111 16,
1112 &no_mm_options.statistics,
1113 );
1114 Some(stats)
1115 } else {
1116 None
1117 };
1118 let array = FixedSizeBinaryArray::new(
1119 ArrowDataType::FixedSizeBinary(16),
1120 array.values().clone().try_transmute().unwrap(),
1121 array.validity().cloned(),
1122 );
1123 fixed_size_binary::nested_array_to_page(
1124 &array,
1125 no_mm_options,
1126 type_,
1127 nested,
1128 statistics,
1129 )
1130 },
1131 UInt128 => {
1132 let array: &PrimitiveArray<u128> = array.as_any().downcast_ref().unwrap();
1133 let statistics = if options.has_statistics() {
1134 let stats = fixed_size_binary::build_statistics_decimal(
1135 array,
1136 type_.clone(),
1137 16,
1138 &options.statistics,
1139 );
1140 Some(stats)
1141 } else {
1142 None
1143 };
1144 let array = FixedSizeBinaryArray::new(
1145 ArrowDataType::FixedSizeBinary(16),
1146 array.values().clone().try_transmute().unwrap(),
1147 array.validity().cloned(),
1148 );
1149 fixed_size_binary::nested_array_to_page(&array, options, type_, nested, statistics)
1150 },
1151 other => polars_bail!(nyi = "Writing nested parquet pages for data type {other:?}"),
1152 }
1153 .map(Page::Data)
1154}
1155
1156fn get_encodings_recursive(dtype: &ArrowDataType, encodings: &mut Vec<Encoding>) {
1157 use arrow::datatypes::PhysicalType::*;
1158 match dtype.to_physical_type() {
1159 Null | Boolean | Primitive(_) | Binary | FixedSizeBinary | LargeBinary | Utf8
1160 | Dictionary(_) | LargeUtf8 | BinaryView | Utf8View => {
1161 encodings.push(get_primitive_dtype_encoding(dtype))
1162 },
1163 List | FixedSizeList | LargeList => {
1164 let a = dtype.to_storage();
1165 if let ArrowDataType::List(inner) = a {
1166 get_encodings_recursive(&inner.dtype, encodings)
1167 } else if let ArrowDataType::LargeList(inner) = a {
1168 get_encodings_recursive(&inner.dtype, encodings)
1169 } else if let ArrowDataType::FixedSizeList(inner, _) = a {
1170 get_encodings_recursive(&inner.dtype, encodings)
1171 } else {
1172 unreachable!()
1173 }
1174 },
1175 Struct => {
1176 if let ArrowDataType::Struct(fields) = dtype.to_storage() {
1177 if fields.is_empty() {
1178 encodings.push(Encoding::Rle)
1180 }
1181
1182 for field in fields {
1183 get_encodings_recursive(&field.dtype, encodings)
1184 }
1185 } else {
1186 unreachable!()
1187 }
1188 },
1189 Map => {
1190 if let ArrowDataType::Map(field, _) = dtype.to_storage() {
1191 if let ArrowDataType::Struct(fields) = field.dtype.to_storage() {
1192 for field in fields {
1193 get_encodings_recursive(&field.dtype, encodings)
1194 }
1195 } else {
1196 unreachable!()
1197 }
1198 } else {
1199 unreachable!()
1200 }
1201 },
1202 Union => todo!(),
1203 }
1204}
1205
1206pub fn get_dtype_encoding(dtype: &ArrowDataType) -> Vec<Encoding> {
1211 let mut encodings = vec![];
1212 get_encodings_recursive(dtype, &mut encodings);
1213 encodings
1214}
1215
1216fn get_primitive_dtype_encoding(dtype: &ArrowDataType) -> Encoding {
1217 match dtype.to_physical_type() {
1218 PhysicalType::Dictionary(_)
1219 | PhysicalType::LargeBinary
1220 | PhysicalType::LargeUtf8
1221 | PhysicalType::Utf8View
1222 | PhysicalType::BinaryView
1223 | PhysicalType::Primitive(_) => Encoding::RleDictionary,
1224 _ => Encoding::Plain,
1226 }
1227}