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(¶m)?;
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(¶m)?;
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(¶m, |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(¶m, |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, ¶m)?;
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, ¶m)?;
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/// ¶m,
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}