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, &params).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, &params).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, &params).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, &params).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, &params).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, &params).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, &params).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, &params).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}