1use crate::{
2 interval_wrap::IntervalWrap,
3 util::{extract_value, flatten_array},
4};
5use bytes::BytesMut;
6use postgres_protocol::types::array_to_sql;
7use postgres_types::{FromSql, IsNull, ToSql, Type, to_sql_checked};
8use rust_decimal::{Decimal, prelude::FromPrimitive};
9use std::{borrow::Cow, error::Error};
10use tank_core::Value;
11
12#[derive(Debug, Clone)]
13pub(crate) struct ValueWrap<'a>(pub(crate) Cow<'a, Value>);
14
15impl<'a> ValueWrap<'a> {
16 pub fn take_value(self) -> Value {
17 match self.0 {
18 Cow::Borrowed(v) => v.clone(),
19 Cow::Owned(v) => v,
20 }
21 }
22}
23
24impl<'a> Default for ValueWrap<'a> {
25 fn default() -> Self {
26 Self(Cow::Borrowed(&Value::Null))
27 }
28}
29
30impl<'a> From<&'a Value> for ValueWrap<'a> {
31 fn from(value: &'a Value) -> Self {
32 ValueWrap(Cow::Borrowed(value))
33 }
34}
35
36impl<'a> From<Value> for ValueWrap<'a> {
37 fn from(value: Value) -> Self {
38 ValueWrap(Cow::Owned(value))
39 }
40}
41
42impl<'a> FromSql<'a> for ValueWrap<'a> {
43 fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
44 Self::from_sql_nullable(ty, Some(raw))
45 }
46 fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
47 Self::from_sql_nullable(ty, None)
48 }
49 fn from_sql_nullable(
50 ty: &Type,
51 raw: Option<&'a [u8]>,
52 ) -> Result<Self, Box<dyn Error + Sync + Send>> {
53 extract_value(ty, raw).map(Into::into)
54 }
55
56 fn accepts(_ty: &Type) -> bool {
57 true
58 }
59}
60
61impl<'a> ToSql for ValueWrap<'a> {
62 fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>>
63 where
64 Self: Sized,
65 {
66 match &self.0.as_ref() {
67 Value::Null => None::<String>.to_sql(ty, out),
68 Value::Boolean(v) => v.to_sql(ty, out),
69 Value::Int8(v) => v.map(|v| v as i16).to_sql(ty, out),
70 Value::Int16(v) => v.to_sql(ty, out),
71 Value::Int32(v) => v.to_sql(ty, out),
72 Value::Int64(v) => v.to_sql(ty, out),
73 Value::Int128(v) => v.map(|v| Decimal::from_i128(v)).to_sql(ty, out),
74 Value::UInt8(v) => v.map(|v| v as i16).to_sql(ty, out),
75 Value::UInt16(v) => v.map(|v| v as i32).to_sql(ty, out),
76 Value::UInt32(v) => v.map(|v| v as i64).to_sql(ty, out),
77 Value::UInt64(v) => v.map(|v| Decimal::from_u64(v)).to_sql(ty, out),
78 Value::UInt128(v) => v.map(|v| Decimal::from_u128(v)).to_sql(ty, out),
79 Value::Float32(v) => v.to_sql(ty, out),
80 Value::Float64(v) => v.to_sql(ty, out),
81 Value::Decimal(v, _, _) => v.to_sql(ty, out),
82 Value::Char(v) => v.map(|v| v.to_string()).to_sql(ty, out),
83 Value::Varchar(v) => v.to_sql(ty, out),
84 Value::Blob(v) => v.as_deref().to_sql(ty, out),
85 Value::Date(v) => v.to_sql(ty, out),
86 Value::Time(v) => v.to_sql(ty, out),
87 Value::Timestamp(v) => v.to_sql(ty, out),
88 Value::TimestampWithTimezone(v) => v.to_sql(ty, out),
89 Value::Interval(v) => v.map(IntervalWrap).to_sql(ty, out),
90 Value::Uuid(v) => v.to_sql(ty, out),
91 Value::Array(v, element, ..) => match v {
92 Some(v) => {
93 let (vector, dimensions, element_type) = flatten_array(&**v, element);
94 array_to_sql(
95 dimensions,
96 element_type.oid(),
97 vector.into_iter().map(|v| ValueWrap(Cow::Borrowed(v))),
98 |e, w| match e.to_sql(&element_type, w)? {
99 IsNull::No => Ok(postgres_protocol::IsNull::No),
100 IsNull::Yes => Ok(postgres_protocol::IsNull::Yes),
101 },
102 out,
103 )?;
104 Ok(IsNull::No)
105 }
106 None => None::<Vec<ValueWrap>>.to_sql(ty, out),
107 },
108 Value::List(v, element) => match v {
109 Some(v) => {
110 let (vector, dimensions, element_type) = flatten_array(v.as_slice(), element);
111 array_to_sql(
112 dimensions,
113 element_type.oid(),
114 vector.into_iter().map(|v| ValueWrap(Cow::Borrowed(v))),
115 |e, w| match e.to_sql(&element_type, w)? {
116 IsNull::No => Ok(postgres_protocol::IsNull::No),
117 IsNull::Yes => Ok(postgres_protocol::IsNull::Yes),
118 },
119 out,
120 )?;
121 Ok(IsNull::No)
122 }
123 None => None::<Vec<ValueWrap>>.to_sql(ty, out),
124 },
125 _ => {
126 return Err(tank_core::Error::msg(format!(
127 "tank::Value variant `{:?}` is not supported by Postgres",
128 &self.0
129 ))
130 .into());
131 }
132 }
133 }
134
135 fn accepts(_ty: &Type) -> bool
136 where
137 Self: Sized,
138 {
139 true
140 }
141
142 to_sql_checked!();
143}