tank_postgres/
value_wrap.rs

1use crate::IntervalWrap;
2use bytes::BytesMut;
3use postgres_types::{FromSql, IsNull, ToSql, Type, to_sql_checked};
4use rust_decimal::{Decimal, prelude::FromPrimitive};
5use std::{error::Error, io::Read};
6use tank_core::Value;
7use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
8use uuid::Uuid;
9
10#[derive(Debug)]
11pub(crate) struct ValueWrap(pub(crate) Value);
12
13impl From<Value> for ValueWrap {
14    fn from(value: Value) -> Self {
15        ValueWrap(value)
16    }
17}
18
19impl<'a> FromSql<'a> for ValueWrap {
20    fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
21        Self::from_sql_nullable(ty, Some(raw))
22    }
23    fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
24        Self::from_sql_nullable(ty, None)
25    }
26    fn from_sql_nullable(
27        ty: &Type,
28        raw: Option<&'a [u8]>,
29    ) -> Result<Self, Box<dyn Error + Sync + Send>> {
30        macro_rules! to_value {
31            ($ty_var:ident, $raw:ident, $($($ty:path)|+ => ( $value:path, $source:ty $(, $additional:expr)* ) ,)+) => {
32                match *$ty_var {
33                    $($($ty)|+ => $value(if let Some($raw) = $raw { Some(<$source>::from_sql($ty_var, $raw)?.into()) } else { None } $(, $additional)*),)+
34                    _ => {
35                        if let Some(mut raw) = $raw {
36                            let mut buf = String::new();
37                            let _ = raw.read_to_string(&mut buf);
38                            return Err(tank_core::Error::msg(format!("Cannot decode sql type: `{}`, value: `{}`", $ty_var, buf)).into());
39                        }
40                        Value::Null
41                    }
42                }
43            };
44        }
45        let value = to_value!(ty, raw,
46            Type::BOOL => (Value::Boolean, bool),
47            Type::CHAR => (Value::Int8, i8),
48            Type::INT2 => (Value::Int16, i16),
49            Type::INT4 => (Value::Int32, i32),
50            Type::INT8 => (Value::Int64, i64),
51            Type::FLOAT4 => (Value::Float32, f32),
52            Type::FLOAT8 => (Value::Float64, f64),
53            Type::NUMERIC => (Value::Decimal, Decimal, 0, 0),
54            Type::OID => (Value::UInt32, u32),
55            Type::VARCHAR
56            | Type::TEXT
57            | Type::NAME
58            | Type::BPCHAR
59            | Type::JSON
60            | Type::XML => (Value::Varchar, String),
61            Type::BYTEA => (Value::Blob, Vec<u8>),
62            Type::DATE => (Value::Date, Date),
63            Type::TIME => (Value::Time, Time),
64            Type::TIMESTAMP => (Value::Timestamp, PrimitiveDateTime),
65            Type::TIMESTAMPTZ => (Value::TimestampWithTimezone, OffsetDateTime),
66            Type::INTERVAL =>(Value::Interval, IntervalWrap),
67            Type::UUID => (Value::Uuid, Uuid),
68            Type::INT2_ARRAY => (Value::List, VecWrap<ValueWrap>, Box::new(Value::Int16(None))),
69            Type::INT4_ARRAY => (Value::List, VecWrap<ValueWrap>, Box::new(Value::Int32(None))),
70            Type::INT8_ARRAY => (Value::List, VecWrap<ValueWrap>, Box::new(Value::Int64(None))),
71            Type::FLOAT4_ARRAY => (Value::List, VecWrap<ValueWrap>, Box::new(Value::Float32(None))),
72            Type::FLOAT8_ARRAY => (Value::List, VecWrap<ValueWrap>, Box::new(Value::Float64(None))),
73            Type::BPCHAR_ARRAY => (Value::List, VecWrap<ValueWrap>, Box::new(Value::Varchar(None))),
74            Type::UNKNOWN => (Value::Unknown, String),
75        );
76        Ok(value.into())
77    }
78
79    fn accepts(_ty: &Type) -> bool {
80        true
81    }
82}
83
84impl ToSql for ValueWrap {
85    fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>>
86    where
87        Self: Sized,
88    {
89        match &self.0 {
90            Value::Null => None::<String>.to_sql(ty, out),
91            Value::Boolean(v) => v.to_sql(ty, out),
92            Value::Int8(v) => v.to_sql(ty, out),
93            Value::Int16(v) => v.to_sql(ty, out),
94            Value::Int32(v) => v.to_sql(ty, out),
95            Value::Int64(v) => v.to_sql(ty, out),
96            Value::Int128(v) => v.map(|v| Decimal::from_i128(v)).to_sql(ty, out),
97            Value::UInt8(v) => v.map(|v| v as i16).to_sql(ty, out),
98            Value::UInt16(v) => v.map(|v| v as i32).to_sql(ty, out),
99            Value::UInt32(v) => v.to_sql(ty, out),
100            Value::UInt64(v) => v.map(|v| Decimal::from_u64(v)).to_sql(ty, out),
101            Value::UInt128(v) => v.map(|v| Decimal::from_u128(v)).to_sql(ty, out),
102            Value::Float32(v) => v.to_sql(ty, out),
103            Value::Float64(v) => v.to_sql(ty, out),
104            Value::Decimal(v, _, _) => v.to_sql(ty, out),
105            Value::Char(v) => v.map(|v| v.to_string()).to_sql(ty, out),
106            Value::Varchar(v) => v.to_sql(ty, out),
107            Value::Blob(v) => v.as_deref().to_sql(ty, out),
108            Value::Date(v) => v.to_sql(ty, out),
109            Value::Time(v) => v.to_sql(ty, out),
110            Value::Timestamp(v) => v.to_sql(ty, out),
111            Value::TimestampWithTimezone(v) => v.to_sql(ty, out),
112            Value::Uuid(v) => v.to_sql(ty, out),
113            Value::Array(v, ..) => v
114                .as_ref()
115                .map(|v| v.clone().into_iter().map(ValueWrap).collect::<Vec<_>>())
116                .to_sql(ty, out),
117            Value::List(v, ..) => v
118                .as_ref()
119                .map(|v| v.clone().into_iter().map(ValueWrap).collect::<Vec<_>>())
120                .to_sql(ty, out),
121            _ => {
122                return Err(tank_core::Error::msg(format!(
123                    "tank::Value variant `{:?}` is not supported by Postgres",
124                    &self.0
125                ))
126                .into());
127            }
128        }
129    }
130
131    fn accepts(_ty: &Type) -> bool
132    where
133        Self: Sized,
134    {
135        true
136    }
137
138    to_sql_checked!();
139}
140pub fn postgres_type_to_value(ty: &Type) -> Value {
141    match *ty {
142        Type::BOOL => Value::Boolean(None),
143        Type::CHAR => Value::Int8(None),
144        Type::INT2 => Value::Int16(None),
145        Type::INT4 => Value::Int32(None),
146        Type::INT8 => Value::Int64(None),
147        Type::FLOAT4 => Value::Float32(None),
148        Type::FLOAT8 => Value::Float64(None),
149        Type::NUMERIC => Value::Decimal(None, 0, 0),
150        Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::JSON | Type::XML => Value::Varchar(None),
151        Type::BYTEA => Value::Blob(None),
152        Type::DATE => Value::Date(None),
153        Type::TIME => Value::Time(None),
154        Type::TIMESTAMP => Value::Timestamp(None),
155        Type::TIMESTAMPTZ => Value::TimestampWithTimezone(None),
156        Type::INTERVAL => Value::Interval(None),
157        Type::UUID => Value::Uuid(None),
158        Type::BOOL_ARRAY => Value::List(None, Box::new(Value::Boolean(None))),
159        Type::INT2_ARRAY => Value::List(None, Box::new(Value::Int16(None))),
160        Type::INT4_ARRAY => Value::List(None, Box::new(Value::Int32(None))),
161        Type::INT8_ARRAY => Value::List(None, Box::new(Value::Int64(None))),
162        Type::FLOAT4_ARRAY => Value::List(None, Box::new(Value::Float32(None))),
163        Type::FLOAT8_ARRAY => Value::List(None, Box::new(Value::Float64(None))),
164        Type::NUMERIC_ARRAY => Value::List(None, Box::new(Value::Decimal(None, 0, 0))),
165        Type::TEXT_ARRAY | Type::VARCHAR_ARRAY => Value::List(None, Box::new(Value::Varchar(None))),
166        Type::BYTEA_ARRAY => Value::List(None, Box::new(Value::Blob(None))),
167        Type::DATE_ARRAY => Value::List(None, Box::new(Value::Date(None))),
168        Type::TIME_ARRAY => Value::List(None, Box::new(Value::Time(None))),
169        Type::TIMESTAMP_ARRAY => Value::List(None, Box::new(Value::Timestamp(None))),
170        Type::TIMESTAMPTZ_ARRAY => Value::List(None, Box::new(Value::TimestampWithTimezone(None))),
171        Type::INTERVAL_ARRAY => Value::List(None, Box::new(Value::Interval(None))),
172        Type::UUID_ARRAY => Value::List(None, Box::new(Value::Uuid(None))),
173        _ => Value::Null,
174    }
175}
176
177struct VecWrap<T>(pub Vec<T>);
178
179impl<'a, T: FromSql<'a>> FromSql<'a> for VecWrap<T> {
180    fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
181        Vec::<T>::from_sql_null(ty).map(VecWrap)
182    }
183    fn from_sql_nullable(
184        ty: &Type,
185        raw: Option<&'a [u8]>,
186    ) -> Result<Self, Box<dyn Error + Sync + Send>> {
187        Vec::<T>::from_sql_nullable(ty, raw).map(VecWrap)
188    }
189    fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
190        Vec::<T>::from_sql(ty, raw).map(VecWrap)
191    }
192    fn accepts(ty: &Type) -> bool {
193        Vec::<T>::accepts(ty)
194    }
195}
196
197impl From<VecWrap<ValueWrap>> for Vec<Value> {
198    fn from(value: VecWrap<ValueWrap>) -> Self {
199        value.0.into_iter().map(|v| v.0).collect()
200    }
201}