use crate::error::ConversionError;
use crate::types::{
conversion::{
parse_date_to_days as parse_date_to_days_impl,
parse_decimal_to_i128 as parse_decimal_to_i128_impl,
parse_timestamp_to_micros as parse_timestamp_to_micros_impl,
},
ExasolType,
};
use arrow::array::{
ArrayRef, BinaryBuilder, BooleanBuilder, Date32Builder, Decimal128Builder, Float64Builder,
IntervalMonthDayNanoBuilder, StringBuilder, TimestampMicrosecondBuilder,
};
use serde_json::Value;
use std::sync::Arc;
pub trait ArrayBuilder {
fn append_value(
&mut self,
value: &Value,
row: usize,
column: usize,
) -> Result<(), ConversionError>;
fn finish(&mut self) -> Result<ArrayRef, ConversionError>;
}
pub fn build_array(
exasol_type: &ExasolType,
values: &[&Value],
column: usize,
) -> Result<ArrayRef, ConversionError> {
match exasol_type {
ExasolType::Boolean => build_boolean_array(values, column),
ExasolType::Char { .. } | ExasolType::Varchar { .. } => build_string_array(values, column),
ExasolType::Decimal { precision, scale } => {
build_decimal128_array(values, *precision, *scale, column)
}
ExasolType::Double => build_double_array(values, column),
ExasolType::Date => build_date_array(values, column),
ExasolType::Timestamp {
with_local_time_zone,
} => build_timestamp_array(values, *with_local_time_zone, column),
ExasolType::IntervalYearToMonth => build_interval_year_to_month_array(values, column),
ExasolType::IntervalDayToSecond { .. } => {
build_interval_day_to_second_array(values, column)
}
ExasolType::Geometry { .. } | ExasolType::Hashtype { .. } => {
build_binary_array(values, column)
}
}
}
fn build_boolean_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
let mut builder = BooleanBuilder::with_capacity(values.len());
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(b) = value.as_bool() {
builder.append_value(b);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected boolean, got: {:?}", value),
});
}
}
Ok(Arc::new(builder.finish()))
}
fn estimate_string_capacity(values: &[&Value]) -> usize {
const SAMPLE_SIZE: usize = 10;
const DEFAULT_AVG_LEN: usize = 32;
let mut total_len = 0;
let mut count = 0;
for value in values.iter().take(SAMPLE_SIZE) {
if let Some(s) = value.as_str() {
total_len += s.len();
count += 1;
}
}
if count > 0 {
let avg_len = total_len / count;
(avg_len + 8) * values.len()
} else {
DEFAULT_AVG_LEN * values.len()
}
}
fn build_string_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
let estimated_bytes = estimate_string_capacity(values);
let mut builder = StringBuilder::with_capacity(values.len(), estimated_bytes);
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(s) = value.as_str() {
builder.append_value(s);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected string, got: {:?}", value),
});
}
}
Ok(Arc::new(builder.finish()))
}
fn validate_decimal_precision(
value_str: &str,
precision: u8,
_scale: i8,
row: usize,
column: usize,
) -> Result<(), ConversionError> {
let digits: String = value_str.chars().filter(|c| c.is_ascii_digit()).collect();
let total_digits = digits.len();
let max_digits = precision as usize;
let significant_digits = digits.trim_start_matches('0');
let significant_count = if significant_digits.is_empty() {
1 } else {
significant_digits.len()
};
if significant_count > max_digits {
return Err(ConversionError::NumericOverflow { row, column });
}
if total_digits > max_digits + 1 {
return Err(ConversionError::NumericOverflow { row, column });
}
Ok(())
}
fn build_decimal128_array(
values: &[&Value],
precision: u8,
scale: i8,
column: usize,
) -> Result<ArrayRef, ConversionError> {
let mut builder = Decimal128Builder::with_capacity(values.len())
.with_precision_and_scale(precision, scale)
.map_err(|e| ConversionError::ArrowError(e.to_string()))?;
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else {
let decimal_value = parse_decimal_to_i128(value, precision, scale, row, column)?;
builder.append_value(decimal_value);
}
}
Ok(Arc::new(builder.finish()))
}
fn parse_decimal_to_i128(
value: &Value,
precision: u8,
scale: i8,
row: usize,
column: usize,
) -> Result<i128, ConversionError> {
if let Some(n) = value.as_i64() {
return Ok((n as i128) * 10_i128.pow(scale as u32));
} else if let Some(n) = value.as_f64() {
let scaled = (n * 10_f64.powi(scale as i32)).round();
return scaled
.to_string()
.parse::<i128>()
.map_err(|_| ConversionError::NumericOverflow { row, column });
}
let value_str = value
.as_str()
.ok_or_else(|| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected numeric value, got: {:?}", value),
})?;
validate_decimal_precision(value_str, precision, scale, row, column)?;
parse_decimal_to_i128_impl(value_str, scale).map_err(|e| {
ConversionError::ValueConversionFailed {
row,
column,
message: e,
}
})
}
fn build_double_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
let mut builder = Float64Builder::with_capacity(values.len());
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(f) = value.as_f64() {
builder.append_value(f);
} else if let Some(i) = value.as_i64() {
builder.append_value(i as f64);
} else if let Some(s) = value.as_str() {
let f = match s {
"Infinity" => f64::INFINITY,
"-Infinity" => f64::NEG_INFINITY,
"NaN" => f64::NAN,
_ => s
.parse::<f64>()
.map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid float value: {}", s),
})?,
};
builder.append_value(f);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected number, got: {:?}", value),
});
}
}
Ok(Arc::new(builder.finish()))
}
fn build_date_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
let mut builder = Date32Builder::with_capacity(values.len());
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(s) = value.as_str() {
let days = parse_date_to_days(s, row, column)?;
builder.append_value(days);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected date string, got: {:?}", value),
});
}
}
Ok(Arc::new(builder.finish()))
}
fn parse_date_to_days(date_str: &str, row: usize, column: usize) -> Result<i32, ConversionError> {
parse_date_to_days_impl(date_str).map_err(|e| ConversionError::ValueConversionFailed {
row,
column,
message: e,
})
}
fn build_timestamp_array(
values: &[&Value],
with_local_time_zone: bool,
column: usize,
) -> Result<ArrayRef, ConversionError> {
let mut builder = TimestampMicrosecondBuilder::with_capacity(values.len());
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(s) = value.as_str() {
let micros = parse_timestamp_to_micros(s, row, column)?;
builder.append_value(micros);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected timestamp string, got: {:?}", value),
});
}
}
let array = builder.finish();
if with_local_time_zone {
Ok(Arc::new(array.with_timezone("UTC")))
} else {
Ok(Arc::new(array))
}
}
fn parse_timestamp_to_micros(
timestamp_str: &str,
row: usize,
column: usize,
) -> Result<i64, ConversionError> {
parse_timestamp_to_micros_impl(timestamp_str).map_err(|e| {
ConversionError::ValueConversionFailed {
row,
column,
message: e,
}
})
}
fn build_interval_year_to_month_array(
values: &[&Value],
column: usize,
) -> Result<ArrayRef, ConversionError> {
let mut builder = IntervalMonthDayNanoBuilder::with_capacity(values.len());
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(s) = value.as_str() {
let months = parse_interval_year_to_month(s, row, column)?;
let interval = arrow::datatypes::IntervalMonthDayNano {
months,
days: 0,
nanoseconds: 0,
};
builder.append_value(interval);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected interval string, got: {:?}", value),
});
}
}
Ok(Arc::new(builder.finish()))
}
fn parse_interval_year_to_month(
interval_str: &str,
row: usize,
column: usize,
) -> Result<i32, ConversionError> {
let is_negative = interval_str.starts_with('-');
let parts: Vec<&str> = interval_str
.trim_start_matches(&['+', '-'][..])
.split('-')
.collect();
if parts.len() != 2 {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid interval format: {}", interval_str),
});
}
let years: i32 = parts[0]
.parse()
.map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid years: {}", parts[0]),
})?;
let months: i32 = parts[1]
.parse()
.map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid months: {}", parts[1]),
})?;
let total_months = years * 12 + months;
Ok(if is_negative {
-total_months
} else {
total_months
})
}
fn build_interval_day_to_second_array(
values: &[&Value],
column: usize,
) -> Result<ArrayRef, ConversionError> {
let mut builder = IntervalMonthDayNanoBuilder::with_capacity(values.len());
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(s) = value.as_str() {
let (days, nanos) = parse_interval_day_to_second(s, row, column)?;
let interval = arrow::datatypes::IntervalMonthDayNano {
months: 0,
days,
nanoseconds: nanos,
};
builder.append_value(interval);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected interval string, got: {:?}", value),
});
}
}
Ok(Arc::new(builder.finish()))
}
fn parse_interval_day_to_second(
interval_str: &str,
row: usize,
column: usize,
) -> Result<(i32, i64), ConversionError> {
let is_negative = interval_str.starts_with('-');
let trimmed = interval_str.trim_start_matches(&['+', '-'][..]);
let parts: Vec<&str> = trimmed.split(' ').collect();
if parts.is_empty() {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid interval format: {}", interval_str),
});
}
let days: i32 = parts[0]
.parse()
.map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid days: {}", parts[0]),
})?;
let mut nanos: i64 = 0;
if parts.len() > 1 {
let time_parts: Vec<&str> = parts[1].split(':').collect();
if time_parts.len() >= 2 {
let hours: i64 =
time_parts[0]
.parse()
.map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid hours: {}", time_parts[0]),
})?;
let minutes: i64 =
time_parts[1]
.parse()
.map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid minutes: {}", time_parts[1]),
})?;
nanos += hours * 3600 * 1_000_000_000;
nanos += minutes * 60 * 1_000_000_000;
if time_parts.len() >= 3 {
let sec_parts: Vec<&str> = time_parts[2].split('.').collect();
let seconds: i64 =
sec_parts[0]
.parse()
.map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid seconds: {}", sec_parts[0]),
})?;
nanos += seconds * 1_000_000_000;
if sec_parts.len() > 1 {
let frac = sec_parts[1];
let frac_nanos = if frac.len() <= 9 {
let padding = 9 - frac.len();
let padded = format!("{}{}", frac, "0".repeat(padding));
padded.parse::<i64>().unwrap_or(0)
} else {
frac[..9].parse::<i64>().unwrap_or(0)
};
nanos += frac_nanos;
}
}
}
}
Ok(if is_negative {
(-days, -nanos)
} else {
(days, nanos)
})
}
fn build_binary_array(values: &[&Value], column: usize) -> Result<ArrayRef, ConversionError> {
const SAMPLE_SIZE: usize = 10;
let sample_count = values.len().min(SAMPLE_SIZE);
let sample_total: usize = values
.iter()
.take(SAMPLE_SIZE)
.filter_map(|v| v.as_str())
.map(|s| s.len() / 2)
.sum();
let estimated_bytes = if sample_count > 0 {
let avg_size = (sample_total / sample_count).max(1);
avg_size * values.len()
} else {
values.len() * 32 };
let mut builder = BinaryBuilder::with_capacity(values.len(), estimated_bytes.max(1024));
for (row, value) in values.iter().enumerate() {
if value.is_null() {
builder.append_null();
} else if let Some(s) = value.as_str() {
let bytes = decode_hex(s).map_err(|_| ConversionError::ValueConversionFailed {
row,
column,
message: format!("Invalid hex string: {}", s),
})?;
builder.append_value(&bytes);
} else {
return Err(ConversionError::ValueConversionFailed {
row,
column,
message: format!("Expected hex string, got: {:?}", value),
});
}
}
Ok(Arc::new(builder.finish()))
}
fn decode_hex(s: &str) -> Result<Vec<u8>, ()> {
if !s.len().is_multiple_of(2) {
return Err(());
}
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| ()))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn to_refs(values: &[Value]) -> Vec<&Value> {
values.iter().collect()
}
#[test]
fn test_build_boolean_array() {
let values = vec![json!(true), json!(false), json!(null), json!(true)];
let refs = to_refs(&values);
let array = build_boolean_array(&refs, 0).unwrap();
assert_eq!(array.len(), 4);
assert_eq!(array.null_count(), 1);
}
#[test]
fn test_build_string_array() {
let values = vec![json!("hello"), json!("world"), json!(null), json!("test")];
let refs = to_refs(&values);
let array = build_string_array(&refs, 0).unwrap();
assert_eq!(array.len(), 4);
assert_eq!(array.null_count(), 1);
}
#[test]
fn test_build_decimal128_array() {
let values = vec![json!("123.45"), json!("678.90"), json!(null)];
let refs = to_refs(&values);
let array = build_decimal128_array(&refs, 10, 2, 0).unwrap();
assert_eq!(array.len(), 3);
assert_eq!(array.null_count(), 1);
}
#[test]
fn test_build_double_array() {
let values = vec![json!(1.5), json!(2.7), json!(null), json!("Infinity")];
let refs = to_refs(&values);
let array = build_double_array(&refs, 0).unwrap();
assert_eq!(array.len(), 4);
assert_eq!(array.null_count(), 1);
}
#[test]
fn test_parse_date_to_days() {
let days = parse_date_to_days("1970-01-01", 0, 0).unwrap();
assert_eq!(days, 0);
let days = parse_date_to_days("1970-01-02", 0, 0).unwrap();
assert_eq!(days, 1);
}
#[test]
fn test_parse_timestamp_to_micros() {
let micros = parse_timestamp_to_micros("1970-01-01 00:00:00", 0, 0).unwrap();
assert_eq!(micros, 0);
let micros = parse_timestamp_to_micros("1970-01-01 00:00:01", 0, 0).unwrap();
assert_eq!(micros, 1_000_000);
}
#[test]
fn test_parse_interval_year_to_month() {
let months = parse_interval_year_to_month("+01-06", 0, 0).unwrap();
assert_eq!(months, 18);
let months = parse_interval_year_to_month("-02-03", 0, 0).unwrap();
assert_eq!(months, -27); }
#[test]
fn test_parse_interval_day_to_second() {
let (days, nanos) = parse_interval_day_to_second("+01 12:30:45.123456789", 0, 0).unwrap();
assert_eq!(days, 1);
assert_eq!(
nanos,
12 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000 + 45 * 1_000_000_000 + 123_456_789
);
}
#[test]
fn test_decode_hex() {
let bytes = decode_hex("48656c6c6f").unwrap();
assert_eq!(bytes, b"Hello");
let bytes = decode_hex("").unwrap();
assert_eq!(bytes, b"");
assert!(decode_hex("xyz").is_err());
assert!(decode_hex("1").is_err()); }
#[test]
fn test_parse_decimal_to_i128() {
let result = parse_decimal_to_i128(&json!("123"), 10, 2, 0, 0).unwrap();
assert_eq!(result, 12300);
let result = parse_decimal_to_i128(&json!("123.45"), 10, 2, 0, 0).unwrap();
assert_eq!(result, 12345);
let result = parse_decimal_to_i128(&json!("-123.45"), 10, 2, 0, 0).unwrap();
assert_eq!(result, -12345);
let result = parse_decimal_to_i128(&json!(123), 10, 2, 0, 0).unwrap();
assert_eq!(result, 12300);
}
#[test]
fn test_invalid_conversions() {
let values = vec![json!("not a bool")];
let refs = to_refs(&values);
assert!(build_boolean_array(&refs, 0).is_err());
let values = vec![json!("2024-13-01")]; let refs = to_refs(&values);
assert!(build_date_array(&refs, 0).is_err());
let values = vec![json!("zzz")];
let refs = to_refs(&values);
assert!(build_binary_array(&refs, 0).is_err());
}
#[test]
fn test_validate_decimal_precision() {
assert!(validate_decimal_precision("12345", 10, 2, 0, 0).is_ok());
assert!(validate_decimal_precision("123.45", 10, 2, 0, 0).is_ok());
assert!(validate_decimal_precision("-12345", 10, 2, 0, 0).is_ok());
assert!(validate_decimal_precision("123456", 5, 2, 0, 0).is_err());
assert!(validate_decimal_precision("00123", 5, 2, 0, 0).is_ok());
assert!(validate_decimal_precision("0", 1, 0, 0, 0).is_ok());
assert!(validate_decimal_precision("000", 5, 0, 0, 0).is_ok());
}
#[test]
fn test_decimal_precision_overflow() {
let values = vec![json!("12345678901234567890")]; let refs = to_refs(&values);
let result = build_decimal128_array(&refs, 10, 2, 0);
assert!(result.is_err());
let values = vec![json!("12345")]; let refs = to_refs(&values);
let result = build_decimal128_array(&refs, 10, 2, 0);
assert!(result.is_ok());
}
#[test]
fn test_estimate_string_capacity() {
let values = vec![json!("hello"), json!("world"), json!("test")];
let refs = to_refs(&values);
let capacity = estimate_string_capacity(&refs);
assert!(capacity > 0);
assert!(capacity >= 15);
let empty_values: Vec<Value> = vec![];
let empty_refs = to_refs(&empty_values);
let empty_capacity = estimate_string_capacity(&empty_refs);
assert_eq!(empty_capacity, 0);
let null_values = vec![json!(null), json!(null), json!(null)];
let null_refs = to_refs(&null_values);
let null_capacity = estimate_string_capacity(&null_refs);
assert!(null_capacity > 0);
let mixed_values = vec![json!("hello"), json!(null), json!("world")];
let mixed_refs = to_refs(&mixed_values);
let mixed_capacity = estimate_string_capacity(&mixed_refs);
assert!(mixed_capacity > 0);
}
#[test]
fn test_build_array_dispatches_to_boolean() {
let values = vec![json!(true), json!(false)];
let refs = to_refs(&values);
let result = build_array(&ExasolType::Boolean, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_char() {
let values = vec![json!("abc"), json!("def")];
let refs = to_refs(&values);
let result = build_array(&ExasolType::Char { size: 3 }, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_varchar() {
let values = vec![json!("hello"), json!("world")];
let refs = to_refs(&values);
let result = build_array(&ExasolType::Varchar { size: 100 }, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_decimal() {
let values = vec![json!("123.45"), json!("678.90")];
let refs = to_refs(&values);
let result = build_array(
&ExasolType::Decimal {
precision: 10,
scale: 2,
},
&refs,
0,
)
.unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_double() {
let values = vec![json!(1.5), json!(2.5)];
let refs = to_refs(&values);
let result = build_array(&ExasolType::Double, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_date() {
let values = vec![json!("2024-01-15"), json!("2024-06-20")];
let refs = to_refs(&values);
let result = build_array(&ExasolType::Date, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_timestamp_without_tz() {
let values = vec![json!("2024-01-15 10:30:00"), json!("2024-06-20 14:45:30")];
let refs = to_refs(&values);
let result = build_array(
&ExasolType::Timestamp {
with_local_time_zone: false,
},
&refs,
0,
)
.unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_timestamp_with_tz() {
let values = vec![json!("2024-01-15 10:30:00"), json!("2024-06-20 14:45:30")];
let refs = to_refs(&values);
let result = build_array(
&ExasolType::Timestamp {
with_local_time_zone: true,
},
&refs,
0,
)
.unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_interval_year_to_month() {
let values = vec![json!("+01-06"), json!("-02-03")];
let refs = to_refs(&values);
let result = build_array(&ExasolType::IntervalYearToMonth, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_interval_day_to_second() {
let values = vec![json!("+01 12:30:45"), json!("-02 08:15:30")];
let refs = to_refs(&values);
let result =
build_array(&ExasolType::IntervalDayToSecond { precision: 3 }, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_geometry() {
let values = vec![json!("48656c6c6f"), json!("576f726c64")];
let refs = to_refs(&values);
let result = build_array(&ExasolType::Geometry { srid: Some(4326) }, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_array_dispatches_to_hashtype() {
let values = vec![json!("deadbeef"), json!("cafebabe")];
let refs = to_refs(&values);
let result = build_array(&ExasolType::Hashtype { byte_size: 16 }, &refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_date_array_with_valid_dates() {
let values = vec![
json!("2024-01-15"),
json!("2023-06-20"),
json!("1970-01-01"),
];
let refs = to_refs(&values);
let result = build_date_array(&refs, 0).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_date_array_with_null_values() {
let values = vec![json!("2024-01-15"), json!(null), json!("2023-06-20")];
let refs = to_refs(&values);
let result = build_date_array(&refs, 0).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result.null_count(), 1);
}
#[test]
fn test_build_date_array_invalid_format_not_string() {
let values = vec![json!(12345)];
let refs = to_refs(&values);
let result = build_date_array(&refs, 0);
assert!(result.is_err());
match result.unwrap_err() {
ConversionError::ValueConversionFailed { message, .. } => {
assert!(message.contains("Expected date string"));
}
_ => panic!("Expected ValueConversionFailed"),
}
}
#[test]
fn test_build_date_array_invalid_date_format() {
let values = vec![json!("2024/01/15")]; let refs = to_refs(&values);
let result = build_date_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_build_date_array_invalid_month_out_of_range() {
let values = vec![json!("2024-13-01")]; let refs = to_refs(&values);
let result = build_date_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_build_date_array_invalid_day_out_of_range() {
let values = vec![json!("2024-01-32")]; let refs = to_refs(&values);
let result = build_date_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_build_date_array_invalid_month_zero() {
let values = vec![json!("2024-00-15")]; let refs = to_refs(&values);
let result = build_date_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_build_date_array_invalid_day_zero() {
let values = vec![json!("2024-01-00")]; let refs = to_refs(&values);
let result = build_date_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_date_to_days_january() {
let days = parse_date_to_days("1970-01-15", 0, 0).unwrap();
assert_eq!(days, 14); }
#[test]
fn test_parse_date_to_days_february() {
let days = parse_date_to_days("1970-02-01", 0, 0).unwrap();
assert_eq!(days, 31); }
#[test]
fn test_parse_date_to_days_march() {
let days = parse_date_to_days("1970-03-01", 0, 0).unwrap();
assert_eq!(days, 59); }
#[test]
fn test_parse_date_to_days_april() {
let days = parse_date_to_days("1970-04-01", 0, 0).unwrap();
assert_eq!(days, 90); }
#[test]
fn test_parse_date_to_days_may() {
let days = parse_date_to_days("1970-05-01", 0, 0).unwrap();
assert_eq!(days, 120); }
#[test]
fn test_parse_date_to_days_june() {
let days = parse_date_to_days("1970-06-01", 0, 0).unwrap();
assert_eq!(days, 151);
}
#[test]
fn test_parse_date_to_days_july() {
let days = parse_date_to_days("1970-07-01", 0, 0).unwrap();
assert_eq!(days, 181);
}
#[test]
fn test_parse_date_to_days_august() {
let days = parse_date_to_days("1970-08-01", 0, 0).unwrap();
assert_eq!(days, 212);
}
#[test]
fn test_parse_date_to_days_september() {
let days = parse_date_to_days("1970-09-01", 0, 0).unwrap();
assert_eq!(days, 243);
}
#[test]
fn test_parse_date_to_days_october() {
let days = parse_date_to_days("1970-10-01", 0, 0).unwrap();
assert_eq!(days, 273);
}
#[test]
fn test_parse_date_to_days_november() {
let days = parse_date_to_days("1970-11-01", 0, 0).unwrap();
assert_eq!(days, 304);
}
#[test]
fn test_parse_date_to_days_december() {
let days = parse_date_to_days("1970-12-01", 0, 0).unwrap();
assert_eq!(days, 334);
}
#[test]
fn test_parse_date_to_days_leap_year_2000() {
let march_1_2000 = parse_date_to_days("2000-03-01", 0, 0).unwrap();
let feb_28_2000 = parse_date_to_days("2000-02-28", 0, 0).unwrap();
assert_eq!(march_1_2000 - feb_28_2000, 2); }
#[test]
fn test_parse_date_to_days_leap_year_2004() {
let march_1_2004 = parse_date_to_days("2004-03-01", 0, 0).unwrap();
let feb_28_2004 = parse_date_to_days("2004-02-28", 0, 0).unwrap();
assert_eq!(march_1_2004 - feb_28_2004, 2); }
#[test]
fn test_parse_date_to_days_non_leap_year_1900() {
let march_1_1900 = parse_date_to_days("1900-03-01", 0, 0).unwrap();
let feb_28_1900 = parse_date_to_days("1900-02-28", 0, 0).unwrap();
assert_eq!(march_1_1900 - feb_28_1900, 1); }
#[test]
fn test_parse_date_to_days_non_leap_year_2023() {
let march_1_2023 = parse_date_to_days("2023-03-01", 0, 0).unwrap();
let feb_28_2023 = parse_date_to_days("2023-02-28", 0, 0).unwrap();
assert_eq!(march_1_2023 - feb_28_2023, 1); }
#[test]
fn test_parse_date_to_days_leap_adjustment_only_after_february() {
let jan_31_2000 = parse_date_to_days("2000-01-31", 0, 0).unwrap();
let jan_30_2000 = parse_date_to_days("2000-01-30", 0, 0).unwrap();
assert_eq!(jan_31_2000 - jan_30_2000, 1); }
#[test]
fn test_parse_date_to_days_before_unix_epoch() {
let days = parse_date_to_days("1969-12-31", 0, 0).unwrap();
assert_eq!(days, -1);
}
#[test]
fn test_parse_date_to_days_far_future() {
let days = parse_date_to_days("2100-01-01", 0, 0).unwrap();
assert!(days > 0);
}
#[test]
fn test_parse_date_to_days_invalid_format_missing_parts() {
let result = parse_date_to_days("2024-01", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_date_to_days_invalid_format_too_many_parts() {
let result = parse_date_to_days("2024-01-15-extra", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_date_to_days_invalid_year() {
let result = parse_date_to_days("XXXX-01-15", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_date_to_days_invalid_month() {
let result = parse_date_to_days("2024-XX-15", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_date_to_days_invalid_day() {
let result = parse_date_to_days("2024-01-XX", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_build_timestamp_array_without_timezone() {
let values = vec![
json!("2024-01-15 10:30:00"),
json!("2024-06-20 14:45:30.123456"),
];
let refs = to_refs(&values);
let result = build_timestamp_array(&refs, false, 0).unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_timestamp_array_with_timezone() {
let values = vec![
json!("2024-01-15 10:30:00"),
json!("2024-06-20 14:45:30.123456"),
];
let refs = to_refs(&values);
let result = build_timestamp_array(&refs, true, 0).unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_timestamp_array_with_null_values() {
let values = vec![
json!("2024-01-15 10:30:00"),
json!(null),
json!("2024-06-20 14:45:30"),
];
let refs = to_refs(&values);
let result = build_timestamp_array(&refs, false, 0).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result.null_count(), 1);
}
#[test]
fn test_build_timestamp_array_date_only() {
let values = vec![json!("2024-01-15")];
let refs = to_refs(&values);
let result = build_timestamp_array(&refs, false, 0).unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_build_timestamp_array_invalid_not_string() {
let values = vec![json!(12345)];
let refs = to_refs(&values);
let result = build_timestamp_array(&refs, false, 0);
assert!(result.is_err());
match result.unwrap_err() {
ConversionError::ValueConversionFailed { message, .. } => {
assert!(message.contains("Expected timestamp string"));
}
_ => panic!("Expected ValueConversionFailed"),
}
}
#[test]
fn test_parse_timestamp_to_micros_with_fractional_seconds_3_digits() {
let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.123", 0, 0).unwrap();
assert_eq!(micros, 123_000);
}
#[test]
fn test_parse_timestamp_to_micros_with_fractional_seconds_6_digits() {
let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.123456", 0, 0).unwrap();
assert_eq!(micros, 123_456);
}
#[test]
fn test_parse_timestamp_to_micros_with_fractional_seconds_9_digits() {
let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.123456789", 0, 0).unwrap();
assert_eq!(micros, 123_456);
}
#[test]
fn test_parse_timestamp_to_micros_with_fractional_seconds_1_digit() {
let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.1", 0, 0).unwrap();
assert_eq!(micros, 100_000);
}
#[test]
fn test_parse_timestamp_to_micros_with_fractional_seconds_2_digits() {
let micros = parse_timestamp_to_micros("1970-01-01 00:00:00.12", 0, 0).unwrap();
assert_eq!(micros, 120_000);
}
#[test]
fn test_parse_timestamp_to_micros_hours_minutes_only() {
let micros = parse_timestamp_to_micros("1970-01-01 12:30", 0, 0).unwrap();
assert_eq!(micros, 12 * 3600 * 1_000_000 + 30 * 60 * 1_000_000);
}
#[test]
fn test_parse_timestamp_to_micros_full_time() {
let micros = parse_timestamp_to_micros("1970-01-01 12:30:45", 0, 0).unwrap();
assert_eq!(
micros,
12 * 3600 * 1_000_000 + 30 * 60 * 1_000_000 + 45 * 1_000_000
);
}
#[test]
fn test_parse_timestamp_to_micros_empty_string() {
let result = parse_timestamp_to_micros("", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_timestamp_to_micros_invalid_hour() {
let result = parse_timestamp_to_micros("1970-01-01 XX:30:00", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_timestamp_to_micros_invalid_minute() {
let result = parse_timestamp_to_micros("1970-01-01 12:XX:00", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_timestamp_to_micros_invalid_second() {
let result = parse_timestamp_to_micros("1970-01-01 12:30:XX", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_build_interval_year_to_month_array_positive() {
let values = vec![json!("+01-06"), json!("+00-03")];
let refs = to_refs(&values);
let result = build_interval_year_to_month_array(&refs, 0).unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_interval_year_to_month_array_negative() {
let values = vec![json!("-02-03"), json!("-00-11")];
let refs = to_refs(&values);
let result = build_interval_year_to_month_array(&refs, 0).unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_interval_year_to_month_array_with_nulls() {
let values = vec![json!("+01-06"), json!(null), json!("-02-03")];
let refs = to_refs(&values);
let result = build_interval_year_to_month_array(&refs, 0).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result.null_count(), 1);
}
#[test]
fn test_build_interval_year_to_month_array_invalid_not_string() {
let values = vec![json!(12345)];
let refs = to_refs(&values);
let result = build_interval_year_to_month_array(&refs, 0);
assert!(result.is_err());
match result.unwrap_err() {
ConversionError::ValueConversionFailed { message, .. } => {
assert!(message.contains("Expected interval string"));
}
_ => panic!("Expected ValueConversionFailed"),
}
}
#[test]
fn test_parse_interval_year_to_month_zero_years() {
let months = parse_interval_year_to_month("+00-06", 0, 0).unwrap();
assert_eq!(months, 6);
}
#[test]
fn test_parse_interval_year_to_month_zero_months() {
let months = parse_interval_year_to_month("+05-00", 0, 0).unwrap();
assert_eq!(months, 60);
}
#[test]
fn test_parse_interval_year_to_month_large_value() {
let months = parse_interval_year_to_month("+99-11", 0, 0).unwrap();
assert_eq!(months, 99 * 12 + 11);
}
#[test]
fn test_parse_interval_year_to_month_negative_large_value() {
let months = parse_interval_year_to_month("-99-11", 0, 0).unwrap();
assert_eq!(months, -(99 * 12 + 11));
}
#[test]
fn test_parse_interval_year_to_month_invalid_format() {
let result = parse_interval_year_to_month("01-06", 0, 0); assert!(result.is_ok());
}
#[test]
fn test_parse_interval_year_to_month_invalid_format_missing_parts() {
let result = parse_interval_year_to_month("+01", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_interval_year_to_month_invalid_years() {
let result = parse_interval_year_to_month("+XX-06", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_interval_year_to_month_invalid_months() {
let result = parse_interval_year_to_month("+01-XX", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_build_interval_day_to_second_array_with_fractional_seconds() {
let values = vec![json!("+01 12:30:45.123456789")];
let refs = to_refs(&values);
let result = build_interval_day_to_second_array(&refs, 0).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_interval_day_to_second_array_without_fractional_seconds() {
let values = vec![json!("+01 12:30:45")];
let refs = to_refs(&values);
let result = build_interval_day_to_second_array(&refs, 0).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_interval_day_to_second_array_negative() {
let values = vec![json!("-02 08:15:30.500000000")];
let refs = to_refs(&values);
let result = build_interval_day_to_second_array(&refs, 0).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_interval_day_to_second_array_with_nulls() {
let values = vec![json!("+01 12:30:45"), json!(null), json!("-02 08:15:30")];
let refs = to_refs(&values);
let result = build_interval_day_to_second_array(&refs, 0).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result.null_count(), 1);
}
#[test]
fn test_build_interval_day_to_second_array_invalid_not_string() {
let values = vec![json!(12345)];
let refs = to_refs(&values);
let result = build_interval_day_to_second_array(&refs, 0);
assert!(result.is_err());
match result.unwrap_err() {
ConversionError::ValueConversionFailed { message, .. } => {
assert!(message.contains("Expected interval string"));
}
_ => panic!("Expected ValueConversionFailed"),
}
}
#[test]
fn test_parse_interval_day_to_second_days_only() {
let (days, nanos) = parse_interval_day_to_second("+05", 0, 0).unwrap();
assert_eq!(days, 5);
assert_eq!(nanos, 0);
}
#[test]
fn test_parse_interval_day_to_second_hours_minutes_only() {
let (days, nanos) = parse_interval_day_to_second("+01 12:30", 0, 0).unwrap();
assert_eq!(days, 1);
assert_eq!(nanos, 12 * 3600 * 1_000_000_000 + 30 * 60 * 1_000_000_000);
}
#[test]
fn test_parse_interval_day_to_second_negative_values() {
let (days, nanos) = parse_interval_day_to_second("-03 06:15:30", 0, 0).unwrap();
assert_eq!(days, -3);
assert_eq!(
nanos,
-(6 * 3600 * 1_000_000_000 + 15 * 60 * 1_000_000_000 + 30 * 1_000_000_000)
);
}
#[test]
fn test_parse_interval_day_to_second_with_fractional_3_digits() {
let (days, nanos) = parse_interval_day_to_second("+00 00:00:00.123", 0, 0).unwrap();
assert_eq!(days, 0);
assert_eq!(nanos, 123_000_000);
}
#[test]
fn test_parse_interval_day_to_second_with_fractional_6_digits() {
let (days, nanos) = parse_interval_day_to_second("+00 00:00:00.123456", 0, 0).unwrap();
assert_eq!(days, 0);
assert_eq!(nanos, 123_456_000);
}
#[test]
fn test_parse_interval_day_to_second_with_fractional_9_digits() {
let (days, nanos) = parse_interval_day_to_second("+00 00:00:00.123456789", 0, 0).unwrap();
assert_eq!(days, 0);
assert_eq!(nanos, 123_456_789);
}
#[test]
fn test_parse_interval_day_to_second_with_fractional_12_digits() {
let (days, nanos) =
parse_interval_day_to_second("+00 00:00:00.123456789012", 0, 0).unwrap();
assert_eq!(days, 0);
assert_eq!(nanos, 123_456_789);
}
#[test]
fn test_parse_interval_day_to_second_empty_string() {
let result = parse_interval_day_to_second("", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_interval_day_to_second_invalid_days() {
let result = parse_interval_day_to_second("+XX 12:30:45", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_interval_day_to_second_invalid_hours() {
let result = parse_interval_day_to_second("+01 XX:30:45", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_interval_day_to_second_invalid_minutes() {
let result = parse_interval_day_to_second("+01 12:XX:45", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_interval_day_to_second_invalid_seconds() {
let result = parse_interval_day_to_second("+01 12:30:XX", 0, 0);
assert!(result.is_err());
}
#[test]
fn test_build_binary_array_valid_hex() {
let values = vec![json!("48656c6c6f"), json!("576f726c64")];
let refs = to_refs(&values);
let result = build_binary_array(&refs, 0).unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_binary_array_with_null_values() {
let values = vec![json!("48656c6c6f"), json!(null), json!("576f726c64")];
let refs = to_refs(&values);
let result = build_binary_array(&refs, 0).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result.null_count(), 1);
}
#[test]
fn test_build_binary_array_empty_hex() {
let values = vec![json!("")];
let refs = to_refs(&values);
let result = build_binary_array(&refs, 0).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result.null_count(), 0);
}
#[test]
fn test_build_binary_array_invalid_not_string() {
let values = vec![json!(12345)];
let refs = to_refs(&values);
let result = build_binary_array(&refs, 0);
assert!(result.is_err());
match result.unwrap_err() {
ConversionError::ValueConversionFailed { message, .. } => {
assert!(message.contains("Expected hex string"));
}
_ => panic!("Expected ValueConversionFailed"),
}
}
#[test]
fn test_build_binary_array_invalid_hex_odd_length() {
let values = vec![json!("abc")]; let refs = to_refs(&values);
let result = build_binary_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_build_binary_array_invalid_hex_chars() {
let values = vec![json!("ghij")]; let refs = to_refs(&values);
let result = build_binary_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_build_binary_array_capacity_estimation_with_samples() {
let values: Vec<Value> = (0..15).map(|_| json!("deadbeef")).collect();
let refs = to_refs(&values);
let result = build_binary_array(&refs, 0).unwrap();
assert_eq!(result.len(), 15);
}
#[test]
fn test_build_binary_array_capacity_estimation_all_nulls() {
let values: Vec<Value> = (0..5).map(|_| json!(null)).collect();
let refs = to_refs(&values);
let result = build_binary_array(&refs, 0).unwrap();
assert_eq!(result.len(), 5);
assert_eq!(result.null_count(), 5);
}
#[test]
fn test_build_double_array_from_integer() {
let values = vec![json!(42), json!(100)];
let refs = to_refs(&values);
let result = build_double_array(&refs, 0).unwrap();
assert_eq!(result.len(), 2);
}
#[test]
fn test_build_double_array_from_string_numeric() {
let values = vec![json!("3.14159")];
let refs = to_refs(&values);
let result = build_double_array(&refs, 0).unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_build_double_array_neg_infinity() {
let values = vec![json!("-Infinity")];
let refs = to_refs(&values);
let result = build_double_array(&refs, 0).unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_build_double_array_nan() {
let values = vec![json!("NaN")];
let refs = to_refs(&values);
let result = build_double_array(&refs, 0).unwrap();
assert_eq!(result.len(), 1);
}
#[test]
fn test_build_double_array_invalid_string() {
let values = vec![json!("not a number")];
let refs = to_refs(&values);
let result = build_double_array(&refs, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_decimal_to_i128_from_float() {
let result = parse_decimal_to_i128(&json!(123.45), 10, 2, 0, 0).unwrap();
assert_eq!(result, 12345);
}
#[test]
fn test_parse_decimal_to_i128_invalid_decimal_format() {
let result = parse_decimal_to_i128(&json!("1.2.3"), 10, 2, 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_decimal_to_i128_invalid_integer_part() {
let result = parse_decimal_to_i128(&json!("abc.45"), 10, 2, 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_decimal_to_i128_invalid_decimal_part() {
let result = parse_decimal_to_i128(&json!("123.xyz"), 10, 2, 0, 0);
assert!(result.is_err());
}
#[test]
fn test_parse_decimal_to_i128_non_numeric_value() {
let result = parse_decimal_to_i128(&json!({"key": "value"}), 10, 2, 0, 0);
assert!(result.is_err());
}
#[test]
fn test_decode_hex_uppercase() {
let bytes = decode_hex("DEADBEEF").unwrap();
assert_eq!(bytes, vec![0xDE, 0xAD, 0xBE, 0xEF]);
}
#[test]
fn test_decode_hex_mixed_case() {
let bytes = decode_hex("DeAdBeEf").unwrap();
assert_eq!(bytes, vec![0xDE, 0xAD, 0xBE, 0xEF]);
}
}