1use std::sync::Arc;
4
5use prax_query::error::{QueryError, QueryResult};
6use prax_query::filter::FilterValue;
7use prax_query::row::FromRow;
8use prax_query::traits::{BoxFuture, Model, QueryEngine};
9use rusqlite::types::Value as SqlValue;
10use tokio_rusqlite::Connection as RusqliteConnection;
11use tracing::trace;
12
13use crate::connection::SqliteConnection;
14use crate::pool::SqlitePool;
15use crate::row_ref::SqliteRowRef;
16use crate::types::filter_value_to_sqlite;
17
18#[derive(Clone)]
56pub struct SqliteEngine {
57 pool: SqlitePool,
58 tx_conn: Option<Arc<SqliteConnection>>,
61}
62
63impl SqliteEngine {
64 pub fn new(pool: SqlitePool) -> Self {
66 Self {
67 pool,
68 tx_conn: None,
69 }
70 }
71
72 pub fn pool(&self) -> &SqlitePool {
74 &self.pool
75 }
76
77 async fn resolve_conn(&self) -> QueryResult<(RusqliteConnection, Option<SqliteConnection>)> {
88 if let Some(tx) = &self.tx_conn {
89 Ok((tx.inner().clone(), None))
90 } else {
91 let conn = self
92 .pool
93 .get()
94 .await
95 .map_err(|e| QueryError::connection(e.to_string()).with_source(e))?;
96 let handle = conn.inner().clone();
97 Ok((handle, Some(conn)))
98 }
99 }
100
101 fn bind(params: &[FilterValue]) -> Vec<SqlValue> {
102 params.iter().map(filter_value_to_sqlite).collect()
103 }
104
105 async fn query_rows<T: Model + FromRow>(
115 &self,
116 sql: String,
117 params: Vec<FilterValue>,
118 ) -> QueryResult<Vec<T>> {
119 trace!(sql = %sql, "sqlite query_rows");
120 let (handle, _guard) = self.resolve_conn().await?;
121 let bound = Self::bind(¶ms);
122 let snapshots: Vec<SqliteRowRef> = handle
123 .call(move |c| {
124 let mut stmt = c.prepare(&sql)?;
125 let refs: Vec<&dyn rusqlite::ToSql> =
126 bound.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
127 let mut rows = stmt.query(refs.as_slice())?;
128 let mut out = Vec::new();
129 while let Some(row) = rows.next()? {
130 out.push(SqliteRowRef::from_rusqlite(row).map_err(|e| {
131 rusqlite::Error::ToSqlConversionFailure(Box::new(std::io::Error::other(
132 e.to_string(),
133 )))
134 })?);
135 }
136 Ok(out)
137 })
138 .await
139 .map_err(|e| QueryError::database(e.to_string()).with_source(e))?;
140
141 snapshots
142 .into_iter()
143 .map(|r| {
144 T::from_row(&r).map_err(|e| {
145 let msg = e.to_string();
146 QueryError::deserialization(msg).with_source(e)
147 })
148 })
149 .collect()
150 }
151
152 async fn query_first_row<T: Model + FromRow>(
158 &self,
159 sql: String,
160 params: Vec<FilterValue>,
161 ) -> QueryResult<Option<T>> {
162 trace!(sql = %sql, "sqlite query_first_row");
163 let (handle, _guard) = self.resolve_conn().await?;
164 let bound = Self::bind(¶ms);
165 let snapshot: Option<SqliteRowRef> = handle
166 .call(move |c| {
167 let mut stmt = c.prepare(&sql)?;
168 let refs: Vec<&dyn rusqlite::ToSql> =
169 bound.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
170 let mut rows = stmt.query(refs.as_slice())?;
171 match rows.next()? {
172 Some(row) => Ok(Some(SqliteRowRef::from_rusqlite(row).map_err(|e| {
173 rusqlite::Error::ToSqlConversionFailure(Box::new(std::io::Error::other(
174 e.to_string(),
175 )))
176 })?)),
177 None => Ok(None),
178 }
179 })
180 .await
181 .map_err(|e| QueryError::database(e.to_string()).with_source(e))?;
182
183 snapshot
184 .map(|r| {
185 T::from_row(&r).map_err(|e| {
186 let msg = e.to_string();
187 QueryError::deserialization(msg).with_source(e)
188 })
189 })
190 .transpose()
191 }
192
193 async fn exec_raw(&self, sql: String, params: Vec<FilterValue>) -> QueryResult<u64> {
194 let (handle, _guard) = self.resolve_conn().await?;
195 let bound = Self::bind(¶ms);
196 let n = handle
197 .call(move |c| {
198 let refs: Vec<&dyn rusqlite::ToSql> =
199 bound.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
200 Ok(c.execute(&sql, refs.as_slice())?)
201 })
202 .await
203 .map_err(|e| QueryError::database(e.to_string()).with_source(e))?;
204 Ok(n as u64)
205 }
206
207 async fn count_rows(&self, sql: String, params: Vec<FilterValue>) -> QueryResult<u64> {
208 let (handle, _guard) = self.resolve_conn().await?;
209 let bound = Self::bind(¶ms);
210 let n = handle
211 .call(move |c| {
212 let refs: Vec<&dyn rusqlite::ToSql> =
213 bound.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
214 let mut stmt = c.prepare(&sql)?;
215 let n: i64 = stmt.query_row(refs.as_slice(), |r| r.get(0))?;
216 Ok(n)
217 })
218 .await
219 .map_err(|e| QueryError::database(e.to_string()).with_source(e))?;
220 Ok(n as u64)
221 }
222}
223
224impl QueryEngine for SqliteEngine {
225 fn dialect(&self) -> &dyn prax_query::dialect::SqlDialect {
226 &prax_query::dialect::Sqlite
227 }
228
229 fn query_many<T: Model + FromRow + Send + 'static>(
230 &self,
231 sql: &str,
232 params: Vec<FilterValue>,
233 ) -> BoxFuture<'_, QueryResult<Vec<T>>> {
234 let sql = sql.to_string();
235 Box::pin(self.query_rows::<T>(sql, params))
236 }
237
238 fn query_one<T: Model + FromRow + Send + 'static>(
239 &self,
240 sql: &str,
241 params: Vec<FilterValue>,
242 ) -> BoxFuture<'_, QueryResult<T>> {
243 let sql = sql.to_string();
244 Box::pin(async move {
245 self.query_first_row::<T>(sql, params)
246 .await?
247 .ok_or_else(|| QueryError::not_found(T::MODEL_NAME))
248 })
249 }
250
251 fn query_optional<T: Model + FromRow + Send + 'static>(
252 &self,
253 sql: &str,
254 params: Vec<FilterValue>,
255 ) -> BoxFuture<'_, QueryResult<Option<T>>> {
256 let sql = sql.to_string();
257 Box::pin(self.query_first_row::<T>(sql, params))
258 }
259
260 fn execute_insert<T: Model + FromRow + Send + 'static>(
261 &self,
262 sql: &str,
263 params: Vec<FilterValue>,
264 ) -> BoxFuture<'_, QueryResult<T>> {
265 let sql = sql.to_string();
271 Box::pin(async move {
272 self.query_first_row::<T>(sql, params)
273 .await?
274 .ok_or_else(|| QueryError::not_found(T::MODEL_NAME))
275 })
276 }
277
278 fn execute_update<T: Model + FromRow + Send + 'static>(
279 &self,
280 sql: &str,
281 params: Vec<FilterValue>,
282 ) -> BoxFuture<'_, QueryResult<Vec<T>>> {
283 let sql = sql.to_string();
284 Box::pin(self.query_rows::<T>(sql, params))
285 }
286
287 fn execute_delete(
288 &self,
289 sql: &str,
290 params: Vec<FilterValue>,
291 ) -> BoxFuture<'_, QueryResult<u64>> {
292 let sql = sql.to_string();
293 Box::pin(self.exec_raw(sql, params))
294 }
295
296 fn execute_raw(&self, sql: &str, params: Vec<FilterValue>) -> BoxFuture<'_, QueryResult<u64>> {
297 let sql = sql.to_string();
298 Box::pin(self.exec_raw(sql, params))
299 }
300
301 fn count(&self, sql: &str, params: Vec<FilterValue>) -> BoxFuture<'_, QueryResult<u64>> {
302 let sql = sql.to_string();
303 Box::pin(self.count_rows(sql, params))
304 }
305
306 fn aggregate_query(
307 &self,
308 sql: &str,
309 params: Vec<FilterValue>,
310 ) -> BoxFuture<'_, QueryResult<Vec<std::collections::HashMap<String, FilterValue>>>> {
311 let sql = sql.to_string();
312 Box::pin(async move {
313 trace!(sql = %sql, "sqlite aggregate_query");
314 let (handle, _guard) = self.resolve_conn().await?;
315 let bound = Self::bind(¶ms);
316 let rows: Vec<std::collections::HashMap<String, FilterValue>> = handle
317 .call(move |c| {
318 let mut stmt = c.prepare(&sql)?;
319 let column_names: Vec<String> =
320 stmt.column_names().iter().map(|s| s.to_string()).collect();
321 let refs: Vec<&dyn rusqlite::ToSql> =
322 bound.iter().map(|v| v as &dyn rusqlite::ToSql).collect();
323 let mut rows = stmt.query(refs.as_slice())?;
324 let mut out = Vec::new();
325 while let Some(row) = rows.next()? {
326 let mut map = std::collections::HashMap::new();
327 for (i, name) in column_names.iter().enumerate() {
328 let v: SqlValue = row.get(i).unwrap_or(SqlValue::Null);
337 let fv = match v {
338 SqlValue::Null => FilterValue::Null,
339 SqlValue::Integer(n) => FilterValue::Int(n),
340 SqlValue::Real(f) => FilterValue::Float(f),
341 SqlValue::Text(s) => FilterValue::String(s),
342 SqlValue::Blob(_) => FilterValue::Null,
343 };
344 map.insert(name.clone(), fv);
345 }
346 out.push(map);
347 }
348 Ok(out)
349 })
350 .await
351 .map_err(|e| QueryError::database(e.to_string()).with_source(e))?;
352 Ok(rows)
353 })
354 }
355
356 fn transaction<'a, R, Fut, F>(&'a self, f: F) -> BoxFuture<'a, QueryResult<R>>
357 where
358 F: FnOnce(Self) -> Fut + Send + 'a,
359 Fut: std::future::Future<Output = QueryResult<R>> + Send + 'a,
360 R: Send + 'a,
361 Self: Clone,
362 {
363 Box::pin(async move {
364 if self.tx_conn.is_some() {
368 return Err(QueryError::internal(
369 "nested transactions not yet implemented \
370 (call .transaction() on the outer engine only, or \
371 issue SAVEPOINT via execute_raw)",
372 ));
373 }
374
375 let conn = self
383 .pool
384 .get()
385 .await
386 .map_err(|e| QueryError::connection(e.to_string()).with_source(e))?;
387 let handle = conn.inner().clone();
388 handle
389 .call(|c| {
390 c.execute_batch("BEGIN")?;
391 Ok(())
392 })
393 .await
394 .map_err(|e| QueryError::database(e.to_string()).with_source(e))?;
395
396 let tx_conn = Arc::new(conn);
397 let tx_engine = SqliteEngine {
398 pool: self.pool.clone(),
399 tx_conn: Some(tx_conn.clone()),
400 };
401
402 let result = f(tx_engine).await;
403
404 match result {
410 Ok(v) => {
411 handle
412 .call(|c| {
413 c.execute_batch("COMMIT")?;
414 Ok(())
415 })
416 .await
417 .map_err(|e| QueryError::database(e.to_string()).with_source(e))?;
418 Ok(v)
419 }
420 Err(e) => {
421 let _ = handle
422 .call(|c| {
423 c.execute_batch("ROLLBACK")?;
424 Ok(())
425 })
426 .await;
427 Err(e)
428 }
429 }
430 })
431 }
432}