nativeclick 0.26.9

Nativeclick is a pure Rust SDK for working with Clickhouse with the native protocol in async environments with minimal boilerplate and maximal performance.
Documentation
use std::{
    any::TypeId,
    collections::{BTreeMap, HashMap},
    hash::Hash,
};

#[cfg(feature = "bfloat16")]
use crate::values::bf16;
use indexmap::IndexMap;

use super::*;

impl FromSql for bool {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        match value {
            Value::Bool(x) => Ok(x),
            Value::UInt8(x) => Ok(x != 0),
            _ => Err(unexpected_type(type_)),
        }
    }
}

/// Also reads `Bool` columns (as 0 or 1), as before they had their own type.
impl FromSql for u8 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        match value {
            Value::UInt8(x) => Ok(x),
            Value::Bool(x) => Ok(x as u8),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for u16 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::UInt16) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::UInt16(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for u32 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::UInt32) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::UInt32(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for u64 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::UInt64) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::UInt64(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for u128 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::UInt128) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::UInt128(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

/// Also the raw value of an `Enum8`.
impl FromSql for i8 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::Int8 | Type::Enum8(_)) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::Int8(x) | Value::Enum8(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

/// Also the raw value of an `Enum16`.
impl FromSql for i16 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::Int16 | Type::Enum16(_)) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::Int16(x) | Value::Enum16(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for i32 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::Int32) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::Int32(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for i64 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::Int64) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::Int64(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for i128 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::Int128) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::Int128(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for f32 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::Float32) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::Float32(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl FromSql for f64 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::Float64) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::Float64(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

#[cfg(feature = "bfloat16")]
impl FromSql for bf16 {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        if !matches!(type_, Type::BFloat16) {
            return Err(unexpected_type(type_));
        }
        match value {
            Value::BFloat16(x) => Ok(x),
            _ => Err(unexpected_type(type_)),
        }
    }
}

/// Also the name of an `Enum8`/`Enum16` value.
impl FromSql for String {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        let enum_name = |name: Option<&String>| {
            name.cloned().ok_or_else(|| {
                NativeclickError::DeserializeError(format!("value not declared in {type_}"))
            })
        };
        match (type_, value) {
            (Type::Enum8(items), Value::Enum8(x)) => {
                enum_name(items.iter().find(|i| i.1 == x).map(|i| &i.0))
            }
            (Type::Enum16(items), Value::Enum16(x)) => {
                enum_name(items.iter().find(|i| i.1 == x).map(|i| &i.0))
            }
            (Type::String | Type::FixedString(_) | Type::Json(_), Value::String(x)) => {
                Ok(String::from_utf8(x)?)
            }
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl<T: FromSql + 'static> FromSql for Vec<T> {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        let subtype = match type_ {
            Type::Array(x) => x,
            x => return Err(unexpected_type(x)),
        }
        .strip_low_cardinality();
        match value {
            Value::String(x) if *subtype == Type::UInt8 || *subtype == Type::Int8 => {
                let type_id = TypeId::of::<T>();
                if type_id != TypeId::of::<u8>() && type_id != TypeId::of::<i8>() {
                    return Err(unexpected_type(subtype));
                }
                assert_eq!(std::mem::size_of::<T>(), 1);
                Ok(unsafe { std::mem::transmute::<Vec<u8>, Vec<T>>(x) })
            }
            Value::Array(x) => Ok(x
                .into_iter()
                .map(|x| from_sql_resolved(subtype, x))
                .collect::<Result<Vec<_>>>()?),
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl<T: FromSql + Hash + Eq, Y: FromSql> FromSql for HashMap<T, Y> {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        let (x_type, y_type) = match type_ {
            Type::Map(x_type, y_type) => (
                x_type.strip_low_cardinality(),
                y_type.strip_low_cardinality(),
            ),
            x => return Err(unexpected_type(x)),
        };
        match value {
            Value::Map(x, y) => {
                let mut out = HashMap::new();
                for (x, y) in x.into_iter().zip(y) {
                    out.insert(from_sql_resolved(x_type, x)?, from_sql_resolved(y_type, y)?);
                }
                Ok(out)
            }
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl<T: FromSql + Ord, Y: FromSql> FromSql for BTreeMap<T, Y> {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        let (x_type, y_type) = match type_ {
            Type::Map(x_type, y_type) => (
                x_type.strip_low_cardinality(),
                y_type.strip_low_cardinality(),
            ),
            x => return Err(unexpected_type(x)),
        };
        match value {
            Value::Map(x, y) => {
                let mut out = BTreeMap::new();
                for (x, y) in x.into_iter().zip(y) {
                    out.insert(from_sql_resolved(x_type, x)?, from_sql_resolved(y_type, y)?);
                }
                Ok(out)
            }
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl<T: FromSql + Hash + Eq, Y: FromSql> FromSql for IndexMap<T, Y> {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        let (x_type, y_type) = match type_ {
            Type::Map(x_type, y_type) => (
                x_type.strip_low_cardinality(),
                y_type.strip_low_cardinality(),
            ),
            x => return Err(unexpected_type(x)),
        };
        match value {
            Value::Map(x, y) => {
                let mut out = IndexMap::new();
                for (x, y) in x.into_iter().zip(y) {
                    out.insert(from_sql_resolved(x_type, x)?, from_sql_resolved(y_type, y)?);
                }
                Ok(out)
            }
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl<T: FromSql> FromSql for Option<T> {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        Self::from_sql_column(type_, value)
    }

    fn from_sql_column(type_: &Type, value: Value) -> Result<Self> {
        let subtype = type_.unnull().unwrap_or(type_);
        match value {
            Value::Null => Ok(None),
            x => Ok(Some(T::from_sql_column(subtype, x)?)),
        }
    }
}

impl<T: FromSql + Default + Copy, const N: usize> FromSql for [T; N] {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        let subtype = match type_ {
            Type::Array(x) => x.strip_low_cardinality(),
            x => return Err(unexpected_type(x)),
        };
        match value {
            Value::Array(x) => {
                if x.len() != N {
                    return Err(NativeclickError::DeserializeError(format!(
                        "invalid length for array: {} expected {}",
                        x.len(),
                        N
                    )));
                }
                let mut out = [T::default(); N];
                for (i, value) in x.into_iter().enumerate() {
                    out[i] = from_sql_resolved(subtype, value)?;
                }
                Ok(out)
            }
            _ => Err(unexpected_type(type_)),
        }
    }
}

impl<T: FromSql> FromSql for Box<T> {
    fn from_sql(type_: &Type, value: Value) -> Result<Self> {
        Ok(Box::new(T::from_sql(type_, value)?))
    }
}

macro_rules! tuple_impls {
    ($($len:expr => ($($n:tt $name:ident)+))+) => {
        $(
            impl<$($name: FromSql),+> FromSql for ($($name,)+) {
                fn from_sql(type_: &Type, value: Value) -> Result<Self> {
                    let subtype = match type_ {
                        Type::Tuple(x) => &**x,
                        x => return Err(unexpected_type(x)),
                    };
                    let values = match value {
                        Value::Tuple(n) => n,
                        _ => return Err(unexpected_type(type_)),
                    };
                    if values.len() != subtype.len() {
                        return Err(NativeclickError::DeserializeError(format!("unexpected type: mismatch tuple length expected {}, got {}", subtype.len(), values.len())));
                    }
                    if values.len() != $len {
                        return Err(NativeclickError::DeserializeError(format!("unexpected type: mismatch tuple length expected {}, got {}", $len, values.len())));
                    }
                    let mut deque = ::std::collections::VecDeque::from(values);
                    Ok((
                        $(
                            from_sql_resolved(&subtype[$n], deque.pop_front().unwrap())?,
                        )+
                    ))
                }
            }
        )+
    }
}

tuple_impls! {
    1 => (0 T0)
    2 => (0 T0 1 T1)
    3 => (0 T0 1 T1 2 T2)
    4 => (0 T0 1 T1 2 T2 3 T3)
    5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
    6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
    7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
    8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
    9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
    10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
    11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
    12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
    13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
    14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
    15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
    16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
}