parsql_tokio_postgres/
crud_ops.rs

1use crate::traits::{CrudOps, FromRow, SqlParams, SqlQuery, UpdateParams};
2use postgres::types::FromSql;
3use std::sync::OnceLock;
4use tokio_postgres::{Client, Error, Row, Transaction};
5
6#[async_trait::async_trait]
7impl CrudOps for Client {
8    async fn insert<T, P: for<'a> FromSql<'a> + Send + Sync>(&self, entity: T) -> Result<P, Error>
9    where
10        T: SqlQuery + SqlParams + Send + Sync + 'static,
11    {
12        let sql = T::query();
13
14        static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
15        let is_trace_enabled =
16            *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
17
18        if is_trace_enabled {
19            println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
20        }
21
22        let params = entity.params();
23        let row = self.query_one(&sql, &params).await?;
24        row.try_get::<_, P>(0)
25    }
26
27    async fn update<T>(&self, entity: T) -> Result<bool, Error>
28    where
29        T: SqlQuery + UpdateParams + Send + Sync + 'static,
30    {
31        let sql = T::query();
32
33        static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
34        let is_trace_enabled =
35            *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
36
37        if is_trace_enabled {
38            println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
39        }
40
41        let params = entity.params();
42        let result = self.execute(&sql, &params).await?;
43        Ok(result > 0)
44    }
45
46    async fn delete<T>(&self, entity: T) -> Result<u64, Error>
47    where
48        T: SqlQuery + SqlParams + Send + Sync + 'static,
49    {
50        let sql = T::query();
51
52        static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
53        let is_trace_enabled =
54            *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
55
56        if is_trace_enabled {
57            println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
58        }
59
60        let params = entity.params();
61        self.execute(&sql, &params).await
62    }
63
64    async fn fetch<T>(&self, params: T) -> Result<T, Error>
65    where
66        T: SqlQuery + FromRow + SqlParams + Send + Sync + 'static,
67    {
68        let sql = T::query();
69
70        static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
71        let is_trace_enabled =
72            *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
73
74        if is_trace_enabled {
75            println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
76        }
77
78        let query_params = params.params();
79        let row = self.query_one(&sql, &query_params).await?;
80        T::from_row(&row)
81    }
82
83    async fn fetch_all<T>(&self, params: T) -> Result<Vec<T>, Error>
84    where
85        T: SqlQuery + FromRow + SqlParams + Send + Sync + 'static,
86    {
87        let sql = T::query();
88
89        static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
90        let is_trace_enabled =
91            *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
92
93        if is_trace_enabled {
94            println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
95        }
96
97        let query_params = params.params();
98        let rows = self.query(&sql, &query_params).await?;
99
100        let mut results = Vec::with_capacity(rows.len());
101        for row in rows {
102            results.push(T::from_row(&row)?);
103        }
104
105        Ok(results)
106    }
107
108    async fn select<T, F, R>(&self, entity: T, to_model: F) -> Result<R, Error>
109    where
110        T: SqlQuery + SqlParams + Send + Sync + 'static,
111        F: Fn(&Row) -> Result<R, Error> + Send + Sync + 'static,
112        R: Send + 'static,
113    {
114        let sql = T::query();
115
116        static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
117        let is_trace_enabled =
118            *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
119
120        if is_trace_enabled {
121            println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
122        }
123
124        let params = entity.params();
125        let row = self.query_one(&sql, &params).await?;
126        to_model(&row)
127    }
128
129    async fn select_all<T, F, R>(&self, entity: T, to_model: F) -> Result<Vec<R>, Error>
130    where
131        T: SqlQuery + SqlParams + Send + Sync + 'static,
132        F: Fn(&Row) -> R + Send + Sync + 'static,
133        R: Send + 'static,
134    {
135        let sql = T::query();
136
137        static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
138        let is_trace_enabled =
139            *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
140
141        if is_trace_enabled {
142            println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
143        }
144
145        let params = entity.params();
146        let rows = self.query(&sql, &params).await?;
147
148        let mut results = Vec::with_capacity(rows.len());
149        for row in rows {
150            results.push(to_model(&row));
151        }
152
153        Ok(results)
154    }
155}
156
157/// # insert
158///
159/// Inserts a new record into the database.
160///
161/// ## Parameters
162/// - `client`: Database connection object
163/// - `entity`: Data object to be inserted (must implement SqlQuery and SqlParams traits)
164///
165/// ## Return Value
166/// - `Result<u64, Error>`: On success, returns the number of inserted records; on failure, returns Error
167pub async fn insert<T, P: for<'a> FromSql<'a> + Send + Sync>(
168    client: &Client,
169    entity: T,
170) -> Result<P, Error>
171where
172    T: SqlQuery + SqlParams + Send + Sync + 'static,
173{
174    client.insert::<T, P>(entity).await
175}
176
177/// # update
178///
179/// Updates an existing record in the database.
180///
181/// ## Parameters
182/// - `client`: Database connection object
183/// - `entity`: Data object containing the update information (must implement SqlQuery and UpdateParams traits)
184///
185/// ## Return Value
186/// - `Result<bool, Error>`: On success, returns true; on failure, returns Error
187pub async fn update<T>(client: &Client, entity: T) -> Result<bool, Error>
188where
189    T: SqlQuery + UpdateParams + Send + Sync + 'static,
190{
191    client.update(entity).await
192}
193
194/// # delete
195///
196/// Deletes a record from the database.
197///
198/// ## Parameters
199/// - `client`: Database connection object
200/// - `entity`: Data object containing delete conditions (must implement SqlQuery and SqlParams traits)
201///
202/// ## Return Value
203/// - `Result<u64, Error>`: On success, returns the number of deleted records; on failure, returns Error
204pub async fn delete<T>(client: &Client, entity: T) -> Result<u64, Error>
205where
206    T: SqlQuery + SqlParams + Send + Sync + 'static,
207{
208    client.delete(entity).await
209}
210
211/// # fetch
212///
213/// Retrieves a single record from the database and converts it to a struct.
214///
215/// ## Parameters
216/// - `client`: Database connection object
217/// - `params`: Data object containing query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
218///
219/// ## Return Value
220/// - `Result<T, Error>`: On success, returns the retrieved record as a struct; on failure, returns Error
221pub async fn fetch<T>(client: &Client, params: T) -> Result<T, Error>
222where
223    T: SqlQuery + FromRow + SqlParams + Send + Sync + 'static,
224{
225    client.fetch(params).await
226}
227
228/// # fetch_all
229///
230/// Retrieves multiple records from the database.
231///
232/// ## Parameters
233/// - `client`: Database connection object
234/// - `params`: Query parameter object (must implement SqlQuery, FromRow, and SqlParams traits)
235///
236/// ## Return Value
237/// - `Result<Vec<T>, Error>`: On success, returns the list of found records; on failure, returns Error
238pub async fn fetch_all<T>(client: &Client, params: T) -> Result<Vec<T>, Error>
239where
240    T: SqlQuery + FromRow + SqlParams + Send + Sync + 'static,
241{
242    client.fetch_all(params).await
243}
244
245/// # select
246///
247/// Retrieves a single record from the database using a custom transformation function.
248/// This is useful when you want to use a custom transformation function instead of the FromRow trait.
249///
250/// ## Parameters
251/// - `client`: Database connection object
252/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
253/// - `to_model`: Function to convert a Row object to the target object type
254///
255/// ## Return Value
256/// - `Result<R, Error>`: On success, returns the transformed object; on failure, returns Error
257pub async fn select<T, F, R>(client: &Client, entity: T, to_model: F) -> Result<R, Error>
258where
259    T: SqlQuery + SqlParams + Send + Sync + 'static,
260    F: Fn(&Row) -> Result<R, Error> + Send + Sync + 'static,
261    R: Send + 'static,
262{
263    client.select(entity, to_model).await
264}
265
266/// # select_all
267///
268/// Retrieves multiple records from the database using a custom transformation function.
269/// This is useful when you want to use a custom transformation function instead of the FromRow trait.
270///
271/// ## Parameters
272/// - `client`: Database connection object
273/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
274/// - `to_model`: Function to convert a Row object to the target object type
275///
276/// ## Return Value
277/// - `Result<Vec<R>, Error>`: On success, returns the list of transformed objects; on failure, returns Error
278pub async fn select_all<T, F, R>(client: &Client, entity: T, to_model: F) -> Result<Vec<R>, Error>
279where
280    T: SqlQuery + SqlParams + Send + Sync + 'static,
281    F: Fn(&Row) -> R + Send + Sync + 'static,
282    R: Send + 'static,
283{
284    client.select_all(entity, to_model).await
285}
286
287// DEPRECATED FUNCTIONS - For backward compatibility
288
289/// # get
290///
291/// Retrieves a single record from the database and converts it to a struct.
292///
293/// # Deprecated
294/// This function has been renamed to `fetch`. Please use `fetch` instead.
295///
296/// # Arguments
297/// * `client` - Database connection client
298/// * `params` - Query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
299///
300/// # Return Value
301/// * `Result<T, Error>` - On success, returns the retrieved record; on failure, returns Error
302#[deprecated(
303    since = "0.2.0",
304    note = "Renamed to `fetch`. Please use `fetch` function instead."
305)]
306pub async fn get<T>(client: &Client, params: T) -> Result<T, Error>
307where
308    T: SqlQuery + FromRow + SqlParams + Send + Sync + 'static,
309{
310    fetch(client, params).await
311}
312
313/// # get_all
314///
315/// Retrieves multiple records from the database.
316///
317/// # Deprecated
318/// This function has been renamed to `fetch_all`. Please use `fetch_all` instead.
319///
320/// # Arguments
321/// * `client` - Database connection client
322/// * `params` - Query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
323///
324/// # Return Value
325/// * `Result<Vec<T>, Error>` - On success, returns the list of found records; on failure, returns Error
326#[deprecated(
327    since = "0.2.0",
328    note = "Renamed to `fetch_all`. Please use `fetch_all` function instead."
329)]
330pub async fn get_all<T>(client: &Client, params: T) -> Result<Vec<T>, Error>
331where
332    T: SqlQuery + FromRow + SqlParams + Send + Sync + 'static,
333{
334    fetch_all(client, params).await
335}