Skip to main content

spin_sdk/
pg.rs

1//! Postgres relational database storage.
2//!
3//! You can use the [`into()`](std::convert::Into) method to convert
4//! a Rust value into a [`ParameterValue`]. You can use the
5//! [`Decode`] trait to convert a [`DbValue`] to a suitable Rust type.
6//! The following table shows available conversions.
7//!
8//! # Types
9//!
10//! | Rust type               | WIT (db-value)                                | Postgres type(s)             |
11//! |-------------------------|-----------------------------------------------|----------------------------- |
12//! | `bool`                  | boolean(bool)                                 | BOOL                         |
13//! | `i16`                   | int16(s16)                                    | SMALLINT, SMALLSERIAL, INT2  |
14//! | `i32`                   | int32(s32)                                    | INT, SERIAL, INT4            |
15//! | `i64`                   | int64(s64)                                    | BIGINT, BIGSERIAL, INT8      |
16//! | `f32`                   | floating32(float32)                           | REAL, FLOAT4                 |
17//! | `f64`                   | floating64(float64)                           | DOUBLE PRECISION, FLOAT8     |
18//! | `String`                | str(string)                                   | VARCHAR, CHAR(N), TEXT       |
19//! | `Vec<u8>`               | binary(list\<u8\>)                            | BYTEA                        |
20//! | `chrono::NaiveDate`     | date(tuple<s32, u8, u8>)                      | DATE                         |
21//! | `chrono::NaiveTime`     | time(tuple<u8, u8, u8, u32>)                  | TIME                         |
22//! | `chrono::NaiveDateTime` | datetime(tuple<s32, u8, u8, u8, u8, u8, u32>) | TIMESTAMP                    |
23//! | `chrono::Duration`      | timestamp(s64)                                | BIGINT                       |
24//! | `uuid::Uuid`            | uuid(string)                                  | UUID                         |
25//! | `serde_json::Value`     | jsonb(list\<u8\>)                             | JSONB                        |
26//! | `serde::De/Serialize`   | jsonb(list\<u8\>)                             | JSONB                        |
27//! | `rust_decimal::Decimal` | decimal(string)                               | NUMERIC                      |
28//! | `postgres_range`        | range-int32(...), range-int64(...)            | INT4RANGE, INT8RANGE         |
29//! | lower/upper tuple       | range-decimal(...)                            | NUMERICRANGE                 |
30//! | `Vec<Option<...>>`      | array-int32(...), array-int64(...), array-str(...), array-decimal(...) | INT4[], INT8[], TEXT[], NUMERIC[] |
31//! | `pg4::Interval`         | interval(interval)                            | INTERVAL                     |
32
33// pg4 errors can be large, because they now include a breakdown of the PostgreSQL
34// error fields instead of just a string
35#![allow(clippy::result_large_err)]
36
37use crate::wit_bindgen;
38use std::sync::Arc;
39
40#[doc(hidden)]
41/// Module containing wit bindgen generated code.
42///
43/// This is only meant for internal consumption.
44pub mod wit {
45    #![allow(missing_docs)]
46    use crate::wit_bindgen;
47
48    wit_bindgen::generate!({
49        runtime_path: "crate::wit_bindgen::rt",
50        world: "spin-sdk-pg",
51        path: "wit",
52        generate_all,
53    });
54
55    pub use spin::postgres::postgres;
56}
57
58#[doc(inline)]
59pub use wit::postgres::{
60    Column, DbDataType, DbError, DbValue, Error as PgError, ParameterValue, QueryError,
61    RangeBoundKind,
62};
63
64/// The PostgreSQL INTERVAL data type.
65pub use wit::postgres::Interval;
66
67use chrono::{Datelike, Timelike};
68
69/// An open connection to a PostgreSQL database.
70///
71/// # Examples
72///
73/// Load a set of rows from a local PostgreSQL database, and iterate over them.
74///
75/// ```no_run
76/// use spin_sdk::pg::{Connection, Decode};
77///
78/// # async fn run() -> anyhow::Result<()> {
79/// # let min_age = 0;
80/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb").await?;
81///
82/// let mut query_result = db.query(
83///     "SELECT * FROM users WHERE age >= $1",
84///     &[min_age.into()]
85/// ).await?;
86///
87/// while let Some(row) = query_result.next().await {
88///     let name = row.get::<String>("name").unwrap();
89///     println!("Found user {name}");
90/// }
91///
92/// query_result.result().await?;
93/// # Ok(())
94/// # }
95/// ```
96///
97/// Perform an aggregate (scalar) operation over a table. The result set
98/// contains a single column, with a single row.
99///
100/// ```no_run
101/// use spin_sdk::pg::{Connection, Decode};
102///
103/// # async fn run() -> anyhow::Result<()> {
104/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb").await?;
105///
106/// let query_result = db.query("SELECT COUNT(*) FROM users", &[]).await?;
107///
108/// assert_eq!(1, query_result.columns().len());
109/// assert_eq!("count", query_result.columns()[0].name);
110///
111/// let rows = query_result.collect().await?;
112///
113/// assert_eq!(1, rows.len());
114///
115/// let count = &rows[0][0];
116/// # Ok(())
117/// # }
118/// ```
119///
120/// Delete rows from a PostgreSQL table. This uses [Connection::execute()]
121/// instead of the `query` method.
122///
123/// ```no_run
124/// use spin_sdk::pg::Connection;
125///
126/// # async fn run() -> anyhow::Result<()> {
127/// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb").await?;
128///
129/// let rows_affected = db.execute(
130///     "DELETE FROM users WHERE name = $1",
131///     &["Baldrick".to_owned().into()]
132/// ).await?;
133/// # Ok(())
134/// # }
135/// ```
136pub struct Connection(wit::postgres::Connection);
137
138/// Options for opening a [`Connection`].
139#[derive(Default)]
140pub struct OpenOptions {
141    /// A certificate for the root certificate authority to use in TLS
142    /// for the connection.
143    pub ca_root: Option<Certificate>,
144}
145
146/// A TLS certificate. This is a text document (starting with `-----BEGIN CERTIFICATE-----`).
147pub enum Certificate {
148    /// The certificate is a file mounted in the guest at the given path.
149    FilePath(String),
150    /// The certificate text is the given string.
151    Text(String),
152}
153
154impl Certificate {
155    fn load(self) -> Result<String, Error> {
156        match self {
157            Certificate::FilePath(path) => std::fs::read_to_string(path)
158                .map_err(|e| Error::PgError(PgError::Other(e.to_string()))),
159            Certificate::Text(text) => Ok(text),
160        }
161    }
162}
163
164impl Connection {
165    /// Open a connection to a PostgreSQL database.
166    ///
167    /// The address may be in connection string form (`"host=... dbname=..."`)
168    /// or in URL form (`"postgres://<host>/<dbname>?..."`).
169    ///
170    /// This constructor does not support options such as custom CA roots.
171    /// See [`Connection::open_with_options`] for a more flexible constructor.
172    pub async fn open(address: impl Into<String>) -> Result<Self, Error> {
173        let inner = wit::postgres::Connection::open_async(address.into()).await?;
174        Ok(Self(inner))
175    }
176
177    /// Open a connection to a PostgreSQL database.
178    ///
179    /// The address may be in connection string form (`"host=... dbname=..."`)
180    /// or in URL form (`"postgres://<host>/<dbname>?..."`).
181    ///
182    /// The `options` parameter allows for passing options not available in the address string.
183    pub async fn open_with_options(
184        address: impl AsRef<str>,
185        options: OpenOptions,
186    ) -> Result<Self, Error> {
187        let builder = wit::postgres::ConnectionBuilder::new(address.as_ref());
188        let OpenOptions { ca_root } = options;
189
190        if let Some(ca_root) = ca_root {
191            let ca_root_text = ca_root.load()?;
192            builder.set_ca_root(&ca_root_text)?;
193        }
194
195        let inner = builder.build_async().await?;
196        Ok(Self(inner))
197    }
198
199    /// Query the database.
200    ///
201    /// Use this function for queries that return rows (typically `SELECT` queries).
202    /// For side-effectful queries, see [`Connection::execute`].
203    pub async fn query(
204        &self,
205        statement: impl Into<String>,
206        params: impl Into<Vec<ParameterValue>>,
207    ) -> Result<QueryResult, Error> {
208        let (columns, rows, result) = self.0.query_async(statement.into(), params.into()).await?;
209        Ok(QueryResult {
210            columns: Arc::new(columns),
211            rows,
212            result,
213        })
214    }
215
216    /// Execute a command against the database.
217    ///
218    /// Use this function for side-effectful queries (such as `INSERT` or `DELETE` queries).
219    /// For queries that return row data, see [`Connection::query`].
220    pub async fn execute(
221        &self,
222        statement: impl Into<String>,
223        params: impl Into<Vec<ParameterValue>>,
224    ) -> Result<u64, Error> {
225        self.0
226            .execute_async(statement.into(), params.into())
227            .await
228            .map_err(Error::PgError)
229    }
230
231    /// Extracts the underlying Wasm Component Model resource for the connection.
232    pub fn into_inner(self) -> wit::postgres::Connection {
233        self.0
234    }
235}
236
237/// The result of a [`Connection::query`] operation.
238pub struct QueryResult {
239    columns: Arc<Vec<Column>>,
240    rows: wit_bindgen::StreamReader<Vec<DbValue>>,
241    result: wit_bindgen::FutureReader<Result<(), PgError>>,
242}
243
244impl QueryResult {
245    /// The columns in the query result.
246    pub fn columns(&self) -> &[Column] {
247        &self.columns
248    }
249
250    // TODO: should this return Result<Option<Row>> so users
251    // could write `q.next().await?` instead of checking `result`
252    // separately???
253
254    /// Gets the next row in the result set.
255    ///
256    /// If this is `None`, there are no more rows available. You _must_
257    /// await [`QueryResult::result()`] to determine if all rows
258    /// were read successfully.
259    pub async fn next(&mut self) -> Option<Row> {
260        self.rows.next().await.map(|r| Row {
261            columns: self.columns.clone(),
262            result: r,
263        })
264    }
265
266    /// Whether the query completed successfully or with an error.
267    pub async fn result(self) -> Result<(), Error> {
268        self.result.await.map_err(Error::PgError)
269    }
270
271    /// Collect all rows in the result set.
272    ///
273    /// This is provided for when the result set is small enough to fit in
274    /// memory and you do not require streaming behaviour.
275    pub async fn collect(mut self) -> Result<Vec<Row>, Error> {
276        let mut rows = vec![];
277        while let Some(row) = self.next().await {
278            rows.push(row);
279        }
280        self.result.await.map_err(Error::PgError)?;
281        Ok(rows)
282    }
283
284    /// An asynchronous reader for the rows of the query result. Call
285    /// `.next().await` to iterate over the rows. When this returns `None`,
286    /// you have read all available rows. At this point you _must_ check
287    /// [`QueryResult::result()`] to determine if the read completed
288    /// successfully.
289    ///
290    /// This provides each row as a plain vector of database values.
291    /// [`QueryResult::next()`] provides a more ergonomic wrapper.
292    ///
293    /// To collect all rows into a vector, see [`QueryResult::collect`].
294    pub fn rows(&mut self) -> &mut wit_bindgen::StreamReader<Vec<DbValue>> {
295        &mut self.rows
296    }
297
298    /// Extracts the underlying Wasm Component Model results of the query.
299    #[allow(
300        clippy::type_complexity,
301        reason = "sorry clippy that's just what the inner bits are"
302    )]
303    pub fn into_inner(
304        self,
305    ) -> (
306        Vec<Column>,
307        wit_bindgen::StreamReader<Vec<DbValue>>,
308        wit_bindgen::FutureReader<Result<(), PgError>>,
309    ) {
310        ((*self.columns).clone(), self.rows, self.result)
311    }
312}
313
314/// A database row result.
315///
316/// There are two representations of a PostgreSQL row in the SDK.  This type is useful for
317/// addressing elements by column name, and is obtained from the [QueryResult::next()] function.
318/// The [DbValue] vector representation is obtained from the [QueryResult::rows()] function, and provides
319/// index-based lookup or low-level access to row values via a vector.
320pub struct Row {
321    columns: Arc<Vec<wit::postgres::Column>>,
322    result: Vec<DbValue>,
323}
324
325impl Row {
326    /// Get a value by its column name. The value is converted to the target type as per the
327    /// conversion table shown in the module documentation.
328    ///
329    /// This function returns None for both no such column _and_ failed conversion. You should use
330    /// it only if you do not need to address errors (that is, if you know that conversion should
331    /// never fail). If your code does not know the type in advance, use the raw [QueryResult::rows()] function
332    /// instead of the [`QueryResult::next()`] or [`QueryResult::collect()`] wrappers to access
333    /// the underlying [DbValue] enum: this will allow you to
334    /// determine the type and process it accordingly.
335    ///
336    /// Additionally, this function performs a name lookup each time it is called. If you are iterating
337    /// over a large number of rows, it's more efficient to use column indexes, either calculated or
338    /// statically known from the column order in the SQL.
339    ///
340    /// # Examples
341    ///
342    /// ```no_run
343    /// use spin_sdk::pg::{Connection, DbValue};
344    ///
345    /// # async fn run() -> anyhow::Result<()> {
346    /// # let user_id = 0;
347    /// let db = Connection::open("host=localhost user=postgres password=my_password dbname=mydb").await?;
348    /// let mut query_result = db.query(
349    ///     "SELECT * FROM users WHERE id = $1",
350    ///     &[user_id.into()]
351    /// ).await?;
352    /// let user_row = query_result.next().await.unwrap();
353    ///
354    /// let name = user_row.get::<String>("name").unwrap();
355    /// let age = user_row.get::<i16>("age").unwrap();
356    /// # Ok(())
357    /// # }
358    /// ```
359    pub fn get<T: Decode>(&self, column: &str) -> Option<T> {
360        let i = self.columns.iter().position(|c| c.name == column)?;
361        let db_value = self.result.get(i)?;
362        Decode::decode(db_value).ok()
363    }
364}
365
366impl std::ops::Index<usize> for Row {
367    type Output = DbValue;
368
369    fn index(&self, index: usize) -> &Self::Output {
370        &self.result[index]
371    }
372}
373
374/// A Postgres error
375#[derive(Debug, thiserror::Error)]
376pub enum Error {
377    /// Failed to deserialize [`DbValue`]
378    #[error("error value decoding: {0}")]
379    Decode(String),
380    /// Postgres query failed with an error
381    #[error(transparent)]
382    PgError(#[from] PgError),
383}
384
385/// A type that can be decoded from the database.
386pub trait Decode: Sized {
387    /// Decode a new value of this type using a [`DbValue`].
388    fn decode(value: &DbValue) -> Result<Self, Error>;
389}
390
391impl<T> Decode for Option<T>
392where
393    T: Decode,
394{
395    fn decode(value: &DbValue) -> Result<Self, Error> {
396        match value {
397            DbValue::DbNull => Ok(None),
398            v => Ok(Some(T::decode(v)?)),
399        }
400    }
401}
402
403impl Decode for bool {
404    fn decode(value: &DbValue) -> Result<Self, Error> {
405        match value {
406            DbValue::Boolean(boolean) => Ok(*boolean),
407            _ => Err(Error::Decode(format_decode_err("BOOL", value))),
408        }
409    }
410}
411
412impl Decode for i16 {
413    fn decode(value: &DbValue) -> Result<Self, Error> {
414        match value {
415            DbValue::Int16(n) => Ok(*n),
416            _ => Err(Error::Decode(format_decode_err("SMALLINT", value))),
417        }
418    }
419}
420
421impl Decode for i32 {
422    fn decode(value: &DbValue) -> Result<Self, Error> {
423        match value {
424            DbValue::Int32(n) => Ok(*n),
425            _ => Err(Error::Decode(format_decode_err("INT", value))),
426        }
427    }
428}
429
430impl Decode for i64 {
431    fn decode(value: &DbValue) -> Result<Self, Error> {
432        match value {
433            DbValue::Int64(n) => Ok(*n),
434            _ => Err(Error::Decode(format_decode_err("BIGINT", value))),
435        }
436    }
437}
438
439impl Decode for f32 {
440    fn decode(value: &DbValue) -> Result<Self, Error> {
441        match value {
442            DbValue::Floating32(n) => Ok(*n),
443            _ => Err(Error::Decode(format_decode_err("REAL", value))),
444        }
445    }
446}
447
448impl Decode for f64 {
449    fn decode(value: &DbValue) -> Result<Self, Error> {
450        match value {
451            DbValue::Floating64(n) => Ok(*n),
452            _ => Err(Error::Decode(format_decode_err("DOUBLE PRECISION", value))),
453        }
454    }
455}
456
457impl Decode for Vec<u8> {
458    fn decode(value: &DbValue) -> Result<Self, Error> {
459        match value {
460            DbValue::Binary(n) => Ok(n.to_owned()),
461            _ => Err(Error::Decode(format_decode_err("BYTEA", value))),
462        }
463    }
464}
465
466impl Decode for String {
467    fn decode(value: &DbValue) -> Result<Self, Error> {
468        match value {
469            DbValue::Str(s) => Ok(s.to_owned()),
470            _ => Err(Error::Decode(format_decode_err(
471                "CHAR, VARCHAR, TEXT",
472                value,
473            ))),
474        }
475    }
476}
477
478impl Decode for chrono::NaiveDate {
479    fn decode(value: &DbValue) -> Result<Self, Error> {
480        match value {
481            DbValue::Date((year, month, day)) => {
482                let naive_date =
483                    chrono::NaiveDate::from_ymd_opt(*year, (*month).into(), (*day).into())
484                        .ok_or_else(|| {
485                            Error::Decode(format!(
486                                "invalid date y={}, m={}, d={}",
487                                year, month, day
488                            ))
489                        })?;
490                Ok(naive_date)
491            }
492            _ => Err(Error::Decode(format_decode_err("DATE", value))),
493        }
494    }
495}
496
497impl Decode for chrono::NaiveTime {
498    fn decode(value: &DbValue) -> Result<Self, Error> {
499        match value {
500            DbValue::Time((hour, minute, second, nanosecond)) => {
501                let naive_time = chrono::NaiveTime::from_hms_nano_opt(
502                    (*hour).into(),
503                    (*minute).into(),
504                    (*second).into(),
505                    *nanosecond,
506                )
507                .ok_or_else(|| {
508                    Error::Decode(format!(
509                        "invalid time {}:{}:{}:{}",
510                        hour, minute, second, nanosecond
511                    ))
512                })?;
513                Ok(naive_time)
514            }
515            _ => Err(Error::Decode(format_decode_err("TIME", value))),
516        }
517    }
518}
519
520impl Decode for chrono::NaiveDateTime {
521    fn decode(value: &DbValue) -> Result<Self, Error> {
522        match value {
523            DbValue::Datetime((year, month, day, hour, minute, second, nanosecond)) => {
524                let naive_date =
525                    chrono::NaiveDate::from_ymd_opt(*year, (*month).into(), (*day).into())
526                        .ok_or_else(|| {
527                            Error::Decode(format!(
528                                "invalid date y={}, m={}, d={}",
529                                year, month, day
530                            ))
531                        })?;
532                let naive_time = chrono::NaiveTime::from_hms_nano_opt(
533                    (*hour).into(),
534                    (*minute).into(),
535                    (*second).into(),
536                    *nanosecond,
537                )
538                .ok_or_else(|| {
539                    Error::Decode(format!(
540                        "invalid time {}:{}:{}:{}",
541                        hour, minute, second, nanosecond
542                    ))
543                })?;
544                let dt = chrono::NaiveDateTime::new(naive_date, naive_time);
545                Ok(dt)
546            }
547            _ => Err(Error::Decode(format_decode_err("DATETIME", value))),
548        }
549    }
550}
551
552impl Decode for chrono::Duration {
553    fn decode(value: &DbValue) -> Result<Self, Error> {
554        match value {
555            DbValue::Timestamp(n) => Ok(chrono::Duration::seconds(*n)),
556            _ => Err(Error::Decode(format_decode_err("BIGINT", value))),
557        }
558    }
559}
560
561#[cfg(feature = "postgres4-types")]
562impl Decode for uuid::Uuid {
563    fn decode(value: &DbValue) -> Result<Self, Error> {
564        match value {
565            DbValue::Uuid(s) => uuid::Uuid::parse_str(s).map_err(|e| Error::Decode(e.to_string())),
566            _ => Err(Error::Decode(format_decode_err("UUID", value))),
567        }
568    }
569}
570
571#[cfg(feature = "json")]
572impl Decode for serde_json::Value {
573    fn decode(value: &DbValue) -> Result<Self, Error> {
574        from_jsonb(value)
575    }
576}
577
578/// Convert a Postgres JSONB value to a `Deserialize`-able type.
579#[cfg(feature = "json")]
580pub fn from_jsonb<'a, T: serde::Deserialize<'a>>(value: &'a DbValue) -> Result<T, Error> {
581    match value {
582        DbValue::Jsonb(j) => serde_json::from_slice(j).map_err(|e| Error::Decode(e.to_string())),
583        _ => Err(Error::Decode(format_decode_err("JSONB", value))),
584    }
585}
586
587#[cfg(feature = "postgres4-types")]
588impl Decode for rust_decimal::Decimal {
589    fn decode(value: &DbValue) -> Result<Self, Error> {
590        match value {
591            DbValue::Decimal(s) => {
592                rust_decimal::Decimal::from_str_exact(s).map_err(|e| Error::Decode(e.to_string()))
593            }
594            _ => Err(Error::Decode(format_decode_err("NUMERIC", value))),
595        }
596    }
597}
598
599#[cfg(feature = "postgres4-types")]
600fn bound_type_from_wit(kind: RangeBoundKind) -> postgres_range::BoundType {
601    match kind {
602        RangeBoundKind::Inclusive => postgres_range::BoundType::Inclusive,
603        RangeBoundKind::Exclusive => postgres_range::BoundType::Exclusive,
604    }
605}
606
607#[cfg(feature = "postgres4-types")]
608impl Decode for postgres_range::Range<i32> {
609    fn decode(value: &DbValue) -> Result<Self, Error> {
610        match value {
611            DbValue::RangeInt32((lbound, ubound)) => {
612                let lower = lbound.map(|(value, kind)| {
613                    postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
614                });
615                let upper = ubound.map(|(value, kind)| {
616                    postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
617                });
618                Ok(postgres_range::Range::new(lower, upper))
619            }
620            _ => Err(Error::Decode(format_decode_err("INT4RANGE", value))),
621        }
622    }
623}
624
625#[cfg(feature = "postgres4-types")]
626impl Decode for postgres_range::Range<i64> {
627    fn decode(value: &DbValue) -> Result<Self, Error> {
628        match value {
629            DbValue::RangeInt64((lbound, ubound)) => {
630                let lower = lbound.map(|(value, kind)| {
631                    postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
632                });
633                let upper = ubound.map(|(value, kind)| {
634                    postgres_range::RangeBound::new(value, bound_type_from_wit(kind))
635                });
636                Ok(postgres_range::Range::new(lower, upper))
637            }
638            _ => Err(Error::Decode(format_decode_err("INT8RANGE", value))),
639        }
640    }
641}
642
643// We can't use postgres_range::Range because rust_decimal::Decimal
644// is not Normalizable
645#[cfg(feature = "postgres4-types")]
646impl Decode
647    for (
648        Option<(rust_decimal::Decimal, RangeBoundKind)>,
649        Option<(rust_decimal::Decimal, RangeBoundKind)>,
650    )
651{
652    fn decode(value: &DbValue) -> Result<Self, Error> {
653        fn parse(
654            value: &str,
655            kind: RangeBoundKind,
656        ) -> Result<(rust_decimal::Decimal, RangeBoundKind), Error> {
657            let dec = rust_decimal::Decimal::from_str_exact(value)
658                .map_err(|e| Error::Decode(e.to_string()))?;
659            Ok((dec, kind))
660        }
661
662        match value {
663            DbValue::RangeDecimal((lbound, ubound)) => {
664                let lower = lbound
665                    .as_ref()
666                    .map(|(value, kind)| parse(value, *kind))
667                    .transpose()?;
668                let upper = ubound
669                    .as_ref()
670                    .map(|(value, kind)| parse(value, *kind))
671                    .transpose()?;
672                Ok((lower, upper))
673            }
674            _ => Err(Error::Decode(format_decode_err("NUMERICRANGE", value))),
675        }
676    }
677}
678
679// TODO: can we return a slice here? It seems like it should be possible but
680// I wasn't able to get the lifetimes to work with the trait
681impl Decode for Vec<Option<i32>> {
682    fn decode(value: &DbValue) -> Result<Self, Error> {
683        match value {
684            DbValue::ArrayInt32(a) => Ok(a.to_vec()),
685            _ => Err(Error::Decode(format_decode_err("INT4[]", value))),
686        }
687    }
688}
689
690impl Decode for Vec<Option<i64>> {
691    fn decode(value: &DbValue) -> Result<Self, Error> {
692        match value {
693            DbValue::ArrayInt64(a) => Ok(a.to_vec()),
694            _ => Err(Error::Decode(format_decode_err("INT8[]", value))),
695        }
696    }
697}
698
699impl Decode for Vec<Option<String>> {
700    fn decode(value: &DbValue) -> Result<Self, Error> {
701        match value {
702            DbValue::ArrayStr(a) => Ok(a.to_vec()),
703            _ => Err(Error::Decode(format_decode_err("TEXT[]", value))),
704        }
705    }
706}
707
708#[cfg(feature = "postgres4-types")]
709fn map_decimal(s: &Option<String>) -> Result<Option<rust_decimal::Decimal>, Error> {
710    s.as_ref()
711        .map(|s| rust_decimal::Decimal::from_str_exact(s))
712        .transpose()
713        .map_err(|e| Error::Decode(e.to_string()))
714}
715
716#[cfg(feature = "postgres4-types")]
717impl Decode for Vec<Option<rust_decimal::Decimal>> {
718    fn decode(value: &DbValue) -> Result<Self, Error> {
719        match value {
720            DbValue::ArrayDecimal(a) => {
721                let decs = a.iter().map(map_decimal).collect::<Result<_, _>>()?;
722                Ok(decs)
723            }
724            _ => Err(Error::Decode(format_decode_err("NUMERIC[]", value))),
725        }
726    }
727}
728
729impl Decode for Interval {
730    fn decode(value: &DbValue) -> Result<Self, Error> {
731        match value {
732            DbValue::Interval(i) => Ok(*i),
733            _ => Err(Error::Decode(format_decode_err("INTERVAL", value))),
734        }
735    }
736}
737
738macro_rules! impl_parameter_value_conversions {
739    ($($ty:ty => $id:ident),*) => {
740        $(
741            impl From<$ty> for ParameterValue {
742                fn from(v: $ty) -> ParameterValue {
743                    ParameterValue::$id(v)
744                }
745            }
746        )*
747    };
748}
749
750impl_parameter_value_conversions! {
751    i8 => Int8,
752    i16 => Int16,
753    i32 => Int32,
754    i64 => Int64,
755    f32 => Floating32,
756    f64 => Floating64,
757    bool => Boolean,
758    String => Str,
759    Vec<u8> => Binary,
760    Vec<Option<i32>> => ArrayInt32,
761    Vec<Option<i64>> => ArrayInt64,
762    Vec<Option<String>> => ArrayStr
763}
764
765impl From<chrono::NaiveDateTime> for ParameterValue {
766    fn from(v: chrono::NaiveDateTime) -> ParameterValue {
767        ParameterValue::Datetime((
768            v.year(),
769            v.month() as u8,
770            v.day() as u8,
771            v.hour() as u8,
772            v.minute() as u8,
773            v.second() as u8,
774            v.nanosecond(),
775        ))
776    }
777}
778
779impl From<chrono::NaiveTime> for ParameterValue {
780    fn from(v: chrono::NaiveTime) -> ParameterValue {
781        ParameterValue::Time((
782            v.hour() as u8,
783            v.minute() as u8,
784            v.second() as u8,
785            v.nanosecond(),
786        ))
787    }
788}
789
790impl From<chrono::NaiveDate> for ParameterValue {
791    fn from(v: chrono::NaiveDate) -> ParameterValue {
792        ParameterValue::Date((v.year(), v.month() as u8, v.day() as u8))
793    }
794}
795
796impl From<chrono::TimeDelta> for ParameterValue {
797    fn from(v: chrono::TimeDelta) -> ParameterValue {
798        ParameterValue::Timestamp(v.num_seconds())
799    }
800}
801
802#[cfg(feature = "postgres4-types")]
803impl From<uuid::Uuid> for ParameterValue {
804    fn from(v: uuid::Uuid) -> ParameterValue {
805        ParameterValue::Uuid(v.to_string())
806    }
807}
808
809#[cfg(feature = "json")]
810impl TryFrom<serde_json::Value> for ParameterValue {
811    type Error = serde_json::Error;
812
813    fn try_from(v: serde_json::Value) -> Result<ParameterValue, Self::Error> {
814        jsonb(&v)
815    }
816}
817
818/// Converts a `Serialize` value to a Postgres JSONB SQL parameter.
819#[cfg(feature = "json")]
820pub fn jsonb<T: serde::Serialize>(value: &T) -> Result<ParameterValue, serde_json::Error> {
821    let json = serde_json::to_vec(value)?;
822    Ok(ParameterValue::Jsonb(json))
823}
824
825#[cfg(feature = "postgres4-types")]
826impl From<rust_decimal::Decimal> for ParameterValue {
827    fn from(v: rust_decimal::Decimal) -> ParameterValue {
828        ParameterValue::Decimal(v.to_string())
829    }
830}
831
832// We cannot impl From<T: RangeBounds<...>> because Rust fears that some future
833// knave or rogue might one day add RangeBounds to NaiveDateTime. The best we can
834// do is therefore a helper function we can call from range Froms.
835#[allow(
836    clippy::type_complexity,
837    reason = "I sure hope 'blame Alex' works here too"
838)]
839fn range_bounds_to_wit<T, U>(
840    range: impl std::ops::RangeBounds<T>,
841    f: impl Fn(&T) -> U,
842) -> (Option<(U, RangeBoundKind)>, Option<(U, RangeBoundKind)>) {
843    (
844        range_bound_to_wit(range.start_bound(), &f),
845        range_bound_to_wit(range.end_bound(), &f),
846    )
847}
848
849fn range_bound_to_wit<T, U>(
850    bound: std::ops::Bound<&T>,
851    f: &dyn Fn(&T) -> U,
852) -> Option<(U, RangeBoundKind)> {
853    match bound {
854        std::ops::Bound::Included(v) => Some((f(v), RangeBoundKind::Inclusive)),
855        std::ops::Bound::Excluded(v) => Some((f(v), RangeBoundKind::Exclusive)),
856        std::ops::Bound::Unbounded => None,
857    }
858}
859
860#[cfg(feature = "postgres4-types")]
861fn pg_range_bound_to_wit<S: postgres_range::BoundSided, T: Copy>(
862    bound: &postgres_range::RangeBound<S, T>,
863) -> (T, RangeBoundKind) {
864    let kind = match &bound.type_ {
865        postgres_range::BoundType::Inclusive => RangeBoundKind::Inclusive,
866        postgres_range::BoundType::Exclusive => RangeBoundKind::Exclusive,
867    };
868    (bound.value, kind)
869}
870
871impl From<std::ops::Range<i32>> for ParameterValue {
872    fn from(v: std::ops::Range<i32>) -> ParameterValue {
873        ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
874    }
875}
876
877impl From<std::ops::RangeInclusive<i32>> for ParameterValue {
878    fn from(v: std::ops::RangeInclusive<i32>) -> ParameterValue {
879        ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
880    }
881}
882
883impl From<std::ops::RangeFrom<i32>> for ParameterValue {
884    fn from(v: std::ops::RangeFrom<i32>) -> ParameterValue {
885        ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
886    }
887}
888
889impl From<std::ops::RangeTo<i32>> for ParameterValue {
890    fn from(v: std::ops::RangeTo<i32>) -> ParameterValue {
891        ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
892    }
893}
894
895impl From<std::ops::RangeToInclusive<i32>> for ParameterValue {
896    fn from(v: std::ops::RangeToInclusive<i32>) -> ParameterValue {
897        ParameterValue::RangeInt32(range_bounds_to_wit(v, |n| *n))
898    }
899}
900
901#[cfg(feature = "postgres4-types")]
902impl From<postgres_range::Range<i32>> for ParameterValue {
903    fn from(v: postgres_range::Range<i32>) -> ParameterValue {
904        let lbound = v.lower().map(pg_range_bound_to_wit);
905        let ubound = v.upper().map(pg_range_bound_to_wit);
906        ParameterValue::RangeInt32((lbound, ubound))
907    }
908}
909
910impl From<std::ops::Range<i64>> for ParameterValue {
911    fn from(v: std::ops::Range<i64>) -> ParameterValue {
912        ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
913    }
914}
915
916impl From<std::ops::RangeInclusive<i64>> for ParameterValue {
917    fn from(v: std::ops::RangeInclusive<i64>) -> ParameterValue {
918        ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
919    }
920}
921
922impl From<std::ops::RangeFrom<i64>> for ParameterValue {
923    fn from(v: std::ops::RangeFrom<i64>) -> ParameterValue {
924        ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
925    }
926}
927
928impl From<std::ops::RangeTo<i64>> for ParameterValue {
929    fn from(v: std::ops::RangeTo<i64>) -> ParameterValue {
930        ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
931    }
932}
933
934impl From<std::ops::RangeToInclusive<i64>> for ParameterValue {
935    fn from(v: std::ops::RangeToInclusive<i64>) -> ParameterValue {
936        ParameterValue::RangeInt64(range_bounds_to_wit(v, |n| *n))
937    }
938}
939
940#[cfg(feature = "postgres4-types")]
941impl From<postgres_range::Range<i64>> for ParameterValue {
942    fn from(v: postgres_range::Range<i64>) -> ParameterValue {
943        let lbound = v.lower().map(pg_range_bound_to_wit);
944        let ubound = v.upper().map(pg_range_bound_to_wit);
945        ParameterValue::RangeInt64((lbound, ubound))
946    }
947}
948
949#[cfg(feature = "postgres4-types")]
950impl From<std::ops::Range<rust_decimal::Decimal>> for ParameterValue {
951    fn from(v: std::ops::Range<rust_decimal::Decimal>) -> ParameterValue {
952        ParameterValue::RangeDecimal(range_bounds_to_wit(v, |d| d.to_string()))
953    }
954}
955
956impl From<Vec<i32>> for ParameterValue {
957    fn from(v: Vec<i32>) -> ParameterValue {
958        ParameterValue::ArrayInt32(v.into_iter().map(Some).collect())
959    }
960}
961
962impl From<Vec<i64>> for ParameterValue {
963    fn from(v: Vec<i64>) -> ParameterValue {
964        ParameterValue::ArrayInt64(v.into_iter().map(Some).collect())
965    }
966}
967
968impl From<Vec<String>> for ParameterValue {
969    fn from(v: Vec<String>) -> ParameterValue {
970        ParameterValue::ArrayStr(v.into_iter().map(Some).collect())
971    }
972}
973
974#[cfg(feature = "postgres4-types")]
975impl From<Vec<Option<rust_decimal::Decimal>>> for ParameterValue {
976    fn from(v: Vec<Option<rust_decimal::Decimal>>) -> ParameterValue {
977        let strs = v
978            .into_iter()
979            .map(|optd| optd.map(|d| d.to_string()))
980            .collect();
981        ParameterValue::ArrayDecimal(strs)
982    }
983}
984
985#[cfg(feature = "postgres4-types")]
986impl From<Vec<rust_decimal::Decimal>> for ParameterValue {
987    fn from(v: Vec<rust_decimal::Decimal>) -> ParameterValue {
988        let strs = v.into_iter().map(|d| Some(d.to_string())).collect();
989        ParameterValue::ArrayDecimal(strs)
990    }
991}
992
993impl From<Interval> for ParameterValue {
994    fn from(v: Interval) -> ParameterValue {
995        ParameterValue::Interval(v)
996    }
997}
998
999impl<T: Into<ParameterValue>> From<Option<T>> for ParameterValue {
1000    fn from(o: Option<T>) -> ParameterValue {
1001        match o {
1002            Some(v) => v.into(),
1003            None => ParameterValue::DbNull,
1004        }
1005    }
1006}
1007
1008fn format_decode_err(types: &str, value: &DbValue) -> String {
1009    format!("Expected {} from the DB but got {:?}", types, value)
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014    use chrono::NaiveDateTime;
1015
1016    use super::*;
1017
1018    #[test]
1019    fn boolean() {
1020        assert!(bool::decode(&DbValue::Boolean(true)).unwrap());
1021        assert!(bool::decode(&DbValue::Int32(0)).is_err());
1022        assert!(Option::<bool>::decode(&DbValue::DbNull).unwrap().is_none());
1023    }
1024
1025    #[test]
1026    fn int16() {
1027        assert_eq!(i16::decode(&DbValue::Int16(0)).unwrap(), 0);
1028        assert!(i16::decode(&DbValue::Int32(0)).is_err());
1029        assert!(Option::<i16>::decode(&DbValue::DbNull).unwrap().is_none());
1030    }
1031
1032    #[test]
1033    fn int32() {
1034        assert_eq!(i32::decode(&DbValue::Int32(0)).unwrap(), 0);
1035        assert!(i32::decode(&DbValue::Boolean(false)).is_err());
1036        assert!(Option::<i32>::decode(&DbValue::DbNull).unwrap().is_none());
1037    }
1038
1039    #[test]
1040    fn int64() {
1041        assert_eq!(i64::decode(&DbValue::Int64(0)).unwrap(), 0);
1042        assert!(i64::decode(&DbValue::Boolean(false)).is_err());
1043        assert!(Option::<i64>::decode(&DbValue::DbNull).unwrap().is_none());
1044    }
1045
1046    #[test]
1047    fn floating32() {
1048        assert!(f32::decode(&DbValue::Floating32(0.0)).is_ok());
1049        assert!(f32::decode(&DbValue::Boolean(false)).is_err());
1050        assert!(Option::<f32>::decode(&DbValue::DbNull).unwrap().is_none());
1051    }
1052
1053    #[test]
1054    fn floating64() {
1055        assert!(f64::decode(&DbValue::Floating64(0.0)).is_ok());
1056        assert!(f64::decode(&DbValue::Boolean(false)).is_err());
1057        assert!(Option::<f64>::decode(&DbValue::DbNull).unwrap().is_none());
1058    }
1059
1060    #[test]
1061    fn str() {
1062        assert_eq!(
1063            String::decode(&DbValue::Str(String::from("foo"))).unwrap(),
1064            String::from("foo")
1065        );
1066
1067        assert!(String::decode(&DbValue::Int32(0)).is_err());
1068        assert!(
1069            Option::<String>::decode(&DbValue::DbNull)
1070                .unwrap()
1071                .is_none()
1072        );
1073    }
1074
1075    #[test]
1076    fn binary() {
1077        assert!(Vec::<u8>::decode(&DbValue::Binary(vec![0, 0])).is_ok());
1078        assert!(Vec::<u8>::decode(&DbValue::Boolean(false)).is_err());
1079        assert!(
1080            Option::<Vec<u8>>::decode(&DbValue::DbNull)
1081                .unwrap()
1082                .is_none()
1083        );
1084    }
1085
1086    #[test]
1087    fn date() {
1088        assert_eq!(
1089            chrono::NaiveDate::decode(&DbValue::Date((1, 2, 4))).unwrap(),
1090            chrono::NaiveDate::from_ymd_opt(1, 2, 4).unwrap()
1091        );
1092        assert_ne!(
1093            chrono::NaiveDate::decode(&DbValue::Date((1, 2, 4))).unwrap(),
1094            chrono::NaiveDate::from_ymd_opt(1, 2, 5).unwrap()
1095        );
1096        assert!(
1097            Option::<chrono::NaiveDate>::decode(&DbValue::DbNull)
1098                .unwrap()
1099                .is_none()
1100        );
1101    }
1102
1103    #[test]
1104    fn time() {
1105        assert_eq!(
1106            chrono::NaiveTime::decode(&DbValue::Time((1, 2, 3, 4))).unwrap(),
1107            chrono::NaiveTime::from_hms_nano_opt(1, 2, 3, 4).unwrap()
1108        );
1109        assert_ne!(
1110            chrono::NaiveTime::decode(&DbValue::Time((1, 2, 3, 4))).unwrap(),
1111            chrono::NaiveTime::from_hms_nano_opt(1, 2, 4, 5).unwrap()
1112        );
1113        assert!(
1114            Option::<chrono::NaiveTime>::decode(&DbValue::DbNull)
1115                .unwrap()
1116                .is_none()
1117        );
1118    }
1119
1120    #[test]
1121    fn datetime() {
1122        let date = chrono::NaiveDate::from_ymd_opt(1, 2, 3).unwrap();
1123        let mut time = chrono::NaiveTime::from_hms_nano_opt(4, 5, 6, 7).unwrap();
1124        assert_eq!(
1125            chrono::NaiveDateTime::decode(&DbValue::Datetime((1, 2, 3, 4, 5, 6, 7))).unwrap(),
1126            chrono::NaiveDateTime::new(date, time)
1127        );
1128
1129        time = chrono::NaiveTime::from_hms_nano_opt(4, 5, 6, 8).unwrap();
1130        assert_ne!(
1131            NaiveDateTime::decode(&DbValue::Datetime((1, 2, 3, 4, 5, 6, 7))).unwrap(),
1132            chrono::NaiveDateTime::new(date, time)
1133        );
1134        assert!(
1135            Option::<chrono::NaiveDateTime>::decode(&DbValue::DbNull)
1136                .unwrap()
1137                .is_none()
1138        );
1139    }
1140
1141    #[test]
1142    fn timestamp() {
1143        assert_eq!(
1144            chrono::Duration::decode(&DbValue::Timestamp(1)).unwrap(),
1145            chrono::Duration::seconds(1),
1146        );
1147        assert_ne!(
1148            chrono::Duration::decode(&DbValue::Timestamp(2)).unwrap(),
1149            chrono::Duration::seconds(1)
1150        );
1151        assert!(
1152            Option::<chrono::Duration>::decode(&DbValue::DbNull)
1153                .unwrap()
1154                .is_none()
1155        );
1156    }
1157
1158    #[test]
1159    #[cfg(feature = "postgres4-types")]
1160    fn uuid() {
1161        let uuid_str = "12341234-1234-1234-1234-123412341234";
1162        assert_eq!(
1163            uuid::Uuid::try_parse(uuid_str).unwrap(),
1164            uuid::Uuid::decode(&DbValue::Uuid(uuid_str.to_owned())).unwrap(),
1165        );
1166        assert!(
1167            Option::<uuid::Uuid>::decode(&DbValue::DbNull)
1168                .unwrap()
1169                .is_none()
1170        );
1171    }
1172
1173    #[derive(Debug, serde::Deserialize, PartialEq)]
1174    struct JsonTest {
1175        hello: String,
1176    }
1177
1178    #[test]
1179    #[cfg(feature = "json")]
1180    fn jsonb() {
1181        let json_val = serde_json::json!({
1182            "hello": "world"
1183        });
1184        let dbval = DbValue::Jsonb(r#"{"hello":"world"}"#.into());
1185
1186        assert_eq!(json_val, serde_json::Value::decode(&dbval).unwrap(),);
1187
1188        let json_struct = JsonTest {
1189            hello: "world".to_owned(),
1190        };
1191        assert_eq!(json_struct, from_jsonb(&dbval).unwrap());
1192    }
1193
1194    #[test]
1195    #[cfg(feature = "postgres4-types")]
1196    fn ranges() {
1197        let i32_range = postgres_range::Range::<i32>::decode(&DbValue::RangeInt32((
1198            Some((45, RangeBoundKind::Inclusive)),
1199            Some((89, RangeBoundKind::Exclusive)),
1200        )))
1201        .unwrap();
1202        assert_eq!(45, i32_range.lower().unwrap().value);
1203        assert_eq!(
1204            postgres_range::BoundType::Inclusive,
1205            i32_range.lower().unwrap().type_
1206        );
1207        assert_eq!(89, i32_range.upper().unwrap().value);
1208        assert_eq!(
1209            postgres_range::BoundType::Exclusive,
1210            i32_range.upper().unwrap().type_
1211        );
1212
1213        let i32_range_from = postgres_range::Range::<i32>::decode(&DbValue::RangeInt32((
1214            Some((45, RangeBoundKind::Inclusive)),
1215            None,
1216        )))
1217        .unwrap();
1218        assert!(i32_range_from.upper().is_none());
1219
1220        let i64_range = postgres_range::Range::<i64>::decode(&DbValue::RangeInt64((
1221            Some((4567456745674567, RangeBoundKind::Inclusive)),
1222            Some((890189018901890189, RangeBoundKind::Exclusive)),
1223        )))
1224        .unwrap();
1225        assert_eq!(4567456745674567, i64_range.lower().unwrap().value);
1226        assert_eq!(890189018901890189, i64_range.upper().unwrap().value);
1227
1228        #[allow(clippy::type_complexity)]
1229        let (dec_lbound, dec_ubound): (
1230            Option<(rust_decimal::Decimal, RangeBoundKind)>,
1231            Option<(rust_decimal::Decimal, RangeBoundKind)>,
1232        ) = Decode::decode(&DbValue::RangeDecimal((
1233            Some(("4567.8901".to_owned(), RangeBoundKind::Inclusive)),
1234            Some(("8901.2345678901".to_owned(), RangeBoundKind::Exclusive)),
1235        )))
1236        .unwrap();
1237        assert_eq!(
1238            rust_decimal::Decimal::from_i128_with_scale(45678901, 4),
1239            dec_lbound.unwrap().0
1240        );
1241        assert_eq!(
1242            rust_decimal::Decimal::from_i128_with_scale(89012345678901, 10),
1243            dec_ubound.unwrap().0
1244        );
1245    }
1246
1247    #[test]
1248    #[cfg(feature = "postgres4-types")]
1249    fn arrays() {
1250        let v32 = vec![Some(123), None, Some(456)];
1251        let i32_arr = Vec::<Option<i32>>::decode(&DbValue::ArrayInt32(v32.clone())).unwrap();
1252        assert_eq!(v32, i32_arr);
1253
1254        let v64 = vec![Some(123), None, Some(456)];
1255        let i64_arr = Vec::<Option<i64>>::decode(&DbValue::ArrayInt64(v64.clone())).unwrap();
1256        assert_eq!(v64, i64_arr);
1257
1258        let vdec = vec![Some("1.23".to_owned()), None];
1259        let dec_arr =
1260            Vec::<Option<rust_decimal::Decimal>>::decode(&DbValue::ArrayDecimal(vdec)).unwrap();
1261        assert_eq!(
1262            vec![
1263                Some(rust_decimal::Decimal::from_i128_with_scale(123, 2)),
1264                None
1265            ],
1266            dec_arr
1267        );
1268
1269        let vstr = vec![Some("alice".to_owned()), None, Some("bob".to_owned())];
1270        let str_arr = Vec::<Option<String>>::decode(&DbValue::ArrayStr(vstr.clone())).unwrap();
1271        assert_eq!(vstr, str_arr);
1272    }
1273}