parsql_sqlite/
transactional_ops.rs

1//! Transaction operations for SQLite
2//!
3//! This module provides functions for performing CRUD operations within a transaction.
4
5use crate::traits::{CrudOps, FromRow, SqlCommand, SqlParams, SqlQuery, UpdateParams};
6use rusqlite::{types::FromSql, Connection, Error, Row, ToSql, Transaction};
7
8/// CrudOps trait implementasyonu Transaction<'_> için.
9impl<'a> CrudOps for Transaction<'a> {
10    /// Inserts a record into the database and returns the number of rows affected.
11    /// This function is an extension to the Transaction struct and is available when the CrudOps trait is in scope.
12    ///
13    /// # Arguments
14    /// * `entity` - A struct that implements Insertable and SqlParams traits
15    ///
16    /// # Returns
17    /// * `Result<usize, Error>` - Number of affected rows or an error
18    ///
19    /// # Example
20    /// ```rust,no_run
21    /// use rusqlite::{Connection, Result};
22    /// use parsql::sqlite::CrudOps;
23    /// use parsql::sqlite::transactional;
24    /// use parsql::macros::{Insertable, SqlParams};
25    ///
26    /// #[derive(Insertable, SqlParams)]
27    /// #[table("users")]
28    /// struct InsertUser {
29    ///     name: String,
30    ///     email: String,
31    /// }
32    ///
33    /// fn main() -> Result<()> {
34    ///     let conn = Connection::open("test.db")?;
35    ///     let tx = transactional::begin(&conn)?;
36    ///     
37    ///     let user = InsertUser {
38    ///         name: "John".to_string(),
39    ///         email: "john@example.com".to_string(),
40    ///     };
41    ///     
42    ///     let rows_affected = tx.insert(user)?;
43    ///     
44    ///     tx.commit()?;
45    ///     Ok(())
46    /// }
47    /// ```
48    fn insert<T: SqlCommand + SqlParams, P: FromSql + Send + Sync>(
49        &self,
50        entity: T,
51    ) -> Result<P, Error> {
52        let sql = T::query();
53        let params_vec = entity.params();
54        let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
55        let mut stmt = self.prepare(&sql)?;
56        let mut rows = stmt.query(param_refs.as_slice())?;
57        if let Some(row) = rows.next()? {
58            row.get(0)
59        } else {
60            Err(Error::QueryReturnedNoRows)
61        }
62    }
63
64    /// Updates a record in the database and returns the number of rows affected.
65    /// This function is an extension to the Transaction struct and is available when the CrudOps trait is in scope.
66    ///
67    /// # Arguments
68    /// * `entity` - A struct that implements Updateable and UpdateParams traits
69    ///
70    /// # Returns
71    /// * `Result<usize, Error>` - Number of affected rows or an error
72    ///
73    /// # Example
74    /// ```rust,no_run
75    /// use rusqlite::{Connection, Result};
76    /// use parsql::sqlite::CrudOps;
77    /// use parsql::sqlite::transactional;
78    /// use parsql::macros::{Updateable, UpdateParams};
79    ///
80    /// #[derive(Updateable, UpdateParams)]
81    /// #[table("users")]
82    /// #[update("name, email")]
83    /// #[where_clause("id = ?")]
84    /// struct UpdateUser {
85    ///     id: i64,
86    ///     name: String,
87    ///     email: String,
88    /// }
89    ///
90    /// fn main() -> Result<()> {
91    ///     let conn = Connection::open("test.db")?;
92    ///     let tx = transactional::begin(&conn)?;
93    ///     
94    ///     let user = UpdateUser {
95    ///         id: 1,
96    ///         name: "John Doe".to_string(),
97    ///         email: "john.doe@example.com".to_string(),
98    ///     };
99    ///     
100    ///     let rows_affected = tx.update(user)?;
101    ///     
102    ///     tx.commit()?;
103    ///     Ok(())
104    /// }
105    /// ```
106    fn update<T: SqlCommand + UpdateParams>(&self, entity: T) -> Result<usize, Error> {
107        let sql = T::query();
108        let params_vec = entity.params();
109        let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
110        self.execute(&sql, param_refs.as_slice())
111    }
112
113    /// Deletes a record from the database and returns the number of rows affected.
114    /// This function is an extension to the Transaction struct and is available when the CrudOps trait is in scope.
115    ///
116    /// # Arguments
117    /// * `entity` - A struct that implements Deletable and SqlParams traits
118    ///
119    /// # Returns
120    /// * `Result<usize, Error>` - Number of affected rows or an error
121    ///
122    /// # Example
123    /// ```rust,no_run
124    /// use rusqlite::{Connection, Result};
125    /// use parsql::sqlite::CrudOps;
126    /// use parsql::sqlite::transactional;
127    /// use parsql::macros::{Deletable, SqlParams};
128    ///
129    /// #[derive(Deletable, SqlParams)]
130    /// #[table("users")]
131    /// #[where_clause("id = ?")]
132    /// struct DeleteUser {
133    ///     id: i64,
134    /// }
135    ///
136    /// fn main() -> Result<()> {
137    ///     let conn = Connection::open("test.db")?;
138    ///     let tx = transactional::begin(&conn)?;
139    ///     
140    ///     let user = DeleteUser { id: 1 };
141    ///     
142    ///     let rows_affected = tx.delete(user)?;
143    ///     
144    ///     tx.commit()?;
145    ///     Ok(())
146    /// }
147    /// ```
148    fn delete<T: SqlCommand + SqlParams>(&self, entity: T) -> Result<usize, Error> {
149        let sql = T::query();
150        let params_vec = entity.params();
151        let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
152        self.execute(&sql, param_refs.as_slice())
153    }
154
155    /// Retrieves a single record from the database and converts it to a struct.
156    /// This function is an extension to the Transaction struct and is available when the CrudOps trait is in scope.
157    ///
158    /// # Arguments
159    /// * `entity` - A struct that implements Queryable, SqlParams, and FromRow traits
160    ///
161    /// # Returns
162    /// * `Result<T, Error>` - The retrieved record as a struct or an error
163    ///
164    /// # Example
165    /// ```rust,no_run
166    /// use rusqlite::{Connection, Result};
167    /// use parsql::sqlite::CrudOps;
168    /// use parsql::sqlite::transactional;
169    /// use parsql::macros::{Queryable, SqlParams, FromRow};
170    ///
171    /// #[derive(Queryable, SqlParams, FromRow)]
172    /// #[table("users")]
173    /// #[where_clause("id = ?")]
174    /// struct GetUser {
175    ///     id: i64,
176    ///     name: String,
177    ///     email: String,
178    /// }
179    ///
180    /// fn main() -> Result<()> {
181    ///     let conn = Connection::open("test.db")?;
182    ///     let tx = transactional::begin(&conn)?;
183    ///     
184    ///     let param = GetUser {
185    ///         id: 1,
186    ///         name: String::new(),
187    ///         email: String::new(),
188    ///     };
189    ///     
190    ///     let user = tx.fetch(&param)?;
191    ///     
192    ///     tx.commit()?;
193    ///     println!("Found user: {} - {}", user.name, user.email);
194    ///     Ok(())
195    /// }
196    /// ```
197    fn fetch<P, R>(&self, params: &P) -> Result<R, Error>
198    where
199        P: SqlQuery<R> + SqlParams,
200        R: FromRow,
201    {
202        let sql = P::query();
203        let params_vec = params.params();
204        let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
205        let mut stmt = self.prepare(&sql)?;
206        let mut rows = stmt.query(param_refs.as_slice())?;
207        if let Some(row) = rows.next()? {
208            let result = R::from_row(row)?;
209            Ok(result)
210        } else {
211            Err(Error::QueryReturnedNoRows)
212        }
213    }
214
215    /// Retrieves multiple records from the database and converts them to a vector of structs.
216    /// This function is an extension to the Transaction struct and is available when the CrudOps trait is in scope.
217    ///
218    /// # Arguments
219    /// * `entity` - A struct that implements Queryable, SqlParams, and FromRow traits
220    ///
221    /// # Returns
222    /// * `Result<Vec<T>, Error>` - A vector of retrieved records as structs or an error
223    ///
224    /// # Example
225    /// ```rust,no_run
226    /// use rusqlite::{Connection, Result};
227    /// use parsql::sqlite::CrudOps;
228    /// use parsql::sqlite::transactional;
229    /// use parsql::macros::{Queryable, SqlParams, FromRow};
230    ///
231    /// #[derive(Queryable, SqlParams, FromRow)]
232    /// #[table("users")]
233    /// #[where_clause("email LIKE ?")]
234    /// struct GetUsers {
235    ///     id: i64,
236    ///     name: String,
237    ///     email: String,
238    /// }
239    ///
240    /// fn main() -> Result<()> {
241    ///     let conn = Connection::open("test.db")?;
242    ///     let tx = transactional::begin(&conn)?;
243    ///     
244    ///     let param = GetUsers {
245    ///         id: 0,
246    ///         name: String::new(),
247    ///         email: "%@example.com".to_string(),
248    ///     };
249    ///     
250    ///     let users = tx.fetch_all(&param)?;
251    ///     
252    ///     tx.commit()?;
253    ///     println!("Found {} users", users.len());
254    ///     Ok(())
255    /// }
256    /// ```
257    fn fetch_all<P, R>(&self, params: &P) -> Result<Vec<R>, Error>
258    where
259        P: SqlQuery<R> + SqlParams,
260        R: FromRow,
261    {
262        let sql = P::query();
263        let params_vec = params.params();
264        let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
265        let mut stmt = self.prepare(&sql)?;
266        let rows = stmt.query_map(param_refs.as_slice(), |row| R::from_row(row))?;
267        let mut results = Vec::new();
268        for row_result in rows {
269            results.push(row_result?);
270        }
271        Ok(results)
272    }
273
274    /// Executes a custom SELECT query and transforms the result using a provided function.
275    /// This function is an extension to the Transaction struct and is available when the CrudOps trait is in scope.
276    ///
277    /// # Arguments
278    /// * `entity` - Data object containing query parameters
279    /// * `to_model` - Function to transform a row into a value
280    ///
281    /// # Returns
282    /// * `Result<R, Error>` - The transformed value or an error
283    ///
284    /// # Example
285    /// ```rust,no_run
286    /// use rusqlite::{Connection, Result};
287    /// use parsql::sqlite::CrudOps;
288    /// use parsql::sqlite::transactional;
289    /// use parsql::macros::{Queryable, SqlParams};
290    ///
291    /// #[derive(Queryable, SqlParams)]
292    /// #[table("users")]
293    /// #[where_clause("email LIKE ?")]
294    /// struct CountUsers {
295    ///     email: String,
296    /// }
297    ///
298    /// fn main() -> Result<()> {
299    ///     let conn = Connection::open("test.db")?;
300    ///     let tx = transactional::begin(&conn)?;
301    ///     
302    ///     let param = CountUsers {
303    ///         email: "%example.com".to_string(),
304    ///     };
305    ///     
306    ///     let count: i64 = tx.select(&param, |row| row.get(0))?;
307    ///     
308    ///     tx.commit()?;
309    ///     println!("Number of users: {}", count);
310    ///     Ok(())
311    /// }
312    /// ```
313    fn select<T: SqlQuery<T> + SqlParams, F, R>(&self, entity: &T, to_model: F) -> Result<R, Error>
314    where
315        F: Fn(&rusqlite::Row) -> Result<R, Error>,
316    {
317        let sql = T::query();
318        let params_vec = entity.params();
319        let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
320        let mut stmt = self.prepare(&sql)?;
321        stmt.query_row(param_refs.as_slice(), to_model)
322    }
323
324    /// Executes a custom SELECT query and transforms all results using a provided function.
325    /// This function is an extension to the Transaction struct and is available when the CrudOps trait is in scope.
326    ///
327    /// # Arguments
328    /// * `entity` - Data object containing query parameters
329    /// * `to_model` - Function to transform rows into values
330    ///
331    /// # Returns
332    /// * `Result<Vec<R>, Error>` - A vector of transformed values or an error
333    ///
334    /// # Example
335    /// ```rust,no_run
336    /// use rusqlite::{Connection, Result};
337    /// use parsql::sqlite::CrudOps;
338    /// use parsql::sqlite::transactional;
339    /// use parsql::macros::{Queryable, SqlParams};
340    ///
341    /// #[derive(Queryable, SqlParams)]
342    /// #[table("users")]
343    /// #[where_clause("email LIKE ?")]
344    /// struct GetUserNames {
345    ///     email: String,
346    /// }
347    ///
348    /// fn main() -> Result<()> {
349    ///     let conn = Connection::open("test.db")?;
350    ///     let tx = transactional::begin(&conn)?;
351    ///     
352    ///     let param = GetUserNames {
353    ///         email: "%example.com".to_string(),
354    ///     };
355    ///     
356    ///     let names: Vec<String> = tx.select_all(&param, |row| row.get(0))?;
357    ///     
358    ///     tx.commit()?;
359    ///     for name in names {
360    ///         println!("User name: {}", name);
361    ///     }
362    ///     Ok(())
363    /// }
364    /// ```
365    fn select_all<T: SqlQuery<T> + SqlParams, F, R>(
366        &self,
367        entity: &T,
368        to_model: F,
369    ) -> Result<Vec<R>, Error>
370    where
371        F: Fn(&rusqlite::Row) -> Result<R, Error>,
372    {
373        let sql = T::query();
374        let params_vec = entity.params();
375        let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
376        let mut stmt = self.prepare(&sql)?;
377        let rows = stmt.query_map(param_refs.as_slice(), to_model)?;
378        let mut results = Vec::new();
379        for row in rows {
380            results.push(row?);
381        }
382        Ok(results)
383    }
384}
385
386/// Begins a new transaction.
387///
388/// # Arguments
389/// * `conn` - SQLite connection
390///
391/// # Returns
392/// * `Result<Transaction<'_>, Error>` - Transaction or an error
393///
394/// # Example
395/// ```rust,no_run
396/// use rusqlite::{Connection, Result};
397/// use parsql::sqlite::transactional;
398///
399/// fn main() -> Result<()> {
400///     let conn = Connection::open("test.db")?;
401///     let tx = transactional::begin(&conn)?;
402///     // Perform operations within the transaction
403///     tx.commit()?;
404///     Ok(())
405/// }
406/// ```
407pub fn begin(conn: &Connection) -> Result<Transaction<'_>, Error> {
408    conn.unchecked_transaction()
409}
410
411/// Inserts a record into the database within a transaction.
412///
413/// # Arguments
414/// * `tx` - Transaction
415/// * `entity` - A struct that implements Insertable and SqlParams traits
416///
417/// # Returns
418/// * `Result<(Transaction<'_>, usize), Error>` - Transaction and number of affected rows or an error
419///
420/// # Example
421/// ```rust,no_run
422/// use rusqlite::{Connection, Result};
423/// use parsql::sqlite::transactional;
424/// use parsql::macros::{Insertable, SqlParams};
425///
426/// #[derive(Insertable, SqlParams)]
427/// #[table("users")]
428/// struct InsertUser {
429///     name: String,
430///     email: String,
431/// }
432///
433/// fn main() -> Result<()> {
434///     let conn = Connection::open("test.db")?;
435///     let tx = transactional::begin(&conn)?;
436///     
437///     let user = InsertUser {
438///         name: "John".to_string(),
439///         email: "john@example.com".to_string(),
440///     };
441///     
442///     let (tx, rows_affected) = transactional::tx_insert(tx, user)?;
443///     
444///     tx.commit()?;
445///     Ok(())
446/// }
447/// ```
448pub fn tx_insert<'a, T: SqlCommand + SqlParams, P: FromSql + Send + Sync>(
449    tx: &mut Transaction<'a>,
450    entity: T,
451) -> Result<P, Error> {
452    tx.insert(entity)
453}
454
455/// Updates a record in the database within a transaction.
456///
457/// # Arguments
458/// * `tx` - Transaction
459/// * `entity` - A struct that implements Updateable and UpdateParams traits
460///
461/// # Returns
462/// * `Result<(Transaction<'_>, usize), Error>` - Transaction and number of affected rows or an error
463///
464/// # Example
465/// ```rust,no_run
466/// use rusqlite::{Connection, Result};
467/// use parsql::sqlite::transactional;
468/// use parsql::macros::{Updateable, UpdateParams};
469///
470/// #[derive(Updateable, UpdateParams)]
471/// #[table("users")]
472/// #[update("name, email")]
473/// #[where_clause("id = ?")]
474/// struct UpdateUser {
475///     id: i64,
476///     name: String,
477///     email: String,
478/// }
479///
480/// fn main() -> Result<()> {
481///     let conn = Connection::open("test.db")?;
482///     let tx = transactional::begin(&conn)?;
483///     
484///     let user = UpdateUser {
485///         id: 1,
486///         name: "John Doe".to_string(),
487///         email: "john.doe@example.com".to_string(),
488///     };
489///     
490///     let (tx, rows_affected) = transactional::tx_update(tx, user)?;
491///     
492///     tx.commit()?;
493///     Ok(())
494/// }
495/// ```
496pub fn tx_update<'a, T: SqlCommand + UpdateParams>(
497    tx: Transaction<'a>,
498    entity: T,
499) -> Result<(Transaction<'a>, usize), Error> {
500    let result = tx.update(entity)?;
501    Ok((tx, result))
502}
503
504/// Deletes a record from the database within a transaction.
505///
506/// # Arguments
507/// * `tx` - Transaction
508/// * `entity` - A struct that implements Deletable and SqlParams traits
509///
510/// # Returns
511/// * `Result<(Transaction<'_>, usize), Error>` - Transaction and number of affected rows or an error
512///
513/// # Example
514/// ```rust,no_run
515/// use rusqlite::{Connection, Result};
516/// use parsql::sqlite::transactional;
517/// use parsql::macros::{Deletable, SqlParams};
518///
519/// #[derive(Deletable, SqlParams)]
520/// #[table("users")]
521/// #[where_clause("id = ?")]
522/// struct DeleteUser {
523///     id: i64,
524/// }
525///
526/// fn main() -> Result<()> {
527///     let conn = Connection::open("test.db")?;
528///     let tx = transactional::begin(&conn)?;
529///     
530///     let user = DeleteUser { id: 1 };
531///     
532///     let (tx, rows_affected) = transactional::tx_delete(tx, user)?;
533///     
534///     tx.commit()?;
535///     Ok(())
536/// }
537/// ```
538pub fn tx_delete<'a, T: SqlCommand + SqlParams>(
539    tx: Transaction<'a>,
540    entity: T,
541) -> Result<(Transaction<'a>, usize), Error> {
542    let result = tx.delete(entity)?;
543    Ok((tx, result))
544}
545
546/// Fetches a single record from the database within a transaction.
547///
548/// # Arguments
549/// * `tx` - Transaction
550/// * `entity` - A struct that implements Queryable, SqlParams, and FromRow traits
551///
552/// # Returns
553/// * `Result<(Transaction<'_>, T), Error>` - Transaction and the retrieved record or an error
554///
555/// # Example
556/// ```rust,no_run
557/// use rusqlite::{Connection, Result};
558/// use parsql::sqlite::transactional;
559/// use parsql::macros::{Queryable, SqlParams, FromRow};
560///
561/// #[derive(Queryable, SqlParams, FromRow)]
562/// #[table("users")]
563/// #[where_clause("id = ?")]
564/// struct GetUser {
565///     id: i64,
566///     name: String,
567///     email: String,
568/// }
569///
570/// fn main() -> Result<()> {
571///     let conn = Connection::open("test.db")?;
572///     let tx = transactional::begin(&conn)?;
573///     
574///     let param = GetUser {
575///         id: 1,
576///         name: String::new(),
577///         email: String::new(),
578///     };
579///     
580///     let (tx, user) = transactional::tx_fetch(tx, &param)?;
581///     
582///     println!("Found user: {} - {}", user.name, user.email);
583///     
584///     tx.commit()?;
585///     Ok(())
586/// }
587/// ```
588pub fn tx_fetch<'a, P, R>(tx: &mut Transaction<'a>, params: &P) -> Result<R, Error>
589where
590    P: SqlQuery<R> + SqlParams,
591    R: FromRow,
592{
593    let sql = P::query();
594    let params_vec = params.params();
595    let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
596    let mut stmt = tx.prepare(&sql)?;
597    let mut rows = stmt.query(param_refs.as_slice())?;
598    if let Some(row) = rows.next()? {
599        let result = R::from_row(row)?;
600        Ok(result)
601    } else {
602        Err(Error::QueryReturnedNoRows)
603    }
604}
605
606/// Fetches multiple records from the database within a transaction.
607///
608/// # Arguments
609/// * `tx` - Transaction
610/// * `entity` - A struct that implements Queryable, SqlParams, and FromRow traits
611///
612/// # Returns
613/// * `Result<(Transaction<'_>, Vec<T>), Error>` - Transaction and a vector of retrieved records or an error
614///
615/// # Example
616/// ```rust,no_run
617/// use rusqlite::{Connection, Result};
618/// use parsql::sqlite::transactional;
619/// use parsql::macros::{Queryable, SqlParams, FromRow};
620///
621/// #[derive(Queryable, SqlParams, FromRow)]
622/// #[table("users")]
623/// #[where_clause("active = ?")]
624/// struct GetActiveUsers {
625///     id: i64,
626///     name: String,
627///     email: String,
628///     active: i32,
629/// }
630///
631/// fn main() -> Result<()> {
632///     let conn = Connection::open("test.db")?;
633///     let tx = transactional::begin(&conn)?;
634///     
635///     let param = GetActiveUsers {
636///         id: 0,
637///         name: String::new(),
638///         email: String::new(),
639///         active: 1,
640///     };
641///     
642///     let (tx, users) = transactional::tx_fetch_all(tx, &param)?;
643///     
644///     println!("Found {} active users", users.len());
645///     
646///     tx.commit()?;
647///     Ok(())
648/// }
649/// ```
650pub fn tx_fetch_all<'a, P, R>(tx: &mut Transaction<'a>, params: &P) -> Result<Vec<R>, Error>
651where
652    P: SqlQuery<R> + SqlParams,
653    R: FromRow,
654{
655    let sql = P::query();
656    let params_vec = params.params();
657    let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
658    let mut stmt = tx.prepare(&sql)?;
659    let rows = stmt.query_map(param_refs.as_slice(), |row| R::from_row(row))?;
660    let mut results = Vec::new();
661    for row_result in rows {
662        results.push(row_result?);
663    }
664    Ok(results)
665}
666
667/// Gets a single record from the database within a transaction.
668///
669/// # Deprecated
670/// This function has been renamed to `tx_fetch`. Please use `tx_fetch` instead.
671///
672/// # Arguments
673/// * `tx` - Transaction
674/// * `entity` - A struct that implements Queryable, SqlParams, and FromRow traits
675///
676/// # Returns
677/// * `Result<T, Error>` - The retrieved record or an error
678#[deprecated(
679    since = "0.3.7",
680    note = "Renamed to `tx_fetch`. Please use `tx_fetch` function instead."
681)]
682pub fn tx_get<'a, T: SqlQuery<T> + FromRow + SqlParams>(
683    tx: &mut Transaction<'a>,
684    entity: &T,
685) -> Result<T, Error> {
686    tx_fetch(tx, entity)
687}
688
689/// Gets multiple records from the database within a transaction.
690///
691/// # Deprecated
692/// This function has been renamed to `tx_fetch_all`. Please use `tx_fetch_all` instead.
693///
694/// # Arguments
695/// * `tx` - Transaction
696/// * `entity` - A struct that implements Queryable, SqlParams, and FromRow traits
697///
698/// # Returns
699/// * `Result<Vec<T>, Error>` - A vector of retrieved records or an error
700#[deprecated(
701    since = "0.3.7",
702    note = "Renamed to `tx_fetch_all`. Please use `tx_fetch_all` function instead."
703)]
704pub fn tx_get_all<'a, T: SqlQuery<T> + FromRow + SqlParams>(
705    tx: &mut Transaction<'a>,
706    entity: &T,
707) -> Result<Vec<T>, Error> {
708    tx_fetch_all(tx, entity)
709}
710
711/// Execute a custom SELECT query within a transaction and transform the result.
712///
713/// # Arguments
714/// * `tx` - A transaction
715/// * `entity` - Data object containing query parameters
716/// * `to_model` - Function to transform a row into a value
717///
718/// # Returns
719/// * `Result<(Transaction, R)>` - The transaction and transformed value, or an error
720///
721/// # Example
722/// ```rust,no_run
723/// use rusqlite::{Connection, Result};
724/// use parsql::sqlite::transactional;
725/// use parsql::macros::{Queryable, SqlParams};
726///
727/// #[derive(Queryable, SqlParams)]
728/// #[table("users")]
729/// #[where_clause("email LIKE ?")]
730/// struct CountUsers {
731///     email: String,
732/// }
733///
734/// fn main() -> Result<()> {
735///     let conn = Connection::open("test.db")?;
736///     let tx = transactional::begin(&conn)?;
737///     
738///     let param = CountUsers {
739///         email: "%example.com".to_string(),
740///     };
741///     
742///     let (tx, count): (_, i64) = transactional::tx_select(
743///         tx,
744///         &param,
745///         |row| row.get(0)
746///     )?;
747///     
748///     println!("Number of users: {}", count);
749///     
750///     tx.commit()?;
751///     Ok(())
752/// }
753/// ```
754pub fn tx_select<'a, T: SqlQuery<T> + SqlParams, F, R>(
755    tx: &mut Transaction<'a>,
756    entity: &T,
757    to_model: F,
758) -> Result<R, Error>
759where
760    F: Fn(&rusqlite::Row) -> Result<R, Error>,
761{
762    let sql = T::query();
763    let params_vec = entity.params();
764    let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
765    let mut stmt = tx.prepare(&sql)?;
766    stmt.query_row(param_refs.as_slice(), to_model)
767}
768
769/// Execute a custom SELECT query within a transaction and transform all results.
770///
771/// # Arguments
772/// * `tx`
773pub fn tx_select_all<'a, T: SqlQuery<T> + SqlParams, F, R>(
774    tx: &mut Transaction<'a>,
775    entity: &T,
776    to_model: F,
777) -> Result<Vec<R>, Error>
778where
779    F: Fn(&rusqlite::Row) -> Result<R, Error>,
780{
781    let sql = T::query();
782    let params_vec = entity.params();
783    let param_refs: Vec<&dyn ToSql> = params_vec.iter().map(|p| *p as &dyn ToSql).collect();
784    let mut stmt = tx.prepare(&sql)?;
785    let rows = stmt.query_map(param_refs.as_slice(), to_model)?;
786    let mut results = Vec::new();
787    for row in rows {
788        results.push(row?);
789    }
790    Ok(results)
791}