rsdbc_sqlite/lib.rs
1pub mod connection;
2pub mod options;
3pub mod error;
4
5use std::collections::HashMap;
6use std::ops::Deref;
7use rusqlite::{Rows, TransactionBehavior};
8use crate::connection::SqliteConnectionMetadata;
9use rusqlite::Error as RusqliteError;
10use std::sync::{Arc, Mutex};
11use std::rc::Rc;
12use rsdbc_core::connection::{Batch, ConnectionMetadata, IsolationLevel, SQLResult, Statement, ValidationDepth};
13use rsdbc_core::{DatabaseMetadata, Result, ResultSetMetaData};
14
15// https://tedspence.com/investigating-rust-with-sqlite-53d1f9a41112
16// https://www.reddit.com/r/rust/comments/dqa4t3/how_to_put_two_variables_one_borrows_from_other/
17// https://bryce.fisher-fleig.org/strategies-for-returning-references-in-rust/
18
19
20
21/// Convert a Sqlite error into an RSDBC error
22fn to_rsdbc_err(e: rusqlite::Error) -> rsdbc_core::error::RsdbcErrors {
23 rsdbc_core::error::RsdbcErrors::General(format!("{:?}", e))
24}
25
26// #[derive(Debug)]
27// pub enum SqliteError {
28// General(String),
29// Unsupported(String),
30// }
31//
32// impl From<RusqliteError> for SqliteError {
33// fn from(_err: ExecuteReturnedResults) -> Mishap {
34// General
35// }
36// }
37
38
39
40// const SQLITE_OPEN_READ_ONLY = ffi::SQLITE_OPEN_READONLY;
41// /// The database is opened for reading and writing if possible,
42// /// or reading only if the file is write protected by the operating system.
43// /// In either case the database must already exist, otherwise an error is returned.
44// const SQLITE_OPEN_READ_WRITE = ffi::SQLITE_OPEN_READWRITE;
45// /// The database is created if it does not already exist
46// const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE;
47// /// The filename can be interpreted as a URI if this flag is set.
48// const SQLITE_OPEN_URI = 0x0000_0040;
49// /// The database will be opened as an in-memory database.
50// const SQLITE_OPEN_MEMORY = 0x0000_0080;
51// /// The new database connection will use the "multi-thread" threading mode.
52// const SQLITE_OPEN_NO_MUTEX = ffi::SQLITE_OPEN_NOMUTEX;
53// /// The new database connection will use the "serialized" threading mode.
54// const SQLITE_OPEN_FULL_MUTEX = ffi::SQLITE_OPEN_FULLMUTEX;
55// /// The database is opened shared cache enabled.
56// const SQLITE_OPEN_SHARED_CACHE = 0x0002_0000;
57// /// The database is opened shared cache disabled.
58// const SQLITE_OPEN_PRIVATE_CACHE = 0x0004_0000;
59// /// The database filename is not allowed to be a symbolic link.
60// const SQLITE_OPEN_NOFOLLOW = 0x0100_0000;
61
62// these are the default flags
63// OpenFlags::SQLITE_OPEN_READ_WRITE
64// | OpenFlags::SQLITE_OPEN_CREATE
65// | OpenFlags::SQLITE_OPEN_NO_MUTEX
66// | OpenFlags::SQLITE_OPEN_URI
67
68
69// in_memory
70// in_memory with flags
71// path
72// path with flags
73
74pub struct SqliteConnection {
75 // TODO: given Transaction can reference a conn i'm not sure this is feasible
76 // conn: Mutex<Option<&'conn rusqlite::Connection>>,
77 // this needs to be a reference to Rusqlite::Connection otherwise we get an error like
78 // move occurs because `self.conn` has type `Option<rusqlite::Connection>`, which does not implement the `Copy` trait
79 // help: consider borrowing the `Option`'s content: `self.conn.as_ref()`
80 //
81 // because of E0106 it requires a lifetime
82 conn: Option<Arc<Mutex<rusqlite::Connection>>>,
83}
84
85impl SqliteConnection {
86
87 pub fn new(conn: rusqlite::Connection) -> Self {
88 // Self { conn: Mutex::new(Some(conn)), transaction: None }
89 // Self { conn: Some(conn), transaction: None }
90 // Self {
91 // conn: Some(Rc::new(conn))
92 // }
93 Self {
94 conn: Some(Arc::new(Mutex::new(conn))),
95 }
96 }
97
98 fn drop(&mut self) {
99 // if let Some(transaction) = self.transaction.take() {
100 // let _ = transaction.rollback();
101 // }
102 }
103}
104
105impl rsdbc_core::connection::Connection for SqliteConnection {
106 // type Statement = SqliteStatement<'conn>;
107
108 // TODO: result?
109 fn begin_transaction(&mut self) -> Result<()> {
110 // TODO: call begin_transaction_with_definition with an empty instance
111 // let mut connection = self.conn.take().unwrap();
112 // let mut connection = self.conn.lock().unwrap().take().unwrap();
113 // connection.transaction().map_err(to_rsdbc_err);
114 // self.transaction = Some(trans);
115 Ok(())
116 }
117
118 // fn begin_transaction_with_definition(&mut self, definition: &dyn TransactionDefinition) {
119 // // TODO: convert definition to TransactionBehavior
120 // // let mut connection = self.conn.take().unwrap();
121 // // let mut connection = self.conn.lock().unwrap().take().unwrap();
122 // // connection.transaction_with_behavior(TransactionBehavior::Deferred);
123 // }
124
125 // https://www.reddit.com/r/rust/comments/2t8i2s/yet_another_problem_with_mutable_struct_members/
126 // TODO: should return a result
127 fn close(&mut self) -> Result<()> {
128 // let close_result = self.conn.get_mut().unwrap().close();
129
130 // self.conn.lock().unwrap().map(|c| c.close());
131 let mut _c = self.conn.take();
132 // let mut _c = self.conn.get_mut().unwrap().take();
133 _c = None;
134
135 // close_result.map_err(move |e| to_rsdbc_err(e.1))?;
136 Ok(())
137 }
138
139 fn commit_transaction(&mut self) {
140 // if let Some(transaction) = self.transaction.take() {
141 // let _ = transaction.commit();
142 // }
143 }
144
145 fn create_batch(&mut self) -> Result<Box<dyn Batch>> {
146 todo!()
147 }
148
149 // TODO: return result
150 // UnsupportedOperationException if not supported
151 fn create_savepoint(&mut self, name: &str) {
152 // if self.transaction.is_none() {
153 // // return error
154 // }
155
156 // let sp = self.conn.savepoint_with_name(name)?;
157 // let savepoint = self.transaction.unwrap().savepoint_with_name(name)?;
158 }
159
160 // fn create_statement(&mut self, sql: &str) -> Result<Box<Self::Statement>> {
161 fn create_statement(&mut self, sql: &str) -> Result<Box<dyn Statement<'_> + '_>> {
162 // let mut c = self.conn.take();
163 // let stmt = c.unwrap()
164 // .prepare(sql)
165 // .map_err(to_rsdbc_err)?;
166
167 // let c: &'conn rusqlite::Connection = self.conn.unwrap();
168
169 // let stmt: rusqlite::Statement = self.conn.unwrap()
170 // .prepare(sql)
171 // .map_err(to_rsdbc_err)?;
172
173 // let stmt = self.conn.get_mut().unwrap().take().unwrap()
174 // .prepare(sql)
175 // .map_err(to_rsdbc_err)?;
176
177 // let stmt = self.conn.lock().unwrap().take().unwrap()
178 // .prepare(sql)
179 // .map_err(to_rsdbc_err)?;
180
181 // let stmt = self.conn
182 // .as_ref()
183 // .unwrap()
184 // .clone()
185 // .deref()
186 // .lock()
187 // .unwrap()
188 // .prepare(sql)
189 // .map_err(to_rsdbc_err)?;
190 //
191 // Ok(Box::new(SqliteStatement {
192 // stmt,
193 // }))
194
195 todo!()
196 }
197
198 fn is_auto_commit(&mut self) -> bool {
199 let connection = self.conn.take().unwrap();
200 // let connection = self.conn.lock().unwrap().take().unwrap();
201 connection.clone().deref().lock().unwrap().is_autocommit()
202 }
203
204 fn metadata(&mut self) -> Result<Box<dyn ConnectionMetadata>> {
205 todo!()
206 }
207
208 fn transaction_isolation_level(&mut self) -> IsolationLevel {
209 todo!()
210 }
211
212 fn release_savepoint(&mut self, name: &str) {
213 todo!()
214 // do we need to keep savepoint? I dont see rusqlite giving us an option to get a savepoint
215
216 }
217
218 /// This is equivalent to `Transaction`'s `Drop` implementation, but provides any error
219 /// encountered to the caller.
220 fn rollback_transaction(&mut self) {
221 todo!()
222 }
223
224 fn rollback_transaction_to_savepoint(&mut self, name: String) {
225 todo!()
226 }
227
228 fn auto_commit(&mut self, commit: bool) {
229 todo!()
230 // The sqlite3_get_autocommit() interface returns non-zero or zero if the given database
231 // connection is or is not in autocommit mode, respectively.
232 // Autocommit mode is on by default.
233 // Autocommit mode is disabled by a BEGIN statement. Autocommit mode is re-enabled by a
234 // COMMIT or ROLLBACK.
235 }
236
237 fn set_transaction_isolation_level(&mut self, isolation_level: IsolationLevel) {
238 // Error::Unsupported(String::from(
239 // "Except in the case of shared cache database connections with PRAGMA read_uncommitted \
240 // turned on, all transactions in SQLite show \"serializable\" isolation. \
241 // SQLite implements serializable transactions by actually serializing the writes."
242 // ))
243 }
244
245 fn validate(&mut self, depth: ValidationDepth) -> bool {
246 todo!()
247 }
248}
249
250// impl Drop for SqliteConnection {
251// fn drop(&mut self) {
252// let _ = self.close();
253// }
254// }
255
256// TODO: Do we need this? Can we just use CallableStatement/PreparedStatement
257pub struct SqliteStatement<'a> {
258 stmt: rusqlite::Statement<'a>,
259}
260
261impl rsdbc_core::connection::Statement<'_> for SqliteStatement<'_> {
262 fn add(&mut self) -> &mut Self where Self: Sized {
263 todo!()
264 }
265
266 fn bind_index<T>(&mut self, index: u32, value: T) -> &mut Self where Self: Sized {
267 todo!()
268 }
269
270 fn bind_name<T>(&mut self, name: &str, value: T) -> &mut Self where Self: Sized {
271 todo!()
272 }
273
274 fn bind_null_index(&mut self, index: u32) -> &mut Self where Self: Sized {
275 todo!()
276 }
277
278 fn bind_null_name(&mut self, name: &str) -> &mut Self where Self: Sized {
279 todo!()
280 }
281
282 fn execute<T: SQLResult>(&self) -> Result<T> where Self: Sized {
283 todo!()
284 }
285
286 fn return_generated_values(&mut self, columns: &[&str]) -> &mut Self where Self: Sized {
287 todo!()
288 }
289
290 fn fetch_size(&mut self, rows: u32) -> &mut Self where Self: Sized {
291 todo!()
292 }
293}
294
295struct SqliteResultSet<'stmt> {
296 rows: Rows<'stmt>,
297}
298
299struct SqliteDatabaseMetadata {
300
301}
302
303impl DatabaseMetadata for SqliteDatabaseMetadata {
304
305}
306
307impl<'stmt> rsdbc_core::ResultSet for SqliteResultSet<'stmt> {
308 fn meta_data(&self) -> Result<Box<dyn ResultSetMetaData>> {
309 todo!()
310 }
311
312 fn next(&mut self) -> bool {
313 todo!()
314 }
315
316 fn get_bool(&self, i: u64) -> Result<Option<bool>> {
317 todo!()
318 }
319
320 fn get_i8(&self, i: u64) -> Result<Option<i8>> {
321 todo!()
322 }
323
324 fn get_i16(&self, i: u64) -> Result<Option<i16>> {
325 todo!()
326 }
327
328 fn get_i32(&self, i: u64) -> Result<Option<i32>> {
329 todo!()
330 }
331
332 fn get_i64(&self, i: u64) -> Result<Option<i64>> {
333 todo!()
334 }
335
336 fn get_f32(&self, i: u64) -> Result<Option<f32>> {
337 todo!()
338 }
339
340 fn get_f64(&self, i: u64) -> Result<Option<f64>> {
341 todo!()
342 }
343
344 fn get_string(&self, i: u64) -> Result<Option<String>> {
345 todo!()
346 }
347
348 fn get_bytes(&self, i: u64) -> Result<Option<Vec<u8>>> {
349 todo!()
350 }
351}
352
353fn to_rsdbc_type(t: Option<&str>) -> rsdbc_core::DataType {
354 //TODO implement for real
355 match t {
356 Some("INT") => rsdbc_core::DataType::Integer,
357 _ => rsdbc_core::DataType::Utf8,
358 }
359}
360
361struct Values<'a>(&'a [rsdbc_core::Value]);
362struct ValuesIter<'a>(std::slice::Iter<'a, rsdbc_core::Value>);
363
364impl<'a> IntoIterator for &'a Values<'a> {
365 type Item = &'a dyn rusqlite::types::ToSql;
366 type IntoIter = ValuesIter<'a>;
367
368 fn into_iter(self) -> ValuesIter<'a> {
369 ValuesIter(self.0.iter())
370 }
371}
372impl<'a> Iterator for ValuesIter<'a> {
373 type Item = &'a dyn rusqlite::types::ToSql;
374
375 fn next(&mut self) -> Option<&'a dyn rusqlite::types::ToSql> {
376 self.0.next().map(|v| match v {
377 rsdbc_core::Value::String(ref s) => s as &dyn rusqlite::types::ToSql,
378 rsdbc_core::Value::Int32(ref n) => n as &dyn rusqlite::types::ToSql,
379 rsdbc_core::Value::UInt32(ref n) => n as &dyn rusqlite::types::ToSql,
380 })
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387 use std::{collections::HashMap, sync::Arc};
388 use crate::options::SqliteConnectOptions;
389
390 // // low-level, Executor trait
391 // conn.execute("BEGIN").await?; // unprepared, simple query
392 // conn.execute(sqlx::query("DELETE FROM table")).await?; // prepared, cached query
393
394 // sqlx::query("DELETE FROM table").execute(&mut conn).await?;
395 // sqlx::query("DELETE FROM table").execute(&pool).await?;
396
397 // let mut rows = sqlx::query("SELECT * FROM users WHERE email = ?")
398 // .bind(email)
399 // .fetch(&mut conn);
400 //
401 // while let Some(row) = rows.try_next().await? {
402 // // map the row into a user-defined domain type
403 // let email: &str = row.try_get("email")?;
404 // }
405
406// let mut stream = sqlx::query("SELECT * FROM users")
407// .map(|row: PgRow| {
408// // map the row into a user-defined domain type
409// })
410// .fetch(&mut conn);
411// #[derive(sqlx::FromRow)]
412// struct User { name: String, id: i64 }
413//
414// let mut stream = sqlx::query_as::<_, User>("SELECT * FROM users WHERE email = ? OR name = ?")
415// .bind(user_email)
416// .bind(user_name)
417// .fetch(&mut conn);
418
419
420
421 #[test]
422 fn execute_query() -> rsdbc_core::Result<()> {
423 // let mut connection = SqliteConnectOptions::new().connect().await?;
424 // let stmt = connection.create_statement("SELECT 1").unwrap();
425 // let mut rs = stmt.execute();
426 //
427 // while rs.next() {
428 // println!("{:?}", rs.get_string(1));
429 // }
430
431 Ok(())
432 }
433
434 // #[test]
435 // fn execute_query() -> rsdbc::Result<()> {
436 // let driver: Arc<dyn rsdbc::Driver> = Arc::new(SqliteDriver::new());
437 // let url = "";
438 // let mut conn = driver.connect(url, HashMap::new())?;
439 // execute(&mut *conn, "DROP TABLE IF EXISTS test", &vec![])?;
440 // execute(&mut *conn, "CREATE TABLE test (a INT NOT NULL)", &vec![])?;
441 // execute(
442 // &mut *conn,
443 // "INSERT INTO test (a) VALUES (?)",
444 // &vec![rsdbc::Value::Int32(123)],
445 // )?;
446 //
447 // let mut stmt = conn.prepare("SELECT a FROM test")?;
448 // let mut rs = stmt.execute_query(&vec![])?;
449 //
450 // let meta = rs.meta_data()?;
451 // assert_eq!(1, meta.num_columns());
452 // assert_eq!("a".to_owned(), meta.column_name(0));
453 // assert_eq!(DataType::Integer, meta.column_type(0));
454 //
455 // assert!(rs.next());
456 // assert_eq!(Some(123), rs.get_i32(0)?);
457 // assert!(!rs.next());
458 //
459 // Ok(())
460 // }
461
462 // fn execute(
463 // conn: &mut dyn Connection,
464 // sql: &str,
465 // values: &Vec<rsdbc::Value>,
466 // ) -> rsdbc::Result<u64> {
467 // println!("Executing '{}' with {} params", sql, values.len());
468 // let mut stmt = conn.prepare(sql)?;
469 // stmt.execute_update(values)
470 // }
471}