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