mirror of
https://github.com/fluencelabs/sqlite-wasm-connector
synced 2025-03-15 06:20:50 +00:00
Implement prepared statements
This commit is contained in:
parent
1dfeee881b
commit
f7f9af43ed
@ -3,25 +3,25 @@ use raw;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::Path;
|
||||
|
||||
use Result;
|
||||
use {Result, Statement};
|
||||
|
||||
/// A database.
|
||||
pub struct Database<'d> {
|
||||
db: *mut raw::sqlite3,
|
||||
_phantom: PhantomData<&'d raw::sqlite3>,
|
||||
pub struct Database<'l> {
|
||||
raw: *mut raw::sqlite3,
|
||||
_phantom: PhantomData<&'l raw::sqlite3>,
|
||||
}
|
||||
|
||||
/// A callback executed for each row of the result of an SQL query.
|
||||
pub type ExecuteCallback<'c> = FnMut(Vec<(String, String)>) -> bool + 'c;
|
||||
pub type ExecuteCallback<'l> = FnMut(Vec<(String, String)>) -> bool + 'l;
|
||||
|
||||
impl<'d> Database<'d> {
|
||||
impl<'l> Database<'l> {
|
||||
/// Open a database.
|
||||
pub fn open(path: &Path) -> Result<Database<'d>> {
|
||||
let mut db = 0 as *mut _;
|
||||
pub fn open(path: &Path) -> Result<Database> {
|
||||
let mut raw = 0 as *mut _;
|
||||
unsafe {
|
||||
success!(raw::sqlite3_open(path_to_c_str!(path), &mut db));
|
||||
success!(raw::sqlite3_open(path_to_c_str!(path), &mut raw));
|
||||
}
|
||||
Ok(Database { db: db, _phantom: PhantomData })
|
||||
Ok(Database { raw: raw, _phantom: PhantomData })
|
||||
}
|
||||
|
||||
/// Execute an SQL statement.
|
||||
@ -32,24 +32,35 @@ impl<'d> Database<'d> {
|
||||
match callback {
|
||||
Some(callback) => {
|
||||
let mut callback = Box::new(callback);
|
||||
success!(raw::sqlite3_exec(self.db, str_to_c_str!(sql), Some(execute_callback),
|
||||
success!(raw::sqlite3_exec(self.raw, str_to_c_str!(sql),
|
||||
Some(execute_callback),
|
||||
&mut callback as *mut _ as *mut _, 0 as *mut _));
|
||||
},
|
||||
None => {
|
||||
success!(raw::sqlite3_exec(self.db, str_to_c_str!(sql), None,
|
||||
0 as *mut _, 0 as *mut _));
|
||||
success!(raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _,
|
||||
0 as *mut _));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a prepared statement.
|
||||
pub fn statement(&mut self, sql: &str) -> Result<Statement<'l>> {
|
||||
let mut raw = 0 as *mut _;
|
||||
unsafe {
|
||||
success!(raw::sqlite3_prepare(self.raw, str_to_c_str!(sql), -1, &mut raw,
|
||||
0 as *mut _));
|
||||
}
|
||||
Ok(::statement::from_raw(raw))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for Database<'d> {
|
||||
impl<'l> Drop for Database<'l> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { ::raw::sqlite3_close(self.db) };
|
||||
unsafe { ::raw::sqlite3_close(self.raw) };
|
||||
}
|
||||
}
|
||||
|
||||
|
22
src/lib.rs
22
src/lib.rs
@ -3,6 +3,7 @@
|
||||
extern crate libc;
|
||||
extern crate sqlite3_sys as raw;
|
||||
|
||||
use libc::c_int;
|
||||
use std::path::Path;
|
||||
|
||||
/// A result.
|
||||
@ -11,13 +12,13 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
/// An error.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub code: ErrorCode,
|
||||
pub code: ResultCode,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
/// An error code.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ErrorCode {
|
||||
/// A result code.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ResultCode {
|
||||
Abort = raw::SQLITE_ABORT as isize,
|
||||
Authorization = raw::SQLITE_AUTH as isize,
|
||||
Busy = raw::SQLITE_BUSY as isize,
|
||||
@ -51,9 +52,16 @@ pub enum ErrorCode {
|
||||
Warning = raw::SQLITE_WARNING as isize,
|
||||
}
|
||||
|
||||
impl ResultCode {
|
||||
#[inline]
|
||||
fn from_raw(code: c_int) -> ResultCode {
|
||||
unsafe { std::mem::transmute(code as i8) }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! raise(
|
||||
($message:expr) => (
|
||||
return Err(::Error { code: ::ErrorCode::Error, message: Some($message.to_string()) })
|
||||
return Err(::Error { code: ::ResultCode::Error, message: Some($message.to_string()) })
|
||||
);
|
||||
($code:expr, $message:expr) => (
|
||||
return Err(::Error { code: $code, message: $message })
|
||||
@ -64,7 +72,7 @@ macro_rules! success(
|
||||
($result:expr) => (
|
||||
match $result {
|
||||
::raw::SQLITE_OK => {},
|
||||
code => raise!(unsafe { ::std::mem::transmute(code as i8) }, None),
|
||||
code => raise!(::ResultCode::from_raw(code), None),
|
||||
}
|
||||
);
|
||||
);
|
||||
@ -91,8 +99,10 @@ macro_rules! str_to_c_str(
|
||||
);
|
||||
|
||||
mod database;
|
||||
mod statement;
|
||||
|
||||
pub use database::{Database, ExecuteCallback};
|
||||
pub use statement::{Statement, Binding};
|
||||
|
||||
/// Open a database.
|
||||
#[inline]
|
||||
|
65
src/statement.rs
Normal file
65
src/statement.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use libc::{c_double, c_int};
|
||||
use raw;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use {Result, ResultCode};
|
||||
|
||||
/// A prepared statement.
|
||||
pub struct Statement<'l> {
|
||||
raw: *mut raw::sqlite3_stmt,
|
||||
_phantom: PhantomData<&'l raw::sqlite3_stmt>,
|
||||
}
|
||||
|
||||
/// A binding of a prepared statement.
|
||||
pub enum Binding<'l> {
|
||||
Float(usize, f64),
|
||||
Integer(usize, i64),
|
||||
Text(usize, &'l str),
|
||||
}
|
||||
|
||||
impl<'l> Statement<'l> {
|
||||
/// Assign values to the placeholders.
|
||||
pub fn bind(&mut self, bindings: &[Binding]) -> Result<()> {
|
||||
for binding in bindings.iter() {
|
||||
match *binding {
|
||||
Binding::Float(i, value) => unsafe {
|
||||
success!(raw::sqlite3_bind_double(self.raw, i as c_int, value as c_double));
|
||||
},
|
||||
Binding::Integer(i, value) => unsafe {
|
||||
success!(raw::sqlite3_bind_int64(self.raw, i as c_int,
|
||||
value as raw::sqlite3_int64));
|
||||
},
|
||||
Binding::Text(i, value) => unsafe {
|
||||
success!(raw::sqlite3_bind_text(self.raw, i as c_int, str_to_c_str!(value),
|
||||
-1, None));
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Take a step.
|
||||
#[inline]
|
||||
pub fn step(&mut self) -> ResultCode {
|
||||
unsafe { ResultCode::from_raw(raw::sqlite3_step(self.raw)) }
|
||||
}
|
||||
|
||||
/// Reset.
|
||||
#[inline]
|
||||
pub fn reset(&mut self) -> Result<()> {
|
||||
unsafe { success!(raw::sqlite3_reset(self.raw)) };
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Drop for Statement<'l> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { ::raw::sqlite3_finalize(self.raw) };
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_raw<'l>(raw: *mut raw::sqlite3_stmt) -> Statement<'l> {
|
||||
Statement { raw: raw, _phantom: PhantomData }
|
||||
}
|
11
tests/lib.rs
11
tests/lib.rs
@ -9,7 +9,10 @@ macro_rules! ok(
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
fn workflow() {
|
||||
use sqlite::Binding::*;
|
||||
use sqlite::ResultCode;
|
||||
|
||||
macro_rules! pair(
|
||||
($one:expr, $two:expr) => ((String::from($one), String::from($two)));
|
||||
);
|
||||
@ -20,8 +23,10 @@ fn execute() {
|
||||
let sql = r#"CREATE TABLE `users` (id INTEGER, name VARCHAR(255), age REAL);"#;
|
||||
ok!(database.execute(sql, None));
|
||||
|
||||
let sql = r#"INSERT INTO `users` (id, name, age) VALUES (1, "Alice", 20.99);"#;
|
||||
ok!(database.execute(sql, None));
|
||||
let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#;
|
||||
let mut statement = ok!(database.statement(sql));
|
||||
ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)]));
|
||||
assert!(statement.step() == ResultCode::Done);
|
||||
|
||||
let mut done = false;
|
||||
let sql = r#"SELECT * FROM `users`;"#;
|
||||
|
Loading…
x
Reference in New Issue
Block a user