Skip to main content

exarrow_rs/arrow_conversion/
builders.rs

1//! Array builders for converting JSON values to Arrow arrays.
2//!
3//! This module provides type-specific builders that convert columnar JSON data
4//! into Arrow arrays with proper NULL handling.
5
6use crate::error::ConversionError;
7use crate::types::{
8    conversion::{
9        parse_date_to_days as parse_date_to_days_impl,
10        parse_decimal_to_i128 as parse_decimal_to_i128_impl,
11        parse_timestamp_to_micros as parse_timestamp_to_micros_impl,
12    },
13    ExasolType,
14};
15use arrow::array::{
16    ArrayRef, BinaryBuilder, BooleanBuilder, Date32Builder, Decimal128Builder, Float64Builder,
17    IntervalMonthDayNanoBuilder, StringBuilder, TimestampMicrosecondBuilder,
18};
19use serde_json::Value;
20use std::sync::Arc;
21
22/// Trait for building Arrow arrays from JSON values.
23pub trait ArrayBuilder {
24    /// Append a JSON value to the array builder.
25    ///
26    /// # Arguments
27    /// * `value` - The JSON value to append (or null)
28    /// * `row` - The row index for error reporting
29    /// * `column` - The column index for error reporting
30    fn append_value(
31        &mut self,
32        value: &Value,
33        row: usize,
34        column: usize,
35    ) -> Result<(), ConversionError>;
36
37    /// Finish building and return the Arrow array.
38    fn finish(&mut self) -> Result<ArrayRef, ConversionError>;
39}
40
41pub fn build_array(
42    exasol_type: &ExasolType,
43    values: &[&Value],
44    column: usize,
45) -> Result<ArrayRef, ConversionError> {
46    match exasol_type {
47        ExasolType::Boolean => build_boolean_array(values, column),
48        ExasolType::Char { .. } | ExasolType::Varchar { .. } => build_string_array(values, column),
49        // Exasol DECIMAL precision is 1-36; always fits in Decimal128
50        ExasolType::Decimal { precision, scale } => {
51            build_decimal128_array(values, *precision, *scale, column)
52        }
53        ExasolType::Double => build_double_array(values, column),
54        ExasolType::Date => build_date_array(values, column),
55        ExasolType::Timestamp {
56            with_local_time_zone,
57        } => build_timestamp_array(values, *with_local_time_zone, column),
58        ExasolType::IntervalYearToMonth => build_interval_year_to_month_array(values, column),
59        ExasolType::IntervalDayToSecond { .. } => {
60            build_interval_day_to_second_array(values, column)
61        }
62        ExasolType::Geometry { .. } | ExasolType::Hashtype { .. } => {
63            build_binary_array(values, column)
64        }
65    }
66}
67
68fn build_boolean_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
69    let mut builder = BooleanBuilder::with_capacity(values.len());
70
71    for (row, value) in values.iter().enumerate() {
72        if value.is_null() {
73            builder.append_null();
74        } else if let Some(b) = value.as_bool() {
75            builder.append_value(b);
76        } else {
77            return Err(ConversionError::ValueConversionFailed {
78                row,
79                column,
80                message: format!("Expected boolean, got: {:?}", value),
81            });
82        }
83    }
84
85    Ok(Arc::new(builder.finish()))
86}
87
88fn estimate_string_capacity(values: &[&Value]) -> usize {
89    const SAMPLE_SIZE: usize = 10;
90    const DEFAULT_AVG_LEN: usize = 32;
91
92    let mut total_len = 0;
93    let mut count = 0;
94
95    for value in values.iter().take(SAMPLE_SIZE) {
96        if let Some(s) = value.as_str() {
97            total_len += s.len();
98            count += 1;
99        }
100    }
101
102    if count > 0 {
103        let avg_len = total_len / count;
104        // Add some buffer and multiply by total values
105        (avg_len + 8) * values.len()
106    } else {
107        DEFAULT_AVG_LEN * values.len()
108    }
109}
110
111fn build_string_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
112    // Use better capacity estimation based on actual string lengths
113    let estimated_bytes = estimate_string_capacity(values);
114    let mut builder = StringBuilder::with_capacity(values.len(), estimated_bytes);
115
116    for (row, value) in values.iter().enumerate() {
117        if value.is_null() {
118            builder.append_null();
119        } else if let Some(s) = value.as_str() {
120            // UTF-8 validation is automatic in Rust strings
121            builder.append_value(s);
122        } else {
123            return Err(ConversionError::ValueConversionFailed {
124                row,
125                column,
126                message: format!("Expected string, got: {:?}", value),
127            });
128        }
129    }
130
131    Ok(Arc::new(builder.finish()))
132}
133
134/// Validate that a decimal string fits within the target precision.
135///
136/// This function checks that the total number of significant digits in the
137/// decimal value does not exceed the specified precision.
138///
139/// # Arguments
140/// * `value_str` - The decimal value as a string
141/// * `precision` - The maximum number of significant digits allowed
142/// * `_scale` - The scale (reserved for future use)
143/// * `row` - The row index for error reporting
144/// * `column` - The column index for error reporting
145///
146/// # Returns
147/// `Ok(())` if the value fits within precision, or `ConversionError::NumericOverflow` otherwise.
148fn validate_decimal_precision(
149    value_str: &str,
150    precision: u8,
151    _scale: i8,
152    row: usize,
153    column: usize,
154) -> Result<(), ConversionError> {
155    // Remove sign and decimal point for digit counting
156    let digits: String = value_str.chars().filter(|c| c.is_ascii_digit()).collect();
157
158    let total_digits = digits.len();
159    let max_digits = precision as usize;
160
161    // Also strip leading zeros for accurate precision check
162    let significant_digits = digits.trim_start_matches('0');
163    let significant_count = if significant_digits.is_empty() {
164        1 // "0" has 1 significant digit
165    } else {
166        significant_digits.len()
167    };
168
169    if significant_count > max_digits {
170        return Err(ConversionError::NumericOverflow { row, column });
171    }
172
173    // Also check total raw digit count as a sanity check
174    // (precision includes both integer and fractional parts)
175    if total_digits > max_digits + 1 {
176        // +1 for potential leading zero
177        return Err(ConversionError::NumericOverflow { row, column });
178    }
179
180    Ok(())
181}
182
183fn build_decimal128_array(
184    values: &[&Value],
185    precision: u8,
186    scale: i8,
187    column: usize,
188) -> Result<ArrayRef, ConversionError> {
189    let mut builder = Decimal128Builder::with_capacity(values.len())
190        .with_precision_and_scale(precision, scale)
191        .map_err(|e| ConversionError::ArrowError(e.to_string()))?;
192
193    for (row, value) in values.iter().enumerate() {
194        if value.is_null() {
195            builder.append_null();
196        } else {
197            let decimal_value = parse_decimal_to_i128(value, precision, scale, row, column)?;
198            builder.append_value(decimal_value);
199        }
200    }
201
202    Ok(Arc::new(builder.finish()))
203}
204
205/// Parse a JSON value to i128 for Decimal128.
206fn parse_decimal_to_i128(
207    value: &Value,
208    precision: u8,
209    scale: i8,
210    row: usize,
211    column: usize,
212) -> Result<i128, ConversionError> {
213    // Handle non-string JSON values directly
214    if let Some(n) = value.as_i64() {
215        return Ok((n as i128) * 10_i128.pow(scale as u32));
216    } else if let Some(n) = value.as_f64() {
217        // Convert float to decimal by multiplying by 10^scale
218        let scaled = (n * 10_f64.powi(scale as i32)).round();
219        return scaled
220            .to_string()
221            .parse::<i128>()
222            .map_err(|_| ConversionError::NumericOverflow { row, column });
223    }
224
225    // Handle string values using shared implementation
226    let value_str = value
227        .as_str()
228        .ok_or_else(|| ConversionError::ValueConversionFailed {
229            row,
230            column,
231            message: format!("Expected numeric value, got: {:?}", value),
232        })?;
233
234    // Validate precision before conversion
235    validate_decimal_precision(value_str, precision, scale, row, column)?;
236
237    // Use shared decimal parsing implementation
238    parse_decimal_to_i128_impl(value_str, scale).map_err(|e| {
239        ConversionError::ValueConversionFailed {
240            row,
241            column,
242            message: e,
243        }
244    })
245}
246
247fn build_double_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
248    let mut builder = Float64Builder::with_capacity(values.len());
249
250    for (row, value) in values.iter().enumerate() {
251        if value.is_null() {
252            builder.append_null();
253        } else if let Some(f) = value.as_f64() {
254            builder.append_value(f);
255        } else if let Some(i) = value.as_i64() {
256            builder.append_value(i as f64);
257        } else if let Some(s) = value.as_str() {
258            // Handle special values like "Infinity", "-Infinity", "NaN"
259            let f = match s {
260                "Infinity" => f64::INFINITY,
261                "-Infinity" => f64::NEG_INFINITY,
262                "NaN" => f64::NAN,
263                _ => s
264                    .parse::<f64>()
265                    .map_err(|_| ConversionError::ValueConversionFailed {
266                        row,
267                        column,
268                        message: format!("Invalid float value: {}", s),
269                    })?,
270            };
271            builder.append_value(f);
272        } else {
273            return Err(ConversionError::ValueConversionFailed {
274                row,
275                column,
276                message: format!("Expected number, got: {:?}", value),
277            });
278        }
279    }
280
281    Ok(Arc::new(builder.finish()))
282}
283
284fn build_date_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
285    let mut builder = Date32Builder::with_capacity(values.len());
286
287    for (row, value) in values.iter().enumerate() {
288        if value.is_null() {
289            builder.append_null();
290        } else if let Some(s) = value.as_str() {
291            // Parse date string in format "YYYY-MM-DD"
292            let days = parse_date_to_days(s, row, column)?;
293            builder.append_value(days);
294        } else {
295            return Err(ConversionError::ValueConversionFailed {
296                row,
297                column,
298                message: format!("Expected date string, got: {:?}", value),
299            });
300        }
301    }
302
303    Ok(Arc::new(builder.finish()))
304}
305
306/// Parse a date string to days since Unix epoch (1970-01-01).
307fn parse_date_to_days(date_str: &str, row: usize, column: usize) -> Result<i32, ConversionError> {
308    parse_date_to_days_impl(date_str).map_err(|e| ConversionError::ValueConversionFailed {
309        row,
310        column,
311        message: e,
312    })
313}
314
315fn build_timestamp_array(
316    values: &[&Value],
317    _with_local_time_zone: bool,
318    column: usize,
319) -> Result<ArrayRef, ConversionError> {
320    let mut builder = TimestampMicrosecondBuilder::with_capacity(values.len());
321
322    for (row, value) in values.iter().enumerate() {
323        if value.is_null() {
324            builder.append_null();
325        } else if let Some(s) = value.as_str() {
326            // Parse timestamp string in format "YYYY-MM-DD HH:MM:SS.ffffff"
327            let micros = parse_timestamp_to_micros(s, row, column)?;
328            builder.append_value(micros);
329        } else {
330            return Err(ConversionError::ValueConversionFailed {
331                row,
332                column,
333                message: format!("Expected timestamp string, got: {:?}", value),
334            });
335        }
336    }
337
338    Ok(Arc::new(builder.finish()))
339}
340
341/// Parse a timestamp string to microseconds since Unix epoch.
342fn parse_timestamp_to_micros(
343    timestamp_str: &str,
344    row: usize,
345    column: usize,
346) -> Result<i64, ConversionError> {
347    parse_timestamp_to_micros_impl(timestamp_str).map_err(|e| {
348        ConversionError::ValueConversionFailed {
349            row,
350            column,
351            message: e,
352        }
353    })
354}
355
356fn build_interval_year_to_month_array(
357    values: &[&Value],
358    column: usize,
359) -> Result<ArrayRef, ConversionError> {
360    let mut builder = IntervalMonthDayNanoBuilder::with_capacity(values.len());
361
362    for (row, value) in values.iter().enumerate() {
363        if value.is_null() {
364            builder.append_null();
365        } else if let Some(s) = value.as_str() {
366            // Parse format "+YY-MM" or "-YY-MM"
367            let months = parse_interval_year_to_month(s, row, column)?;
368            // IntervalMonthDayNano stores months as i32, days as i32, nanoseconds as i64
369            let interval = arrow::datatypes::IntervalMonthDayNano {
370                months,
371                days: 0,
372                nanoseconds: 0,
373            };
374            builder.append_value(interval);
375        } else {
376            return Err(ConversionError::ValueConversionFailed {
377                row,
378                column,
379                message: format!("Expected interval string, got: {:?}", value),
380            });
381        }
382    }
383
384    Ok(Arc::new(builder.finish()))
385}
386
387/// Parse INTERVAL YEAR TO MONTH string to months.
388fn parse_interval_year_to_month(
389    interval_str: &str,
390    row: usize,
391    column: usize,
392) -> Result<i32, ConversionError> {
393    // Format: "+YY-MM" or "-YY-MM"
394    let is_negative = interval_str.starts_with('-');
395    let parts: Vec<&str> = interval_str
396        .trim_start_matches(&['+', '-'][..])
397        .split('-')
398        .collect();
399
400    if parts.len() != 2 {
401        return Err(ConversionError::ValueConversionFailed {
402            row,
403            column,
404            message: format!("Invalid interval format: {}", interval_str),
405        });
406    }
407
408    let years: i32 = parts[0]
409        .parse()
410        .map_err(|_| ConversionError::ValueConversionFailed {
411            row,
412            column,
413            message: format!("Invalid years: {}", parts[0]),
414        })?;
415
416    let months: i32 = parts[1]
417        .parse()
418        .map_err(|_| ConversionError::ValueConversionFailed {
419            row,
420            column,
421            message: format!("Invalid months: {}", parts[1]),
422        })?;
423
424    let total_months = years * 12 + months;
425    Ok(if is_negative {
426        -total_months
427    } else {
428        total_months
429    })
430}
431
432fn build_interval_day_to_second_array(
433    values: &[&Value],
434    column: usize,
435) -> Result<ArrayRef, ConversionError> {
436    let mut builder = IntervalMonthDayNanoBuilder::with_capacity(values.len());
437
438    for (row, value) in values.iter().enumerate() {
439        if value.is_null() {
440            builder.append_null();
441        } else if let Some(s) = value.as_str() {
442            // Parse format "+DD HH:MM:SS.ffffff"
443            let (days, nanos) = parse_interval_day_to_second(s, row, column)?;
444            // IntervalMonthDayNano stores months as i32, days as i32, nanoseconds as i64
445            let interval = arrow::datatypes::IntervalMonthDayNano {
446                months: 0,
447                days,
448                nanoseconds: nanos,
449            };
450            builder.append_value(interval);
451        } else {
452            return Err(ConversionError::ValueConversionFailed {
453                row,
454                column,
455                message: format!("Expected interval string, got: {:?}", value),
456            });
457        }
458    }
459
460    Ok(Arc::new(builder.finish()))
461}
462
463/// Parse INTERVAL DAY TO SECOND string to (days, nanoseconds).
464fn parse_interval_day_to_second(
465    interval_str: &str,
466    row: usize,
467    column: usize,
468) -> Result<(i32, i64), ConversionError> {
469    // Format: "+DD HH:MM:SS.ffffff" or "-DD HH:MM:SS.ffffff"
470    let is_negative = interval_str.starts_with('-');
471    let trimmed = interval_str.trim_start_matches(&['+', '-'][..]);
472    let parts: Vec<&str> = trimmed.split(' ').collect();
473
474    if parts.is_empty() {
475        return Err(ConversionError::ValueConversionFailed {
476            row,
477            column,
478            message: format!("Invalid interval format: {}", interval_str),
479        });
480    }
481
482    let days: i32 = parts[0]
483        .parse()
484        .map_err(|_| ConversionError::ValueConversionFailed {
485            row,
486            column,
487            message: format!("Invalid days: {}", parts[0]),
488        })?;
489
490    let mut nanos: i64 = 0;
491
492    if parts.len() > 1 {
493        let time_parts: Vec<&str> = parts[1].split(':').collect();
494        if time_parts.len() >= 2 {
495            let hours: i64 =
496                time_parts[0]
497                    .parse()
498                    .map_err(|_| ConversionError::ValueConversionFailed {
499                        row,
500                        column,
501                        message: format!("Invalid hours: {}", time_parts[0]),
502                    })?;
503
504            let minutes: i64 =
505                time_parts[1]
506                    .parse()
507                    .map_err(|_| ConversionError::ValueConversionFailed {
508                        row,
509                        column,
510                        message: format!("Invalid minutes: {}", time_parts[1]),
511                    })?;
512
513            nanos += hours * 3600 * 1_000_000_000;
514            nanos += minutes * 60 * 1_000_000_000;
515
516            if time_parts.len() >= 3 {
517                let sec_parts: Vec<&str> = time_parts[2].split('.').collect();
518                let seconds: i64 =
519                    sec_parts[0]
520                        .parse()
521                        .map_err(|_| ConversionError::ValueConversionFailed {
522                            row,
523                            column,
524                            message: format!("Invalid seconds: {}", sec_parts[0]),
525                        })?;
526
527                nanos += seconds * 1_000_000_000;
528
529                if sec_parts.len() > 1 {
530                    // Parse fractional seconds (nanoseconds)
531                    let frac = sec_parts[1];
532                    let frac_nanos = if frac.len() <= 9 {
533                        let padding = 9 - frac.len();
534                        let padded = format!("{}{}", frac, "0".repeat(padding));
535                        padded.parse::<i64>().unwrap_or(0)
536                    } else {
537                        frac[..9].parse::<i64>().unwrap_or(0)
538                    };
539                    nanos += frac_nanos;
540                }
541            }
542        }
543    }
544
545    Ok(if is_negative {
546        (-days, -nanos)
547    } else {
548        (days, nanos)
549    })
550}
551
552fn build_binary_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
553    // Estimate binary capacity: hex strings are 2 chars per byte
554    // Sample up to 10 values to estimate average size
555    const SAMPLE_SIZE: usize = 10;
556    let sample_count = values.len().min(SAMPLE_SIZE);
557    let sample_total: usize = values
558        .iter()
559        .take(SAMPLE_SIZE)
560        .filter_map(|v| v.as_str())
561        .map(|s| s.len() / 2)
562        .sum();
563
564    let estimated_bytes = if sample_count > 0 {
565        let avg_size = (sample_total / sample_count).max(1);
566        avg_size * values.len()
567    } else {
568        values.len() * 32 // Default estimate
569    };
570
571    let mut builder = BinaryBuilder::with_capacity(values.len(), estimated_bytes.max(1024));
572
573    for (row, value) in values.iter().enumerate() {
574        if value.is_null() {
575            builder.append_null();
576        } else if let Some(s) = value.as_str() {
577            // Assume hex-encoded string, decode to bytes
578            let bytes = decode_hex(s).map_err(|_| ConversionError::ValueConversionFailed {
579                row,
580                column,
581                message: format!("Invalid hex string: {}", s),
582            })?;
583            builder.append_value(&bytes);
584        } else {
585            return Err(ConversionError::ValueConversionFailed {
586                row,
587                column,
588                message: format!("Expected hex string, got: {:?}", value),
589            });
590        }
591    }
592
593    Ok(Arc::new(builder.finish()))
594}
595
596/// Decode a hex string to bytes.
597fn decode_hex(s: &str) -> Result<Vec<u8>, ()> {
598    if !s.len().is_multiple_of(2) {
599        return Err(());
600    }
601
602    (0..s.len())
603        .step_by(2)
604        .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| ()))
605        .collect()
606}
607
608#[cfg(test)]
609mod tests {
610    use super::*;
611    use serde_json::json;
612
613    /// Helper to convert owned values to references for testing
614    fn to_refs(values: &[Value]) -> Vec<&Value> {
615        values.iter().collect()
616    }
617
618    #[test]
619    fn test_build_boolean_array() {
620        let values = vec![json!(true), json!(false), json!(null), json!(true)];
621        let refs = to_refs(&values);
622        let array = build_boolean_array(&refs, 0).unwrap();
623        assert_eq!(array.len(), 4);
624        assert_eq!(array.null_count(), 1);
625    }
626
627    #[test]
628    fn test_build_string_array() {
629        let values = vec![json!("hello"), json!("world"), json!(null), json!("test")];
630        let refs = to_refs(&values);
631        let array = build_string_array(&refs, 0).unwrap();
632        assert_eq!(array.len(), 4);
633        assert_eq!(array.null_count(), 1);
634    }
635
636    #[test]
637    fn test_build_decimal128_array() {
638        let values = vec![json!("123.45"), json!("678.90"), json!(null)];
639        let refs = to_refs(&values);
640        let array = build_decimal128_array(&refs, 10, 2, 0).unwrap();
641        assert_eq!(array.len(), 3);
642        assert_eq!(array.null_count(), 1);
643    }
644
645    #[test]
646    fn test_build_double_array() {
647        let values = vec![json!(1.5), json!(2.7), json!(null), json!("Infinity")];
648        let refs = to_refs(&values);
649        let array = build_double_array(&refs, 0).unwrap();
650        assert_eq!(array.len(), 4);
651        assert_eq!(array.null_count(), 1);
652    }
653
654    #[test]
655    fn test_parse_date_to_days() {
656        // 1970-01-01 should be day 0
657        let days = parse_date_to_days("1970-01-01", 0, 0).unwrap();
658        assert_eq!(days, 0);
659
660        // 1970-01-02 should be day 1
661        let days = parse_date_to_days("1970-01-02", 0, 0).unwrap();
662        assert_eq!(days, 1);
663    }
664
665    #[test]
666    fn test_parse_timestamp_to_micros() {
667        // 1970-01-01 00:00:00 should be 0 microseconds
668        let micros = parse_timestamp_to_micros("1970-01-01 00:00:00", 0, 0).unwrap();
669        assert_eq!(micros, 0);
670
671        // 1970-01-01 00:00:01 should be 1,000,000 microseconds
672        let micros = parse_timestamp_to_micros("1970-01-01 00:00:01", 0, 0).unwrap();
673        assert_eq!(micros, 1_000_000);
674    }
675
676    #[test]
677    fn test_parse_interval_year_to_month() {
678        let months = parse_interval_year_to_month("+01-06", 0, 0).unwrap();
679        assert_eq!(months, 18); // 1 year + 6 months
680
681        let months = parse_interval_year_to_month("-02-03", 0, 0).unwrap();
682        assert_eq!(months, -27); // -(2 years + 3 months)
683    }
684
685    #[test]
686    fn test_parse_interval_day_to_second() {
687        let (days, nanos) = parse_interval_day_to_second("+01 12:30:45.123456789", 0, 0).unwrap();
688        assert_eq!(days, 1);
689        assert_eq!(
690            nanos,
691            12 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000 + 45 * 1_000_000_000 + 123_456_789
692        );
693    }
694
695    #[test]
696    fn test_decode_hex() {
697        let bytes = decode_hex("48656c6c6f").unwrap();
698        assert_eq!(bytes, b"Hello");
699
700        let bytes = decode_hex("").unwrap();
701        assert_eq!(bytes, b"");
702
703        assert!(decode_hex("xyz").is_err());
704        assert!(decode_hex("1").is_err()); // Odd length
705    }
706
707    #[test]
708    fn test_parse_decimal_to_i128() {
709        // Test integer
710        let result = parse_decimal_to_i128(&json!("123"), 10, 2, 0, 0).unwrap();
711        assert_eq!(result, 12300); // 123 * 10^2
712
713        // Test decimal
714        let result = parse_decimal_to_i128(&json!("123.45"), 10, 2, 0, 0).unwrap();
715        assert_eq!(result, 12345); // 123.45 * 10^2
716
717        // Test negative
718        let result = parse_decimal_to_i128(&json!("-123.45"), 10, 2, 0, 0).unwrap();
719        assert_eq!(result, -12345);
720
721        // Test from number
722        let result = parse_decimal_to_i128(&json!(123), 10, 2, 0, 0).unwrap();
723        assert_eq!(result, 12300);
724    }
725
726    #[test]
727    fn test_invalid_conversions() {
728        // Invalid boolean
729        let values = vec![json!("not a bool")];
730        let refs = to_refs(&values);
731        assert!(build_boolean_array(&refs, 0).is_err());
732
733        // Invalid date
734        let values = vec![json!("2024-13-01")]; // Invalid month
735        let refs = to_refs(&values);
736        assert!(build_date_array(&refs, 0).is_err());
737
738        // Invalid hex
739        let values = vec![json!("zzz")];
740        let refs = to_refs(&values);
741        assert!(build_binary_array(&refs, 0).is_err());
742    }
743
744    #[test]
745    fn test_validate_decimal_precision() {
746        // Valid: 5 digits fits in precision 10
747        assert!(validate_decimal_precision("12345", 10, 2, 0, 0).is_ok());
748
749        // Valid: decimal point doesn't count
750        assert!(validate_decimal_precision("123.45", 10, 2, 0, 0).is_ok());
751
752        // Valid: sign doesn't count
753        assert!(validate_decimal_precision("-12345", 10, 2, 0, 0).is_ok());
754
755        // Invalid: 6 significant digits in precision 5
756        assert!(validate_decimal_precision("123456", 5, 2, 0, 0).is_err());
757
758        // Valid: leading zeros don't count as significant
759        assert!(validate_decimal_precision("00123", 5, 2, 0, 0).is_ok());
760
761        // Valid: zero
762        assert!(validate_decimal_precision("0", 1, 0, 0, 0).is_ok());
763
764        // Valid: zero with leading zeros
765        assert!(validate_decimal_precision("000", 5, 0, 0, 0).is_ok());
766    }
767
768    #[test]
769    fn test_decimal_precision_overflow() {
770        // Test that precision overflow is detected
771        let values = vec![json!("12345678901234567890")]; // 20 digits
772        let refs = to_refs(&values);
773        let result = build_decimal128_array(&refs, 10, 2, 0);
774        assert!(result.is_err());
775
776        // Should succeed with sufficient precision
777        let values = vec![json!("12345")]; // 5 digits
778        let refs = to_refs(&values);
779        let result = build_decimal128_array(&refs, 10, 2, 0);
780        assert!(result.is_ok());
781    }
782
783    #[test]
784    fn test_estimate_string_capacity() {
785        // Test with normal strings
786        let values = vec![json!("hello"), json!("world"), json!("test")];
787        let refs = to_refs(&values);
788        let capacity = estimate_string_capacity(&refs);
789        // Average length is about 5, plus buffer, times 3 values
790        assert!(capacity > 0);
791        assert!(capacity >= 15); // At least the actual total length
792
793        // Test with empty values - should return 0
794        let empty_values: Vec<Value> = vec![];
795        let empty_refs = to_refs(&empty_values);
796        let empty_capacity = estimate_string_capacity(&empty_refs);
797        assert_eq!(empty_capacity, 0);
798
799        // Test with all nulls - should use default average
800        let null_values = vec![json!(null), json!(null), json!(null)];
801        let null_refs = to_refs(&null_values);
802        let null_capacity = estimate_string_capacity(&null_refs);
803        assert!(null_capacity > 0);
804
805        // Test with mixed nulls and strings
806        let mixed_values = vec![json!("hello"), json!(null), json!("world")];
807        let mixed_refs = to_refs(&mixed_values);
808        let mixed_capacity = estimate_string_capacity(&mixed_refs);
809        assert!(mixed_capacity > 0);
810    }
811
812    // ==========================================================================
813    // Tests for build_array dispatch
814    // ==========================================================================
815
816    #[test]
817    fn test_build_array_dispatches_to_boolean() {
818        let values = vec![json!(true), json!(false)];
819        let refs = to_refs(&values);
820        let result = build_array(&ExasolType::Boolean, &refs, 0).unwrap();
821        assert_eq!(result.len(), 2);
822    }
823
824    #[test]
825    fn test_build_array_dispatches_to_char() {
826        let values = vec![json!("abc"), json!("def")];
827        let refs = to_refs(&values);
828        let result = build_array(&ExasolType::Char { size: 3 }, &refs, 0).unwrap();
829        assert_eq!(result.len(), 2);
830    }
831
832    #[test]
833    fn test_build_array_dispatches_to_varchar() {
834        let values = vec![json!("hello"), json!("world")];
835        let refs = to_refs(&values);
836        let result = build_array(&ExasolType::Varchar { size: 100 }, &refs, 0).unwrap();
837        assert_eq!(result.len(), 2);
838    }
839
840    #[test]
841    fn test_build_array_dispatches_to_decimal() {
842        let values = vec![json!("123.45"), json!("678.90")];
843        let refs = to_refs(&values);
844        let result = build_array(
845            &ExasolType::Decimal {
846                precision: 10,
847                scale: 2,
848            },
849            &refs,
850            0,
851        )
852        .unwrap();
853        assert_eq!(result.len(), 2);
854    }
855
856    #[test]
857    fn test_build_array_dispatches_to_double() {
858        let values = vec![json!(1.5), json!(2.5)];
859        let refs = to_refs(&values);
860        let result = build_array(&ExasolType::Double, &refs, 0).unwrap();
861        assert_eq!(result.len(), 2);
862    }
863
864    #[test]
865    fn test_build_array_dispatches_to_date() {
866        let values = vec![json!("2024-01-15"), json!("2024-06-20")];
867        let refs = to_refs(&values);
868        let result = build_array(&ExasolType::Date, &refs, 0).unwrap();
869        assert_eq!(result.len(), 2);
870    }
871
872    #[test]
873    fn test_build_array_dispatches_to_timestamp_without_tz() {
874        let values = vec![json!("2024-01-15 10:30:00"), json!("2024-06-20 14:45:30")];
875        let refs = to_refs(&values);
876        let result = build_array(
877            &ExasolType::Timestamp {
878                with_local_time_zone: false,
879            },
880            &refs,
881            0,
882        )
883        .unwrap();
884        assert_eq!(result.len(), 2);
885    }
886
887    #[test]
888    fn test_build_array_dispatches_to_timestamp_with_tz() {
889        let values = vec![json!("2024-01-15 10:30:00"), json!("2024-06-20 14:45:30")];
890        let refs = to_refs(&values);
891        let result = build_array(
892            &ExasolType::Timestamp {
893                with_local_time_zone: true,
894            },
895            &refs,
896            0,
897        )
898        .unwrap();
899        assert_eq!(result.len(), 2);
900    }
901
902    #[test]
903    fn test_build_array_dispatches_to_interval_year_to_month() {
904        let values = vec![json!("+01-06"), json!("-02-03")];
905        let refs = to_refs(&values);
906        let result = build_array(&ExasolType::IntervalYearToMonth, &refs, 0).unwrap();
907        assert_eq!(result.len(), 2);
908    }
909
910    #[test]
911    fn test_build_array_dispatches_to_interval_day_to_second() {
912        let values = vec![json!("+01 12:30:45"), json!("-02 08:15:30")];
913        let refs = to_refs(&values);
914        let result =
915            build_array(&ExasolType::IntervalDayToSecond { precision: 3 }, &refs, 0).unwrap();
916        assert_eq!(result.len(), 2);
917    }
918
919    #[test]
920    fn test_build_array_dispatches_to_geometry() {
921        let values = vec![json!("48656c6c6f"), json!("576f726c64")];
922        let refs = to_refs(&values);
923        let result = build_array(&ExasolType::Geometry { srid: Some(4326) }, &refs, 0).unwrap();
924        assert_eq!(result.len(), 2);
925    }
926
927    #[test]
928    fn test_build_array_dispatches_to_hashtype() {
929        let values = vec![json!("deadbeef"), json!("cafebabe")];
930        let refs = to_refs(&values);
931        let result = build_array(&ExasolType::Hashtype { byte_size: 16 }, &refs, 0).unwrap();
932        assert_eq!(result.len(), 2);
933    }
934
935    // ==========================================================================
936    // Tests for build_date_array
937    // ==========================================================================
938
939    #[test]
940    fn test_build_date_array_with_valid_dates() {
941        let values = vec![
942            json!("2024-01-15"),
943            json!("2023-06-20"),
944            json!("1970-01-01"),
945        ];
946        let refs = to_refs(&values);
947        let result = build_date_array(&refs, 0).unwrap();
948        assert_eq!(result.len(), 3);
949        assert_eq!(result.null_count(), 0);
950    }
951
952    #[test]
953    fn test_build_date_array_with_null_values() {
954        let values = vec![json!("2024-01-15"), json!(null), json!("2023-06-20")];
955        let refs = to_refs(&values);
956        let result = build_date_array(&refs, 0).unwrap();
957        assert_eq!(result.len(), 3);
958        assert_eq!(result.null_count(), 1);
959    }
960
961    #[test]
962    fn test_build_date_array_invalid_format_not_string() {
963        let values = vec![json!(12345)];
964        let refs = to_refs(&values);
965        let result = build_date_array(&refs, 0);
966        assert!(result.is_err());
967        match result.unwrap_err() {
968            ConversionError::ValueConversionFailed { message, .. } => {
969                assert!(message.contains("Expected date string"));
970            }
971            _ => panic!("Expected ValueConversionFailed"),
972        }
973    }
974
975    #[test]
976    fn test_build_date_array_invalid_date_format() {
977        let values = vec![json!("2024/01/15")]; // Wrong separator
978        let refs = to_refs(&values);
979        let result = build_date_array(&refs, 0);
980        assert!(result.is_err());
981    }
982
983    #[test]
984    fn test_build_date_array_invalid_month_out_of_range() {
985        let values = vec![json!("2024-13-01")]; // Month 13
986        let refs = to_refs(&values);
987        let result = build_date_array(&refs, 0);
988        assert!(result.is_err());
989    }
990
991    #[test]
992    fn test_build_date_array_invalid_day_out_of_range() {
993        let values = vec![json!("2024-01-32")]; // Day 32
994        let refs = to_refs(&values);
995        let result = build_date_array(&refs, 0);
996        assert!(result.is_err());
997    }
998
999    #[test]
1000    fn test_build_date_array_invalid_month_zero() {
1001        let values = vec![json!("2024-00-15")]; // Month 0
1002        let refs = to_refs(&values);
1003        let result = build_date_array(&refs, 0);
1004        assert!(result.is_err());
1005    }
1006
1007    #[test]
1008    fn test_build_date_array_invalid_day_zero() {
1009        let values = vec![json!("2024-01-00")]; // Day 0
1010        let refs = to_refs(&values);
1011        let result = build_date_array(&refs, 0);
1012        assert!(result.is_err());
1013    }
1014
1015    // ==========================================================================
1016    // Tests for parse_date_to_days - all months
1017    // ==========================================================================
1018
1019    #[test]
1020    fn test_parse_date_to_days_january() {
1021        let days = parse_date_to_days("1970-01-15", 0, 0).unwrap();
1022        assert_eq!(days, 14); // 15th day, 0-indexed
1023    }
1024
1025    #[test]
1026    fn test_parse_date_to_days_february() {
1027        let days = parse_date_to_days("1970-02-01", 0, 0).unwrap();
1028        assert_eq!(days, 31); // Jan has 31 days
1029    }
1030
1031    #[test]
1032    fn test_parse_date_to_days_march() {
1033        let days = parse_date_to_days("1970-03-01", 0, 0).unwrap();
1034        assert_eq!(days, 59); // Jan(31) + Feb(28)
1035    }
1036
1037    #[test]
1038    fn test_parse_date_to_days_april() {
1039        let days = parse_date_to_days("1970-04-01", 0, 0).unwrap();
1040        assert_eq!(days, 90); // Jan(31) + Feb(28) + Mar(31)
1041    }
1042
1043    #[test]
1044    fn test_parse_date_to_days_may() {
1045        let days = parse_date_to_days("1970-05-01", 0, 0).unwrap();
1046        assert_eq!(days, 120); // Jan(31) + Feb(28) + Mar(31) + Apr(30)
1047    }
1048
1049    #[test]
1050    fn test_parse_date_to_days_june() {
1051        let days = parse_date_to_days("1970-06-01", 0, 0).unwrap();
1052        assert_eq!(days, 151);
1053    }
1054
1055    #[test]
1056    fn test_parse_date_to_days_july() {
1057        let days = parse_date_to_days("1970-07-01", 0, 0).unwrap();
1058        assert_eq!(days, 181);
1059    }
1060
1061    #[test]
1062    fn test_parse_date_to_days_august() {
1063        let days = parse_date_to_days("1970-08-01", 0, 0).unwrap();
1064        assert_eq!(days, 212);
1065    }
1066
1067    #[test]
1068    fn test_parse_date_to_days_september() {
1069        let days = parse_date_to_days("1970-09-01", 0, 0).unwrap();
1070        assert_eq!(days, 243);
1071    }
1072
1073    #[test]
1074    fn test_parse_date_to_days_october() {
1075        let days = parse_date_to_days("1970-10-01", 0, 0).unwrap();
1076        assert_eq!(days, 273);
1077    }
1078
1079    #[test]
1080    fn test_parse_date_to_days_november() {
1081        let days = parse_date_to_days("1970-11-01", 0, 0).unwrap();
1082        assert_eq!(days, 304);
1083    }
1084
1085    #[test]
1086    fn test_parse_date_to_days_december() {
1087        let days = parse_date_to_days("1970-12-01", 0, 0).unwrap();
1088        assert_eq!(days, 334);
1089    }
1090
1091    // ==========================================================================
1092    // Tests for leap year handling
1093    // ==========================================================================
1094
1095    #[test]
1096    fn test_parse_date_to_days_leap_year_2000() {
1097        // 2000 is a leap year (divisible by 400)
1098        // March 1, 2000 should include the leap day
1099        let march_1_2000 = parse_date_to_days("2000-03-01", 0, 0).unwrap();
1100        let feb_28_2000 = parse_date_to_days("2000-02-28", 0, 0).unwrap();
1101        assert_eq!(march_1_2000 - feb_28_2000, 2); // Feb 29 exists
1102    }
1103
1104    #[test]
1105    fn test_parse_date_to_days_leap_year_2004() {
1106        // 2004 is a leap year (divisible by 4, not by 100)
1107        let march_1_2004 = parse_date_to_days("2004-03-01", 0, 0).unwrap();
1108        let feb_28_2004 = parse_date_to_days("2004-02-28", 0, 0).unwrap();
1109        assert_eq!(march_1_2004 - feb_28_2004, 2); // Feb 29 exists
1110    }
1111
1112    #[test]
1113    fn test_parse_date_to_days_non_leap_year_1900() {
1114        // 1900 is NOT a leap year (divisible by 100, not by 400)
1115        let march_1_1900 = parse_date_to_days("1900-03-01", 0, 0).unwrap();
1116        let feb_28_1900 = parse_date_to_days("1900-02-28", 0, 0).unwrap();
1117        assert_eq!(march_1_1900 - feb_28_1900, 1); // No Feb 29
1118    }
1119
1120    #[test]
1121    fn test_parse_date_to_days_non_leap_year_2023() {
1122        // 2023 is NOT a leap year
1123        let march_1_2023 = parse_date_to_days("2023-03-01", 0, 0).unwrap();
1124        let feb_28_2023 = parse_date_to_days("2023-02-28", 0, 0).unwrap();
1125        assert_eq!(march_1_2023 - feb_28_2023, 1); // No Feb 29
1126    }
1127
1128    #[test]
1129    fn test_parse_date_to_days_leap_adjustment_only_after_february() {
1130        // In a leap year, January should not have the leap adjustment
1131        let jan_31_2000 = parse_date_to_days("2000-01-31", 0, 0).unwrap();
1132        let jan_30_2000 = parse_date_to_days("2000-01-30", 0, 0).unwrap();
1133        assert_eq!(jan_31_2000 - jan_30_2000, 1); // Normal day difference
1134    }
1135
1136    #[test]
1137    fn test_parse_date_to_days_before_unix_epoch() {
1138        // 1969-12-31 should be -1
1139        let days = parse_date_to_days("1969-12-31", 0, 0).unwrap();
1140        assert_eq!(days, -1);
1141    }
1142
1143    #[test]
1144    fn test_parse_date_to_days_far_future() {
1145        // Test a date far in the future
1146        let days = parse_date_to_days("2100-01-01", 0, 0).unwrap();
1147        assert!(days > 0);
1148    }
1149
1150    // ==========================================================================
1151    // Tests for parse_date_to_days error cases
1152    // ==========================================================================
1153
1154    #[test]
1155    fn test_parse_date_to_days_invalid_format_missing_parts() {
1156        let result = parse_date_to_days("2024-01", 0, 0);
1157        assert!(result.is_err());
1158    }
1159
1160    #[test]
1161    fn test_parse_date_to_days_invalid_format_too_many_parts() {
1162        let result = parse_date_to_days("2024-01-15-extra", 0, 0);
1163        assert!(result.is_err());
1164    }
1165
1166    #[test]
1167    fn test_parse_date_to_days_invalid_year() {
1168        let result = parse_date_to_days("XXXX-01-15", 0, 0);
1169        assert!(result.is_err());
1170    }
1171
1172    #[test]
1173    fn test_parse_date_to_days_invalid_month() {
1174        let result = parse_date_to_days("2024-XX-15", 0, 0);
1175        assert!(result.is_err());
1176    }
1177
1178    #[test]
1179    fn test_parse_date_to_days_invalid_day() {
1180        let result = parse_date_to_days("2024-01-XX", 0, 0);
1181        assert!(result.is_err());
1182    }
1183
1184    // ==========================================================================
1185    // Tests for build_timestamp_array
1186    // ==========================================================================
1187
1188    #[test]
1189    fn test_build_timestamp_array_without_timezone() {
1190        let values = vec![
1191            json!("2024-01-15 10:30:00"),
1192            json!("2024-06-20 14:45:30.123456"),
1193        ];
1194        let refs = to_refs(&values);
1195        let result = build_timestamp_array(&refs, false, 0).unwrap();
1196        assert_eq!(result.len(), 2);
1197        assert_eq!(result.null_count(), 0);
1198    }
1199
1200    #[test]
1201    fn test_build_timestamp_array_with_timezone() {
1202        let values = vec![
1203            json!("2024-01-15 10:30:00"),
1204            json!("2024-06-20 14:45:30.123456"),
1205        ];
1206        let refs = to_refs(&values);
1207        let result = build_timestamp_array(&refs, true, 0).unwrap();
1208        assert_eq!(result.len(), 2);
1209        assert_eq!(result.null_count(), 0);
1210    }
1211
1212    #[test]
1213    fn test_build_timestamp_array_with_null_values() {
1214        let values = vec![
1215            json!("2024-01-15 10:30:00"),
1216            json!(null),
1217            json!("2024-06-20 14:45:30"),
1218        ];
1219        let refs = to_refs(&values);
1220        let result = build_timestamp_array(&refs, false, 0).unwrap();
1221        assert_eq!(result.len(), 3);
1222        assert_eq!(result.null_count(), 1);
1223    }
1224
1225    #[test]
1226    fn test_build_timestamp_array_date_only() {
1227        // Test timestamp with only date part (no time)
1228        let values = vec![json!("2024-01-15")];
1229        let refs = to_refs(&values);
1230        let result = build_timestamp_array(&refs, false, 0).unwrap();
1231        assert_eq!(result.len(), 1);
1232    }
1233
1234    #[test]
1235    fn test_build_timestamp_array_invalid_not_string() {
1236        let values = vec![json!(12345)];
1237        let refs = to_refs(&values);
1238        let result = build_timestamp_array(&refs, false, 0);
1239        assert!(result.is_err());
1240        match result.unwrap_err() {
1241            ConversionError::ValueConversionFailed { message, .. } => {
1242                assert!(message.contains("Expected timestamp string"));
1243            }
1244            _ => panic!("Expected ValueConversionFailed"),
1245        }
1246    }
1247
1248    // ==========================================================================
1249    // Tests for parse_timestamp_to_micros
1250    // ==========================================================================
1251
1252    #[test]
1253    fn test_parse_timestamp_to_micros_with_fractional_seconds_3_digits() {
1254        let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.123", 0, 0).unwrap();
1255        assert_eq!(micros, 123_000);
1256    }
1257
1258    #[test]
1259    fn test_parse_timestamp_to_micros_with_fractional_seconds_6_digits() {
1260        let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.123456", 0, 0).unwrap();
1261        assert_eq!(micros, 123_456);
1262    }
1263
1264    #[test]
1265    fn test_parse_timestamp_to_micros_with_fractional_seconds_9_digits() {
1266        // Only first 6 digits are used for microseconds
1267        let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.123456789", 0, 0).unwrap();
1268        assert_eq!(micros, 123_456);
1269    }
1270
1271    #[test]
1272    fn test_parse_timestamp_to_micros_with_fractional_seconds_1_digit() {
1273        let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.1", 0, 0).unwrap();
1274        assert_eq!(micros, 100_000);
1275    }
1276
1277    #[test]
1278    fn test_parse_timestamp_to_micros_with_fractional_seconds_2_digits() {
1279        let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.12", 0, 0).unwrap();
1280        assert_eq!(micros, 120_000);
1281    }
1282
1283    #[test]
1284    fn test_parse_timestamp_to_micros_hours_minutes_only() {
1285        let micros = parse_timestamp_to_micros("1970-01-01 12:30", 0, 0).unwrap();
1286        assert_eq!(micros, 12 * 3600 * 1_000_000 + 30 * 60 * 1_000_000);
1287    }
1288
1289    #[test]
1290    fn test_parse_timestamp_to_micros_full_time() {
1291        let micros = parse_timestamp_to_micros("1970-01-01 12:30:45", 0, 0).unwrap();
1292        assert_eq!(
1293            micros,
1294            12 * 3600 * 1_000_000 + 30 * 60 * 1_000_000 + 45 * 1_000_000
1295        );
1296    }
1297
1298    #[test]
1299    fn test_parse_timestamp_to_micros_empty_string() {
1300        let result = parse_timestamp_to_micros("", 0, 0);
1301        assert!(result.is_err());
1302    }
1303
1304    #[test]
1305    fn test_parse_timestamp_to_micros_invalid_hour() {
1306        let result = parse_timestamp_to_micros("1970-01-01 XX:30:00", 0, 0);
1307        assert!(result.is_err());
1308    }
1309
1310    #[test]
1311    fn test_parse_timestamp_to_micros_invalid_minute() {
1312        let result = parse_timestamp_to_micros("1970-01-01 12:XX:00", 0, 0);
1313        assert!(result.is_err());
1314    }
1315
1316    #[test]
1317    fn test_parse_timestamp_to_micros_invalid_second() {
1318        let result = parse_timestamp_to_micros("1970-01-01 12:30:XX", 0, 0);
1319        assert!(result.is_err());
1320    }
1321
1322    // ==========================================================================
1323    // Tests for build_interval_year_to_month_array
1324    // ==========================================================================
1325
1326    #[test]
1327    fn test_build_interval_year_to_month_array_positive() {
1328        let values = vec![json!("+01-06"), json!("+00-03")];
1329        let refs = to_refs(&values);
1330        let result = build_interval_year_to_month_array(&refs, 0).unwrap();
1331        assert_eq!(result.len(), 2);
1332        assert_eq!(result.null_count(), 0);
1333    }
1334
1335    #[test]
1336    fn test_build_interval_year_to_month_array_negative() {
1337        let values = vec![json!("-02-03"), json!("-00-11")];
1338        let refs = to_refs(&values);
1339        let result = build_interval_year_to_month_array(&refs, 0).unwrap();
1340        assert_eq!(result.len(), 2);
1341        assert_eq!(result.null_count(), 0);
1342    }
1343
1344    #[test]
1345    fn test_build_interval_year_to_month_array_with_nulls() {
1346        let values = vec![json!("+01-06"), json!(null), json!("-02-03")];
1347        let refs = to_refs(&values);
1348        let result = build_interval_year_to_month_array(&refs, 0).unwrap();
1349        assert_eq!(result.len(), 3);
1350        assert_eq!(result.null_count(), 1);
1351    }
1352
1353    #[test]
1354    fn test_build_interval_year_to_month_array_invalid_not_string() {
1355        let values = vec![json!(12345)];
1356        let refs = to_refs(&values);
1357        let result = build_interval_year_to_month_array(&refs, 0);
1358        assert!(result.is_err());
1359        match result.unwrap_err() {
1360            ConversionError::ValueConversionFailed { message, .. } => {
1361                assert!(message.contains("Expected interval string"));
1362            }
1363            _ => panic!("Expected ValueConversionFailed"),
1364        }
1365    }
1366
1367    // ==========================================================================
1368    // Tests for parse_interval_year_to_month
1369    // ==========================================================================
1370
1371    #[test]
1372    fn test_parse_interval_year_to_month_zero_years() {
1373        let months = parse_interval_year_to_month("+00-06", 0, 0).unwrap();
1374        assert_eq!(months, 6);
1375    }
1376
1377    #[test]
1378    fn test_parse_interval_year_to_month_zero_months() {
1379        let months = parse_interval_year_to_month("+05-00", 0, 0).unwrap();
1380        assert_eq!(months, 60);
1381    }
1382
1383    #[test]
1384    fn test_parse_interval_year_to_month_large_value() {
1385        let months = parse_interval_year_to_month("+99-11", 0, 0).unwrap();
1386        assert_eq!(months, 99 * 12 + 11);
1387    }
1388
1389    #[test]
1390    fn test_parse_interval_year_to_month_negative_large_value() {
1391        let months = parse_interval_year_to_month("-99-11", 0, 0).unwrap();
1392        assert_eq!(months, -(99 * 12 + 11));
1393    }
1394
1395    #[test]
1396    fn test_parse_interval_year_to_month_invalid_format() {
1397        let result = parse_interval_year_to_month("01-06", 0, 0); // Missing sign
1398                                                                  // This should still work since we trim the sign
1399        assert!(result.is_ok());
1400    }
1401
1402    #[test]
1403    fn test_parse_interval_year_to_month_invalid_format_missing_parts() {
1404        let result = parse_interval_year_to_month("+01", 0, 0);
1405        assert!(result.is_err());
1406    }
1407
1408    #[test]
1409    fn test_parse_interval_year_to_month_invalid_years() {
1410        let result = parse_interval_year_to_month("+XX-06", 0, 0);
1411        assert!(result.is_err());
1412    }
1413
1414    #[test]
1415    fn test_parse_interval_year_to_month_invalid_months() {
1416        let result = parse_interval_year_to_month("+01-XX", 0, 0);
1417        assert!(result.is_err());
1418    }
1419
1420    // ==========================================================================
1421    // Tests for build_interval_day_to_second_array
1422    // ==========================================================================
1423
1424    #[test]
1425    fn test_build_interval_day_to_second_array_with_fractional_seconds() {
1426        let values = vec![json!("+01 12:30:45.123456789")];
1427        let refs = to_refs(&values);
1428        let result = build_interval_day_to_second_array(&refs, 0).unwrap();
1429        assert_eq!(result.len(), 1);
1430        assert_eq!(result.null_count(), 0);
1431    }
1432
1433    #[test]
1434    fn test_build_interval_day_to_second_array_without_fractional_seconds() {
1435        let values = vec![json!("+01 12:30:45")];
1436        let refs = to_refs(&values);
1437        let result = build_interval_day_to_second_array(&refs, 0).unwrap();
1438        assert_eq!(result.len(), 1);
1439        assert_eq!(result.null_count(), 0);
1440    }
1441
1442    #[test]
1443    fn test_build_interval_day_to_second_array_negative() {
1444        let values = vec![json!("-02 08:15:30.500000000")];
1445        let refs = to_refs(&values);
1446        let result = build_interval_day_to_second_array(&refs, 0).unwrap();
1447        assert_eq!(result.len(), 1);
1448        assert_eq!(result.null_count(), 0);
1449    }
1450
1451    #[test]
1452    fn test_build_interval_day_to_second_array_with_nulls() {
1453        let values = vec![json!("+01 12:30:45"), json!(null), json!("-02 08:15:30")];
1454        let refs = to_refs(&values);
1455        let result = build_interval_day_to_second_array(&refs, 0).unwrap();
1456        assert_eq!(result.len(), 3);
1457        assert_eq!(result.null_count(), 1);
1458    }
1459
1460    #[test]
1461    fn test_build_interval_day_to_second_array_invalid_not_string() {
1462        let values = vec![json!(12345)];
1463        let refs = to_refs(&values);
1464        let result = build_interval_day_to_second_array(&refs, 0);
1465        assert!(result.is_err());
1466        match result.unwrap_err() {
1467            ConversionError::ValueConversionFailed { message, .. } => {
1468                assert!(message.contains("Expected interval string"));
1469            }
1470            _ => panic!("Expected ValueConversionFailed"),
1471        }
1472    }
1473
1474    // ==========================================================================
1475    // Tests for parse_interval_day_to_second
1476    // ==========================================================================
1477
1478    #[test]
1479    fn test_parse_interval_day_to_second_days_only() {
1480        let (days, nanos) = parse_interval_day_to_second("+05", 0, 0).unwrap();
1481        assert_eq!(days, 5);
1482        assert_eq!(nanos, 0);
1483    }
1484
1485    #[test]
1486    fn test_parse_interval_day_to_second_hours_minutes_only() {
1487        let (days, nanos) = parse_interval_day_to_second("+01 12:30", 0, 0).unwrap();
1488        assert_eq!(days, 1);
1489        assert_eq!(nanos, 12 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000);
1490    }
1491
1492    #[test]
1493    fn test_parse_interval_day_to_second_negative_values() {
1494        let (days, nanos) = parse_interval_day_to_second("-03 06:15:30", 0, 0).unwrap();
1495        assert_eq!(days, -3);
1496        assert_eq!(
1497            nanos,
1498            -(6 * 3600 * 1_000_000_000 + 15 * 60 * 1_000_000_000 + 30 * 1_000_000_000)
1499        );
1500    }
1501
1502    #[test]
1503    fn test_parse_interval_day_to_second_with_fractional_3_digits() {
1504        let (days, nanos) = parse_interval_day_to_second("+00 00:00:00.123", 0, 0).unwrap();
1505        assert_eq!(days, 0);
1506        assert_eq!(nanos, 123_000_000);
1507    }
1508
1509    #[test]
1510    fn test_parse_interval_day_to_second_with_fractional_6_digits() {
1511        let (days, nanos) = parse_interval_day_to_second("+00 00:00:00.123456", 0, 0).unwrap();
1512        assert_eq!(days, 0);
1513        assert_eq!(nanos, 123_456_000);
1514    }
1515
1516    #[test]
1517    fn test_parse_interval_day_to_second_with_fractional_9_digits() {
1518        let (days, nanos) = parse_interval_day_to_second("+00 00:00:00.123456789", 0, 0).unwrap();
1519        assert_eq!(days, 0);
1520        assert_eq!(nanos, 123_456_789);
1521    }
1522
1523    #[test]
1524    fn test_parse_interval_day_to_second_with_fractional_12_digits() {
1525        // Only first 9 digits should be used
1526        let (days, nanos) =
1527            parse_interval_day_to_second("+00 00:00:00.123456789012", 0, 0).unwrap();
1528        assert_eq!(days, 0);
1529        assert_eq!(nanos, 123_456_789);
1530    }
1531
1532    #[test]
1533    fn test_parse_interval_day_to_second_empty_string() {
1534        let result = parse_interval_day_to_second("", 0, 0);
1535        assert!(result.is_err());
1536    }
1537
1538    #[test]
1539    fn test_parse_interval_day_to_second_invalid_days() {
1540        let result = parse_interval_day_to_second("+XX 12:30:45", 0, 0);
1541        assert!(result.is_err());
1542    }
1543
1544    #[test]
1545    fn test_parse_interval_day_to_second_invalid_hours() {
1546        let result = parse_interval_day_to_second("+01 XX:30:45", 0, 0);
1547        assert!(result.is_err());
1548    }
1549
1550    #[test]
1551    fn test_parse_interval_day_to_second_invalid_minutes() {
1552        let result = parse_interval_day_to_second("+01 12:XX:45", 0, 0);
1553        assert!(result.is_err());
1554    }
1555
1556    #[test]
1557    fn test_parse_interval_day_to_second_invalid_seconds() {
1558        let result = parse_interval_day_to_second("+01 12:30:XX", 0, 0);
1559        assert!(result.is_err());
1560    }
1561
1562    // ==========================================================================
1563    // Tests for build_binary_array
1564    // ==========================================================================
1565
1566    #[test]
1567    fn test_build_binary_array_valid_hex() {
1568        let values = vec![json!("48656c6c6f"), json!("576f726c64")];
1569        let refs = to_refs(&values);
1570        let result = build_binary_array(&refs, 0).unwrap();
1571        assert_eq!(result.len(), 2);
1572        assert_eq!(result.null_count(), 0);
1573    }
1574
1575    #[test]
1576    fn test_build_binary_array_with_null_values() {
1577        let values = vec![json!("48656c6c6f"), json!(null), json!("576f726c64")];
1578        let refs = to_refs(&values);
1579        let result = build_binary_array(&refs, 0).unwrap();
1580        assert_eq!(result.len(), 3);
1581        assert_eq!(result.null_count(), 1);
1582    }
1583
1584    #[test]
1585    fn test_build_binary_array_empty_hex() {
1586        let values = vec![json!("")];
1587        let refs = to_refs(&values);
1588        let result = build_binary_array(&refs, 0).unwrap();
1589        assert_eq!(result.len(), 1);
1590        assert_eq!(result.null_count(), 0);
1591    }
1592
1593    #[test]
1594    fn test_build_binary_array_invalid_not_string() {
1595        let values = vec![json!(12345)];
1596        let refs = to_refs(&values);
1597        let result = build_binary_array(&refs, 0);
1598        assert!(result.is_err());
1599        match result.unwrap_err() {
1600            ConversionError::ValueConversionFailed { message, .. } => {
1601                assert!(message.contains("Expected hex string"));
1602            }
1603            _ => panic!("Expected ValueConversionFailed"),
1604        }
1605    }
1606
1607    #[test]
1608    fn test_build_binary_array_invalid_hex_odd_length() {
1609        let values = vec![json!("abc")]; // Odd length
1610        let refs = to_refs(&values);
1611        let result = build_binary_array(&refs, 0);
1612        assert!(result.is_err());
1613    }
1614
1615    #[test]
1616    fn test_build_binary_array_invalid_hex_chars() {
1617        let values = vec![json!("ghij")]; // Invalid hex chars
1618        let refs = to_refs(&values);
1619        let result = build_binary_array(&refs, 0);
1620        assert!(result.is_err());
1621    }
1622
1623    #[test]
1624    fn test_build_binary_array_capacity_estimation_with_samples() {
1625        // Create more than 10 values to test sampling
1626        let values: Vec<Value> = (0..15).map(|_| json!("deadbeef")).collect();
1627        let refs = to_refs(&values);
1628        let result = build_binary_array(&refs, 0).unwrap();
1629        assert_eq!(result.len(), 15);
1630    }
1631
1632    #[test]
1633    fn test_build_binary_array_capacity_estimation_all_nulls() {
1634        // All null values for capacity estimation
1635        let values: Vec<Value> = (0..5).map(|_| json!(null)).collect();
1636        let refs = to_refs(&values);
1637        let result = build_binary_array(&refs, 0).unwrap();
1638        assert_eq!(result.len(), 5);
1639        assert_eq!(result.null_count(), 5);
1640    }
1641
1642    #[test]
1643    fn test_build_double_array_from_integer() {
1644        let values = vec![json!(42), json!(100)];
1645        let refs = to_refs(&values);
1646        let result = build_double_array(&refs, 0).unwrap();
1647        assert_eq!(result.len(), 2);
1648    }
1649
1650    #[test]
1651    fn test_build_double_array_from_string_numeric() {
1652        let values = vec![json!("3.14159")];
1653        let refs = to_refs(&values);
1654        let result = build_double_array(&refs, 0).unwrap();
1655        assert_eq!(result.len(), 1);
1656    }
1657
1658    #[test]
1659    fn test_build_double_array_neg_infinity() {
1660        let values = vec![json!("-Infinity")];
1661        let refs = to_refs(&values);
1662        let result = build_double_array(&refs, 0).unwrap();
1663        assert_eq!(result.len(), 1);
1664    }
1665
1666    #[test]
1667    fn test_build_double_array_nan() {
1668        let values = vec![json!("NaN")];
1669        let refs = to_refs(&values);
1670        let result = build_double_array(&refs, 0).unwrap();
1671        assert_eq!(result.len(), 1);
1672    }
1673
1674    #[test]
1675    fn test_build_double_array_invalid_string() {
1676        let values = vec![json!("not a number")];
1677        let refs = to_refs(&values);
1678        let result = build_double_array(&refs, 0);
1679        assert!(result.is_err());
1680    }
1681
1682    #[test]
1683    fn test_parse_decimal_to_i128_from_float() {
1684        let result = parse_decimal_to_i128(&json!(123.45), 10, 2, 0, 0).unwrap();
1685        assert_eq!(result, 12345);
1686    }
1687
1688    #[test]
1689    fn test_parse_decimal_to_i128_invalid_decimal_format() {
1690        let result = parse_decimal_to_i128(&json!("1.2.3"), 10, 2, 0, 0);
1691        assert!(result.is_err());
1692    }
1693
1694    #[test]
1695    fn test_parse_decimal_to_i128_invalid_integer_part() {
1696        let result = parse_decimal_to_i128(&json!("abc.45"), 10, 2, 0, 0);
1697        assert!(result.is_err());
1698    }
1699
1700    #[test]
1701    fn test_parse_decimal_to_i128_invalid_decimal_part() {
1702        let result = parse_decimal_to_i128(&json!("123.xyz"), 10, 2, 0, 0);
1703        assert!(result.is_err());
1704    }
1705
1706    #[test]
1707    fn test_parse_decimal_to_i128_non_numeric_value() {
1708        let result = parse_decimal_to_i128(&json!({"key": "value"}), 10, 2, 0, 0);
1709        assert!(result.is_err());
1710    }
1711
1712    #[test]
1713    fn test_decode_hex_uppercase() {
1714        let bytes = decode_hex("DEADBEEF").unwrap();
1715        assert_eq!(bytes, vec![0xDE, 0xAD, 0xBE, 0xEF]);
1716    }
1717
1718    #[test]
1719    fn test_decode_hex_mixed_case() {
1720        let bytes = decode_hex("DeAdBeEf").unwrap();
1721        assert_eq!(bytes, vec![0xDE, 0xAD, 0xBE, 0xEF]);
1722    }
1723}