1mod builder;
15mod pruning;
16mod reader;
17mod schema;
18pub mod writer;
19pub mod zone_map;
20
21use std::num::NonZeroUsize;
22use std::sync::Arc;
23
24pub(crate) use builder::AggregateStatsAccumulator;
25pub(crate) use builder::aggregate_partials;
26use prost::Message;
27pub use schema::MAX_IS_TRUNCATED;
28pub use schema::MIN_IS_TRUNCATED;
29use vortex_array::DeserializeMetadata;
30use vortex_array::SerializeMetadata;
31use vortex_array::aggregate_fn::AggregateFnRef;
32use vortex_array::dtype::DType;
33use vortex_array::dtype::TryFromBytes;
34use vortex_array::expr::stats::Stat;
35use vortex_array::stats::as_stat_bitset_bytes;
36use vortex_array::stats::stats_from_bitset_bytes;
37use vortex_error::VortexExpect;
38use vortex_error::VortexResult;
39use vortex_error::vortex_bail;
40use vortex_error::vortex_ensure;
41use vortex_error::vortex_ensure_eq;
42use vortex_error::vortex_panic;
43use vortex_session::VortexSession;
44use vortex_session::registry::CachedId;
45
46use crate::Layout;
47use crate::LayoutChildType;
48use crate::LayoutDeserializeArgs;
49use crate::LayoutId;
50use crate::LayoutParts;
51use crate::LayoutReaderContext;
52use crate::LayoutReaderRef;
53use crate::LayoutRef;
54use crate::VTable;
55use crate::children::OwnedLayoutChildren;
56use crate::layouts::zoned::reader::ZonedReader;
57use crate::layouts::zoned::schema::AggregateSpecProto;
58use crate::layouts::zoned::schema::aggregate_specs_from_fns;
59use crate::layouts::zoned::schema::aggregate_stats_table_dtype;
60use crate::layouts::zoned::schema::legacy_stats_table_dtype;
61use crate::layouts::zoned::schema::try_aggregate_fns_from_specs;
62use crate::segments::SegmentSource;
63
64#[derive(Clone, Debug)]
66pub struct Zoned;
67
68#[derive(Clone, Debug)]
70pub struct LegacyStats;
71
72pub use LegacyStats as LegacyStatsLayoutEncoding;
74pub use Zoned as ZonedLayoutEncoding;
75
76#[derive(Clone, Debug)]
78pub struct ZonedData {
79 zone_len: usize,
80 zone_map_schema: ZoneMapSchema,
81 stats_table_dtype: DType,
82}
83
84pub type ZonedLayout = Layout<Zoned>;
86pub type LegacyStatsLayout = Layout<LegacyStats>;
88
89impl VTable for Zoned {
90 type LayoutData = ZonedData;
91 type Metadata = ZonedMetadata;
92
93 fn id(&self) -> LayoutId {
94 static ID: CachedId = CachedId::new("vortex.zoned");
95 *ID
96 }
97
98 fn metadata(layout: &Layout<Self>) -> Self::Metadata {
99 ZonedMetadata {
100 zone_len: u32::try_from(layout.zone_len).vortex_expect("Invalid zone length"),
101 aggregate_specs: match &layout.zone_map_schema {
102 ZoneMapSchema::AggregateFns(aggregate_fns) => {
103 aggregate_specs_from_fns(aggregate_fns).vortex_expect(
104 "aggregate functions should be validated as serializable during build",
105 )
106 }
107 ZoneMapSchema::LegacyStats(_) => {
108 vortex_panic!("Cannot serialize legacy stats schema as vortex.zoned")
109 }
110 },
111 }
112 }
113
114 fn deserialize(
115 &self,
116 args: &LayoutDeserializeArgs<'_>,
117 metadata: &ZonedMetadata,
118 ) -> VortexResult<Self::LayoutData> {
119 vortex_ensure_eq!(
120 args.children.nchildren(),
121 2,
122 "ZonedLayout expects exactly 2 children (data, zones)"
123 );
124 vortex_ensure_eq!(args.children.child_row_count(0), args.row_count);
125 let Some(aggregate_fns) =
126 try_aggregate_fns_from_specs(&metadata.aggregate_specs, args.session)?
127 else {
128 args.children.child(0, args.dtype)?;
129 return Ok(ZonedData {
130 zone_len: 0,
131 zone_map_schema: ZoneMapSchema::AggregateFns(Arc::new([])),
132 stats_table_dtype: aggregate_stats_table_dtype(args.dtype, &[]),
133 });
134 };
135 aggregate_specs_from_fns(&aggregate_fns)?;
136 let stats_table_dtype = aggregate_stats_table_dtype(args.dtype, &aggregate_fns);
137 args.children.child(0, args.dtype)?;
138 args.children.child(1, &stats_table_dtype)?;
139 Ok(ZonedData {
140 zone_len: metadata.zone_len as usize,
141 zone_map_schema: ZoneMapSchema::AggregateFns(aggregate_fns),
142 stats_table_dtype,
143 })
144 }
145
146 fn child_dtype(layout: &Layout<Self>, idx: usize) -> VortexResult<DType> {
147 match idx {
148 0 => Ok(layout.dtype().clone()),
149 1 => Ok(layout.stats_table_dtype.clone()),
150 _ => vortex_bail!("Invalid child index: {idx}"),
151 }
152 }
153
154 fn child_type(_layout: &Layout<Self>, idx: usize) -> LayoutChildType {
155 match idx {
156 0 => LayoutChildType::Transparent("data".into()),
157 1 => LayoutChildType::Auxiliary("zones".into()),
158 _ => vortex_panic!("Invalid child index: {}", idx),
159 }
160 }
161
162 fn new_reader(
163 layout: &Layout<Self>,
164 name: Arc<str>,
165 segment_source: Arc<dyn SegmentSource>,
166 session: &VortexSession,
167 ctx: &LayoutReaderContext,
168 ) -> VortexResult<LayoutReaderRef> {
169 layout.zoned_reader(name, segment_source, session, ctx)
170 }
171}
172
173impl VTable for LegacyStats {
174 type LayoutData = ZonedData;
175 type Metadata = LegacyStatsMetadata;
176
177 fn id(&self) -> LayoutId {
178 static ID: CachedId = CachedId::new("vortex.stats");
179 *ID
180 }
181
182 fn metadata(layout: &Layout<Self>) -> Self::Metadata {
183 LegacyStatsMetadata {
184 zone_len: u32::try_from(layout.zone_len).vortex_expect("Invalid zone length"),
185 zone_map_schema: layout.zone_map_schema.clone(),
186 }
187 }
188
189 fn deserialize(
190 &self,
191 args: &LayoutDeserializeArgs<'_>,
192 metadata: &LegacyStatsMetadata,
193 ) -> VortexResult<Self::LayoutData> {
194 vortex_ensure_eq!(
195 args.children.nchildren(),
196 2,
197 "LegacyStatsLayout expects exactly 2 children (data, zones)"
198 );
199 let stats_table_dtype = match &metadata.zone_map_schema {
200 ZoneMapSchema::LegacyStats(stats) => legacy_stats_table_dtype(args.dtype, stats),
201 ZoneMapSchema::AggregateFns(aggregate_fns) => {
202 aggregate_stats_table_dtype(args.dtype, aggregate_fns)
203 }
204 };
205 args.children.child(0, args.dtype)?;
206 args.children.child(1, &stats_table_dtype)?;
207 Ok(ZonedData {
208 zone_len: metadata.zone_len as usize,
209 zone_map_schema: metadata.zone_map_schema.clone(),
210 stats_table_dtype,
211 })
212 }
213
214 fn child_dtype(layout: &Layout<Self>, idx: usize) -> VortexResult<DType> {
215 match idx {
216 0 => Ok(layout.dtype().clone()),
217 1 => Ok(layout.stats_table_dtype.clone()),
218 _ => vortex_bail!("Invalid child index: {idx}"),
219 }
220 }
221
222 fn child_type(_layout: &Layout<Self>, idx: usize) -> LayoutChildType {
223 match idx {
224 0 => LayoutChildType::Transparent("data".into()),
225 1 => LayoutChildType::Auxiliary("zones".into()),
226 _ => vortex_panic!("Invalid child index: {idx}"),
227 }
228 }
229
230 fn new_reader(
231 layout: &Layout<Self>,
232 name: Arc<str>,
233 segment_source: Arc<dyn SegmentSource>,
234 session: &VortexSession,
235 ctx: &LayoutReaderContext,
236 ) -> VortexResult<LayoutReaderRef> {
237 layout.zoned_reader(name, segment_source, session, ctx)
238 }
239}
240
241impl LegacyStatsLayout {
242 fn zoned_reader(
243 &self,
244 name: Arc<str>,
245 segment_source: Arc<dyn SegmentSource>,
246 session: &VortexSession,
247 ctx: &LayoutReaderContext,
248 ) -> VortexResult<LayoutReaderRef> {
249 if self.zone_len == 0 {
250 return self
251 .child(0)?
252 .new_reader(name, segment_source, session, ctx);
253 }
254
255 Ok(Arc::new(ZonedReader::try_new(
256 self.clone(),
257 self.nzones(),
258 name,
259 segment_source,
260 session.clone(),
261 ctx.clone(),
262 )?))
263 }
264
265 pub fn nzones(&self) -> usize {
266 usize::try_from(self.children().child_row_count(1))
267 .vortex_expect("Invalid number of zones, cannot handle more than usize zones")
268 }
269
270 pub fn present_aggregates(&self) -> Arc<[String]> {
272 present_aggregates(&self.zone_map_schema)
273 }
274}
275
276#[derive(Clone, Debug, PartialEq, Eq)]
277pub(crate) enum ZoneMapSchema {
278 LegacyStats(Arc<[Stat]>),
279 AggregateFns(Arc<[AggregateFnRef]>),
280}
281
282impl ZonedLayout {
283 pub fn try_new(
286 data: LayoutRef,
287 zones: LayoutRef,
288 zone_len: NonZeroUsize,
289 aggregate_fns: Arc<[AggregateFnRef]>,
290 ) -> VortexResult<Self> {
291 let expected_dtype = aggregate_stats_table_dtype(data.dtype(), &aggregate_fns);
292 if zones.dtype() != &expected_dtype {
293 vortex_bail!("Invalid zone map layout: zones dtype does not match expected dtype");
294 }
295
296 aggregate_specs_from_fns(&aggregate_fns)?;
298
299 let dtype = data.dtype().clone();
300 let row_count = data.row_count();
301 Ok(LayoutParts::new(
302 Zoned,
303 dtype,
304 row_count,
305 Vec::new(),
306 OwnedLayoutChildren::layout_children(vec![data, zones]),
307 ZonedData {
308 zone_len: zone_len.get(),
309 zone_map_schema: ZoneMapSchema::AggregateFns(aggregate_fns),
310 stats_table_dtype: expected_dtype,
311 },
312 )
313 .into_typed())
314 }
315
316 pub fn zone_len(&self) -> usize {
317 self.zone_len
318 }
319
320 pub fn present_aggregates(&self) -> Arc<[String]> {
322 present_aggregates(&self.zone_map_schema)
323 }
324
325 fn zoned_reader(
330 &self,
331 name: Arc<str>,
332 segment_source: Arc<dyn SegmentSource>,
333 session: &VortexSession,
334 ctx: &LayoutReaderContext,
335 ) -> VortexResult<LayoutReaderRef> {
336 if self.zone_len == 0 {
337 return self
338 .child(0)?
339 .new_reader(name, segment_source, session, ctx);
340 }
341
342 Ok(Arc::new(ZonedReader::try_new(
343 self.clone(),
344 self.nzones(),
345 name,
346 segment_source,
347 session.clone(),
348 ctx.clone(),
349 )?))
350 }
351
352 pub fn nzones(&self) -> usize {
353 usize::try_from(self.children().child_row_count(1))
354 .vortex_expect("Invalid number of zones, cannot handle more than usize zones")
355 }
356}
357
358impl ZonedData {
359 fn aggregate_fns(&self) -> Arc<[AggregateFnRef]> {
360 match &self.zone_map_schema {
361 ZoneMapSchema::LegacyStats(stats) => stats
362 .iter()
363 .filter_map(Stat::aggregate_fn)
364 .collect::<Vec<_>>()
365 .into(),
366 ZoneMapSchema::AggregateFns(aggregate_fns) => Arc::clone(aggregate_fns),
367 }
368 }
369}
370
371fn present_aggregates(schema: &ZoneMapSchema) -> Arc<[String]> {
372 match schema {
373 ZoneMapSchema::LegacyStats(stats) => stats
374 .iter()
375 .filter_map(Stat::aggregate_fn)
376 .map(|aggregate_fn| aggregate_fn.to_string())
377 .collect::<Vec<_>>()
378 .into(),
379 ZoneMapSchema::AggregateFns(aggregate_fns) => aggregate_fns
380 .iter()
381 .map(ToString::to_string)
382 .collect::<Vec<_>>()
383 .into(),
384 }
385}
386
387#[derive(Debug, PartialEq, Eq, Clone)]
392pub struct ZonedMetadata {
393 pub(super) zone_len: u32,
394 pub(super) aggregate_specs: Arc<[AggregateSpecProto]>,
395}
396
397#[derive(Debug, PartialEq, Eq, Clone)]
399pub struct LegacyStatsMetadata {
400 pub(super) zone_len: u32,
401 pub(crate) zone_map_schema: ZoneMapSchema,
402}
403
404const ZONED_METADATA_PROTO_VERSION: u8 = 1;
405
406#[derive(Clone, PartialEq, Message)]
407struct ZonedMetadataProto {
408 #[prost(uint32, tag = "1")]
409 zone_len: u32,
410 #[prost(message, repeated, tag = "2")]
411 aggregate_specs: Vec<AggregateSpecProto>,
412}
413
414impl DeserializeMetadata for ZonedMetadata {
415 type Output = Self;
416
417 fn deserialize(metadata: &[u8]) -> VortexResult<Self::Output> {
418 let Some((&version, proto_bytes)) = metadata.split_first() else {
419 vortex_bail!("Zoned metadata missing protobuf version");
420 };
421
422 vortex_ensure!(
423 version == ZONED_METADATA_PROTO_VERSION,
424 "Unsupported zoned metadata version: {}",
425 version
426 );
427 vortex_ensure!(!proto_bytes.is_empty(), "Zoned metadata missing protobuf");
428
429 let proto = ZonedMetadataProto::decode(proto_bytes)?;
430 Ok(Self {
431 zone_len: proto.zone_len,
432 aggregate_specs: proto.aggregate_specs.into(),
433 })
434 }
435}
436
437impl SerializeMetadata for ZonedMetadata {
438 fn serialize(self) -> Vec<u8> {
439 let proto = ZonedMetadataProto {
440 zone_len: self.zone_len,
441 aggregate_specs: self.aggregate_specs.to_vec(),
442 };
443 let mut metadata = vec![ZONED_METADATA_PROTO_VERSION];
444 metadata.extend(proto.encode_to_vec());
445 metadata
446 }
447}
448
449impl DeserializeMetadata for LegacyStatsMetadata {
450 type Output = Self;
451
452 fn deserialize(metadata: &[u8]) -> VortexResult<Self::Output> {
453 vortex_ensure!(
454 metadata.len() >= 4,
455 "Legacy zoned metadata must contain at least 4 bytes for zone length, got {}",
456 metadata.len()
457 );
458
459 let zone_len = u32::try_from_le_bytes(&metadata[0..4])?;
463 let present_stats: Arc<[Stat]> = stats_from_bitset_bytes(&metadata[4..]).into();
464
465 Ok(Self {
466 zone_len,
467 zone_map_schema: ZoneMapSchema::LegacyStats(present_stats),
468 })
469 }
470}
471
472impl SerializeMetadata for LegacyStatsMetadata {
473 fn serialize(self) -> Vec<u8> {
474 match self.zone_map_schema {
475 ZoneMapSchema::LegacyStats(stats) => {
476 let mut metadata = self.zone_len.to_le_bytes().to_vec();
477 metadata.extend(as_stat_bitset_bytes(&stats));
478 metadata
479 }
480 ZoneMapSchema::AggregateFns(_) => {
481 vortex_panic!("Cannot serialize aggregate specs as legacy stats metadata")
482 }
483 }
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use std::panic;
490
491 use rstest::rstest;
492 use vortex_array::aggregate_fn::AggregateFnRef;
493 use vortex_array::aggregate_fn::AggregateFnVTableExt;
494 use vortex_array::aggregate_fn::NumericalAggregateOpts;
495 use vortex_array::aggregate_fn::fns::bounded_max::BoundedMax;
496 use vortex_array::aggregate_fn::fns::bounded_max::BoundedMaxOptions;
497 use vortex_array::aggregate_fn::fns::max::Max;
498 use vortex_array::aggregate_fn::fns::min::Min;
499 use vortex_array::aggregate_fn::session::AggregateFnSession;
500 use vortex_array::dtype::DType;
501 use vortex_array::dtype::Nullability;
502 use vortex_array::dtype::PType;
503 use vortex_array::stats::as_stat_bitset_bytes;
504 use vortex_session::VortexSession;
505 use vortex_session::registry::ReadContext;
506
507 use super::*;
508 use crate::LayoutBuildContext;
509 use crate::children::OwnedLayoutChildren;
510 use crate::layouts::flat::FlatLayout;
511 use crate::segments::SegmentId;
512
513 fn aggregate_spec(aggregate_fn: AggregateFnRef) -> AggregateSpecProto {
514 AggregateSpecProto::try_from_aggregate_fn(&aggregate_fn).unwrap()
515 }
516
517 #[rstest]
518 #[case(ZonedMetadata {
519 zone_len: u32::MAX,
520 aggregate_specs: Arc::new([]),
521 })]
522 #[case::min_max(ZonedMetadata {
523 zone_len: 314,
524 aggregate_specs: Arc::new([
525 aggregate_spec(Max.bind(NumericalAggregateOpts::skip_nans())),
526 aggregate_spec(Min.bind(NumericalAggregateOpts::skip_nans())),
527 ]),
528 })]
529 fn test_metadata_serialization(#[case] metadata: ZonedMetadata) {
530 let serialized = metadata.clone().serialize();
531 assert_eq!(serialized[0], ZONED_METADATA_PROTO_VERSION);
532 let deserialized = ZonedMetadata::deserialize(&serialized).unwrap();
533 assert_eq!(deserialized, metadata);
534 }
535
536 #[test]
537 fn test_metadata_serialization_preserves_aggregate_options() -> VortexResult<()> {
538 let aggregate_fn = BoundedMax.bind(BoundedMaxOptions {
539 max_bytes: unsafe { NonZeroUsize::new_unchecked(128) },
541 });
542 let metadata = ZonedMetadata {
543 zone_len: 314,
544 aggregate_specs: Arc::new([AggregateSpecProto::try_from_aggregate_fn(&aggregate_fn)?]),
545 };
546
547 let deserialized = ZonedMetadata::deserialize(&metadata.serialize())?;
548 let session = VortexSession::empty().with::<AggregateFnSession>();
549 let aggregate_fns = try_aggregate_fns_from_specs(&deserialized.aggregate_specs, &session)?
550 .expect("known aggregates resolve");
551
552 assert_eq!(aggregate_fns.as_ref(), std::slice::from_ref(&aggregate_fn));
553 Ok(())
554 }
555
556 #[test]
557 fn test_deserialize_legacy_stat_bitset_as_legacy_stats() {
558 let mut serialized = u32::MAX.to_le_bytes().to_vec();
559 serialized.extend(as_stat_bitset_bytes(&[
560 Stat::IsStrictSorted,
561 Stat::IsSorted,
562 Stat::Max,
563 ]));
564 let deserialized = LegacyStatsMetadata::deserialize(&serialized).unwrap();
565 let ZoneMapSchema::LegacyStats(legacy_stats) = deserialized.zone_map_schema else {
566 panic!("legacy bitset metadata should deserialize as legacy stats");
567 };
568
569 assert!(legacy_stats.is_sorted());
570 assert_eq!(
571 legacy_stats.as_ref(),
572 &[Stat::IsSorted, Stat::IsStrictSorted, Stat::Max]
573 );
574 }
575
576 #[rstest]
577 #[case::empty(vec![])]
578 #[case::unsupported_version(vec![0])]
579 #[case::missing_proto(vec![ZONED_METADATA_PROTO_VERSION])]
580 #[case::malformed_proto(vec![ZONED_METADATA_PROTO_VERSION, 0])]
581 fn test_deserialize_short_metadata_errors(#[case] metadata: Vec<u8>) {
582 assert!(ZonedMetadata::deserialize(&metadata).is_err());
583 }
584
585 #[test]
586 fn test_deserialize_short_metadata_returns_error_not_panic() {
587 let result = panic::catch_unwind(|| ZonedMetadata::deserialize(&[]));
588 assert!(
589 result.is_ok(),
590 "deserialize should return an error, not panic"
591 );
592 assert!(result.unwrap().is_err());
593 }
594
595 #[test]
596 fn test_deserialize_zero_zone_len_is_allowed_for_backcompat() {
597 let metadata = 0u32.to_le_bytes();
598 let deserialized = LegacyStatsMetadata::deserialize(&metadata).unwrap();
599 assert_eq!(deserialized.zone_len, 0);
600 let ZoneMapSchema::LegacyStats(legacy_stats) = deserialized.zone_map_schema else {
601 panic!("legacy bitset metadata should deserialize as legacy stats");
602 };
603 assert!(legacy_stats.is_empty());
604 }
605
606 #[test]
607 fn test_build_allows_zero_zone_len_for_backcompat() -> VortexResult<()> {
608 let dtype = DType::Primitive(PType::I32, Nullability::NonNullable);
609 let read_ctx = ReadContext::new([]);
610 let children = OwnedLayoutChildren::layout_children(vec![
611 FlatLayout::new(0, dtype.clone(), SegmentId::from(0), read_ctx.clone()).into_layout(),
612 FlatLayout::new(
613 0,
614 legacy_stats_table_dtype(&dtype, &[]),
615 SegmentId::from(1),
616 read_ctx,
617 )
618 .into_layout(),
619 ]);
620 let session = vortex_array::array_session();
621 let build_read_ctx = ReadContext::new([]);
622 let build_ctx = LayoutBuildContext {
623 session: &session,
624 array_read_ctx: &build_read_ctx,
625 };
626
627 let layout = <LegacyStats as VTable>::build(
628 &LegacyStatsLayoutEncoding,
629 &dtype,
630 0,
631 &LegacyStatsMetadata {
632 zone_len: 0,
633 zone_map_schema: ZoneMapSchema::LegacyStats(Arc::new([])),
634 },
635 vec![],
636 children.as_ref(),
637 &build_ctx,
638 )?;
639
640 assert_eq!(layout.zone_len, 0);
641 Ok(())
642 }
643
644 #[test]
645 fn test_build_rejects_invalid_child_count() {
646 let metadata = ZonedMetadata {
647 zone_len: 3,
648 aggregate_specs: Arc::new([]),
649 };
650 let children = OwnedLayoutChildren::layout_children(vec![]);
651 let session = vortex_array::array_session();
652 let build_read_ctx = ReadContext::new([]);
653 let build_ctx = LayoutBuildContext {
654 session: &session,
655 array_read_ctx: &build_read_ctx,
656 };
657
658 let result = <Zoned as VTable>::build(
659 &ZonedLayoutEncoding,
660 &DType::Primitive(PType::I32, Nullability::NonNullable),
661 0,
662 &metadata,
663 vec![],
664 children.as_ref(),
665 &build_ctx,
666 );
667
668 assert!(result.is_err());
669 }
670
671 #[test]
672 fn test_build_unknown_aggregate_disables_pruning_when_allowed() -> VortexResult<()> {
673 let dtype = DType::Primitive(PType::I32, Nullability::NonNullable);
674 let read_ctx = ReadContext::new([]);
675 let children = OwnedLayoutChildren::layout_children(vec![
676 FlatLayout::new(0, dtype.clone(), SegmentId::from(0), read_ctx.clone()).into_layout(),
677 FlatLayout::new(0, dtype.clone(), SegmentId::from(1), read_ctx).into_layout(),
678 ]);
679 let session = vortex_array::array_session();
680 session.allow_unknown();
681 let build_read_ctx = ReadContext::new([]);
682 let build_ctx = LayoutBuildContext {
683 session: &session,
684 array_read_ctx: &build_read_ctx,
685 };
686
687 let metadata = ZonedMetadata {
688 zone_len: 8,
689 aggregate_specs: Arc::new([AggregateSpecProto::new_unknown("vortex.test.unknown")]),
690 };
691
692 let layout = <Zoned as VTable>::build(
693 &ZonedLayoutEncoding,
694 &dtype,
695 0,
696 &metadata,
697 vec![],
698 children.as_ref(),
699 &build_ctx,
700 )?;
701
702 assert_eq!(layout.zone_len, 0);
705 Ok(())
706 }
707
708 #[test]
709 fn test_build_unknown_aggregate_errors_without_allow_unknown() {
710 let dtype = DType::Primitive(PType::I32, Nullability::NonNullable);
711 let read_ctx = ReadContext::new([]);
712 let children = OwnedLayoutChildren::layout_children(vec![
713 FlatLayout::new(0, dtype.clone(), SegmentId::from(0), read_ctx.clone()).into_layout(),
714 FlatLayout::new(0, dtype.clone(), SegmentId::from(1), read_ctx).into_layout(),
715 ]);
716 let session = vortex_array::array_session();
717 let build_read_ctx = ReadContext::new([]);
718 let build_ctx = LayoutBuildContext {
719 session: &session,
720 array_read_ctx: &build_read_ctx,
721 };
722
723 let metadata = ZonedMetadata {
724 zone_len: 8,
725 aggregate_specs: Arc::new([AggregateSpecProto::new_unknown("vortex.test.unknown")]),
726 };
727
728 let result = <Zoned as VTable>::build(
729 &ZonedLayoutEncoding,
730 &dtype,
731 0,
732 &metadata,
733 vec![],
734 children.as_ref(),
735 &build_ctx,
736 );
737
738 assert!(result.is_err());
739 }
740}