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::ArrayRef;
422 use crate::ExecutionCtx;
423 use crate::IntoArray;
424 use crate::VortexSessionExecute;
425 use crate::arrays::Chunked;
426 use crate::arrays::Constant;
427 use crate::arrays::ConstantArray;
428 use crate::arrays::Extension;
429 use crate::arrays::FixedSizeList;
430 use crate::arrays::ListView;
431 use crate::arrays::Struct;
432 use crate::arrays::chunked::ChunkedArrayExt;
433 use crate::arrays::constant::vtable::canonical::constant_canonicalize;
434 use crate::arrays::extension::ExtensionArrayExt;
435 use crate::arrays::fixed_size_list::FixedSizeListArraySlotsExt;
436 use crate::arrays::listview::ListViewArraySlotsExt;
437 use crate::arrays::struct_::StructArrayExt;
438 use crate::assert_arrays_eq;
439 use crate::builders::ArrayBuilder;
440 use crate::builders::ListBuilder;
441 use crate::builders::builder_with_capacity_in;
442 use crate::dtype::DType;
443 use crate::dtype::Nullability;
444 use crate::dtype::PType;
445 use crate::dtype::StructFields;
446 use crate::extension::datetime::Date;
447 use crate::extension::datetime::TimeUnit;
448 use crate::scalar::Scalar;
449
450 fn assert_constant_values(
451 array: &ArrayRef,
452 scalar: &Scalar,
453 len: usize,
454 ctx: &mut ExecutionCtx,
455 ) -> VortexResult<()> {
456 assert_eq!(array.dtype(), scalar.dtype());
457 assert_eq!(array.len(), len);
458 for index in 0..len {
459 assert_eq!(&array.execute_scalar(index, ctx)?, scalar);
460 }
461 Ok(())
462 }
463
464 fn assert_append_matches_canonical(array: ConstantArray) -> VortexResult<()> {
466 let mut ctx = crate::array_session().create_execution_ctx();
467
468 let scalar = array.scalar().clone();
469 let len = array.len();
470 let expected = constant_canonicalize(array.as_view(), &mut ctx)?.into_array();
471 let mut builder = builder_with_capacity_in(
472 array.dtype(),
473 array.len(),
474 vortex_buffer::BufferAllocatorRef::static_ref(),
475 );
476 array
477 .into_array()
478 .append_to_builder(builder.as_mut(), &mut ctx)?;
479 let result = builder.finish();
480 assert_arrays_eq!(&result, &expected, &mut ctx);
481 assert_constant_values(&result, &scalar, len, &mut ctx)?;
482 Ok(())
483 }
484
485 #[test]
486 fn test_null_constant_append() -> VortexResult<()> {
487 assert_append_matches_canonical(ConstantArray::new(Scalar::null(DType::Null), 5))
488 }
489
490 #[test]
491 fn test_with_buffers_rejects_serialized_scalar_buffer() {
492 let array =
493 ConstantArray::new(Scalar::primitive(42i32, Nullability::NonNullable), 3).into_array();
494 let buffers = array.buffer_handles();
495
496 let Err(err) = (unsafe { array.with_buffers(buffers) }) else {
499 panic!("ConstantArray should reject replacing its serialized scalar buffer");
500 };
501 assert!(
502 err.to_string()
503 .contains("does not support in-memory buffer replacement")
504 );
505 }
506
507 #[rstest]
508 #[case::bool_true(true, 5)]
509 #[case::bool_false(false, 3)]
510 fn test_bool_constant_append(#[case] value: bool, #[case] n: usize) -> VortexResult<()> {
511 assert_append_matches_canonical(ConstantArray::new(
512 Scalar::bool(value, Nullability::NonNullable),
513 n,
514 ))
515 }
516
517 #[test]
518 fn test_bool_null_constant_append() -> VortexResult<()> {
519 assert_append_matches_canonical(ConstantArray::new(
520 Scalar::null(DType::Bool(Nullability::Nullable)),
521 4,
522 ))
523 }
524
525 #[rstest]
526 #[case::i32(Scalar::primitive(42i32, Nullability::NonNullable), 5)]
527 #[case::u8(Scalar::primitive(7u8, Nullability::NonNullable), 3)]
528 #[case::f64(Scalar::primitive(1.5f64, Nullability::NonNullable), 4)]
529 #[case::i32_null(Scalar::null(DType::Primitive(PType::I32, Nullability::Nullable)), 3)]
530 fn test_primitive_constant_append(
531 #[case] scalar: Scalar,
532 #[case] n: usize,
533 ) -> VortexResult<()> {
534 assert_append_matches_canonical(ConstantArray::new(scalar, n))
535 }
536
537 #[rstest]
538 #[case::utf8_inline("hi", 5)] #[case::utf8_noninline("hello world!!", 5)] #[case::utf8_empty("", 3)]
541 #[case::utf8_n_zero("hello world!!", 0)] fn test_utf8_constant_append(#[case] value: &str, #[case] n: usize) -> VortexResult<()> {
543 assert_append_matches_canonical(ConstantArray::new(
544 Scalar::utf8(value, Nullability::NonNullable),
545 n,
546 ))
547 }
548
549 #[test]
550 fn test_utf8_null_constant_append() -> VortexResult<()> {
551 assert_append_matches_canonical(ConstantArray::new(
552 Scalar::null(DType::Utf8(Nullability::Nullable)),
553 4,
554 ))
555 }
556
557 #[rstest]
558 #[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<()> {
561 assert_append_matches_canonical(ConstantArray::new(
562 Scalar::binary(value, Nullability::NonNullable),
563 n,
564 ))
565 }
566
567 #[test]
568 fn test_binary_null_constant_append() -> VortexResult<()> {
569 assert_append_matches_canonical(ConstantArray::new(
570 Scalar::null(DType::Binary(Nullability::Nullable)),
571 4,
572 ))
573 }
574
575 #[rstest]
576 #[case::non_empty(vec![Scalar::from(1i32), Scalar::from(2i32)], 4)]
577 #[case::empty(vec![], 3)]
578 #[case::n_zero(vec![Scalar::from(1i32)], 0)]
579 fn test_list_constant_append(
580 #[case] elements: Vec<Scalar>,
581 #[case] n: usize,
582 ) -> VortexResult<()> {
583 let scalar = Scalar::list(
584 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
585 elements,
586 Nullability::NonNullable,
587 );
588 assert_append_matches_canonical(ConstantArray::new(scalar, n))
589 }
590
591 #[test]
592 fn test_null_list_constant_append() -> VortexResult<()> {
593 let dtype = DType::List(
594 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
595 Nullability::Nullable,
596 );
597 assert_append_matches_canonical(ConstantArray::new(Scalar::null(dtype), 3))
598 }
599
600 #[test]
603 fn test_list_constant_append_keeps_one_copy_of_the_elements() -> VortexResult<()> {
604 let mut ctx = crate::array_session().create_execution_ctx();
605 let scalar = Scalar::list(
606 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
607 vec![Scalar::from(1i32), Scalar::from(2i32), Scalar::from(3i32)],
608 Nullability::NonNullable,
609 );
610 let array = ConstantArray::new(scalar.clone(), 1_000);
611
612 let mut builder = builder_with_capacity_in(
613 array.dtype(),
614 array.len(),
615 vortex_buffer::BufferAllocatorRef::static_ref(),
616 );
617 array
618 .into_array()
619 .append_to_builder(builder.as_mut(), &mut ctx)?;
620 let result = builder.finish();
621 assert_constant_values(&result, &scalar, 1_000, &mut ctx)?;
622
623 assert_eq!(
624 result.as_::<ListView>().elements().len(),
625 3,
626 "the run's elements should be stored once, not once per row",
627 );
628 Ok(())
629 }
630
631 #[test]
634 fn test_list_constant_append_into_list_builder() -> VortexResult<()> {
635 let mut ctx = crate::array_session().create_execution_ctx();
636 let element_dtype: Arc<DType> =
637 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable));
638 let scalar = Scalar::list(
639 Arc::clone(&element_dtype),
640 vec![Scalar::from(1i32), Scalar::from(2i32)],
641 Nullability::NonNullable,
642 );
643 let array = ConstantArray::new(scalar, 4).into_array();
644
645 let mut builder = ListBuilder::<u32>::with_capacity_in(
646 element_dtype,
647 Nullability::NonNullable,
648 0,
649 0,
650 vortex_buffer::BufferAllocatorRef::static_ref(),
651 );
652 array.append_to_builder(&mut builder, &mut ctx)?;
653
654 assert_arrays_eq!(&builder.finish(), &array, &mut ctx);
655 Ok(())
656 }
657
658 #[test]
659 fn test_struct_constant_append() -> VortexResult<()> {
660 let fields = StructFields::new(
661 ["x", "y"].into(),
662 vec![
663 DType::Primitive(PType::I32, Nullability::NonNullable),
664 DType::Utf8(Nullability::NonNullable),
665 ],
666 );
667 let scalar = Scalar::struct_(
668 DType::Struct(fields, Nullability::NonNullable),
669 [
670 Scalar::primitive(42i32, Nullability::NonNullable),
671 Scalar::utf8("hi", Nullability::NonNullable),
672 ],
673 );
674 assert_append_matches_canonical(ConstantArray::new(scalar, 3))
675 }
676
677 #[test]
678 fn test_null_struct_constant_append() -> VortexResult<()> {
679 let fields = StructFields::new(
680 ["x"].into(),
681 vec![DType::Primitive(PType::I32, Nullability::Nullable)],
682 );
683 let dtype = DType::Struct(fields, Nullability::Nullable);
684 assert_append_matches_canonical(ConstantArray::new(Scalar::null(dtype), 4))
685 }
686
687 #[test]
690 fn test_struct_constant_append_keeps_fields_constant() -> VortexResult<()> {
691 let mut ctx = crate::array_session().create_execution_ctx();
692 let fields = StructFields::new(
693 ["x", "y"].into(),
694 vec![
695 DType::Primitive(PType::I32, Nullability::NonNullable),
696 DType::Utf8(Nullability::NonNullable),
697 ],
698 );
699 let scalar = Scalar::struct_(
700 DType::Struct(fields, Nullability::NonNullable),
701 [
702 Scalar::primitive(42i32, Nullability::NonNullable),
703 Scalar::utf8("hi", Nullability::NonNullable),
704 ],
705 );
706 let array = ConstantArray::new(scalar.clone(), 1_000);
707
708 let mut builder = builder_with_capacity_in(
709 array.dtype(),
710 array.len(),
711 vortex_buffer::BufferAllocatorRef::static_ref(),
712 );
713 array
714 .into_array()
715 .append_to_builder(builder.as_mut(), &mut ctx)?;
716 let result = builder.finish();
717 assert_constant_values(&result, &scalar, 1_000, &mut ctx)?;
718
719 let struct_array = result.as_::<Struct>();
720 for field in 0..2 {
721 assert!(
722 struct_array.unmasked_field(field).is::<Constant>(),
723 "field {field} should have stayed constant-encoded",
724 );
725 }
726 Ok(())
727 }
728
729 #[rstest]
730 #[case::non_uniform(vec![Scalar::from(1i32), Scalar::from(2i32)])]
731 #[case::uniform(vec![Scalar::from(7i32), Scalar::from(7i32)])]
732 fn test_fixed_size_list_constant_append(#[case] elements: Vec<Scalar>) -> VortexResult<()> {
733 let scalar = Scalar::fixed_size_list(
734 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
735 elements,
736 Nullability::NonNullable,
737 );
738 assert_append_matches_canonical(ConstantArray::new(scalar, 4))
739 }
740
741 #[test]
742 fn test_null_fixed_size_list_constant_append() -> VortexResult<()> {
743 let dtype = DType::FixedSizeList(
744 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
745 2,
746 Nullability::Nullable,
747 );
748 assert_append_matches_canonical(ConstantArray::new(Scalar::null(dtype), 3))
749 }
750
751 #[test]
754 fn test_uniform_fixed_size_list_constant_append_keeps_elements_constant() -> VortexResult<()> {
755 let mut ctx = crate::array_session().create_execution_ctx();
756 let scalar = Scalar::fixed_size_list(
757 Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
758 vec![Scalar::from(7i32), Scalar::from(7i32)],
759 Nullability::NonNullable,
760 );
761 let array = ConstantArray::new(scalar.clone(), 1_000);
762
763 let mut builder = builder_with_capacity_in(
764 array.dtype(),
765 array.len(),
766 vortex_buffer::BufferAllocatorRef::static_ref(),
767 );
768 array
769 .into_array()
770 .append_to_builder(builder.as_mut(), &mut ctx)?;
771 let result = builder.finish();
772 assert_constant_values(&result, &scalar, 1_000, &mut ctx)?;
773
774 let elements = result.as_::<FixedSizeList>().elements().clone();
775 assert!(
776 elements
777 .as_::<Chunked>()
778 .iter_chunks()
779 .all(|chunk| chunk.is::<Constant>()),
780 "a uniform tile should have stayed constant-encoded",
781 );
782 Ok(())
783 }
784
785 #[test]
786 fn test_extension_constant_append() -> VortexResult<()> {
787 let scalar = Scalar::extension::<Date>(TimeUnit::Days, Scalar::from(Some(42i32)));
788 assert_append_matches_canonical(ConstantArray::new(scalar, 5))
789 }
790
791 #[test]
794 fn test_extension_constant_append_keeps_storage_constant() -> VortexResult<()> {
795 let mut ctx = crate::array_session().create_execution_ctx();
796 let scalar = Scalar::extension::<Date>(TimeUnit::Days, Scalar::from(Some(42i32)));
797 let array = ConstantArray::new(scalar.clone(), 1_000);
798
799 let mut builder = builder_with_capacity_in(
800 array.dtype(),
801 array.len(),
802 vortex_buffer::BufferAllocatorRef::static_ref(),
803 );
804 array
805 .into_array()
806 .append_to_builder(builder.as_mut(), &mut ctx)?;
807 let result = builder.finish();
808 assert_constant_values(&result, &scalar, 1_000, &mut ctx)?;
809
810 assert!(
811 result.as_::<Extension>().storage_array().is::<Constant>(),
812 "the storage should have stayed constant-encoded",
813 );
814 Ok(())
815 }
816}