Skip to main content

hdbconnect_arrow/builders/
dispatch.rs

1//! Enum-based builder dispatch for eliminating dynamic dispatch overhead.
2//!
3//! This module provides `BuilderEnum` which wraps all concrete builder types
4//! in a single enum, enabling monomorphized dispatch instead of vtable lookups.
5//!
6//! Large variants (string, binary, decimal) are wrapped in `Box` to reduce
7//! enum size and improve cache locality for temporal/primitive builders.
8
9use arrow_array::ArrayRef;
10
11use super::boolean::BooleanBuilderWrapper;
12use super::decimal::Decimal128BuilderWrapper;
13use super::primitive::{
14    Float32BuilderWrapper, Float64BuilderWrapper, Int16BuilderWrapper, Int32BuilderWrapper,
15    Int64BuilderWrapper, UInt8BuilderWrapper,
16};
17use super::string::{
18    BinaryBuilderWrapper, FixedSizeBinaryBuilderWrapper, LargeBinaryBuilderWrapper,
19    LargeStringBuilderWrapper, StringBuilderWrapper,
20};
21use super::temporal::{
22    Date32BuilderWrapper, Time64NanosecondBuilderWrapper, TimestampNanosecondBuilderWrapper,
23};
24use crate::Result;
25use crate::traits::builder::HanaCompatibleBuilder;
26
27/// Discriminant identifying the builder type without data.
28///
29/// Used for schema profile analysis to detect homogeneous schemas
30/// where all columns share the same builder type.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum BuilderKind {
33    /// `UInt8` builder (HANA TINYINT)
34    UInt8,
35    /// `Int16` builder (HANA SMALLINT)
36    Int16,
37    /// `Int32` builder (HANA INT)
38    Int32,
39    /// `Int64` builder (HANA BIGINT)
40    Int64,
41    /// `Float32` builder (HANA REAL)
42    Float32,
43    /// `Float64` builder (HANA DOUBLE)
44    Float64,
45    /// `Decimal128` builder (HANA DECIMAL)
46    Decimal128,
47    /// `Boolean` builder (HANA BOOLEAN)
48    Boolean,
49    /// `Utf8` builder (HANA VARCHAR, NVARCHAR)
50    Utf8,
51    /// `LargeUtf8` builder (HANA CLOB, NCLOB)
52    LargeUtf8,
53    /// `Binary` builder (HANA BINARY)
54    Binary,
55    /// `LargeBinary` builder (HANA BLOB)
56    LargeBinary,
57    /// `FixedSizeBinary` builder (HANA FIXED8, FIXED12, FIXED16)
58    FixedSizeBinary,
59    /// `Date32` builder (HANA DAYDATE)
60    Date32,
61    /// `Time64Nanosecond` builder (HANA SECONDTIME)
62    Time64Nanosecond,
63    /// `TimestampNanosecond` builder (HANA LONGDATE, SECONDDATE)
64    TimestampNanosecond,
65}
66
67/// Enum-wrapped builder for static dispatch.
68///
69/// Wraps all concrete builder types in a single enum, eliminating vtable
70/// lookups and enabling the compiler to inline and optimize dispatch.
71///
72/// Large variants (string, binary, decimal) are wrapped in `Box` to reduce
73/// enum size and improve cache locality for temporal/primitive builders.
74///
75/// For homogeneous schemas (all columns same type), the match can be hoisted
76/// outside the row loop for monomorphized inner loops.
77#[derive(Debug)]
78pub enum BuilderEnum {
79    // ═══════════════════════════════════════════════════════════════════════════
80    // Inline small variants (primitive and temporal types)
81    // ═══════════════════════════════════════════════════════════════════════════
82    /// `UInt8` builder variant
83    UInt8(UInt8BuilderWrapper),
84    /// `Int16` builder variant
85    Int16(Int16BuilderWrapper),
86    /// `Int32` builder variant
87    Int32(Int32BuilderWrapper),
88    /// `Int64` builder variant
89    Int64(Int64BuilderWrapper),
90    /// `Float32` builder variant
91    Float32(Float32BuilderWrapper),
92    /// `Float64` builder variant
93    Float64(Float64BuilderWrapper),
94    /// `Boolean` builder variant
95    Boolean(BooleanBuilderWrapper),
96    /// `Date32` builder variant
97    Date32(Date32BuilderWrapper),
98    /// `Time64Nanosecond` builder variant
99    Time64Nanosecond(Time64NanosecondBuilderWrapper),
100    /// `TimestampNanosecond` builder variant
101    TimestampNanosecond(TimestampNanosecondBuilderWrapper),
102
103    // ═══════════════════════════════════════════════════════════════════════════
104    // Boxed large variants (string, binary, decimal - larger inner size)
105    // ═══════════════════════════════════════════════════════════════════════════
106    /// `Decimal128` builder variant (boxed)
107    Decimal128(Box<Decimal128BuilderWrapper>),
108    /// `Utf8` builder variant (boxed)
109    Utf8(Box<StringBuilderWrapper>),
110    /// `LargeUtf8` builder variant (boxed)
111    LargeUtf8(Box<LargeStringBuilderWrapper>),
112    /// `Binary` builder variant (boxed)
113    Binary(Box<BinaryBuilderWrapper>),
114    /// `LargeBinary` builder variant (boxed)
115    LargeBinary(Box<LargeBinaryBuilderWrapper>),
116    /// `FixedSizeBinary` builder variant (boxed)
117    FixedSizeBinary(Box<FixedSizeBinaryBuilderWrapper>),
118}
119
120impl BuilderEnum {
121    /// Returns the kind of this builder (discriminant only).
122    #[must_use]
123    pub const fn kind(&self) -> BuilderKind {
124        match self {
125            Self::UInt8(_) => BuilderKind::UInt8,
126            Self::Int16(_) => BuilderKind::Int16,
127            Self::Int32(_) => BuilderKind::Int32,
128            Self::Int64(_) => BuilderKind::Int64,
129            Self::Float32(_) => BuilderKind::Float32,
130            Self::Float64(_) => BuilderKind::Float64,
131            Self::Decimal128(_) => BuilderKind::Decimal128,
132            Self::Boolean(_) => BuilderKind::Boolean,
133            Self::Utf8(_) => BuilderKind::Utf8,
134            Self::LargeUtf8(_) => BuilderKind::LargeUtf8,
135            Self::Binary(_) => BuilderKind::Binary,
136            Self::LargeBinary(_) => BuilderKind::LargeBinary,
137            Self::FixedSizeBinary(_) => BuilderKind::FixedSizeBinary,
138            Self::Date32(_) => BuilderKind::Date32,
139            Self::Time64Nanosecond(_) => BuilderKind::Time64Nanosecond,
140            Self::TimestampNanosecond(_) => BuilderKind::TimestampNanosecond,
141        }
142    }
143
144    /// Append a HANA value to this builder.
145    ///
146    /// # Errors
147    ///
148    /// Returns an error if the value cannot be converted to the target type.
149    #[inline]
150    pub fn append_hana_value(&mut self, value: &hdbconnect::HdbValue) -> Result<()> {
151        match self {
152            Self::UInt8(b) => b.append_hana_value(value),
153            Self::Int16(b) => b.append_hana_value(value),
154            Self::Int32(b) => b.append_hana_value(value),
155            Self::Int64(b) => b.append_hana_value(value),
156            Self::Float32(b) => b.append_hana_value(value),
157            Self::Float64(b) => b.append_hana_value(value),
158            Self::Decimal128(b) => b.append_hana_value(value),
159            Self::Boolean(b) => b.append_hana_value(value),
160            Self::Utf8(b) => b.append_hana_value(value),
161            Self::LargeUtf8(b) => b.append_hana_value(value),
162            Self::Binary(b) => b.append_hana_value(value),
163            Self::LargeBinary(b) => b.append_hana_value(value),
164            Self::FixedSizeBinary(b) => b.append_hana_value(value),
165            Self::Date32(b) => b.append_hana_value(value),
166            Self::Time64Nanosecond(b) => b.append_hana_value(value),
167            Self::TimestampNanosecond(b) => b.append_hana_value(value),
168        }
169    }
170
171    /// Append a null value to this builder.
172    #[inline]
173    pub fn append_null(&mut self) {
174        match self {
175            Self::UInt8(b) => b.append_null(),
176            Self::Int16(b) => b.append_null(),
177            Self::Int32(b) => b.append_null(),
178            Self::Int64(b) => b.append_null(),
179            Self::Float32(b) => b.append_null(),
180            Self::Float64(b) => b.append_null(),
181            Self::Decimal128(b) => b.append_null(),
182            Self::Boolean(b) => b.append_null(),
183            Self::Utf8(b) => b.append_null(),
184            Self::LargeUtf8(b) => b.append_null(),
185            Self::Binary(b) => b.append_null(),
186            Self::LargeBinary(b) => b.append_null(),
187            Self::FixedSizeBinary(b) => b.append_null(),
188            Self::Date32(b) => b.append_null(),
189            Self::Time64Nanosecond(b) => b.append_null(),
190            Self::TimestampNanosecond(b) => b.append_null(),
191        }
192    }
193
194    /// Finish building and return the Arrow array.
195    ///
196    /// After calling this method, the builder is reset and can be reused.
197    pub fn finish(&mut self) -> ArrayRef {
198        match self {
199            Self::UInt8(b) => b.finish(),
200            Self::Int16(b) => b.finish(),
201            Self::Int32(b) => b.finish(),
202            Self::Int64(b) => b.finish(),
203            Self::Float32(b) => b.finish(),
204            Self::Float64(b) => b.finish(),
205            Self::Decimal128(b) => b.finish(),
206            Self::Boolean(b) => b.finish(),
207            Self::Utf8(b) => b.finish(),
208            Self::LargeUtf8(b) => b.finish(),
209            Self::Binary(b) => b.finish(),
210            Self::LargeBinary(b) => b.finish(),
211            Self::FixedSizeBinary(b) => b.finish(),
212            Self::Date32(b) => b.finish(),
213            Self::Time64Nanosecond(b) => b.finish(),
214            Self::TimestampNanosecond(b) => b.finish(),
215        }
216    }
217
218    /// Returns the number of values (including nulls) appended so far.
219    #[must_use]
220    pub fn len(&self) -> usize {
221        match self {
222            Self::UInt8(b) => b.len(),
223            Self::Int16(b) => b.len(),
224            Self::Int32(b) => b.len(),
225            Self::Int64(b) => b.len(),
226            Self::Float32(b) => b.len(),
227            Self::Float64(b) => b.len(),
228            Self::Decimal128(b) => b.len(),
229            Self::Boolean(b) => b.len(),
230            Self::Utf8(b) => b.len(),
231            Self::LargeUtf8(b) => b.len(),
232            Self::Binary(b) => b.len(),
233            Self::LargeBinary(b) => b.len(),
234            Self::FixedSizeBinary(b) => b.len(),
235            Self::Date32(b) => b.len(),
236            Self::Time64Nanosecond(b) => b.len(),
237            Self::TimestampNanosecond(b) => b.len(),
238        }
239    }
240
241    /// Returns true if no values have been appended.
242    #[must_use]
243    pub fn is_empty(&self) -> bool {
244        self.len() == 0
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use arrow_array::{
251        Array, BinaryArray, BooleanArray, Date32Array, Float32Array, Float64Array, Int16Array,
252        Int32Array, Int64Array, LargeBinaryArray, LargeStringArray, StringArray,
253        Time64NanosecondArray, TimestampNanosecondArray, UInt8Array,
254    };
255    use hdbconnect::HdbValue;
256
257    use super::*;
258
259    // ═══════════════════════════════════════════════════════════════════════════
260    // BuilderEnum Size Tests
261    // ═══════════════════════════════════════════════════════════════════════════
262
263    #[test]
264    fn test_boxed_variants_smaller_than_inline() {
265        // Verify that the boxed variants would be larger if inline
266        let string_wrapper_size = size_of::<StringBuilderWrapper>();
267        let decimal_wrapper_size = size_of::<Decimal128BuilderWrapper>();
268        let box_size = size_of::<Box<StringBuilderWrapper>>();
269
270        assert!(
271            string_wrapper_size > box_size,
272            "StringBuilderWrapper ({string_wrapper_size}) should be larger than Box ({box_size})"
273        );
274        assert!(
275            decimal_wrapper_size > box_size,
276            "Decimal128BuilderWrapper ({decimal_wrapper_size}) should be larger than Box ({box_size})"
277        );
278    }
279
280    // ═══════════════════════════════════════════════════════════════════════════
281    // BuilderKind Tests
282    // ═══════════════════════════════════════════════════════════════════════════
283
284    #[test]
285    fn test_builder_kind_equality() {
286        assert_eq!(BuilderKind::Int32, BuilderKind::Int32);
287        assert_ne!(BuilderKind::Int32, BuilderKind::Int64);
288    }
289
290    #[test]
291    fn test_builder_kind_copy() {
292        let kind = BuilderKind::Utf8;
293        let kind2 = kind;
294        assert_eq!(kind, kind2);
295    }
296
297    #[test]
298    fn test_builder_kind_debug() {
299        let kind = BuilderKind::Decimal128;
300        let debug = format!("{kind:?}");
301        assert!(debug.contains("Decimal128"));
302    }
303
304    // ═══════════════════════════════════════════════════════════════════════════
305    // BuilderEnum Creation Tests
306    // ═══════════════════════════════════════════════════════════════════════════
307
308    #[test]
309    fn test_builder_enum_uint8() {
310        let builder = BuilderEnum::UInt8(UInt8BuilderWrapper::new(10));
311        assert_eq!(builder.kind(), BuilderKind::UInt8);
312        assert!(builder.is_empty());
313    }
314
315    #[test]
316    fn test_builder_enum_int16() {
317        let builder = BuilderEnum::Int16(Int16BuilderWrapper::new(10));
318        assert_eq!(builder.kind(), BuilderKind::Int16);
319    }
320
321    #[test]
322    fn test_builder_enum_int32() {
323        let builder = BuilderEnum::Int32(Int32BuilderWrapper::new(10));
324        assert_eq!(builder.kind(), BuilderKind::Int32);
325    }
326
327    #[test]
328    fn test_builder_enum_int64() {
329        let builder = BuilderEnum::Int64(Int64BuilderWrapper::new(10));
330        assert_eq!(builder.kind(), BuilderKind::Int64);
331    }
332
333    #[test]
334    fn test_builder_enum_float32() {
335        let builder = BuilderEnum::Float32(Float32BuilderWrapper::new(10));
336        assert_eq!(builder.kind(), BuilderKind::Float32);
337    }
338
339    #[test]
340    fn test_builder_enum_float64() {
341        let builder = BuilderEnum::Float64(Float64BuilderWrapper::new(10));
342        assert_eq!(builder.kind(), BuilderKind::Float64);
343    }
344
345    #[test]
346    fn test_builder_enum_decimal128() {
347        let builder = BuilderEnum::Decimal128(Box::new(Decimal128BuilderWrapper::new(10, 18, 2)));
348        assert_eq!(builder.kind(), BuilderKind::Decimal128);
349    }
350
351    #[test]
352    fn test_builder_enum_boolean() {
353        let builder = BuilderEnum::Boolean(BooleanBuilderWrapper::new(10));
354        assert_eq!(builder.kind(), BuilderKind::Boolean);
355    }
356
357    #[test]
358    fn test_builder_enum_utf8() {
359        let builder = BuilderEnum::Utf8(Box::new(StringBuilderWrapper::new(10, 100)));
360        assert_eq!(builder.kind(), BuilderKind::Utf8);
361    }
362
363    #[test]
364    fn test_builder_enum_large_utf8() {
365        let builder = BuilderEnum::LargeUtf8(Box::new(LargeStringBuilderWrapper::new(10, 1000)));
366        assert_eq!(builder.kind(), BuilderKind::LargeUtf8);
367    }
368
369    #[test]
370    fn test_builder_enum_binary() {
371        let builder = BuilderEnum::Binary(Box::new(BinaryBuilderWrapper::new(10, 100)));
372        assert_eq!(builder.kind(), BuilderKind::Binary);
373    }
374
375    #[test]
376    fn test_builder_enum_large_binary() {
377        let builder = BuilderEnum::LargeBinary(Box::new(LargeBinaryBuilderWrapper::new(10, 1000)));
378        assert_eq!(builder.kind(), BuilderKind::LargeBinary);
379    }
380
381    #[test]
382    fn test_builder_enum_fixed_size_binary() {
383        let builder =
384            BuilderEnum::FixedSizeBinary(Box::new(FixedSizeBinaryBuilderWrapper::new(10, 8)));
385        assert_eq!(builder.kind(), BuilderKind::FixedSizeBinary);
386    }
387
388    #[test]
389    fn test_builder_enum_date32() {
390        let builder = BuilderEnum::Date32(Date32BuilderWrapper::new(10));
391        assert_eq!(builder.kind(), BuilderKind::Date32);
392    }
393
394    #[test]
395    fn test_builder_enum_time64() {
396        let builder = BuilderEnum::Time64Nanosecond(Time64NanosecondBuilderWrapper::new(10));
397        assert_eq!(builder.kind(), BuilderKind::Time64Nanosecond);
398    }
399
400    #[test]
401    fn test_builder_enum_timestamp() {
402        let builder = BuilderEnum::TimestampNanosecond(TimestampNanosecondBuilderWrapper::new(10));
403        assert_eq!(builder.kind(), BuilderKind::TimestampNanosecond);
404    }
405
406    // ═══════════════════════════════════════════════════════════════════════════
407    // BuilderEnum Append and Finish Tests
408    // ═══════════════════════════════════════════════════════════════════════════
409
410    #[test]
411    fn test_builder_enum_uint8_append() {
412        let mut builder = BuilderEnum::UInt8(UInt8BuilderWrapper::new(10));
413        builder.append_hana_value(&HdbValue::TINYINT(42)).unwrap();
414        builder.append_null();
415        assert_eq!(builder.len(), 2);
416
417        let array = builder.finish();
418        let uint_array = array.as_any().downcast_ref::<UInt8Array>().unwrap();
419        assert_eq!(uint_array.value(0), 42);
420        assert!(uint_array.is_null(1));
421    }
422
423    #[test]
424    fn test_builder_enum_int16_append() {
425        let mut builder = BuilderEnum::Int16(Int16BuilderWrapper::new(10));
426        builder
427            .append_hana_value(&HdbValue::SMALLINT(1000))
428            .unwrap();
429        assert_eq!(builder.len(), 1);
430
431        let array = builder.finish();
432        let int_array = array.as_any().downcast_ref::<Int16Array>().unwrap();
433        assert_eq!(int_array.value(0), 1000);
434    }
435
436    #[test]
437    fn test_builder_enum_int32_append() {
438        let mut builder = BuilderEnum::Int32(Int32BuilderWrapper::new(10));
439        builder.append_hana_value(&HdbValue::INT(12345)).unwrap();
440        builder.append_null();
441        assert_eq!(builder.len(), 2);
442
443        let array = builder.finish();
444        let int_array = array.as_any().downcast_ref::<Int32Array>().unwrap();
445        assert_eq!(int_array.value(0), 12345);
446        assert!(int_array.is_null(1));
447    }
448
449    #[test]
450    fn test_builder_enum_int64_append() {
451        let mut builder = BuilderEnum::Int64(Int64BuilderWrapper::new(10));
452        builder
453            .append_hana_value(&HdbValue::BIGINT(i64::MAX))
454            .unwrap();
455        assert_eq!(builder.len(), 1);
456
457        let array = builder.finish();
458        let int_array = array.as_any().downcast_ref::<Int64Array>().unwrap();
459        assert_eq!(int_array.value(0), i64::MAX);
460    }
461
462    #[test]
463    fn test_builder_enum_float32_append() {
464        let mut builder = BuilderEnum::Float32(Float32BuilderWrapper::new(10));
465        builder.append_hana_value(&HdbValue::REAL(3.14)).unwrap();
466        assert_eq!(builder.len(), 1);
467
468        let array = builder.finish();
469        let float_array = array.as_any().downcast_ref::<Float32Array>().unwrap();
470        assert!((float_array.value(0) - 3.14).abs() < 0.001);
471    }
472
473    #[test]
474    fn test_builder_enum_float64_append() {
475        let mut builder = BuilderEnum::Float64(Float64BuilderWrapper::new(10));
476        builder.append_hana_value(&HdbValue::DOUBLE(2.718)).unwrap();
477        builder.append_null();
478        assert_eq!(builder.len(), 2);
479
480        let array = builder.finish();
481        let float_array = array.as_any().downcast_ref::<Float64Array>().unwrap();
482        assert!((float_array.value(0) - 2.718).abs() < 0.0001);
483        assert!(float_array.is_null(1));
484    }
485
486    #[test]
487    fn test_builder_enum_boolean_append() {
488        let mut builder = BuilderEnum::Boolean(BooleanBuilderWrapper::new(10));
489        builder.append_hana_value(&HdbValue::BOOLEAN(true)).unwrap();
490        builder
491            .append_hana_value(&HdbValue::BOOLEAN(false))
492            .unwrap();
493        builder.append_null();
494        assert_eq!(builder.len(), 3);
495
496        let array = builder.finish();
497        let bool_array = array.as_any().downcast_ref::<BooleanArray>().unwrap();
498        assert!(bool_array.value(0));
499        assert!(!bool_array.value(1));
500        assert!(bool_array.is_null(2));
501    }
502
503    #[test]
504    fn test_builder_enum_utf8_append() {
505        let mut builder = BuilderEnum::Utf8(Box::new(StringBuilderWrapper::new(10, 100)));
506        builder
507            .append_hana_value(&HdbValue::STRING("hello".to_string()))
508            .unwrap();
509        builder.append_null();
510        assert_eq!(builder.len(), 2);
511
512        let array = builder.finish();
513        let str_array = array.as_any().downcast_ref::<StringArray>().unwrap();
514        assert_eq!(str_array.value(0), "hello");
515        assert!(str_array.is_null(1));
516    }
517
518    #[test]
519    fn test_builder_enum_large_utf8_append() {
520        let mut builder =
521            BuilderEnum::LargeUtf8(Box::new(LargeStringBuilderWrapper::new(10, 1000)));
522        builder
523            .append_hana_value(&HdbValue::STRING("large text".to_string()))
524            .unwrap();
525        assert_eq!(builder.len(), 1);
526
527        let array = builder.finish();
528        let str_array = array.as_any().downcast_ref::<LargeStringArray>().unwrap();
529        assert_eq!(str_array.value(0), "large text");
530    }
531
532    #[test]
533    fn test_builder_enum_binary_append() {
534        let mut builder = BuilderEnum::Binary(Box::new(BinaryBuilderWrapper::new(10, 100)));
535        builder
536            .append_hana_value(&HdbValue::BINARY(vec![1, 2, 3]))
537            .unwrap();
538        builder.append_null();
539        assert_eq!(builder.len(), 2);
540
541        let array = builder.finish();
542        let bin_array = array.as_any().downcast_ref::<BinaryArray>().unwrap();
543        assert_eq!(bin_array.value(0), &[1, 2, 3]);
544        assert!(bin_array.is_null(1));
545    }
546
547    #[test]
548    fn test_builder_enum_large_binary_append() {
549        let mut builder =
550            BuilderEnum::LargeBinary(Box::new(LargeBinaryBuilderWrapper::new(10, 1000)));
551        builder
552            .append_hana_value(&HdbValue::BINARY(vec![4, 5, 6, 7]))
553            .unwrap();
554        assert_eq!(builder.len(), 1);
555
556        let array = builder.finish();
557        let bin_array = array.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
558        assert_eq!(bin_array.value(0), &[4, 5, 6, 7]);
559    }
560
561    #[test]
562    fn test_builder_enum_date32_append_null() {
563        let mut builder = BuilderEnum::Date32(Date32BuilderWrapper::new(10));
564        builder.append_null();
565        assert_eq!(builder.len(), 1);
566
567        let array = builder.finish();
568        let date_array = array.as_any().downcast_ref::<Date32Array>().unwrap();
569        assert!(date_array.is_null(0));
570    }
571
572    #[test]
573    fn test_builder_enum_time64_append_null() {
574        let mut builder = BuilderEnum::Time64Nanosecond(Time64NanosecondBuilderWrapper::new(10));
575        builder.append_null();
576        assert_eq!(builder.len(), 1);
577
578        let array = builder.finish();
579        let time_array = array
580            .as_any()
581            .downcast_ref::<Time64NanosecondArray>()
582            .unwrap();
583        assert!(time_array.is_null(0));
584    }
585
586    #[test]
587    fn test_builder_enum_timestamp_append_null() {
588        let mut builder =
589            BuilderEnum::TimestampNanosecond(TimestampNanosecondBuilderWrapper::new(10));
590        builder.append_null();
591        assert_eq!(builder.len(), 1);
592
593        let array = builder.finish();
594        let ts_array = array
595            .as_any()
596            .downcast_ref::<TimestampNanosecondArray>()
597            .unwrap();
598        assert!(ts_array.is_null(0));
599    }
600
601    // ═══════════════════════════════════════════════════════════════════════════
602    // BuilderEnum Error Handling Tests
603    // ═══════════════════════════════════════════════════════════════════════════
604
605    #[test]
606    fn test_builder_enum_type_mismatch() {
607        let mut builder = BuilderEnum::Int32(Int32BuilderWrapper::new(10));
608        let result = builder.append_hana_value(&HdbValue::STRING("hello".to_string()));
609        assert!(result.is_err());
610    }
611
612    // ═══════════════════════════════════════════════════════════════════════════
613    // BuilderEnum Reuse Tests
614    // ═══════════════════════════════════════════════════════════════════════════
615
616    #[test]
617    fn test_builder_enum_reuse_after_finish() {
618        let mut builder = BuilderEnum::Int32(Int32BuilderWrapper::new(10));
619        builder.append_hana_value(&HdbValue::INT(1)).unwrap();
620        let array1 = builder.finish();
621        assert_eq!(array1.len(), 1);
622        assert!(builder.is_empty());
623
624        builder.append_hana_value(&HdbValue::INT(2)).unwrap();
625        builder.append_hana_value(&HdbValue::INT(3)).unwrap();
626        let array2 = builder.finish();
627        assert_eq!(array2.len(), 2);
628
629        let int_array = array2.as_any().downcast_ref::<Int32Array>().unwrap();
630        assert_eq!(int_array.value(0), 2);
631        assert_eq!(int_array.value(1), 3);
632    }
633
634    // ═══════════════════════════════════════════════════════════════════════════
635    // All 16 Variants Test
636    // ═══════════════════════════════════════════════════════════════════════════
637
638    #[test]
639    fn test_all_16_builder_kinds() {
640        let kinds = [
641            BuilderKind::UInt8,
642            BuilderKind::Int16,
643            BuilderKind::Int32,
644            BuilderKind::Int64,
645            BuilderKind::Float32,
646            BuilderKind::Float64,
647            BuilderKind::Decimal128,
648            BuilderKind::Boolean,
649            BuilderKind::Utf8,
650            BuilderKind::LargeUtf8,
651            BuilderKind::Binary,
652            BuilderKind::LargeBinary,
653            BuilderKind::FixedSizeBinary,
654            BuilderKind::Date32,
655            BuilderKind::Time64Nanosecond,
656            BuilderKind::TimestampNanosecond,
657        ];
658        assert_eq!(kinds.len(), 16);
659
660        for (i, kind) in kinds.iter().enumerate() {
661            for (j, other) in kinds.iter().enumerate() {
662                if i == j {
663                    assert_eq!(kind, other);
664                } else {
665                    assert_ne!(kind, other);
666                }
667            }
668        }
669    }
670
671    #[test]
672    fn test_all_16_builder_enum_variants() {
673        let builders: Vec<BuilderEnum> = vec![
674            BuilderEnum::UInt8(UInt8BuilderWrapper::new(1)),
675            BuilderEnum::Int16(Int16BuilderWrapper::new(1)),
676            BuilderEnum::Int32(Int32BuilderWrapper::new(1)),
677            BuilderEnum::Int64(Int64BuilderWrapper::new(1)),
678            BuilderEnum::Float32(Float32BuilderWrapper::new(1)),
679            BuilderEnum::Float64(Float64BuilderWrapper::new(1)),
680            BuilderEnum::Decimal128(Box::new(Decimal128BuilderWrapper::new(1, 18, 2))),
681            BuilderEnum::Boolean(BooleanBuilderWrapper::new(1)),
682            BuilderEnum::Utf8(Box::new(StringBuilderWrapper::new(1, 10))),
683            BuilderEnum::LargeUtf8(Box::new(LargeStringBuilderWrapper::new(1, 100))),
684            BuilderEnum::Binary(Box::new(BinaryBuilderWrapper::new(1, 10))),
685            BuilderEnum::LargeBinary(Box::new(LargeBinaryBuilderWrapper::new(1, 100))),
686            BuilderEnum::FixedSizeBinary(Box::new(FixedSizeBinaryBuilderWrapper::new(1, 8))),
687            BuilderEnum::Date32(Date32BuilderWrapper::new(1)),
688            BuilderEnum::Time64Nanosecond(Time64NanosecondBuilderWrapper::new(1)),
689            BuilderEnum::TimestampNanosecond(TimestampNanosecondBuilderWrapper::new(1)),
690        ];
691        assert_eq!(builders.len(), 16);
692
693        let expected_kinds = [
694            BuilderKind::UInt8,
695            BuilderKind::Int16,
696            BuilderKind::Int32,
697            BuilderKind::Int64,
698            BuilderKind::Float32,
699            BuilderKind::Float64,
700            BuilderKind::Decimal128,
701            BuilderKind::Boolean,
702            BuilderKind::Utf8,
703            BuilderKind::LargeUtf8,
704            BuilderKind::Binary,
705            BuilderKind::LargeBinary,
706            BuilderKind::FixedSizeBinary,
707            BuilderKind::Date32,
708            BuilderKind::Time64Nanosecond,
709            BuilderKind::TimestampNanosecond,
710        ];
711
712        for (builder, expected) in builders.iter().zip(expected_kinds.iter()) {
713            assert_eq!(builder.kind(), *expected);
714            assert!(builder.is_empty());
715        }
716    }
717}