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