1pub use arrow;
2
3use arrow::array::*;
4use arrow::datatypes::i256;
5use sea_query::{ColumnType, Value};
6
7#[derive(Debug, thiserror::Error)]
13pub enum ArrowError {
14 #[error("expected {expected} for column type {col_type}, got Arrow type {actual}")]
16 TypeMismatch {
17 expected: &'static str,
18 col_type: &'static str,
19 actual: String,
20 },
21
22 #[error("{0}")]
24 OutOfRange(String),
25
26 #[error("{0}")]
28 Unsupported(String),
29}
30
31fn type_err(expected: &'static str, col_type: &'static str, array: &dyn Array) -> ArrowError {
32 ArrowError::TypeMismatch {
33 expected,
34 col_type,
35 actual: format!("{:?}", array.data_type()),
36 }
37}
38
39pub fn arrow_array_to_value(
50 array: &dyn Array,
51 col_type: &ColumnType,
52 row: usize,
53) -> Result<Value, ArrowError> {
54 if array.is_null(row) {
55 return Ok(null_value_for_type(col_type));
56 }
57 match col_type {
58 ColumnType::TinyInteger => {
59 let arr = array
60 .as_any()
61 .downcast_ref::<Int8Array>()
62 .ok_or_else(|| type_err("Int8Array", "TinyInteger", array))?;
63 Ok(Value::TinyInt(Some(arr.value(row))))
64 }
65 ColumnType::SmallInteger => {
66 let arr = array
67 .as_any()
68 .downcast_ref::<Int16Array>()
69 .ok_or_else(|| type_err("Int16Array", "SmallInteger", array))?;
70 Ok(Value::SmallInt(Some(arr.value(row))))
71 }
72 ColumnType::Integer => {
73 let arr = array
74 .as_any()
75 .downcast_ref::<Int32Array>()
76 .ok_or_else(|| type_err("Int32Array", "Integer", array))?;
77 Ok(Value::Int(Some(arr.value(row))))
78 }
79 ColumnType::BigInteger => {
80 let arr = array
81 .as_any()
82 .downcast_ref::<Int64Array>()
83 .ok_or_else(|| type_err("Int64Array", "BigInteger", array))?;
84 Ok(Value::BigInt(Some(arr.value(row))))
85 }
86 ColumnType::TinyUnsigned => {
87 let arr = array
88 .as_any()
89 .downcast_ref::<UInt8Array>()
90 .ok_or_else(|| type_err("UInt8Array", "TinyUnsigned", array))?;
91 Ok(Value::TinyUnsigned(Some(arr.value(row))))
92 }
93 ColumnType::SmallUnsigned => {
94 let arr = array
95 .as_any()
96 .downcast_ref::<UInt16Array>()
97 .ok_or_else(|| type_err("UInt16Array", "SmallUnsigned", array))?;
98 Ok(Value::SmallUnsigned(Some(arr.value(row))))
99 }
100 ColumnType::Unsigned => {
101 let arr = array
102 .as_any()
103 .downcast_ref::<UInt32Array>()
104 .ok_or_else(|| type_err("UInt32Array", "Unsigned", array))?;
105 Ok(Value::Unsigned(Some(arr.value(row))))
106 }
107 ColumnType::BigUnsigned => {
108 let arr = array
109 .as_any()
110 .downcast_ref::<UInt64Array>()
111 .ok_or_else(|| type_err("UInt64Array", "BigUnsigned", array))?;
112 Ok(Value::BigUnsigned(Some(arr.value(row))))
113 }
114 ColumnType::Float => {
115 let arr = array
116 .as_any()
117 .downcast_ref::<Float32Array>()
118 .ok_or_else(|| type_err("Float32Array", "Float", array))?;
119 Ok(Value::Float(Some(arr.value(row))))
120 }
121 ColumnType::Double => {
122 let arr = array
123 .as_any()
124 .downcast_ref::<Float64Array>()
125 .ok_or_else(|| type_err("Float64Array", "Double", array))?;
126 Ok(Value::Double(Some(arr.value(row))))
127 }
128 ColumnType::String(_) | ColumnType::Text | ColumnType::Char(_) => {
129 if let Some(arr) = array.as_any().downcast_ref::<StringArray>() {
130 Ok(Value::String(Some(arr.value(row).to_owned())))
131 } else if let Some(arr) = array.as_any().downcast_ref::<LargeStringArray>() {
132 Ok(Value::String(Some(arr.value(row).to_owned())))
133 } else {
134 Err(type_err(
135 "StringArray or LargeStringArray",
136 "String/Text",
137 array,
138 ))
139 }
140 }
141 ColumnType::Boolean => {
142 let arr = array
143 .as_any()
144 .downcast_ref::<BooleanArray>()
145 .ok_or_else(|| type_err("BooleanArray", "Boolean", array))?;
146 Ok(Value::Bool(Some(arr.value(row))))
147 }
148 ColumnType::Binary(_) | ColumnType::VarBinary(_) => arrow_to_bytes(array, row),
150 ColumnType::Decimal(_) | ColumnType::Money(_) => arrow_to_decimal(array, row),
152 #[cfg(feature = "with-chrono")]
155 ColumnType::Date => arrow_to_chrono_date(array, row),
156 #[cfg(feature = "with-chrono")]
157 ColumnType::Time => arrow_to_chrono_time(array, row),
158 #[cfg(feature = "with-chrono")]
159 ColumnType::DateTime | ColumnType::Timestamp => arrow_to_chrono_datetime(array, row),
160 #[cfg(feature = "with-chrono")]
161 ColumnType::TimestampWithTimeZone => arrow_to_chrono_datetime_utc(array, row),
162
163 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
164 ColumnType::Date => arrow_to_time_date(array, row),
165 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
166 ColumnType::Time => arrow_to_time_time(array, row),
167 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
168 ColumnType::DateTime | ColumnType::Timestamp => arrow_to_time_datetime(array, row),
169 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
170 ColumnType::TimestampWithTimeZone => arrow_to_time_datetime_tz(array, row),
171
172 _ => Err(ArrowError::Unsupported(format!(
173 "Unsupported column type for Arrow conversion: {col_type:?}"
174 ))),
175 }
176}
177
178#[cfg(all(feature = "with-chrono", feature = "with-time"))]
182pub fn arrow_array_to_value_alt(
183 array: &dyn Array,
184 col_type: &ColumnType,
185 row: usize,
186) -> Result<Option<Value>, ArrowError> {
187 if array.is_null(row) {
188 return Ok(Some(null_value_for_type_time(col_type)));
189 }
190 match col_type {
191 ColumnType::Date => arrow_to_time_date(array, row).map(Some),
192 ColumnType::Time => arrow_to_time_time(array, row).map(Some),
193 ColumnType::DateTime | ColumnType::Timestamp => {
194 arrow_to_time_datetime(array, row).map(Some)
195 }
196 ColumnType::TimestampWithTimeZone => arrow_to_time_datetime_tz(array, row).map(Some),
197 _ => Ok(None),
198 }
199}
200
201pub fn is_datetime_column(col_type: &ColumnType) -> bool {
203 matches!(
204 col_type,
205 ColumnType::Date
206 | ColumnType::Time
207 | ColumnType::DateTime
208 | ColumnType::Timestamp
209 | ColumnType::TimestampWithTimeZone
210 )
211}
212
213fn arrow_to_bytes(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
218 if let Some(arr) = array.as_any().downcast_ref::<BinaryArray>() {
219 return Ok(Value::Bytes(Some(arr.value(row).to_vec())));
220 }
221 if let Some(arr) = array.as_any().downcast_ref::<LargeBinaryArray>() {
222 return Ok(Value::Bytes(Some(arr.value(row).to_vec())));
223 }
224 if let Some(arr) = array.as_any().downcast_ref::<FixedSizeBinaryArray>() {
225 return Ok(Value::Bytes(Some(arr.value(row).to_vec())));
226 }
227 Err(type_err(
228 "BinaryArray, LargeBinaryArray, or FixedSizeBinaryArray",
229 "Binary/VarBinary",
230 array,
231 ))
232}
233
234fn arrow_to_decimal(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
241 if let Some(arr) = array.as_any().downcast_ref::<Decimal128Array>() {
242 let value = arr.value(row);
243 let precision = arr.precision();
244 let scale = arr.scale();
245 return decimal128_to_value(value, precision, scale);
246 }
247
248 if let Some(arr) = array.as_any().downcast_ref::<Decimal64Array>() {
249 let value = arr.value(row);
250 let precision = arr.precision();
251 let scale = arr.scale();
252 return decimal64_to_value(value, precision, scale);
253 }
254
255 if let Some(arr) = array.as_any().downcast_ref::<Decimal256Array>() {
256 let value = arr.value(row);
257 let precision = arr.precision();
258 let scale = arr.scale();
259 return decimal256_to_value(value, precision, scale);
260 }
261
262 Err(type_err(
263 "Decimal64Array, Decimal128Array, or Decimal256Array",
264 "Decimal",
265 array,
266 ))
267}
268
269#[cfg(feature = "with-rust_decimal")]
270fn decimal64_to_value(value: i64, _precision: u8, scale: i8) -> Result<Value, ArrowError> {
271 use sea_query::prelude::Decimal;
272
273 if scale < 0 {
274 #[cfg(feature = "with-bigdecimal")]
275 return decimal64_to_bigdecimal(value, scale);
276
277 #[cfg(not(feature = "with-bigdecimal"))]
278 return Err(ArrowError::Unsupported(format!(
279 "Decimal64 with negative scale={scale} not supported by rust_decimal. \
280 Enable 'with-bigdecimal' feature."
281 )));
282 }
283
284 let decimal = Decimal::from_i128_with_scale(value as i128, scale as u32);
285 Ok(Value::Decimal(Some(decimal)))
286}
287
288#[cfg(not(feature = "with-rust_decimal"))]
289fn decimal64_to_value(_value: i64, _precision: u8, _scale: i8) -> Result<Value, ArrowError> {
290 #[cfg(feature = "with-bigdecimal")]
291 return decimal64_to_bigdecimal(_value, _scale);
292
293 #[cfg(not(feature = "with-bigdecimal"))]
294 Err(ArrowError::Unsupported(
295 "Decimal64Array requires 'with-rust_decimal' or 'with-bigdecimal' feature".into(),
296 ))
297}
298
299#[cfg(feature = "with-bigdecimal")]
300fn decimal64_to_bigdecimal(value: i64, scale: i8) -> Result<Value, ArrowError> {
301 use sea_query::prelude::bigdecimal::{BigDecimal, num_bigint::BigInt};
302
303 let bigint = BigInt::from(value);
304 let decimal = BigDecimal::new(bigint, scale as i64);
305 Ok(Value::BigDecimal(Some(Box::new(decimal))))
306}
307
308#[cfg(feature = "with-rust_decimal")]
309fn decimal128_to_value(value: i128, precision: u8, scale: i8) -> Result<Value, ArrowError> {
310 use sea_query::prelude::Decimal;
311
312 if precision > 28 || scale > 28 || scale < 0 {
313 #[cfg(feature = "with-bigdecimal")]
314 return decimal128_to_bigdecimal(value, scale);
315
316 #[cfg(not(feature = "with-bigdecimal"))]
317 return Err(ArrowError::Unsupported(format!(
318 "Decimal128 with precision={precision}, scale={scale} exceeds rust_decimal limits \
319 (max precision=28, scale=0-28). Enable 'with-bigdecimal' feature for arbitrary precision."
320 )));
321 }
322
323 let decimal = Decimal::from_i128_with_scale(value, scale as u32);
324 Ok(Value::Decimal(Some(decimal)))
325}
326
327#[cfg(not(feature = "with-rust_decimal"))]
328fn decimal128_to_value(_value: i128, _precision: u8, _scale: i8) -> Result<Value, ArrowError> {
329 #[cfg(feature = "with-bigdecimal")]
330 return decimal128_to_bigdecimal(_value, _scale);
331
332 #[cfg(not(feature = "with-bigdecimal"))]
333 Err(ArrowError::Unsupported(
334 "Decimal128Array requires 'with-rust_decimal' or 'with-bigdecimal' feature".into(),
335 ))
336}
337
338#[cfg(feature = "with-bigdecimal")]
339fn decimal128_to_bigdecimal(value: i128, scale: i8) -> Result<Value, ArrowError> {
340 use sea_query::prelude::bigdecimal::{BigDecimal, num_bigint::BigInt};
341
342 let bigint = BigInt::from(value);
343 let decimal = BigDecimal::new(bigint, scale as i64);
344 Ok(Value::BigDecimal(Some(Box::new(decimal))))
345}
346
347fn decimal256_to_value(_value: i256, _precision: u8, _scale: i8) -> Result<Value, ArrowError> {
348 #[cfg(feature = "with-bigdecimal")]
349 {
350 use sea_query::prelude::bigdecimal::{
351 BigDecimal,
352 num_bigint::{BigInt, Sign},
353 };
354
355 let bytes = _value.to_be_bytes();
356
357 let (sign, magnitude) = if _value.is_negative() {
358 let mut abs_bytes = [0u8; 32];
359 let mut carry = true;
360
361 for i in (0..32).rev() {
362 abs_bytes[i] = !bytes[i];
363 if carry {
364 if abs_bytes[i] == 255 {
365 abs_bytes[i] = 0;
366 } else {
367 abs_bytes[i] += 1;
368 carry = false;
369 }
370 }
371 }
372
373 (Sign::Minus, abs_bytes.to_vec())
374 } else if _value == i256::ZERO {
375 (Sign::NoSign, vec![0])
376 } else {
377 let first_nonzero = bytes.iter().position(|&b| b != 0).unwrap_or(31);
378 (Sign::Plus, bytes[first_nonzero..].to_vec())
379 };
380
381 let bigint = BigInt::from_bytes_be(sign, &magnitude);
382 let decimal = BigDecimal::new(bigint, _scale as i64);
383 return Ok(Value::BigDecimal(Some(Box::new(decimal))));
384 }
385
386 #[cfg(not(feature = "with-bigdecimal"))]
387 Err(ArrowError::Unsupported(
388 "Decimal256Array requires 'with-bigdecimal' feature for arbitrary precision support".into(),
389 ))
390}
391
392#[cfg(feature = "with-chrono")]
397fn arrow_to_chrono_date(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
398 use sea_query::prelude::chrono::NaiveDate;
399 let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).expect("valid date");
400
401 if let Some(arr) = array.as_any().downcast_ref::<Date32Array>() {
402 let days = arr.value(row);
403 let date = epoch
404 .checked_add_signed(sea_query::prelude::chrono::Duration::days(days as i64))
405 .ok_or_else(|| ArrowError::OutOfRange(format!("Date32 value {days} out of range")))?;
406 Ok(Value::ChronoDate(Some(date)))
407 } else if let Some(arr) = array.as_any().downcast_ref::<Date64Array>() {
408 let ms = arr.value(row);
409 let date = epoch
410 .checked_add_signed(sea_query::prelude::chrono::Duration::milliseconds(ms))
411 .ok_or_else(|| ArrowError::OutOfRange(format!("Date64 value {ms} out of range")))?;
412 Ok(Value::ChronoDate(Some(date)))
413 } else {
414 Err(type_err("Date32Array or Date64Array", "Date", array))
415 }
416}
417
418#[cfg(feature = "with-chrono")]
419fn arrow_to_chrono_time(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
420 use sea_query::prelude::chrono::NaiveTime;
421
422 if let Some(arr) = array.as_any().downcast_ref::<Time32SecondArray>() {
423 let secs = arr.value(row) as u32;
424 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, 0).ok_or_else(|| {
425 ArrowError::OutOfRange(format!("Time32Second value {secs} out of range"))
426 })?;
427 Ok(Value::ChronoTime(Some(t)))
428 } else if let Some(arr) = array.as_any().downcast_ref::<Time32MillisecondArray>() {
429 let ms = arr.value(row);
430 let secs = (ms / 1_000) as u32;
431 let nanos = ((ms % 1_000) * 1_000_000) as u32;
432 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or_else(|| {
433 ArrowError::OutOfRange(format!("Time32Millisecond value {ms} out of range"))
434 })?;
435 Ok(Value::ChronoTime(Some(t)))
436 } else if let Some(arr) = array.as_any().downcast_ref::<Time64MicrosecondArray>() {
437 let us = arr.value(row);
438 let secs = (us / 1_000_000) as u32;
439 let nanos = ((us % 1_000_000) * 1_000) as u32;
440 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or_else(|| {
441 ArrowError::OutOfRange(format!("Time64Microsecond value {us} out of range"))
442 })?;
443 Ok(Value::ChronoTime(Some(t)))
444 } else if let Some(arr) = array.as_any().downcast_ref::<Time64NanosecondArray>() {
445 let ns = arr.value(row);
446 let secs = (ns / 1_000_000_000) as u32;
447 let nanos = (ns % 1_000_000_000) as u32;
448 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or_else(|| {
449 ArrowError::OutOfRange(format!("Time64Nanosecond value {ns} out of range"))
450 })?;
451 Ok(Value::ChronoTime(Some(t)))
452 } else {
453 Err(type_err("Time32/Time64 Array", "Time", array))
454 }
455}
456
457#[cfg(feature = "with-chrono")]
458fn arrow_timestamp_to_utc(
459 array: &dyn Array,
460 row: usize,
461) -> Result<sea_query::prelude::chrono::DateTime<sea_query::prelude::chrono::Utc>, ArrowError> {
462 use sea_query::prelude::chrono::{DateTime, Utc};
463
464 if let Some(arr) = array.as_any().downcast_ref::<TimestampSecondArray>() {
465 DateTime::<Utc>::from_timestamp(arr.value(row), 0)
466 .ok_or_else(|| ArrowError::OutOfRange("Timestamp seconds out of range".into()))
467 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMillisecondArray>() {
468 DateTime::<Utc>::from_timestamp_millis(arr.value(row))
469 .ok_or_else(|| ArrowError::OutOfRange("Timestamp milliseconds out of range".into()))
470 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMicrosecondArray>() {
471 DateTime::<Utc>::from_timestamp_micros(arr.value(row))
472 .ok_or_else(|| ArrowError::OutOfRange("Timestamp microseconds out of range".into()))
473 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampNanosecondArray>() {
474 let nanos = arr.value(row);
475 let secs = nanos.div_euclid(1_000_000_000);
476 let nsec = nanos.rem_euclid(1_000_000_000) as u32;
477 DateTime::<Utc>::from_timestamp(secs, nsec)
478 .ok_or_else(|| ArrowError::OutOfRange("Timestamp nanoseconds out of range".into()))
479 } else {
480 Err(type_err(
481 "TimestampSecond/Millisecond/Microsecond/NanosecondArray",
482 "DateTime/Timestamp",
483 array,
484 ))
485 }
486}
487
488#[cfg(feature = "with-chrono")]
489fn arrow_to_chrono_datetime(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
490 let dt = arrow_timestamp_to_utc(array, row)?;
491 Ok(Value::ChronoDateTime(Some(dt.naive_utc())))
492}
493
494#[cfg(feature = "with-chrono")]
495fn arrow_to_chrono_datetime_utc(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
496 let dt = arrow_timestamp_to_utc(array, row)?;
497 Ok(Value::ChronoDateTimeUtc(Some(dt)))
498}
499
500#[cfg(feature = "with-time")]
505fn arrow_to_time_date(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
506 const EPOCH_JULIAN: i32 = 2_440_588;
507
508 if let Some(arr) = array.as_any().downcast_ref::<Date32Array>() {
509 let days = arr.value(row);
510 let date =
511 sea_query::prelude::time::Date::from_julian_day(EPOCH_JULIAN + days).map_err(|e| {
512 ArrowError::OutOfRange(format!("Date32 value {days} out of range: {e}"))
513 })?;
514 Ok(Value::TimeDate(Some(date)))
515 } else if let Some(arr) = array.as_any().downcast_ref::<Date64Array>() {
516 let ms = arr.value(row);
517 let days = (ms / 86_400_000) as i32;
518 let date = sea_query::prelude::time::Date::from_julian_day(EPOCH_JULIAN + days)
519 .map_err(|e| ArrowError::OutOfRange(format!("Date64 value {ms} out of range: {e}")))?;
520 Ok(Value::TimeDate(Some(date)))
521 } else {
522 Err(type_err("Date32Array or Date64Array", "Date", array))
523 }
524}
525
526#[cfg(feature = "with-time")]
527fn arrow_to_time_time(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
528 if let Some(arr) = array.as_any().downcast_ref::<Time32SecondArray>() {
529 let secs = arr.value(row);
530 let t = sea_query::prelude::time::Time::from_hms(
531 (secs / 3600) as u8,
532 ((secs % 3600) / 60) as u8,
533 (secs % 60) as u8,
534 )
535 .map_err(|e| {
536 ArrowError::OutOfRange(format!("Time32Second value {secs} out of range: {e}"))
537 })?;
538 Ok(Value::TimeTime(Some(t)))
539 } else if let Some(arr) = array.as_any().downcast_ref::<Time32MillisecondArray>() {
540 let ms = arr.value(row);
541 let total_secs = ms / 1_000;
542 let nanos = ((ms % 1_000) * 1_000_000) as u32;
543 let t = sea_query::prelude::time::Time::from_hms_nano(
544 (total_secs / 3600) as u8,
545 ((total_secs % 3600) / 60) as u8,
546 (total_secs % 60) as u8,
547 nanos,
548 )
549 .map_err(|e| {
550 ArrowError::OutOfRange(format!("Time32Millisecond value {ms} out of range: {e}"))
551 })?;
552 Ok(Value::TimeTime(Some(t)))
553 } else if let Some(arr) = array.as_any().downcast_ref::<Time64MicrosecondArray>() {
554 let us = arr.value(row);
555 let total_secs = us / 1_000_000;
556 let nanos = ((us % 1_000_000) * 1_000) as u32;
557 let t = sea_query::prelude::time::Time::from_hms_nano(
558 (total_secs / 3600) as u8,
559 ((total_secs % 3600) / 60) as u8,
560 (total_secs % 60) as u8,
561 nanos,
562 )
563 .map_err(|e| {
564 ArrowError::OutOfRange(format!("Time64Microsecond value {us} out of range: {e}"))
565 })?;
566 Ok(Value::TimeTime(Some(t)))
567 } else if let Some(arr) = array.as_any().downcast_ref::<Time64NanosecondArray>() {
568 let ns = arr.value(row);
569 let total_secs = ns / 1_000_000_000;
570 let nanos = (ns % 1_000_000_000) as u32;
571 let t = sea_query::prelude::time::Time::from_hms_nano(
572 (total_secs / 3600) as u8,
573 ((total_secs % 3600) / 60) as u8,
574 (total_secs % 60) as u8,
575 nanos,
576 )
577 .map_err(|e| {
578 ArrowError::OutOfRange(format!("Time64Nanosecond value {ns} out of range: {e}"))
579 })?;
580 Ok(Value::TimeTime(Some(t)))
581 } else {
582 Err(type_err("Time32/Time64 Array", "Time", array))
583 }
584}
585
586#[cfg(feature = "with-time")]
587fn arrow_timestamp_to_offset_dt(
588 array: &dyn Array,
589 row: usize,
590) -> Result<sea_query::prelude::time::OffsetDateTime, ArrowError> {
591 if let Some(arr) = array.as_any().downcast_ref::<TimestampSecondArray>() {
592 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp(arr.value(row))
593 .map_err(|e| ArrowError::OutOfRange(format!("Timestamp seconds out of range: {e}")))
594 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMillisecondArray>() {
595 let ms = arr.value(row);
596 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp_nanos(ms as i128 * 1_000_000)
597 .map_err(|e| {
598 ArrowError::OutOfRange(format!("Timestamp milliseconds out of range: {e}"))
599 })
600 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMicrosecondArray>() {
601 let us = arr.value(row);
602 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp_nanos(us as i128 * 1_000)
603 .map_err(|e| {
604 ArrowError::OutOfRange(format!("Timestamp microseconds out of range: {e}"))
605 })
606 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampNanosecondArray>() {
607 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp_nanos(arr.value(row) as i128)
608 .map_err(|e| ArrowError::OutOfRange(format!("Timestamp nanoseconds out of range: {e}")))
609 } else {
610 Err(type_err(
611 "TimestampSecond/Millisecond/Microsecond/NanosecondArray",
612 "DateTime/Timestamp",
613 array,
614 ))
615 }
616}
617
618#[cfg(feature = "with-time")]
619fn arrow_to_time_datetime(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
620 let odt = arrow_timestamp_to_offset_dt(array, row)?;
621 Ok(Value::TimeDateTime(Some(
622 sea_query::prelude::time::PrimitiveDateTime::new(odt.date(), odt.time()),
623 )))
624}
625
626#[cfg(feature = "with-time")]
627fn arrow_to_time_datetime_tz(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
628 let odt = arrow_timestamp_to_offset_dt(array, row)?;
629 Ok(Value::TimeDateTimeWithTimeZone(Some(odt)))
630}
631
632fn null_value_for_type(col_type: &ColumnType) -> Value {
637 match col_type {
638 ColumnType::TinyInteger => Value::TinyInt(None),
639 ColumnType::SmallInteger => Value::SmallInt(None),
640 ColumnType::Integer => Value::Int(None),
641 ColumnType::BigInteger => Value::BigInt(None),
642 ColumnType::TinyUnsigned => Value::TinyUnsigned(None),
643 ColumnType::SmallUnsigned => Value::SmallUnsigned(None),
644 ColumnType::Unsigned => Value::Unsigned(None),
645 ColumnType::BigUnsigned => Value::BigUnsigned(None),
646 ColumnType::Float => Value::Float(None),
647 ColumnType::Double => Value::Double(None),
648 ColumnType::String(_) | ColumnType::Text | ColumnType::Char(_) => Value::String(None),
649 ColumnType::Binary(_) | ColumnType::VarBinary(_) => Value::Bytes(None),
650 ColumnType::Boolean => Value::Bool(None),
651 #[cfg(feature = "with-rust_decimal")]
652 ColumnType::Decimal(_) | ColumnType::Money(_) => Value::Decimal(None),
653 #[cfg(all(feature = "with-bigdecimal", not(feature = "with-rust_decimal")))]
654 ColumnType::Decimal(_) | ColumnType::Money(_) => Value::BigDecimal(None),
655 #[cfg(feature = "with-chrono")]
656 ColumnType::Date => Value::ChronoDate(None),
657 #[cfg(feature = "with-chrono")]
658 ColumnType::Time => Value::ChronoTime(None),
659 #[cfg(feature = "with-chrono")]
660 ColumnType::DateTime | ColumnType::Timestamp => Value::ChronoDateTime(None),
661 #[cfg(feature = "with-chrono")]
662 ColumnType::TimestampWithTimeZone => Value::ChronoDateTimeUtc(None),
663 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
664 ColumnType::Date => Value::TimeDate(None),
665 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
666 ColumnType::Time => Value::TimeTime(None),
667 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
668 ColumnType::DateTime | ColumnType::Timestamp => Value::TimeDateTime(None),
669 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
670 ColumnType::TimestampWithTimeZone => Value::TimeDateTimeWithTimeZone(None),
671 _ => Value::Int(None),
672 }
673}
674
675#[cfg(all(feature = "with-chrono", feature = "with-time"))]
677fn null_value_for_type_time(col_type: &ColumnType) -> Value {
678 match col_type {
679 ColumnType::Date => Value::TimeDate(None),
680 ColumnType::Time => Value::TimeTime(None),
681 ColumnType::DateTime | ColumnType::Timestamp => Value::TimeDateTime(None),
682 ColumnType::TimestampWithTimeZone => Value::TimeDateTimeWithTimeZone(None),
683 _ => null_value_for_type(col_type),
684 }
685}
686
687pub fn values_to_arrow_array(
696 values: &[Value],
697 data_type: &arrow::datatypes::DataType,
698) -> Result<std::sync::Arc<dyn Array>, ArrowError> {
699 use arrow::datatypes::{DataType, TimeUnit};
700 use std::sync::Arc;
701
702 match data_type {
703 DataType::Int8 => {
704 let arr: Int8Array = values
705 .iter()
706 .map(|v| match v {
707 Value::TinyInt(inner) => *inner,
708 _ => None,
709 })
710 .collect();
711 Ok(Arc::new(arr))
712 }
713 DataType::Int16 => {
714 let arr: Int16Array = values
715 .iter()
716 .map(|v| match v {
717 Value::SmallInt(inner) => *inner,
718 _ => None,
719 })
720 .collect();
721 Ok(Arc::new(arr))
722 }
723 DataType::Int32 => {
724 let arr: Int32Array = values
725 .iter()
726 .map(|v| match v {
727 Value::Int(inner) => *inner,
728 _ => None,
729 })
730 .collect();
731 Ok(Arc::new(arr))
732 }
733 DataType::Int64 => {
734 let arr: Int64Array = values
735 .iter()
736 .map(|v| match v {
737 Value::BigInt(inner) => *inner,
738 _ => None,
739 })
740 .collect();
741 Ok(Arc::new(arr))
742 }
743 DataType::UInt8 => {
744 let arr: UInt8Array = values
745 .iter()
746 .map(|v| match v {
747 Value::TinyUnsigned(inner) => *inner,
748 _ => None,
749 })
750 .collect();
751 Ok(Arc::new(arr))
752 }
753 DataType::UInt16 => {
754 let arr: UInt16Array = values
755 .iter()
756 .map(|v| match v {
757 Value::SmallUnsigned(inner) => *inner,
758 _ => None,
759 })
760 .collect();
761 Ok(Arc::new(arr))
762 }
763 DataType::UInt32 => {
764 let arr: UInt32Array = values
765 .iter()
766 .map(|v| match v {
767 Value::Unsigned(inner) => *inner,
768 _ => None,
769 })
770 .collect();
771 Ok(Arc::new(arr))
772 }
773 DataType::UInt64 => {
774 let arr: UInt64Array = values
775 .iter()
776 .map(|v| match v {
777 Value::BigUnsigned(inner) => *inner,
778 _ => None,
779 })
780 .collect();
781 Ok(Arc::new(arr))
782 }
783 DataType::Float32 => {
784 let arr: Float32Array = values
785 .iter()
786 .map(|v| match v {
787 Value::Float(inner) => *inner,
788 _ => None,
789 })
790 .collect();
791 Ok(Arc::new(arr))
792 }
793 DataType::Float64 => {
794 let arr: Float64Array = values
795 .iter()
796 .map(|v| match v {
797 Value::Double(inner) => *inner,
798 _ => None,
799 })
800 .collect();
801 Ok(Arc::new(arr))
802 }
803 DataType::Boolean => {
804 let arr: BooleanArray = values
805 .iter()
806 .map(|v| match v {
807 Value::Bool(inner) => *inner,
808 _ => None,
809 })
810 .collect();
811 Ok(Arc::new(arr))
812 }
813 DataType::Utf8 => {
814 let strs: Vec<Option<&str>> = values
815 .iter()
816 .map(|v| match v {
817 Value::String(Some(s)) => Some(s.as_str()),
818 _ => None,
819 })
820 .collect();
821 Ok(Arc::new(StringArray::from(strs)))
822 }
823 DataType::LargeUtf8 => {
824 let strs: Vec<Option<&str>> = values
825 .iter()
826 .map(|v| match v {
827 Value::String(Some(s)) => Some(s.as_str()),
828 _ => None,
829 })
830 .collect();
831 Ok(Arc::new(LargeStringArray::from(strs)))
832 }
833 DataType::Binary => {
834 let bufs: Vec<Option<&[u8]>> = values
835 .iter()
836 .map(|v| match v {
837 Value::Bytes(Some(b)) => Some(b.as_slice()),
838 _ => None,
839 })
840 .collect();
841 Ok(Arc::new(BinaryArray::from(bufs)))
842 }
843 DataType::LargeBinary => {
844 let bufs: Vec<Option<&[u8]>> = values
845 .iter()
846 .map(|v| match v {
847 Value::Bytes(Some(b)) => Some(b.as_slice()),
848 _ => None,
849 })
850 .collect();
851 Ok(Arc::new(LargeBinaryArray::from(bufs)))
852 }
853 DataType::FixedSizeBinary(byte_width) => {
854 let mut builder = FixedSizeBinaryBuilder::with_capacity(values.len(), *byte_width);
855 for v in values {
856 match v {
857 Value::Bytes(Some(b)) => builder.append_value(b.as_slice()).map_err(|e| {
858 ArrowError::Unsupported(format!("FixedSizeBinary append error: {e}"))
859 })?,
860 _ => builder.append_null(),
861 }
862 }
863 Ok(Arc::new(builder.finish()))
864 }
865 DataType::Date32 => {
866 let arr: Date32Array = values.iter().map(extract_date32).collect();
867 Ok(Arc::new(arr))
868 }
869 DataType::Time32(unit) => {
870 let vals: Vec<Option<i32>> = values.iter().map(|v| extract_time32(v, unit)).collect();
871 let arr: Arc<dyn Array> = match unit {
872 TimeUnit::Second => Arc::new(Time32SecondArray::from(vals)),
873 TimeUnit::Millisecond => Arc::new(Time32MillisecondArray::from(vals)),
874 _ => {
875 return Err(ArrowError::Unsupported(format!(
876 "Unsupported Time32 unit: {unit:?}"
877 )));
878 }
879 };
880 Ok(arr)
881 }
882 DataType::Time64(unit) => {
883 let vals: Vec<Option<i64>> = values.iter().map(|v| extract_time64(v, unit)).collect();
884 let arr: Arc<dyn Array> = match unit {
885 TimeUnit::Microsecond => Arc::new(Time64MicrosecondArray::from(vals)),
886 TimeUnit::Nanosecond => Arc::new(Time64NanosecondArray::from(vals)),
887 _ => {
888 return Err(ArrowError::Unsupported(format!(
889 "Unsupported Time64 unit: {unit:?}"
890 )));
891 }
892 };
893 Ok(arr)
894 }
895 DataType::Timestamp(unit, tz) => {
896 let vals: Vec<Option<i64>> =
897 values.iter().map(|v| extract_timestamp(v, unit)).collect();
898 let arr: Arc<dyn Array> = match unit {
899 TimeUnit::Second => {
900 let mut a = TimestampSecondArray::from(vals);
901 if let Some(tz) = tz {
902 a = a.with_timezone(tz.as_ref());
903 }
904 Arc::new(a)
905 }
906 TimeUnit::Millisecond => {
907 let mut a = TimestampMillisecondArray::from(vals);
908 if let Some(tz) = tz {
909 a = a.with_timezone(tz.as_ref());
910 }
911 Arc::new(a)
912 }
913 TimeUnit::Microsecond => {
914 let mut a = TimestampMicrosecondArray::from(vals);
915 if let Some(tz) = tz {
916 a = a.with_timezone(tz.as_ref());
917 }
918 Arc::new(a)
919 }
920 TimeUnit::Nanosecond => {
921 let mut a = TimestampNanosecondArray::from(vals);
922 if let Some(tz) = tz {
923 a = a.with_timezone(tz.as_ref());
924 }
925 Arc::new(a)
926 }
927 };
928 Ok(arr)
929 }
930 DataType::Decimal64(precision, scale) => {
931 let arr: Decimal64Array = values
932 .iter()
933 .map(|v| extract_decimal64(v, *scale))
934 .collect();
935 let arr = arr
936 .with_precision_and_scale(*precision, *scale)
937 .map_err(|e| {
938 ArrowError::Unsupported(format!("Invalid Decimal64 precision/scale: {e}"))
939 })?;
940 Ok(Arc::new(arr))
941 }
942 DataType::Decimal128(precision, scale) => {
943 let arr: Decimal128Array = values
944 .iter()
945 .map(|v| extract_decimal128(v, *scale))
946 .collect();
947 let arr = arr
948 .with_precision_and_scale(*precision, *scale)
949 .map_err(|e| {
950 ArrowError::Unsupported(format!("Invalid Decimal128 precision/scale: {e}"))
951 })?;
952 Ok(Arc::new(arr))
953 }
954 DataType::Decimal256(precision, scale) => {
955 let arr: Decimal256Array = values
956 .iter()
957 .map(|v| extract_decimal256(v, *scale))
958 .collect();
959 let arr = arr
960 .with_precision_and_scale(*precision, *scale)
961 .map_err(|e| {
962 ArrowError::Unsupported(format!("Invalid Decimal256 precision/scale: {e}"))
963 })?;
964 Ok(Arc::new(arr))
965 }
966 _ => Err(ArrowError::Unsupported(format!(
967 "Unsupported Arrow DataType for to_arrow: {data_type:?}"
968 ))),
969 }
970}
971
972pub fn option_values_to_arrow_array(
978 values: &[Option<Value>],
979 data_type: &arrow::datatypes::DataType,
980) -> Result<std::sync::Arc<dyn Array>, ArrowError> {
981 use arrow::datatypes::{DataType, TimeUnit};
982 use std::sync::Arc;
983
984 match data_type {
985 DataType::Int8 => {
986 let arr: Int8Array = values
987 .iter()
988 .map(|v| match v {
989 Some(Value::TinyInt(inner)) => *inner,
990 _ => None,
991 })
992 .collect();
993 Ok(Arc::new(arr))
994 }
995 DataType::Int16 => {
996 let arr: Int16Array = values
997 .iter()
998 .map(|v| match v {
999 Some(Value::SmallInt(inner)) => *inner,
1000 _ => None,
1001 })
1002 .collect();
1003 Ok(Arc::new(arr))
1004 }
1005 DataType::Int32 => {
1006 let arr: Int32Array = values
1007 .iter()
1008 .map(|v| match v {
1009 Some(Value::Int(inner)) => *inner,
1010 _ => None,
1011 })
1012 .collect();
1013 Ok(Arc::new(arr))
1014 }
1015 DataType::Int64 => {
1016 let arr: Int64Array = values
1017 .iter()
1018 .map(|v| match v {
1019 Some(Value::BigInt(inner)) => *inner,
1020 _ => None,
1021 })
1022 .collect();
1023 Ok(Arc::new(arr))
1024 }
1025 DataType::UInt8 => {
1026 let arr: UInt8Array = values
1027 .iter()
1028 .map(|v| match v {
1029 Some(Value::TinyUnsigned(inner)) => *inner,
1030 _ => None,
1031 })
1032 .collect();
1033 Ok(Arc::new(arr))
1034 }
1035 DataType::UInt16 => {
1036 let arr: UInt16Array = values
1037 .iter()
1038 .map(|v| match v {
1039 Some(Value::SmallUnsigned(inner)) => *inner,
1040 _ => None,
1041 })
1042 .collect();
1043 Ok(Arc::new(arr))
1044 }
1045 DataType::UInt32 => {
1046 let arr: UInt32Array = values
1047 .iter()
1048 .map(|v| match v {
1049 Some(Value::Unsigned(inner)) => *inner,
1050 _ => None,
1051 })
1052 .collect();
1053 Ok(Arc::new(arr))
1054 }
1055 DataType::UInt64 => {
1056 let arr: UInt64Array = values
1057 .iter()
1058 .map(|v| match v {
1059 Some(Value::BigUnsigned(inner)) => *inner,
1060 _ => None,
1061 })
1062 .collect();
1063 Ok(Arc::new(arr))
1064 }
1065 DataType::Float32 => {
1066 let arr: Float32Array = values
1067 .iter()
1068 .map(|v| match v {
1069 Some(Value::Float(inner)) => *inner,
1070 _ => None,
1071 })
1072 .collect();
1073 Ok(Arc::new(arr))
1074 }
1075 DataType::Float64 => {
1076 let arr: Float64Array = values
1077 .iter()
1078 .map(|v| match v {
1079 Some(Value::Double(inner)) => *inner,
1080 _ => None,
1081 })
1082 .collect();
1083 Ok(Arc::new(arr))
1084 }
1085 DataType::Boolean => {
1086 let arr: BooleanArray = values
1087 .iter()
1088 .map(|v| match v {
1089 Some(Value::Bool(inner)) => *inner,
1090 _ => None,
1091 })
1092 .collect();
1093 Ok(Arc::new(arr))
1094 }
1095 DataType::Utf8 => {
1096 let strs: Vec<Option<&str>> = values
1097 .iter()
1098 .map(|v| match v {
1099 Some(Value::String(Some(s))) => Some(s.as_str()),
1100 _ => None,
1101 })
1102 .collect();
1103 Ok(Arc::new(StringArray::from(strs)))
1104 }
1105 DataType::LargeUtf8 => {
1106 let strs: Vec<Option<&str>> = values
1107 .iter()
1108 .map(|v| match v {
1109 Some(Value::String(Some(s))) => Some(s.as_str()),
1110 _ => None,
1111 })
1112 .collect();
1113 Ok(Arc::new(LargeStringArray::from(strs)))
1114 }
1115 DataType::Binary => {
1116 let bufs: Vec<Option<&[u8]>> = values
1117 .iter()
1118 .map(|v| match v {
1119 Some(Value::Bytes(Some(b))) => Some(b.as_slice()),
1120 _ => None,
1121 })
1122 .collect();
1123 Ok(Arc::new(BinaryArray::from(bufs)))
1124 }
1125 DataType::LargeBinary => {
1126 let bufs: Vec<Option<&[u8]>> = values
1127 .iter()
1128 .map(|v| match v {
1129 Some(Value::Bytes(Some(b))) => Some(b.as_slice()),
1130 _ => None,
1131 })
1132 .collect();
1133 Ok(Arc::new(LargeBinaryArray::from(bufs)))
1134 }
1135 DataType::FixedSizeBinary(byte_width) => {
1136 let mut builder = FixedSizeBinaryBuilder::with_capacity(values.len(), *byte_width);
1137 for v in values {
1138 match v {
1139 Some(Value::Bytes(Some(b))) => {
1140 builder.append_value(b.as_slice()).map_err(|e| {
1141 ArrowError::Unsupported(format!("FixedSizeBinary append error: {e}"))
1142 })?
1143 }
1144 _ => builder.append_null(),
1145 }
1146 }
1147 Ok(Arc::new(builder.finish()))
1148 }
1149 DataType::Date32 => {
1150 let arr: Date32Array = values.iter().map(extract_date32_option).collect();
1151 Ok(Arc::new(arr))
1152 }
1153 DataType::Time32(unit) => {
1154 let vals: Vec<Option<i32>> = values
1155 .iter()
1156 .map(|v| extract_time32_option(v, unit))
1157 .collect();
1158 let arr: Arc<dyn Array> = match unit {
1159 TimeUnit::Second => Arc::new(Time32SecondArray::from(vals)),
1160 TimeUnit::Millisecond => Arc::new(Time32MillisecondArray::from(vals)),
1161 _ => {
1162 return Err(ArrowError::Unsupported(format!(
1163 "Unsupported Time32 unit: {unit:?}"
1164 )));
1165 }
1166 };
1167 Ok(arr)
1168 }
1169 DataType::Time64(unit) => {
1170 let vals: Vec<Option<i64>> = values
1171 .iter()
1172 .map(|v| extract_time64_option(v, unit))
1173 .collect();
1174 let arr: Arc<dyn Array> = match unit {
1175 TimeUnit::Microsecond => Arc::new(Time64MicrosecondArray::from(vals)),
1176 TimeUnit::Nanosecond => Arc::new(Time64NanosecondArray::from(vals)),
1177 _ => {
1178 return Err(ArrowError::Unsupported(format!(
1179 "Unsupported Time64 unit: {unit:?}"
1180 )));
1181 }
1182 };
1183 Ok(arr)
1184 }
1185 DataType::Timestamp(unit, tz) => {
1186 let vals: Vec<Option<i64>> = values
1187 .iter()
1188 .map(|v| extract_timestamp_option(v, unit))
1189 .collect();
1190 let arr: Arc<dyn Array> = match unit {
1191 TimeUnit::Second => {
1192 let mut a = TimestampSecondArray::from(vals);
1193 if let Some(tz) = tz {
1194 a = a.with_timezone(tz.as_ref());
1195 }
1196 Arc::new(a)
1197 }
1198 TimeUnit::Millisecond => {
1199 let mut a = TimestampMillisecondArray::from(vals);
1200 if let Some(tz) = tz {
1201 a = a.with_timezone(tz.as_ref());
1202 }
1203 Arc::new(a)
1204 }
1205 TimeUnit::Microsecond => {
1206 let mut a = TimestampMicrosecondArray::from(vals);
1207 if let Some(tz) = tz {
1208 a = a.with_timezone(tz.as_ref());
1209 }
1210 Arc::new(a)
1211 }
1212 TimeUnit::Nanosecond => {
1213 let mut a = TimestampNanosecondArray::from(vals);
1214 if let Some(tz) = tz {
1215 a = a.with_timezone(tz.as_ref());
1216 }
1217 Arc::new(a)
1218 }
1219 };
1220 Ok(arr)
1221 }
1222 DataType::Decimal64(precision, scale) => {
1223 let arr: Decimal64Array = values
1224 .iter()
1225 .map(|v| extract_decimal64_option(v, *scale))
1226 .collect();
1227 let arr = arr
1228 .with_precision_and_scale(*precision, *scale)
1229 .map_err(|e| {
1230 ArrowError::Unsupported(format!("Invalid Decimal64 precision/scale: {e}"))
1231 })?;
1232 Ok(Arc::new(arr))
1233 }
1234 DataType::Decimal128(precision, scale) => {
1235 let arr: Decimal128Array = values
1236 .iter()
1237 .map(|v| extract_decimal128_option(v, *scale))
1238 .collect();
1239 let arr = arr
1240 .with_precision_and_scale(*precision, *scale)
1241 .map_err(|e| {
1242 ArrowError::Unsupported(format!("Invalid Decimal128 precision/scale: {e}"))
1243 })?;
1244 Ok(Arc::new(arr))
1245 }
1246 DataType::Decimal256(precision, scale) => {
1247 let arr: Decimal256Array = values
1248 .iter()
1249 .map(|v| extract_decimal256_option(v, *scale))
1250 .collect();
1251 let arr = arr
1252 .with_precision_and_scale(*precision, *scale)
1253 .map_err(|e| {
1254 ArrowError::Unsupported(format!("Invalid Decimal256 precision/scale: {e}"))
1255 })?;
1256 Ok(Arc::new(arr))
1257 }
1258 _ => Err(ArrowError::Unsupported(format!(
1259 "Unsupported Arrow DataType for to_arrow: {data_type:?}"
1260 ))),
1261 }
1262}
1263
1264fn extract_date32_option(v: &Option<Value>) -> Option<i32> {
1269 extract_date32(v.as_ref()?)
1270}
1271
1272fn extract_date32(v: &Value) -> Option<i32> {
1273 #[cfg(feature = "with-chrono")]
1274 if let Value::ChronoDate(Some(d)) = v {
1275 let epoch = sea_query::prelude::chrono::NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
1276 return Some((*d - epoch).num_days() as i32);
1277 }
1278 #[cfg(feature = "with-time")]
1279 if let Value::TimeDate(Some(d)) = v {
1280 return Some(d.to_julian_day() - 2_440_588);
1281 }
1282 let _ = v;
1283 None
1284}
1285
1286fn extract_time32_option(v: &Option<Value>, unit: &arrow::datatypes::TimeUnit) -> Option<i32> {
1291 extract_time32(v.as_ref()?, unit)
1292}
1293
1294fn extract_time32(v: &Value, unit: &arrow::datatypes::TimeUnit) -> Option<i32> {
1295 #[cfg(any(feature = "with-chrono", feature = "with-time"))]
1296 use arrow::datatypes::TimeUnit;
1297
1298 #[cfg(feature = "with-chrono")]
1299 if let Value::ChronoTime(Some(t)) = v {
1300 use sea_query::prelude::chrono::Timelike;
1301 let secs = t.num_seconds_from_midnight() as i32;
1302 return match unit {
1303 TimeUnit::Second => Some(secs),
1304 TimeUnit::Millisecond => {
1305 let ms = (t.nanosecond() / 1_000_000) as i32;
1306 Some(secs * 1_000 + ms)
1307 }
1308 _ => None,
1309 };
1310 }
1311 #[cfg(feature = "with-time")]
1312 if let Value::TimeTime(Some(t)) = v {
1313 let secs = (t.hour() as i32) * 3600 + (t.minute() as i32) * 60 + (t.second() as i32);
1314 return match unit {
1315 TimeUnit::Second => Some(secs),
1316 TimeUnit::Millisecond => {
1317 let ms = (t.nanosecond() / 1_000_000) as i32;
1318 Some(secs * 1_000 + ms)
1319 }
1320 _ => None,
1321 };
1322 }
1323 let _ = (v, unit);
1324 None
1325}
1326
1327fn extract_time64_option(v: &Option<Value>, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1328 extract_time64(v.as_ref()?, unit)
1329}
1330
1331fn extract_time64(v: &Value, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1332 #[cfg(any(feature = "with-chrono", feature = "with-time"))]
1333 use arrow::datatypes::TimeUnit;
1334
1335 #[cfg(feature = "with-chrono")]
1336 if let Value::ChronoTime(Some(t)) = v {
1337 use sea_query::prelude::chrono::Timelike;
1338 let secs = t.num_seconds_from_midnight() as i64;
1339 let nanos = (t.nanosecond() % 1_000_000_000) as i64;
1340 return match unit {
1341 TimeUnit::Microsecond => Some(secs * 1_000_000 + nanos / 1_000),
1342 TimeUnit::Nanosecond => Some(secs * 1_000_000_000 + nanos),
1343 _ => None,
1344 };
1345 }
1346 #[cfg(feature = "with-time")]
1347 if let Value::TimeTime(Some(t)) = v {
1348 let secs = (t.hour() as i64) * 3600 + (t.minute() as i64) * 60 + (t.second() as i64);
1349 let nanos = t.nanosecond() as i64;
1350 return match unit {
1351 TimeUnit::Microsecond => Some(secs * 1_000_000 + nanos / 1_000),
1352 TimeUnit::Nanosecond => Some(secs * 1_000_000_000 + nanos),
1353 _ => None,
1354 };
1355 }
1356 let _ = (v, unit);
1357 None
1358}
1359
1360fn extract_timestamp_option(v: &Option<Value>, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1365 extract_timestamp(v.as_ref()?, unit)
1366}
1367
1368fn extract_timestamp(v: &Value, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1369 #[cfg(any(feature = "with-chrono", feature = "with-time"))]
1370 use arrow::datatypes::TimeUnit;
1371
1372 #[cfg(feature = "with-chrono")]
1373 {
1374 if let Value::ChronoDateTime(Some(dt)) = v {
1375 let utc = dt.and_utc();
1376 return Some(match unit {
1377 TimeUnit::Second => utc.timestamp(),
1378 TimeUnit::Millisecond => utc.timestamp_millis(),
1379 TimeUnit::Microsecond => utc.timestamp_micros(),
1380 TimeUnit::Nanosecond => utc.timestamp_nanos_opt().unwrap_or(0),
1381 });
1382 }
1383 if let Value::ChronoDateTimeUtc(Some(dt)) = v {
1384 return Some(match unit {
1385 TimeUnit::Second => dt.timestamp(),
1386 TimeUnit::Millisecond => dt.timestamp_millis(),
1387 TimeUnit::Microsecond => dt.timestamp_micros(),
1388 TimeUnit::Nanosecond => dt.timestamp_nanos_opt().unwrap_or(0),
1389 });
1390 }
1391 }
1392 #[cfg(feature = "with-time")]
1393 {
1394 if let Value::TimeDateTime(Some(dt)) = v {
1395 let odt = dt.assume_utc();
1396 return Some(offset_dt_to_timestamp(&odt, unit));
1397 }
1398 if let Value::TimeDateTimeWithTimeZone(Some(dt)) = v {
1399 return Some(offset_dt_to_timestamp(dt, unit));
1400 }
1401 }
1402 let _ = (v, unit);
1403 None
1404}
1405
1406#[cfg(feature = "with-time")]
1407fn offset_dt_to_timestamp(
1408 dt: &sea_query::prelude::time::OffsetDateTime,
1409 unit: &arrow::datatypes::TimeUnit,
1410) -> i64 {
1411 use arrow::datatypes::TimeUnit;
1412 match unit {
1413 TimeUnit::Second => dt.unix_timestamp(),
1414 TimeUnit::Millisecond => (dt.unix_timestamp_nanos() / 1_000_000) as i64,
1415 TimeUnit::Microsecond => (dt.unix_timestamp_nanos() / 1_000) as i64,
1416 TimeUnit::Nanosecond => dt.unix_timestamp_nanos() as i64,
1417 }
1418}
1419
1420fn extract_decimal64_option(v: &Option<Value>, target_scale: i8) -> Option<i64> {
1425 extract_decimal64(v.as_ref()?, target_scale)
1426}
1427
1428fn extract_decimal64(v: &Value, target_scale: i8) -> Option<i64> {
1429 #[cfg(feature = "with-rust_decimal")]
1430 if let Value::Decimal(Some(d)) = v {
1431 let mantissa = d.mantissa();
1432 let current_scale = d.scale() as i8;
1433 let scale_diff = target_scale - current_scale;
1434 let scaled = if scale_diff >= 0 {
1435 mantissa * 10i128.pow(scale_diff as u32)
1436 } else {
1437 mantissa / 10i128.pow((-scale_diff) as u32)
1438 };
1439 return i64::try_from(scaled).ok();
1440 }
1441 #[cfg(feature = "with-bigdecimal")]
1442 if let Value::BigDecimal(Some(d)) = v {
1443 return bigdecimal_to_i64(d, target_scale);
1444 }
1445 let _ = (v, target_scale);
1446 None
1447}
1448
1449#[cfg(feature = "with-bigdecimal")]
1450fn bigdecimal_to_i64(
1451 d: &sea_query::prelude::bigdecimal::BigDecimal,
1452 target_scale: i8,
1453) -> Option<i64> {
1454 use sea_query::prelude::bigdecimal::ToPrimitive;
1455
1456 let rescaled = d.clone().with_scale(target_scale as i64);
1457 let (digits, _) = rescaled.into_bigint_and_exponent();
1458 digits.to_i64()
1459}
1460
1461fn extract_decimal128_option(v: &Option<Value>, target_scale: i8) -> Option<i128> {
1462 extract_decimal128(v.as_ref()?, target_scale)
1463}
1464
1465fn extract_decimal128(v: &Value, target_scale: i8) -> Option<i128> {
1466 #[cfg(feature = "with-rust_decimal")]
1467 if let Value::Decimal(Some(d)) = v {
1468 let mantissa = d.mantissa();
1469 let current_scale = d.scale() as i8;
1470 let scale_diff = target_scale - current_scale;
1471 return if scale_diff >= 0 {
1472 Some(mantissa * 10i128.pow(scale_diff as u32))
1473 } else {
1474 Some(mantissa / 10i128.pow((-scale_diff) as u32))
1475 };
1476 }
1477 #[cfg(feature = "with-bigdecimal")]
1478 if let Value::BigDecimal(Some(d)) = v {
1479 return bigdecimal_to_i128(d, target_scale);
1480 }
1481 let _ = (v, target_scale);
1482 None
1483}
1484
1485#[cfg(feature = "with-bigdecimal")]
1486fn bigdecimal_to_i128(
1487 d: &sea_query::prelude::bigdecimal::BigDecimal,
1488 target_scale: i8,
1489) -> Option<i128> {
1490 use sea_query::prelude::bigdecimal::ToPrimitive;
1491
1492 let rescaled = d.clone().with_scale(target_scale as i64);
1493 let (digits, _) = rescaled.into_bigint_and_exponent();
1494 digits.to_i128()
1495}
1496
1497fn extract_decimal256_option(v: &Option<Value>, target_scale: i8) -> Option<i256> {
1498 extract_decimal256(v.as_ref()?, target_scale)
1499}
1500
1501fn extract_decimal256(v: &Value, target_scale: i8) -> Option<i256> {
1502 #[cfg(feature = "with-bigdecimal")]
1503 if let Value::BigDecimal(Some(d)) = v {
1504 return bigdecimal_to_i256(d, target_scale);
1505 }
1506 #[cfg(feature = "with-rust_decimal")]
1507 if let Value::Decimal(Some(d)) = v {
1508 let mantissa = d.mantissa();
1509 let current_scale = d.scale() as i8;
1510 let scale_diff = target_scale - current_scale;
1511 let scaled = if scale_diff >= 0 {
1512 mantissa * 10i128.pow(scale_diff as u32)
1513 } else {
1514 mantissa / 10i128.pow((-scale_diff) as u32)
1515 };
1516 return Some(i256::from_i128(scaled));
1517 }
1518 let _ = (v, target_scale);
1519 None
1520}
1521
1522#[cfg(feature = "with-bigdecimal")]
1523fn bigdecimal_to_i256(
1524 d: &sea_query::prelude::bigdecimal::BigDecimal,
1525 target_scale: i8,
1526) -> Option<i256> {
1527 let rescaled = d.clone().with_scale(target_scale as i64);
1528 let (digits, _) = rescaled.into_bigint_and_exponent();
1529 bigint_to_i256(&digits)
1530}
1531
1532#[cfg(feature = "with-bigdecimal")]
1533fn bigint_to_i256(bi: &sea_query::prelude::bigdecimal::num_bigint::BigInt) -> Option<i256> {
1534 use sea_query::prelude::bigdecimal::num_bigint::Sign;
1535
1536 let (sign, bytes) = bi.to_bytes_be();
1537 if bytes.len() > 32 {
1538 return None;
1539 }
1540
1541 let mut buf = [0u8; 32];
1542 let start = 32 - bytes.len();
1543 buf[start..].copy_from_slice(&bytes);
1544
1545 let val = i256::from_be_bytes(buf);
1546 match sign {
1547 Sign::Minus => Some(val.wrapping_neg()),
1548 _ => Some(val),
1549 }
1550}