spin_sdk/
pg.rs

1//! Spin 2 Postgres relational database storage. Applications that do not require
2//! Spin 2 support should use the [`pg3`](crate::pg3) module instead.
3//!
4//! Conversions between Rust, WIT and **Postgres** types.
5//!
6//! # Types
7//!
8//! | Rust type  | WIT (db-value)      | Postgres type(s)             |
9//! |------------|---------------------|----------------------------- |
10//! | `bool`     | boolean(bool)       | BOOL                         |
11//! | `i16`      | int16(s16)          | SMALLINT, SMALLSERIAL, INT2  |
12//! | `i32`      | int32(s32)          | INT, SERIAL, INT4            |
13//! | `i64`      | int64(s64)          | BIGINT, BIGSERIAL, INT8      |
14//! | `f32`      | floating32(float32) | REAL, FLOAT4                 |
15//! | `f64`      | floating64(float64) | DOUBLE PRECISION, FLOAT8     |
16//! | `String`   | str(string)         | VARCHAR, CHAR(N), TEXT       |
17//! | `Vec<u8>`  | binary(list\<u8\>)  | BYTEA                        |
18
19#[doc(inline)]
20pub use super::wit::v2::postgres::{Connection, Error as PgError};
21#[doc(inline)]
22pub use super::wit::v2::rdbms_types::*;
23
24/// A pg error
25#[derive(Debug, thiserror::Error)]
26pub enum Error {
27    /// Failed to deserialize [`DbValue`]
28    #[error("error value decoding: {0}")]
29    Decode(String),
30    /// Pg query failed with an error
31    #[error(transparent)]
32    PgError(#[from] PgError),
33}
34
35/// A type that can be decoded from the database.
36pub trait Decode: Sized {
37    /// Decode a new value of this type using a [`DbValue`].
38    fn decode(value: &DbValue) -> Result<Self, Error>;
39}
40
41impl<T> Decode for Option<T>
42where
43    T: Decode,
44{
45    fn decode(value: &DbValue) -> Result<Self, Error> {
46        match value {
47            DbValue::DbNull => Ok(None),
48            v => Ok(Some(T::decode(v)?)),
49        }
50    }
51}
52
53impl Decode for bool {
54    fn decode(value: &DbValue) -> Result<Self, Error> {
55        match value {
56            DbValue::Boolean(boolean) => Ok(*boolean),
57            _ => Err(Error::Decode(format_decode_err("BOOL", value))),
58        }
59    }
60}
61
62impl Decode for i16 {
63    fn decode(value: &DbValue) -> Result<Self, Error> {
64        match value {
65            DbValue::Int16(n) => Ok(*n),
66            _ => Err(Error::Decode(format_decode_err("SMALLINT", value))),
67        }
68    }
69}
70
71impl Decode for i32 {
72    fn decode(value: &DbValue) -> Result<Self, Error> {
73        match value {
74            DbValue::Int32(n) => Ok(*n),
75            _ => Err(Error::Decode(format_decode_err("INT", value))),
76        }
77    }
78}
79
80impl Decode for i64 {
81    fn decode(value: &DbValue) -> Result<Self, Error> {
82        match value {
83            DbValue::Int64(n) => Ok(*n),
84            _ => Err(Error::Decode(format_decode_err("BIGINT", value))),
85        }
86    }
87}
88
89impl Decode for f32 {
90    fn decode(value: &DbValue) -> Result<Self, Error> {
91        match value {
92            DbValue::Floating32(n) => Ok(*n),
93            _ => Err(Error::Decode(format_decode_err("REAL", value))),
94        }
95    }
96}
97
98impl Decode for f64 {
99    fn decode(value: &DbValue) -> Result<Self, Error> {
100        match value {
101            DbValue::Floating64(n) => Ok(*n),
102            _ => Err(Error::Decode(format_decode_err("DOUBLE PRECISION", value))),
103        }
104    }
105}
106
107impl Decode for Vec<u8> {
108    fn decode(value: &DbValue) -> Result<Self, Error> {
109        match value {
110            DbValue::Binary(n) => Ok(n.to_owned()),
111            _ => Err(Error::Decode(format_decode_err("BYTEA", value))),
112        }
113    }
114}
115
116impl Decode for String {
117    fn decode(value: &DbValue) -> Result<Self, Error> {
118        match value {
119            DbValue::Str(s) => Ok(s.to_owned()),
120            _ => Err(Error::Decode(format_decode_err(
121                "CHAR, VARCHAR, TEXT",
122                value,
123            ))),
124        }
125    }
126}
127
128fn format_decode_err(types: &str, value: &DbValue) -> String {
129    format!("Expected {} from the DB but got {:?}", types, value)
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn boolean() {
138        assert!(bool::decode(&DbValue::Boolean(true)).unwrap());
139        assert!(bool::decode(&DbValue::Int32(0)).is_err());
140        assert!(Option::<bool>::decode(&DbValue::DbNull).unwrap().is_none());
141    }
142
143    #[test]
144    fn int16() {
145        assert_eq!(i16::decode(&DbValue::Int16(0)).unwrap(), 0);
146        assert!(i16::decode(&DbValue::Int32(0)).is_err());
147        assert!(Option::<i16>::decode(&DbValue::DbNull).unwrap().is_none());
148    }
149
150    #[test]
151    fn int32() {
152        assert_eq!(i32::decode(&DbValue::Int32(0)).unwrap(), 0);
153        assert!(i32::decode(&DbValue::Boolean(false)).is_err());
154        assert!(Option::<i32>::decode(&DbValue::DbNull).unwrap().is_none());
155    }
156
157    #[test]
158    fn int64() {
159        assert_eq!(i64::decode(&DbValue::Int64(0)).unwrap(), 0);
160        assert!(i64::decode(&DbValue::Boolean(false)).is_err());
161        assert!(Option::<i64>::decode(&DbValue::DbNull).unwrap().is_none());
162    }
163
164    #[test]
165    fn floating32() {
166        assert!(f32::decode(&DbValue::Floating32(0.0)).is_ok());
167        assert!(f32::decode(&DbValue::Boolean(false)).is_err());
168        assert!(Option::<f32>::decode(&DbValue::DbNull).unwrap().is_none());
169    }
170
171    #[test]
172    fn floating64() {
173        assert!(f64::decode(&DbValue::Floating64(0.0)).is_ok());
174        assert!(f64::decode(&DbValue::Boolean(false)).is_err());
175        assert!(Option::<f64>::decode(&DbValue::DbNull).unwrap().is_none());
176    }
177
178    #[test]
179    fn str() {
180        assert_eq!(
181            String::decode(&DbValue::Str(String::from("foo"))).unwrap(),
182            String::from("foo")
183        );
184
185        assert!(String::decode(&DbValue::Int32(0)).is_err());
186        assert!(Option::<String>::decode(&DbValue::DbNull)
187            .unwrap()
188            .is_none());
189    }
190
191    #[test]
192    fn binary() {
193        assert!(Vec::<u8>::decode(&DbValue::Binary(vec![0, 0])).is_ok());
194        assert!(Vec::<u8>::decode(&DbValue::Boolean(false)).is_err());
195        assert!(Option::<Vec<u8>>::decode(&DbValue::DbNull)
196            .unwrap()
197            .is_none());
198    }
199}