Skip to main content

reifydb_core/value/column/
builder.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{fmt::Debug, marker::PhantomData, sync::Arc};
5
6use arrow_array::{
7	Array, ArrayRef, ArrowPrimitiveType, FixedSizeBinaryArray, GenericByteArray, PrimitiveArray,
8	builder::{
9		ArrayBuilder, BooleanBuilder, FixedSizeBinaryBuilder, GenericByteBuilder, LargeBinaryBuilder,
10		LargeStringBuilder, NullBuilder, PrimitiveBuilder,
11	},
12	types::{
13		ByteArrayType, Date32Type, Decimal128Type, Decimal256Type, Float32Type, Float64Type, Int8Type,
14		Int16Type, Int32Type, Int64Type, IntervalMonthDayNanoType, Time64NanosecondType,
15		TimestampNanosecondType, UInt8Type, UInt16Type, UInt32Type, UInt64Type,
16	},
17};
18use arrow_buffer::{Buffer, NullBuffer, bit_util, i256};
19use arrow_schema::FieldRef;
20use reifydb_value::{
21	Result,
22	value::{
23		column_view::{ColumnView, ViewData},
24		constraint::{bytes::MaxBytes, precision::Precision, scale::Scale},
25		container::{
26			decimal_array::{self, DECIMAL128_MAX_PRECISION, DecimalArray, DecimalView, decimal_at},
27			dictionary_array::DICTIONARY_ENTRY_WIDTH,
28			temporal_array::datetime_timezone,
29			uuid_array::UUID_WIDTH,
30			varlen_array,
31			wide_int_array::WideInt,
32		},
33		decimal::{Decimal, unscaled},
34		dictionary::DictionaryId,
35		value_type::{
36			ValueType,
37			field::{FieldType, named},
38		},
39	},
40};
41
42use crate::{internal_err, internal_error, value::column::push::Push};
43
44#[derive(Debug)]
45pub enum DecimalBuilder {
46	Decimal128 {
47		builder: PrimitiveBuilder<Decimal128Type>,
48		precision: Precision,
49		scale: Scale,
50	},
51	Decimal256 {
52		builder: PrimitiveBuilder<Decimal256Type>,
53		precision: Precision,
54		scale: Scale,
55	},
56}
57
58impl DecimalBuilder {
59	pub fn with_capacity(precision: Precision, scale: Scale, capacity: usize) -> Self {
60		let data_type = decimal_array::data_type(precision, scale);
61		if precision.value() <= DECIMAL128_MAX_PRECISION {
62			DecimalBuilder::Decimal128 {
63				builder: PrimitiveBuilder::with_capacity(capacity).with_data_type(data_type),
64				precision,
65				scale,
66			}
67		} else {
68			DecimalBuilder::Decimal256 {
69				builder: PrimitiveBuilder::with_capacity(capacity).with_data_type(data_type),
70				precision,
71				scale,
72			}
73		}
74	}
75
76	pub(crate) fn from_array(array: DecimalArray) -> Self {
77		let (precision, scale) = (array.precision(), array.scale());
78		match array {
79			DecimalArray::Decimal128(array) => DecimalBuilder::Decimal128 {
80				builder: primitive_builder(array),
81				precision,
82				scale,
83			},
84			DecimalArray::Decimal256(array) => DecimalBuilder::Decimal256 {
85				builder: primitive_builder(array),
86				precision,
87				scale,
88			},
89		}
90	}
91
92	pub fn precision(&self) -> Precision {
93		match self {
94			DecimalBuilder::Decimal128 {
95				precision,
96				..
97			}
98			| DecimalBuilder::Decimal256 {
99				precision,
100				..
101			} => *precision,
102		}
103	}
104
105	pub fn scale(&self) -> Scale {
106		match self {
107			DecimalBuilder::Decimal128 {
108				scale,
109				..
110			}
111			| DecimalBuilder::Decimal256 {
112				scale,
113				..
114			} => *scale,
115		}
116	}
117
118	pub fn len(&self) -> usize {
119		match self {
120			DecimalBuilder::Decimal128 {
121				builder,
122				..
123			} => builder.len(),
124			DecimalBuilder::Decimal256 {
125				builder,
126				..
127			} => builder.len(),
128		}
129	}
130
131	pub fn is_empty(&self) -> bool {
132		self.len() == 0
133	}
134
135	pub fn finish(&mut self) -> DecimalArray {
136		match self {
137			DecimalBuilder::Decimal128 {
138				builder,
139				..
140			} => DecimalArray::Decimal128(builder.finish()),
141			DecimalBuilder::Decimal256 {
142				builder,
143				..
144			} => DecimalArray::Decimal256(builder.finish()),
145		}
146	}
147
148	pub(crate) fn append_null(&mut self) {
149		match self {
150			DecimalBuilder::Decimal128 {
151				builder,
152				..
153			} => builder.append_null(),
154			DecimalBuilder::Decimal256 {
155				builder,
156				..
157			} => builder.append_null(),
158		}
159	}
160
161	pub fn push(&mut self, value: &Decimal) {
162		if let Some(fitted) = value.fits(self.precision().value(), self.scale().value()) {
163			return self.append_fitting(fitted.unscaled());
164		}
165		let fitted = self.widen_for(value);
166		self.append_fitting(fitted.unscaled());
167	}
168
169	pub(crate) fn append_array(&mut self, array: DecimalView<'_>) {
170		match (self, array) {
171			(
172				DecimalBuilder::Decimal128 {
173					builder,
174					precision,
175					scale,
176				},
177				DecimalView::Decimal128(array),
178			) if array.precision() == precision.value() && array.scale() as u8 == scale.value() => builder.append_array(array),
179			(
180				DecimalBuilder::Decimal256 {
181					builder,
182					precision,
183					scale,
184				},
185				DecimalView::Decimal256(array),
186			) if array.precision() == precision.value() && array.scale() as u8 == scale.value() => builder.append_array(array),
187			(this, array) => {
188				let nulls = decimal_nulls(array);
189				for index in 0..array.len() {
190					if nulls.as_ref().is_some_and(|nulls| nulls.is_null(index)) {
191						this.append_null();
192						continue;
193					}
194					let value = decimal_at(array, index).expect("index is below the array length");
195					this.push(&value);
196				}
197			}
198		}
199	}
200
201	fn append_fitting(&mut self, unscaled: i256) {
202		match self {
203			DecimalBuilder::Decimal128 {
204				builder,
205				..
206			} => builder.append_value(unscaled.to_i128().expect("a value within precision 38 fits i128")),
207			DecimalBuilder::Decimal256 {
208				builder,
209				..
210			} => builder.append_value(unscaled),
211		}
212	}
213
214	fn widen_for(&mut self, value: &Decimal) -> Decimal {
215		let (precision, scale) = (self.precision().value(), self.scale().value());
216		let finished = self.finish();
217		let nulls = decimal_nulls(&finished);
218		let existing: Vec<Decimal> = finished
219			.unscaled_values()
220			.into_iter()
221			.map(|unscaled| {
222				Decimal::from_parts(unscaled, scale)
223					.expect("a decimal builder holds only valid decimals")
224			})
225			.collect();
226		let integer_digits = |decimal: &Decimal| decimal.digits().saturating_sub(decimal.scale());
227		let needed = existing.iter().chain([value]).map(integer_digits).max().unwrap_or(0);
228		let wide_scale = scale.max(value.scale()).min(unscaled::MAX_DIGITS - needed);
229		let round = |decimal: &Decimal| {
230			decimal.round_to_scale(wide_scale)
231				.expect("rounding never adds a whole digit past the widest value")
232		};
233		let rounded: Vec<Decimal> = existing.iter().map(round).collect();
234		let fitted = round(value);
235		let wide_precision = ((precision - scale).max(needed) + wide_scale).min(unscaled::MAX_DIGITS);
236		let mut widened = DecimalBuilder::with_capacity(
237			Precision::new(wide_precision),
238			Scale::new(wide_scale),
239			rounded.len() + 1,
240		);
241		for (index, decimal) in rounded.into_iter().enumerate() {
242			match nulls.as_ref().is_some_and(|nulls| nulls.is_null(index)) {
243				true => widened.append_null(),
244				false => widened.append_fitting(decimal.unscaled()),
245			}
246		}
247		*self = widened;
248		fitted
249	}
250}
251
252fn decimal_nulls<'a>(array: impl Into<DecimalView<'a>>) -> Option<NullBuffer> {
253	array.into().array().logical_nulls()
254}
255
256#[derive(Debug)]
257pub struct WideBuilder<T: WideInt> {
258	builder: FixedSizeBinaryBuilder,
259	marker: PhantomData<T>,
260}
261
262impl<T: WideInt> WideBuilder<T> {
263	pub(crate) fn with_capacity(capacity: usize) -> Self {
264		Self {
265			builder: FixedSizeBinaryBuilder::with_capacity(capacity, T::WIDTH as i32),
266			marker: PhantomData,
267		}
268	}
269
270	pub(crate) fn from_array(array: &FixedSizeBinaryArray) -> Self {
271		Self {
272			builder: fixed_size_builder(array),
273			marker: PhantomData,
274		}
275	}
276
277	pub(crate) fn append_null(&mut self) {
278		let mut row = [0u8; 16];
279		T::default().to_ordered(&mut row);
280		let null_row = FixedSizeBinaryArray::new(
281			T::WIDTH as i32,
282			Buffer::from(row[..T::WIDTH].to_vec()),
283			Some(NullBuffer::new_null(1)),
284		);
285		append_fixed_array(&mut self.builder, &null_row)
286			.expect("a null row of the builder width always appends");
287	}
288
289	pub(crate) fn append_value(&mut self, value: T) {
290		let mut row = [0u8; 16];
291		value.to_ordered(&mut row);
292		append_fixed(&mut self.builder, &row);
293	}
294
295	pub(crate) fn append_array(&mut self, array: &FixedSizeBinaryArray) -> Result<()> {
296		append_fixed_array(&mut self.builder, array)
297	}
298
299	pub(crate) fn len(&self) -> usize {
300		self.builder.len()
301	}
302
303	pub(crate) fn finish(&mut self) -> FixedSizeBinaryArray {
304		self.builder.finish()
305	}
306}
307
308#[derive(Debug)]
309pub struct ColumnBuilder {
310	pub(crate) inner: TypedBuilder,
311	pub(crate) optional: bool,
312}
313
314#[derive(Debug)]
315pub enum TypedBuilder {
316	Bool(BooleanBuilder),
317	Int1(PrimitiveBuilder<Int8Type>),
318	Int2(PrimitiveBuilder<Int16Type>),
319	Int4(PrimitiveBuilder<Int32Type>),
320	Int8(PrimitiveBuilder<Int64Type>),
321	Int16(WideBuilder<i128>),
322	Uint1(PrimitiveBuilder<UInt8Type>),
323	Uint2(PrimitiveBuilder<UInt16Type>),
324	Uint4(PrimitiveBuilder<UInt32Type>),
325	Uint8(PrimitiveBuilder<UInt64Type>),
326	Uint16(WideBuilder<u128>),
327	Float4(PrimitiveBuilder<Float32Type>),
328	Float8(PrimitiveBuilder<Float64Type>),
329	Date(PrimitiveBuilder<Date32Type>),
330	DateTime(PrimitiveBuilder<TimestampNanosecondType>),
331	Time(PrimitiveBuilder<Time64NanosecondType>),
332	Duration(PrimitiveBuilder<IntervalMonthDayNanoType>),
333	IdentityId(FixedSizeBinaryBuilder),
334	Uuid4(FixedSizeBinaryBuilder),
335	Uuid7(FixedSizeBinaryBuilder),
336	DictionaryId {
337		builder: FixedSizeBinaryBuilder,
338		dictionary_id: Option<DictionaryId>,
339	},
340	Utf8 {
341		builder: LargeStringBuilder,
342		max_bytes: MaxBytes,
343	},
344	Blob {
345		builder: LargeBinaryBuilder,
346		max_bytes: MaxBytes,
347	},
348	Decimal(DecimalBuilder),
349	Any {
350		builder: LargeBinaryBuilder,
351		declared_type: Option<ValueType>,
352	},
353	Digest {
354		builder: LargeBinaryBuilder,
355		inner: ValueType,
356		accuracy: u32,
357	},
358	None(NullBuilder),
359}
360
361impl ColumnBuilder {
362	pub fn with_capacity(target: ValueType, capacity: usize) -> Self {
363		let inner = match target {
364			ValueType::Option(inner) => {
365				return ColumnBuilder {
366					optional: true,
367					..ColumnBuilder::with_capacity(*inner, capacity)
368				};
369			}
370			ValueType::Boolean => TypedBuilder::Bool(BooleanBuilder::with_capacity(capacity)),
371			ValueType::Int1 => TypedBuilder::Int1(PrimitiveBuilder::with_capacity(capacity)),
372			ValueType::Int2 => TypedBuilder::Int2(PrimitiveBuilder::with_capacity(capacity)),
373			ValueType::Int4 => TypedBuilder::Int4(PrimitiveBuilder::with_capacity(capacity)),
374			ValueType::Int8 => TypedBuilder::Int8(PrimitiveBuilder::with_capacity(capacity)),
375			ValueType::Int16 => TypedBuilder::Int16(WideBuilder::with_capacity(capacity)),
376			ValueType::Uint1 => TypedBuilder::Uint1(PrimitiveBuilder::with_capacity(capacity)),
377			ValueType::Uint2 => TypedBuilder::Uint2(PrimitiveBuilder::with_capacity(capacity)),
378			ValueType::Uint4 => TypedBuilder::Uint4(PrimitiveBuilder::with_capacity(capacity)),
379			ValueType::Uint8 => TypedBuilder::Uint8(PrimitiveBuilder::with_capacity(capacity)),
380			ValueType::Uint16 => TypedBuilder::Uint16(WideBuilder::with_capacity(capacity)),
381			ValueType::Float4 => TypedBuilder::Float4(PrimitiveBuilder::with_capacity(capacity)),
382			ValueType::Float8 => TypedBuilder::Float8(PrimitiveBuilder::with_capacity(capacity)),
383			ValueType::Date => TypedBuilder::Date(PrimitiveBuilder::with_capacity(capacity)),
384			ValueType::DateTime => TypedBuilder::DateTime(
385				PrimitiveBuilder::<TimestampNanosecondType>::with_capacity(capacity)
386					.with_timezone(datetime_timezone()),
387			),
388			ValueType::Time => TypedBuilder::Time(PrimitiveBuilder::with_capacity(capacity)),
389			ValueType::Duration => TypedBuilder::Duration(PrimitiveBuilder::with_capacity(capacity)),
390			ValueType::IdentityId => TypedBuilder::IdentityId(FixedSizeBinaryBuilder::with_capacity(
391				capacity,
392				UUID_WIDTH as i32,
393			)),
394			ValueType::Uuid4 => {
395				TypedBuilder::Uuid4(FixedSizeBinaryBuilder::with_capacity(capacity, UUID_WIDTH as i32))
396			}
397			ValueType::Uuid7 => {
398				TypedBuilder::Uuid7(FixedSizeBinaryBuilder::with_capacity(capacity, UUID_WIDTH as i32))
399			}
400			ValueType::DictionaryId => TypedBuilder::DictionaryId {
401				builder: FixedSizeBinaryBuilder::with_capacity(capacity, DICTIONARY_ENTRY_WIDTH as i32),
402				dictionary_id: None,
403			},
404			ValueType::Utf8 => TypedBuilder::Utf8 {
405				builder: LargeStringBuilder::with_capacity(capacity, capacity * 16),
406				max_bytes: MaxBytes::MAX,
407			},
408			ValueType::Blob => TypedBuilder::Blob {
409				builder: LargeBinaryBuilder::with_capacity(capacity, capacity * 32),
410				max_bytes: MaxBytes::MAX,
411			},
412			ValueType::Decimal {
413				precision,
414				scale,
415			} => TypedBuilder::Decimal(DecimalBuilder::with_capacity(precision, scale, capacity)),
416			ValueType::Any | ValueType::Tuple(_) => TypedBuilder::Any {
417				builder: LargeBinaryBuilder::with_capacity(capacity, 0),
418				declared_type: None,
419			},
420			declared @ (ValueType::List(_) | ValueType::Record(_)) => TypedBuilder::Any {
421				builder: LargeBinaryBuilder::with_capacity(capacity, 0),
422				declared_type: Some(declared),
423			},
424			ValueType::Digest {
425				inner,
426				accuracy,
427			} => TypedBuilder::Digest {
428				builder: LargeBinaryBuilder::with_capacity(capacity, 0),
429				inner: *inner,
430				accuracy,
431			},
432		};
433		ColumnBuilder {
434			inner,
435			optional: false,
436		}
437	}
438
439	pub fn like(view: &ColumnView, capacity: usize) -> Self {
440		if view.is_none() {
441			return ColumnBuilder::untyped_none();
442		}
443		let mut builder = ColumnBuilder::with_capacity(view.get_type(), capacity);
444		builder.copy_details(&view.data);
445		builder
446	}
447
448	pub fn from_view(view: &ColumnView) -> Self {
449		ColumnBuilder {
450			inner: TypedBuilder::from_view(&view.data),
451			optional: view.is_nullable() || view.none_count() > 0,
452		}
453	}
454
455	pub(crate) fn untyped_none() -> Self {
456		ColumnBuilder {
457			inner: TypedBuilder::None(NullBuilder::new()),
458			optional: true,
459		}
460	}
461
462	pub fn inner(&self) -> &TypedBuilder {
463		&self.inner
464	}
465
466	pub fn push<T>(&mut self, value: T)
467	where
468		ColumnBuilder: Push<T>,
469		T: Debug,
470	{
471		<Self as Push<T>>::push(self, value)
472	}
473
474	pub fn set_dictionary_id(&mut self, id: DictionaryId) {
475		if let TypedBuilder::DictionaryId {
476			dictionary_id,
477			..
478		} = &mut self.inner
479		{
480			*dictionary_id = Some(id);
481		}
482	}
483
484	pub fn dictionary_id(&self) -> Option<DictionaryId> {
485		match &self.inner {
486			TypedBuilder::DictionaryId {
487				dictionary_id,
488				..
489			} => *dictionary_id,
490			_ => None,
491		}
492	}
493
494	pub fn extend(&mut self, other: &ColumnView) -> Result<()> {
495		match (self.is_untyped_none(), other.is_untyped_none()) {
496			(true, true) => {
497				let len = self.len() + other.len();
498				*self = ColumnBuilder::untyped_none();
499				self.append_nones(len);
500				return Ok(());
501			}
502			(true, false) => {
503				let len = self.len();
504				*self = ColumnBuilder::like(other, len + other.len());
505				self.append_nones(len);
506				self.optional = true;
507			}
508			(false, true) => {
509				self.append_nones(other.len());
510				return Ok(());
511			}
512			(false, false) => {}
513		}
514		let other_optional = other.is_nullable() || other.none_count() > 0;
515		if self.optional && other_optional && !self.same_type(other) {
516			return internal_err!("column type mismatch in Option extend");
517		}
518		self.append_view(other)?;
519		self.optional |= other_optional;
520		Ok(())
521	}
522
523	pub fn append_values(&mut self, other: &ColumnView) -> Result<()> {
524		let nones = other.none_count();
525		if nones == other.len() {
526			self.append_nones(other.len());
527			return Ok(());
528		}
529		self.append_view(other)?;
530		self.optional |= nones > 0;
531		Ok(())
532	}
533
534	fn append_nones(&mut self, count: usize) {
535		for _ in 0..count {
536			self.push_none();
537		}
538	}
539
540	fn same_type(&self, other: &ColumnView) -> bool {
541		let same_family =
542			matches!((&self.inner, &other.data), (TypedBuilder::Decimal(_), ViewData::Decimal(_)));
543		same_family || self.inner.get_type() == other.base_type()
544	}
545
546	fn is_untyped_none(&self) -> bool {
547		match &self.inner {
548			TypedBuilder::None(_) => true,
549			TypedBuilder::Any {
550				builder,
551				declared_type: None,
552			} if self.optional => match builder.validity_slice() {
553				Some(bits) => (0..builder.len()).all(|index| !bit_util::get_bit(bits, index)),
554				None => builder.is_empty(),
555			},
556			_ => false,
557		}
558	}
559
560	fn copy_details(&mut self, data: &ViewData) {
561		match (data, &mut self.inner) {
562			(
563				ViewData::Utf8 {
564					max_bytes: source,
565					..
566				},
567				TypedBuilder::Utf8 {
568					max_bytes: target,
569					..
570				},
571			)
572			| (
573				ViewData::Blob {
574					max_bytes: source,
575					..
576				},
577				TypedBuilder::Blob {
578					max_bytes: target,
579					..
580				},
581			) => *target = *source,
582			(
583				ViewData::DictionaryId {
584					dictionary_id: Some(source),
585					..
586				},
587				TypedBuilder::DictionaryId {
588					dictionary_id: target,
589					..
590				},
591			) => *target = Some(*source),
592			_ => {}
593		}
594	}
595
596	fn append_view(&mut self, other: &ColumnView) -> Result<()> {
597		match (&mut self.inner, &other.data) {
598			(TypedBuilder::Bool(l), ViewData::Bool(r)) => l.append_array(r),
599			(TypedBuilder::Int1(l), ViewData::Int1(r)) => l.append_array(r),
600			(TypedBuilder::Int2(l), ViewData::Int2(r)) => l.append_array(r),
601			(TypedBuilder::Int4(l), ViewData::Int4(r)) => l.append_array(r),
602			(TypedBuilder::Int8(l), ViewData::Int8(r)) => l.append_array(r),
603			(TypedBuilder::Int16(l), ViewData::Int16(r)) => l.append_array(r)?,
604			(TypedBuilder::Uint1(l), ViewData::Uint1(r)) => l.append_array(r),
605			(TypedBuilder::Uint2(l), ViewData::Uint2(r)) => l.append_array(r),
606			(TypedBuilder::Uint4(l), ViewData::Uint4(r)) => l.append_array(r),
607			(TypedBuilder::Uint8(l), ViewData::Uint8(r)) => l.append_array(r),
608			(TypedBuilder::Uint16(l), ViewData::Uint16(r)) => l.append_array(r)?,
609			(TypedBuilder::Float4(l), ViewData::Float4(r)) => l.append_array(r),
610			(TypedBuilder::Float8(l), ViewData::Float8(r)) => l.append_array(r),
611			(TypedBuilder::Date(l), ViewData::Date(r)) => l.append_array(r),
612			(TypedBuilder::DateTime(l), ViewData::DateTime(r)) => l.append_array(r),
613			(TypedBuilder::Time(l), ViewData::Time(r)) => l.append_array(r),
614			(TypedBuilder::Duration(l), ViewData::Duration(r)) => l.append_array(r),
615			(TypedBuilder::IdentityId(l), ViewData::IdentityId(r)) => append_fixed_array(l, r)?,
616			(TypedBuilder::Uuid4(l), ViewData::Uuid4(r)) => append_fixed_array(l, r)?,
617			(TypedBuilder::Uuid7(l), ViewData::Uuid7(r)) => append_fixed_array(l, r)?,
618			(
619				TypedBuilder::DictionaryId {
620					builder,
621					..
622				},
623				ViewData::DictionaryId {
624					container,
625					..
626				},
627			) => append_fixed_array(builder, container)?,
628			(
629				TypedBuilder::Utf8 {
630					builder,
631					..
632				},
633				ViewData::Utf8 {
634					container,
635					..
636				},
637			) => append_varlen(builder, container)?,
638			(
639				TypedBuilder::Blob {
640					builder,
641					..
642				},
643				ViewData::Blob {
644					container,
645					..
646				},
647			) => append_varlen(builder, container)?,
648			(TypedBuilder::Decimal(l), ViewData::Decimal(r)) => l.append_array(*r),
649			(
650				TypedBuilder::Any {
651					builder,
652					..
653				},
654				ViewData::Any {
655					container,
656					..
657				},
658			) => append_varlen(builder, container)?,
659			(
660				TypedBuilder::Digest {
661					builder,
662					inner: l_inner,
663					accuracy: l_accuracy,
664				},
665				ViewData::Digest {
666					container,
667					inner: r_inner,
668					accuracy: r_accuracy,
669				},
670			) if l_inner == r_inner && l_accuracy == r_accuracy => append_varlen(builder, container)?,
671			_ => return internal_err!("column type mismatch"),
672		}
673		Ok(())
674	}
675
676	pub fn len(&self) -> usize {
677		self.inner.len()
678	}
679
680	pub fn is_empty(&self) -> bool {
681		self.len() == 0
682	}
683
684	pub fn get_type(&self) -> ValueType {
685		match self.optional {
686			true => ValueType::Option(Box::new(self.inner.get_type())),
687			false => self.inner.get_type(),
688		}
689	}
690
691	pub fn finish(self, name: &str) -> (FieldRef, ArrayRef) {
692		let (mut field_type, array) = self.inner.finish();
693		if let Some(value_type) = field_type.value_type.take() {
694			field_type.value_type = Some(match self.optional || array.null_count() > 0 {
695				true => ValueType::Option(Box::new(value_type)),
696				false => value_type,
697			});
698		}
699		named(name, field_type, array)
700	}
701}
702
703impl TypedBuilder {
704	pub(crate) fn append_null(&mut self) {
705		match self {
706			TypedBuilder::Bool(b) => b.append_null(),
707			TypedBuilder::Int1(b) => b.append_null(),
708			TypedBuilder::Int2(b) => b.append_null(),
709			TypedBuilder::Int4(b) => b.append_null(),
710			TypedBuilder::Int8(b) => b.append_null(),
711			TypedBuilder::Int16(b) => b.append_null(),
712			TypedBuilder::Uint1(b) => b.append_null(),
713			TypedBuilder::Uint2(b) => b.append_null(),
714			TypedBuilder::Uint4(b) => b.append_null(),
715			TypedBuilder::Uint8(b) => b.append_null(),
716			TypedBuilder::Uint16(b) => b.append_null(),
717			TypedBuilder::Float4(b) => b.append_null(),
718			TypedBuilder::Float8(b) => b.append_null(),
719			TypedBuilder::Date(b) => b.append_null(),
720			TypedBuilder::DateTime(b) => b.append_null(),
721			TypedBuilder::Time(b) => b.append_null(),
722			TypedBuilder::Duration(b) => b.append_null(),
723			TypedBuilder::IdentityId(b) => b.append_null(),
724			TypedBuilder::Uuid4(b) => b.append_null(),
725			TypedBuilder::Uuid7(b) => b.append_null(),
726			TypedBuilder::DictionaryId {
727				builder,
728				..
729			} => builder.append_null(),
730			TypedBuilder::Utf8 {
731				builder,
732				..
733			} => builder.append_null(),
734			TypedBuilder::Blob {
735				builder,
736				..
737			}
738			| TypedBuilder::Any {
739				builder,
740				..
741			}
742			| TypedBuilder::Digest {
743				builder,
744				..
745			} => builder.append_null(),
746			TypedBuilder::Decimal(b) => b.append_null(),
747			TypedBuilder::None(b) => b.append_null(),
748		}
749	}
750
751	pub fn len(&self) -> usize {
752		match self {
753			TypedBuilder::Bool(b) => b.len(),
754			TypedBuilder::Int1(b) => b.len(),
755			TypedBuilder::Int2(b) => b.len(),
756			TypedBuilder::Int4(b) => b.len(),
757			TypedBuilder::Int8(b) => b.len(),
758			TypedBuilder::Int16(b) => b.len(),
759			TypedBuilder::Uint1(b) => b.len(),
760			TypedBuilder::Uint2(b) => b.len(),
761			TypedBuilder::Uint4(b) => b.len(),
762			TypedBuilder::Uint8(b) => b.len(),
763			TypedBuilder::Uint16(b) => b.len(),
764			TypedBuilder::Float4(b) => b.len(),
765			TypedBuilder::Float8(b) => b.len(),
766			TypedBuilder::Date(b) => b.len(),
767			TypedBuilder::DateTime(b) => b.len(),
768			TypedBuilder::Time(b) => b.len(),
769			TypedBuilder::Duration(b) => b.len(),
770			TypedBuilder::IdentityId(b) => b.len(),
771			TypedBuilder::Uuid4(b) => b.len(),
772			TypedBuilder::Uuid7(b) => b.len(),
773			TypedBuilder::DictionaryId {
774				builder,
775				..
776			} => builder.len(),
777			TypedBuilder::Utf8 {
778				builder,
779				..
780			} => builder.len(),
781			TypedBuilder::Blob {
782				builder,
783				..
784			}
785			| TypedBuilder::Any {
786				builder,
787				..
788			}
789			| TypedBuilder::Digest {
790				builder,
791				..
792			} => builder.len(),
793			TypedBuilder::Decimal(b) => b.len(),
794			TypedBuilder::None(b) => b.len(),
795		}
796	}
797
798	pub fn is_empty(&self) -> bool {
799		self.len() == 0
800	}
801
802	pub fn get_type(&self) -> ValueType {
803		match self {
804			TypedBuilder::Bool(_) => ValueType::Boolean,
805			TypedBuilder::Int1(_) => ValueType::Int1,
806			TypedBuilder::Int2(_) => ValueType::Int2,
807			TypedBuilder::Int4(_) => ValueType::Int4,
808			TypedBuilder::Int8(_) => ValueType::Int8,
809			TypedBuilder::Int16(_) => ValueType::Int16,
810			TypedBuilder::Uint1(_) => ValueType::Uint1,
811			TypedBuilder::Uint2(_) => ValueType::Uint2,
812			TypedBuilder::Uint4(_) => ValueType::Uint4,
813			TypedBuilder::Uint8(_) => ValueType::Uint8,
814			TypedBuilder::Uint16(_) => ValueType::Uint16,
815			TypedBuilder::Float4(_) => ValueType::Float4,
816			TypedBuilder::Float8(_) => ValueType::Float8,
817			TypedBuilder::Date(_) => ValueType::Date,
818			TypedBuilder::DateTime(_) => ValueType::DateTime,
819			TypedBuilder::Time(_) => ValueType::Time,
820			TypedBuilder::Duration(_) => ValueType::Duration,
821			TypedBuilder::IdentityId(_) => ValueType::IdentityId,
822			TypedBuilder::Uuid4(_) => ValueType::Uuid4,
823			TypedBuilder::Uuid7(_) => ValueType::Uuid7,
824			TypedBuilder::DictionaryId {
825				..
826			} => ValueType::DictionaryId,
827			TypedBuilder::Utf8 {
828				..
829			} => ValueType::Utf8,
830			TypedBuilder::Blob {
831				..
832			} => ValueType::Blob,
833			TypedBuilder::Decimal(b) => ValueType::decimal(b.precision(), b.scale()),
834			TypedBuilder::Any {
835				declared_type,
836				..
837			} => declared_type.clone().unwrap_or(ValueType::Any),
838			TypedBuilder::Digest {
839				inner,
840				accuracy,
841				..
842			} => ValueType::Digest {
843				inner: Box::new(inner.clone()),
844				accuracy: *accuracy,
845			},
846			TypedBuilder::None(_) => ValueType::Any,
847		}
848	}
849
850	fn finish(self) -> (FieldType, ArrayRef) {
851		let value_type = self.get_type();
852		let bare = |array: ArrayRef| {
853			(
854				FieldType {
855					value_type: Some(value_type.clone()),
856					..FieldType::default()
857				},
858				array,
859			)
860		};
861		match self {
862			TypedBuilder::Bool(mut b) => bare(Arc::new(b.finish())),
863			TypedBuilder::Int1(mut b) => bare(Arc::new(b.finish())),
864			TypedBuilder::Int2(mut b) => bare(Arc::new(b.finish())),
865			TypedBuilder::Int4(mut b) => bare(Arc::new(b.finish())),
866			TypedBuilder::Int8(mut b) => bare(Arc::new(b.finish())),
867			TypedBuilder::Int16(mut b) => bare(Arc::new(b.finish())),
868			TypedBuilder::Uint1(mut b) => bare(Arc::new(b.finish())),
869			TypedBuilder::Uint2(mut b) => bare(Arc::new(b.finish())),
870			TypedBuilder::Uint4(mut b) => bare(Arc::new(b.finish())),
871			TypedBuilder::Uint8(mut b) => bare(Arc::new(b.finish())),
872			TypedBuilder::Uint16(mut b) => bare(Arc::new(b.finish())),
873			TypedBuilder::Float4(mut b) => bare(Arc::new(b.finish())),
874			TypedBuilder::Float8(mut b) => bare(Arc::new(b.finish())),
875			TypedBuilder::Date(mut b) => bare(Arc::new(b.finish())),
876			TypedBuilder::DateTime(mut b) => bare(Arc::new(b.finish())),
877			TypedBuilder::Time(mut b) => bare(Arc::new(b.finish())),
878			TypedBuilder::Duration(mut b) => bare(Arc::new(b.finish())),
879			TypedBuilder::IdentityId(mut b) => bare(Arc::new(b.finish())),
880			TypedBuilder::Uuid4(mut b) => bare(Arc::new(b.finish())),
881			TypedBuilder::Uuid7(mut b) => bare(Arc::new(b.finish())),
882			TypedBuilder::DictionaryId {
883				mut builder,
884				dictionary_id,
885			} => (
886				FieldType {
887					value_type: Some(value_type),
888					dictionary_id,
889					..FieldType::default()
890				},
891				Arc::new(builder.finish()),
892			),
893			TypedBuilder::Utf8 {
894				mut builder,
895				max_bytes,
896			} => (
897				FieldType {
898					value_type: Some(value_type),
899					max_bytes: limit(max_bytes),
900					..FieldType::default()
901				},
902				Arc::new(builder.finish()),
903			),
904			TypedBuilder::Blob {
905				mut builder,
906				max_bytes,
907			} => (
908				FieldType {
909					value_type: Some(value_type),
910					max_bytes: limit(max_bytes),
911					..FieldType::default()
912				},
913				Arc::new(builder.finish()),
914			),
915			TypedBuilder::Decimal(mut b) => bare(b.finish().into_array()),
916			TypedBuilder::Any {
917				mut builder,
918				declared_type,
919			} => (
920				FieldType {
921					value_type: Some(value_type),
922					declared_type,
923					..FieldType::default()
924				},
925				Arc::new(builder.finish()),
926			),
927			TypedBuilder::Digest {
928				mut builder,
929				..
930			} => bare(Arc::new(builder.finish())),
931			TypedBuilder::None(mut b) => (FieldType::default(), Arc::new(b.finish())),
932		}
933	}
934
935	fn from_view(data: &ViewData) -> TypedBuilder {
936		match data {
937			ViewData::Bool(a) => {
938				let mut builder = BooleanBuilder::with_capacity(a.len());
939				builder.append_array(a);
940				TypedBuilder::Bool(builder)
941			}
942			ViewData::Int1(a) => TypedBuilder::Int1(primitive_builder((*a).clone())),
943			ViewData::Int2(a) => TypedBuilder::Int2(primitive_builder((*a).clone())),
944			ViewData::Int4(a) => TypedBuilder::Int4(primitive_builder((*a).clone())),
945			ViewData::Int8(a) => TypedBuilder::Int8(primitive_builder((*a).clone())),
946			ViewData::Int16(a) => TypedBuilder::Int16(WideBuilder::from_array(a)),
947			ViewData::Uint1(a) => TypedBuilder::Uint1(primitive_builder((*a).clone())),
948			ViewData::Uint2(a) => TypedBuilder::Uint2(primitive_builder((*a).clone())),
949			ViewData::Uint4(a) => TypedBuilder::Uint4(primitive_builder((*a).clone())),
950			ViewData::Uint8(a) => TypedBuilder::Uint8(primitive_builder((*a).clone())),
951			ViewData::Uint16(a) => TypedBuilder::Uint16(WideBuilder::from_array(a)),
952			ViewData::Float4(a) => TypedBuilder::Float4(primitive_builder((*a).clone())),
953			ViewData::Float8(a) => TypedBuilder::Float8(primitive_builder((*a).clone())),
954			ViewData::Date(a) => TypedBuilder::Date(primitive_builder((*a).clone())),
955			ViewData::DateTime(a) => TypedBuilder::DateTime(primitive_builder((*a).clone())),
956			ViewData::Time(a) => TypedBuilder::Time(primitive_builder((*a).clone())),
957			ViewData::Duration(a) => TypedBuilder::Duration(primitive_builder((*a).clone())),
958			ViewData::IdentityId(a) => TypedBuilder::IdentityId(fixed_size_builder(a)),
959			ViewData::Uuid4(a) => TypedBuilder::Uuid4(fixed_size_builder(a)),
960			ViewData::Uuid7(a) => TypedBuilder::Uuid7(fixed_size_builder(a)),
961			ViewData::DictionaryId {
962				container,
963				dictionary_id,
964			} => TypedBuilder::DictionaryId {
965				builder: fixed_size_builder(container),
966				dictionary_id: *dictionary_id,
967			},
968			ViewData::Utf8 {
969				container,
970				max_bytes,
971			} => TypedBuilder::Utf8 {
972				builder: varlen_builder((*container).clone()),
973				max_bytes: *max_bytes,
974			},
975			ViewData::Blob {
976				container,
977				max_bytes,
978			} => TypedBuilder::Blob {
979				builder: varlen_builder((*container).clone()),
980				max_bytes: *max_bytes,
981			},
982			ViewData::Decimal(a) => {
983				TypedBuilder::Decimal(DecimalBuilder::from_array(DecimalArray::from(*a)))
984			}
985			ViewData::Any {
986				container,
987				declared_type,
988			} => TypedBuilder::Any {
989				builder: varlen_builder((*container).clone()),
990				declared_type: declared_type.clone(),
991			},
992			ViewData::Digest {
993				container,
994				inner,
995				accuracy,
996			} => TypedBuilder::Digest {
997				builder: varlen_builder((*container).clone()),
998				inner: inner.clone(),
999				accuracy: *accuracy,
1000			},
1001			ViewData::None {
1002				array,
1003			} => {
1004				let mut builder = NullBuilder::new();
1005				builder.append_nulls(array.len());
1006				TypedBuilder::None(builder)
1007			}
1008		}
1009	}
1010}
1011
1012fn limit(max_bytes: MaxBytes) -> Option<MaxBytes> {
1013	(max_bytes != MaxBytes::MAX).then_some(max_bytes)
1014}
1015
1016pub(crate) fn primitive_builder<A>(array: PrimitiveArray<A>) -> PrimitiveBuilder<A>
1017where
1018	A: ArrowPrimitiveType,
1019{
1020	let data_type = array.data_type().clone();
1021	match array.into_builder() {
1022		Ok(builder) => builder.with_data_type(data_type),
1023		Err(array) => {
1024			let mut builder = PrimitiveBuilder::with_capacity(array.len());
1025			builder.append_array(&array);
1026			builder.with_data_type(data_type)
1027		}
1028	}
1029}
1030
1031pub(crate) fn append_fixed(builder: &mut FixedSizeBinaryBuilder, row: &[u8]) {
1032	if builder.append_value(row).is_err() {
1033		panic!(
1034			"a fixed size builder of {} byte rows can not take a row of {} bytes",
1035			builder.finish_cloned().value_length(),
1036			row.len()
1037		);
1038	}
1039}
1040
1041fn fixed_size_builder(array: &FixedSizeBinaryArray) -> FixedSizeBinaryBuilder {
1042	let mut builder = FixedSizeBinaryBuilder::with_capacity(array.len(), array.value_length());
1043	append_fixed_array(&mut builder, array)
1044		.expect("copying a fixed size array into a builder of its own width failed");
1045	builder
1046}
1047
1048pub(crate) fn append_fixed_array(builder: &mut FixedSizeBinaryBuilder, array: &FixedSizeBinaryArray) -> Result<()> {
1049	builder.append_array(array).map_err(|e| internal_error!("fixed size append failed: {}", e))
1050}
1051
1052pub(crate) fn varlen_builder<T>(array: GenericByteArray<T>) -> GenericByteBuilder<T>
1053where
1054	T: ByteArrayType<Offset = i64>,
1055{
1056	let array = if array.value_offsets()[0] == 0 {
1057		match array.into_builder() {
1058			Ok(builder) => return builder,
1059			Err(array) => array,
1060		}
1061	} else {
1062		array
1063	};
1064	let (data, _) = varlen_array::compact_parts(&array);
1065	let mut builder = GenericByteBuilder::<T>::with_capacity(array.len(), data.len());
1066	append_varlen(&mut builder, &array)
1067		.expect("copying a shared or offset utf8 / blob array into a new builder failed");
1068	builder
1069}
1070
1071pub(crate) fn append_varlen<T>(builder: &mut GenericByteBuilder<T>, array: &GenericByteArray<T>) -> Result<()>
1072where
1073	T: ByteArrayType<Offset = i64>,
1074{
1075	builder.append_array(array).map_err(|e| internal_error!("utf8 / blob append failed: {}", e))
1076}
1077
1078#[cfg(test)]
1079mod tests {
1080	use std::str::FromStr;
1081
1082	use arrow_array::ArrayRef;
1083	use arrow_buffer::{BooleanBuffer, NullBuffer};
1084	use arrow_schema::FieldRef;
1085	use reifydb_value::value::{
1086		Value,
1087		column_view::{ColumnView, ViewData},
1088		constraint::{precision::Precision, scale::Scale},
1089		decimal::Decimal,
1090		value_type::ValueType,
1091	};
1092
1093	use super::*;
1094	use crate::value::column::{factory, nulls::with_nulls};
1095
1096	fn concat(parts: &[(FieldRef, ArrayRef)]) -> (FieldRef, ArrayRef) {
1097		let (first, rest) = parts.split_first().unwrap();
1098		let mut builder = ColumnBuilder::from_view(&ColumnView::try_from(first).unwrap());
1099		for part in rest {
1100			builder.extend(&ColumnView::try_from(part).unwrap()).unwrap();
1101		}
1102		builder.finish("c")
1103	}
1104
1105	#[test]
1106	fn concat_keeps_a_nullable_column_nullable_across_chunks() {
1107		// An all-valid chunk still declares Option, and joining it with a bare chunk must not drop that.
1108		let first = with_nulls(factory::int4("c", [1, 2]), NullBuffer::new_valid(2)).unwrap();
1109		let second = factory::int4("c", [3, 4]);
1110
1111		let joined = concat(&[first, second]);
1112		let joined = ColumnView::try_from(&joined).unwrap();
1113
1114		assert_eq!(joined.len(), 4);
1115		assert!(joined.is_nullable());
1116		assert_eq!(joined.get_type(), ValueType::Option(Box::new(ValueType::Int4)));
1117		assert_eq!(joined.get_value(3), Value::Int4(4));
1118	}
1119
1120	#[test]
1121	fn concat_carries_the_none_rows_of_every_chunk() {
1122		// The null bits of each chunk have to land at that chunk's offset, never be rebuilt as all valid.
1123		let first = factory::int4_with_bitvec("c", [1, 0], BooleanBuffer::from(vec![true, false]));
1124		let second = factory::int4_with_bitvec("c", [0, 4], BooleanBuffer::from(vec![false, true]));
1125
1126		let joined = concat(&[first, second]);
1127		let joined = ColumnView::try_from(&joined).unwrap();
1128
1129		assert_eq!(joined.get_value(0), Value::Int4(1));
1130		assert_eq!(joined.get_value(1), Value::none_of(ValueType::Int4));
1131		assert_eq!(joined.get_value(2), Value::none_of(ValueType::Int4));
1132		assert_eq!(joined.get_value(3), Value::Int4(4));
1133	}
1134
1135	#[test]
1136	fn concat_keeps_the_decimal_precision_and_scale() {
1137		// Precision and scale live in the arrow type, so joining chunks must never reset them to defaults.
1138		let first =
1139			factory::decimal("c", Precision::new(9), Scale::new(2), [Decimal::from_str("1.25").unwrap()]);
1140		let second =
1141			factory::decimal("c", Precision::new(9), Scale::new(2), [Decimal::from_str("2.50").unwrap()]);
1142
1143		let joined = concat(&[first, second]);
1144		let joined = ColumnView::try_from(&joined).unwrap();
1145
1146		let ViewData::Decimal(array) = &joined.data else {
1147			panic!("expected a decimal column");
1148		};
1149		assert_eq!(array.precision(), Precision::new(9));
1150		assert_eq!(array.scale(), Scale::new(2));
1151		assert_eq!(joined.len(), 2);
1152	}
1153}