parsql_tokio_postgres/transaction_ops.rs
1use crate::traits::{CrudOps, FromRow, SqlCommand, SqlParams, SqlQuery, UpdateParams};
2use postgres::types::FromSql;
3use std::sync::OnceLock;
4use tokio_postgres::{Client, Error, Row, Transaction};
5
6/// Creates and begins a new transaction.
7///
8/// This function is a wrapper around the tokio-postgres `transaction()` method.
9/// It allows starting a new database transaction for performing multiple operations atomically.
10///
11/// # Return Value
12/// * `Result<Transaction<'_>, Error>` - On success, returns the new transaction; on failure, returns Error
13///
14/// # Example
15/// ```rust,no_run
16/// # use tokio_postgres::{NoTls, Error};
17/// # use parsql::tokio_postgres::transactional;
18/// #
19/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
20/// # let (mut client, connection) = tokio_postgres::connect("", NoTls).await?;
21/// # tokio::spawn(async move { connection.await; });
22/// let transaction = transactional::begin(&mut client).await?;
23///
24/// // Transaction işlemlerini gerçekleştir
25///
26/// transaction.commit().await?;
27/// # Ok(())
28/// # }
29/// ```
30pub async fn begin(client: &mut Client) -> Result<Transaction<'_>, Error> {
31 client.transaction().await
32}
33
34/// Inserts a record within a transaction.
35///
36/// This function executes an INSERT SQL query within the given transaction.
37/// It returns the transaction object, allowing for method chaining.
38///
39/// # Arguments
40/// * `transaction` - An active transaction
41/// * `entity` - Data object to be inserted (must implement SqlQuery and SqlParams traits)
42///
43/// # Return Value
44/// * `Result<(Transaction<'_>, u64), Error>` - On success, returns the transaction and the number of affected rows; on failure, returns Error
45///
46/// # Example
47/// ```rust,no_run
48/// # use tokio_postgres::{NoTls, Error};
49/// # use parsql::tokio_postgres::transactional;
50/// # use parsql::macros::{Insertable, SqlParams};
51/// #
52/// #[derive(Insertable, SqlParams)]
53/// #[table("users")]
54/// struct InsertUser {
55/// name: String,
56/// email: String,
57/// }
58///
59/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
60/// # let (client, connection) = tokio_postgres::connect("", NoTls).await?;
61/// # tokio::spawn(async move { connection.await; });
62/// let user = InsertUser {
63/// name: "John".to_string(),
64/// email: "john@example.com".to_string(),
65/// };
66///
67/// let transaction = transactional::begin(&client).await?;
68/// let (transaction, rows_affected) = transactional::tx_insert(transaction, user).await?;
69/// transaction.commit().await?;
70/// # Ok(())
71/// # }
72/// ```
73pub async fn tx_insert<T>(
74 transaction: Transaction<'_>,
75 entity: T,
76) -> Result<(Transaction<'_>, u64), Error>
77where
78 T: SqlQuery<T> + SqlParams + Send + Sync + 'static,
79{
80 let sql = T::query();
81
82 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
83 let is_trace_enabled =
84 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
85
86 if is_trace_enabled {
87 println!("[PARSQL-TOKIO-POSTGRES-TX] Execute SQL: {}", sql);
88 }
89
90 let params = entity.params();
91 let result = transaction.execute(&sql, ¶ms).await?;
92 Ok((transaction, result))
93}
94
95/// Updates a record within a transaction.
96///
97/// # Arguments
98/// * `transaction` - An active transaction
99/// * `entity` - Data object containing the update information (must implement SqlQuery and UpdateParams traits)
100///
101/// # Return Value
102/// * `Result<(Transaction<'_>, bool), Error>` - On success, returns the transaction and whether any record was updated
103///
104/// # Example
105/// ```rust,no_run
106/// # use tokio_postgres::{NoTls, Error};
107/// # use parsql::tokio_postgres::transactional;
108/// # use parsql::macros::{Updateable, UpdateParams};
109/// #
110/// #[derive(Updateable, UpdateParams)]
111/// #[table("users")]
112/// #[update("name, email")]
113/// #[where_clause("id = $")]
114/// struct UpdateUser {
115/// id: i64,
116/// name: String,
117/// email: String,
118/// }
119///
120/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
121/// # let (client, connection) = tokio_postgres::connect("", NoTls).await?;
122/// # tokio::spawn(async move { connection.await; });
123/// let user = UpdateUser {
124/// id: 1,
125/// name: "John Smith".to_string(),
126/// email: "john.smith@example.com".to_string(),
127/// };
128///
129/// let transaction = transactional::begin(&client).await?;
130/// let (transaction, updated) = transactional::tx_update(transaction, user).await?;
131/// transaction.commit().await?;
132/// # Ok(())
133/// # }
134/// ```
135pub async fn tx_update<T>(
136 transaction: Transaction<'_>,
137 entity: T,
138) -> Result<(Transaction<'_>, bool), Error>
139where
140 T: SqlQuery<T> + UpdateParams + Send + Sync + 'static,
141{
142 let sql = T::query();
143
144 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
145 let is_trace_enabled =
146 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
147
148 if is_trace_enabled {
149 println!("[PARSQL-TOKIO-POSTGRES-TX] Execute SQL: {}", sql);
150 }
151
152 let params = entity.params();
153 let result = transaction.execute(&sql, ¶ms).await?;
154 Ok((transaction, result > 0))
155}
156
157/// Deletes a record within a transaction.
158///
159/// # Arguments
160/// * `transaction` - An active transaction
161/// * `entity` - Data object containing delete conditions (must implement SqlQuery and SqlParams traits)
162///
163/// # Return Value
164/// * `Result<(Transaction<'_>, u64), Error>` - On success, returns the transaction and number of deleted records
165///
166/// # Example
167/// ```rust,no_run
168/// # use tokio_postgres::{NoTls, Error};
169/// # use parsql::tokio_postgres::transactional;
170/// # use parsql::macros::{Deletable, SqlParams};
171/// #
172/// #[derive(Deletable, SqlParams)]
173/// #[table("users")]
174/// #[where_clause("id = $")]
175/// struct DeleteUser {
176/// id: i64,
177/// }
178///
179/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
180/// # let (client, connection) = tokio_postgres::connect("", NoTls).await?;
181/// # tokio::spawn(async move { connection.await; });
182/// let user = DeleteUser { id: 1 };
183///
184/// let transaction = transactional::begin(&client).await?;
185/// let (transaction, deleted) = transactional::tx_delete(transaction, user).await?;
186/// transaction.commit().await?;
187/// # Ok(())
188/// # }
189/// ```
190pub async fn tx_delete<T>(
191 transaction: Transaction<'_>,
192 entity: T,
193) -> Result<(Transaction<'_>, u64), Error>
194where
195 T: SqlQuery<T> + SqlParams + Send + Sync + 'static,
196{
197 let sql = T::query();
198
199 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
200 let is_trace_enabled =
201 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
202
203 if is_trace_enabled {
204 println!("[PARSQL-TOKIO-POSTGRES-TX] Execute SQL: {}", sql);
205 }
206
207 let params = entity.params();
208 let result = transaction.execute(&sql, ¶ms).await?;
209 Ok((transaction, result))
210}
211
212/// Retrieves a single record within a transaction.
213///
214/// # Arguments
215/// * `transaction` - An active transaction
216/// * `params` - Data object containing query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
217///
218/// # Return Value
219/// * `Result<(Transaction<'_>, T), Error>` - On success, returns the transaction and the record
220///
221/// # Example
222/// ```rust,no_run
223/// # use tokio_postgres::{NoTls, Error};
224/// # use parsql::tokio_postgres::transactional;
225/// # use parsql::macros::{Queryable, FromRow, SqlParams};
226/// #
227/// #[derive(Queryable, FromRow, SqlParams, Debug)]
228/// #[table("users")]
229/// #[where_clause("id = $")]
230/// struct GetUser {
231/// id: i64,
232/// name: String,
233/// email: String,
234/// }
235///
236/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
237/// # let (client, connection) = tokio_postgres::connect("", NoTls).await?;
238/// # tokio::spawn(async move { connection.await; });
239/// let query = GetUser {
240/// id: 1,
241/// name: Default::default(),
242/// email: Default::default(),
243/// };
244///
245/// let transaction = transactional::begin(&client).await?;
246/// let (transaction, user) = transactional::tx_fetch(transaction, query).await?;
247/// transaction.commit().await?;
248/// # Ok(())
249/// # }
250/// ```
251pub async fn tx_fetch<T>(
252 transaction: Transaction<'_>,
253 params: T,
254) -> Result<(Transaction<'_>, T), Error>
255where
256 T: SqlQuery<T> + FromRow + SqlParams + Send + Sync + 'static,
257{
258 let sql = T::query();
259
260 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
261 let is_trace_enabled =
262 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
263
264 if is_trace_enabled {
265 println!("[PARSQL-TOKIO-POSTGRES-TX] Execute SQL: {}", sql);
266 }
267
268 let query_params = params.params();
269 let row = transaction.query_one(&sql, &query_params).await?;
270 let result = T::from_row(&row)?;
271 Ok((transaction, result))
272}
273
274/// Retrieves multiple records within a transaction.
275///
276/// # Arguments
277/// * `transaction` - An active transaction
278/// * `params` - Data object containing query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
279///
280/// # Return Value
281/// * `Result<(Transaction<'_>, Vec<T>), Error>` - On success, returns the transaction and a vector of records
282///
283/// # Example
284/// ```rust,no_run
285/// # use tokio_postgres::{NoTls, Error};
286/// # use parsql::tokio_postgres::transactional;
287/// # use parsql::macros::{Queryable, FromRow, SqlParams};
288/// #
289/// #[derive(Queryable, FromRow, SqlParams, Debug)]
290/// #[table("users")]
291/// #[where_clause("state = $")]
292/// struct GetActiveUsers {
293/// id: i64,
294/// name: String,
295/// email: String,
296/// state: i16,
297/// }
298///
299/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
300/// # let (client, connection) = tokio_postgres::connect("", NoTls).await?;
301/// # tokio::spawn(async move { connection.await; });
302/// let query = GetActiveUsers {
303/// id: 0,
304/// name: Default::default(),
305/// email: Default::default(),
306/// state: 1, // active users
307/// };
308///
309/// let transaction = transactional::begin(&client).await?;
310/// let (transaction, users) = transactional::tx_fetch_all(transaction, query).await?;
311/// transaction.commit().await?;
312/// # Ok(())
313/// # }
314/// ```
315pub async fn tx_fetch_all<T>(
316 transaction: Transaction<'_>,
317 params: T,
318) -> Result<(Transaction<'_>, Vec<T>), Error>
319where
320 T: SqlQuery<T> + FromRow + SqlParams + Send + Sync + 'static,
321{
322 let sql = T::query();
323
324 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
325 let is_trace_enabled =
326 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
327
328 if is_trace_enabled {
329 println!("[PARSQL-TOKIO-POSTGRES-TX] Execute SQL: {}", sql);
330 }
331
332 let query_params = params.params();
333 let rows = transaction.query(&sql, &query_params).await?;
334
335 let mut results = Vec::with_capacity(rows.len());
336 for row in rows {
337 results.push(T::from_row(&row)?);
338 }
339
340 Ok((transaction, results))
341}
342
343/// Retrieves a single record within a transaction.
344///
345/// # Deprecated
346/// This function has been renamed to `tx_fetch`. Please use `tx_fetch` instead.
347///
348/// # Arguments
349/// * `transaction` - An active transaction
350/// * `params` - Data object containing query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
351///
352/// # Return Value
353/// * `Result<(Transaction<'_>, T), Error>` - On success, returns the transaction and the record
354#[deprecated(
355 since = "0.2.0",
356 note = "Renamed to `tx_fetch`. Please use `tx_fetch` function instead."
357)]
358pub async fn tx_get<T>(
359 transaction: Transaction<'_>,
360 params: T,
361) -> Result<(Transaction<'_>, T), Error>
362where
363 T: SqlQuery<T> + FromRow + SqlParams + Send + Sync + 'static,
364{
365 tx_fetch(transaction, params).await
366}
367
368/// Retrieves multiple records within a transaction.
369///
370/// # Deprecated
371/// This function has been renamed to `tx_fetch_all`. Please use `tx_fetch_all` instead.
372///
373/// # Arguments
374/// * `transaction` - An active transaction
375/// * `params` - Data object containing query parameters (must implement SqlQuery, FromRow, and SqlParams traits)
376///
377/// # Return Value
378/// * `Result<(Transaction<'_>, Vec<T>), Error>` - On success, returns the transaction and a vector of records
379#[deprecated(
380 since = "0.2.0",
381 note = "Renamed to `tx_fetch_all`. Please use `tx_fetch_all` function instead."
382)]
383pub async fn tx_get_all<T>(
384 transaction: Transaction<'_>,
385 params: T,
386) -> Result<(Transaction<'_>, Vec<T>), Error>
387where
388 T: SqlQuery<T> + FromRow + SqlParams + Send + Sync + 'static,
389{
390 tx_fetch_all(transaction, params).await
391}
392
393/// Implementation of the CrudOps trait for Transactions
394///
395/// This implementation allows using the `CrudOps` trait methods directly on
396/// `Transaction<'a>` objects, similar to how they are used on `Client` objects.
397/// This provides a consistent API for both regular client operations and transaction operations.
398///
399/// # Examples
400///
401/// ```rust,no_run
402/// # use tokio_postgres::{NoTls, Error};
403/// # use parsql::tokio_postgres::{CrudOps};
404/// # use parsql::macros::{Insertable, SqlParams};
405/// #
406/// # #[derive(Insertable, SqlParams)]
407/// # #[table("users")]
408/// # struct InsertUser {
409/// # name: String,
410/// # email: String,
411/// # }
412/// #
413/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
414/// # let (mut client, connection) = tokio_postgres::connect("", NoTls).await?;
415/// # tokio::spawn(async move { connection.await; });
416/// let transaction = client.transaction().await?;
417///
418/// // Using CrudOps trait method directly on transaction
419/// let user = InsertUser {
420/// name: "John".to_string(),
421/// email: "john@example.com".to_string(),
422/// };
423///
424/// let rows_affected = transaction.insert(user).await?;
425/// println!("Rows affected: {}", rows_affected);
426///
427/// transaction.commit().await?;
428/// # Ok(())
429/// # }
430/// ```
431#[async_trait::async_trait]
432impl<'a> CrudOps for Transaction<'a> {
433 async fn insert<T, P: for<'b> FromSql<'b> + Send + Sync>(&self, entity: T) -> Result<P, Error>
434 where
435 T: SqlCommand + SqlParams + Send + Sync + 'static,
436 {
437 insert(self, entity).await
438 }
439
440 async fn update<T>(&self, entity: T) -> Result<bool, Error>
441 where
442 T: SqlCommand + UpdateParams + Send + Sync + 'static,
443 {
444 update(self, entity).await
445 }
446
447 async fn delete<T>(&self, entity: T) -> Result<u64, Error>
448 where
449 T: SqlCommand + SqlParams + Send + Sync + 'static,
450 {
451 delete(self, entity).await
452 }
453
454 async fn fetch<P, R>(&self, params: P) -> Result<R, Error>
455 where
456 P: SqlQuery<R> + SqlParams + Send + Sync + 'static,
457 R: FromRow + Send + Sync + 'static,
458 {
459 fetch(self, params).await
460 }
461
462 async fn fetch_all<P, R>(&self, params: P) -> Result<Vec<R>, Error>
463 where
464 P: SqlQuery<R> + SqlParams + Send + Sync + 'static,
465 R: FromRow + Send + Sync + 'static,
466 {
467 fetch_all(self, params).await
468 }
469
470 async fn select<T, F, R>(&self, entity: T, to_model: F) -> Result<R, Error>
471 where
472 T: SqlQuery<T> + SqlParams + Send + Sync + 'static,
473 F: Fn(&Row) -> Result<R, Error> + Send + Sync + 'static,
474 R: Send + 'static,
475 {
476 select(self, entity, to_model).await
477 }
478
479 async fn select_all<T, F, R>(&self, entity: T, to_model: F) -> Result<Vec<R>, Error>
480 where
481 T: SqlQuery<T> + SqlParams + Send + Sync + 'static,
482 F: Fn(&Row) -> R + Send + Sync + 'static,
483 R: Send + 'static,
484 {
485 select_all(self, entity, to_model).await
486 }
487}
488
489/// # insert
490///
491/// Inserts a new record into the database within a transaction.
492///
493/// ## Parameters
494/// - `transaction`: Transaction object
495/// - `entity`: Data object to be inserted (must implement SqlCommand and SqlParams traits)
496///
497/// ## Return Value
498/// - `Result<P, Error>`: On success, returns the generated primary key; on failure, returns Error
499pub async fn insert<T, P: for<'a> FromSql<'a> + Send + Sync>(
500 transaction: &Transaction<'_>,
501 entity: T,
502) -> Result<P, Error>
503where
504 T: SqlCommand + SqlParams + Send + Sync + 'static,
505{
506 let sql = T::query();
507
508 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
509 let is_trace_enabled =
510 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
511
512 if is_trace_enabled {
513 println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
514 }
515
516 let params = entity.params();
517 let row = transaction.query_one(&sql, ¶ms).await?;
518 row.try_get::<_, P>(0)
519}
520
521/// # update
522///
523/// Updates an existing record in the database within a transaction.
524///
525/// ## Parameters
526/// - `transaction`: Transaction object
527/// - `entity`: Data object containing update information (must implement SqlCommand and UpdateParams traits)
528///
529/// ## Return Value
530/// - `Result<bool, Error>`: On success, returns true if updated; on failure, returns Error
531pub async fn update<T>(transaction: &Transaction<'_>, entity: T) -> Result<bool, Error>
532where
533 T: SqlCommand + UpdateParams + Send + Sync + 'static,
534{
535 let sql = T::query();
536
537 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
538 let is_trace_enabled =
539 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
540
541 if is_trace_enabled {
542 println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
543 }
544
545 let params = entity.params();
546 let result = transaction.execute(&sql, ¶ms).await?;
547 Ok(result > 0)
548}
549
550/// # delete
551///
552/// Deletes a record from the database within a transaction.
553///
554/// ## Parameters
555/// - `transaction`: Transaction object
556/// - `entity`: Data object containing delete conditions (must implement SqlCommand and SqlParams traits)
557///
558/// ## Return Value
559/// - `Result<u64, Error>`: On success, returns the number of deleted records; on failure, returns Error
560pub async fn delete<T>(transaction: &Transaction<'_>, entity: T) -> Result<u64, Error>
561where
562 T: SqlCommand + SqlParams + Send + Sync + 'static,
563{
564 let sql = T::query();
565
566 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
567 let is_trace_enabled =
568 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
569
570 if is_trace_enabled {
571 println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
572 }
573
574 let params = entity.params();
575 transaction.execute(&sql, ¶ms).await
576}
577
578/// # fetch
579///
580/// Retrieves a single record from the database within a transaction.
581///
582/// ## Parameters
583/// - `transaction`: Transaction object
584/// - `params`: Query parameters (must implement SqlQuery and SqlParams traits)
585///
586/// ## Return Value
587/// - `Result<R, Error>`: On success, returns the retrieved record; on failure, returns Error
588pub async fn fetch<P, R>(transaction: &Transaction<'_>, params: P) -> Result<R, Error>
589where
590 P: SqlQuery<R> + SqlParams + Send + Sync + 'static,
591 R: FromRow + Send + Sync + 'static,
592{
593 let sql = P::query();
594
595 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
596 let is_trace_enabled =
597 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
598
599 if is_trace_enabled {
600 println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
601 }
602
603 let query_params = params.params();
604 let row = transaction.query_one(&sql, &query_params).await?;
605 R::from_row(&row)
606}
607
608/// # fetch_all
609///
610/// Retrieves multiple records from the database within a transaction.
611///
612/// ## Parameters
613/// - `transaction`: Transaction object
614/// - `params`: Query parameters (must implement SqlQuery and SqlParams traits)
615///
616/// ## Return Value
617/// - `Result<Vec<R>, Error>`: On success, returns a vector of records; on failure, returns Error
618pub async fn fetch_all<P, R>(transaction: &Transaction<'_>, params: P) -> Result<Vec<R>, Error>
619where
620 P: SqlQuery<R> + SqlParams + Send + Sync + 'static,
621 R: FromRow + Send + Sync + 'static,
622{
623 let sql = P::query();
624
625 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
626 let is_trace_enabled =
627 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
628
629 if is_trace_enabled {
630 println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
631 }
632
633 let query_params = params.params();
634 let rows = transaction.query(&sql, &query_params).await?;
635
636 let mut results = Vec::with_capacity(rows.len());
637 for row in rows {
638 results.push(R::from_row(&row)?);
639 }
640
641 Ok(results)
642}
643
644/// # select
645///
646/// Retrieves a single record from the database within a transaction using a custom transformation function.
647///
648/// ## Parameters
649/// - `transaction`: Transaction object
650/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
651/// - `to_model`: Function to convert a Row object to the target object type
652///
653/// ## Return Value
654/// - `Result<R, Error>`: On success, returns the transformed object; on failure, returns Error
655pub async fn select<T, F, R>(
656 transaction: &Transaction<'_>,
657 entity: T,
658 to_model: F,
659) -> Result<R, Error>
660where
661 T: SqlQuery<T> + SqlParams + Send + Sync + 'static,
662 F: Fn(&Row) -> Result<R, Error> + Send + Sync + 'static,
663 R: Send + 'static,
664{
665 let sql = T::query();
666
667 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
668 let is_trace_enabled =
669 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
670
671 if is_trace_enabled {
672 println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
673 }
674
675 let params = entity.params();
676 let row = transaction.query_one(&sql, ¶ms).await?;
677 to_model(&row)
678}
679
680/// # select_all
681///
682/// Retrieves multiple records from the database within a transaction using a custom transformation function.
683///
684/// ## Parameters
685/// - `transaction`: Transaction object
686/// - `entity`: Query parameter object (must implement SqlQuery and SqlParams traits)
687/// - `to_model`: Function to convert a Row object to the target object type
688///
689/// ## Return Value
690/// - `Result<Vec<R>, Error>`: On success, returns a vector of transformed objects; on failure, returns Error
691pub async fn select_all<T, F, R>(
692 transaction: &Transaction<'_>,
693 entity: T,
694 to_model: F,
695) -> Result<Vec<R>, Error>
696where
697 T: SqlQuery<T> + SqlParams + Send + Sync + 'static,
698 F: Fn(&Row) -> R + Send + Sync + 'static,
699 R: Send + 'static,
700{
701 let sql = T::query();
702
703 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
704 let is_trace_enabled =
705 *TRACE_ENABLED.get_or_init(|| std::env::var("PARSQL_TRACE").unwrap_or_default() == "1");
706
707 if is_trace_enabled {
708 println!("[PARSQL-TOKIO-POSTGRES] Execute SQL: {}", sql);
709 }
710
711 let params = entity.params();
712 let rows = transaction.query(&sql, ¶ms).await?;
713
714 let mut results = Vec::with_capacity(rows.len());
715 for row in rows {
716 results.push(to_model(&row));
717 }
718
719 Ok(results)
720}