Skip to main content

vortex_array/builders/
primitive.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::any::Any;
5use std::mem::MaybeUninit;
6
7use vortex_buffer::BufferMut;
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_ensure;
11use vortex_mask::Mask;
12
13use crate::ArrayRef;
14use crate::IntoArray;
15use crate::LEGACY_SESSION;
16use crate::VortexSessionExecute;
17use crate::arrays::PrimitiveArray;
18use crate::builders::ArrayBuilder;
19use crate::builders::DEFAULT_BUILDER_CAPACITY;
20use crate::builders::LazyBitBufferBuilder;
21use crate::canonical::Canonical;
22#[expect(deprecated)]
23use crate::canonical::ToCanonical as _;
24use crate::dtype::DType;
25use crate::dtype::NativePType;
26use crate::dtype::Nullability;
27use crate::scalar::Scalar;
28
29/// The builder for building a [`PrimitiveArray`], parametrized by the `PType`.
30pub struct PrimitiveBuilder<T> {
31    dtype: DType,
32    values: BufferMut<T>,
33    nulls: LazyBitBufferBuilder,
34}
35
36impl<T: NativePType> PrimitiveBuilder<T> {
37    /// Creates a new `PrimitiveBuilder` with a capacity of [`DEFAULT_BUILDER_CAPACITY`].
38    pub fn new(nullability: Nullability) -> Self {
39        Self::with_capacity(nullability, DEFAULT_BUILDER_CAPACITY)
40    }
41
42    /// Creates a new `PrimitiveBuilder` with the given `capacity`.
43    pub fn with_capacity(nullability: Nullability, capacity: usize) -> Self {
44        Self {
45            values: BufferMut::with_capacity(capacity),
46            nulls: LazyBitBufferBuilder::new(capacity),
47            dtype: DType::Primitive(T::PTYPE, nullability),
48        }
49    }
50
51    /// Appends a primitive `value` to the builder.
52    pub fn append_value(&mut self, value: T) {
53        self.values.push(value);
54        self.nulls.append_non_null();
55    }
56
57    /// Appends `n` copies of `value` as non-null entries, directly writing into the buffer.
58    pub fn append_n_values(&mut self, value: T, n: usize) {
59        self.values.push_n(value, n);
60        self.nulls.append_n_non_nulls(n);
61    }
62
63    /// Returns the raw primitive values in this builder as a slice.
64    pub fn values(&self) -> &[T] {
65        self.values.as_ref()
66    }
67
68    /// Returns the raw primitive values in this builder as a mutable slice.
69    pub fn values_mut(&mut self) -> &mut [T] {
70        self.values.as_mut()
71    }
72
73    /// Create a new handle to the next `len` uninitialized values in the builder.
74    ///
75    /// All reads/writes through the handle to the values buffer or the validity buffer will operate
76    /// on indices relative to the start of the range.
77    ///
78    /// # Panics
79    ///
80    /// Panics if `len` is 0 or if the current length of the builder plus `len` would exceed the
81    /// capacity of the builder's memory.
82    ///
83    /// ## Example
84    ///
85    /// ```
86    /// use std::mem::MaybeUninit;
87    /// use vortex_array::builders::{ArrayBuilder, PrimitiveBuilder};
88    /// use vortex_array::dtype::Nullability;
89    ///
90    /// // Create a new builder.
91    /// let mut builder: PrimitiveBuilder<i32> =
92    ///     PrimitiveBuilder::with_capacity(Nullability::NonNullable, 5);
93    ///
94    /// // Populate the values.
95    /// let mut uninit_range = builder.uninit_range(5);
96    /// uninit_range.copy_from_slice(0, &[0, 1, 2, 3, 4]);
97    ///
98    /// // SAFETY: We have initialized all 5 values in the range, and since the array builder is
99    /// // non-nullable, we don't need to set any null bits.
100    /// unsafe { uninit_range.finish(); }
101    ///
102    /// let built = builder.finish_into_primitive();
103    ///
104    /// assert_eq!(built.as_slice::<i32>(), &[0i32, 1, 2, 3, 4]);
105    /// ```
106    pub fn uninit_range(&mut self, len: usize) -> UninitRange<'_, T> {
107        assert_ne!(0, len, "cannot create an uninit range of length 0");
108
109        let current_len = self.values.len();
110        assert!(
111            current_len + len <= self.values.capacity(),
112            "uninit_range of len {len} exceeds builder with length {} and capacity {}",
113            current_len,
114            self.values.capacity()
115        );
116
117        UninitRange { len, builder: self }
118    }
119
120    /// Finishes the builder directly into a [`PrimitiveArray`].
121    pub fn finish_into_primitive(&mut self) -> PrimitiveArray {
122        let validity = self
123            .nulls
124            .finish_with_nullability(self.dtype().nullability());
125
126        PrimitiveArray::new(std::mem::take(&mut self.values).freeze(), validity)
127    }
128
129    /// Extends the primitive array with an iterator.
130    pub fn extend_with_iterator(&mut self, iter: impl IntoIterator<Item = T>, mask: &Mask) {
131        self.values.extend(iter);
132        self.nulls.append_validity_mask(mask);
133    }
134}
135
136impl<T: NativePType> ArrayBuilder for PrimitiveBuilder<T> {
137    fn as_any(&self) -> &dyn Any {
138        self
139    }
140
141    fn as_any_mut(&mut self) -> &mut dyn Any {
142        self
143    }
144
145    fn dtype(&self) -> &DType {
146        &self.dtype
147    }
148
149    fn len(&self) -> usize {
150        self.values.len()
151    }
152
153    fn append_zeros(&mut self, n: usize) {
154        self.values.push_n(T::default(), n);
155        self.nulls.append_n_non_nulls(n);
156    }
157
158    unsafe fn append_nulls_unchecked(&mut self, n: usize) {
159        self.values.push_n(T::default(), n);
160        self.nulls.append_n_nulls(n);
161    }
162
163    fn append_scalar(&mut self, scalar: &Scalar) -> VortexResult<()> {
164        vortex_ensure!(
165            scalar.dtype() == self.dtype(),
166            "PrimitiveBuilder expected scalar with dtype {}, got {}",
167            self.dtype(),
168            scalar.dtype()
169        );
170
171        if let Some(pv) = scalar.as_primitive().pvalue() {
172            self.append_value(pv.cast::<T>()?)
173        } else {
174            self.append_null()
175        }
176
177        Ok(())
178    }
179
180    unsafe fn extend_from_array_unchecked(&mut self, array: &ArrayRef) {
181        #[expect(deprecated)]
182        let array = array.to_primitive();
183
184        // This should be checked in `extend_from_array` but we can check it again.
185        debug_assert_eq!(
186            array.ptype(),
187            T::PTYPE,
188            "Cannot extend from array with different ptype"
189        );
190
191        self.values.extend_from_slice(array.as_slice::<T>());
192        self.nulls.append_validity_mask(
193            &array
194                .as_ref()
195                .validity()
196                .vortex_expect("validity_mask")
197                .execute_mask(
198                    array.as_ref().len(),
199                    &mut LEGACY_SESSION.create_execution_ctx(),
200                )
201                .vortex_expect("Failed to compute validity mask"),
202        );
203    }
204
205    fn reserve_exact(&mut self, additional: usize) {
206        self.values.reserve(additional);
207        self.nulls.reserve_exact(additional);
208    }
209
210    unsafe fn set_validity_unchecked(&mut self, validity: Mask) {
211        self.nulls = LazyBitBufferBuilder::from_validity_mask(validity);
212    }
213
214    fn finish(&mut self) -> ArrayRef {
215        self.finish_into_primitive().into_array()
216    }
217
218    fn finish_into_canonical(&mut self) -> Canonical {
219        Canonical::Primitive(self.finish_into_primitive())
220    }
221}
222
223/// A range of uninitialized values in the primitive builder that can be filled.
224pub struct UninitRange<'a, T> {
225    /// The length of the uninitialized range.
226    ///
227    /// This is guaranteed to be within the memory capacity of the builder.
228    len: usize,
229
230    /// A mutable reference to the builder.
231    ///
232    /// Since this is a mutable reference, we can guarantee that nothing else can modify the builder
233    /// while this `UninitRange` exists.
234    builder: &'a mut PrimitiveBuilder<T>,
235}
236
237impl<T> UninitRange<'_, T> {
238    /// Returns the length of this uninitialized range.
239    #[inline]
240    pub fn len(&self) -> usize {
241        self.len
242    }
243
244    /// Returns true if this range has zero length.
245    #[inline]
246    pub fn is_empty(&self) -> bool {
247        self.len == 0
248    }
249
250    /// Set a value at the given index within this range.
251    ///
252    /// # Panics
253    ///
254    /// Panics if the index is out of bounds.
255    #[inline]
256    pub fn set_value(&mut self, index: usize, value: T) {
257        assert!(index < self.len, "index out of bounds");
258        let spare = self.builder.values.spare_capacity_mut();
259        spare[index] = MaybeUninit::new(value);
260    }
261
262    /// Append a [`Mask`] to this builder's null buffer.
263    ///
264    /// # Panics
265    ///
266    /// Panics if the mask length is not equal to the the length of the current `UninitRange`.
267    ///
268    /// # Safety
269    ///
270    /// - The caller must ensure that they safely initialize `mask.len()` primitive values via
271    ///   [`UninitRange::copy_from_slice`].
272    /// - The caller must also ensure that they only call this method once.
273    pub unsafe fn append_mask(&mut self, mask: &Mask) {
274        assert_eq!(
275            mask.len(),
276            self.len,
277            "Tried to append a mask to an `UninitRange` that was beyond the allowed range"
278        );
279
280        // TODO(connor): Ideally, we would call this function `set_mask` and directly set all of the
281        // bits (so that we can call this multiple times), but the underlying `BooleanBuffer` does
282        // not have an easy way to do this correctly.
283
284        self.builder.nulls.append_validity_mask(mask);
285    }
286
287    /// Set a validity bit at the given index.
288    ///
289    /// The index is relative to the start of this range (not relative to the values already in the
290    /// builder).
291    ///
292    /// Note that this will have no effect if the builder is non-nullable.
293    pub fn set_validity_bit(&mut self, index: usize, v: bool) {
294        assert!(index < self.len, "set_bit index out of bounds");
295        // Note that this won't panic because we can only create an `UninitRange` within the
296        // capacity of the builder (it will not automatically resize).
297        let absolute_index = self.builder.values.len() + index;
298        self.builder.nulls.set_bit(absolute_index, v);
299    }
300
301    /// Set values from an initialized range.
302    ///
303    /// Note that the input `offset` should be an offset relative to the local `UninitRange`, not
304    /// the entire `PrimitiveBuilder`.
305    pub fn copy_from_slice(&mut self, local_offset: usize, src: &[T])
306    where
307        T: Copy,
308    {
309        debug_assert!(
310            local_offset + src.len() <= self.len,
311            "tried to copy a slice into a `UninitRange` past its boundary"
312        );
313
314        // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout.
315        let uninit_src: &[MaybeUninit<T>] = unsafe { std::mem::transmute(src) };
316
317        // Note: spare_capacity_mut() returns the spare capacity starting from the current length,
318        // so we just use local_offset directly.
319        let dst =
320            &mut self.builder.values.spare_capacity_mut()[local_offset..local_offset + src.len()];
321        dst.copy_from_slice(uninit_src);
322    }
323
324    /// Get a mutable slice of uninitialized memory at the specified offset within this range.
325    ///
326    /// Note that the offsets are relative to this local range, not to the values already in the
327    /// builder.
328    ///
329    /// # Safety
330    ///
331    /// The caller must ensure that they properly initialize the returned memory before calling
332    /// `finish()` on this range.
333    ///
334    /// # Panics
335    ///
336    /// Panics if `offset + len` exceeds the range bounds.
337    pub unsafe fn slice_uninit_mut(&mut self, offset: usize, len: usize) -> &mut [MaybeUninit<T>] {
338        assert!(
339            offset + len <= self.len,
340            "slice_uninit_mut: offset {} + len {} exceeds range length {}",
341            offset,
342            len,
343            self.len
344        );
345        &mut self.builder.values.spare_capacity_mut()[offset..offset + len]
346    }
347
348    /// Finish building this range, marking it as initialized and advancing the length of the
349    /// underlying values buffer.
350    ///
351    /// # Safety
352    ///
353    /// The caller must ensure that they have safely initialized all `len` values via
354    /// [`copy_from_slice()`] or [`set_value()`], as well as correctly set all of the null bits via
355    /// [`set_validity_bit()`] or [`append_mask()`] if the builder is nullable.
356    ///
357    /// [`copy_from_slice()`]: UninitRange::copy_from_slice
358    /// [`set_value()`]: UninitRange::set_value
359    /// [`set_validity_bit()`]: UninitRange::set_validity_bit
360    /// [`append_mask()`]: UninitRange::append_mask
361    pub unsafe fn finish(self) {
362        // SAFETY: constructor enforces that current length + len does not exceed the capacity of the array.
363        let new_len = self.builder.values.len() + self.len;
364        unsafe { self.builder.values.set_len(new_len) };
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use vortex_error::VortexExpect;
371
372    use super::*;
373    use crate::assert_arrays_eq;
374
375    /// REGRESSION TEST: This test verifies that multiple sequential ranges have correct offsets.
376    ///
377    /// This would have caught the `Deref` bug where it always returned from the start of the
378    /// buffer.
379    #[test]
380    fn test_multiple_uninit_ranges_correct_offsets() {
381        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::NonNullable, 10);
382
383        // First range.
384        let mut range1 = builder.uninit_range(3);
385        range1.copy_from_slice(0, &[1, 2, 3]);
386
387        // SAFETY: We initialized all 3 values.
388        unsafe {
389            range1.finish();
390        }
391
392        // Verify the builder now has these values.
393        assert_eq!(builder.values(), &[1, 2, 3]);
394
395        // Second range - this would fail with the old Deref implementation.
396        let mut range2 = builder.uninit_range(2);
397
398        // Set values using copy_from_slice.
399        range2.copy_from_slice(0, &[4, 5]);
400
401        // SAFETY: We initialized both values.
402        unsafe {
403            range2.finish();
404        }
405
406        // Verify the builder now has all 5 values.
407        assert_eq!(builder.values(), &[1, 2, 3, 4, 5]);
408
409        let array = builder.finish_into_primitive();
410        assert_arrays_eq!(array, PrimitiveArray::from_iter([1i32, 2, 3, 4, 5]));
411    }
412
413    /// REGRESSION TEST: This test verifies that `append_mask` was correctly moved from
414    /// `PrimitiveBuilder` to `UninitRange`.
415    ///
416    /// The old API had `append_mask` on the builder, which was confusing when used with ranges.
417    /// This test ensures the new API works correctly.
418    #[test]
419    fn test_append_mask_on_uninit_range() {
420        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::Nullable, 5);
421        let mut range = builder.uninit_range(3);
422
423        // Create a mask for 3 values.
424        let mask = Mask::from_iter([true, false, true]);
425
426        // SAFETY: We're about to initialize the values.
427        unsafe {
428            range.append_mask(&mask);
429        }
430
431        // Initialize the values.
432        range.copy_from_slice(0, &[10, 20, 30]);
433
434        // SAFETY: We've initialized all values and set the mask.
435        unsafe {
436            range.finish();
437        }
438
439        let array = builder.finish_into_primitive();
440        assert_eq!(array.len(), 3);
441        // Check validity using scalar_at - nulls will return is_null() = true.
442        assert!(
443            !array
444                .execute_scalar(0, &mut LEGACY_SESSION.create_execution_ctx())
445                .unwrap()
446                .is_null()
447        );
448        assert!(
449            array
450                .execute_scalar(1, &mut LEGACY_SESSION.create_execution_ctx())
451                .unwrap()
452                .is_null()
453        );
454        assert!(
455            !array
456                .execute_scalar(2, &mut LEGACY_SESSION.create_execution_ctx())
457                .unwrap()
458                .is_null()
459        );
460    }
461
462    /// REGRESSION TEST: This test verifies that `append_mask` validates the mask length.
463    ///
464    /// This ensures that masks can only be appended if they match the range length.
465    #[test]
466    #[should_panic(
467        expected = "Tried to append a mask to an `UninitRange` that was beyond the allowed range"
468    )]
469    fn test_append_mask_wrong_length_panics() {
470        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::Nullable, 10);
471        let mut range = builder.uninit_range(5);
472
473        // Try to append a mask with wrong length (3 instead of 5).
474        let wrong_mask = Mask::from_iter([true, false, true]);
475
476        // SAFETY: This is expected to panic due to length mismatch.
477        unsafe {
478            range.append_mask(&wrong_mask);
479        }
480    }
481
482    /// Test that `copy_from_slice` works correctly with different offsets.
483    ///
484    /// This verifies the new simplified API without the redundant `len` parameter.
485    #[test]
486    fn test_copy_from_slice_with_offsets() {
487        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::NonNullable, 10);
488        let mut range = builder.uninit_range(6);
489
490        // Copy to different offsets.
491        range.copy_from_slice(0, &[1, 2]);
492        range.copy_from_slice(2, &[3, 4]);
493        range.copy_from_slice(4, &[5, 6]);
494
495        // SAFETY: We've initialized all 6 values.
496        unsafe {
497            range.finish();
498        }
499
500        let array = builder.finish_into_primitive();
501        assert_arrays_eq!(array, PrimitiveArray::from_iter([1i32, 2, 3, 4, 5, 6]));
502    }
503
504    /// Test that `set_bit` uses relative indexing within the range.
505    ///
506    /// Note: `set_bit` requires the null buffer to already be initialized, so we first
507    /// use `append_mask` to set up the buffer, then demonstrate that `set_bit` can
508    /// modify individual bits with relative indexing.
509    #[test]
510    fn test_set_bit_relative_indexing() {
511        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::Nullable, 10);
512
513        // First add some values to the builder.
514        builder.append_value(100);
515        builder.append_value(200);
516
517        // Create a range for new values.
518        let mut range = builder.uninit_range(3);
519
520        // Use append_mask to initialize the validity buffer for this range.
521        let initial_mask = Mask::from_iter([false, false, false]);
522        // SAFETY: We're about to initialize the values.
523        unsafe {
524            range.append_mask(&initial_mask);
525        }
526
527        // Now we can use set_bit to modify individual bits with relative indexing.
528        range.set_validity_bit(0, true); // Change first bit to valid
529        range.set_validity_bit(2, true); // Change third bit to valid
530        // Leave middle bit as false (null)
531
532        // Initialize the values.
533        range.copy_from_slice(0, &[10, 20, 30]);
534
535        // SAFETY: We've initialized all 3 values and set their validity.
536        unsafe {
537            range.finish();
538        }
539
540        let array = builder.finish_into_primitive();
541
542        // Verify the total length and values.
543        assert_eq!(array.len(), 5);
544        assert_eq!(array.as_slice::<i32>(), &[100, 200, 10, 20, 30]);
545
546        // Check validity - the first two should be valid (from append_value).
547        assert!(
548            !array
549                .execute_scalar(0, &mut LEGACY_SESSION.create_execution_ctx())
550                .unwrap()
551                .is_null()
552        ); // initial value 100
553        assert!(
554            !array
555                .execute_scalar(1, &mut LEGACY_SESSION.create_execution_ctx())
556                .unwrap()
557                .is_null()
558        ); // initial value 200
559
560        // Check the range items with modified validity.
561        assert!(
562            !array
563                .execute_scalar(2, &mut LEGACY_SESSION.create_execution_ctx())
564                .unwrap()
565                .is_null()
566        ); // range index 0 - set to valid
567        assert!(
568            array
569                .execute_scalar(3, &mut LEGACY_SESSION.create_execution_ctx())
570                .unwrap()
571                .is_null()
572        ); // range index 1 - left as null
573        assert!(
574            !array
575                .execute_scalar(4, &mut LEGACY_SESSION.create_execution_ctx())
576                .unwrap()
577                .is_null()
578        ); // range index 2 - set to valid
579    }
580
581    /// Test that creating a zero-length uninit range panics.
582    #[test]
583    #[should_panic(expected = "cannot create an uninit range of length 0")]
584    fn test_zero_length_uninit_range_panics() {
585        let mut builder = PrimitiveBuilder::<i32>::new(Nullability::NonNullable);
586        let _range = builder.uninit_range(0);
587    }
588
589    /// Test that creating an uninit range exceeding capacity panics.
590    #[test]
591    #[should_panic(
592        expected = "uninit_range of len 10 exceeds builder with length 0 and capacity 6"
593    )]
594    fn test_uninit_range_exceeds_capacity_panics() {
595        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::NonNullable, 5);
596        let _range = builder.uninit_range(10);
597    }
598
599    /// Test that `copy_from_slice` debug asserts on out-of-bounds access.
600    ///
601    /// Note: This only panics in debug mode due to `debug_assert!`.
602    #[test]
603    #[cfg(debug_assertions)]
604    #[should_panic(expected = "tried to copy a slice into a `UninitRange` past its boundary")]
605    fn test_copy_from_slice_out_of_bounds() {
606        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::NonNullable, 10);
607        let mut range = builder.uninit_range(3);
608
609        // Try to copy 3 elements starting at offset 1 (would need 4 slots total).
610        range.copy_from_slice(1, &[1, 2, 3]);
611    }
612
613    /// Test that the unsafe contract of `finish` is documented and works correctly.
614    ///
615    /// This test demonstrates proper usage of the unsafe `finish` method.
616    #[test]
617    fn test_finish_unsafe_contract() {
618        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::Nullable, 5);
619        let mut range = builder.uninit_range(3);
620
621        // Set validity mask.
622        let mask = Mask::from_iter([true, true, false]);
623        // SAFETY: We're about to initialize the matching number of values.
624        unsafe {
625            range.append_mask(&mask);
626        }
627
628        // Initialize all values.
629        range.copy_from_slice(0, &[10, 20, 30]);
630
631        // SAFETY: We have initialized all 3 values and set their validity.
632        unsafe {
633            range.finish();
634        }
635
636        let array = builder.finish_into_primitive();
637        assert_eq!(array.len(), 3);
638        assert_eq!(array.as_slice::<i32>(), &[10, 20, 30]);
639    }
640
641    #[test]
642    fn test_append_scalar() {
643        use crate::dtype::DType;
644        use crate::scalar::Scalar;
645
646        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::Nullable, 10);
647
648        // Test appending a valid primitive value.
649        let scalar1 = Scalar::primitive(42i32, Nullability::Nullable);
650        builder.append_scalar(&scalar1).unwrap();
651
652        // Test appending another value.
653        let scalar2 = Scalar::primitive(84i32, Nullability::Nullable);
654        builder.append_scalar(&scalar2).unwrap();
655
656        // Test appending null value.
657        let null_scalar = Scalar::null(DType::Primitive(
658            crate::dtype::PType::I32,
659            Nullability::Nullable,
660        ));
661        builder.append_scalar(&null_scalar).unwrap();
662
663        let array = builder.finish_into_primitive();
664        assert_eq!(array.len(), 3);
665
666        // Check actual values.
667        let values = array.as_slice::<i32>();
668        assert_eq!(values[0], 42);
669        assert_eq!(values[1], 84);
670        // values[2] might be any value since it's null.
671
672        // Check validity - first two should be valid, third should be null.
673        let mut ctx = LEGACY_SESSION.create_execution_ctx();
674        assert!(
675            array
676                .validity()
677                .vortex_expect("primitive validity should be derivable")
678                .execute_is_valid(0, &mut ctx)
679                .unwrap()
680        );
681        assert!(
682            array
683                .validity()
684                .vortex_expect("primitive validity should be derivable")
685                .execute_is_valid(1, &mut ctx)
686                .unwrap()
687        );
688        assert!(
689            !array
690                .validity()
691                .vortex_expect("primitive validity should be derivable")
692                .execute_is_valid(2, &mut ctx)
693                .unwrap()
694        );
695
696        // Test wrong dtype error.
697        let mut builder = PrimitiveBuilder::<i32>::with_capacity(Nullability::NonNullable, 10);
698        let wrong_scalar = Scalar::from(true);
699        assert!(builder.append_scalar(&wrong_scalar).is_err());
700    }
701}