Remove macros

This commit is contained in:
gwenn 2018-07-14 18:47:52 +02:00
parent b19d050e39
commit e2df03f474
8 changed files with 406 additions and 548 deletions

View File

@ -29,7 +29,7 @@ limits = []
hooks = [] hooks = []
sqlcipher = ["libsqlite3-sys/sqlcipher"] sqlcipher = ["libsqlite3-sys/sqlcipher"]
unlock_notify = ["libsqlite3-sys/unlock_notify"] unlock_notify = ["libsqlite3-sys/unlock_notify"]
vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"] vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7", "lazy_static"]
csvtab = ["csv", "vtab"] csvtab = ["csv", "vtab"]
# pointer passing interfaces: 3.20.0 # pointer passing interfaces: 3.20.0
array = ["vtab"] array = ["vtab"]
@ -41,6 +41,7 @@ lru-cache = "0.1"
chrono = { version = "0.4", optional = true } chrono = { version = "0.4", optional = true }
serde_json = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true }
csv = { version = "1.0", optional = true } csv = { version = "1.0", optional = true }
lazy_static = { version = "1.0", optional = true }
[dev-dependencies] [dev-dependencies]
tempdir = "0.3" tempdir = "0.3"
@ -62,7 +63,7 @@ name = "deny_single_threaded_sqlite_config"
name = "vtab" 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", "vtab" ]
all-features = false all-features = false
no-default-features = true no-default-features = true
default-target = "x86_64-unknown-linux-gnu" default-target = "x86_64-unknown-linux-gnu"

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

@ -56,7 +56,7 @@ extern crate libsqlite3_sys as ffi;
extern crate lru_cache; extern crate lru_cache;
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
#[cfg(all(test, feature = "trace"))] #[cfg(any(all(test, feature = "trace"), feature = "vtab"))]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -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_handle; use error::{error_from_sqlite_code, 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, error_from_sqlite_code}; pub use error::Error;
pub use ffi::ErrorCode; pub use ffi::ErrorCode;
pub use cache::CachedStatement; pub use cache::CachedStatement;

View File

@ -4,11 +4,10 @@ use std::default::Default;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc; use std::rc::Rc;
use error::error_from_sqlite_code;
use ffi; use ffi;
use types::{ToSql, ToSqlOutput, Value}; use types::{ToSql, ToSqlOutput, Value};
use vtab::{self, Context, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values}; use vtab::{eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
use {Connection, Error, Result}; use {Connection, Result};
// http://sqlite.org/bindptr.html // http://sqlite.org/bindptr.html
@ -29,50 +28,11 @@ impl ToSql for Array {
/// Register the "rarray" module. /// Register the "rarray" module.
pub fn load_module(conn: &Connection) -> Result<()> { pub fn load_module(conn: &Connection) -> Result<()> {
let aux: Option<()> = None; let aux: Option<()> = None;
conn.create_module("rarray", ArrayModule(&ARRAY_MODULE), aux) conn.create_module::<ArrayTab>("rarray", &ARRAY_MODULE, aux)
} }
eponymous_module!( lazy_static! {
ARRAY_MODULE, static ref ARRAY_MODULE: Module<ArrayTab> = eponymous_only_module::<ArrayTab>();
ArrayModule,
ArrayTab,
(),
ArrayTabCursor,
None,
array_connect,
array_best_index,
array_disconnect,
None,
array_open,
array_close,
array_filter,
array_next,
array_eof,
array_column,
array_rowid
);
#[repr(C)]
struct ArrayModule(&'static ffi::sqlite3_module);
impl Module for ArrayModule {
type Aux = ();
type Table = ArrayTab;
fn as_ptr(&self) -> *const ffi::sqlite3_module {
self.0
}
fn connect(
_: &mut VTabConnection,
_aux: Option<&()>,
_args: &[&[u8]],
) -> Result<(String, ArrayTab)> {
let vtab = ArrayTab {
base: ffi::sqlite3_vtab::default(),
};
Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
}
} }
// Column numbers // Column numbers
@ -87,8 +47,20 @@ struct ArrayTab {
} }
impl VTab for ArrayTab { impl VTab for ArrayTab {
type Aux = ();
type Cursor = ArrayTabCursor; type Cursor = ArrayTabCursor;
fn connect(
_: &mut VTabConnection,
_aux: Option<&()>,
_args: &[&[u8]],
) -> Result<(String, ArrayTab)> {
let vtab = ArrayTab {
base: ffi::sqlite3_vtab::default(),
};
Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
}
fn best_index(&self, info: &mut IndexInfo) -> Result<()> { fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
// Index of the pointer= constraint // Index of the pointer= constraint
let mut ptr_idx = None; let mut ptr_idx = None;
@ -96,7 +68,7 @@ impl VTab for ArrayTab {
if !constraint.is_usable() { if !constraint.is_usable() {
continue; continue;
} }
if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ { if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
continue; continue;
} }
if let CARRAY_COLUMN_POINTER = constraint.column() { if let CARRAY_COLUMN_POINTER = constraint.column() {

View File

@ -2,16 +2,15 @@
//! Port of [csv](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/csv.c) C extension. //! Port of [csv](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/csv.c) C extension.
extern crate csv; extern crate csv;
use std::fs::File; use std::fs::File;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::c_int;
use std::path::Path; use std::path::Path;
use std::result; use std::result;
use std::str; use std::str;
use error::error_from_sqlite_code;
use ffi; use ffi;
use types::Null; use types::Null;
use vtab::{ use vtab::{
dequote, escape_double_quote, parse_boolean, Context, IndexInfo, Module, VTab, VTabConnection, dequote, escape_double_quote, parse_boolean, simple_module, Context, IndexInfo, Module, VTab, VTabConnection,
VTabCursor, Values, VTabCursor, Values,
}; };
use {Connection, Error, Result}; use {Connection, Error, Result};
@ -29,32 +28,36 @@ use {Connection, Error, Result};
/// ``` /// ```
pub fn load_module(conn: &Connection) -> Result<()> { pub fn load_module(conn: &Connection) -> Result<()> {
let aux: Option<()> = None; let aux: Option<()> = None;
conn.create_module("csv", CSVModule(&CSV_MODULE), aux) conn.create_module::<CSVTab>("csv", &CSV_MODULE, aux)
} }
init_module!( lazy_static! {
CSV_MODULE, static ref CSV_MODULE: Module<CSVTab> = simple_module::<CSVTab>();
CSVModule, }
CSVTab,
(),
CSVTabCursor,
csv_create,
csv_connect,
csv_best_index,
csv_disconnect,
csv_disconnect,
csv_open,
csv_close,
csv_filter,
csv_next,
csv_eof,
csv_column,
csv_rowid
);
struct CSVModule(&'static ffi::sqlite3_module); /// An instance of the CSV virtual table
#[repr(C)]
struct CSVTab {
/// Base class. Must be first
base: ffi::sqlite3_vtab,
/// Name of the CSV file
filename: String,
has_headers: bool,
delimiter: u8,
quote: u8,
/// Offset to start of data
offset_first_row: csv::Position,
}
impl CSVTab {
fn reader(&self) -> result::Result<csv::Reader<File>, csv::Error> {
csv::ReaderBuilder::new()
.has_headers(self.has_headers)
.delimiter(self.delimiter)
.quote(self.quote)
.from_path(&self.filename)
}
impl CSVModule {
fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> { fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> {
let arg = try!(str::from_utf8(c_slice)).trim(); let arg = try!(str::from_utf8(c_slice)).trim();
let mut split = arg.split('='); let mut split = arg.split('=');
@ -77,13 +80,9 @@ impl CSVModule {
} }
} }
impl Module for CSVModule { impl VTab for CSVTab {
type Aux = (); type Aux = ();
type Table = CSVTab; type Cursor = CSVTabCursor;
fn as_ptr(&self) -> *const ffi::sqlite3_module {
self.0
}
fn connect( fn connect(
_: &mut VTabConnection, _: &mut VTabConnection,
@ -107,7 +106,7 @@ impl Module for CSVModule {
let args = &args[3..]; let args = &args[3..];
for c_slice in args { for c_slice in args {
let (param, value) = try!(CSVModule::parameter(c_slice)); let (param, value) = try!(CSVTab::parameter(c_slice));
match param { match param {
"filename" => { "filename" => {
if !Path::new(value).exists() { if !Path::new(value).exists() {
@ -151,7 +150,7 @@ impl Module for CSVModule {
} }
} }
"delimiter" => { "delimiter" => {
if let Some(b) = CSVModule::parse_byte(value) { if let Some(b) = CSVTab::parse_byte(value) {
vtab.delimiter = b; vtab.delimiter = b;
} else { } else {
return Err(Error::ModuleError(format!( return Err(Error::ModuleError(format!(
@ -161,7 +160,7 @@ impl Module for CSVModule {
} }
} }
"quote" => { "quote" => {
if let Some(b) = CSVModule::parse_byte(value) { if let Some(b) = CSVTab::parse_byte(value) {
if b == b'0' { if b == b'0' {
vtab.quote = 0; vtab.quote = 0;
} else { } else {
@ -237,34 +236,6 @@ impl Module for CSVModule {
Ok((schema.unwrap().to_owned(), vtab)) Ok((schema.unwrap().to_owned(), vtab))
} }
}
/// An instance of the CSV virtual table
#[repr(C)]
struct CSVTab {
/// Base class. Must be first
base: ffi::sqlite3_vtab,
/// Name of the CSV file
filename: String,
has_headers: bool,
delimiter: u8,
quote: u8,
/// Offset to start of data
offset_first_row: csv::Position,
}
impl CSVTab {
fn reader(&self) -> result::Result<csv::Reader<File>, csv::Error> {
csv::ReaderBuilder::new()
.has_headers(self.has_headers)
.delimiter(self.delimiter)
.quote(self.quote)
.from_path(&self.filename)
}
}
impl VTab for CSVTab {
type Cursor = CSVTabCursor;
// Only a forward full table scan is supported. // Only a forward full table scan is supported.
fn best_index(&self, info: &mut IndexInfo) -> Result<()> { fn best_index(&self, info: &mut IndexInfo) -> Result<()> {

View File

@ -2,11 +2,14 @@
//! (See http://sqlite.org/vtab.html) //! (See http://sqlite.org/vtab.html)
use std::borrow::Cow::{self, Borrowed, Owned}; use std::borrow::Cow::{self, Borrowed, Owned};
use std::ffi::CString; use std::ffi::CString;
use std::marker::PhantomData;
use std::marker::Sync;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use context::set_result; use context::set_result;
use error::error_from_sqlite_code;
use ffi; use ffi;
use types::{FromSql, FromSqlError, ToSql, ValueRef}; use types::{FromSql, FromSqlError, ToSql, ValueRef};
use {str_to_cstring, Connection, Error, InnerConnection, Result}; use {str_to_cstring, Connection, Error, InnerConnection, Result};
@ -39,13 +42,91 @@ use {str_to_cstring, Connection, Error, InnerConnection, Result};
// \-> if not eof { cursor.column or xrowid } else { cursor.xclose } // \-> if not eof { cursor.column or xrowid } else { cursor.xclose }
// //
// db: *mut ffi::sqlite3 // db: *mut ffi::sqlite3 => VTabConnection
// module: *const ffi::sqlite3_module => Module // module: *const ffi::sqlite3_module => Module
// aux: *mut c_void => Module::Aux // aux: *mut c_void => Module::Aux
// ffi::sqlite3_vtab => VTab // ffi::sqlite3_vtab => VTab
// ffi::sqlite3_vtab_cursor => VTabCursor // ffi::sqlite3_vtab_cursor => VTabCursor
pub struct VTabConnection(pub *mut ffi::sqlite3); #[repr(C)]
pub struct Module<T: VTab> {
base: ffi::sqlite3_module,
phantom: PhantomData<T>,
}
unsafe impl<T: VTab> Sync for Module<T> {}
/// Create a read-only virtual table implementation.
pub fn simple_module<T: VTab>() -> Module<T> {
// The xConnect and xCreate methods do the same thing, but they must be
// different so that the virtual table is not an eponymous virtual table.
let ffi_module = ffi::sqlite3_module {
iVersion: 1,
xCreate: Some(rust_create::<T>),
xConnect: Some(rust_connect::<T>),
xBestIndex: Some(rust_best_index::<T>),
xDisconnect: Some(rust_disconnect::<T>),
xDestroy: Some(rust_disconnect::<T>), // TODO Validate: no rust_destroy
xOpen: Some(rust_open::<T>),
xClose: Some(rust_close::<T::Cursor>),
xFilter: Some(rust_filter::<T::Cursor>),
xNext: Some(rust_next::<T::Cursor>),
xEof: Some(rust_eof::<T::Cursor>),
xColumn: Some(rust_column::<T::Cursor>),
xRowid: Some(rust_rowid::<T::Cursor>),
xUpdate: None,
xBegin: None,
xSync: None,
xCommit: None,
xRollback: None,
xFindFunction: None,
xRename: None,
xSavepoint: None,
xRelease: None,
xRollbackTo: None,
};
Module {
base: ffi_module,
phantom: PhantomData::<T>,
}
}
/// Create an eponymous only virtual table implementation.
pub fn eponymous_only_module<T: VTab>() -> Module<T> {
// A virtual table is eponymous if its xCreate method is the exact same function as the xConnect method
// For eponymous-only virtual tables, the xCreate method is NULL
let ffi_module = ffi::sqlite3_module {
iVersion: 1,
xCreate: None,
xConnect: Some(rust_connect::<T>),
xBestIndex: Some(rust_best_index::<T>),
xDisconnect: Some(rust_disconnect::<T>),
xDestroy: None,
xOpen: Some(rust_open::<T>),
xClose: Some(rust_close::<T::Cursor>),
xFilter: Some(rust_filter::<T::Cursor>),
xNext: Some(rust_next::<T::Cursor>),
xEof: Some(rust_eof::<T::Cursor>),
xColumn: Some(rust_column::<T::Cursor>),
xRowid: Some(rust_rowid::<T::Cursor>),
xUpdate: None,
xBegin: None,
xSync: None,
xCommit: None,
xRollback: None,
xFindFunction: None,
xRename: None,
xSavepoint: None,
xRelease: None,
xRollbackTo: None,
};
Module {
base: ffi_module,
phantom: PhantomData::<T>,
}
}
pub struct VTabConnection(*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.
@ -61,12 +142,10 @@ impl VTabConnection {
} }
} }
/// Module instance trait /// Virtual table instance trait.
pub trait Module { pub trait VTab: Sized {
type Aux; type Aux;
type Table: VTab; type Cursor: VTabCursor;
fn as_ptr(&self) -> *const ffi::sqlite3_module;
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement. /// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement.
/// The `db` parameter is a pointer to the SQLite database connection that is executing /// The `db` parameter is a pointer to the SQLite database connection that is executing
@ -75,22 +154,19 @@ pub trait Module {
db: &mut VTabConnection, db: &mut VTabConnection,
aux: Option<&Self::Aux>, aux: Option<&Self::Aux>,
args: &[&[u8]], args: &[&[u8]],
) -> Result<(String, Self::Table)> { ) -> Result<(String, Self)> {
Self::connect(db, aux, args) Self::connect(db, aux, args)
} }
// TODO Validate: no destroy
/// Similar to `create`. The difference is that `connect` is called to establish a new connection /// Similar to `create`. The difference is that `connect` is called to establish a new connection
/// to an _existing_ virtual table whereas `create` is called to create a new virtual table from scratch. /// to an _existing_ virtual table whereas `create` is called to create a new virtual table from scratch.
fn connect( fn connect(
db: &mut VTabConnection, db: &mut VTabConnection,
aux: Option<&Self::Aux>, aux: Option<&Self::Aux>,
args: &[&[u8]], args: &[&[u8]],
) -> Result<(String, Self::Table)>; ) -> Result<(String, Self)>;
}
/// Virtual table instance trait.
pub trait VTab: Sized {
type Cursor: VTabCursor;
/// Determine the best way to access the virtual table. /// Determine the best way to access the virtual table.
fn best_index(&self, info: &mut IndexInfo) -> Result<()>; fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
/// Create a new cursor used for accessing a virtual table. /// Create a new cursor used for accessing a virtual table.
@ -110,7 +186,7 @@ bitflags! {
} }
} }
pub struct IndexInfo(pub *mut ffi::sqlite3_index_info); pub struct IndexInfo(*mut ffi::sqlite3_index_info);
impl IndexInfo { impl IndexInfo {
pub fn constraints(&self) -> IndexConstraintIter { pub fn constraints(&self) -> IndexConstraintIter {
@ -265,7 +341,7 @@ pub trait VTabCursor: Sized {
fn rowid(&self) -> Result<i64>; fn rowid(&self) -> Result<i64>;
} }
pub struct Context(pub *mut ffi::sqlite3_context); pub struct Context(*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 +352,7 @@ impl Context {
} }
pub struct Values<'a> { pub struct Values<'a> {
pub args: &'a [*mut ffi::sqlite3_value], args: &'a [*mut ffi::sqlite3_value],
} }
impl<'a> Values<'a> { impl<'a> Values<'a> {
@ -352,34 +428,34 @@ impl<'a> Iterator for ValueIter<'a> {
impl Connection { impl Connection {
/// Register a virtual table implementation. /// Register a virtual table implementation.
pub fn create_module<M: Module>( pub fn create_module<T: VTab>(
&self, &self,
module_name: &str, module_name: &str,
module: M, module: &Module<T>,
aux: Option<M::Aux>, aux: Option<T::Aux>,
) -> Result<()> { ) -> Result<()> {
self.db.borrow_mut().create_module(module_name, module, aux) self.db.borrow_mut().create_module::<T>(module_name, module, aux)
} }
} }
impl InnerConnection { impl InnerConnection {
fn create_module<M: Module>( fn create_module<T: VTab>(
&mut self, &mut self,
module_name: &str, module_name: &str,
module: M, module: &Module<T>,
aux: Option<M::Aux>, aux: Option<T::Aux>,
) -> Result<()> { ) -> Result<()> {
let c_name = try!(str_to_cstring(module_name)); let c_name = try!(str_to_cstring(module_name));
let r = match aux { let r = match aux {
Some(aux) => { Some(aux) => {
let boxed_aux: *mut M::Aux = Box::into_raw(Box::new(aux)); let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
unsafe { unsafe {
ffi::sqlite3_create_module_v2( ffi::sqlite3_create_module_v2(
self.db(), self.db(),
c_name.as_ptr(), c_name.as_ptr(),
module.as_ptr(), &module.base,
boxed_aux as *mut c_void, boxed_aux as *mut c_void,
Some(free_boxed_value::<M::Aux>), Some(free_boxed_value::<T::Aux>),
) )
} }
} }
@ -387,7 +463,7 @@ impl InnerConnection {
ffi::sqlite3_create_module_v2( ffi::sqlite3_create_module_v2(
self.db(), self.db(),
c_name.as_ptr(), c_name.as_ptr(),
module.as_ptr(), &module.base,
ptr::null_mut(), ptr::null_mut(),
None, None,
) )
@ -447,339 +523,237 @@ unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
let _: Box<T> = Box::from_raw(p as *mut T); let _: Box<T> = Box::from_raw(p as *mut T);
} }
#[macro_export] unsafe extern "C" fn rust_create<T>(
macro_rules! init_module { db: *mut ffi::sqlite3,
( aux: *mut c_void,
$module_name:ident, argc: c_int,
$module:ident, argv: *const *const c_char,
$vtab:ident, pp_vtab: *mut *mut ffi::sqlite3_vtab,
$aux:ty, err_msg: *mut *mut c_char,
$cursor:ty, ) -> c_int
$create:ident, where T: VTab {
$connect:ident, use std::error::Error as StdError;
$best_index:ident, use std::ffi::CStr;
$disconnect:ident, use std::slice;
$destroy:ident,
$open:ident,
$close:ident,
$filter:ident,
$next:ident,
$eof:ident,
$column:ident,
$rowid:ident
) => {
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
iVersion: 1,
xCreate: Some($create),
xConnect: Some($connect),
xBestIndex: Some($best_index),
xDisconnect: Some($disconnect),
xDestroy: Some($destroy),
xOpen: Some($open),
xClose: Some($close),
xFilter: Some($filter),
xNext: Some($next),
xEof: Some($eof),
xColumn: Some($column),
xRowid: Some($rowid),
xUpdate: None, // TODO
xBegin: None,
xSync: None,
xCommit: None,
xRollback: None,
xFindFunction: None,
xRename: None,
xSavepoint: None,
xRelease: None,
xRollbackTo: None,
};
// The xConnect and xCreate methods do the same thing, but they must be let mut conn = VTabConnection(db);
// different so that the virtual table is not an eponymous virtual table. let aux = aux as *mut T::Aux;
create_or_connect!($module, $vtab, $aux, $create, create); let args = slice::from_raw_parts(argv, argc as usize);
common_decl!( let vec = args
$module, .iter()
$vtab, .map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
$aux, .collect::<Vec<_>>();
$cursor, match T::create(&mut conn, aux.as_ref(), &vec[..]) {
$connect, Ok((sql, vtab)) => {
$best_index, match ::std::ffi::CString::new(sql) {
$disconnect, Ok(c_sql) => {
$destroy, let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
$open, if rc == ffi::SQLITE_OK {
$close, let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
$filter, *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
$next, ffi::SQLITE_OK
$eof, } else {
$column, let err = error_from_sqlite_code(rc, None);
$rowid *err_msg = mprintf(err.description());
); rc
};
} // init_module macro end
#[macro_export]
macro_rules! eponymous_module {
(
$module_name:ident,
$module:ident,
$vtab:ident,
$aux:ty,
$cursor:ty,
$create:expr,
$connect:ident,
$best_index:ident,
$disconnect:ident,
$destroy:expr,
$open:ident,
$close:ident,
$filter:ident,
$next:ident,
$eof:ident,
$column:ident,
$rowid:ident
) => {
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
iVersion: 1,
xCreate: $create, /* For eponymous-only virtual tables, the xCreate method is NULL */
xConnect: Some($connect), /* A virtual table is eponymous if its xCreate method is
the exact same function as the xConnect method */
xBestIndex: Some($best_index),
xDisconnect: Some($disconnect),
xDestroy: $destroy,
xOpen: Some($open),
xClose: Some($close),
xFilter: Some($filter),
xNext: Some($next),
xEof: Some($eof),
xColumn: Some($column),
xRowid: Some($rowid),
xUpdate: None, // TODO
xBegin: None,
xSync: None,
xCommit: None,
xRollback: None,
xFindFunction: None,
xRename: None,
xSavepoint: None,
xRelease: None,
xRollbackTo: None,
};
common_decl!(
$module,
$vtab,
$aux,
$cursor,
$connect,
$best_index,
$disconnect,
$destroy,
$open,
$close,
$filter,
$next,
$eof,
$column,
$rowid
);
};
} // eponymous_module macro end
#[macro_export]
macro_rules! create_or_connect {
($module:ident, $vtab:ident, $aux:ty, $create_or_connect:ident, $module_func:ident) => {
unsafe extern "C" fn $create_or_connect(
db: *mut ffi::sqlite3,
aux: *mut c_void,
argc: c_int,
argv: *const *const c_char,
pp_vtab: *mut *mut ffi::sqlite3_vtab,
err_msg: *mut *mut c_char,
) -> c_int {
use std::error::Error as StdError;
use std::ffi::CStr;
use std::slice;
use $crate::vtab::mprintf;
let mut conn = VTabConnection(db);
let aux = aux as *mut $aux;
let args = slice::from_raw_parts(argv, argc as usize);
let vec = args
.iter()
.map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
.collect::<Vec<_>>();
match $module::$module_func(&mut conn, aux.as_ref(), &vec[..]) {
Ok((sql, vtab)) => {
match ::std::ffi::CString::new(sql) {
Ok(c_sql) => {
let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
if rc == ffi::SQLITE_OK {
let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab));
*pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
ffi::SQLITE_OK
} else {
let err = error_from_sqlite_code(rc, None);
*err_msg = mprintf(err.description());
rc
}
}
Err(err) => {
*err_msg = mprintf(err.description());
ffi::SQLITE_ERROR
}
} }
} }
Err(Error::SqliteFailure(err, s)) => {
if let Some(s) = s {
*err_msg = mprintf(&s);
}
err.extended_code
}
Err(err) => { Err(err) => {
*err_msg = mprintf(err.description()); *err_msg = mprintf(err.description());
ffi::SQLITE_ERROR ffi::SQLITE_ERROR
} }
} }
} }
}; Err(Error::SqliteFailure(err, s)) => {
} // create_or_connect macro end if let Some(s) = s {
*err_msg = mprintf(&s);
}
err.extended_code
}
Err(err) => {
*err_msg = mprintf(err.description());
ffi::SQLITE_ERROR
}
}
}
#[macro_export] unsafe extern "C" fn rust_connect<T>(
macro_rules! common_decl { db: *mut ffi::sqlite3,
( aux: *mut c_void,
$module:ident, argc: c_int,
$vtab:ident, argv: *const *const c_char,
$aux:ty, pp_vtab: *mut *mut ffi::sqlite3_vtab,
$cursor:ty, err_msg: *mut *mut c_char,
$connect:ident, ) -> c_int
$best_index:ident, where T: VTab {
$disconnect:ident, use std::error::Error as StdError;
$destroy:expr, use std::ffi::CStr;
$open:ident, use std::slice;
$close:ident,
$filter:ident, let mut conn = VTabConnection(db);
$next:ident, let aux = aux as *mut T::Aux;
$eof:ident, let args = slice::from_raw_parts(argv, argc as usize);
$column:ident, let vec = args
$rowid:ident .iter()
) => { .map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
create_or_connect!($module, $vtab, $aux, $connect, connect); .collect::<Vec<_>>();
unsafe extern "C" fn $best_index( match T::connect(&mut conn, aux.as_ref(), &vec[..]) {
vtab: *mut ffi::sqlite3_vtab, Ok((sql, vtab)) => {
info: *mut ffi::sqlite3_index_info, match ::std::ffi::CString::new(sql) {
) -> c_int { Ok(c_sql) => {
use std::error::Error as StdError; let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
use $crate::vtab::set_err_msg; if rc == ffi::SQLITE_OK {
let vt = vtab as *mut $vtab; let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
let mut idx_info = IndexInfo(info); *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
match (*vt).best_index(&mut idx_info) { ffi::SQLITE_OK
Ok(_) => ffi::SQLITE_OK, } else {
Err(Error::SqliteFailure(err, s)) => { let err = error_from_sqlite_code(rc, None);
if let Some(err_msg) = s { *err_msg = mprintf(err.description());
set_err_msg(vtab, &err_msg); rc
} }
err.extended_code
} }
Err(err) => { Err(err) => {
set_err_msg(vtab, err.description()); *err_msg = mprintf(err.description());
ffi::SQLITE_ERROR ffi::SQLITE_ERROR
} }
} }
} }
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> c_int { Err(Error::SqliteFailure(err, s)) => {
let vtab = vtab as *mut $vtab; if let Some(s) = s {
let _: Box<$vtab> = Box::from_raw(vtab); *err_msg = mprintf(&s);
}
err.extended_code
}
Err(err) => {
*err_msg = mprintf(err.description());
ffi::SQLITE_ERROR
}
}
}
unsafe extern "C" fn rust_best_index<T>(
vtab: *mut ffi::sqlite3_vtab,
info: *mut ffi::sqlite3_index_info,
) -> c_int
where T: VTab {
use std::error::Error as StdError;
let vt = vtab as *mut T;
let mut idx_info = IndexInfo(info);
match (*vt).best_index(&mut idx_info) {
Ok(_) => ffi::SQLITE_OK,
Err(Error::SqliteFailure(err, s)) => {
if let Some(err_msg) = s {
set_err_msg(vtab, &err_msg);
}
err.extended_code
}
Err(err) => {
set_err_msg(vtab, err.description());
ffi::SQLITE_ERROR
}
}
}
unsafe extern "C" fn rust_disconnect<T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
where T: VTab {
let vtab = vtab as *mut T;
let _: Box<T> = Box::from_raw(vtab);
ffi::SQLITE_OK
}
unsafe extern "C" fn rust_open<T>(
vtab: *mut ffi::sqlite3_vtab,
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
) -> c_int
where T: VTab {
use std::error::Error as StdError;
let vt = vtab as *mut T;
match (*vt).open() {
Ok(cursor) => {
let boxed_cursor: *mut T::Cursor = Box::into_raw(Box::new(cursor));
*pp_cursor = boxed_cursor as *mut ffi::sqlite3_vtab_cursor;
ffi::SQLITE_OK ffi::SQLITE_OK
} }
Err(Error::SqliteFailure(err, s)) => {
unsafe extern "C" fn $open( if let Some(err_msg) = s {
vtab: *mut ffi::sqlite3_vtab, set_err_msg(vtab, &err_msg);
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
) -> c_int {
use std::error::Error as StdError;
use $crate::vtab::set_err_msg;
let vt = vtab as *mut $vtab;
match (*vt).open() {
Ok(cursor) => {
let boxed_cursor: *mut $cursor = Box::into_raw(Box::new(cursor));
*pp_cursor = boxed_cursor as *mut ffi::sqlite3_vtab_cursor;
ffi::SQLITE_OK
}
Err(Error::SqliteFailure(err, s)) => {
if let Some(err_msg) = s {
set_err_msg(vtab, &err_msg);
}
err.extended_code
}
Err(err) => {
set_err_msg(vtab, err.description());
ffi::SQLITE_ERROR
}
} }
err.extended_code
} }
unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int { Err(err) => {
let cr = cursor as *mut $cursor; set_err_msg(vtab, err.description());
let _: Box<$cursor> = Box::from_raw(cr); ffi::SQLITE_ERROR
ffi::SQLITE_OK
} }
}
}
unsafe extern "C" fn $filter( unsafe extern "C" fn rust_close<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
cursor: *mut ffi::sqlite3_vtab_cursor, where C: VTabCursor {
idx_num: c_int, let cr = cursor as *mut C;
idx_str: *const c_char, let _: Box<C> = Box::from_raw(cr);
argc: c_int, ffi::SQLITE_OK
argv: *mut *mut ffi::sqlite3_value, }
) -> c_int {
use std::ffi::CStr; unsafe extern "C" fn rust_filter<C>(
use std::slice; cursor: *mut ffi::sqlite3_vtab_cursor,
use std::str; idx_num: c_int,
use $crate::vtab::{cursor_error, Values}; idx_str: *const c_char,
let idx_name = if idx_str.is_null() { argc: c_int,
None argv: *mut *mut ffi::sqlite3_value,
} else { ) -> c_int
let c_slice = CStr::from_ptr(idx_str).to_bytes(); where C: VTabCursor {
Some(str::from_utf8_unchecked(c_slice)) use std::ffi::CStr;
}; use std::slice;
let args = slice::from_raw_parts_mut(argv, argc as usize); use std::str;
let values = Values { args: args }; let idx_name = if idx_str.is_null() {
let cr = cursor as *mut $cursor; None
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values)) } else {
} let c_slice = CStr::from_ptr(idx_str).to_bytes();
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int { Some(str::from_utf8_unchecked(c_slice))
use $crate::vtab::cursor_error;
let cr = cursor as *mut $cursor;
cursor_error(cursor, (*cr).next())
}
unsafe extern "C" fn $eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
let cr = cursor as *mut $cursor;
(*cr).eof() as c_int
}
unsafe extern "C" fn $column(
cursor: *mut ffi::sqlite3_vtab_cursor,
ctx: *mut ffi::sqlite3_context,
i: c_int,
) -> c_int {
use $crate::vtab::{result_error, Context};
let cr = cursor as *mut $cursor;
let mut ctxt = Context(ctx);
result_error(ctx, (*cr).column(&mut ctxt, i))
}
unsafe extern "C" fn $rowid(
cursor: *mut ffi::sqlite3_vtab_cursor,
p_rowid: *mut ffi::sqlite3_int64,
) -> c_int {
use $crate::vtab::cursor_error;
let cr = cursor as *mut $cursor;
match (*cr).rowid() {
Ok(rowid) => {
*p_rowid = rowid;
ffi::SQLITE_OK
}
err => cursor_error(cursor, err),
}
}
}; };
} // common_decl macro end let args = slice::from_raw_parts_mut(argv, argc as usize);
let values = Values { args: args };
let cr = cursor as *mut C;
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
}
unsafe extern "C" fn rust_next<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
where C: VTabCursor {
let cr = cursor as *mut C;
cursor_error(cursor, (*cr).next())
}
unsafe extern "C" fn rust_eof<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
where C: VTabCursor {
let cr = cursor as *mut C;
(*cr).eof() as c_int
}
unsafe extern "C" fn rust_column<C>(
cursor: *mut ffi::sqlite3_vtab_cursor,
ctx: *mut ffi::sqlite3_context,
i: c_int,
) -> c_int
where C: VTabCursor {
let cr = cursor as *mut C;
let mut ctxt = Context(ctx);
result_error(ctx, (*cr).column(&mut ctxt, i))
}
unsafe extern "C" fn rust_rowid<C>(
cursor: *mut ffi::sqlite3_vtab_cursor,
p_rowid: *mut ffi::sqlite3_int64,
) -> c_int
where C: VTabCursor {
let cr = cursor as *mut C;
match (*cr).rowid() {
Ok(rowid) => {
*p_rowid = rowid;
ffi::SQLITE_OK
}
err => cursor_error(cursor, err),
}
}
/// Virtual table cursors can set an error message by assigning a string to `zErrMsg`. /// Virtual table cursors can set an error message by assigning a string to `zErrMsg`.
pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int { pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int {
@ -800,7 +774,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`.
pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) { 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);
} }
@ -809,7 +783,7 @@ pub 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.
pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int { 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,

View File

@ -1,64 +1,21 @@
//! generate series virtual table. //! generate series virtual table.
//! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c). //! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c).
use std::default::Default; use std::default::Default;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::c_int;
use error::error_from_sqlite_code;
use ffi; use ffi;
use types::Type; use types::Type;
use vtab::{self, Context, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values}; use vtab::{eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
use {Connection, Error, Result}; use {Connection, Result};
/// Register the "generate_series" module. /// Register the "generate_series" module.
pub fn load_module(conn: &Connection) -> Result<()> { pub fn load_module(conn: &Connection) -> Result<()> {
let aux: Option<()> = None; let aux: Option<()> = None;
conn.create_module("generate_series", Series(&SERIES_MODULE), aux) conn.create_module::<SeriesTab>("generate_series", &SERIES_MODULE, aux)
} }
eponymous_module!( lazy_static! {
SERIES_MODULE, static ref SERIES_MODULE: Module<SeriesTab> = eponymous_only_module::<SeriesTab>();
Series,
SeriesTab,
(),
SeriesTabCursor,
None,
series_connect,
series_best_index,
series_disconnect,
None,
series_open,
series_close,
series_filter,
series_next,
series_eof,
series_column,
series_rowid
);
#[repr(C)]
struct Series(&'static ffi::sqlite3_module);
impl Module for Series {
type Aux = ();
type Table = SeriesTab;
fn as_ptr(&self) -> *const ffi::sqlite3_module {
self.0
}
fn connect(
_: &mut VTabConnection,
_aux: Option<&()>,
_args: &[&[u8]],
) -> Result<(String, SeriesTab)> {
let vtab = SeriesTab {
base: ffi::sqlite3_vtab::default(),
};
Ok((
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)".to_owned(),
vtab,
))
}
} }
// Column numbers // Column numbers
@ -91,8 +48,23 @@ struct SeriesTab {
} }
impl VTab for SeriesTab { impl VTab for SeriesTab {
type Aux = ();
type Cursor = SeriesTabCursor; type Cursor = SeriesTabCursor;
fn connect(
_: &mut VTabConnection,
_aux: Option<&()>,
_args: &[&[u8]],
) -> Result<(String, SeriesTab)> {
let vtab = SeriesTab {
base: ffi::sqlite3_vtab::default(),
};
Ok((
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)".to_owned(),
vtab,
))
}
fn best_index(&self, info: &mut IndexInfo) -> Result<()> { fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
// The query plan bitmask // The query plan bitmask
let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty(); let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();
@ -106,7 +78,7 @@ impl VTab for SeriesTab {
if !constraint.is_usable() { if !constraint.is_usable() {
continue; continue;
} }
if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ { if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
continue; continue;
} }
match constraint.column() { match constraint.column() {

View File

@ -1,7 +1,6 @@
//! Ensure Virtual tables can be declared outside `rusqlite` crate. //! Ensure Virtual tables can be declared outside `rusqlite` crate.
#[cfg(feature = "vtab")] #[cfg(feature = "vtab")]
#[macro_use]
extern crate rusqlite; extern crate rusqlite;
extern crate libsqlite3_sys as ffi; extern crate libsqlite3_sys as ffi;
@ -9,40 +8,21 @@ extern crate libsqlite3_sys as ffi;
#[test] #[test]
fn test_dummy_module() { fn test_dummy_module() {
use ffi; use ffi;
use rusqlite::vtab::{Context, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values}; use rusqlite::vtab::{eponymous_only_module, Context, IndexInfo, VTab, VTabConnection, VTabCursor, Values};
use rusqlite::{error_from_sqlite_code, Connection, Error, Result}; use rusqlite::{Connection, Result};
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::c_int;
eponymous_module!( let module = eponymous_only_module::<DummyTab>();
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)] #[repr(C)]
struct DummyModule(&'static ffi::sqlite3_module); struct DummyTab {
/// Base class. Must be first
base: ffi::sqlite3_vtab,
}
impl Module for DummyModule { impl VTab for DummyTab {
type Aux = (); type Aux = ();
type Table = DummyTab; type Cursor = DummyTabCursor;
fn as_ptr(&self) -> *const ffi::sqlite3_module {
self.0
}
fn connect( fn connect(
_: &mut VTabConnection, _: &mut VTabConnection,
@ -54,16 +34,6 @@ fn test_dummy_module() {
}; };
Ok(("CREATE TABLE x(value)".to_owned(), vtab)) 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<()> { fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
info.set_estimated_cost(1.); info.set_estimated_cost(1.);
@ -116,9 +86,7 @@ fn test_dummy_module() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let module = DummyModule(&DUMMY_MODULE); db.create_module::<DummyTab>("dummy", &module, None).unwrap();
db.create_module("dummy", module, None).unwrap();
let version = unsafe { ffi::sqlite3_libversion_number() }; let version = unsafe { ffi::sqlite3_libversion_number() };
if version < 3008012 { if version < 3008012 {