1use crate::row::{Row, RowKind};
2use serde::ser::{SerializeStruct, Serializer};
3use std::sync::Arc;
4
5#[cfg_attr(docsrs, doc(cfg(feature = "sea-ql")))]
24#[derive(Debug, Clone, PartialEq)]
25pub struct DataRow {
26 pub column_names: Arc<[Arc<str>]>,
28 pub column_types: Arc<[clickhouse_types::DataTypeNode]>,
30 pub values: Vec<sea_query::Value>,
32}
33
34impl DataRow {
35 pub fn try_get<T, I>(&self, idx: I) -> Result<T, TypeError>
57 where
58 T: FromValue,
59 I: ColumnIndex,
60 {
61 let i = idx.get_index(self)?;
62 T::from_value(&self.values[i])
63 }
64}
65
66#[cfg_attr(docsrs, doc(cfg(feature = "sea-ql")))]
92#[derive(Debug, Clone, PartialEq)]
93pub struct RowBatch {
94 pub column_names: Arc<[Arc<str>]>,
96 pub column_types: Arc<[clickhouse_types::DataTypeNode]>,
98 pub column_data: Vec<Vec<sea_query::Value>>,
102 pub num_rows: usize,
104}
105
106#[cfg_attr(docsrs, doc(cfg(feature = "sea-ql")))]
110#[derive(Debug, thiserror::Error)]
111pub enum TypeError {
112 #[error("column '{0}' not found")]
114 ColumnNotFound(String),
115 #[error("column index {0} is out of bounds")]
117 IndexOutOfBounds(usize),
118 #[error("column value is NULL")]
120 UnexpectedNull,
121 #[error("cannot convert {got} to {expected}")]
123 TypeMismatch {
124 expected: &'static str,
125 got: &'static str,
126 },
127 #[error("value out of range for {0}")]
129 OutOfRange(&'static str),
130}
131
132mod col_index_sealed {
135 pub trait Sealed {}
136 impl Sealed for usize {}
137 impl Sealed for &str {}
138}
139
140#[cfg_attr(docsrs, doc(cfg(feature = "sea-ql")))]
142pub trait ColumnIndex: col_index_sealed::Sealed {
143 #[doc(hidden)]
144 fn get_index(&self, row: &DataRow) -> Result<usize, TypeError>;
145}
146
147impl ColumnIndex for usize {
148 fn get_index(&self, row: &DataRow) -> Result<usize, TypeError> {
149 if *self < row.values.len() {
150 Ok(*self)
151 } else {
152 Err(TypeError::IndexOutOfBounds(*self))
153 }
154 }
155}
156
157impl ColumnIndex for &str {
158 fn get_index(&self, row: &DataRow) -> Result<usize, TypeError> {
159 row.column_names
160 .iter()
161 .position(|c| c.as_ref() == *self)
162 .ok_or_else(|| TypeError::ColumnNotFound(self.to_string()))
163 }
164}
165
166#[cfg_attr(docsrs, doc(cfg(feature = "sea-ql")))]
173pub trait FromValue: Sized {
174 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError>;
176}
177
178enum NumericI128 {
182 Got(i128),
183 Overflow,
185 NotNumeric,
187}
188
189fn try_as_i128(val: &sea_query::Value) -> NumericI128 {
190 use sea_query::Value as V;
191 match val {
192 V::Bool(Some(b)) => NumericI128::Got(*b as i128),
193 V::TinyInt(Some(v)) => NumericI128::Got(*v as i128),
194 V::SmallInt(Some(v)) => NumericI128::Got(*v as i128),
195 V::Int(Some(v)) => NumericI128::Got(*v as i128),
196 V::BigInt(Some(v)) => NumericI128::Got(*v as i128),
197 V::TinyUnsigned(Some(v)) => NumericI128::Got(*v as i128),
198 V::SmallUnsigned(Some(v)) => NumericI128::Got(*v as i128),
199 V::Unsigned(Some(v)) => NumericI128::Got(*v as i128),
200 V::BigUnsigned(Some(v)) => NumericI128::Got(*v as i128),
201 V::Float(Some(f)) => f64_truncate_to_i128(*f as f64),
202 V::Double(Some(f)) => f64_truncate_to_i128(*f),
203 #[cfg(feature = "rust_decimal")]
204 V::Decimal(Some(d)) => {
205 use sea_query::prelude::rust_decimal::prelude::ToPrimitive;
206 match d.to_i128() {
207 Some(i) => NumericI128::Got(i),
208 None => NumericI128::Overflow,
209 }
210 }
211 #[cfg(feature = "bigdecimal")]
212 V::BigDecimal(Some(d)) => {
213 use sea_query::prelude::bigdecimal::ToPrimitive;
214 match d.to_i128() {
215 Some(i) => NumericI128::Got(i),
216 None => NumericI128::Overflow,
217 }
218 }
219 _ => NumericI128::NotNumeric,
220 }
221}
222
223fn try_as_f64(val: &sea_query::Value) -> Option<f64> {
224 use sea_query::Value as V;
225 match val {
226 V::Bool(Some(b)) => Some(*b as u8 as f64),
227 V::TinyInt(Some(v)) => Some(*v as f64),
228 V::SmallInt(Some(v)) => Some(*v as f64),
229 V::Int(Some(v)) => Some(*v as f64),
230 V::BigInt(Some(v)) => Some(*v as f64),
231 V::TinyUnsigned(Some(v)) => Some(*v as f64),
232 V::SmallUnsigned(Some(v)) => Some(*v as f64),
233 V::Unsigned(Some(v)) => Some(*v as f64),
234 V::BigUnsigned(Some(v)) => Some(*v as f64),
235 V::Float(Some(f)) => Some(*f as f64),
236 V::Double(Some(f)) => Some(*f),
237 #[cfg(feature = "rust_decimal")]
238 V::Decimal(Some(d)) => {
239 use sea_query::prelude::rust_decimal::prelude::ToPrimitive;
240 d.to_f64()
241 }
242 #[cfg(feature = "bigdecimal")]
243 V::BigDecimal(Some(d)) => {
244 use sea_query::prelude::bigdecimal::ToPrimitive;
245 d.to_f64()
246 }
247 _ => None,
248 }
249}
250
251fn f64_truncate_to_i128(f: f64) -> NumericI128 {
253 if !f.is_finite() {
254 return NumericI128::Overflow;
255 }
256 const MIN: f64 = i128::MIN as f64;
259 const MAX_EXCL: f64 = -(i128::MIN as f64);
260 if f >= MIN && f < MAX_EXCL {
261 NumericI128::Got(f as i128)
262 } else {
263 NumericI128::Overflow
264 }
265}
266
267fn is_null(val: &sea_query::Value) -> bool {
269 use sea_query::Value as V;
270 #[allow(unused_mut)]
271 let mut null = matches!(
272 val,
273 V::Bool(None)
274 | V::TinyInt(None)
275 | V::SmallInt(None)
276 | V::Int(None)
277 | V::BigInt(None)
278 | V::TinyUnsigned(None)
279 | V::SmallUnsigned(None)
280 | V::Unsigned(None)
281 | V::BigUnsigned(None)
282 | V::Float(None)
283 | V::Double(None)
284 | V::String(None)
285 | V::Char(None)
286 | V::Bytes(None)
287 | V::Json(None)
288 );
289 #[cfg(feature = "rust_decimal")]
290 {
291 null = null || matches!(val, V::Decimal(None));
292 }
293 #[cfg(feature = "bigdecimal")]
294 {
295 null = null || matches!(val, V::BigDecimal(None));
296 }
297 #[cfg(feature = "chrono")]
298 {
299 null = null
300 || matches!(
301 val,
302 V::ChronoDate(None) | V::ChronoDateTime(None) | V::ChronoTime(None)
303 );
304 }
305 #[cfg(feature = "time")]
306 {
307 null = null
308 || matches!(
309 val,
310 V::TimeDate(None) | V::TimeDateTime(None) | V::TimeTime(None)
311 );
312 }
313 #[cfg(feature = "uuid")]
314 {
315 null = null || matches!(val, V::Uuid(None));
316 }
317 null
318}
319
320fn value_variant_name(val: &sea_query::Value) -> &'static str {
322 use sea_query::Value as V;
323 match val {
324 V::Bool(_) => "Bool",
325 V::TinyInt(_) => "TinyInt (i8)",
326 V::SmallInt(_) => "SmallInt (i16)",
327 V::Int(_) => "Int (i32)",
328 V::BigInt(_) => "BigInt (i64)",
329 V::TinyUnsigned(_) => "TinyUnsigned (u8)",
330 V::SmallUnsigned(_) => "SmallUnsigned (u16)",
331 V::Unsigned(_) => "Unsigned (u32)",
332 V::BigUnsigned(_) => "BigUnsigned (u64)",
333 V::Float(_) => "Float (f32)",
334 V::Double(_) => "Double (f64)",
335 V::String(_) => "String",
336 V::Char(_) => "Char",
337 V::Bytes(_) => "Bytes",
338 V::Json(_) => "Json",
339 #[cfg(feature = "rust_decimal")]
340 V::Decimal(_) => "Decimal",
341 #[cfg(feature = "bigdecimal")]
342 V::BigDecimal(_) => "BigDecimal",
343 #[cfg(feature = "uuid")]
344 V::Uuid(_) => "Uuid",
345 #[cfg(feature = "chrono")]
346 V::ChronoDate(_) => "ChronoDate",
347 #[cfg(feature = "chrono")]
348 V::ChronoDateTime(_) => "ChronoDateTime",
349 #[cfg(feature = "chrono")]
350 V::ChronoTime(_) => "ChronoTime",
351 #[cfg(feature = "time")]
352 V::TimeDate(_) => "TimeDate",
353 #[cfg(feature = "time")]
354 V::TimeDateTime(_) => "TimeDateTime",
355 #[cfg(feature = "time")]
356 V::TimeTime(_) => "TimeTime",
357 #[allow(unreachable_patterns)]
358 _ => "unknown",
359 }
360}
361
362impl FromValue for bool {
365 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
366 use sea_query::Value as V;
367 if is_null(val) {
368 return Err(TypeError::UnexpectedNull);
369 }
370 match val {
371 V::Bool(Some(b)) => Ok(*b),
372 _ => match try_as_i128(val) {
373 NumericI128::Got(0) => Ok(false),
374 NumericI128::Got(1) => Ok(true),
375 NumericI128::Got(_) | NumericI128::Overflow => Err(TypeError::OutOfRange("bool")),
376 NumericI128::NotNumeric => Err(TypeError::TypeMismatch {
377 expected: "bool",
378 got: value_variant_name(val),
379 }),
380 },
381 }
382 }
383}
384
385macro_rules! impl_from_value_int {
387 ($t:ty) => {
388 impl FromValue for $t {
389 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
390 if is_null(val) {
391 return Err(TypeError::UnexpectedNull);
392 }
393 match try_as_i128(val) {
394 NumericI128::Got(n) => {
395 <$t>::try_from(n).map_err(|_| TypeError::OutOfRange(stringify!($t)))
396 }
397 NumericI128::Overflow => Err(TypeError::OutOfRange(stringify!($t))),
398 NumericI128::NotNumeric => Err(TypeError::TypeMismatch {
399 expected: stringify!($t),
400 got: value_variant_name(val),
401 }),
402 }
403 }
404 }
405 };
406}
407
408impl_from_value_int!(i8);
409impl_from_value_int!(i16);
410impl_from_value_int!(i32);
411impl_from_value_int!(i64);
412impl_from_value_int!(i128);
413impl_from_value_int!(u8);
414impl_from_value_int!(u16);
415impl_from_value_int!(u32);
416impl_from_value_int!(u64);
417impl_from_value_int!(u128);
418
419impl FromValue for f64 {
420 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
421 if is_null(val) {
422 return Err(TypeError::UnexpectedNull);
423 }
424 try_as_f64(val).ok_or_else(|| TypeError::TypeMismatch {
425 expected: "f64",
426 got: value_variant_name(val),
427 })
428 }
429}
430
431impl FromValue for f32 {
432 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
433 use sea_query::Value as V;
434 if is_null(val) {
435 return Err(TypeError::UnexpectedNull);
436 }
437 if let V::Float(Some(f)) = val {
439 return Ok(*f);
440 }
441 try_as_f64(val)
442 .map(|f| f as f32)
443 .ok_or_else(|| TypeError::TypeMismatch {
444 expected: "f32",
445 got: value_variant_name(val),
446 })
447 }
448}
449
450impl FromValue for String {
451 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
452 use sea_query::Value as V;
453 if is_null(val) {
454 return Err(TypeError::UnexpectedNull);
455 }
456 match val {
457 V::String(Some(s)) => Ok(s.clone()),
458 V::Char(Some(c)) => Ok(c.to_string()),
459 _ => Err(TypeError::TypeMismatch {
460 expected: "String",
461 got: value_variant_name(val),
462 }),
463 }
464 }
465}
466
467impl FromValue for Vec<u8> {
468 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
469 use sea_query::Value as V;
470 if is_null(val) {
471 return Err(TypeError::UnexpectedNull);
472 }
473 match val {
474 V::Bytes(Some(b)) => Ok(b.clone()),
475 _ => Err(TypeError::TypeMismatch {
476 expected: "Vec<u8>",
477 got: value_variant_name(val),
478 }),
479 }
480 }
481}
482
483#[cfg(feature = "rust_decimal")]
484impl FromValue for sea_query::value::prelude::Decimal {
485 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
486 use sea_query::Value as V;
487 use sea_query::value::prelude::Decimal;
488 if is_null(val) {
489 return Err(TypeError::UnexpectedNull);
490 }
491 match val {
492 V::Decimal(Some(d)) => return Ok(*d),
493 #[cfg(feature = "bigdecimal")]
494 V::BigDecimal(Some(bd)) => {
495 use std::str::FromStr;
496 return Decimal::from_str(&bd.to_string())
497 .map_err(|_| TypeError::OutOfRange("Decimal"));
498 }
499 V::Float(Some(f)) => {
500 return Decimal::try_from(*f as f64).map_err(|_| TypeError::OutOfRange("Decimal"));
501 }
502 V::Double(Some(f)) => {
503 return Decimal::try_from(*f).map_err(|_| TypeError::OutOfRange("Decimal"));
504 }
505 _ => {}
506 }
507 match try_as_i128(val) {
509 NumericI128::Got(n) => {
510 use std::str::FromStr;
511 Decimal::from_str(&n.to_string()).map_err(|_| TypeError::OutOfRange("Decimal"))
512 }
513 NumericI128::Overflow => Err(TypeError::OutOfRange("Decimal")),
514 NumericI128::NotNumeric => Err(TypeError::TypeMismatch {
515 expected: "Decimal",
516 got: value_variant_name(val),
517 }),
518 }
519 }
520}
521
522#[cfg(feature = "bigdecimal")]
523impl FromValue for sea_query::value::prelude::BigDecimal {
524 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
525 use sea_query::Value as V;
526 use sea_query::value::prelude::BigDecimal;
527 if is_null(val) {
528 return Err(TypeError::UnexpectedNull);
529 }
530 match val {
531 V::BigDecimal(Some(bd)) => return Ok((**bd).clone()),
532 #[cfg(feature = "rust_decimal")]
533 V::Decimal(Some(d)) => {
534 use std::str::FromStr;
535 return BigDecimal::from_str(&d.to_string())
536 .map_err(|_| TypeError::OutOfRange("BigDecimal"));
537 }
538 V::Float(Some(f)) => {
539 let f = *f as f64;
540 if !f.is_finite() {
541 return Err(TypeError::OutOfRange("BigDecimal"));
542 }
543 use std::str::FromStr;
544 return BigDecimal::from_str(&f.to_string())
545 .map_err(|_| TypeError::OutOfRange("BigDecimal"));
546 }
547 V::Double(Some(f)) => {
548 if !f.is_finite() {
549 return Err(TypeError::OutOfRange("BigDecimal"));
550 }
551 use std::str::FromStr;
552 return BigDecimal::from_str(&f.to_string())
553 .map_err(|_| TypeError::OutOfRange("BigDecimal"));
554 }
555 _ => {}
556 }
557 match try_as_i128(val) {
559 NumericI128::Got(n) => {
560 use sea_query::prelude::bigdecimal::num_bigint::BigInt;
561 Ok(BigDecimal::new(BigInt::from(n), 0))
562 }
563 NumericI128::Overflow => Err(TypeError::OutOfRange("BigDecimal")),
564 NumericI128::NotNumeric => Err(TypeError::TypeMismatch {
565 expected: "BigDecimal",
566 got: value_variant_name(val),
567 }),
568 }
569 }
570}
571
572#[cfg(feature = "uuid")]
573impl FromValue for sea_query::value::prelude::Uuid {
574 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
575 use sea_query::Value as V;
576 if is_null(val) {
577 return Err(TypeError::UnexpectedNull);
578 }
579 match val {
580 V::Uuid(Some(u)) => Ok(*u),
581 _ => Err(TypeError::TypeMismatch {
582 expected: "Uuid",
583 got: value_variant_name(val),
584 }),
585 }
586 }
587}
588
589impl FromValue for sea_query::prelude::serde_json::Value {
590 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
591 use sea_query::Value as V;
592 if is_null(val) {
593 return Err(TypeError::UnexpectedNull);
594 }
595 match val {
596 V::Json(Some(j)) => Ok((**j).clone()),
597 _ => Err(TypeError::TypeMismatch {
598 expected: "serde_json::Value",
599 got: value_variant_name(val),
600 }),
601 }
602 }
603}
604
605impl<T: FromValue> FromValue for Option<T> {
606 fn from_value(val: &sea_query::Value) -> Result<Self, TypeError> {
607 if is_null(val) {
608 return Ok(None);
609 }
610 T::from_value(val).map(Some)
611 }
612}
613
614impl Row for DataRow {
616 const NAME: &'static str = "DataRow";
617 const COLUMN_NAMES: &'static [&'static str] = &[];
620 const COLUMN_COUNT: usize = 0;
621 const KIND: RowKind = RowKind::Struct;
622 type Value<'a> = DataRow;
623}
624
625impl serde::Serialize for DataRow {
626 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
635 let mut state = serializer.serialize_struct("DataRow", self.values.len())?;
636 for val in &self.values {
637 state.serialize_field("_", &SeaValueSer(val))?;
638 }
639 state.end()
640 }
641}
642
643struct SeaValueSer<'a>(&'a sea_query::Value);
646
647impl serde::Serialize for SeaValueSer<'_> {
648 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
649 use sea_query::Value;
650 match self.0 {
651 Value::Bool(None)
653 | Value::TinyInt(None)
654 | Value::SmallInt(None)
655 | Value::Int(None)
656 | Value::BigInt(None)
657 | Value::TinyUnsigned(None)
658 | Value::SmallUnsigned(None)
659 | Value::Unsigned(None)
660 | Value::BigUnsigned(None)
661 | Value::Float(None)
662 | Value::Double(None)
663 | Value::String(None)
664 | Value::Char(None)
665 | Value::Bytes(None)
666 | Value::Json(None) => serializer.serialize_none(),
667
668 Value::Bool(Some(v)) => serializer.serialize_bool(*v),
670 Value::TinyInt(Some(v)) => serializer.serialize_i8(*v),
671 Value::SmallInt(Some(v)) => serializer.serialize_i16(*v),
672 Value::Int(Some(v)) => serializer.serialize_i32(*v),
673 Value::BigInt(Some(v)) => serializer.serialize_i64(*v),
674 Value::TinyUnsigned(Some(v)) => serializer.serialize_u8(*v),
675 Value::SmallUnsigned(Some(v)) => serializer.serialize_u16(*v),
676 Value::Unsigned(Some(v)) => serializer.serialize_u32(*v),
677 Value::BigUnsigned(Some(v)) => serializer.serialize_u64(*v),
678 Value::Float(Some(v)) => serializer.serialize_f32(*v),
679 Value::Double(Some(v)) => serializer.serialize_f64(*v),
680 Value::String(Some(s)) => serializer.serialize_str(s),
681 Value::Bytes(Some(b)) => serializer.serialize_bytes(b),
682 Value::Json(Some(j)) => {
683 let s = j.to_string();
684 serializer.serialize_str(&s)
685 }
686
687 other => Err(serde::ser::Error::custom(format!(
688 "Cannot serialize {other:?} via serde; \
689 use Client::insert_data_row for complex types (Date, UUID, Decimal, …)"
690 ))),
691 }
692 }
693}