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}