use std::{borrow::Cow, fmt, hash::Hash};
use chrono::{NaiveDate, SecondsFormat};
use chrono_tz::Tz;
use crate::{
Result,
convert::{FromSql, ToSql, unexpected_type},
types::Type,
};
mod bfloat16;
mod bytes;
mod clickhouse_uuid;
mod date;
#[cfg(feature = "rust_decimal")]
mod decimal;
mod dynamic;
mod fixed_point;
mod geo;
mod int256;
mod ip;
pub use bfloat16::*;
pub use bytes::*;
pub use date::*;
pub use dynamic::*;
pub use fixed_point::*;
pub use geo::*;
pub use int256::*;
pub use ip::*;
#[cfg(test)]
mod tests;
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum Value {
Int8(i8),
Int16(i16),
Int32(i32),
Int64(i64),
Int128(i128),
Int256(i256),
UInt8(u8),
UInt16(u16),
UInt32(u32),
UInt64(u64),
UInt128(u128),
UInt256(u256),
Float32(f32),
Float64(f64),
BFloat16(bf16),
Decimal32(usize, i32),
Decimal64(usize, i64),
Decimal128(usize, i128),
Decimal256(usize, i256),
String(Vec<u8>),
Uuid(::uuid::Uuid),
Date(Date),
DateTime(DateTime),
DateTime64(DynDateTime64),
Enum8(i8),
Enum16(i16),
Array(Vec<Value>),
Tuple(Vec<Value>),
Null,
Map(Vec<Value>, Vec<Value>),
Ipv4(Ipv4),
Ipv6(Ipv6),
Point(Point),
Ring(Ring),
Polygon(Polygon),
MultiPolygon(MultiPolygon),
Bool(bool),
Date32(Date32),
Time(i32),
Time64(usize, i64),
Dynamic(Box<DynamicValue>),
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Int8(l0), Self::Int8(r0)) => l0 == r0,
(Self::Int16(l0), Self::Int16(r0)) => l0 == r0,
(Self::Int32(l0), Self::Int32(r0)) => l0 == r0,
(Self::Int64(l0), Self::Int64(r0)) => l0 == r0,
(Self::Int128(l0), Self::Int128(r0)) => l0 == r0,
(Self::Int256(l0), Self::Int256(r0)) => l0 == r0,
(Self::UInt8(l0), Self::UInt8(r0)) => l0 == r0,
(Self::UInt16(l0), Self::UInt16(r0)) => l0 == r0,
(Self::UInt32(l0), Self::UInt32(r0)) => l0 == r0,
(Self::UInt64(l0), Self::UInt64(r0)) => l0 == r0,
(Self::UInt128(l0), Self::UInt128(r0)) => l0 == r0,
(Self::UInt256(l0), Self::UInt256(r0)) => l0 == r0,
(Self::Float32(l0), Self::Float32(r0)) => l0.to_bits() == r0.to_bits(),
(Self::Float64(l0), Self::Float64(r0)) => l0.to_bits() == r0.to_bits(),
(Self::BFloat16(l0), Self::BFloat16(r0)) => l0.to_bits() == r0.to_bits(),
(Self::Decimal32(l0, l1), Self::Decimal32(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Decimal64(l0, l1), Self::Decimal64(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Decimal128(l0, l1), Self::Decimal128(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Decimal256(l0, l1), Self::Decimal256(r0, r1)) => l0 == r0 && l1 == r1,
(Self::String(l0), Self::String(r0)) => l0 == r0,
(Self::Uuid(l0), Self::Uuid(r0)) => l0 == r0,
(Self::Date(l0), Self::Date(r0)) => l0 == r0,
(Self::DateTime(l0), Self::DateTime(r0)) => l0 == r0,
(Self::DateTime64(l0), Self::DateTime64(r0)) => l0 == r0,
(Self::Enum8(l0), Self::Enum8(r0)) => l0 == r0,
(Self::Enum16(l0), Self::Enum16(r0)) => l0 == r0,
(Self::Array(l0), Self::Array(r0)) => l0 == r0,
(Self::Tuple(l0), Self::Tuple(r0)) => l0 == r0,
(Self::Map(l0, l1), Self::Map(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Ipv4(l0), Self::Ipv4(r0)) => l0 == r0,
(Self::Ipv6(l0), Self::Ipv6(r0)) => l0 == r0,
(Self::Point(l0), Self::Point(r0)) => l0 == r0,
(Self::Ring(l0), Self::Ring(r0)) => l0 == r0,
(Self::Polygon(l0), Self::Polygon(r0)) => l0 == r0,
(Self::MultiPolygon(l0), Self::MultiPolygon(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Date32(l0), Self::Date32(r0)) => l0 == r0,
(Self::Time(l0), Self::Time(r0)) => l0 == r0,
(Self::Time64(l0, l1), Self::Time64(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Dynamic(l0), Self::Dynamic(r0)) => l0 == r0,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl Hash for Value {
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
Hash::hash(&core::mem::discriminant(self), state);
match self {
Value::Int8(x) => ::core::hash::Hash::hash(x, state),
Value::Int16(x) => ::core::hash::Hash::hash(x, state),
Value::Int32(x) => ::core::hash::Hash::hash(x, state),
Value::Int64(x) => ::core::hash::Hash::hash(x, state),
Value::Int128(x) => ::core::hash::Hash::hash(x, state),
Value::Int256(x) => ::core::hash::Hash::hash(x, state),
Value::UInt8(x) => ::core::hash::Hash::hash(x, state),
Value::UInt16(x) => ::core::hash::Hash::hash(x, state),
Value::UInt32(x) => ::core::hash::Hash::hash(x, state),
Value::UInt64(x) => ::core::hash::Hash::hash(x, state),
Value::UInt128(x) => ::core::hash::Hash::hash(x, state),
Value::UInt256(x) => ::core::hash::Hash::hash(x, state),
Value::Float32(x) => ::core::hash::Hash::hash(&x.to_bits(), state),
Value::Float64(x) => ::core::hash::Hash::hash(&x.to_bits(), state),
Value::BFloat16(x) => hash_bf16(x, state),
Value::Decimal32(x, __self_1) => {
::core::hash::Hash::hash(x, state);
::core::hash::Hash::hash(__self_1, state)
}
Value::Decimal64(x, __self_1) => {
::core::hash::Hash::hash(x, state);
::core::hash::Hash::hash(__self_1, state)
}
Value::Decimal128(x, __self_1) => {
::core::hash::Hash::hash(x, state);
::core::hash::Hash::hash(__self_1, state)
}
Value::Decimal256(x, __self_1) => {
::core::hash::Hash::hash(x, state);
::core::hash::Hash::hash(__self_1, state)
}
Value::String(x) => ::core::hash::Hash::hash(x, state),
Value::Uuid(x) => ::core::hash::Hash::hash(x, state),
Value::Date(x) => ::core::hash::Hash::hash(x, state),
Value::DateTime(x) => ::core::hash::Hash::hash(x, state),
Value::DateTime64(x) => {
::core::hash::Hash::hash(x, state);
}
Value::Enum8(x) => ::core::hash::Hash::hash(x, state),
Value::Enum16(x) => ::core::hash::Hash::hash(x, state),
Value::Array(x) => ::core::hash::Hash::hash(x, state),
Value::Tuple(x) => ::core::hash::Hash::hash(x, state),
Value::Map(x, __self_1) => {
::core::hash::Hash::hash(x, state);
::core::hash::Hash::hash(__self_1, state)
}
Value::Ipv4(x) => ::core::hash::Hash::hash(x, state),
Value::Ipv6(x) => ::core::hash::Hash::hash(x, state),
Value::Point(x) => ::core::hash::Hash::hash(x, state),
Value::Ring(x) => ::core::hash::Hash::hash(x, state),
Value::Polygon(x) => ::core::hash::Hash::hash(x, state),
Value::MultiPolygon(x) => ::core::hash::Hash::hash(x, state),
Value::Bool(x) => ::core::hash::Hash::hash(x, state),
Value::Date32(x) => ::core::hash::Hash::hash(x, state),
Value::Time(x) => ::core::hash::Hash::hash(x, state),
Value::Time64(x, y) => {
::core::hash::Hash::hash(x, state);
::core::hash::Hash::hash(y, state)
}
Value::Dynamic(x) => ::core::hash::Hash::hash(x, state),
_ => {}
}
}
}
impl Eq for Value {}
impl Value {
pub fn string(value: impl Into<String>) -> Self {
Value::String(value.into().into_bytes())
}
pub(crate) fn index_value(&self) -> usize {
match self {
Value::UInt8(x) => *x as usize,
Value::UInt16(x) => *x as usize,
Value::UInt32(x) => *x as usize,
Value::UInt64(x) => *x as usize,
_ => usize::MAX,
}
}
pub fn unwrap_array_ref(&self) -> &[Value] {
match self {
Value::Array(a) => &a[..],
_ => unimplemented!(),
}
}
pub fn unwrap_array(self) -> Vec<Value> {
match self {
Value::Array(a) => a,
_ => unimplemented!(),
}
}
pub fn unwrap_tuple(self) -> Vec<Value> {
match self {
Value::Tuple(a) => a,
_ => unimplemented!(),
}
}
pub fn unarray(self) -> Option<Vec<Value>> {
match self {
Value::Array(a) => Some(a),
_ => None,
}
}
pub(crate) fn justify_null_ref<'a>(&'a self, type_: &Type) -> Cow<'a, Value> {
if self == &Value::Null {
Cow::Owned(type_.default_value())
} else {
Cow::Borrowed(self)
}
}
pub fn to_value<T: FromSql>(self, type_: &Type) -> Result<T> {
crate::from_sql_resolved(type_, self)
}
pub fn from_value<T: ToSql>(value: T) -> Result<Self> {
value.to_sql(None)
}
pub fn guess_type(&self) -> Type {
match self {
Value::Int8(_) => Type::Int8,
Value::Int16(_) => Type::Int16,
Value::Int32(_) => Type::Int32,
Value::Int64(_) => Type::Int64,
Value::Int128(_) => Type::Int128,
Value::Int256(_) => Type::Int256,
Value::UInt8(_) => Type::UInt8,
Value::UInt16(_) => Type::UInt16,
Value::UInt32(_) => Type::UInt32,
Value::UInt64(_) => Type::UInt64,
Value::UInt128(_) => Type::UInt128,
Value::UInt256(_) => Type::UInt256,
Value::Float32(_) => Type::Float32,
Value::Float64(_) => Type::Float64,
Value::BFloat16(_) => Type::BFloat16,
Value::Decimal32(p, _) => Type::Decimal32(*p),
Value::Decimal64(p, _) => Type::Decimal64(*p),
Value::Decimal128(p, _) => Type::Decimal128(*p),
Value::Decimal256(p, _) => Type::Decimal256(*p),
Value::String(_) => Type::String,
Value::Uuid(_) => Type::Uuid,
Value::Date(_) => Type::Date,
Value::DateTime(time) => Type::DateTime(time.0),
Value::DateTime64(x) => Type::DateTime64(x.2, x.0),
Value::Enum8(x) => Type::Enum8(vec![(x.to_string(), *x)]),
Value::Enum16(x) => Type::Enum16(vec![(x.to_string(), *x)]),
Value::Array(x) => Type::Array(Box::new(
x.first().map(|x| x.guess_type()).unwrap_or(Type::String),
)),
Value::Tuple(values) => Type::Tuple(values.iter().map(|x| x.guess_type()).collect()),
Value::Null => Type::Nullable(Box::new(Type::String)),
Value::Map(k, v) => Type::Map(
Box::new(k.first().map(|x| x.guess_type()).unwrap_or(Type::String)),
Box::new(v.first().map(|x| x.guess_type()).unwrap_or(Type::String)),
),
Value::Ipv4(_) => Type::Ipv4,
Value::Ipv6(_) => Type::Ipv6,
Value::Point(_) => Type::Point,
Value::Ring(_) => Type::Ring,
Value::Polygon(_) => Type::Polygon,
Value::MultiPolygon(_) => Type::MultiPolygon,
Value::Bool(_) => Type::Bool,
Value::Date32(_) => Type::Date32,
Value::Time(_) => Type::Time,
Value::Time64(precision, _) => Type::Time64(*precision),
Value::Dynamic(x) => x.type_.clone(),
}
}
}
fn write_decimal(f: &mut fmt::Formatter<'_>, digits: &str, scale: usize) -> fmt::Result {
let (sign, digits) = match digits.strip_prefix('-') {
Some(digits) => ("-", digits),
None => ("", digits),
};
if scale == 0 {
return write!(f, "{sign}{digits}");
}
let digits = format!("{digits:0>width$}", width = scale + 1);
let (int, fraction) = digits.split_at(digits.len() - scale);
write!(f, "{sign}{int}.{fraction}")
}
fn escape_string(f: &mut fmt::Formatter<'_>, from: impl AsRef<[u8]>) -> fmt::Result {
let from = from.as_ref();
for byte in from.iter().copied() {
if byte < 128 {
match byte {
b'\\' => write!(f, "\\\\")?,
b'\'' => write!(f, "\\'")?,
0x08 => write!(f, "\\b")?,
0x0C => write!(f, "\\f")?,
b'\r' => write!(f, "\\r")?,
b'\n' => write!(f, "\\n")?,
b'\t' => write!(f, "\\t")?,
b'\0' => write!(f, "\\0")?,
0x07 => write!(f, "\\a")?,
0x0B => write!(f, "\\v")?,
_ => write!(f, "{}", Into::<char>::into(byte))?,
}
} else {
write!(f, "\\x{byte:02X}")?;
}
}
Ok(())
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Self as fmt::Display>::fmt(self, f)
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Int8(x) => write!(f, "{x}"),
Value::Int16(x) => write!(f, "{x}"),
Value::Int32(x) => write!(f, "{x}"),
Value::Int64(x) => write!(f, "{x}"),
Value::Int128(x) => write!(f, "{x}::Int128"),
Value::Int256(x) => write!(f, "{x}::Int256"),
Value::UInt8(x) => write!(f, "{x}"),
Value::UInt16(x) => write!(f, "{x}"),
Value::UInt32(x) => write!(f, "{x}"),
Value::UInt64(x) => write!(f, "{x}"),
Value::UInt128(x) => write!(f, "{x}::UInt128"),
Value::UInt256(x) => write!(f, "{x}::UInt256"),
Value::Float32(x) => write!(f, "{x}"),
Value::Float64(x) => write!(f, "{x}"),
Value::BFloat16(x) => write!(f, "{x}"),
Value::Decimal32(scale, value) => write_decimal(f, &value.to_string(), *scale),
Value::Decimal64(scale, value) => write_decimal(f, &value.to_string(), *scale),
Value::Decimal128(scale, value) => write_decimal(f, &value.to_string(), *scale),
Value::Decimal256(scale, value) => write_decimal(f, &value.to_decimal(), *scale),
Value::String(string) => {
write!(f, "'")?;
escape_string(f, string)?;
write!(f, "'")
}
Value::Uuid(uuid) => {
write!(f, "toUUID('{uuid}')")
}
Value::Date(date) => {
let chrono_date: NaiveDate = (*date).into();
write!(f, "{}", chrono_date.format("makeDate(%Y,%m,%d)"))
}
Value::DateTime(datetime) => {
let chrono_date: chrono::DateTime<Tz> =
(*datetime).try_into().map_err(|_| fmt::Error)?;
let string = chrono_date.to_rfc3339_opts(SecondsFormat::AutoSi, true);
write!(f, "parseDateTimeBestEffort('")?;
escape_string(f, &string)?;
write!(f, "')")
}
Value::DateTime64(datetime) => {
let chrono_date: chrono::DateTime<Tz> =
FromSql::from_sql(&Type::DateTime64(datetime.2, datetime.0), self.clone())
.map_err(|_| fmt::Error)?;
let string = chrono_date.to_rfc3339_opts(SecondsFormat::AutoSi, true);
write!(f, "parseDateTime64BestEffort('")?;
escape_string(f, &string)?;
write!(f, "', {})", datetime.2)
}
Value::Enum8(x) => write!(f, "{x}"),
Value::Enum16(x) => write!(f, "{x}"),
Value::Array(array) => {
write!(f, "[")?;
if let Some(item) = array.first() {
write!(f, "{item}")?;
}
for item in array.iter().skip(1) {
write!(f, ",{item}")?;
}
write!(f, "]")
}
Value::Tuple(tuple) => {
write!(f, "(")?;
if let Some(item) = tuple.first() {
write!(f, "{item}")?;
}
for item in tuple.iter().skip(1) {
write!(f, ",{item}")?;
}
write!(f, ")")
}
Value::Null => write!(f, "NULL"),
Value::Map(keys, values) => {
assert_eq!(keys.len(), values.len());
write!(f, "{{")?;
let mut iter = keys.iter().zip(values.iter());
if let Some((key, value)) = iter.next() {
write!(f, "{key}:{value}")?;
}
for (key, value) in iter {
write!(f, ",{key}:{value}")?;
}
write!(f, "}}")
}
Value::Ipv4(ipv4) => write!(f, "'{ipv4}'"),
Value::Ipv6(ipv6) => write!(f, "'{ipv6}'"),
Value::Point(x) => write!(f, "{x:?}"),
Value::Ring(x) => write!(f, "{x:?}"),
Value::Polygon(x) => write!(f, "{x:?}"),
Value::MultiPolygon(x) => write!(f, "{x:?}"),
Value::Bool(x) => write!(f, "{x}"),
Value::Date32(date) => {
let chrono_date: NaiveDate = (*date).try_into().map_err(|_| fmt::Error)?;
write!(f, "{}", chrono_date.format("toDate32('%Y-%m-%d')"))
}
Value::Time(x) => write!(f, "toTime({x})"),
Value::Time64(precision, x) => {
let scale = 10i64.pow(*precision as u32);
let sign = if *x < 0 { "-" } else { "" };
let (seconds, fraction) = (
x.unsigned_abs() / scale as u64,
x.unsigned_abs() % scale as u64,
);
write!(
f,
"toTime64('{sign}{seconds}.{fraction:0width$}', {precision})",
width = *precision
)
}
Value::Dynamic(x) => write!(f, "CAST({}, '{}')", x.value, x.type_),
}
}
}