Skip to main content

tank_postgres/
value_wrap.rs

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}