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::Decimal(_) | ColumnType::Money(_) => arrow_to_decimal(array, row),
150 #[cfg(feature = "with-chrono")]
153 ColumnType::Date => arrow_to_chrono_date(array, row),
154 #[cfg(feature = "with-chrono")]
155 ColumnType::Time => arrow_to_chrono_time(array, row),
156 #[cfg(feature = "with-chrono")]
157 ColumnType::DateTime | ColumnType::Timestamp => arrow_to_chrono_datetime(array, row),
158 #[cfg(feature = "with-chrono")]
159 ColumnType::TimestampWithTimeZone => arrow_to_chrono_datetime_utc(array, row),
160
161 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
162 ColumnType::Date => arrow_to_time_date(array, row),
163 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
164 ColumnType::Time => arrow_to_time_time(array, row),
165 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
166 ColumnType::DateTime | ColumnType::Timestamp => arrow_to_time_datetime(array, row),
167 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
168 ColumnType::TimestampWithTimeZone => arrow_to_time_datetime_tz(array, row),
169
170 _ => Err(ArrowError::Unsupported(format!(
171 "Unsupported column type for Arrow conversion: {col_type:?}"
172 ))),
173 }
174}
175
176#[cfg(all(feature = "with-chrono", feature = "with-time"))]
180pub fn arrow_array_to_value_alt(
181 array: &dyn Array,
182 col_type: &ColumnType,
183 row: usize,
184) -> Result<Option<Value>, ArrowError> {
185 if array.is_null(row) {
186 return Ok(Some(null_value_for_type_time(col_type)));
187 }
188 match col_type {
189 ColumnType::Date => arrow_to_time_date(array, row).map(Some),
190 ColumnType::Time => arrow_to_time_time(array, row).map(Some),
191 ColumnType::DateTime | ColumnType::Timestamp => {
192 arrow_to_time_datetime(array, row).map(Some)
193 }
194 ColumnType::TimestampWithTimeZone => arrow_to_time_datetime_tz(array, row).map(Some),
195 _ => Ok(None),
196 }
197}
198
199pub fn is_datetime_column(col_type: &ColumnType) -> bool {
201 matches!(
202 col_type,
203 ColumnType::Date
204 | ColumnType::Time
205 | ColumnType::DateTime
206 | ColumnType::Timestamp
207 | ColumnType::TimestampWithTimeZone
208 )
209}
210
211fn arrow_to_decimal(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
218 if let Some(arr) = array.as_any().downcast_ref::<Decimal128Array>() {
219 let value = arr.value(row);
220 let precision = arr.precision();
221 let scale = arr.scale();
222 return decimal128_to_value(value, precision, scale);
223 }
224
225 if let Some(arr) = array.as_any().downcast_ref::<Decimal256Array>() {
226 let value = arr.value(row);
227 let precision = arr.precision();
228 let scale = arr.scale();
229 return decimal256_to_value(value, precision, scale);
230 }
231
232 Err(type_err(
233 "Decimal128Array or Decimal256Array",
234 "Decimal",
235 array,
236 ))
237}
238
239#[cfg(feature = "with-rust_decimal")]
240fn decimal128_to_value(value: i128, precision: u8, scale: i8) -> Result<Value, ArrowError> {
241 use sea_query::prelude::Decimal;
242
243 if precision > 28 || scale > 28 || scale < 0 {
244 #[cfg(feature = "with-bigdecimal")]
245 return decimal128_to_bigdecimal(value, scale);
246
247 #[cfg(not(feature = "with-bigdecimal"))]
248 return Err(ArrowError::Unsupported(format!(
249 "Decimal128 with precision={precision}, scale={scale} exceeds rust_decimal limits \
250 (max precision=28, scale=0-28). Enable 'with-bigdecimal' feature for arbitrary precision."
251 )));
252 }
253
254 let decimal = Decimal::from_i128_with_scale(value, scale as u32);
255 Ok(Value::Decimal(Some(decimal)))
256}
257
258#[cfg(not(feature = "with-rust_decimal"))]
259fn decimal128_to_value(_value: i128, _precision: u8, _scale: i8) -> Result<Value, ArrowError> {
260 #[cfg(feature = "with-bigdecimal")]
261 return decimal128_to_bigdecimal(_value, _scale);
262
263 #[cfg(not(feature = "with-bigdecimal"))]
264 Err(ArrowError::Unsupported(
265 "Decimal128Array requires 'with-rust_decimal' or 'with-bigdecimal' feature".into(),
266 ))
267}
268
269#[cfg(feature = "with-bigdecimal")]
270fn decimal128_to_bigdecimal(value: i128, scale: i8) -> Result<Value, ArrowError> {
271 use sea_query::prelude::bigdecimal::{BigDecimal, num_bigint::BigInt};
272
273 let bigint = BigInt::from(value);
274 let decimal = BigDecimal::new(bigint, scale as i64);
275 Ok(Value::BigDecimal(Some(Box::new(decimal))))
276}
277
278fn decimal256_to_value(_value: i256, _precision: u8, _scale: i8) -> Result<Value, ArrowError> {
279 #[cfg(feature = "with-bigdecimal")]
280 {
281 use sea_query::prelude::bigdecimal::{
282 BigDecimal,
283 num_bigint::{BigInt, Sign},
284 };
285
286 let bytes = _value.to_be_bytes();
287
288 let (sign, magnitude) = if _value.is_negative() {
289 let mut abs_bytes = [0u8; 32];
290 let mut carry = true;
291
292 for i in (0..32).rev() {
293 abs_bytes[i] = !bytes[i];
294 if carry {
295 if abs_bytes[i] == 255 {
296 abs_bytes[i] = 0;
297 } else {
298 abs_bytes[i] += 1;
299 carry = false;
300 }
301 }
302 }
303
304 (Sign::Minus, abs_bytes.to_vec())
305 } else if _value == i256::ZERO {
306 (Sign::NoSign, vec![0])
307 } else {
308 let first_nonzero = bytes.iter().position(|&b| b != 0).unwrap_or(31);
309 (Sign::Plus, bytes[first_nonzero..].to_vec())
310 };
311
312 let bigint = BigInt::from_bytes_be(sign, &magnitude);
313 let decimal = BigDecimal::new(bigint, _scale as i64);
314 return Ok(Value::BigDecimal(Some(Box::new(decimal))));
315 }
316
317 #[cfg(not(feature = "with-bigdecimal"))]
318 Err(ArrowError::Unsupported(
319 "Decimal256Array requires 'with-bigdecimal' feature for arbitrary precision support".into(),
320 ))
321}
322
323#[cfg(feature = "with-chrono")]
328fn arrow_to_chrono_date(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
329 use sea_query::prelude::chrono::NaiveDate;
330 let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).expect("valid date");
331
332 if let Some(arr) = array.as_any().downcast_ref::<Date32Array>() {
333 let days = arr.value(row);
334 let date = epoch
335 .checked_add_signed(sea_query::prelude::chrono::Duration::days(days as i64))
336 .ok_or_else(|| ArrowError::OutOfRange(format!("Date32 value {days} out of range")))?;
337 Ok(Value::ChronoDate(Some(date)))
338 } else if let Some(arr) = array.as_any().downcast_ref::<Date64Array>() {
339 let ms = arr.value(row);
340 let date = epoch
341 .checked_add_signed(sea_query::prelude::chrono::Duration::milliseconds(ms))
342 .ok_or_else(|| ArrowError::OutOfRange(format!("Date64 value {ms} out of range")))?;
343 Ok(Value::ChronoDate(Some(date)))
344 } else {
345 Err(type_err("Date32Array or Date64Array", "Date", array))
346 }
347}
348
349#[cfg(feature = "with-chrono")]
350fn arrow_to_chrono_time(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
351 use sea_query::prelude::chrono::NaiveTime;
352
353 if let Some(arr) = array.as_any().downcast_ref::<Time32SecondArray>() {
354 let secs = arr.value(row) as u32;
355 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, 0).ok_or_else(|| {
356 ArrowError::OutOfRange(format!("Time32Second value {secs} out of range"))
357 })?;
358 Ok(Value::ChronoTime(Some(t)))
359 } else if let Some(arr) = array.as_any().downcast_ref::<Time32MillisecondArray>() {
360 let ms = arr.value(row);
361 let secs = (ms / 1_000) as u32;
362 let nanos = ((ms % 1_000) * 1_000_000) as u32;
363 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or_else(|| {
364 ArrowError::OutOfRange(format!("Time32Millisecond value {ms} out of range"))
365 })?;
366 Ok(Value::ChronoTime(Some(t)))
367 } else if let Some(arr) = array.as_any().downcast_ref::<Time64MicrosecondArray>() {
368 let us = arr.value(row);
369 let secs = (us / 1_000_000) as u32;
370 let nanos = ((us % 1_000_000) * 1_000) as u32;
371 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or_else(|| {
372 ArrowError::OutOfRange(format!("Time64Microsecond value {us} out of range"))
373 })?;
374 Ok(Value::ChronoTime(Some(t)))
375 } else if let Some(arr) = array.as_any().downcast_ref::<Time64NanosecondArray>() {
376 let ns = arr.value(row);
377 let secs = (ns / 1_000_000_000) as u32;
378 let nanos = (ns % 1_000_000_000) as u32;
379 let t = NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).ok_or_else(|| {
380 ArrowError::OutOfRange(format!("Time64Nanosecond value {ns} out of range"))
381 })?;
382 Ok(Value::ChronoTime(Some(t)))
383 } else {
384 Err(type_err("Time32/Time64 Array", "Time", array))
385 }
386}
387
388#[cfg(feature = "with-chrono")]
389fn arrow_timestamp_to_utc(
390 array: &dyn Array,
391 row: usize,
392) -> Result<sea_query::prelude::chrono::DateTime<sea_query::prelude::chrono::Utc>, ArrowError> {
393 use sea_query::prelude::chrono::{DateTime, Utc};
394
395 if let Some(arr) = array.as_any().downcast_ref::<TimestampSecondArray>() {
396 DateTime::<Utc>::from_timestamp(arr.value(row), 0)
397 .ok_or_else(|| ArrowError::OutOfRange("Timestamp seconds out of range".into()))
398 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMillisecondArray>() {
399 DateTime::<Utc>::from_timestamp_millis(arr.value(row))
400 .ok_or_else(|| ArrowError::OutOfRange("Timestamp milliseconds out of range".into()))
401 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMicrosecondArray>() {
402 DateTime::<Utc>::from_timestamp_micros(arr.value(row))
403 .ok_or_else(|| ArrowError::OutOfRange("Timestamp microseconds out of range".into()))
404 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampNanosecondArray>() {
405 let nanos = arr.value(row);
406 let secs = nanos.div_euclid(1_000_000_000);
407 let nsec = nanos.rem_euclid(1_000_000_000) as u32;
408 DateTime::<Utc>::from_timestamp(secs, nsec)
409 .ok_or_else(|| ArrowError::OutOfRange("Timestamp nanoseconds out of range".into()))
410 } else {
411 Err(type_err(
412 "TimestampSecond/Millisecond/Microsecond/NanosecondArray",
413 "DateTime/Timestamp",
414 array,
415 ))
416 }
417}
418
419#[cfg(feature = "with-chrono")]
420fn arrow_to_chrono_datetime(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
421 let dt = arrow_timestamp_to_utc(array, row)?;
422 Ok(Value::ChronoDateTime(Some(dt.naive_utc())))
423}
424
425#[cfg(feature = "with-chrono")]
426fn arrow_to_chrono_datetime_utc(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
427 let dt = arrow_timestamp_to_utc(array, row)?;
428 Ok(Value::ChronoDateTimeUtc(Some(dt)))
429}
430
431#[cfg(feature = "with-time")]
436fn arrow_to_time_date(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
437 const EPOCH_JULIAN: i32 = 2_440_588;
438
439 if let Some(arr) = array.as_any().downcast_ref::<Date32Array>() {
440 let days = arr.value(row);
441 let date =
442 sea_query::prelude::time::Date::from_julian_day(EPOCH_JULIAN + days).map_err(|e| {
443 ArrowError::OutOfRange(format!("Date32 value {days} out of range: {e}"))
444 })?;
445 Ok(Value::TimeDate(Some(date)))
446 } else if let Some(arr) = array.as_any().downcast_ref::<Date64Array>() {
447 let ms = arr.value(row);
448 let days = (ms / 86_400_000) as i32;
449 let date = sea_query::prelude::time::Date::from_julian_day(EPOCH_JULIAN + days)
450 .map_err(|e| ArrowError::OutOfRange(format!("Date64 value {ms} out of range: {e}")))?;
451 Ok(Value::TimeDate(Some(date)))
452 } else {
453 Err(type_err("Date32Array or Date64Array", "Date", array))
454 }
455}
456
457#[cfg(feature = "with-time")]
458fn arrow_to_time_time(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
459 if let Some(arr) = array.as_any().downcast_ref::<Time32SecondArray>() {
460 let secs = arr.value(row);
461 let t = sea_query::prelude::time::Time::from_hms(
462 (secs / 3600) as u8,
463 ((secs % 3600) / 60) as u8,
464 (secs % 60) as u8,
465 )
466 .map_err(|e| {
467 ArrowError::OutOfRange(format!("Time32Second value {secs} out of range: {e}"))
468 })?;
469 Ok(Value::TimeTime(Some(t)))
470 } else if let Some(arr) = array.as_any().downcast_ref::<Time32MillisecondArray>() {
471 let ms = arr.value(row);
472 let total_secs = ms / 1_000;
473 let nanos = ((ms % 1_000) * 1_000_000) as u32;
474 let t = sea_query::prelude::time::Time::from_hms_nano(
475 (total_secs / 3600) as u8,
476 ((total_secs % 3600) / 60) as u8,
477 (total_secs % 60) as u8,
478 nanos,
479 )
480 .map_err(|e| {
481 ArrowError::OutOfRange(format!("Time32Millisecond value {ms} out of range: {e}"))
482 })?;
483 Ok(Value::TimeTime(Some(t)))
484 } else if let Some(arr) = array.as_any().downcast_ref::<Time64MicrosecondArray>() {
485 let us = arr.value(row);
486 let total_secs = us / 1_000_000;
487 let nanos = ((us % 1_000_000) * 1_000) as u32;
488 let t = sea_query::prelude::time::Time::from_hms_nano(
489 (total_secs / 3600) as u8,
490 ((total_secs % 3600) / 60) as u8,
491 (total_secs % 60) as u8,
492 nanos,
493 )
494 .map_err(|e| {
495 ArrowError::OutOfRange(format!("Time64Microsecond value {us} out of range: {e}"))
496 })?;
497 Ok(Value::TimeTime(Some(t)))
498 } else if let Some(arr) = array.as_any().downcast_ref::<Time64NanosecondArray>() {
499 let ns = arr.value(row);
500 let total_secs = ns / 1_000_000_000;
501 let nanos = (ns % 1_000_000_000) as u32;
502 let t = sea_query::prelude::time::Time::from_hms_nano(
503 (total_secs / 3600) as u8,
504 ((total_secs % 3600) / 60) as u8,
505 (total_secs % 60) as u8,
506 nanos,
507 )
508 .map_err(|e| {
509 ArrowError::OutOfRange(format!("Time64Nanosecond value {ns} out of range: {e}"))
510 })?;
511 Ok(Value::TimeTime(Some(t)))
512 } else {
513 Err(type_err("Time32/Time64 Array", "Time", array))
514 }
515}
516
517#[cfg(feature = "with-time")]
518fn arrow_timestamp_to_offset_dt(
519 array: &dyn Array,
520 row: usize,
521) -> Result<sea_query::prelude::time::OffsetDateTime, ArrowError> {
522 if let Some(arr) = array.as_any().downcast_ref::<TimestampSecondArray>() {
523 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp(arr.value(row))
524 .map_err(|e| ArrowError::OutOfRange(format!("Timestamp seconds out of range: {e}")))
525 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMillisecondArray>() {
526 let ms = arr.value(row);
527 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp_nanos(ms as i128 * 1_000_000)
528 .map_err(|e| {
529 ArrowError::OutOfRange(format!("Timestamp milliseconds out of range: {e}"))
530 })
531 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMicrosecondArray>() {
532 let us = arr.value(row);
533 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp_nanos(us as i128 * 1_000)
534 .map_err(|e| {
535 ArrowError::OutOfRange(format!("Timestamp microseconds out of range: {e}"))
536 })
537 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampNanosecondArray>() {
538 sea_query::prelude::time::OffsetDateTime::from_unix_timestamp_nanos(arr.value(row) as i128)
539 .map_err(|e| ArrowError::OutOfRange(format!("Timestamp nanoseconds out of range: {e}")))
540 } else {
541 Err(type_err(
542 "TimestampSecond/Millisecond/Microsecond/NanosecondArray",
543 "DateTime/Timestamp",
544 array,
545 ))
546 }
547}
548
549#[cfg(feature = "with-time")]
550fn arrow_to_time_datetime(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
551 let odt = arrow_timestamp_to_offset_dt(array, row)?;
552 Ok(Value::TimeDateTime(Some(
553 sea_query::prelude::time::PrimitiveDateTime::new(odt.date(), odt.time()),
554 )))
555}
556
557#[cfg(feature = "with-time")]
558fn arrow_to_time_datetime_tz(array: &dyn Array, row: usize) -> Result<Value, ArrowError> {
559 let odt = arrow_timestamp_to_offset_dt(array, row)?;
560 Ok(Value::TimeDateTimeWithTimeZone(Some(odt)))
561}
562
563fn null_value_for_type(col_type: &ColumnType) -> Value {
568 match col_type {
569 ColumnType::TinyInteger => Value::TinyInt(None),
570 ColumnType::SmallInteger => Value::SmallInt(None),
571 ColumnType::Integer => Value::Int(None),
572 ColumnType::BigInteger => Value::BigInt(None),
573 ColumnType::TinyUnsigned => Value::TinyUnsigned(None),
574 ColumnType::SmallUnsigned => Value::SmallUnsigned(None),
575 ColumnType::Unsigned => Value::Unsigned(None),
576 ColumnType::BigUnsigned => Value::BigUnsigned(None),
577 ColumnType::Float => Value::Float(None),
578 ColumnType::Double => Value::Double(None),
579 ColumnType::String(_) | ColumnType::Text | ColumnType::Char(_) => Value::String(None),
580 ColumnType::Boolean => Value::Bool(None),
581 #[cfg(feature = "with-rust_decimal")]
582 ColumnType::Decimal(_) | ColumnType::Money(_) => Value::Decimal(None),
583 #[cfg(all(feature = "with-bigdecimal", not(feature = "with-rust_decimal")))]
584 ColumnType::Decimal(_) | ColumnType::Money(_) => Value::BigDecimal(None),
585 #[cfg(feature = "with-chrono")]
586 ColumnType::Date => Value::ChronoDate(None),
587 #[cfg(feature = "with-chrono")]
588 ColumnType::Time => Value::ChronoTime(None),
589 #[cfg(feature = "with-chrono")]
590 ColumnType::DateTime | ColumnType::Timestamp => Value::ChronoDateTime(None),
591 #[cfg(feature = "with-chrono")]
592 ColumnType::TimestampWithTimeZone => Value::ChronoDateTimeUtc(None),
593 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
594 ColumnType::Date => Value::TimeDate(None),
595 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
596 ColumnType::Time => Value::TimeTime(None),
597 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
598 ColumnType::DateTime | ColumnType::Timestamp => Value::TimeDateTime(None),
599 #[cfg(all(feature = "with-time", not(feature = "with-chrono")))]
600 ColumnType::TimestampWithTimeZone => Value::TimeDateTimeWithTimeZone(None),
601 _ => Value::Int(None),
602 }
603}
604
605#[cfg(all(feature = "with-chrono", feature = "with-time"))]
607fn null_value_for_type_time(col_type: &ColumnType) -> Value {
608 match col_type {
609 ColumnType::Date => Value::TimeDate(None),
610 ColumnType::Time => Value::TimeTime(None),
611 ColumnType::DateTime | ColumnType::Timestamp => Value::TimeDateTime(None),
612 ColumnType::TimestampWithTimeZone => Value::TimeDateTimeWithTimeZone(None),
613 _ => null_value_for_type(col_type),
614 }
615}
616
617pub fn values_to_arrow_array(
626 values: &[Value],
627 data_type: &arrow::datatypes::DataType,
628) -> Result<std::sync::Arc<dyn Array>, ArrowError> {
629 use arrow::datatypes::{DataType, TimeUnit};
630 use std::sync::Arc;
631
632 match data_type {
633 DataType::Int8 => {
634 let arr: Int8Array = values
635 .iter()
636 .map(|v| match v {
637 Value::TinyInt(inner) => *inner,
638 _ => None,
639 })
640 .collect();
641 Ok(Arc::new(arr))
642 }
643 DataType::Int16 => {
644 let arr: Int16Array = values
645 .iter()
646 .map(|v| match v {
647 Value::SmallInt(inner) => *inner,
648 _ => None,
649 })
650 .collect();
651 Ok(Arc::new(arr))
652 }
653 DataType::Int32 => {
654 let arr: Int32Array = values
655 .iter()
656 .map(|v| match v {
657 Value::Int(inner) => *inner,
658 _ => None,
659 })
660 .collect();
661 Ok(Arc::new(arr))
662 }
663 DataType::Int64 => {
664 let arr: Int64Array = values
665 .iter()
666 .map(|v| match v {
667 Value::BigInt(inner) => *inner,
668 _ => None,
669 })
670 .collect();
671 Ok(Arc::new(arr))
672 }
673 DataType::UInt8 => {
674 let arr: UInt8Array = values
675 .iter()
676 .map(|v| match v {
677 Value::TinyUnsigned(inner) => *inner,
678 _ => None,
679 })
680 .collect();
681 Ok(Arc::new(arr))
682 }
683 DataType::UInt16 => {
684 let arr: UInt16Array = values
685 .iter()
686 .map(|v| match v {
687 Value::SmallUnsigned(inner) => *inner,
688 _ => None,
689 })
690 .collect();
691 Ok(Arc::new(arr))
692 }
693 DataType::UInt32 => {
694 let arr: UInt32Array = values
695 .iter()
696 .map(|v| match v {
697 Value::Unsigned(inner) => *inner,
698 _ => None,
699 })
700 .collect();
701 Ok(Arc::new(arr))
702 }
703 DataType::UInt64 => {
704 let arr: UInt64Array = values
705 .iter()
706 .map(|v| match v {
707 Value::BigUnsigned(inner) => *inner,
708 _ => None,
709 })
710 .collect();
711 Ok(Arc::new(arr))
712 }
713 DataType::Float32 => {
714 let arr: Float32Array = values
715 .iter()
716 .map(|v| match v {
717 Value::Float(inner) => *inner,
718 _ => None,
719 })
720 .collect();
721 Ok(Arc::new(arr))
722 }
723 DataType::Float64 => {
724 let arr: Float64Array = values
725 .iter()
726 .map(|v| match v {
727 Value::Double(inner) => *inner,
728 _ => None,
729 })
730 .collect();
731 Ok(Arc::new(arr))
732 }
733 DataType::Boolean => {
734 let arr: BooleanArray = values
735 .iter()
736 .map(|v| match v {
737 Value::Bool(inner) => *inner,
738 _ => None,
739 })
740 .collect();
741 Ok(Arc::new(arr))
742 }
743 DataType::Utf8 => {
744 let strs: Vec<Option<&str>> = values
745 .iter()
746 .map(|v| match v {
747 Value::String(Some(s)) => Some(s.as_str()),
748 _ => None,
749 })
750 .collect();
751 Ok(Arc::new(StringArray::from(strs)))
752 }
753 DataType::LargeUtf8 => {
754 let strs: Vec<Option<&str>> = values
755 .iter()
756 .map(|v| match v {
757 Value::String(Some(s)) => Some(s.as_str()),
758 _ => None,
759 })
760 .collect();
761 Ok(Arc::new(LargeStringArray::from(strs)))
762 }
763 DataType::Binary => {
764 let bufs: Vec<Option<&[u8]>> = values
765 .iter()
766 .map(|v| match v {
767 Value::Bytes(Some(b)) => Some(b.as_slice()),
768 _ => None,
769 })
770 .collect();
771 Ok(Arc::new(BinaryArray::from(bufs)))
772 }
773 DataType::Date32 => {
774 let arr: Date32Array = values.iter().map(extract_date32).collect();
775 Ok(Arc::new(arr))
776 }
777 DataType::Time32(unit) => {
778 let vals: Vec<Option<i32>> = values.iter().map(|v| extract_time32(v, unit)).collect();
779 let arr: Arc<dyn Array> = match unit {
780 TimeUnit::Second => Arc::new(Time32SecondArray::from(vals)),
781 TimeUnit::Millisecond => Arc::new(Time32MillisecondArray::from(vals)),
782 _ => {
783 return Err(ArrowError::Unsupported(format!(
784 "Unsupported Time32 unit: {unit:?}"
785 )));
786 }
787 };
788 Ok(arr)
789 }
790 DataType::Time64(unit) => {
791 let vals: Vec<Option<i64>> = values.iter().map(|v| extract_time64(v, unit)).collect();
792 let arr: Arc<dyn Array> = match unit {
793 TimeUnit::Microsecond => Arc::new(Time64MicrosecondArray::from(vals)),
794 TimeUnit::Nanosecond => Arc::new(Time64NanosecondArray::from(vals)),
795 _ => {
796 return Err(ArrowError::Unsupported(format!(
797 "Unsupported Time64 unit: {unit:?}"
798 )));
799 }
800 };
801 Ok(arr)
802 }
803 DataType::Timestamp(unit, tz) => {
804 let vals: Vec<Option<i64>> =
805 values.iter().map(|v| extract_timestamp(v, unit)).collect();
806 let arr: Arc<dyn Array> = match unit {
807 TimeUnit::Second => {
808 let mut a = TimestampSecondArray::from(vals);
809 if let Some(tz) = tz {
810 a = a.with_timezone(tz.as_ref());
811 }
812 Arc::new(a)
813 }
814 TimeUnit::Millisecond => {
815 let mut a = TimestampMillisecondArray::from(vals);
816 if let Some(tz) = tz {
817 a = a.with_timezone(tz.as_ref());
818 }
819 Arc::new(a)
820 }
821 TimeUnit::Microsecond => {
822 let mut a = TimestampMicrosecondArray::from(vals);
823 if let Some(tz) = tz {
824 a = a.with_timezone(tz.as_ref());
825 }
826 Arc::new(a)
827 }
828 TimeUnit::Nanosecond => {
829 let mut a = TimestampNanosecondArray::from(vals);
830 if let Some(tz) = tz {
831 a = a.with_timezone(tz.as_ref());
832 }
833 Arc::new(a)
834 }
835 };
836 Ok(arr)
837 }
838 DataType::Decimal128(precision, scale) => {
839 let arr: Decimal128Array = values
840 .iter()
841 .map(|v| extract_decimal128(v, *scale))
842 .collect();
843 let arr = arr
844 .with_precision_and_scale(*precision, *scale)
845 .map_err(|e| {
846 ArrowError::Unsupported(format!("Invalid Decimal128 precision/scale: {e}"))
847 })?;
848 Ok(Arc::new(arr))
849 }
850 DataType::Decimal256(precision, scale) => {
851 let arr: Decimal256Array = values
852 .iter()
853 .map(|v| extract_decimal256(v, *scale))
854 .collect();
855 let arr = arr
856 .with_precision_and_scale(*precision, *scale)
857 .map_err(|e| {
858 ArrowError::Unsupported(format!("Invalid Decimal256 precision/scale: {e}"))
859 })?;
860 Ok(Arc::new(arr))
861 }
862 _ => Err(ArrowError::Unsupported(format!(
863 "Unsupported Arrow DataType for to_arrow: {data_type:?}"
864 ))),
865 }
866}
867
868pub fn option_values_to_arrow_array(
874 values: &[Option<Value>],
875 data_type: &arrow::datatypes::DataType,
876) -> Result<std::sync::Arc<dyn Array>, ArrowError> {
877 use arrow::datatypes::{DataType, TimeUnit};
878 use std::sync::Arc;
879
880 match data_type {
881 DataType::Int8 => {
882 let arr: Int8Array = values
883 .iter()
884 .map(|v| match v {
885 Some(Value::TinyInt(inner)) => *inner,
886 _ => None,
887 })
888 .collect();
889 Ok(Arc::new(arr))
890 }
891 DataType::Int16 => {
892 let arr: Int16Array = values
893 .iter()
894 .map(|v| match v {
895 Some(Value::SmallInt(inner)) => *inner,
896 _ => None,
897 })
898 .collect();
899 Ok(Arc::new(arr))
900 }
901 DataType::Int32 => {
902 let arr: Int32Array = values
903 .iter()
904 .map(|v| match v {
905 Some(Value::Int(inner)) => *inner,
906 _ => None,
907 })
908 .collect();
909 Ok(Arc::new(arr))
910 }
911 DataType::Int64 => {
912 let arr: Int64Array = values
913 .iter()
914 .map(|v| match v {
915 Some(Value::BigInt(inner)) => *inner,
916 _ => None,
917 })
918 .collect();
919 Ok(Arc::new(arr))
920 }
921 DataType::UInt8 => {
922 let arr: UInt8Array = values
923 .iter()
924 .map(|v| match v {
925 Some(Value::TinyUnsigned(inner)) => *inner,
926 _ => None,
927 })
928 .collect();
929 Ok(Arc::new(arr))
930 }
931 DataType::UInt16 => {
932 let arr: UInt16Array = values
933 .iter()
934 .map(|v| match v {
935 Some(Value::SmallUnsigned(inner)) => *inner,
936 _ => None,
937 })
938 .collect();
939 Ok(Arc::new(arr))
940 }
941 DataType::UInt32 => {
942 let arr: UInt32Array = values
943 .iter()
944 .map(|v| match v {
945 Some(Value::Unsigned(inner)) => *inner,
946 _ => None,
947 })
948 .collect();
949 Ok(Arc::new(arr))
950 }
951 DataType::UInt64 => {
952 let arr: UInt64Array = values
953 .iter()
954 .map(|v| match v {
955 Some(Value::BigUnsigned(inner)) => *inner,
956 _ => None,
957 })
958 .collect();
959 Ok(Arc::new(arr))
960 }
961 DataType::Float32 => {
962 let arr: Float32Array = values
963 .iter()
964 .map(|v| match v {
965 Some(Value::Float(inner)) => *inner,
966 _ => None,
967 })
968 .collect();
969 Ok(Arc::new(arr))
970 }
971 DataType::Float64 => {
972 let arr: Float64Array = values
973 .iter()
974 .map(|v| match v {
975 Some(Value::Double(inner)) => *inner,
976 _ => None,
977 })
978 .collect();
979 Ok(Arc::new(arr))
980 }
981 DataType::Boolean => {
982 let arr: BooleanArray = values
983 .iter()
984 .map(|v| match v {
985 Some(Value::Bool(inner)) => *inner,
986 _ => None,
987 })
988 .collect();
989 Ok(Arc::new(arr))
990 }
991 DataType::Utf8 => {
992 let strs: Vec<Option<&str>> = values
993 .iter()
994 .map(|v| match v {
995 Some(Value::String(Some(s))) => Some(s.as_str()),
996 _ => None,
997 })
998 .collect();
999 Ok(Arc::new(StringArray::from(strs)))
1000 }
1001 DataType::LargeUtf8 => {
1002 let strs: Vec<Option<&str>> = values
1003 .iter()
1004 .map(|v| match v {
1005 Some(Value::String(Some(s))) => Some(s.as_str()),
1006 _ => None,
1007 })
1008 .collect();
1009 Ok(Arc::new(LargeStringArray::from(strs)))
1010 }
1011 DataType::Binary => {
1012 let bufs: Vec<Option<&[u8]>> = values
1013 .iter()
1014 .map(|v| match v {
1015 Some(Value::Bytes(Some(b))) => Some(b.as_slice()),
1016 _ => None,
1017 })
1018 .collect();
1019 Ok(Arc::new(BinaryArray::from(bufs)))
1020 }
1021 DataType::Date32 => {
1022 let arr: Date32Array = values.iter().map(extract_date32_option).collect();
1023 Ok(Arc::new(arr))
1024 }
1025 DataType::Time32(unit) => {
1026 let vals: Vec<Option<i32>> = values
1027 .iter()
1028 .map(|v| extract_time32_option(v, unit))
1029 .collect();
1030 let arr: Arc<dyn Array> = match unit {
1031 TimeUnit::Second => Arc::new(Time32SecondArray::from(vals)),
1032 TimeUnit::Millisecond => Arc::new(Time32MillisecondArray::from(vals)),
1033 _ => {
1034 return Err(ArrowError::Unsupported(format!(
1035 "Unsupported Time32 unit: {unit:?}"
1036 )));
1037 }
1038 };
1039 Ok(arr)
1040 }
1041 DataType::Time64(unit) => {
1042 let vals: Vec<Option<i64>> = values
1043 .iter()
1044 .map(|v| extract_time64_option(v, unit))
1045 .collect();
1046 let arr: Arc<dyn Array> = match unit {
1047 TimeUnit::Microsecond => Arc::new(Time64MicrosecondArray::from(vals)),
1048 TimeUnit::Nanosecond => Arc::new(Time64NanosecondArray::from(vals)),
1049 _ => {
1050 return Err(ArrowError::Unsupported(format!(
1051 "Unsupported Time64 unit: {unit:?}"
1052 )));
1053 }
1054 };
1055 Ok(arr)
1056 }
1057 DataType::Timestamp(unit, tz) => {
1058 let vals: Vec<Option<i64>> = values
1059 .iter()
1060 .map(|v| extract_timestamp_option(v, unit))
1061 .collect();
1062 let arr: Arc<dyn Array> = match unit {
1063 TimeUnit::Second => {
1064 let mut a = TimestampSecondArray::from(vals);
1065 if let Some(tz) = tz {
1066 a = a.with_timezone(tz.as_ref());
1067 }
1068 Arc::new(a)
1069 }
1070 TimeUnit::Millisecond => {
1071 let mut a = TimestampMillisecondArray::from(vals);
1072 if let Some(tz) = tz {
1073 a = a.with_timezone(tz.as_ref());
1074 }
1075 Arc::new(a)
1076 }
1077 TimeUnit::Microsecond => {
1078 let mut a = TimestampMicrosecondArray::from(vals);
1079 if let Some(tz) = tz {
1080 a = a.with_timezone(tz.as_ref());
1081 }
1082 Arc::new(a)
1083 }
1084 TimeUnit::Nanosecond => {
1085 let mut a = TimestampNanosecondArray::from(vals);
1086 if let Some(tz) = tz {
1087 a = a.with_timezone(tz.as_ref());
1088 }
1089 Arc::new(a)
1090 }
1091 };
1092 Ok(arr)
1093 }
1094 DataType::Decimal128(precision, scale) => {
1095 let arr: Decimal128Array = values
1096 .iter()
1097 .map(|v| extract_decimal128_option(v, *scale))
1098 .collect();
1099 let arr = arr
1100 .with_precision_and_scale(*precision, *scale)
1101 .map_err(|e| {
1102 ArrowError::Unsupported(format!("Invalid Decimal128 precision/scale: {e}"))
1103 })?;
1104 Ok(Arc::new(arr))
1105 }
1106 DataType::Decimal256(precision, scale) => {
1107 let arr: Decimal256Array = values
1108 .iter()
1109 .map(|v| extract_decimal256_option(v, *scale))
1110 .collect();
1111 let arr = arr
1112 .with_precision_and_scale(*precision, *scale)
1113 .map_err(|e| {
1114 ArrowError::Unsupported(format!("Invalid Decimal256 precision/scale: {e}"))
1115 })?;
1116 Ok(Arc::new(arr))
1117 }
1118 _ => Err(ArrowError::Unsupported(format!(
1119 "Unsupported Arrow DataType for to_arrow: {data_type:?}"
1120 ))),
1121 }
1122}
1123
1124fn extract_date32_option(v: &Option<Value>) -> Option<i32> {
1129 extract_date32(v.as_ref()?)
1130}
1131
1132fn extract_date32(v: &Value) -> Option<i32> {
1133 #[cfg(feature = "with-chrono")]
1134 if let Value::ChronoDate(Some(d)) = v {
1135 let epoch = sea_query::prelude::chrono::NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
1136 return Some((*d - epoch).num_days() as i32);
1137 }
1138 #[cfg(feature = "with-time")]
1139 if let Value::TimeDate(Some(d)) = v {
1140 return Some(d.to_julian_day() - 2_440_588);
1141 }
1142 let _ = v;
1143 None
1144}
1145
1146fn extract_time32_option(v: &Option<Value>, unit: &arrow::datatypes::TimeUnit) -> Option<i32> {
1151 extract_time32(v.as_ref()?, unit)
1152}
1153
1154fn extract_time32(v: &Value, unit: &arrow::datatypes::TimeUnit) -> Option<i32> {
1155 #[cfg(any(feature = "with-chrono", feature = "with-time"))]
1156 use arrow::datatypes::TimeUnit;
1157
1158 #[cfg(feature = "with-chrono")]
1159 if let Value::ChronoTime(Some(t)) = v {
1160 use sea_query::prelude::chrono::Timelike;
1161 let secs = t.num_seconds_from_midnight() as i32;
1162 return match unit {
1163 TimeUnit::Second => Some(secs),
1164 TimeUnit::Millisecond => {
1165 let ms = (t.nanosecond() / 1_000_000) as i32;
1166 Some(secs * 1_000 + ms)
1167 }
1168 _ => None,
1169 };
1170 }
1171 #[cfg(feature = "with-time")]
1172 if let Value::TimeTime(Some(t)) = v {
1173 let secs = (t.hour() as i32) * 3600 + (t.minute() as i32) * 60 + (t.second() as i32);
1174 return match unit {
1175 TimeUnit::Second => Some(secs),
1176 TimeUnit::Millisecond => {
1177 let ms = (t.nanosecond() / 1_000_000) as i32;
1178 Some(secs * 1_000 + ms)
1179 }
1180 _ => None,
1181 };
1182 }
1183 let _ = (v, unit);
1184 None
1185}
1186
1187fn extract_time64_option(v: &Option<Value>, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1188 extract_time64(v.as_ref()?, unit)
1189}
1190
1191fn extract_time64(v: &Value, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1192 #[cfg(any(feature = "with-chrono", feature = "with-time"))]
1193 use arrow::datatypes::TimeUnit;
1194
1195 #[cfg(feature = "with-chrono")]
1196 if let Value::ChronoTime(Some(t)) = v {
1197 use sea_query::prelude::chrono::Timelike;
1198 let secs = t.num_seconds_from_midnight() as i64;
1199 let nanos = (t.nanosecond() % 1_000_000_000) as i64;
1200 return match unit {
1201 TimeUnit::Microsecond => Some(secs * 1_000_000 + nanos / 1_000),
1202 TimeUnit::Nanosecond => Some(secs * 1_000_000_000 + nanos),
1203 _ => None,
1204 };
1205 }
1206 #[cfg(feature = "with-time")]
1207 if let Value::TimeTime(Some(t)) = v {
1208 let secs = (t.hour() as i64) * 3600 + (t.minute() as i64) * 60 + (t.second() as i64);
1209 let nanos = t.nanosecond() as i64;
1210 return match unit {
1211 TimeUnit::Microsecond => Some(secs * 1_000_000 + nanos / 1_000),
1212 TimeUnit::Nanosecond => Some(secs * 1_000_000_000 + nanos),
1213 _ => None,
1214 };
1215 }
1216 let _ = (v, unit);
1217 None
1218}
1219
1220fn extract_timestamp_option(v: &Option<Value>, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1225 extract_timestamp(v.as_ref()?, unit)
1226}
1227
1228fn extract_timestamp(v: &Value, unit: &arrow::datatypes::TimeUnit) -> Option<i64> {
1229 #[cfg(any(feature = "with-chrono", feature = "with-time"))]
1230 use arrow::datatypes::TimeUnit;
1231
1232 #[cfg(feature = "with-chrono")]
1233 {
1234 if let Value::ChronoDateTime(Some(dt)) = v {
1235 let utc = dt.and_utc();
1236 return Some(match unit {
1237 TimeUnit::Second => utc.timestamp(),
1238 TimeUnit::Millisecond => utc.timestamp_millis(),
1239 TimeUnit::Microsecond => utc.timestamp_micros(),
1240 TimeUnit::Nanosecond => utc.timestamp_nanos_opt().unwrap_or(0),
1241 });
1242 }
1243 if let Value::ChronoDateTimeUtc(Some(dt)) = v {
1244 return Some(match unit {
1245 TimeUnit::Second => dt.timestamp(),
1246 TimeUnit::Millisecond => dt.timestamp_millis(),
1247 TimeUnit::Microsecond => dt.timestamp_micros(),
1248 TimeUnit::Nanosecond => dt.timestamp_nanos_opt().unwrap_or(0),
1249 });
1250 }
1251 }
1252 #[cfg(feature = "with-time")]
1253 {
1254 if let Value::TimeDateTime(Some(dt)) = v {
1255 let odt = dt.assume_utc();
1256 return Some(offset_dt_to_timestamp(&odt, unit));
1257 }
1258 if let Value::TimeDateTimeWithTimeZone(Some(dt)) = v {
1259 return Some(offset_dt_to_timestamp(dt, unit));
1260 }
1261 }
1262 let _ = (v, unit);
1263 None
1264}
1265
1266#[cfg(feature = "with-time")]
1267fn offset_dt_to_timestamp(
1268 dt: &sea_query::prelude::time::OffsetDateTime,
1269 unit: &arrow::datatypes::TimeUnit,
1270) -> i64 {
1271 use arrow::datatypes::TimeUnit;
1272 match unit {
1273 TimeUnit::Second => dt.unix_timestamp(),
1274 TimeUnit::Millisecond => (dt.unix_timestamp_nanos() / 1_000_000) as i64,
1275 TimeUnit::Microsecond => (dt.unix_timestamp_nanos() / 1_000) as i64,
1276 TimeUnit::Nanosecond => dt.unix_timestamp_nanos() as i64,
1277 }
1278}
1279
1280fn extract_decimal128_option(v: &Option<Value>, target_scale: i8) -> Option<i128> {
1285 extract_decimal128(v.as_ref()?, target_scale)
1286}
1287
1288fn extract_decimal128(v: &Value, target_scale: i8) -> Option<i128> {
1289 #[cfg(feature = "with-rust_decimal")]
1290 if let Value::Decimal(Some(d)) = v {
1291 let mantissa = d.mantissa();
1292 let current_scale = d.scale() as i8;
1293 let scale_diff = target_scale - current_scale;
1294 return if scale_diff >= 0 {
1295 Some(mantissa * 10i128.pow(scale_diff as u32))
1296 } else {
1297 Some(mantissa / 10i128.pow((-scale_diff) as u32))
1298 };
1299 }
1300 #[cfg(feature = "with-bigdecimal")]
1301 if let Value::BigDecimal(Some(d)) = v {
1302 return bigdecimal_to_i128(d, target_scale);
1303 }
1304 let _ = (v, target_scale);
1305 None
1306}
1307
1308#[cfg(feature = "with-bigdecimal")]
1309fn bigdecimal_to_i128(
1310 d: &sea_query::prelude::bigdecimal::BigDecimal,
1311 target_scale: i8,
1312) -> Option<i128> {
1313 use sea_query::prelude::bigdecimal::ToPrimitive;
1314
1315 let rescaled = d.clone().with_scale(target_scale as i64);
1316 let (digits, _) = rescaled.into_bigint_and_exponent();
1317 digits.to_i128()
1318}
1319
1320fn extract_decimal256_option(v: &Option<Value>, target_scale: i8) -> Option<i256> {
1321 extract_decimal256(v.as_ref()?, target_scale)
1322}
1323
1324fn extract_decimal256(v: &Value, target_scale: i8) -> Option<i256> {
1325 #[cfg(feature = "with-bigdecimal")]
1326 if let Value::BigDecimal(Some(d)) = v {
1327 return bigdecimal_to_i256(d, target_scale);
1328 }
1329 #[cfg(feature = "with-rust_decimal")]
1330 if let Value::Decimal(Some(d)) = v {
1331 let mantissa = d.mantissa();
1332 let current_scale = d.scale() as i8;
1333 let scale_diff = target_scale - current_scale;
1334 let scaled = if scale_diff >= 0 {
1335 mantissa * 10i128.pow(scale_diff as u32)
1336 } else {
1337 mantissa / 10i128.pow((-scale_diff) as u32)
1338 };
1339 return Some(i256::from_i128(scaled));
1340 }
1341 let _ = (v, target_scale);
1342 None
1343}
1344
1345#[cfg(feature = "with-bigdecimal")]
1346fn bigdecimal_to_i256(
1347 d: &sea_query::prelude::bigdecimal::BigDecimal,
1348 target_scale: i8,
1349) -> Option<i256> {
1350 let rescaled = d.clone().with_scale(target_scale as i64);
1351 let (digits, _) = rescaled.into_bigint_and_exponent();
1352 bigint_to_i256(&digits)
1353}
1354
1355#[cfg(feature = "with-bigdecimal")]
1356fn bigint_to_i256(bi: &sea_query::prelude::bigdecimal::num_bigint::BigInt) -> Option<i256> {
1357 use sea_query::prelude::bigdecimal::num_bigint::Sign;
1358
1359 let (sign, bytes) = bi.to_bytes_be();
1360 if bytes.len() > 32 {
1361 return None;
1362 }
1363
1364 let mut buf = [0u8; 32];
1365 let start = 32 - bytes.len();
1366 buf[start..].copy_from_slice(&bytes);
1367
1368 let val = i256::from_be_bytes(buf);
1369 match sign {
1370 Sign::Minus => Some(val.wrapping_neg()),
1371 _ => Some(val),
1372 }
1373}