Skip to main content

sql_middleware/sqlite/connection/
dml.rs

1use std::sync::Arc;
2
3use crate::adapters::params::convert_params;
4use crate::middleware::{ConversionMode, SqlMiddlewareDbError, StatementCacheMode};
5use crate::types::RowValues;
6
7use super::{SqliteConnection, run_blocking};
8use crate::sqlite::config::SqliteManager;
9use crate::sqlite::params::Params;
10use bb8::PooledConnection;
11
12impl SqliteConnection {
13    /// Execute a batch of statements; wraps in a transaction when not already inside one.
14    ///
15    /// # Errors
16    /// Returns `SqlMiddlewareDbError` if acquiring the `SQLite` guard or executing the batch fails.
17    pub async fn execute_batch(&mut self, query: &str) -> Result<(), SqlMiddlewareDbError> {
18        self.ensure_not_in_tx("execute batch")?;
19        let sql_owned = query.to_owned();
20        run_blocking(self.conn_handle(), move |guard| {
21            if guard.is_autocommit() {
22                let tx = guard
23                    .transaction()
24                    .map_err(SqlMiddlewareDbError::SqliteError)?;
25                tx.execute_batch(&sql_owned)
26                    .map_err(SqlMiddlewareDbError::SqliteError)?;
27                tx.commit().map_err(SqlMiddlewareDbError::SqliteError)
28            } else {
29                guard
30                    .execute_batch(&sql_owned)
31                    .map_err(SqlMiddlewareDbError::SqliteError)
32            }
33        })
34        .await
35    }
36
37    /// Execute a DML statement and return rows affected.
38    ///
39    /// # Errors
40    /// Returns `SqlMiddlewareDbError` if preparing or executing the statement fails.
41    pub async fn execute_dml(
42        &mut self,
43        query: &str,
44        params: &[rusqlite::types::Value],
45        statement_cache_mode: StatementCacheMode,
46    ) -> Result<usize, SqlMiddlewareDbError> {
47        self.ensure_not_in_tx("execute dml")?;
48        let sql_owned = query.to_owned();
49        let params_owned = params.to_vec();
50        run_blocking(self.conn_handle(), move |guard| {
51            let refs: Vec<&dyn rusqlite::ToSql> = params_owned
52                .iter()
53                .map(|v| v as &dyn rusqlite::ToSql)
54                .collect();
55            match statement_cache_mode {
56                StatementCacheMode::Cached => {
57                    let mut stmt = guard
58                        .prepare_cached(&sql_owned)
59                        .map_err(SqlMiddlewareDbError::SqliteError)?;
60                    stmt.execute(&refs[..])
61                        .map_err(SqlMiddlewareDbError::SqliteError)
62                }
63                StatementCacheMode::Uncached => {
64                    let mut stmt = guard
65                        .prepare(&sql_owned)
66                        .map_err(SqlMiddlewareDbError::SqliteError)?;
67                    stmt.execute(&refs[..])
68                        .map_err(SqlMiddlewareDbError::SqliteError)
69                }
70            }
71        })
72        .await
73    }
74
75    /// Execute a DML statement inside an open transaction.
76    ///
77    /// # Errors
78    /// Returns `SqlMiddlewareDbError` if the guard fails or the transaction is not active.
79    pub async fn execute_dml_in_tx(
80        &mut self,
81        query: &str,
82        params: &[rusqlite::types::Value],
83    ) -> Result<usize, SqlMiddlewareDbError> {
84        if !self.in_transaction {
85            return Err(SqlMiddlewareDbError::ExecutionError(
86                "SQLite transaction not active".into(),
87            ));
88        }
89        let sql_owned = query.to_owned();
90        let params_owned = params.to_vec();
91        run_blocking(self.conn_handle(), move |guard| {
92            let mut stmt = guard
93                .prepare_cached(&sql_owned)
94                .map_err(SqlMiddlewareDbError::SqliteError)?;
95            let refs: Vec<&dyn rusqlite::ToSql> = params_owned
96                .iter()
97                .map(|v| v as &dyn rusqlite::ToSql)
98                .collect();
99            let affected = stmt
100                .execute(&refs[..])
101                .map_err(SqlMiddlewareDbError::SqliteError)?;
102            Ok(affected)
103        })
104        .await
105    }
106
107    /// Execute a batch inside an open transaction without implicit commit.
108    ///
109    /// # Errors
110    /// Returns `SqlMiddlewareDbError` if the guard fails or the transaction is not active.
111    pub async fn execute_batch_in_tx(&mut self, sql: &str) -> Result<(), SqlMiddlewareDbError> {
112        if !self.in_transaction {
113            return Err(SqlMiddlewareDbError::ExecutionError(
114                "SQLite transaction not active".into(),
115            ));
116        }
117        let sql_owned = sql.to_owned();
118        run_blocking(self.conn_handle(), move |guard| {
119            guard
120                .execute_batch(&sql_owned)
121                .map_err(SqlMiddlewareDbError::SqliteError)
122        })
123        .await
124    }
125}
126
127/// Adapter for query builder dml (typed-sqlite target).
128///
129/// # Errors
130/// Returns `SqlMiddlewareDbError` if converting parameters or executing the statement fails.
131pub async fn dml(
132    conn: &mut PooledConnection<'static, SqliteManager>,
133    query: &str,
134    params: &[RowValues],
135) -> Result<usize, SqlMiddlewareDbError> {
136    dml_with_cache_mode(conn, query, params, StatementCacheMode::Cached).await
137}
138
139/// Adapter for query builder dml with explicit statement cache mode.
140///
141/// # Errors
142/// Returns `SqlMiddlewareDbError` if converting parameters or executing the statement fails.
143pub async fn dml_with_cache_mode(
144    conn: &mut PooledConnection<'static, SqliteManager>,
145    query: &str,
146    params: &[RowValues],
147    statement_cache_mode: StatementCacheMode,
148) -> Result<usize, SqlMiddlewareDbError> {
149    let converted = convert_params::<Params>(params, ConversionMode::Execute)?.0;
150    let sql_owned = query.to_owned();
151    let params_owned = converted.clone();
152    let handle = Arc::clone(&*conn);
153    run_blocking(handle, move |guard| {
154        let refs: Vec<&dyn rusqlite::ToSql> = params_owned
155            .iter()
156            .map(|v| v as &dyn rusqlite::ToSql)
157            .collect();
158        match statement_cache_mode {
159            StatementCacheMode::Cached => {
160                let mut stmt = guard
161                    .prepare_cached(&sql_owned)
162                    .map_err(SqlMiddlewareDbError::SqliteError)?;
163                stmt.execute(&refs[..])
164                    .map_err(SqlMiddlewareDbError::SqliteError)
165            }
166            StatementCacheMode::Uncached => {
167                let mut stmt = guard
168                    .prepare(&sql_owned)
169                    .map_err(SqlMiddlewareDbError::SqliteError)?;
170                stmt.execute(&refs[..])
171                    .map_err(SqlMiddlewareDbError::SqliteError)
172            }
173        }
174    })
175    .await
176}