1use std::sync::Arc;
10
11use keelson_core::Value;
12use keelson_exec::{
13 Begin, BeginWith, Column, ExecError, ExecFuture, ExecResult, Executor, Family, RawConnection,
14 Row, RowStream, Statement, StreamExecutor, Transaction, TxConflict, TxConflictError, TxOptions,
15};
16use sqlx::sqlite::{SqliteArguments, SqliteRow};
17use sqlx::{Column as _, Row as _, Sqlite, TypeInfo as _, ValueRef as _};
18
19use crate::common::{decode_err, unhandled};
20
21#[derive(Debug, Clone)]
23pub struct Pool {
24 inner: sqlx::SqlitePool,
25}
26
27impl Pool {
28 pub async fn connect(url: &str) -> Result<Self, ExecError> {
33 use std::str::FromStr as _;
34 let opts = sqlx::sqlite::SqliteConnectOptions::from_str(url)
35 .map_err(ExecError::driver)?
36 .create_if_missing(true);
37 let inner = sqlx::sqlite::SqlitePoolOptions::new()
38 .connect_with(opts)
39 .await
40 .map_err(ExecError::driver)?;
41 Ok(Pool { inner })
42 }
43
44 pub fn from_pool(inner: sqlx::SqlitePool) -> Self {
46 Pool { inner }
47 }
48
49 pub fn inner(&self) -> &sqlx::SqlitePool {
51 &self.inner
52 }
53}
54
55impl Executor for Pool {
56 fn family(&self) -> Family {
57 Family::Sqlite
58 }
59
60 fn fetch(&self, stmt: Statement) -> ExecFuture<'_, Result<Vec<Row>, ExecError>> {
61 Box::pin(async move {
62 let Statement { sql, args, .. } = stmt;
63 do_fetch(&self.inner, &sql, args).await
64 })
65 }
66
67 fn execute(&self, stmt: Statement) -> ExecFuture<'_, Result<ExecResult, ExecError>> {
68 Box::pin(async move {
69 let Statement { sql, args, .. } = stmt;
70 do_execute(&self.inner, &sql, args).await
71 })
72 }
73}
74
75impl Begin for Pool {
76 fn begin(&self) -> ExecFuture<'_, Result<Transaction, ExecError>> {
77 Box::pin(async move {
78 let conn = self.inner.acquire().await.map_err(ExecError::driver)?;
79 Transaction::begin_on(Box::new(RawConn { conn })).await
80 })
81 }
82}
83
84impl BeginWith for Pool {
85 fn begin_with(&self, opts: TxOptions) -> ExecFuture<'_, Result<Transaction, ExecError>> {
86 Box::pin(async move {
87 opts.check(Family::Sqlite)?;
91 let conn = self.inner.acquire().await.map_err(ExecError::driver)?;
92 Transaction::begin_on_with(Box::new(RawConn { conn }), opts).await
93 })
94 }
95}
96
97impl StreamExecutor for Pool {
98 fn fetch_stream(&self, stmt: Statement) -> ExecFuture<'_, Result<RowStream, ExecError>> {
99 Box::pin(async move {
100 let pool = self.inner.clone();
101 let (tx, rx) = tokio::sync::mpsc::channel::<Result<Row, ExecError>>(64);
102 tokio::spawn(async move {
103 use futures_util::StreamExt as _;
104 let Statement { sql, args, .. } = stmt;
105 let q = match bind_args(&sql, args) {
106 Ok(q) => q,
107 Err(e) => {
108 let _ = tx.send(Err(e)).await;
109 return;
110 }
111 };
112 let mut native = q.fetch(&pool);
113 let mut header: Option<Arc<[Column]>> = None;
114 while let Some(next) = native.next().await {
115 let msg = match next {
116 Ok(row) => decode_row(&row, &mut header),
117 Err(e) => Err(ExecError::driver(e)),
118 };
119 let stop = msg.is_err();
120 if tx.send(msg).await.is_err() || stop {
121 return;
122 }
123 }
124 });
125 Ok(RowStream::new(rx))
126 })
127 }
128}
129
130#[derive(Debug)]
132struct RawConn {
133 conn: sqlx::pool::PoolConnection<Sqlite>,
134}
135
136impl RawConnection for RawConn {
137 fn family(&self) -> Family {
138 Family::Sqlite
139 }
140
141 fn fetch<'a>(
142 &'a mut self,
143 sql: &'a str,
144 args: Vec<Value>,
145 ) -> ExecFuture<'a, Result<Vec<Row>, ExecError>> {
146 Box::pin(async move { do_fetch(&mut *self.conn, sql, args).await })
147 }
148
149 fn execute<'a>(
150 &'a mut self,
151 sql: &'a str,
152 args: Vec<Value>,
153 ) -> ExecFuture<'a, Result<ExecResult, ExecError>> {
154 Box::pin(async move { do_execute(&mut *self.conn, sql, args).await })
155 }
156
157 fn abandon(self: Box<Self>) {
158 let _ = self.conn.detach();
159 }
160}
161
162fn driver_err(e: sqlx::Error) -> ExecError {
170 const SQLITE_BUSY: i32 = 5;
171 const SQLITE_LOCKED: i32 = 6;
172 if let sqlx::Error::Database(db) = &e {
173 let primary = db
174 .code()
175 .and_then(|c| c.parse::<i32>().ok())
176 .map(|c| c & 0xff);
177 if matches!(primary, Some(SQLITE_BUSY | SQLITE_LOCKED)) {
178 let code = db.code().unwrap_or_default().into_owned();
179 let message = db.message().to_owned();
180 return TxConflictError::new(TxConflict::Busy, code, message)
181 .with_source(e)
182 .into_exec_error();
183 }
184 }
185 ExecError::driver(e)
186}
187
188async fn do_fetch<'e, E>(exec: E, sql: &str, args: Vec<Value>) -> Result<Vec<Row>, ExecError>
189where
190 E: sqlx::Executor<'e, Database = Sqlite>,
191{
192 let rows = bind_args(sql, args)?
193 .fetch_all(exec)
194 .await
195 .map_err(driver_err)?;
196 let mut header: Option<Arc<[Column]>> = None;
197 rows.iter().map(|r| decode_row(r, &mut header)).collect()
198}
199
200async fn do_execute<'e, E>(exec: E, sql: &str, args: Vec<Value>) -> Result<ExecResult, ExecError>
201where
202 E: sqlx::Executor<'e, Database = Sqlite>,
203{
204 let done = if args.is_empty() {
209 exec.execute(sql).await.map_err(driver_err)?
210 } else {
211 bind_args(sql, args)?
212 .execute(exec)
213 .await
214 .map_err(driver_err)?
215 };
216 let last = Some(done.last_insert_rowid()).filter(|id| *id != 0);
217 Ok(ExecResult::new(done.rows_affected(), last))
218}
219
220fn bind_args<'q>(
225 sql: &'q str,
226 args: Vec<Value>,
227) -> Result<sqlx::query::Query<'q, Sqlite, SqliteArguments<'q>>, ExecError> {
228 let mut q = sqlx::query(sql);
229 for v in args {
230 q = match v {
231 Value::Null => q.bind(Option::<String>::None),
232 Value::Bool(x) => q.bind(x),
233 Value::I8(x) => q.bind(i64::from(x)),
234 Value::I16(x) => q.bind(i64::from(x)),
235 Value::I32(x) => q.bind(i64::from(x)),
236 Value::I64(x) => q.bind(x),
237 Value::U8(x) => q.bind(i64::from(x)),
238 Value::U16(x) => q.bind(i64::from(x)),
239 Value::U32(x) => q.bind(i64::from(x)),
240 Value::U64(x) => {
241 q.bind(i64::try_from(x).map_err(|_| unsupported_value("u64 out of i64 range"))?)
242 }
243 Value::F32(x) => q.bind(f64::from(x)),
245 Value::F64(x) => q.bind(x),
246 Value::Text(x) => q.bind(x),
247 Value::Bytes(x) => q.bind(x),
248 #[cfg(feature = "chrono")]
249 Value::Date(x) => q.bind(x.format("%Y-%m-%d").to_string()),
250 #[cfg(feature = "chrono")]
251 Value::Time(x) => q.bind(x.format("%H:%M:%S%.f").to_string()),
252 #[cfg(feature = "chrono")]
253 Value::DateTime(x) => q.bind(x.format("%Y-%m-%dT%H:%M:%S%.f").to_string()),
254 #[cfg(feature = "chrono")]
255 Value::TimestampTz(x) => q.bind(x.to_rfc3339_opts(chrono::SecondsFormat::AutoSi, true)),
256 #[cfg(feature = "uuid")]
257 Value::Uuid(x) => q.bind(x.hyphenated().to_string()),
258 #[cfg(feature = "decimal")]
261 Value::Decimal(x) => q.bind(x.to_string()),
262 #[cfg(feature = "json")]
263 Value::Json(x) => q.bind(
264 serde_json::to_string(&x).map_err(|_| unsupported_value("unserialisable json"))?,
265 ),
266 other => return Err(unsupported_value(other.type_name())),
267 };
268 }
269 Ok(q)
270}
271
272fn unsupported_value(type_name: &'static str) -> ExecError {
273 ExecError::UnsupportedValue {
274 type_name,
275 family: Family::Sqlite,
276 }
277}
278
279fn decode_row(row: &SqliteRow, header: &mut Option<Arc<[Column]>>) -> Result<Row, ExecError> {
280 let columns = header
281 .get_or_insert_with(|| {
282 row.columns()
283 .iter()
284 .map(|c| Column::new(c.name()))
285 .collect::<Vec<_>>()
286 .into()
287 })
288 .clone();
289 let mut values = Vec::with_capacity(row.columns().len());
290 for i in 0..row.columns().len() {
291 values.push(decode_value(row, i)?);
292 }
293 Ok(Row::new(columns, values))
294}
295
296fn decode_value(row: &SqliteRow, i: usize) -> Result<Value, ExecError> {
303 let col = &row.columns()[i];
304 let name = col.name();
305 let raw = row.try_get_raw(i).map_err(|e| decode_err(name, e))?;
306 if raw.is_null() {
307 return Ok(Value::Null);
308 }
309
310 macro_rules! take {
311 ($t:ty) => {
312 row.try_get::<$t, _>(i).map_err(|e| decode_err(name, e))?
313 };
314 }
315
316 let decl = col.type_info();
320 let decl = decl.name();
321 let ty = if decl == "NULL" {
322 let vt = raw.type_info();
323 vt.name().to_owned()
324 } else {
325 decl.to_owned()
326 };
327
328 Ok(match ty.as_str() {
329 "BOOLEAN" => Value::Bool(take!(bool)),
330 "INTEGER" | "INT8" => Value::I64(take!(i64)),
331 "REAL" => Value::F64(take!(f64)),
332 "TEXT" | "DATE" | "TIME" | "DATETIME" | "NUMERIC" => Value::Text(take!(String)),
336 "BLOB" => Value::Bytes(take!(Vec<u8>)),
337 other => return Err(unhandled(name, other)),
338 })
339}