Converted to sqlez, so much nicer
This commit is contained in:
parent
c8face33fa
commit
406663c75e
12 changed files with 278 additions and 197 deletions
|
@ -32,6 +32,9 @@ impl Connection {
|
|||
0 as *const _,
|
||||
);
|
||||
|
||||
// Turn on extended error codes
|
||||
sqlite3_extended_result_codes(connection.sqlite3, 1);
|
||||
|
||||
connection.last_error()?;
|
||||
}
|
||||
|
||||
|
@ -71,6 +74,7 @@ impl Connection {
|
|||
0 as *mut _,
|
||||
0 as *mut _,
|
||||
);
|
||||
sqlite3_errcode(self.sqlite3);
|
||||
self.last_error()?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -95,29 +99,7 @@ impl Connection {
|
|||
}
|
||||
|
||||
pub(crate) fn last_error(&self) -> Result<()> {
|
||||
const NON_ERROR_CODES: &[i32] = &[SQLITE_OK, SQLITE_ROW];
|
||||
unsafe {
|
||||
let code = sqlite3_errcode(self.sqlite3);
|
||||
if NON_ERROR_CODES.contains(&code) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let message = sqlite3_errmsg(self.sqlite3);
|
||||
let message = if message.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
String::from_utf8_lossy(CStr::from_ptr(message as *const _).to_bytes())
|
||||
.into_owned(),
|
||||
)
|
||||
};
|
||||
|
||||
Err(anyhow!(
|
||||
"Sqlite call failed with code {} and message: {:?}",
|
||||
code as isize,
|
||||
message
|
||||
))
|
||||
}
|
||||
unsafe { error_to_result(sqlite3_errcode(self.sqlite3)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,12 +109,37 @@ impl Drop for Connection {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn error_to_result(code: std::os::raw::c_int) -> Result<()> {
|
||||
const NON_ERROR_CODES: &[i32] = &[SQLITE_OK, SQLITE_ROW];
|
||||
unsafe {
|
||||
if NON_ERROR_CODES.contains(&code) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let message = sqlite3_errstr(code);
|
||||
let message = if message.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
String::from_utf8_lossy(CStr::from_ptr(message as *const _).to_bytes())
|
||||
.into_owned(),
|
||||
)
|
||||
};
|
||||
|
||||
Err(anyhow!(
|
||||
"Sqlite call failed with code {} and message: {:?}",
|
||||
code as isize,
|
||||
message
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use anyhow::Result;
|
||||
use indoc::indoc;
|
||||
|
||||
use crate::connection::Connection;
|
||||
use crate::{connection::Connection, migrations::Migration};
|
||||
|
||||
#[test]
|
||||
fn string_round_trips() -> Result<()> {
|
||||
|
@ -234,4 +241,34 @@ mod test {
|
|||
.unwrap();
|
||||
assert_eq!(read_blobs, vec![blob]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kv_store() -> anyhow::Result<()> {
|
||||
let connection = Connection::open_memory("kv_store");
|
||||
|
||||
Migration::new(
|
||||
"kv",
|
||||
&["CREATE TABLE kv_store(
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
) STRICT;"],
|
||||
)
|
||||
.run(&connection)
|
||||
.unwrap();
|
||||
|
||||
let mut stmt = connection.prepare("INSERT INTO kv_store(key, value) VALUES(?, ?)")?;
|
||||
stmt.bind_text(1, "a").unwrap();
|
||||
stmt.bind_text(2, "b").unwrap();
|
||||
stmt.exec().unwrap();
|
||||
let id = connection.last_insert_id();
|
||||
|
||||
let res = connection
|
||||
.prepare("SELECT key, value FROM kv_store WHERE rowid = ?")?
|
||||
.with_bindings(id)?
|
||||
.row::<(String, String)>()?;
|
||||
|
||||
assert_eq!(res, ("a".to_string(), "b".to_string()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ impl Connection {
|
|||
// Run a set of commands within the context of a `SAVEPOINT name`. If the callback
|
||||
// returns Err(_), the savepoint will be rolled back. Otherwise, the save
|
||||
// point is released.
|
||||
pub fn with_savepoint<R, F>(&mut self, name: impl AsRef<str>, f: F) -> Result<R>
|
||||
pub fn with_savepoint<R, F>(&self, name: impl AsRef<str>, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut Connection) -> Result<R>,
|
||||
F: FnOnce(&Connection) -> Result<R>,
|
||||
{
|
||||
let name = name.as_ref().to_owned();
|
||||
self.exec(format!("SAVEPOINT {}", &name))?;
|
||||
|
@ -28,13 +28,9 @@ impl Connection {
|
|||
// Run a set of commands within the context of a `SAVEPOINT name`. If the callback
|
||||
// returns Ok(None) or Err(_), the savepoint will be rolled back. Otherwise, the save
|
||||
// point is released.
|
||||
pub fn with_savepoint_rollback<R, F>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
f: F,
|
||||
) -> Result<Option<R>>
|
||||
pub fn with_savepoint_rollback<R, F>(&self, name: impl AsRef<str>, f: F) -> Result<Option<R>>
|
||||
where
|
||||
F: FnOnce(&mut Connection) -> Result<Option<R>>,
|
||||
F: FnOnce(&Connection) -> Result<Option<R>>,
|
||||
{
|
||||
let name = name.as_ref().to_owned();
|
||||
self.exec(format!("SAVEPOINT {}", &name))?;
|
||||
|
@ -60,7 +56,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_nested_savepoints() -> Result<()> {
|
||||
let mut connection = Connection::open_memory("nested_savepoints");
|
||||
let connection = Connection::open_memory("nested_savepoints");
|
||||
|
||||
connection
|
||||
.exec(indoc! {"
|
||||
|
|
|
@ -6,7 +6,7 @@ use anyhow::{anyhow, Context, Result};
|
|||
use libsqlite3_sys::*;
|
||||
|
||||
use crate::bindable::{Bind, Column};
|
||||
use crate::connection::Connection;
|
||||
use crate::connection::{error_to_result, Connection};
|
||||
|
||||
pub struct Statement<'a> {
|
||||
raw_statement: *mut sqlite3_stmt,
|
||||
|
@ -65,6 +65,7 @@ impl<'a> Statement<'a> {
|
|||
}
|
||||
|
||||
pub fn bind_blob(&self, index: i32, blob: &[u8]) -> Result<()> {
|
||||
// dbg!("bind blob", index);
|
||||
let index = index as c_int;
|
||||
let blob_pointer = blob.as_ptr() as *const _;
|
||||
let len = blob.len() as c_int;
|
||||
|
@ -94,6 +95,7 @@ impl<'a> Statement<'a> {
|
|||
}
|
||||
|
||||
pub fn bind_double(&self, index: i32, double: f64) -> Result<()> {
|
||||
// dbg!("bind double", index);
|
||||
let index = index as c_int;
|
||||
|
||||
unsafe {
|
||||
|
@ -110,6 +112,7 @@ impl<'a> Statement<'a> {
|
|||
}
|
||||
|
||||
pub fn bind_int(&self, index: i32, int: i32) -> Result<()> {
|
||||
// dbg!("bind int", index);
|
||||
let index = index as c_int;
|
||||
|
||||
unsafe {
|
||||
|
@ -126,6 +129,7 @@ impl<'a> Statement<'a> {
|
|||
}
|
||||
|
||||
pub fn bind_int64(&self, index: i32, int: i64) -> Result<()> {
|
||||
// dbg!("bind int64", index);
|
||||
let index = index as c_int;
|
||||
unsafe {
|
||||
sqlite3_bind_int64(self.raw_statement, index, int);
|
||||
|
@ -141,6 +145,7 @@ impl<'a> Statement<'a> {
|
|||
}
|
||||
|
||||
pub fn bind_null(&self, index: i32) -> Result<()> {
|
||||
// dbg!("bind null", index);
|
||||
let index = index as c_int;
|
||||
unsafe {
|
||||
sqlite3_bind_null(self.raw_statement, index);
|
||||
|
@ -149,11 +154,12 @@ impl<'a> Statement<'a> {
|
|||
}
|
||||
|
||||
pub fn bind_text(&self, index: i32, text: &str) -> Result<()> {
|
||||
// dbg!("bind text", index, text);
|
||||
let index = index as c_int;
|
||||
let text_pointer = text.as_ptr() as *const _;
|
||||
let len = text.len() as c_int;
|
||||
unsafe {
|
||||
sqlite3_bind_blob(
|
||||
sqlite3_bind_text(
|
||||
self.raw_statement,
|
||||
index,
|
||||
text_pointer,
|
||||
|
@ -304,10 +310,8 @@ impl<'a> Statement<'a> {
|
|||
impl<'a> Drop for Statement<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
sqlite3_finalize(self.raw_statement);
|
||||
self.connection
|
||||
.last_error()
|
||||
.expect("sqlite3 finalize failed for statement :(");
|
||||
let error = sqlite3_finalize(self.raw_statement);
|
||||
error_to_result(error).expect("failed error");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue