Skip to main content

hdbconnect_arrow/builders/
primitive.rs

1//! Primitive type builders for numeric Arrow arrays.
2//!
3//! Implements builders for:
4//! - `UInt8` (HANA TINYINT)
5//! - `Int16` (HANA SMALLINT)
6//! - `Int32` (HANA INT)
7//! - `Int64` (HANA BIGINT)
8//! - `Float32` (HANA REAL)
9//! - `Float64` (HANA DOUBLE)
10
11use std::sync::Arc;
12
13use arrow_array::ArrayRef;
14use arrow_array::builder::{
15    Float32Builder, Float64Builder, Int16Builder, Int32Builder, Int64Builder, UInt8Builder,
16};
17
18use crate::Result;
19use crate::traits::builder::HanaCompatibleBuilder;
20use crate::traits::sealed::private::Sealed;
21
22// ═══════════════════════════════════════════════════════════════════════════
23// Macro for Primitive Builder Implementation
24// ═══════════════════════════════════════════════════════════════════════════
25
26/// Generate builder wrapper implementation for primitive types.
27///
28/// # Arguments
29///
30/// * `$name` - Wrapper struct name (e.g., `Int32BuilderWrapper`)
31/// * `$builder` - Arrow builder type (e.g., `Int32Builder`)
32/// * `$rust_ty` - Rust target type (e.g., `i32`)
33/// * `$hana_variant` - `HdbValue` variant name (e.g., `INT`)
34macro_rules! impl_primitive_builder {
35    ($name:ident, $builder:ty, $rust_ty:ty, $hana_variant:ident) => {
36        /// Builder wrapper for Arrow primitive arrays.
37        ///
38        /// Implements [`HanaCompatibleBuilder`] for HANA value conversion.
39        #[derive(Debug)]
40        pub struct $name {
41            builder: $builder,
42            len: usize,
43        }
44
45        impl $name {
46            /// Create a new builder with the specified capacity.
47            #[must_use]
48            pub fn new(capacity: usize) -> Self {
49                Self {
50                    builder: <$builder>::with_capacity(capacity),
51                    len: 0,
52                }
53            }
54
55            /// Create a builder with default capacity.
56            #[must_use]
57            pub fn default_capacity() -> Self {
58                Self::new(1024)
59            }
60        }
61
62        impl Sealed for $name {}
63
64        impl HanaCompatibleBuilder for $name {
65            fn append_hana_value(&mut self, value: &hdbconnect::HdbValue) -> Result<()> {
66                use hdbconnect::HdbValue;
67
68                match value {
69                    HdbValue::$hana_variant(v) => {
70                        let converted: $rust_ty = (*v).try_into().map_err(|e| {
71                            crate::ArrowConversionError::value_conversion(
72                                stringify!($name),
73                                format!("cannot convert {} to {}: {}", v, stringify!($rust_ty), e),
74                            )
75                        })?;
76                        self.builder.append_value(converted);
77                    }
78                    other => {
79                        return Err(crate::ArrowConversionError::value_conversion(
80                            stringify!($name),
81                            format!("expected {}, got {:?}", stringify!($hana_variant), other),
82                        ));
83                    }
84                }
85                self.len += 1;
86                Ok(())
87            }
88
89            fn append_null(&mut self) {
90                self.builder.append_null();
91                self.len += 1;
92            }
93
94            fn finish(&mut self) -> ArrayRef {
95                self.len = 0;
96                Arc::new(self.builder.finish())
97            }
98
99            fn len(&self) -> usize {
100                self.len
101            }
102
103            fn capacity(&self) -> Option<usize> {
104                Some(self.builder.capacity())
105            }
106        }
107    };
108}
109
110// ═══════════════════════════════════════════════════════════════════════════
111// Builder Implementations
112// ═══════════════════════════════════════════════════════════════════════════
113
114impl_primitive_builder!(UInt8BuilderWrapper, UInt8Builder, u8, TINYINT);
115impl_primitive_builder!(Int16BuilderWrapper, Int16Builder, i16, SMALLINT);
116impl_primitive_builder!(Int32BuilderWrapper, Int32Builder, i32, INT);
117impl_primitive_builder!(Int64BuilderWrapper, Int64Builder, i64, BIGINT);
118impl_primitive_builder!(Float32BuilderWrapper, Float32Builder, f32, REAL);
119impl_primitive_builder!(Float64BuilderWrapper, Float64Builder, f64, DOUBLE);
120
121#[cfg(test)]
122mod tests {
123    use arrow_array::{Array, Float32Array, Int16Array, Int32Array, Int64Array, UInt8Array};
124    use hdbconnect::HdbValue;
125
126    use super::*;
127
128    // ═══════════════════════════════════════════════════════════════════════════
129    // Int32BuilderWrapper Tests
130    // ═══════════════════════════════════════════════════════════════════════════
131
132    #[test]
133    fn test_int32_builder_wrapper() {
134        let mut builder = Int32BuilderWrapper::new(10);
135
136        assert_eq!(builder.len(), 0);
137        assert!(builder.is_empty());
138
139        builder.append_hana_value(&HdbValue::INT(42)).unwrap();
140        builder.append_null();
141        builder.append_hana_value(&HdbValue::INT(-100)).unwrap();
142
143        assert_eq!(builder.len(), 3);
144
145        let array = builder.finish();
146        assert_eq!(array.len(), 3);
147        assert_eq!(builder.len(), 0);
148    }
149
150    #[test]
151    fn test_int32_builder_default_capacity() {
152        let builder = Int32BuilderWrapper::default_capacity();
153        assert_eq!(builder.len(), 0);
154        assert!(builder.is_empty());
155    }
156
157    #[test]
158    fn test_int32_builder_wrong_type() {
159        let mut builder = Int32BuilderWrapper::new(1);
160        let result = builder.append_hana_value(&HdbValue::STRING("test".to_string()));
161        assert!(result.is_err());
162        assert!(result.unwrap_err().is_value_conversion());
163    }
164
165    #[test]
166    fn test_int32_builder_boundary_values() {
167        let mut builder = Int32BuilderWrapper::new(3);
168        builder.append_hana_value(&HdbValue::INT(i32::MAX)).unwrap();
169        builder.append_hana_value(&HdbValue::INT(i32::MIN)).unwrap();
170        builder.append_hana_value(&HdbValue::INT(0)).unwrap();
171
172        let array = builder.finish();
173        let int_array = array.as_any().downcast_ref::<Int32Array>().unwrap();
174        assert_eq!(int_array.value(0), i32::MAX);
175        assert_eq!(int_array.value(1), i32::MIN);
176        assert_eq!(int_array.value(2), 0);
177    }
178
179    #[test]
180    fn test_int32_builder_reuse() {
181        let mut builder = Int32BuilderWrapper::new(10);
182        builder.append_hana_value(&HdbValue::INT(1)).unwrap();
183        let _ = builder.finish();
184
185        builder.append_hana_value(&HdbValue::INT(2)).unwrap();
186        let array = builder.finish();
187        let int_array = array.as_any().downcast_ref::<Int32Array>().unwrap();
188        assert_eq!(int_array.value(0), 2);
189    }
190
191    // ═══════════════════════════════════════════════════════════════════════════
192    // Float64BuilderWrapper Tests
193    // ═══════════════════════════════════════════════════════════════════════════
194
195    #[test]
196    fn test_float64_builder_wrapper() {
197        let mut builder = Float64BuilderWrapper::new(5);
198
199        builder.append_hana_value(&HdbValue::DOUBLE(3.14)).unwrap();
200        builder.append_null();
201
202        let array = builder.finish();
203        assert_eq!(array.len(), 2);
204    }
205
206    #[test]
207    fn test_float64_builder_default_capacity() {
208        let builder = Float64BuilderWrapper::default_capacity();
209        assert_eq!(builder.len(), 0);
210    }
211
212    #[test]
213    fn test_float64_builder_special_values() {
214        let mut builder = Float64BuilderWrapper::new(5);
215        builder
216            .append_hana_value(&HdbValue::DOUBLE(f64::MAX))
217            .unwrap();
218        builder
219            .append_hana_value(&HdbValue::DOUBLE(f64::MIN))
220            .unwrap();
221        builder.append_hana_value(&HdbValue::DOUBLE(0.0)).unwrap();
222        builder.append_hana_value(&HdbValue::DOUBLE(-0.0)).unwrap();
223        builder
224            .append_hana_value(&HdbValue::DOUBLE(f64::INFINITY))
225            .unwrap();
226
227        let array = builder.finish();
228        assert_eq!(array.len(), 5);
229    }
230
231    #[test]
232    fn test_float64_builder_wrong_type() {
233        let mut builder = Float64BuilderWrapper::new(1);
234        let result = builder.append_hana_value(&HdbValue::INT(42));
235        assert!(result.is_err());
236        assert!(result.unwrap_err().is_value_conversion());
237    }
238
239    // ═══════════════════════════════════════════════════════════════════════════
240    // UInt8BuilderWrapper Tests
241    // ═══════════════════════════════════════════════════════════════════════════
242
243    #[test]
244    fn test_uint8_builder_wrapper() {
245        let mut builder = UInt8BuilderWrapper::new(5);
246
247        builder.append_hana_value(&HdbValue::TINYINT(255)).unwrap();
248        let array = builder.finish();
249        assert_eq!(array.len(), 1);
250    }
251
252    #[test]
253    fn test_uint8_builder_default_capacity() {
254        let builder = UInt8BuilderWrapper::default_capacity();
255        assert_eq!(builder.len(), 0);
256    }
257
258    #[test]
259    fn test_uint8_builder_boundary_values() {
260        let mut builder = UInt8BuilderWrapper::new(3);
261        builder.append_hana_value(&HdbValue::TINYINT(0)).unwrap();
262        builder.append_hana_value(&HdbValue::TINYINT(255)).unwrap();
263        builder.append_hana_value(&HdbValue::TINYINT(128)).unwrap();
264
265        let array = builder.finish();
266        let uint_array = array.as_any().downcast_ref::<UInt8Array>().unwrap();
267        assert_eq!(uint_array.value(0), 0);
268        assert_eq!(uint_array.value(1), 255);
269        assert_eq!(uint_array.value(2), 128);
270    }
271
272    #[test]
273    fn test_uint8_builder_wrong_type() {
274        let mut builder = UInt8BuilderWrapper::new(1);
275        let result = builder.append_hana_value(&HdbValue::STRING("test".to_string()));
276        assert!(result.is_err());
277    }
278
279    // ═══════════════════════════════════════════════════════════════════════════
280    // Int16BuilderWrapper Tests
281    // ═══════════════════════════════════════════════════════════════════════════
282
283    #[test]
284    fn test_int16_builder_wrapper() {
285        let mut builder = Int16BuilderWrapper::new(5);
286        builder
287            .append_hana_value(&HdbValue::SMALLINT(32767))
288            .unwrap();
289        builder
290            .append_hana_value(&HdbValue::SMALLINT(-32768))
291            .unwrap();
292        builder.append_null();
293
294        let array = builder.finish();
295        let int_array = array.as_any().downcast_ref::<Int16Array>().unwrap();
296        assert_eq!(int_array.value(0), 32767);
297        assert_eq!(int_array.value(1), -32768);
298        assert!(int_array.is_null(2));
299    }
300
301    #[test]
302    fn test_int16_builder_default_capacity() {
303        let builder = Int16BuilderWrapper::default_capacity();
304        assert_eq!(builder.len(), 0);
305    }
306
307    #[test]
308    fn test_int16_builder_wrong_type() {
309        let mut builder = Int16BuilderWrapper::new(1);
310        let result = builder.append_hana_value(&HdbValue::BIGINT(100));
311        assert!(result.is_err());
312    }
313
314    // ═══════════════════════════════════════════════════════════════════════════
315    // Int64BuilderWrapper Tests
316    // ═══════════════════════════════════════════════════════════════════════════
317
318    #[test]
319    fn test_int64_builder_wrapper() {
320        let mut builder = Int64BuilderWrapper::new(5);
321        builder
322            .append_hana_value(&HdbValue::BIGINT(i64::MAX))
323            .unwrap();
324        builder
325            .append_hana_value(&HdbValue::BIGINT(i64::MIN))
326            .unwrap();
327        builder.append_null();
328
329        let array = builder.finish();
330        let int_array = array.as_any().downcast_ref::<Int64Array>().unwrap();
331        assert_eq!(int_array.value(0), i64::MAX);
332        assert_eq!(int_array.value(1), i64::MIN);
333        assert!(int_array.is_null(2));
334    }
335
336    #[test]
337    fn test_int64_builder_default_capacity() {
338        let builder = Int64BuilderWrapper::default_capacity();
339        assert_eq!(builder.len(), 0);
340    }
341
342    #[test]
343    fn test_int64_builder_wrong_type() {
344        let mut builder = Int64BuilderWrapper::new(1);
345        let result = builder.append_hana_value(&HdbValue::DOUBLE(1.0));
346        assert!(result.is_err());
347    }
348
349    // ═══════════════════════════════════════════════════════════════════════════
350    // Float32BuilderWrapper Tests
351    // ═══════════════════════════════════════════════════════════════════════════
352
353    #[test]
354    fn test_float32_builder_wrapper() {
355        let mut builder = Float32BuilderWrapper::new(5);
356        builder.append_hana_value(&HdbValue::REAL(1.5)).unwrap();
357        builder.append_hana_value(&HdbValue::REAL(-2.5)).unwrap();
358        builder.append_null();
359
360        let array = builder.finish();
361        let float_array = array.as_any().downcast_ref::<Float32Array>().unwrap();
362        assert!((float_array.value(0) - 1.5).abs() < f32::EPSILON);
363        assert!((float_array.value(1) - (-2.5)).abs() < f32::EPSILON);
364        assert!(float_array.is_null(2));
365    }
366
367    #[test]
368    fn test_float32_builder_default_capacity() {
369        let builder = Float32BuilderWrapper::default_capacity();
370        assert_eq!(builder.len(), 0);
371    }
372
373    #[test]
374    fn test_float32_builder_wrong_type() {
375        let mut builder = Float32BuilderWrapper::new(1);
376        let result = builder.append_hana_value(&HdbValue::INT(42));
377        assert!(result.is_err());
378    }
379
380    // ═══════════════════════════════════════════════════════════════════════════
381    // Common Tests
382    // ═══════════════════════════════════════════════════════════════════════════
383
384    #[test]
385    fn test_capacity_hint() {
386        let builder = Int32BuilderWrapper::new(100);
387        assert_eq!(builder.capacity(), Some(100));
388    }
389
390    #[test]
391    fn test_all_builders_debug() {
392        let _ = format!("{:?}", UInt8BuilderWrapper::new(1));
393        let _ = format!("{:?}", Int16BuilderWrapper::new(1));
394        let _ = format!("{:?}", Int32BuilderWrapper::new(1));
395        let _ = format!("{:?}", Int64BuilderWrapper::new(1));
396        let _ = format!("{:?}", Float32BuilderWrapper::new(1));
397        let _ = format!("{:?}", Float64BuilderWrapper::new(1));
398    }
399
400    #[test]
401    fn test_all_builders_null_handling() {
402        let mut uint8 = UInt8BuilderWrapper::new(1);
403        uint8.append_null();
404        assert_eq!(uint8.len(), 1);
405
406        let mut int16 = Int16BuilderWrapper::new(1);
407        int16.append_null();
408        assert_eq!(int16.len(), 1);
409
410        let mut int32 = Int32BuilderWrapper::new(1);
411        int32.append_null();
412        assert_eq!(int32.len(), 1);
413
414        let mut int64 = Int64BuilderWrapper::new(1);
415        int64.append_null();
416        assert_eq!(int64.len(), 1);
417
418        let mut float32 = Float32BuilderWrapper::new(1);
419        float32.append_null();
420        assert_eq!(float32.len(), 1);
421
422        let mut float64 = Float64BuilderWrapper::new(1);
423        float64.append_null();
424        assert_eq!(float64.len(), 1);
425    }
426}