mirror of
https://github.com/fluencelabs/sqlite-wasm-connector
synced 2025-04-25 08:42:14 +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::marker::PhantomData;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use Result;
|
use {Result, Statement};
|
||||||
|
|
||||||
/// A database.
|
/// A database.
|
||||||
pub struct Database<'d> {
|
pub struct Database<'l> {
|
||||||
db: *mut raw::sqlite3,
|
raw: *mut raw::sqlite3,
|
||||||
_phantom: PhantomData<&'d raw::sqlite3>,
|
_phantom: PhantomData<&'l raw::sqlite3>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A callback executed for each row of the result of an SQL query.
|
/// 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.
|
/// Open a database.
|
||||||
pub fn open(path: &Path) -> Result<Database<'d>> {
|
pub fn open(path: &Path) -> Result<Database> {
|
||||||
let mut db = 0 as *mut _;
|
let mut raw = 0 as *mut _;
|
||||||
unsafe {
|
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.
|
/// Execute an SQL statement.
|
||||||
@ -32,24 +32,35 @@ impl<'d> Database<'d> {
|
|||||||
match callback {
|
match callback {
|
||||||
Some(callback) => {
|
Some(callback) => {
|
||||||
let mut callback = Box::new(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 _));
|
&mut callback as *mut _ as *mut _, 0 as *mut _));
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
success!(raw::sqlite3_exec(self.db, str_to_c_str!(sql), None,
|
success!(raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _,
|
||||||
0 as *mut _, 0 as *mut _));
|
0 as *mut _));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
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 libc;
|
||||||
extern crate sqlite3_sys as raw;
|
extern crate sqlite3_sys as raw;
|
||||||
|
|
||||||
|
use libc::c_int;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// A result.
|
/// A result.
|
||||||
@ -11,13 +12,13 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||||||
/// An error.
|
/// An error.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
pub code: ErrorCode,
|
pub code: ResultCode,
|
||||||
pub message: Option<String>,
|
pub message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error code.
|
/// A result code.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum ErrorCode {
|
pub enum ResultCode {
|
||||||
Abort = raw::SQLITE_ABORT as isize,
|
Abort = raw::SQLITE_ABORT as isize,
|
||||||
Authorization = raw::SQLITE_AUTH as isize,
|
Authorization = raw::SQLITE_AUTH as isize,
|
||||||
Busy = raw::SQLITE_BUSY as isize,
|
Busy = raw::SQLITE_BUSY as isize,
|
||||||
@ -51,9 +52,16 @@ pub enum ErrorCode {
|
|||||||
Warning = raw::SQLITE_WARNING as isize,
|
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(
|
macro_rules! raise(
|
||||||
($message:expr) => (
|
($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) => (
|
($code:expr, $message:expr) => (
|
||||||
return Err(::Error { code: $code, message: $message })
|
return Err(::Error { code: $code, message: $message })
|
||||||
@ -64,7 +72,7 @@ macro_rules! success(
|
|||||||
($result:expr) => (
|
($result:expr) => (
|
||||||
match $result {
|
match $result {
|
||||||
::raw::SQLITE_OK => {},
|
::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 database;
|
||||||
|
mod statement;
|
||||||
|
|
||||||
pub use database::{Database, ExecuteCallback};
|
pub use database::{Database, ExecuteCallback};
|
||||||
|
pub use statement::{Statement, Binding};
|
||||||
|
|
||||||
/// Open a database.
|
/// Open a database.
|
||||||
#[inline]
|
#[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]
|
#[test]
|
||||||
fn execute() {
|
fn workflow() {
|
||||||
|
use sqlite::Binding::*;
|
||||||
|
use sqlite::ResultCode;
|
||||||
|
|
||||||
macro_rules! pair(
|
macro_rules! pair(
|
||||||
($one:expr, $two:expr) => ((String::from($one), String::from($two)));
|
($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);"#;
|
let sql = r#"CREATE TABLE `users` (id INTEGER, name VARCHAR(255), age REAL);"#;
|
||||||
ok!(database.execute(sql, None));
|
ok!(database.execute(sql, None));
|
||||||
|
|
||||||
let sql = r#"INSERT INTO `users` (id, name, age) VALUES (1, "Alice", 20.99);"#;
|
let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#;
|
||||||
ok!(database.execute(sql, None));
|
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 mut done = false;
|
||||||
let sql = r#"SELECT * FROM `users`;"#;
|
let sql = r#"SELECT * FROM `users`;"#;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user