mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 11:31:37 +08:00
IntArray virtual table.
This commit is contained in:
parent
2d6705b03e
commit
e17678b173
@ -89,7 +89,7 @@ mod error;
|
||||
#[cfg(feature = "backup")]pub mod backup;
|
||||
#[cfg(feature = "functions")] pub mod functions;
|
||||
#[cfg(feature = "blob")] pub mod blob;
|
||||
#[cfg(feature = "vtab")] pub mod vtab;
|
||||
#[cfg(all(feature = "vtab", feature = "functions"))] pub mod vtab;
|
||||
|
||||
/// Old name for `Result`. `SqliteResult` is deprecated.
|
||||
pub type SqliteResult<T> = Result<T>;
|
||||
|
239
src/vtab.rs
239
src/vtab.rs
@ -1,15 +1,17 @@
|
||||
//! Create virtual tables.
|
||||
//! (See http://sqlite.org/vtab.html)
|
||||
use std::cell::RefCell;
|
||||
use std::default::Default;
|
||||
use std::error::Error as StdError;
|
||||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use libc;
|
||||
|
||||
use {Connection, Error, Result, InnerConnection, str_to_cstring};
|
||||
use error::error_from_sqlite_code;
|
||||
use ffi;
|
||||
use functions::{ToResult, NoMem, TooBig};
|
||||
use functions::ToResult;
|
||||
|
||||
// let conn: Connection = ...;
|
||||
// let mod: Module = ...; // VTab builder
|
||||
@ -52,12 +54,52 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
impl InnerConnection {
|
||||
fn create_module<A>(&mut self,
|
||||
module_name: &str,
|
||||
module: *const ffi::sqlite3_module,
|
||||
aux: A)
|
||||
-> Result<()> {
|
||||
let boxed_aux: *mut A = Box::into_raw(Box::new(aux));
|
||||
let c_name = try!(str_to_cstring(module_name));
|
||||
let r = unsafe {
|
||||
ffi::sqlite3_create_module_v2(self.db(),
|
||||
c_name.as_ptr(),
|
||||
module,
|
||||
mem::transmute(boxed_aux),
|
||||
Some(mem::transmute(free_boxed_value::<A>)))
|
||||
};
|
||||
self.decode_result(r)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME copy/paste from function.rs
|
||||
unsafe extern "C" fn free_boxed_value<T>(p: *mut libc::c_void) {
|
||||
let _: Box<T> = Box::from_raw(mem::transmute(p));
|
||||
}
|
||||
|
||||
static RUST_MODULE: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
pub fn create_int_array(conn: &Connection, name: &str) -> Result<Rc<RefCell<Vec<i64>>>> {
|
||||
let array = Rc::new(RefCell::new(Vec::new()));
|
||||
try!(conn.create_module(name, &INT_ARRAY_MODULE, array.clone()));
|
||||
try!(conn.execute_batch(&format!("CREATE VIRTUAL TABLE temp.{0} USING {0}",
|
||||
escape_quote(name.to_string()))));
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
pub fn drop_int_array(conn: &Connection, name: &str) -> Result<()> {
|
||||
conn.execute_batch(&format!("DROP TABLE temp.{0}", escape_quote(name.to_string())))
|
||||
}
|
||||
|
||||
fn escape_quote(identifier: String) -> String {
|
||||
if identifier.contains('"') {
|
||||
// escape quote by doubling them
|
||||
identifier.replace('"', "\"\"")
|
||||
} else {
|
||||
identifier
|
||||
}
|
||||
}
|
||||
|
||||
static INT_ARRAY_MODULE: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||
iVersion: 1,
|
||||
xCreate: Some(x_create),
|
||||
xConnect: Some(x_create), /* A virtual table is eponymous if its xCreate method is the exact same function as the xConnect method */
|
||||
@ -90,9 +132,9 @@ unsafe extern "C" fn x_create(db: *mut ffi::sqlite3,
|
||||
pp_vtab: *mut *mut ffi::sqlite3_vtab,
|
||||
err_msg: *mut *mut libc::c_char)
|
||||
-> libc::c_int {
|
||||
match VTab::new(db, aux, argc, argv) {
|
||||
match IntArrayVTab::create(db, aux, argc, argv) {
|
||||
Ok(vtab) => {
|
||||
let boxed_vtab: *mut VTab = Box::into_raw(Box::new(vtab));
|
||||
let boxed_vtab: *mut IntArrayVTab = Box::into_raw(Box::new(vtab));
|
||||
*pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
@ -114,31 +156,35 @@ unsafe extern "C" fn x_create(db: *mut ffi::sqlite3,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn x_destroy(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int {
|
||||
let vtab = vtab as *mut VTab;
|
||||
let _: Box<VTab> = Box::from_raw(mem::transmute(vtab));
|
||||
let vtab = vtab as *mut IntArrayVTab;
|
||||
let _: Box<IntArrayVTab> = Box::from_raw(mem::transmute(vtab));
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct VTab {
|
||||
struct IntArrayVTab {
|
||||
/// Base class
|
||||
base: ffi::sqlite3_vtab,
|
||||
array: *const Rc<RefCell<Vec<i64>>>,
|
||||
}
|
||||
|
||||
impl VTab {
|
||||
fn new(db: *mut ffi::sqlite3,
|
||||
aux: *mut libc::c_void,
|
||||
argc: libc::c_int,
|
||||
argv: *const *const libc::c_char)
|
||||
-> Result<VTab> {
|
||||
unimplemented!()
|
||||
impl IntArrayVTab {
|
||||
fn create(db: *mut ffi::sqlite3,
|
||||
aux: *mut libc::c_void,
|
||||
_argc: libc::c_int,
|
||||
_argv: *const *const libc::c_char)
|
||||
-> Result<IntArrayVTab> {
|
||||
let array = unsafe { mem::transmute(aux) };
|
||||
let vtab = IntArrayVTab {
|
||||
base: Default::default(),
|
||||
array: array,
|
||||
};
|
||||
try!(IntArrayVTab::declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"));
|
||||
Ok(vtab)
|
||||
}
|
||||
fn open(&self) -> VTabCursor {
|
||||
VTabCursor::new()
|
||||
}
|
||||
unsafe fn declare_vtab(db: *mut ffi::sqlite3, sql: &str) -> Result<()> {
|
||||
fn declare_vtab(db: *mut ffi::sqlite3, sql: &str) -> Result<()> {
|
||||
let c_sql = try!(CString::new(sql));
|
||||
let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
|
||||
let rc = unsafe { ffi::sqlite3_declare_vtab(db, c_sql.as_ptr()) };
|
||||
if rc == ffi::SQLITE_OK {
|
||||
Ok(())
|
||||
} else {
|
||||
@ -146,54 +192,65 @@ impl VTab {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_err_msg(&mut self, err_msg: &str) -> libc::c_int {
|
||||
fn best_index(&self, _info: *mut ffi::sqlite3_index_info) {
|
||||
// unimplemented!()
|
||||
}
|
||||
|
||||
fn open(&self) -> IntArrayVTabCursor {
|
||||
IntArrayVTabCursor::new()
|
||||
}
|
||||
|
||||
fn set_err_msg(&mut self, err_msg: &str) {
|
||||
if !self.base.zErrMsg.is_null() {
|
||||
ffi::sqlite3_free(self.base.zErrMsg as *mut libc::c_void)
|
||||
unsafe {
|
||||
ffi::sqlite3_free(self.base.zErrMsg as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
self.base.zErrMsg = mprintf(err_msg);
|
||||
return ffi::SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn x_best_index(_vtab: *mut ffi::sqlite3_vtab,
|
||||
_info: *mut ffi::sqlite3_index_info)
|
||||
unsafe extern "C" fn x_best_index(vtab: *mut ffi::sqlite3_vtab,
|
||||
info: *mut ffi::sqlite3_index_info)
|
||||
-> libc::c_int {
|
||||
let vtab = vtab as *mut IntArrayVTab;
|
||||
(*vtab).best_index(info);
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
|
||||
unsafe extern "C" fn x_open(vtab: *mut ffi::sqlite3_vtab,
|
||||
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor)
|
||||
-> libc::c_int {
|
||||
let vtab = vtab as *mut VTab;
|
||||
let vtab = vtab as *mut IntArrayVTab;
|
||||
let cursor = (*vtab).open();
|
||||
let boxed_cursor: *mut VTabCursor = Box::into_raw(Box::new(cursor));
|
||||
let boxed_cursor: *mut IntArrayVTabCursor = Box::into_raw(Box::new(cursor));
|
||||
*pp_cursor = boxed_cursor as *mut ffi::sqlite3_vtab_cursor;
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
unsafe extern "C" fn x_close(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int {
|
||||
let cursor = cursor as *mut VTabCursor;
|
||||
let _: Box<VTabCursor> = Box::from_raw(mem::transmute(cursor));
|
||||
let cursor = cursor as *mut IntArrayVTabCursor;
|
||||
let _: Box<IntArrayVTabCursor> = Box::from_raw(mem::transmute(cursor));
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct VTabCursor {
|
||||
struct IntArrayVTabCursor {
|
||||
/// Base class
|
||||
base: ffi::sqlite3_vtab_cursor,
|
||||
/// Current cursor position
|
||||
i: usize,
|
||||
}
|
||||
|
||||
impl VTabCursor {
|
||||
fn new() -> VTabCursor {
|
||||
VTabCursor {
|
||||
impl IntArrayVTabCursor {
|
||||
fn new() -> IntArrayVTabCursor {
|
||||
IntArrayVTabCursor {
|
||||
base: Default::default(),
|
||||
i: 0,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn vtab(&self) -> *mut VTab {
|
||||
self.base.pVtab as *mut VTab
|
||||
fn vtab(&self) -> *mut IntArrayVTab {
|
||||
self.base.pVtab as *mut IntArrayVTab
|
||||
}
|
||||
|
||||
fn filter(&mut self) -> libc::c_int {
|
||||
@ -205,11 +262,19 @@ impl VTabCursor {
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
fn eof(&self) -> bool {
|
||||
unimplemented!()
|
||||
let vtab = self.vtab();
|
||||
unsafe {
|
||||
let array = (*(*vtab).array).borrow();
|
||||
self.i >= array.len()
|
||||
}
|
||||
}
|
||||
fn column(&self, ctx: *mut ffi::sqlite3_context, i: libc::c_int) -> libc::c_int {
|
||||
// FIXME Result<()>
|
||||
unimplemented!()
|
||||
fn column(&self, ctx: *mut ffi::sqlite3_context, _i: libc::c_int) -> libc::c_int {
|
||||
let vtab = self.vtab();
|
||||
unsafe {
|
||||
let array = (*(*vtab).array).borrow();
|
||||
array[self.i].set_result(ctx);
|
||||
}
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
fn rowid(&self) -> i64 {
|
||||
self.i as i64
|
||||
@ -222,33 +287,33 @@ unsafe extern "C" fn x_filter(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
_argc: libc::c_int,
|
||||
_argv: *mut *mut ffi::sqlite3_value)
|
||||
-> libc::c_int {
|
||||
let cursor = cursor as *mut VTabCursor;
|
||||
let cursor = cursor as *mut IntArrayVTabCursor;
|
||||
(*cursor).filter()
|
||||
}
|
||||
unsafe extern "C" fn x_next(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int {
|
||||
let cursor = cursor as *mut VTabCursor;
|
||||
let cursor = cursor as *mut IntArrayVTabCursor;
|
||||
(*cursor).next()
|
||||
}
|
||||
unsafe extern "C" fn x_eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int {
|
||||
let cursor = cursor as *mut VTabCursor;
|
||||
let cursor = cursor as *mut IntArrayVTabCursor;
|
||||
(*cursor).eof() as libc::c_int
|
||||
}
|
||||
unsafe extern "C" fn x_column(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
ctx: *mut ffi::sqlite3_context,
|
||||
i: libc::c_int)
|
||||
-> libc::c_int {
|
||||
let cursor = cursor as *mut VTabCursor;
|
||||
let cursor = cursor as *mut IntArrayVTabCursor;
|
||||
(*cursor).column(ctx, i)
|
||||
}
|
||||
unsafe extern "C" fn x_rowid(cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||
p_rowid: *mut ffi::sqlite3_int64)
|
||||
-> libc::c_int {
|
||||
let cursor = cursor as *mut VTabCursor;
|
||||
let cursor = cursor as *mut IntArrayVTabCursor;
|
||||
*p_rowid = (*cursor).rowid();
|
||||
ffi::SQLITE_OK
|
||||
}
|
||||
|
||||
unsafe extern "C" fn result_error(ctx: *mut ffi::sqlite3_context, err: Error) -> libc::c_int {
|
||||
unsafe fn result_error(ctx: *mut ffi::sqlite3_context, err: Error) -> libc::c_int {
|
||||
match err {
|
||||
Error::SqliteFailure(err, s) => {
|
||||
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
||||
@ -268,33 +333,75 @@ unsafe extern "C" fn result_error(ctx: *mut ffi::sqlite3_context, err: Error) ->
|
||||
}
|
||||
|
||||
// Space to hold this error message string must be obtained from an SQLite memory allocation function.
|
||||
unsafe fn mprintf(err_msg: &str) -> *mut ::libc::c_char {
|
||||
fn mprintf(err_msg: &str) -> *mut ::libc::c_char {
|
||||
let c_format = CString::new("%s").unwrap();
|
||||
let c_err = CString::new(err_msg).unwrap();
|
||||
ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr())
|
||||
}
|
||||
|
||||
impl InnerConnection {
|
||||
fn create_module<A>(&mut self,
|
||||
module_name: &str,
|
||||
module: *const ffi::sqlite3_module,
|
||||
aux: A)
|
||||
-> Result<()> {
|
||||
// FIXME Both rust_module and aux need to be boxed
|
||||
let boxed_aux: *mut A = Box::into_raw(Box::new(aux));
|
||||
let c_name = try!(str_to_cstring(module_name));
|
||||
let r = unsafe {
|
||||
ffi::sqlite3_create_module_v2(self.db(),
|
||||
c_name.as_ptr(),
|
||||
module,
|
||||
mem::transmute(boxed_aux),
|
||||
Some(mem::transmute(free_boxed_value::<A>)))
|
||||
};
|
||||
self.decode_result(r)
|
||||
}
|
||||
unsafe { ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr()) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use vtab;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_int_array_module() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE t1 (x INT);
|
||||
INSERT INTO t1 VALUES (1), (3);
|
||||
CREATE TABLE t2 (y INT);
|
||||
INSERT INTO t2 VALUES (11);
|
||||
CREATE TABLE t3 (z INT);
|
||||
INSERT INTO t3 VALUES (-5);").unwrap();
|
||||
let p1 = vtab::create_int_array(&db, "ex1").unwrap();
|
||||
let p2 = vtab::create_int_array(&db, "ex2").unwrap();
|
||||
let p3 = vtab::create_int_array(&db, "ex3").unwrap();
|
||||
|
||||
let mut s = db.prepare("SELECT * FROM t1, t2, t3
|
||||
WHERE t1.x IN ex1
|
||||
AND t2.y IN ex2
|
||||
AND t3.z IN ex3").unwrap();
|
||||
|
||||
p1.borrow_mut().append(&mut vec![1, 2, 3, 4]);
|
||||
p2.borrow_mut().append(&mut vec![5, 6, 7, 8, 9, 10, 11]);
|
||||
p3.borrow_mut().append(&mut vec![-1, -5, -10]);
|
||||
|
||||
{
|
||||
let rows = s.query(&[]).unwrap();
|
||||
for row in rows {
|
||||
let row = row.unwrap();
|
||||
let i1: i64 = row.get(0);
|
||||
assert!(i1 == 1 || i1 == 3);
|
||||
assert_eq!(11, row.get(1));
|
||||
assert_eq!(-5, row.get(2));
|
||||
}
|
||||
}
|
||||
|
||||
s.reset_if_needed();
|
||||
p1.borrow_mut().clear();
|
||||
p2.borrow_mut().clear();
|
||||
p3.borrow_mut().clear();
|
||||
p1.borrow_mut().append(&mut vec![1]);
|
||||
p2.borrow_mut().append(&mut vec![7, 11]);
|
||||
p3.borrow_mut().append(&mut vec![-5, -10]);
|
||||
|
||||
{
|
||||
let row = s.query(&[]).unwrap().next().unwrap().unwrap();
|
||||
assert_eq!(1, row.get(0));
|
||||
assert_eq!(11, row.get(1));
|
||||
assert_eq!(-5, row.get(2));
|
||||
}
|
||||
|
||||
s.reset_if_needed();
|
||||
p2.borrow_mut().clear();
|
||||
p3.borrow_mut().clear();
|
||||
p2.borrow_mut().append(&mut vec![3, 4, 5]);
|
||||
p3.borrow_mut().append(&mut vec![0, -5]);
|
||||
assert!(s.query(&[]).unwrap().next().is_none());
|
||||
|
||||
vtab::drop_int_array(&db, "ex1").unwrap();
|
||||
vtab::drop_int_array(&db, "ex2").unwrap();
|
||||
vtab::drop_int_array(&db, "ex3").unwrap();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user