parsql_tokio_postgres/
crud_ops.rs

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