inline_postgres_impl/
lib.rs

1use core::marker::PhantomData;
2
3pub use async_trait::async_trait;
4pub use schema::*;
5pub use tokio_postgres::{self as postgres, *};
6
7/// Re-exports traits such that the functions can be used without manually importing them.
8pub mod prelude {
9    pub use super::{Exec, Fetch, Table, TableHelper};
10}
11
12pub mod key;
13pub mod schema;
14pub mod untyped_key;
15
16pub struct Statement<'a> {
17    /// The SQL code with placeholders in the form of `$1`, `$2`, etc
18    pub code: &'static str,
19    /// The values being injected into the prepared statement
20    pub vals: &'a [&'a (dyn types::ToSql + Sync)],
21}
22
23impl<'a> Statement<'a> {
24    #[doc(hidden)]
25    pub fn new(code: &'static str, vals: &'a [&'a (dyn types::ToSql + Sync)]) -> Self {
26        Self { code, vals }
27    }
28}
29
30/// The base type that is returned by the SQL query macros.
31///
32/// It is not intended for this type to be created manually.
33pub struct Query<'a, O: From<Row>> {
34    /// The SQL code with placeholders in the form of `$1`, `$2`, etc
35    pub code: &'static str,
36    /// The values being injected into the prepared statement
37    pub vals: &'a [&'a (dyn types::ToSql + Sync)],
38    /// Holding the generic argument to the output type.
39    _phantom: PhantomData<O>,
40}
41
42impl<'a, O: From<Row> + Send + Sync> Query<'a, O> {
43    #[doc(hidden)]
44    /// Creates a new instance of a query.
45    ///
46    /// Do not call manually.
47    pub fn new(code: &'static str, vals: &'a [&'a (dyn types::ToSql + Sync)]) -> Self {
48        Self {
49            code,
50            vals,
51            _phantom: PhantomData,
52        }
53    }
54
55    /// executes this query on a client.
56    pub async fn execute_on(self, client: &mut Client) -> Result<u64, Error> {
57        client.execute(self.code, self.vals).await
58    }
59}
60
61impl<'a, O: From<Row>> Query<'a, O> {
62    #[doc(hidden)]
63    pub fn casted<T: From<Row>>(self) -> Query<'a, T> {
64        Query {
65            code: self.code,
66            vals: self.vals,
67            _phantom: PhantomData,
68        }
69    }
70}
71
72#[async_trait]
73/// Helper Trait to implement the `fetch` method on [`Client`](Client).
74pub trait Fetch {
75    /// fetches records from `self` and converts them to instances of `O`.
76    async fn fetch<'a, O: From<row::Row> + Send + Sync>(
77        &self,
78        query: Query<'a, O>,
79    ) -> Result<Vec<O>, Error>;
80}
81#[async_trait]
82impl<C: GenericClient + Sync> Fetch for C {
83    async fn fetch<'a, O: From<Row> + Send + Sync>(
84        &self,
85        query: Query<'a, O>,
86    ) -> Result<Vec<O>, Error> {
87        let res = self
88            .query(query.code, query.vals)
89            .await?
90            .into_iter()
91            .map(Into::into)
92            .collect();
93        Ok(res)
94    }
95}
96
97#[async_trait]
98/// Helper Trait to implement the `exec` method on [`Client`](Client).
99pub trait Exec {
100    /// executes `stmt` on `self`.
101    ///
102    /// returns the number of affected results
103    async fn exec<'a>(&self, stmt: Statement<'a>) -> Result<u64, Error>;
104}
105#[async_trait]
106impl<C: GenericClient + Sync> Exec for C {
107    async fn exec<'a>(&self, stmt: Statement<'a>) -> Result<u64, Error> {
108        self.execute(stmt.code, stmt.vals).await
109    }
110}
111
112/// A where clause generated by [`iff!`]
113pub struct WhereClause<'a> {
114    /// The SQL code with placeholders in the form of `$1`, `$2`, etc
115    pub clause: &'static str,
116    /// The values being injected into the prepared statement
117    pub vals: &'a [&'a (dyn types::ToSql + Sync)],
118}
119
120impl<'a> WhereClause<'a> {
121    #[doc(hidden)]
122    pub fn new(santa: &'static str, vals: &'a [&'a (dyn types::ToSql + Sync)]) -> Self {
123        Self {
124            clause: santa,
125            vals,
126        }
127    }
128}
129
130#[derive(Default, Debug, PartialEq, Eq, Clone)]
131pub struct InsertOptions {
132    pub on_conflict_do_nothing: bool,
133    pub on_conflict_do_update: bool,
134}
135
136#[async_trait]
137pub trait Table: From<Row> + Sync + Send {
138    fn key(&self) -> key::Key<Self>;
139    async fn fetch_by_key<C: postgres::GenericClient + Sync>(
140        key: key::Key<Self>,
141        client: &C,
142    ) -> Result<Option<Self>, postgres::Error>;
143    async fn fetch_where<C: postgres::GenericClient + Sync>(
144        client: &C,
145        where_clause: WhereClause<'_>,
146    ) -> Result<Vec<Self>, postgres::Error>;
147    async fn delete_by_key<C: postgres::GenericClient + Sync>(
148        key: key::Key<Self>,
149        client: &C,
150    ) -> Result<u64, postgres::Error>;
151    async fn delete<C: postgres::GenericClient + Sync>(
152        &self,
153        client: &C,
154    ) -> Result<u64, postgres::Error> {
155        Self::delete_by_key(self.key(), client).await
156    }
157    async fn insert<C: postgres::GenericClient + Sync>(
158        &self,
159        client: &C,
160    ) -> Result<u64, postgres::Error>;
161    async fn update<C: postgres::GenericClient + Sync>(
162        &self,
163        client: &C,
164    ) -> Result<u64, postgres::Error>;
165    async fn upsert<C: postgres::GenericClient + Sync>(
166        &self,
167        client: &C,
168    ) -> Result<u64, postgres::Error>;
169    async fn create_table<C: postgres::GenericClient + Sync>(
170        client: &C,
171    ) -> Result<(), postgres::Error>;
172    async fn bulk_insert<C: postgres::GenericClient + Sync>(
173        client: &C,
174        values: &[Self],
175    ) -> Result<(), postgres::Error> {
176        Self::bulk_insert_opt(client, values, InsertOptions::default()).await
177    }
178    async fn bulk_insert_opt<C: postgres::GenericClient + Sync>(
179        client: &C,
180        values: &[Self],
181        options: InsertOptions,
182    ) -> Result<(), postgres::Error>;
183}
184
185#[async_trait]
186pub trait TableHelper<T: 'static + Table>: Sync + Send {
187    /// Delete the record associated to the given key
188    async fn delete_by_key(&self, key: key::Key<T>) -> Result<u64, postgres::Error>;
189
190    async fn delete(&self, record: T) -> Result<u64, postgres::Error> {
191        self.delete_by_key(record.key()).await
192    }
193    async fn insert(&self, record: &T) -> Result<u64, postgres::Error>;
194    async fn update(&self, record: &T) -> Result<u64, postgres::Error>;
195    async fn upsert(&self, record: &T) -> Result<u64, postgres::Error>;
196
197    /// Fetch records of the given table with a specific `WHERE` clause.
198    ///
199    /// # Example
200    /// ```
201    /// use inline_postgres as pg;
202    /// use pg::{key::Key, prelude::*, Table, sql};
203    ///
204    /// fn main() -> Result<(), pg::Error> {
205    ///
206    ///     #[derive(Debug, Table)]
207    ///     struct Celebrity {
208    ///         id: Key<Self>,
209    ///         first_name: String,
210    ///         last_name: String,
211    ///     }
212    ///
213    /// #   client.exec(sql! {
214    /// #       CREATE TABLE IF NOT EXISTS CELEBRITY (
215    /// #           ID UUID PRIMARY KEY,
216    /// #           FIRST_NAME VARCHAR(100),
217    /// #           LAST_NAME VARCHAR(100)
218    /// #       )
219    /// #   })?;
220    ///     let mut client = pg::Client::connect("host=localhost, user=postgres", pg::NoTls)?;
221    ///     let client = &mut client;
222    ///
223    ///     client.insert(Celebrity::new("Tony", "Hawk"))?;
224    ///
225    ///     let people: Vec<Celebrity> = client.fetch_where(sql!{ FIRST_NAME = "Tony" })?;
226    ///
227    ///     assert_eq!(people.len(), 1);
228    ///     assert_eq!(people[0].first_name, "Tony");
229    ///     assert_eq!(people[0].last_name, "Hawk");
230    ///
231    /// #   client.exec(sql! {
232    /// #       DROP TABLE CELEBRITY
233    /// #   })?;
234    ///     Ok(())
235    /// }
236    /// ```
237    async fn fetch_table(&self, where_clause: WhereClause<'_>) -> Result<Vec<T>, postgres::Error>;
238
239    async fn bulk_insert(&self, records: &[T]) -> Result<(), postgres::Error>;
240}
241
242#[async_trait]
243impl<T: 'static + Table, C: GenericClient + Send + Sync> TableHelper<T> for C {
244    async fn delete_by_key(&self, key: key::Key<T>) -> Result<u64, postgres::Error> {
245        key.delete(self).await
246    }
247
248    async fn insert(&self, record: &T) -> Result<u64, postgres::Error> {
249        record.insert(self).await
250    }
251
252    async fn update(&self, record: &T) -> Result<u64, postgres::Error> {
253        record.update(self).await
254    }
255
256    async fn upsert(&self, record: &T) -> Result<u64, postgres::Error> {
257        record.upsert(self).await
258    }
259
260    async fn fetch_table(&self, where_clause: WhereClause<'_>) -> Result<Vec<T>, postgres::Error> {
261        T::fetch_where(self, where_clause).await
262    }
263
264    async fn bulk_insert(&self, records: &[T]) -> Result<(), postgres::Error> {
265        T::bulk_insert(self, records).await
266    }
267}
268
269#[derive(Debug)]
270pub struct Ref<T: Table + std::fmt::Debug> {
271    /// the foreign key referencing the entry in the table `T`
272    pub fk: untyped_key::Key,
273    _phantom: PhantomData<T>,
274}