Ensure Virtual tables can be declared outside rusqlite crate

Not sure it is the way to go.
This commit is contained in:
gwenn 2018-07-14 11:01:19 +02:00
parent ce39b9a3c0
commit 823f3c96aa
5 changed files with 149 additions and 17 deletions

View File

@ -58,6 +58,9 @@ harness = false
[[test]] [[test]]
name = "deny_single_threaded_sqlite_config" name = "deny_single_threaded_sqlite_config"
[[test]]
name = "vtab"
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = [ "backup", "blob", "chrono", "functions", "limits", "load_extension", "serde_json", "trace" ] features = [ "backup", "blob", "chrono", "functions", "limits", "load_extension", "serde_json", "trace" ]
all-features = false all-features = false

View File

@ -216,12 +216,12 @@ impl error::Error for Error {
} }
} }
// These are public but not re-exported by lib.rs, so only visible within crate.
pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error { pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
Error::SqliteFailure(ffi::Error::new(code), message) Error::SqliteFailure(ffi::Error::new(code), message)
} }
// These are public but not re-exported by lib.rs, so only visible within crate.
pub fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error { pub fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
let message = if db.is_null() { let message = if db.is_null() {
None None

View File

@ -75,7 +75,7 @@ use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::os::raw::{c_int, c_char}; use std::os::raw::{c_int, c_char};
use types::{ToSql, ValueRef}; use types::{ToSql, ValueRef};
use error::{error_from_sqlite_code, error_from_handle}; use error::error_from_handle;
use raw_statement::RawStatement; use raw_statement::RawStatement;
use cache::StatementCache; use cache::StatementCache;
@ -91,7 +91,7 @@ pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}
#[allow(deprecated)] #[allow(deprecated)]
pub use error::SqliteError; pub use error::SqliteError;
pub use error::Error; pub use error::{Error, error_from_sqlite_code};
pub use ffi::ErrorCode; pub use ffi::ErrorCode;
pub use cache::CachedStatement; pub use cache::CachedStatement;

View File

@ -45,7 +45,7 @@ use {str_to_cstring, Connection, Error, InnerConnection, Result};
// ffi::sqlite3_vtab => VTab // ffi::sqlite3_vtab => VTab
// ffi::sqlite3_vtab_cursor => VTabCursor // ffi::sqlite3_vtab_cursor => VTabCursor
pub struct VTabConnection(*mut ffi::sqlite3); pub struct VTabConnection(pub *mut ffi::sqlite3);
impl VTabConnection { impl VTabConnection {
/// Get access to the underlying SQLite database connection handle. /// Get access to the underlying SQLite database connection handle.
@ -110,7 +110,7 @@ bitflags! {
} }
} }
pub struct IndexInfo(*mut ffi::sqlite3_index_info); pub struct IndexInfo(pub *mut ffi::sqlite3_index_info);
impl IndexInfo { impl IndexInfo {
pub fn constraints(&self) -> IndexConstraintIter { pub fn constraints(&self) -> IndexConstraintIter {
@ -265,7 +265,7 @@ pub trait VTabCursor: Sized {
fn rowid(&self) -> Result<i64>; fn rowid(&self) -> Result<i64>;
} }
pub struct Context(*mut ffi::sqlite3_context); pub struct Context(pub *mut ffi::sqlite3_context);
impl Context { impl Context {
pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> { pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
@ -276,7 +276,7 @@ impl Context {
} }
pub struct Values<'a> { pub struct Values<'a> {
args: &'a [*mut ffi::sqlite3_value], pub args: &'a [*mut ffi::sqlite3_value],
} }
impl<'a> Values<'a> { impl<'a> Values<'a> {
@ -585,6 +585,7 @@ macro_rules! eponymous_module {
}; };
} // eponymous_module macro end } // eponymous_module macro end
#[macro_export]
macro_rules! create_or_connect { macro_rules! create_or_connect {
($module:ident, $vtab:ident, $aux:ty, $create_or_connect:ident, $module_func:ident) => { ($module:ident, $vtab:ident, $aux:ty, $create_or_connect:ident, $module_func:ident) => {
unsafe extern "C" fn $create_or_connect( unsafe extern "C" fn $create_or_connect(
@ -598,7 +599,7 @@ macro_rules! create_or_connect {
use std::error::Error as StdError; use std::error::Error as StdError;
use std::ffi::CStr; use std::ffi::CStr;
use std::slice; use std::slice;
use vtab::mprintf; use $crate::vtab::mprintf;
let mut conn = VTabConnection(db); let mut conn = VTabConnection(db);
let aux = aux as *mut $aux; let aux = aux as *mut $aux;
@ -643,6 +644,7 @@ macro_rules! create_or_connect {
}; };
} // create_or_connect macro end } // create_or_connect macro end
#[macro_export]
macro_rules! common_decl { macro_rules! common_decl {
( (
$module:ident, $module:ident,
@ -667,7 +669,7 @@ macro_rules! common_decl {
info: *mut ffi::sqlite3_index_info, info: *mut ffi::sqlite3_index_info,
) -> c_int { ) -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
use vtab::set_err_msg; use $crate::vtab::set_err_msg;
let vt = vtab as *mut $vtab; let vt = vtab as *mut $vtab;
let mut idx_info = IndexInfo(info); let mut idx_info = IndexInfo(info);
match (*vt).best_index(&mut idx_info) { match (*vt).best_index(&mut idx_info) {
@ -695,7 +697,7 @@ macro_rules! common_decl {
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor, pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
) -> c_int { ) -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
use vtab::set_err_msg; use $crate::vtab::set_err_msg;
let vt = vtab as *mut $vtab; let vt = vtab as *mut $vtab;
match (*vt).open() { match (*vt).open() {
Ok(cursor) => { Ok(cursor) => {
@ -731,7 +733,7 @@ macro_rules! common_decl {
use std::ffi::CStr; use std::ffi::CStr;
use std::slice; use std::slice;
use std::str; use std::str;
use vtab::{cursor_error, Values}; use $crate::vtab::{cursor_error, Values};
let idx_name = if idx_str.is_null() { let idx_name = if idx_str.is_null() {
None None
} else { } else {
@ -744,7 +746,7 @@ macro_rules! common_decl {
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values)) cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
} }
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int { unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
use vtab::cursor_error; use $crate::vtab::cursor_error;
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
cursor_error(cursor, (*cr).next()) cursor_error(cursor, (*cr).next())
} }
@ -757,7 +759,7 @@ macro_rules! common_decl {
ctx: *mut ffi::sqlite3_context, ctx: *mut ffi::sqlite3_context,
i: c_int, i: c_int,
) -> c_int { ) -> c_int {
use vtab::{result_error, Context}; use $crate::vtab::{result_error, Context};
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
let mut ctxt = Context(ctx); let mut ctxt = Context(ctx);
result_error(ctx, (*cr).column(&mut ctxt, i)) result_error(ctx, (*cr).column(&mut ctxt, i))
@ -766,7 +768,7 @@ macro_rules! common_decl {
cursor: *mut ffi::sqlite3_vtab_cursor, cursor: *mut ffi::sqlite3_vtab_cursor,
p_rowid: *mut ffi::sqlite3_int64, p_rowid: *mut ffi::sqlite3_int64,
) -> c_int { ) -> c_int {
use vtab::cursor_error; use $crate::vtab::cursor_error;
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
match (*cr).rowid() { match (*cr).rowid() {
Ok(rowid) => { Ok(rowid) => {
@ -798,7 +800,7 @@ pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Res
} }
/// Virtual tables methods can set an error message by assigning a string to `zErrMsg`. /// Virtual tables methods can set an error message by assigning a string to `zErrMsg`.
unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) { pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
if !(*vtab).zErrMsg.is_null() { if !(*vtab).zErrMsg.is_null() {
ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void); ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void);
} }
@ -807,7 +809,7 @@ unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
/// To raise an error, the `column` method should use this method to set the error message /// To raise an error, the `column` method should use this method to set the error message
/// and return the error code. /// and return the error code.
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int { pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
match result { match result {
Ok(_) => ffi::SQLITE_OK, Ok(_) => ffi::SQLITE_OK,

127
tests/vtab.rs Normal file
View File

@ -0,0 +1,127 @@
//! Ensure Virtual tables can be declared outside `rusqlite` crate.
#[cfg(feature = "vtab")]
#[macro_use]
extern crate rusqlite;
extern crate libsqlite3_sys as ffi;
#[cfg(feature = "vtab")]
#[test]
fn test_dummy_module() {
use ffi;
use rusqlite::vtab::{Context, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
use rusqlite::{error_from_sqlite_code, Connection, Error, Result};
use std::os::raw::{c_char, c_int, c_void};
eponymous_module!(
DUMMY_MODULE,
DummyModule,
DummyTab,
(),
DummyTabCursor,
None,
dummy_connect,
dummy_best_index,
dummy_disconnect,
None,
dummy_open,
dummy_close,
dummy_filter,
dummy_next,
dummy_eof,
dummy_column,
dummy_rowid
);
#[repr(C)]
struct DummyModule(&'static ffi::sqlite3_module);
impl Module for DummyModule {
type Aux = ();
type Table = DummyTab;
fn as_ptr(&self) -> *const ffi::sqlite3_module {
self.0
}
fn connect(
_: &mut VTabConnection,
_aux: Option<&()>,
_args: &[&[u8]],
) -> Result<(String, DummyTab)> {
let vtab = DummyTab {
base: ffi::sqlite3_vtab::default(),
};
Ok(("CREATE TABLE x(value)".to_owned(), vtab))
}
}
#[repr(C)]
struct DummyTab {
/// Base class. Must be first
base: ffi::sqlite3_vtab,
}
impl VTab for DummyTab {
type Cursor = DummyTabCursor;
fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
info.set_estimated_cost(1.);
Ok(())
}
fn open(&self) -> Result<DummyTabCursor> {
Ok(DummyTabCursor::default())
}
}
#[derive(Default)]
#[repr(C)]
struct DummyTabCursor {
/// Base class. Must be first
base: ffi::sqlite3_vtab_cursor,
/// The rowid
row_id: i64,
}
impl VTabCursor for DummyTabCursor {
type Table = DummyTab;
fn vtab(&self) -> &DummyTab {
unsafe { &*(self.base.pVtab as *const DummyTab) }
}
fn filter(
&mut self,
_idx_num: c_int,
_idx_str: Option<&str>,
_args: &Values,
) -> Result<()> {
self.row_id = 1;
Ok(())
}
fn next(&mut self) -> Result<()> {
self.row_id += 1;
Ok(())
}
fn eof(&self) -> bool {
self.row_id > 1
}
fn column(&self, ctx: &mut Context, _: c_int) -> Result<()> {
ctx.set_result(&self.row_id)
}
fn rowid(&self) -> Result<i64> {
Ok(self.row_id)
}
}
let db = Connection::open_in_memory().unwrap();
let module = DummyModule(&DUMMY_MODULE);
db.create_module("dummy", module, None).unwrap();
let mut s = db.prepare("SELECT * FROM dummy()").unwrap();
let dummy = s.query_row(&[], |row| row.get::<_, i32>(0)).unwrap();
assert_eq!(1, dummy);
}