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