Refined sqlez, implemented 60% of workspace serialization sql
This commit is contained in:
parent
6b214acbc4
commit
0186289420
11 changed files with 569 additions and 433 deletions
|
@ -178,8 +178,29 @@ impl<T1: Column, T2: Column, T3: Column, T4: Column> Column for (T1, T2, T3, T4)
|
|||
let (first, next_index) = T1::column(statement, start_index)?;
|
||||
let (second, next_index) = T2::column(statement, next_index)?;
|
||||
let (third, next_index) = T3::column(statement, next_index)?;
|
||||
let (forth, next_index) = T4::column(statement, next_index)?;
|
||||
Ok(((first, second, third, forth), next_index))
|
||||
let (fourth, next_index) = T4::column(statement, next_index)?;
|
||||
Ok(((first, second, third, fourth), next_index))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1: Bind, T2: Bind, T3: Bind, T4: Bind, T5: Bind> Bind for (T1, T2, T3, T4, T5) {
|
||||
fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
|
||||
let next_index = self.0.bind(statement, start_index)?;
|
||||
let next_index = self.1.bind(statement, next_index)?;
|
||||
let next_index = self.2.bind(statement, next_index)?;
|
||||
let next_index = self.3.bind(statement, next_index)?;
|
||||
self.4.bind(statement, next_index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1: Column, T2: Column, T3: Column, T4: Column, T5: Column> Column for (T1, T2, T3, T4, T5) {
|
||||
fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
|
||||
let (first, next_index) = T1::column(statement, start_index)?;
|
||||
let (second, next_index) = T2::column(statement, next_index)?;
|
||||
let (third, next_index) = T3::column(statement, next_index)?;
|
||||
let (fourth, next_index) = T4::column(statement, next_index)?;
|
||||
let (fifth, next_index) = T5::column(statement, next_index)?;
|
||||
Ok(((first, second, third, fourth, fifth), next_index))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,29 @@ impl Connection {
|
|||
}
|
||||
|
||||
pub(crate) fn last_error(&self) -> Result<()> {
|
||||
unsafe { error_to_result(sqlite3_errcode(self.sqlite3)) }
|
||||
unsafe {
|
||||
let code = sqlite3_errcode(self.sqlite3);
|
||||
const NON_ERROR_CODES: &[i32] = &[SQLITE_OK, SQLITE_ROW];
|
||||
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
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,31 +131,6 @@ 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;
|
||||
|
@ -213,6 +210,35 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_round_trips() {
|
||||
let connection = Connection::open_memory("bool_round_trips");
|
||||
connection
|
||||
.exec(indoc! {"
|
||||
CREATE TABLE bools (
|
||||
t INTEGER,
|
||||
f INTEGER
|
||||
);"})
|
||||
.unwrap();
|
||||
|
||||
connection
|
||||
.prepare("INSERT INTO bools(t, f) VALUES (?, ?);")
|
||||
.unwrap()
|
||||
.with_bindings((true, false))
|
||||
.unwrap()
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&connection
|
||||
.prepare("SELECT * FROM bools;")
|
||||
.unwrap()
|
||||
.row::<(bool, bool)>()
|
||||
.unwrap(),
|
||||
&(true, false)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backup_works() {
|
||||
let connection1 = Connection::open_memory("backup_works");
|
||||
|
|
|
@ -8,11 +8,11 @@ impl Connection {
|
|||
// point is released.
|
||||
pub fn with_savepoint<R, F>(&self, name: impl AsRef<str>, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&Connection) -> Result<R>,
|
||||
F: FnOnce() -> Result<R>,
|
||||
{
|
||||
let name = name.as_ref().to_owned();
|
||||
self.exec(format!("SAVEPOINT {}", &name))?;
|
||||
let result = f(self);
|
||||
let result = f();
|
||||
match result {
|
||||
Ok(_) => {
|
||||
self.exec(format!("RELEASE {}", name))?;
|
||||
|
@ -30,11 +30,11 @@ impl Connection {
|
|||
// point is released.
|
||||
pub fn with_savepoint_rollback<R, F>(&self, name: impl AsRef<str>, f: F) -> Result<Option<R>>
|
||||
where
|
||||
F: FnOnce(&Connection) -> Result<Option<R>>,
|
||||
F: FnOnce() -> Result<Option<R>>,
|
||||
{
|
||||
let name = name.as_ref().to_owned();
|
||||
self.exec(format!("SAVEPOINT {}", &name))?;
|
||||
let result = f(self);
|
||||
let result = f();
|
||||
match result {
|
||||
Ok(Some(_)) => {
|
||||
self.exec(format!("RELEASE {}", name))?;
|
||||
|
@ -69,21 +69,21 @@ mod tests {
|
|||
let save1_text = "test save1";
|
||||
let save2_text = "test save2";
|
||||
|
||||
connection.with_savepoint("first", |save1| {
|
||||
save1
|
||||
connection.with_savepoint("first", || {
|
||||
connection
|
||||
.prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
|
||||
.with_bindings((save1_text, 1))?
|
||||
.exec()?;
|
||||
|
||||
assert!(save1
|
||||
.with_savepoint("second", |save2| -> Result<Option<()>, anyhow::Error> {
|
||||
save2
|
||||
assert!(connection
|
||||
.with_savepoint("second", || -> Result<Option<()>, anyhow::Error> {
|
||||
connection
|
||||
.prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
|
||||
.with_bindings((save2_text, 2))?
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(
|
||||
save2
|
||||
connection
|
||||
.prepare("SELECT text FROM text ORDER BY text.idx ASC")?
|
||||
.rows::<String>()?,
|
||||
vec![save1_text, save2_text],
|
||||
|
@ -95,20 +95,20 @@ mod tests {
|
|||
.is_some());
|
||||
|
||||
assert_eq!(
|
||||
save1
|
||||
connection
|
||||
.prepare("SELECT text FROM text ORDER BY text.idx ASC")?
|
||||
.rows::<String>()?,
|
||||
vec![save1_text],
|
||||
);
|
||||
|
||||
save1.with_savepoint_rollback::<(), _>("second", |save2| {
|
||||
save2
|
||||
connection.with_savepoint_rollback::<(), _>("second", || {
|
||||
connection
|
||||
.prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
|
||||
.with_bindings((save2_text, 2))?
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(
|
||||
save2
|
||||
connection
|
||||
.prepare("SELECT text FROM text ORDER BY text.idx ASC")?
|
||||
.rows::<String>()?,
|
||||
vec![save1_text, save2_text],
|
||||
|
@ -118,20 +118,20 @@ mod tests {
|
|||
})?;
|
||||
|
||||
assert_eq!(
|
||||
save1
|
||||
connection
|
||||
.prepare("SELECT text FROM text ORDER BY text.idx ASC")?
|
||||
.rows::<String>()?,
|
||||
vec![save1_text],
|
||||
);
|
||||
|
||||
save1.with_savepoint_rollback("second", |save2| {
|
||||
save2
|
||||
connection.with_savepoint_rollback("second", || {
|
||||
connection
|
||||
.prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
|
||||
.with_bindings((save2_text, 2))?
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(
|
||||
save2
|
||||
connection
|
||||
.prepare("SELECT text FROM text ORDER BY text.idx ASC")?
|
||||
.rows::<String>()?,
|
||||
vec![save1_text, save2_text],
|
||||
|
@ -141,7 +141,7 @@ mod tests {
|
|||
})?;
|
||||
|
||||
assert_eq!(
|
||||
save1
|
||||
connection
|
||||
.prepare("SELECT text FROM text ORDER BY text.idx ASC")?
|
||||
.rows::<String>()?,
|
||||
vec![save1_text, save2_text],
|
||||
|
|
|
@ -6,7 +6,7 @@ use anyhow::{anyhow, Context, Result};
|
|||
use libsqlite3_sys::*;
|
||||
|
||||
use crate::bindable::{Bind, Column};
|
||||
use crate::connection::{error_to_result, Connection};
|
||||
use crate::connection::Connection;
|
||||
|
||||
pub struct Statement<'a> {
|
||||
raw_statement: *mut sqlite3_stmt,
|
||||
|
@ -48,7 +48,9 @@ impl<'a> Statement<'a> {
|
|||
0 as *mut _,
|
||||
);
|
||||
|
||||
connection.last_error().context("Prepare call failed.")?;
|
||||
connection
|
||||
.last_error()
|
||||
.with_context(|| format!("Prepare call failed for query:\n{}", query.as_ref()))?;
|
||||
}
|
||||
|
||||
Ok(statement)
|
||||
|
@ -309,10 +311,7 @@ impl<'a> Statement<'a> {
|
|||
|
||||
impl<'a> Drop for Statement<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let error = sqlite3_finalize(self.raw_statement);
|
||||
error_to_result(error).expect("failed error");
|
||||
};
|
||||
unsafe { sqlite3_finalize(self.raw_statement) };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,9 +326,9 @@ mod test {
|
|||
let connection1 = Connection::open_memory("blob_round_trips");
|
||||
connection1
|
||||
.exec(indoc! {"
|
||||
CREATE TABLE blobs (
|
||||
data BLOB
|
||||
);"})
|
||||
CREATE TABLE blobs (
|
||||
data BLOB
|
||||
);"})
|
||||
.unwrap();
|
||||
|
||||
let blob = &[0, 1, 2, 4, 8, 16, 32, 64];
|
||||
|
@ -352,4 +351,41 @@ mod test {
|
|||
let mut read = connection1.prepare("SELECT * FROM blobs;").unwrap();
|
||||
assert_eq!(read.step().unwrap(), StepResult::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn maybe_returns_options() {
|
||||
let connection = Connection::open_memory("maybe_returns_options");
|
||||
connection
|
||||
.exec(indoc! {"
|
||||
CREATE TABLE texts (
|
||||
text TEXT
|
||||
);"})
|
||||
.unwrap();
|
||||
|
||||
assert!(connection
|
||||
.prepare("SELECT text FROM texts")
|
||||
.unwrap()
|
||||
.maybe_row::<String>()
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
let text_to_insert = "This is a test";
|
||||
|
||||
connection
|
||||
.prepare("INSERT INTO texts VALUES (?)")
|
||||
.unwrap()
|
||||
.with_bindings(text_to_insert)
|
||||
.unwrap()
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
connection
|
||||
.prepare("SELECT text FROM texts")
|
||||
.unwrap()
|
||||
.maybe_row::<String>()
|
||||
.unwrap(),
|
||||
Some(text_to_insert.to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue