1use std::fmt::Debug;
5use std::hash::Hash;
6use std::hash::Hasher;
7
8use itertools::Itertools;
9use vortex_error::VortexExpect;
10use vortex_error::VortexResult;
11use vortex_error::vortex_ensure_eq;
12use vortex_error::vortex_panic;
13use vortex_session::VortexSession;
14use vortex_session::registry::CachedId;
15
16use crate::ArrayEq;
17use crate::ArrayHash;
18use crate::ArrayParts;
19use crate::ArrayRef;
20use crate::ArraySlots;
21use crate::EqMode;
22use crate::ExecutionCtx;
23use crate::ExecutionResult;
24use crate::IntoArray;
25use crate::array::Array;
26use crate::array::ArrayId;
27use crate::array::ArrayView;
28use crate::array::VTable;
29use crate::array::unsupported_buffer_replacement;
30use crate::arrays::ExtensionArray;
31use crate::arrays::constant::ConstantData;
32use crate::arrays::constant::compute::rules::PARENT_RULES;
33use crate::arrays::constant::vtable::canonical::constant_canonicalize;
34use crate::buffer::BufferHandle;
35use crate::builders::ArrayBuilder;
36use crate::builders::BoolBuilder;
37use crate::builders::DecimalBuilder;
38use crate::builders::FixedSizeListBuilder;
39use crate::builders::ListViewBuilder;
40use crate::builders::NullBuilder;
41use crate::builders::PrimitiveBuilder;
42use crate::builders::VarBinViewBuilder;
43use crate::builders::builder_with_capacity_in;
44use crate::canonical::Canonical;
45use crate::dtype::DType;
46use crate::dtype::OffsetBuilderPType;
47use crate::match_each_decimal_value;
48use crate::match_each_listview_builder;
49use crate::match_each_native_ptype;
50use crate::match_each_varbin_builder;
51use crate::scalar::DecimalValue;
52use crate::scalar::ListScalar;
53use crate::scalar::Scalar;
54use crate::scalar::ScalarValue;
55use crate::serde::ArrayChildren;
56pub(crate) mod canonical;
57mod operations;
58mod validity;
59
60pub type ConstantArray = Array<Constant>;
62
63#[derive(Clone, Debug)]
64pub struct Constant;
65
66impl ArrayHash for ConstantData {
67 fn array_hash<H: Hasher>(&self, state: &mut H, _accuracy: EqMode) {
68 self.scalar.hash(state);
69 }
70}
71
72impl ArrayEq for ConstantData {
73 fn array_eq(&self, other: &Self, _accuracy: EqMode) -> bool {
74 self.scalar == other.scalar
75 }
76}
77
78impl VTable for Constant {
79 type TypedArrayData = ConstantData;
80
81 type OperationsVTable = Self;
82 type ValidityVTable = Self;
83
84 fn id(&self) -> ArrayId {
85 static ID: CachedId = CachedId::new("vortex.constant");
86 *ID
87 }
88
89 fn validate(
90 &self,
91 data: &ConstantData,
92 dtype: &DType,
93 _len: usize,
94 _slots: &[Option<ArrayRef>],
95 ) -> VortexResult<()> {
96 vortex_ensure_eq!(
97 data.scalar.dtype(),
98 dtype,
99 "ConstantArray scalar dtype does not match outer dtype",
100 );
101 Ok(())
102 }
103
104 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
105 1
106 }
107
108 fn buffer(array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
109 match idx {
110 0 => BufferHandle::new_host(
111 ScalarValue::to_proto_bytes::<Vec<u8>>(array.scalar.value()).into(),
112 ),
113 _ => vortex_panic!("ConstantArray buffer index {idx} out of bounds"),
114 }
115 }
116
117 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
118 match idx {
119 0 => Some("scalar".to_string()),
120 _ => None,
121 }
122 }
123
124 fn with_buffers(
125 &self,
126 array: ArrayView<'_, Self>,
127 buffers: &[BufferHandle],
128 ) -> VortexResult<ArrayParts<Self>> {
129 unsupported_buffer_replacement(array, buffers)
130 }
131
132 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
133 vortex_panic!("ConstantArray slot_name index {idx} out of bounds")
134 }
135
136 fn serialize(
137 _array: ArrayView<'_, Self>,
138 _session: &VortexSession,
139 ) -> VortexResult<Option<Vec<u8>>> {
140 Ok(Some(vec![]))
143 }
144
145 fn deserialize(
146 &self,
147 dtype: &DType,
148 len: usize,
149 _metadata: &[u8],
150
151 buffers: &[BufferHandle],
152 _children: &dyn ArrayChildren,
153 session: &VortexSession,
154 ) -> VortexResult<ArrayParts<Self>> {
155 vortex_ensure_eq!(buffers.len(), 1);
156
157 let buffer = buffers[0].clone().try_to_host_sync()?;
158 let bytes: &[u8] = buffer.as_ref();
159
160 let scalar_value = ScalarValue::from_proto_bytes(bytes, dtype, session)?;
161 let scalar = Scalar::try_new(dtype.clone(), scalar_value)?;
162
163 Ok(ArrayParts::new(
164 self.clone(),
165 dtype.clone(),
166 len,
167 ConstantData::new(scalar),
168 ArraySlots::new(),
169 ))
170 }
171
172 fn reduce_parent(
173 array: ArrayView<'_, Self>,
174 parent: &ArrayRef,
175 child_idx: usize,
176 ) -> VortexResult<Option<ArrayRef>> {
177 PARENT_RULES.evaluate(array, parent, child_idx)
178 }
179
180 fn execute(array: Array<Self>, ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
181 Ok(ExecutionResult::done(constant_canonicalize(
182 array.as_view(),
183 ctx,
184 )?))
185 }
186
187 fn append_to_builder(
188 array: ArrayView<'_, Self>,
189 builder: &mut dyn ArrayBuilder,
190 ctx: &mut ExecutionCtx,
191 ) -> VortexResult<()> {
192 let n = array.len();
193 let scalar = array.scalar();
194
195 match array.dtype() {
196 DType::Null => append_value_or_nulls::<NullBuilder>(builder, true, n, |_| {}),
197 DType::Bool(_) => {
198 append_value_or_nulls::<BoolBuilder>(builder, scalar.is_null(), n, |b| {
199 b.append_values(
200 scalar
201 .as_bool()
202 .value()
203 .vortex_expect("non-null bool scalar must have a value"),
204 n,
205 );
206 })
207 }
208 DType::Primitive(ptype, _) => {
209 match_each_native_ptype!(ptype, |P| {
210 append_value_or_nulls::<PrimitiveBuilder<P>>(
211 builder,
212 scalar.is_null(),
213 n,
214 |b| {
215 let value = P::try_from(scalar)
216 .vortex_expect("Couldn't unwrap constant scalar to primitive");
217 b.append_n_values(value, n);
218 },
219 );
220 });
221 }
222 DType::Decimal(..) => {
223 append_value_or_nulls::<DecimalBuilder>(builder, scalar.is_null(), n, |b| {
224 let value = scalar
225 .as_decimal()
226 .decimal_value()
227 .vortex_expect("non-null decimal scalar must have a value");
228 match_each_decimal_value!(value, |v| { b.append_n_values(v, n) });
229 });
230 }
231 DType::Utf8(_) => {
232 if let Some(result) = match_each_varbin_builder!(builder, |builder| {
233 builder.append_scalar_repeated(scalar, n)
234 }) {
235 result?;
236 } else {
237 append_value_or_nulls::<VarBinViewBuilder>(builder, scalar.is_null(), n, |b| {
238 let value = scalar
239 .as_utf8()
240 .value()
241 .vortex_expect("non-null utf8 scalar must have a value");
242 b.append_n_values(value.as_bytes(), n);
243 });
244 }
245 }
246 DType::Binary(_) => {
247 if let Some(result) = match_each_varbin_builder!(builder, |builder| {
248 builder.append_scalar_repeated(scalar, n)
249 }) {
250 result?;
251 } else {
252 append_value_or_nulls::<VarBinViewBuilder>(builder, scalar.is_null(), n, |b| {
253 let value = scalar
254 .as_binary()
255 .value()
256 .vortex_expect("non-null binary scalar must have a value");
257 b.append_n_values(value, n);
258 });
259 }
260 }
261 DType::List(..) => append_constant_list_run(array, n, builder, ctx)?,
262 DType::Extension(ext_dtype) => {
263 let storage = ConstantArray::new(scalar.as_extension().to_storage_scalar(), n);
268 ExtensionArray::new(ext_dtype.clone(), storage.into_array())
269 .into_array()
270 .append_to_builder(builder, ctx)?
271 }
272 DType::FixedSizeList(..) => {
273 append_constant_fixed_size_list_run(array, n, builder, ctx)?
274 }
275 _ => append_via_canonical(array, builder, ctx)?,
280 }
281
282 Ok(())
283 }
284}
285
286fn append_constant_list_run(
294 array: ArrayView<'_, Constant>,
295 n: usize,
296 builder: &mut dyn ArrayBuilder,
297 ctx: &mut ExecutionCtx,
298) -> VortexResult<()> {
299 let scalar = array.scalar();
300 match match_each_listview_builder!(builder, |b| append_repeated_list_run(
301 b,
302 scalar.as_list(),
303 n,
304 ctx
305 )) {
306 Some(result) => result,
307 None => append_via_canonical(array, builder, ctx),
308 }
309}
310
311fn append_repeated_list_run<O: OffsetBuilderPType, S: OffsetBuilderPType>(
313 builder: &mut ListViewBuilder<O, S>,
314 scalar: ListScalar,
315 n: usize,
316 ctx: &mut ExecutionCtx,
317) -> VortexResult<()> {
318 if n == 0 {
319 return Ok(());
320 }
321
322 let Some(elements) = scalar.elements() else {
323 builder.append_nulls(n);
325 return Ok(());
326 };
327
328 let mut elements_builder =
329 builder_with_capacity_in(scalar.element_dtype(), elements.len(), ctx.allocator());
330 for element in &elements {
331 elements_builder.append_scalar(element)?;
332 }
333
334 builder.append_array_as_repeated_list(&elements_builder.finish(), n, ctx)
335}
336
337fn append_constant_fixed_size_list_run(
343 array: ArrayView<'_, Constant>,
344 n: usize,
345 builder: &mut dyn ArrayBuilder,
346 ctx: &mut ExecutionCtx,
347) -> VortexResult<()> {
348 let Some(builder) = builder.as_any_mut().downcast_mut::<FixedSizeListBuilder>() else {
349 return append_via_canonical(array, builder, ctx);
350 };
351
352 if n == 0 {
353 return Ok(());
354 }
355
356 let scalar = array.scalar().as_list();
357 let Some(elements) = scalar.elements() else {
358 builder.append_nulls(n);
360 return Ok(());
361 };
362
363 let tile = match elements.iter().all_equal_value() {
364 Ok(uniform) => ConstantArray::new(uniform.clone(), elements.len()).into_array(),
365 Err(_) => {
366 let mut tile_builder =
367 builder_with_capacity_in(builder.element_dtype(), elements.len(), ctx.allocator());
368 for element in &elements {
369 tile_builder.append_scalar(element)?;
370 }
371 tile_builder.finish()
372 }
373 };
374
375 builder.append_array_as_repeated_list(&tile, n, ctx)
376}
377
378fn append_via_canonical(
380 array: ArrayView<'_, Constant>,
381 builder: &mut dyn ArrayBuilder,
382 ctx: &mut ExecutionCtx,
383) -> VortexResult<()> {
384 let canonical = array
385 .array()
386 .clone()
387 .execute::<Canonical>(ctx)?
388 .into_array();
389 canonical.append_to_builder(builder, ctx)
390}
391
392fn append_value_or_nulls<B: ArrayBuilder + 'static>(
397 builder: &mut dyn ArrayBuilder,
398 is_null: bool,
399 n: usize,
400 fill: impl FnOnce(&mut B),
401) {
402 let b = builder
403 .as_any_mut()
404 .downcast_mut::<B>()
405 .vortex_expect("builder dtype must match array dtype");
406 if is_null {
407 unsafe { b.append_nulls_unchecked(n) };
409 } else {
410 fill(b);
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use std::sync::Arc;
417
418 use rstest::rstest;
419 use vortex_error::VortexResult;
420
421 use crate::IntoArray;
422 use crate::VortexSessionExecute;
423 use crate::arrays::Chunked;
424 use crate::arrays::Constant;
425 use crate::arrays::ConstantArray;
426 use crate::arrays::Extension;
427 use crate::arrays::FixedSizeList;
428 use crate::arrays::ListView;
429 use crate::arrays::Struct;
430 use crate::arrays::chunked::ChunkedArrayExt;
431 use crate::arrays::constant::vtable::canonical::constant_canonicalize;
432 use crate::arrays::extension::ExtensionArrayExt;
433 use crate::arrays::fixed_size_list::FixedSizeListArraySlotsExt;
434 use crate::arrays::listview::ListViewArraySlotsExt;
435 use crate::arrays::struct_::StructArrayExt;
436 use crate::assert_arrays_eq;
437 use crate::builders::ArrayBuilder;
438 use crate::builders::ListBuilder;
439 use crate::builders::builder_with_capacity_in;
440 use crate::dtype::DType;
441 use crate::dtype::Nullability;
442 use crate::dtype::PType;
443 use crate::dtype::StructFields;
444 use crate::extension::datetime::Date;
445 use crate::extension::datetime::TimeUnit;
446 use crate::scalar::Scalar;
447
448 fn assert_append_matches_canonical(array: ConstantArray) -> VortexResult<()> {
450 let mut ctx = crate::array_session().create_execution_ctx();
451
452 let expected = constant_canonicalize(array.as_view(), &mut ctx)?.into_array();
453 let mut builder = builder_with_capacity_in(
454 array.dtype(),
455 array.len(),
456 vortex_buffer::BufferAllocatorRef::static_ref(),
457 );
458 array
459 .into_array()
460 .append_to_builder(builder.as_mut(), &mut ctx)?;
461 let result = builder.finish();
462 assert_arrays_eq!(&result, &expected, &mut ctx);
463 Ok(())
464 }
465
466 #[test]
467 fn test_null_constant_append() -> VortexResult<()> {
468 assert_append_matches_canonical(ConstantArray::new(Scalar::null(DType::Null), 5))
469 }
470
471 #[test]
472 fn test_with_buffers_rejects_serialized_scalar_buffer() {
473 let array =
474 ConstantArray::new(Scalar::primitive(42i32, Nullability::NonNullable), 3).into_array();
475 let buffers = array.buffer_handles();
476
477 let Err(err) = (unsafe { array.with_buffers(buffers) }) else {
480 panic!("ConstantArray should reject replacing its serialized scalar buffer");
481 };
482 assert!(
483 err.to_string()
484 .contains("does not support in-memory buffer replacement")
485 );
486 }
487
488 #[rstest]
489 #[case::bool_true(true, 5)]
490 #[case::bool_false(false, 3)]
491 fn test_bool_constant_append(#[case] value: bool, #[case] n: usize) -> VortexResult<()> {
492 assert_append_matches_canonical(ConstantArray::new(
493 Scalar::bool(value, Nullability::NonNullable),
494 n,
495 ))
496 }
497
498 #[test]
499 fn test_bool_null_constant_append() -> VortexResult<()> {
500 assert_append_matches_canonical(ConstantArray::new(
501 Scalar::null(DType::Bool(Nullability::Nullable)),
502 4,
503 ))
504 }
505
506 #[rstest]
507 #[case::i32(Scalar::primitive(42i32, Nullability::NonNullable), 5)]
508 #[case::u8(Scalar::primitive(7u8, Nullability::NonNullable), 3)]
509 #[case::f64(Scalar::primitive(1.5f64, Nullability::NonNullable), 4)]
510 #[case::i32_null(Scalar::null(DType::Primitive(PType::I32, Nullability::Nullable)), 3)]
511 fn test_primitive_constant_append(
512 #[case] scalar: Scalar,
513 #[case] n: usize,
514 ) -> VortexResult<()> {
515 assert_append_matches_canonical(ConstantArray::new(scalar, n))
516 }
517
518 #[rstest]
519 #[case::utf8_inline("hi", 5)] #[case::utf8_noninline("hello world!!", 5)] #[case::utf8_empty("", 3)]
522 #[case::utf8_n_zero("hello world!!", 0)] fn test_utf8_constant_append(#[case] value: &str, #[case] n: usize) -> VortexResult<()> {
524 assert_append_matches_canonical(ConstantArray::new(
525 Scalar::utf8(value, Nullability::NonNullable),
526 n,
527 ))
528 }
529
530 #[test]
531 fn test_utf8_null_constant_append() -> VortexResult<()> {
532 assert_append_matches_canonical(ConstantArray::new(
533 Scalar::null(DType::Utf8(Nullability::Nullable)),
534 4,
535 ))
536 }
537
538 #[rstest]
539 #[case::binary_inline(vec![1u8, 2, 3], 5)] #[case::binary_noninline(vec![0u8; 13], 5)] fn test_binary_constant_append(#[case] value: Vec<u8>, #[case] n: usize) -> VortexResult<()> {
542 assert_append_matches_canonical(ConstantArray::new(
543 Scalar::binary(value, Nullability::NonNullable),
544 n,
545 ))
546 }
547
548 #[test]
549 fn test_binary_null_constant_append() -> VortexResult<()> {
550 assert_append_matches_canonical(ConstantArray::new(
551 Scalar::null(DType::Binary(Nullability::Nullable)),
552 4,
553 ))
554 }
555
556 #[rstest]
557 #[case::non_empty(vec![Scalar::from(1i32), Scalar::from(2i32)], 4)]
558 #[case::empty(vec![], 3)]
559 #[case::n_zero(vec![Scalar::from(1i32)], 0)]
560 fn test_list_constant_append(
561 #[case] elements: Vec<Scalar>,
562 #[case] n: usize,
563 ) -> VortexResult<()> {
564 let scalar = Scalar::list(
565 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
566 elements,
567 Nullability::NonNullable,
568 );
569 assert_append_matches_canonical(ConstantArray::new(scalar, n))
570 }
571
572 #[test]
573 fn test_null_list_constant_append() -> VortexResult<()> {
574 let dtype = DType::List(
575 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
576 Nullability::Nullable,
577 );
578 assert_append_matches_canonical(ConstantArray::new(Scalar::null(dtype), 3))
579 }
580
581 #[test]
584 fn test_list_constant_append_keeps_one_copy_of_the_elements() -> VortexResult<()> {
585 let mut ctx = crate::array_session().create_execution_ctx();
586 let scalar = Scalar::list(
587 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
588 vec![Scalar::from(1i32), Scalar::from(2i32), Scalar::from(3i32)],
589 Nullability::NonNullable,
590 );
591 let array = ConstantArray::new(scalar, 1_000);
592
593 let mut builder = builder_with_capacity_in(
594 array.dtype(),
595 array.len(),
596 vortex_buffer::BufferAllocatorRef::static_ref(),
597 );
598 array
599 .into_array()
600 .append_to_builder(builder.as_mut(), &mut ctx)?;
601 let result = builder.finish();
602
603 assert_eq!(
604 result.as_::<ListView>().elements().len(),
605 3,
606 "the run's elements should be stored once, not once per row",
607 );
608 Ok(())
609 }
610
611 #[test]
614 fn test_list_constant_append_into_list_builder() -> VortexResult<()> {
615 let mut ctx = crate::array_session().create_execution_ctx();
616 let element_dtype: Arc<DType> =
617 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable));
618 let scalar = Scalar::list(
619 Arc::clone(&element_dtype),
620 vec![Scalar::from(1i32), Scalar::from(2i32)],
621 Nullability::NonNullable,
622 );
623 let array = ConstantArray::new(scalar, 4).into_array();
624
625 let mut builder = ListBuilder::<u32>::with_capacity_in(
626 element_dtype,
627 Nullability::NonNullable,
628 0,
629 0,
630 vortex_buffer::BufferAllocatorRef::static_ref(),
631 );
632 array.append_to_builder(&mut builder, &mut ctx)?;
633
634 assert_arrays_eq!(&builder.finish(), &array, &mut ctx);
635 Ok(())
636 }
637
638 #[test]
639 fn test_struct_constant_append() -> VortexResult<()> {
640 let fields = StructFields::new(
641 ["x", "y"].into(),
642 vec![
643 DType::Primitive(PType::I32, Nullability::NonNullable),
644 DType::Utf8(Nullability::NonNullable),
645 ],
646 );
647 let scalar = Scalar::struct_(
648 DType::Struct(fields, Nullability::NonNullable),
649 [
650 Scalar::primitive(42i32, Nullability::NonNullable),
651 Scalar::utf8("hi", Nullability::NonNullable),
652 ],
653 );
654 assert_append_matches_canonical(ConstantArray::new(scalar, 3))
655 }
656
657 #[test]
658 fn test_null_struct_constant_append() -> VortexResult<()> {
659 let fields = StructFields::new(
660 ["x"].into(),
661 vec![DType::Primitive(PType::I32, Nullability::Nullable)],
662 );
663 let dtype = DType::Struct(fields, Nullability::Nullable);
664 assert_append_matches_canonical(ConstantArray::new(Scalar::null(dtype), 4))
665 }
666
667 #[test]
670 fn test_struct_constant_append_keeps_fields_constant() -> VortexResult<()> {
671 let mut ctx = crate::array_session().create_execution_ctx();
672 let fields = StructFields::new(
673 ["x", "y"].into(),
674 vec![
675 DType::Primitive(PType::I32, Nullability::NonNullable),
676 DType::Utf8(Nullability::NonNullable),
677 ],
678 );
679 let scalar = Scalar::struct_(
680 DType::Struct(fields, Nullability::NonNullable),
681 [
682 Scalar::primitive(42i32, Nullability::NonNullable),
683 Scalar::utf8("hi", Nullability::NonNullable),
684 ],
685 );
686 let array = ConstantArray::new(scalar, 1_000);
687
688 let mut builder = builder_with_capacity_in(
689 array.dtype(),
690 array.len(),
691 vortex_buffer::BufferAllocatorRef::static_ref(),
692 );
693 array
694 .into_array()
695 .append_to_builder(builder.as_mut(), &mut ctx)?;
696 let result = builder.finish();
697
698 let struct_array = result.as_::<Struct>();
699 for field in 0..2 {
700 assert!(
701 struct_array.unmasked_field(field).is::<Constant>(),
702 "field {field} should have stayed constant-encoded",
703 );
704 }
705 Ok(())
706 }
707
708 #[rstest]
709 #[case::non_uniform(vec![Scalar::from(1i32), Scalar::from(2i32)])]
710 #[case::uniform(vec![Scalar::from(7i32), Scalar::from(7i32)])]
711 fn test_fixed_size_list_constant_append(#[case] elements: Vec<Scalar>) -> VortexResult<()> {
712 let scalar = Scalar::fixed_size_list(
713 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
714 elements,
715 Nullability::NonNullable,
716 );
717 assert_append_matches_canonical(ConstantArray::new(scalar, 4))
718 }
719
720 #[test]
721 fn test_null_fixed_size_list_constant_append() -> VortexResult<()> {
722 let dtype = DType::FixedSizeList(
723 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
724 2,
725 Nullability::Nullable,
726 );
727 assert_append_matches_canonical(ConstantArray::new(Scalar::null(dtype), 3))
728 }
729
730 #[test]
733 fn test_uniform_fixed_size_list_constant_append_keeps_elements_constant() -> VortexResult<()> {
734 let mut ctx = crate::array_session().create_execution_ctx();
735 let scalar = Scalar::fixed_size_list(
736 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
737 vec![Scalar::from(7i32), Scalar::from(7i32)],
738 Nullability::NonNullable,
739 );
740 let array = ConstantArray::new(scalar, 1_000);
741
742 let mut builder = builder_with_capacity_in(
743 array.dtype(),
744 array.len(),
745 vortex_buffer::BufferAllocatorRef::static_ref(),
746 );
747 array
748 .into_array()
749 .append_to_builder(builder.as_mut(), &mut ctx)?;
750 let result = builder.finish();
751
752 let elements = result.as_::<FixedSizeList>().elements().clone();
753 assert!(
754 elements
755 .as_::<Chunked>()
756 .iter_chunks()
757 .all(|chunk| chunk.is::<Constant>()),
758 "a uniform tile should have stayed constant-encoded",
759 );
760 Ok(())
761 }
762
763 #[test]
764 fn test_extension_constant_append() -> VortexResult<()> {
765 let scalar = Scalar::extension::<Date>(TimeUnit::Days, Scalar::from(Some(42i32)));
766 assert_append_matches_canonical(ConstantArray::new(scalar, 5))
767 }
768
769 #[test]
772 fn test_extension_constant_append_keeps_storage_constant() -> VortexResult<()> {
773 let mut ctx = crate::array_session().create_execution_ctx();
774 let scalar = Scalar::extension::<Date>(TimeUnit::Days, Scalar::from(Some(42i32)));
775 let array = ConstantArray::new(scalar, 1_000);
776
777 let mut builder = builder_with_capacity_in(
778 array.dtype(),
779 array.len(),
780 vortex_buffer::BufferAllocatorRef::static_ref(),
781 );
782 array
783 .into_array()
784 .append_to_builder(builder.as_mut(), &mut ctx)?;
785 let result = builder.finish();
786
787 assert!(
788 result.as_::<Extension>().storage_array().is::<Constant>(),
789 "the storage should have stayed constant-encoded",
790 );
791 Ok(())
792 }
793}