1use std::cmp::Ordering;
26use std::fmt::{Debug, Display};
27use std::sync::Arc;
28
29use arrow::array::*;
30use arrow::buffer::{BooleanBuffer, NullBuffer, OffsetBuffer};
31use arrow::datatypes::{ArrowNativeType, DataType, Field, FieldRef, Fields};
32use itertools::Itertools;
33use mz_ore::cast::CastFrom;
34use mz_ore::soft_assert_eq_no_log;
35use mz_proto::{IntoRustIfSome, ProtoType, RustType, TryFromProtoError};
36use prost::Message;
37
38#[allow(missing_docs)]
39mod proto {
40 include!(concat!(env!("OUT_DIR"), "/mz_persist_types.arrow.rs"));
41}
42use crate::arrow::proto::data_type;
43pub use proto::{DataType as ProtoDataType, ProtoArrayData};
44
45pub fn fields_for_type(data_type: &DataType) -> &[FieldRef] {
47 match data_type {
48 DataType::Struct(fields) => fields,
49 DataType::List(field) => std::slice::from_ref(field),
50 DataType::Map(field, _) => std::slice::from_ref(field),
51 DataType::Null
52 | DataType::Boolean
53 | DataType::Int8
54 | DataType::Int16
55 | DataType::Int32
56 | DataType::Int64
57 | DataType::UInt8
58 | DataType::UInt16
59 | DataType::UInt32
60 | DataType::UInt64
61 | DataType::Float16
62 | DataType::Float32
63 | DataType::Float64
64 | DataType::Timestamp(_, _)
65 | DataType::Date32
66 | DataType::Date64
67 | DataType::Time32(_)
68 | DataType::Time64(_)
69 | DataType::Duration(_)
70 | DataType::Interval(_)
71 | DataType::Binary
72 | DataType::FixedSizeBinary(_)
73 | DataType::LargeBinary
74 | DataType::BinaryView
75 | DataType::Utf8
76 | DataType::LargeUtf8
77 | DataType::Utf8View
78 | DataType::Decimal32(_, _)
79 | DataType::Decimal64(_, _)
80 | DataType::Decimal128(_, _)
81 | DataType::Decimal256(_, _) => &[],
82 DataType::ListView(_)
83 | DataType::FixedSizeList(_, _)
84 | DataType::LargeList(_)
85 | DataType::LargeListView(_)
86 | DataType::Union(_, _)
87 | DataType::Dictionary(_, _)
88 | DataType::RunEndEncoded(_, _) => unimplemented!("not supported"),
89 }
90}
91
92fn into_proto_with_type(data: &ArrayData, expected_type: Option<&DataType>) -> ProtoArrayData {
95 let data_type = match expected_type {
96 Some(expected) => {
97 soft_assert_eq_no_log!(
100 expected,
101 data.data_type(),
102 "actual type should match expected type"
103 );
104 None
105 }
106 None => Some(data.data_type().into_proto()),
107 };
108
109 ProtoArrayData {
110 data_type,
111 length: u64::cast_from(data.len()),
112 offset: u64::cast_from(data.offset()),
113 buffers: data.buffers().iter().map(|b| b.into_proto()).collect(),
114 children: data
115 .child_data()
116 .iter()
117 .zip_eq(fields_for_type(
118 expected_type.unwrap_or_else(|| data.data_type()),
119 ))
120 .map(|(child, expect)| into_proto_with_type(child, Some(expect.data_type())))
121 .collect(),
122 nulls: data.nulls().map(|n| n.inner().into_proto()),
123 }
124}
125
126fn from_proto_with_type(
129 proto: ProtoArrayData,
130 expected_type: Option<&DataType>,
131) -> Result<ArrayData, TryFromProtoError> {
132 let ProtoArrayData {
133 data_type,
134 length,
135 offset,
136 buffers,
137 children,
138 nulls,
139 } = proto;
140 let data_type: Option<DataType> = data_type.into_rust()?;
141 let data_type = match (data_type, expected_type) {
142 (Some(data_type), None) => data_type,
143 (Some(data_type), Some(expected_type)) => {
144 soft_assert_eq_no_log!(
147 data_type,
148 *expected_type,
149 "expected type should match actual type"
150 );
151 data_type
152 }
153 (None, Some(expected_type)) => expected_type.clone(),
154 (None, None) => {
155 return Err(TryFromProtoError::MissingField(
156 "ProtoArrayData::data_type".to_string(),
157 ));
158 }
159 };
160 let nulls = nulls
161 .map(|n| n.into_rust())
162 .transpose()?
163 .map(NullBuffer::new);
164
165 let mut builder = ArrayDataBuilder::new(data_type.clone())
166 .len(usize::cast_from(length))
167 .offset(usize::cast_from(offset))
168 .nulls(nulls);
169
170 for b in buffers.into_iter().map(|b| b.into_rust()) {
171 builder = builder.add_buffer(b?);
172 }
173 let fields = fields_for_type(&data_type);
178 if children.len() != fields.len() {
179 return Err(TryFromProtoError::RowConversionError(format!(
180 "ProtoArrayData for {:?} has {} children but its data type expects {}",
181 data_type,
182 children.len(),
183 fields.len(),
184 )));
185 }
186 for (c, field) in children.into_iter().zip_eq(fields) {
187 let c = from_proto_with_type(c, Some(field.data_type()))?;
188 builder = builder.add_child_data(c);
189 }
190
191 builder
193 .align_buffers(true)
194 .build()
195 .map_err(|e| TryFromProtoError::RowConversionError(e.to_string()))
196}
197
198impl RustType<ProtoArrayData> for arrow::array::ArrayData {
199 fn into_proto(&self) -> ProtoArrayData {
200 into_proto_with_type(self, None)
201 }
202
203 fn from_proto(proto: ProtoArrayData) -> Result<Self, TryFromProtoError> {
204 from_proto_with_type(proto, None)
205 }
206}
207
208impl RustType<proto::DataType> for arrow::datatypes::DataType {
209 fn into_proto(&self) -> proto::DataType {
210 let kind = match self {
211 DataType::Null => proto::data_type::Kind::Null(()),
212 DataType::Boolean => proto::data_type::Kind::Boolean(()),
213 DataType::UInt8 => proto::data_type::Kind::Uint8(()),
214 DataType::UInt16 => proto::data_type::Kind::Uint16(()),
215 DataType::UInt32 => proto::data_type::Kind::Uint32(()),
216 DataType::UInt64 => proto::data_type::Kind::Uint64(()),
217 DataType::Int8 => proto::data_type::Kind::Int8(()),
218 DataType::Int16 => proto::data_type::Kind::Int16(()),
219 DataType::Int32 => proto::data_type::Kind::Int32(()),
220 DataType::Int64 => proto::data_type::Kind::Int64(()),
221 DataType::Float32 => proto::data_type::Kind::Float32(()),
222 DataType::Float64 => proto::data_type::Kind::Float64(()),
223 DataType::Utf8 => proto::data_type::Kind::String(()),
224 DataType::Binary => proto::data_type::Kind::Binary(()),
225 DataType::FixedSizeBinary(size) => proto::data_type::Kind::FixedBinary(*size),
226 DataType::List(inner) => proto::data_type::Kind::List(Box::new(inner.into_proto())),
227 DataType::Map(inner, sorted) => {
228 let map = proto::data_type::Map {
229 value: Some(Box::new(inner.into_proto())),
230 sorted: *sorted,
231 };
232 proto::data_type::Kind::Map(Box::new(map))
233 }
234 DataType::Struct(children) => {
235 let children = children.into_iter().map(|f| f.into_proto()).collect();
236 proto::data_type::Kind::Struct(proto::data_type::Struct { children })
237 }
238 other => unimplemented!("unsupported data type {other:?}"),
239 };
240
241 proto::DataType { kind: Some(kind) }
242 }
243
244 fn from_proto(proto: proto::DataType) -> Result<Self, TryFromProtoError> {
245 let data_type = proto
246 .kind
247 .ok_or_else(|| TryFromProtoError::missing_field("kind"))?;
248 let data_type = match data_type {
249 proto::data_type::Kind::Null(()) => DataType::Null,
250 proto::data_type::Kind::Boolean(()) => DataType::Boolean,
251 proto::data_type::Kind::Uint8(()) => DataType::UInt8,
252 proto::data_type::Kind::Uint16(()) => DataType::UInt16,
253 proto::data_type::Kind::Uint32(()) => DataType::UInt32,
254 proto::data_type::Kind::Uint64(()) => DataType::UInt64,
255 proto::data_type::Kind::Int8(()) => DataType::Int8,
256 proto::data_type::Kind::Int16(()) => DataType::Int16,
257 proto::data_type::Kind::Int32(()) => DataType::Int32,
258 proto::data_type::Kind::Int64(()) => DataType::Int64,
259 proto::data_type::Kind::Float32(()) => DataType::Float32,
260 proto::data_type::Kind::Float64(()) => DataType::Float64,
261 proto::data_type::Kind::String(()) => DataType::Utf8,
262 proto::data_type::Kind::Binary(()) => DataType::Binary,
263 proto::data_type::Kind::FixedBinary(size) => DataType::FixedSizeBinary(size),
264 proto::data_type::Kind::List(inner) => DataType::List(Arc::new((*inner).into_rust()?)),
265 proto::data_type::Kind::Map(inner) => {
266 let value = inner
267 .value
268 .ok_or_else(|| TryFromProtoError::missing_field("map.value"))?;
269 DataType::Map(Arc::new((*value).into_rust()?), inner.sorted)
270 }
271 proto::data_type::Kind::Struct(inner) => {
272 let children: Vec<Field> = inner
273 .children
274 .into_iter()
275 .map(|c| c.into_rust())
276 .collect::<Result<_, _>>()?;
277 DataType::Struct(Fields::from(children))
278 }
279 };
280
281 Ok(data_type)
282 }
283}
284
285impl RustType<proto::Field> for arrow::datatypes::Field {
286 fn into_proto(&self) -> proto::Field {
287 proto::Field {
288 name: self.name().clone(),
289 nullable: self.is_nullable(),
290 data_type: Some(Box::new(self.data_type().into_proto())),
291 }
292 }
293
294 fn from_proto(proto: proto::Field) -> Result<Self, TryFromProtoError> {
295 let proto::Field {
296 name,
297 nullable,
298 data_type,
299 } = proto;
300 let data_type =
301 data_type.ok_or_else(|| TryFromProtoError::missing_field("field.data_type"))?;
302 let data_type = (*data_type).into_rust()?;
303
304 Ok(Field::new(name, data_type, nullable))
305 }
306}
307
308impl RustType<proto::Buffer> for arrow::buffer::Buffer {
309 fn into_proto(&self) -> proto::Buffer {
310 #[repr(transparent)]
312 struct BufferWrapper(arrow::buffer::Buffer);
313 impl AsRef<[u8]> for BufferWrapper {
314 fn as_ref(&self) -> &[u8] {
315 &*self.0
316 }
317 }
318 proto::Buffer {
319 data: bytes::Bytes::from_owner(BufferWrapper(self.clone())),
320 }
321 }
322
323 fn from_proto(proto: proto::Buffer) -> Result<Self, TryFromProtoError> {
324 Ok(arrow::buffer::Buffer::from(proto.data))
325 }
326}
327
328impl RustType<proto::BooleanBuffer> for arrow::buffer::BooleanBuffer {
329 fn into_proto(&self) -> proto::BooleanBuffer {
330 proto::BooleanBuffer {
331 buffer: Some(self.sliced().into_proto()),
332 length: u64::cast_from(self.len()),
333 }
334 }
335
336 fn from_proto(proto: proto::BooleanBuffer) -> Result<Self, TryFromProtoError> {
337 let proto::BooleanBuffer { buffer, length } = proto;
338 let buffer = buffer.into_rust_if_some("buffer")?;
339 Ok(BooleanBuffer::new(buffer, 0, usize::cast_from(length)))
340 }
341}
342
343#[derive(Clone)]
345pub enum ArrayOrd {
346 Null(NullArray),
348 Bool(BooleanArray),
350 Int8(Int8Array),
352 Int16(Int16Array),
354 Int32(Int32Array),
356 Int64(Int64Array),
358 UInt8(UInt8Array),
360 UInt16(UInt16Array),
362 UInt32(UInt32Array),
364 UInt64(UInt64Array),
366 Float32(Float32Array),
368 Float64(Float64Array),
370 String(StringArray),
372 Binary(BinaryArray),
374 FixedSizeBinary(FixedSizeBinaryArray),
376 List(Option<NullBuffer>, OffsetBuffer<i32>, Box<ArrayOrd>),
378 Struct(Option<NullBuffer>, Vec<ArrayOrd>),
380}
381
382impl ArrayOrd {
383 pub fn new(array: &dyn Array) -> Self {
385 match array.data_type() {
386 DataType::Null => ArrayOrd::Null(NullArray::from(array.to_data())),
387 DataType::Boolean => ArrayOrd::Bool(array.as_boolean().clone()),
388 DataType::Int8 => ArrayOrd::Int8(array.as_primitive().clone()),
389 DataType::Int16 => ArrayOrd::Int16(array.as_primitive().clone()),
390 DataType::Int32 => ArrayOrd::Int32(array.as_primitive().clone()),
391 DataType::Int64 => ArrayOrd::Int64(array.as_primitive().clone()),
392 DataType::UInt8 => ArrayOrd::UInt8(array.as_primitive().clone()),
393 DataType::UInt16 => ArrayOrd::UInt16(array.as_primitive().clone()),
394 DataType::UInt32 => ArrayOrd::UInt32(array.as_primitive().clone()),
395 DataType::UInt64 => ArrayOrd::UInt64(array.as_primitive().clone()),
396 DataType::Float32 => ArrayOrd::Float32(array.as_primitive().clone()),
397 DataType::Float64 => ArrayOrd::Float64(array.as_primitive().clone()),
398 DataType::Binary => ArrayOrd::Binary(array.as_binary().clone()),
399 DataType::Utf8 => ArrayOrd::String(array.as_string().clone()),
400 DataType::FixedSizeBinary(_) => {
401 ArrayOrd::FixedSizeBinary(array.as_fixed_size_binary().clone())
402 }
403 DataType::List(_) => {
404 let list_array = array.as_list();
405 ArrayOrd::List(
406 list_array.nulls().cloned(),
407 list_array.offsets().clone(),
408 Box::new(ArrayOrd::new(list_array.values())),
409 )
410 }
411 DataType::Struct(_) => {
412 let struct_array = array.as_struct();
413 let nulls = array.nulls().cloned();
414 let columns: Vec<_> = struct_array
415 .columns()
416 .iter()
417 .map(|a| ArrayOrd::new(a))
418 .collect();
419 ArrayOrd::Struct(nulls, columns)
420 }
421 data_type => unimplemented!("array type {data_type:?} not yet supported"),
422 }
423 }
424
425 pub fn goodbytes(&self) -> usize {
428 match self {
429 ArrayOrd::Null(_) => 0,
430 ArrayOrd::Bool(b) => b.len(),
433 ArrayOrd::Int8(a) => a.values().inner().len(),
434 ArrayOrd::Int16(a) => a.values().inner().len(),
435 ArrayOrd::Int32(a) => a.values().inner().len(),
436 ArrayOrd::Int64(a) => a.values().inner().len(),
437 ArrayOrd::UInt8(a) => a.values().inner().len(),
438 ArrayOrd::UInt16(a) => a.values().inner().len(),
439 ArrayOrd::UInt32(a) => a.values().inner().len(),
440 ArrayOrd::UInt64(a) => a.values().inner().len(),
441 ArrayOrd::Float32(a) => a.values().inner().len(),
442 ArrayOrd::Float64(a) => a.values().inner().len(),
443 ArrayOrd::String(a) => a.values().len(),
444 ArrayOrd::Binary(a) => a.values().len(),
445 ArrayOrd::FixedSizeBinary(a) => a.values().len(),
446 ArrayOrd::List(_, _, nested) => nested.goodbytes(),
447 ArrayOrd::Struct(_, nested) => nested.iter().map(|a| a.goodbytes()).sum(),
448 }
449 }
450
451 pub fn at(&self, idx: usize) -> ArrayIdx<'_> {
453 ArrayIdx { idx, array: self }
454 }
455}
456
457impl Debug for ArrayOrd {
458 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
459 struct DebugType<'a>(&'a ArrayOrd);
460
461 impl Debug for DebugType<'_> {
462 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
463 match self.0 {
464 ArrayOrd::Null(_) => write!(f, "Null"),
465 ArrayOrd::Bool(_) => write!(f, "Bool"),
466 ArrayOrd::Int8(_) => write!(f, "Int8"),
467 ArrayOrd::Int16(_) => write!(f, "Int16"),
468 ArrayOrd::Int32(_) => write!(f, "Int32"),
469 ArrayOrd::Int64(_) => write!(f, "Int64"),
470 ArrayOrd::UInt8(_) => write!(f, "UInt8"),
471 ArrayOrd::UInt16(_) => write!(f, "UInt16"),
472 ArrayOrd::UInt32(_) => write!(f, "UInt32"),
473 ArrayOrd::UInt64(_) => write!(f, "UInt64"),
474 ArrayOrd::Float32(_) => write!(f, "Float32"),
475 ArrayOrd::Float64(_) => write!(f, "Float64"),
476 ArrayOrd::String(_) => write!(f, "String"),
477 ArrayOrd::Binary(_) => write!(f, "Binary"),
478 ArrayOrd::FixedSizeBinary(a) => f
479 .debug_tuple("FixedSizeBinary")
480 .field(&a.value_length())
481 .finish(),
482 ArrayOrd::List(_, _, nested) => f.debug_tuple("List").field(&*nested).finish(),
483 ArrayOrd::Struct(_, fields) => {
484 let mut tuple = f.debug_tuple("Struct");
485 for field in fields {
486 tuple.field(field);
487 }
488 tuple.finish()
489 }
490 }
491 }
492 }
493
494 f.debug_struct("ArrayOrd")
495 .field("type", &DebugType(self))
496 .field("goodbytes", &self.goodbytes())
497 .finish()
498 }
499}
500
501#[derive(Clone, Copy, Debug)]
509pub struct ArrayIdx<'a> {
510 pub idx: usize,
512 pub array: &'a ArrayOrd,
514}
515
516impl Display for ArrayIdx<'_> {
517 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
518 match self.array {
519 ArrayOrd::Null(_) => write!(f, "null"),
520 ArrayOrd::Bool(a) => write!(f, "{}", a.value(self.idx)),
521 ArrayOrd::Int8(a) => write!(f, "{}", a.value(self.idx)),
522 ArrayOrd::Int16(a) => write!(f, "{}", a.value(self.idx)),
523 ArrayOrd::Int32(a) => write!(f, "{}", a.value(self.idx)),
524 ArrayOrd::Int64(a) => write!(f, "{}", a.value(self.idx)),
525 ArrayOrd::UInt8(a) => write!(f, "{}", a.value(self.idx)),
526 ArrayOrd::UInt16(a) => write!(f, "{}", a.value(self.idx)),
527 ArrayOrd::UInt32(a) => write!(f, "{}", a.value(self.idx)),
528 ArrayOrd::UInt64(a) => write!(f, "{}", a.value(self.idx)),
529 ArrayOrd::Float32(a) => write!(f, "{}", a.value(self.idx)),
530 ArrayOrd::Float64(a) => write!(f, "{}", a.value(self.idx)),
531 ArrayOrd::String(a) => write!(f, "{}", a.value(self.idx)),
532 ArrayOrd::Binary(a) => {
533 for byte in a.value(self.idx) {
534 write!(f, "{:02x}", byte)?;
535 }
536 Ok(())
537 }
538 ArrayOrd::FixedSizeBinary(a) => {
539 for byte in a.value(self.idx) {
540 write!(f, "{:02x}", byte)?;
541 }
542 Ok(())
543 }
544 ArrayOrd::List(_, offsets, nested) => {
545 write!(
546 f,
547 "[{}]",
548 mz_ore::str::separated(", ", list_range(offsets, nested, self.idx))
549 )
550 }
551 ArrayOrd::Struct(_, nested) => write!(
552 f,
553 "{{{}}}",
554 mz_ore::str::separated(", ", nested.iter().map(|f| f.at(self.idx)))
555 ),
556 }
557 }
558}
559
560#[inline]
561fn list_range<'a>(
562 offsets: &OffsetBuffer<i32>,
563 values: &'a ArrayOrd,
564 idx: usize,
565) -> impl Iterator<Item = ArrayIdx<'a>> + Clone {
566 let offsets = offsets.inner();
567 let from = offsets[idx].as_usize();
568 let to = offsets[idx + 1].as_usize();
569 (from..to).map(|i| values.at(i))
570}
571
572impl<'a> ArrayIdx<'a> {
573 pub fn goodbytes(&self) -> usize {
576 match self.array {
577 ArrayOrd::Null(_) => 0,
578 ArrayOrd::Bool(_) => size_of::<bool>(),
579 ArrayOrd::Int8(_) => size_of::<i8>(),
580 ArrayOrd::Int16(_) => size_of::<i16>(),
581 ArrayOrd::Int32(_) => size_of::<i32>(),
582 ArrayOrd::Int64(_) => size_of::<i64>(),
583 ArrayOrd::UInt8(_) => size_of::<u8>(),
584 ArrayOrd::UInt16(_) => size_of::<u16>(),
585 ArrayOrd::UInt32(_) => size_of::<u32>(),
586 ArrayOrd::UInt64(_) => size_of::<u64>(),
587 ArrayOrd::Float32(_) => size_of::<f32>(),
588 ArrayOrd::Float64(_) => size_of::<f64>(),
589 ArrayOrd::String(a) => a.value(self.idx).len(),
590 ArrayOrd::Binary(a) => a.value(self.idx).len(),
591 ArrayOrd::FixedSizeBinary(a) => a.value_length().as_usize(),
592 ArrayOrd::List(_, offsets, nested) => {
593 list_range(offsets, nested, self.idx)
595 .map(|a| a.goodbytes())
596 .sum()
597 }
598 ArrayOrd::Struct(_, nested) => nested.iter().map(|a| a.at(self.idx).goodbytes()).sum(),
599 }
600 }
601}
602
603impl<'a> Ord for ArrayIdx<'a> {
604 fn cmp(&self, other: &Self) -> Ordering {
605 #[inline]
606 fn is_null(buffer: &Option<NullBuffer>, idx: usize) -> bool {
607 buffer.as_ref().map_or(false, |b| b.is_null(idx))
608 }
609 #[inline]
610 fn cmp<A: ArrayAccessor>(
611 left: A,
612 left_idx: usize,
613 right: A,
614 right_idx: usize,
615 cmp: fn(&A::Item, &A::Item) -> Ordering,
616 ) -> Ordering {
617 match (left.is_null(left_idx), right.is_null(right_idx)) {
619 (false, true) => Ordering::Less,
620 (true, true) => Ordering::Equal,
621 (true, false) => Ordering::Greater,
622 (false, false) => cmp(&left.value(left_idx), &right.value(right_idx)),
623 }
624 }
625 match (&self.array, &other.array) {
626 (ArrayOrd::Null(s), ArrayOrd::Null(o)) => {
627 debug_assert!(
628 self.idx < s.len() && other.idx < o.len(),
629 "null array indices in bounds"
630 );
631 Ordering::Equal
632 }
633 (ArrayOrd::Bool(s), ArrayOrd::Bool(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
635 (ArrayOrd::Int8(s), ArrayOrd::Int8(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
636 (ArrayOrd::Int16(s), ArrayOrd::Int16(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
637 (ArrayOrd::Int32(s), ArrayOrd::Int32(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
638 (ArrayOrd::Int64(s), ArrayOrd::Int64(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
639 (ArrayOrd::UInt8(s), ArrayOrd::UInt8(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
640 (ArrayOrd::UInt16(s), ArrayOrd::UInt16(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
641 (ArrayOrd::UInt32(s), ArrayOrd::UInt32(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
642 (ArrayOrd::UInt64(s), ArrayOrd::UInt64(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
643 (ArrayOrd::Float32(s), ArrayOrd::Float32(o)) => {
644 cmp(s, self.idx, o, other.idx, f32::total_cmp)
645 }
646 (ArrayOrd::Float64(s), ArrayOrd::Float64(o)) => {
647 cmp(s, self.idx, o, other.idx, f64::total_cmp)
648 }
649 (ArrayOrd::String(s), ArrayOrd::String(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
650 (ArrayOrd::Binary(s), ArrayOrd::Binary(o)) => cmp(s, self.idx, o, other.idx, Ord::cmp),
651 (ArrayOrd::FixedSizeBinary(s), ArrayOrd::FixedSizeBinary(o)) => {
652 cmp(s, self.idx, o, other.idx, Ord::cmp)
653 }
654 (
657 ArrayOrd::List(s_nulls, s_offset, s_values),
658 ArrayOrd::List(o_nulls, o_offset, o_values),
659 ) => match (is_null(s_nulls, self.idx), is_null(o_nulls, other.idx)) {
660 (false, true) => Ordering::Less,
661 (true, true) => Ordering::Equal,
662 (true, false) => Ordering::Greater,
663 (false, false) => list_range(s_offset, s_values, self.idx)
664 .cmp(list_range(o_offset, o_values, other.idx)),
665 },
666 (ArrayOrd::Struct(s_nulls, s_cols), ArrayOrd::Struct(o_nulls, o_cols)) => {
669 match (is_null(s_nulls, self.idx), is_null(o_nulls, other.idx)) {
670 (false, true) => Ordering::Less,
671 (true, true) => Ordering::Equal,
672 (true, false) => Ordering::Greater,
673 (false, false) => {
674 let s = s_cols.iter().map(|array| array.at(self.idx));
675 let o = o_cols.iter().map(|array| array.at(other.idx));
676 s.cmp(o)
677 }
678 }
679 }
680 (a, b) => panic!("array types did not match! {a:?} vs. {b:?}",),
681 }
682 }
683}
684
685impl<'a> PartialOrd for ArrayIdx<'a> {
686 fn partial_cmp(&self, other: &ArrayIdx) -> Option<Ordering> {
687 Some(self.cmp(other))
688 }
689}
690
691impl<'a> PartialEq for ArrayIdx<'a> {
692 fn eq(&self, other: &ArrayIdx) -> bool {
693 self.cmp(other) == Ordering::Equal
694 }
695}
696
697impl<'a> Eq for ArrayIdx<'a> {}
698
699#[derive(Debug, Clone)]
701pub struct ArrayBound {
702 raw: ArrayRef,
703 ord: ArrayOrd,
704 index: usize,
705}
706
707impl PartialEq for ArrayBound {
708 fn eq(&self, other: &Self) -> bool {
709 self.get().eq(&other.get())
710 }
711}
712
713impl Eq for ArrayBound {}
714
715impl ArrayBound {
716 pub fn new(array: ArrayRef, index: usize) -> Self {
718 Self {
719 ord: ArrayOrd::new(array.as_ref()),
720 raw: array,
721 index,
722 }
723 }
724
725 pub fn get(&self) -> ArrayIdx<'_> {
727 self.ord.at(self.index)
728 }
729
730 pub fn to_proto_lower(&self, max_len: usize) -> Option<ProtoArrayData> {
734 let indices = UInt64Array::from_value(u64::usize_as(self.index), 1);
737 let taken = arrow::compute::take(self.raw.as_ref(), &indices, None).ok()?;
738 let array_data = taken.into_data();
739
740 let mut proto = array_data.into_proto();
741 let original_len = proto.encoded_len();
742 if original_len <= max_len {
743 return Some(proto);
744 }
745
746 let mut data_type = proto.data_type.take()?;
747 maybe_trim_proto(&mut data_type, &mut proto, max_len);
748 proto.data_type = Some(data_type);
749
750 if cfg!(debug_assertions) {
751 let array: ArrayData = proto
752 .clone()
753 .into_rust()
754 .expect("trimmed array data can still be decoded");
755 assert_eq!(array.len(), 1);
756 let new_bound = Self::new(make_array(array), 0);
757 assert!(
758 new_bound.get() <= self.get(),
759 "trimmed bound should be comparable to and no larger than the original data"
760 )
761 }
762
763 if proto.encoded_len() <= max_len {
764 Some(proto)
765 } else {
766 None
767 }
768 }
769}
770
771fn maybe_trim_proto(data_type: &mut proto::DataType, body: &mut ProtoArrayData, max_len: usize) {
779 assert!(body.data_type.is_none(), "expected separate data type");
780 let encoded_len = data_type.encoded_len() + body.encoded_len();
782 match &mut data_type.kind {
783 Some(data_type::Kind::Struct(data_type::Struct { children: fields })) => {
784 let mut struct_len = encoded_len;
786 while struct_len > max_len {
787 let Some(mut child) = body.children.pop() else {
788 break;
789 };
790 let Some(mut field) = fields.pop() else { break };
791
792 struct_len -= field.encoded_len() + child.encoded_len();
793 if let Some(remaining_len) = max_len.checked_sub(struct_len) {
794 let Some(field_type) = field.data_type.as_mut() else {
797 break;
798 };
799 maybe_trim_proto(field_type, &mut child, remaining_len);
800 if field.encoded_len() + child.encoded_len() <= remaining_len {
801 fields.push(field);
802 body.children.push(child);
803 }
804 break;
805 }
806 }
807 }
808 _ => {}
809 };
810}
811
812#[cfg(test)]
813mod tests {
814 use crate::arrow::{ArrayBound, ArrayOrd};
815 use arrow::array::{
816 ArrayRef, AsArray, BooleanArray, StringArray, StructArray, UInt64Array, make_array,
817 };
818 use arrow::datatypes::{DataType, Field, Fields};
819 use mz_ore::assert_none;
820 use mz_proto::ProtoType;
821 use std::sync::Arc;
822
823 #[mz_ore::test]
824 fn from_proto_child_count_mismatch_is_error() {
825 use prost::Message;
829 let bytes: &[u8] = &[
830 0x0a, 0x0a, 0x18, 0x32, 0x22, 0x00, 0x18, 0x0a, 0x18, 0x32, 0x22, 0x00, 0x2a, 0x00,
831 0x2a, 0x00, 0xe0, 0x32, 0x24,
832 ];
833 let proto =
834 crate::arrow::ProtoArrayData::decode(bytes).expect("crash input decodes as a proto");
835 let result: Result<arrow::array::ArrayData, _> = proto.into_rust();
836 assert!(
837 result.is_err(),
838 "child-count mismatch must be a decode error"
839 );
840 }
841
842 #[mz_ore::test]
843 fn trim_proto() {
844 let nested_fields: Fields = vec![Field::new("a", DataType::UInt64, true)].into();
845 let array: ArrayRef = Arc::new(StructArray::new(
846 vec![
847 Field::new("a", DataType::UInt64, true),
848 Field::new("b", DataType::Utf8, true),
849 Field::new_struct("c", nested_fields.clone(), true),
850 ]
851 .into(),
852 vec![
853 Arc::new(UInt64Array::from_iter_values([1])),
854 Arc::new(StringArray::from_iter_values(["large".repeat(50)])),
855 Arc::new(StructArray::new_null(nested_fields, 1)),
856 ],
857 None,
858 ));
859 let bound = ArrayBound::new(array, 0);
860
861 assert_none!(bound.to_proto_lower(0));
862 assert_none!(bound.to_proto_lower(1));
863
864 let proto = bound
865 .to_proto_lower(100)
866 .expect("can fit something in less than 100 bytes");
867 let array = make_array(proto.into_rust().expect("valid proto"));
868 assert_eq!(
869 array.as_struct().column_names().as_slice(),
870 &["a"],
871 "only the first column should fit"
872 );
873
874 let proto = bound
875 .to_proto_lower(1000)
876 .expect("can fit everything in less than 1000 bytes");
877 let array = make_array(proto.into_rust().expect("valid proto"));
878 assert_eq!(
879 array.as_struct().column_names().as_slice(),
880 &["a", "b", "c"],
881 "all columns should fit"
882 )
883 }
884
885 #[mz_ore::test]
886 fn struct_ord() {
887 let prefix = StructArray::new(
888 vec![Field::new("a", DataType::UInt64, true)].into(),
889 vec![Arc::new(UInt64Array::from_iter_values([1, 3, 5]))],
890 None,
891 );
892 let full = StructArray::new(
893 vec![
894 Field::new("a", DataType::UInt64, true),
895 Field::new("b", DataType::Utf8, true),
896 ]
897 .into(),
898 vec![
899 Arc::new(UInt64Array::from_iter_values([2, 3, 4])),
900 Arc::new(StringArray::from_iter_values(["a", "b", "c"])),
901 ],
902 None,
903 );
904 let prefix_ord = ArrayOrd::new(&prefix);
905 let full_ord = ArrayOrd::new(&full);
906
907 assert!(prefix_ord.at(0) < full_ord.at(0), "(1) < (2, 'a')");
910 assert!(prefix_ord.at(1) < full_ord.at(1), "(3) < (3, 'b')");
911 assert!(prefix_ord.at(2) > full_ord.at(2), "(5) < (4, 'c')");
912 }
913
914 #[mz_ore::test]
915 #[should_panic(expected = "array types did not match")]
916 fn struct_ord_incompat() {
917 let string = StructArray::new(
920 vec![
921 Field::new("a", DataType::UInt64, true),
922 Field::new("b", DataType::Utf8, true),
923 ]
924 .into(),
925 vec![
926 Arc::new(UInt64Array::from_iter_values([1])),
927 Arc::new(StringArray::from_iter_values(["a"])),
928 ],
929 None,
930 );
931 let boolean = StructArray::new(
932 vec![
933 Field::new("a", DataType::UInt64, true),
934 Field::new("b", DataType::Boolean, true),
935 ]
936 .into(),
937 vec![
938 Arc::new(UInt64Array::from_iter_values([1])),
939 Arc::new(BooleanArray::from_iter([Some(true)])),
940 ],
941 None,
942 );
943 let string_ord = ArrayOrd::new(&string);
944 let bool_ord = ArrayOrd::new(&boolean);
945
946 assert!(string_ord.at(0) < bool_ord.at(0));
948 }
949}