1use crate::util::extract_value;
2use bytes::BytesMut;
3use postgres_types::{FromSql, IsNull, ToSql, Type, to_sql_checked};
4use rust_decimal::{Decimal, prelude::FromPrimitive};
5use std::error::Error;
6use tank_core::Value;
7
8#[derive(Debug, Default, Clone)]
9pub(crate) struct ValueWrap(pub(crate) Value);
10
11impl From<Value> for ValueWrap {
12 fn from(value: Value) -> Self {
13 ValueWrap(value)
14 }
15}
16
17impl<'a> FromSql<'a> for ValueWrap {
18 fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
19 Self::from_sql_nullable(ty, Some(raw))
20 }
21 fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
22 Self::from_sql_nullable(ty, None)
23 }
24 fn from_sql_nullable(
25 ty: &Type,
26 raw: Option<&'a [u8]>,
27 ) -> Result<Self, Box<dyn Error + Sync + Send>> {
28 extract_value(ty, raw).map(Into::into)
29 }
30
31 fn accepts(_ty: &Type) -> bool {
32 true
33 }
34}
35
36impl ToSql for ValueWrap {
37 fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>>
38 where
39 Self: Sized,
40 {
41 match &self.0 {
42 Value::Null => None::<String>.to_sql(ty, out),
43 Value::Boolean(v) => v.to_sql(ty, out),
44 Value::Int8(v) => v.to_sql(ty, out),
45 Value::Int16(v) => v.to_sql(ty, out),
46 Value::Int32(v) => v.to_sql(ty, out),
47 Value::Int64(v) => v.to_sql(ty, out),
48 Value::Int128(v) => v.map(|v| Decimal::from_i128(v)).to_sql(ty, out),
49 Value::UInt8(v) => v.map(|v| v as i16).to_sql(ty, out),
50 Value::UInt16(v) => v.map(|v| v as i32).to_sql(ty, out),
51 Value::UInt32(v) => v.to_sql(ty, out),
52 Value::UInt64(v) => v.map(|v| Decimal::from_u64(v)).to_sql(ty, out),
53 Value::UInt128(v) => v.map(|v| Decimal::from_u128(v)).to_sql(ty, out),
54 Value::Float32(v) => v.to_sql(ty, out),
55 Value::Float64(v) => v.to_sql(ty, out),
56 Value::Decimal(v, _, _) => v.to_sql(ty, out),
57 Value::Char(v) => v.map(|v| v.to_string()).to_sql(ty, out),
58 Value::Varchar(v) => v.to_sql(ty, out),
59 Value::Blob(v) => v.as_deref().to_sql(ty, out),
60 Value::Date(v) => v.to_sql(ty, out),
61 Value::Time(v) => v.to_sql(ty, out),
62 Value::Timestamp(v) => v.to_sql(ty, out),
63 Value::TimestampWithTimezone(v) => v.to_sql(ty, out),
64 Value::Uuid(v) => v.to_sql(ty, out),
65 Value::Array(v, ..) => v
66 .as_ref()
67 .map(|v| v.clone().into_iter().map(ValueWrap).collect::<Vec<_>>())
68 .to_sql(ty, out),
69 Value::List(v, ..) => v
70 .as_ref()
71 .map(|v| v.clone().into_iter().map(ValueWrap).collect::<Vec<_>>())
72 .to_sql(ty, out),
73 _ => {
74 return Err(tank_core::Error::msg(format!(
75 "tank::Value variant `{:?}` is not supported by Postgres",
76 &self.0
77 ))
78 .into());
79 }
80 }
81 }
82
83 fn accepts(_ty: &Type) -> bool
84 where
85 Self: Sized,
86 {
87 true
88 }
89
90 to_sql_checked!();
91}
92pub fn postgres_type_to_value(ty: &Type) -> Value {
93 match *ty {
94 Type::BOOL => Value::Boolean(None),
95 Type::CHAR => Value::Int8(None),
96 Type::INT2 => Value::Int16(None),
97 Type::INT4 => Value::Int32(None),
98 Type::INT8 => Value::Int64(None),
99 Type::FLOAT4 => Value::Float32(None),
100 Type::FLOAT8 => Value::Float64(None),
101 Type::NUMERIC => Value::Decimal(None, 0, 0),
102 Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::JSON | Type::XML => Value::Varchar(None),
103 Type::BYTEA => Value::Blob(None),
104 Type::DATE => Value::Date(None),
105 Type::TIME => Value::Time(None),
106 Type::TIMESTAMP => Value::Timestamp(None),
107 Type::TIMESTAMPTZ => Value::TimestampWithTimezone(None),
108 Type::INTERVAL => Value::Interval(None),
109 Type::UUID => Value::Uuid(None),
110 Type::BOOL_ARRAY => Value::List(None, Box::new(Value::Boolean(None))),
111 Type::INT2_ARRAY => Value::List(None, Box::new(Value::Int16(None))),
112 Type::INT4_ARRAY => Value::List(None, Box::new(Value::Int32(None))),
113 Type::INT8_ARRAY => Value::List(None, Box::new(Value::Int64(None))),
114 Type::FLOAT4_ARRAY => Value::List(None, Box::new(Value::Float32(None))),
115 Type::FLOAT8_ARRAY => Value::List(None, Box::new(Value::Float64(None))),
116 Type::NUMERIC_ARRAY => Value::List(None, Box::new(Value::Decimal(None, 0, 0))),
117 Type::TEXT_ARRAY | Type::VARCHAR_ARRAY => Value::List(None, Box::new(Value::Varchar(None))),
118 Type::BYTEA_ARRAY => Value::List(None, Box::new(Value::Blob(None))),
119 Type::DATE_ARRAY => Value::List(None, Box::new(Value::Date(None))),
120 Type::TIME_ARRAY => Value::List(None, Box::new(Value::Time(None))),
121 Type::TIMESTAMP_ARRAY => Value::List(None, Box::new(Value::Timestamp(None))),
122 Type::TIMESTAMPTZ_ARRAY => Value::List(None, Box::new(Value::TimestampWithTimezone(None))),
123 Type::INTERVAL_ARRAY => Value::List(None, Box::new(Value::Interval(None))),
124 Type::UUID_ARRAY => Value::List(None, Box::new(Value::Uuid(None))),
125 _ => Value::Null,
126 }
127}
128
129pub(crate) struct VecWrap<T>(pub Vec<T>);
130
131impl<'a, T: FromSql<'a>> FromSql<'a> for VecWrap<T> {
132 fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
133 Vec::<T>::from_sql_null(ty).map(VecWrap)
134 }
135 fn from_sql_nullable(
136 ty: &Type,
137 raw: Option<&'a [u8]>,
138 ) -> Result<Self, Box<dyn Error + Sync + Send>> {
139 Vec::<T>::from_sql_nullable(ty, raw).map(VecWrap)
140 }
141 fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
142 Vec::<T>::from_sql(ty, raw).map(VecWrap)
143 }
144 fn accepts(ty: &Type) -> bool {
145 Vec::<T>::accepts(ty)
146 }
147}
148
149impl From<VecWrap<ValueWrap>> for Vec<Value> {
150 fn from(value: VecWrap<ValueWrap>) -> Self {
151 value.0.into_iter().map(|v| v.0).collect()
152 }
153}