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 = []
sqlcipher = ["libsqlite3-sys/sqlcipher"]
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"]
# pointer passing interfaces: 3.20.0
array = ["vtab"]
@ -41,6 +41,7 @@ lru-cache = "0.1"
chrono = { version = "0.4", optional = true }
serde_json = { version = "1.0", optional = true }
csv = { version = "1.0", optional = true }
lazy_static = { version = "1.0", optional = true }
[dev-dependencies]
tempdir = "0.3"
@ -62,7 +63,7 @@ name = "deny_single_threaded_sqlite_config"
name = "vtab"
[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
no-default-features = true
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 {
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 {
let message = if db.is_null() {
None

View File

@ -56,7 +56,7 @@ extern crate libsqlite3_sys as ffi;
extern crate lru_cache;
#[macro_use]
extern crate bitflags;
#[cfg(all(test, feature = "trace"))]
#[cfg(any(all(test, feature = "trace"), feature = "vtab"))]
#[macro_use]
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 types::{ToSql, ValueRef};
use error::error_from_handle;
use error::{error_from_sqlite_code, error_from_handle};
use raw_statement::RawStatement;
use cache::StatementCache;
@ -91,7 +91,7 @@ pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}
#[allow(deprecated)]
pub use error::SqliteError;
pub use error::{Error, error_from_sqlite_code};
pub use error::Error;
pub use ffi::ErrorCode;
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::rc::Rc;
use error::error_from_sqlite_code;
use ffi;
use types::{ToSql, ToSqlOutput, Value};
use vtab::{self, Context, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
use {Connection, Error, Result};
use vtab::{eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
use {Connection, Result};
// http://sqlite.org/bindptr.html
@ -29,50 +28,11 @@ impl ToSql for Array {
/// Register the "rarray" module.
pub fn load_module(conn: &Connection) -> Result<()> {
let aux: Option<()> = None;
conn.create_module("rarray", ArrayModule(&ARRAY_MODULE), aux)
conn.create_module::<ArrayTab>("rarray", &ARRAY_MODULE, aux)
}
eponymous_module!(
ARRAY_MODULE,
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))
}
lazy_static! {
static ref ARRAY_MODULE: Module<ArrayTab> = eponymous_only_module::<ArrayTab>();
}
// Column numbers
@ -87,8 +47,20 @@ struct ArrayTab {
}
impl VTab for ArrayTab {
type Aux = ();
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<()> {
// Index of the pointer= constraint
let mut ptr_idx = None;
@ -96,7 +68,7 @@ impl VTab for ArrayTab {
if !constraint.is_usable() {
continue;
}
if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
continue;
}
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.
extern crate csv;
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::result;
use std::str;
use error::error_from_sqlite_code;
use ffi;
use types::Null;
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,
};
use {Connection, Error, Result};
@ -29,32 +28,36 @@ use {Connection, Error, Result};
/// ```
pub fn load_module(conn: &Connection) -> Result<()> {
let aux: Option<()> = None;
conn.create_module("csv", CSVModule(&CSV_MODULE), aux)
conn.create_module::<CSVTab>("csv", &CSV_MODULE, aux)
}
init_module!(
CSV_MODULE,
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
);
lazy_static! {
static ref CSV_MODULE: Module<CSVTab> = simple_module::<CSVTab>();
}
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)> {
let arg = try!(str::from_utf8(c_slice)).trim();
let mut split = arg.split('=');
@ -77,13 +80,9 @@ impl CSVModule {
}
}
impl Module for CSVModule {
impl VTab for CSVTab {
type Aux = ();
type Table = CSVTab;
fn as_ptr(&self) -> *const ffi::sqlite3_module {
self.0
}
type Cursor = CSVTabCursor;
fn connect(
_: &mut VTabConnection,
@ -107,7 +106,7 @@ impl Module for CSVModule {
let args = &args[3..];
for c_slice in args {
let (param, value) = try!(CSVModule::parameter(c_slice));
let (param, value) = try!(CSVTab::parameter(c_slice));
match param {
"filename" => {
if !Path::new(value).exists() {
@ -151,7 +150,7 @@ impl Module for CSVModule {
}
}
"delimiter" => {
if let Some(b) = CSVModule::parse_byte(value) {
if let Some(b) = CSVTab::parse_byte(value) {
vtab.delimiter = b;
} else {
return Err(Error::ModuleError(format!(
@ -161,7 +160,7 @@ impl Module for CSVModule {
}
}
"quote" => {
if let Some(b) = CSVModule::parse_byte(value) {
if let Some(b) = CSVTab::parse_byte(value) {
if b == b'0' {
vtab.quote = 0;
} else {
@ -237,34 +236,6 @@ impl Module for CSVModule {
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.
fn best_index(&self, info: &mut IndexInfo) -> Result<()> {

View File

@ -2,11 +2,14 @@
//! (See http://sqlite.org/vtab.html)
use std::borrow::Cow::{self, Borrowed, Owned};
use std::ffi::CString;
use std::marker::PhantomData;
use std::marker::Sync;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::slice;
use context::set_result;
use error::error_from_sqlite_code;
use ffi;
use types::{FromSql, FromSqlError, ToSql, ValueRef};
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 }
//
// db: *mut ffi::sqlite3
// db: *mut ffi::sqlite3 => VTabConnection
// module: *const ffi::sqlite3_module => Module
// aux: *mut c_void => Module::Aux
// ffi::sqlite3_vtab => VTab
// 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 {
/// Get access to the underlying SQLite database connection handle.
@ -61,12 +142,10 @@ impl VTabConnection {
}
}
/// Module instance trait
pub trait Module {
/// Virtual table instance trait.
pub trait VTab: Sized {
type Aux;
type Table: VTab;
fn as_ptr(&self) -> *const ffi::sqlite3_module;
type Cursor: VTabCursor;
/// 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
@ -75,22 +154,19 @@ pub trait Module {
db: &mut VTabConnection,
aux: Option<&Self::Aux>,
args: &[&[u8]],
) -> Result<(String, Self::Table)> {
) -> Result<(String, Self)> {
Self::connect(db, aux, args)
}
// TODO Validate: no destroy
/// 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.
fn connect(
db: &mut VTabConnection,
aux: Option<&Self::Aux>,
args: &[&[u8]],
) -> Result<(String, Self::Table)>;
}
/// Virtual table instance trait.
pub trait VTab: Sized {
type Cursor: VTabCursor;
) -> Result<(String, Self)>;
/// Determine the best way to access the virtual table.
fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
/// 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 {
pub fn constraints(&self) -> IndexConstraintIter {
@ -265,7 +341,7 @@ pub trait VTabCursor: Sized {
fn rowid(&self) -> Result<i64>;
}
pub struct Context(pub *mut ffi::sqlite3_context);
pub struct Context(*mut ffi::sqlite3_context);
impl Context {
pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
@ -276,7 +352,7 @@ impl Context {
}
pub struct Values<'a> {
pub args: &'a [*mut ffi::sqlite3_value],
args: &'a [*mut ffi::sqlite3_value],
}
impl<'a> Values<'a> {
@ -352,34 +428,34 @@ impl<'a> Iterator for ValueIter<'a> {
impl Connection {
/// Register a virtual table implementation.
pub fn create_module<M: Module>(
pub fn create_module<T: VTab>(
&self,
module_name: &str,
module: M,
aux: Option<M::Aux>,
module: &Module<T>,
aux: Option<T::Aux>,
) -> Result<()> {
self.db.borrow_mut().create_module(module_name, module, aux)
self.db.borrow_mut().create_module::<T>(module_name, module, aux)
}
}
impl InnerConnection {
fn create_module<M: Module>(
fn create_module<T: VTab>(
&mut self,
module_name: &str,
module: M,
aux: Option<M::Aux>,
module: &Module<T>,
aux: Option<T::Aux>,
) -> Result<()> {
let c_name = try!(str_to_cstring(module_name));
let r = match 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 {
ffi::sqlite3_create_module_v2(
self.db(),
c_name.as_ptr(),
module.as_ptr(),
&module.base,
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(
self.db(),
c_name.as_ptr(),
module.as_ptr(),
&module.base,
ptr::null_mut(),
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);
}
#[macro_export]
macro_rules! init_module {
(
$module_name:ident,
$module:ident,
$vtab:ident,
$aux:ty,
$cursor:ty,
$create:ident,
$connect:ident,
$best_index:ident,
$disconnect:ident,
$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,
};
unsafe extern "C" fn rust_create<T>(
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
where T: VTab {
use std::error::Error as StdError;
use std::ffi::CStr;
use std::slice;
// 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.
create_or_connect!($module, $vtab, $aux, $create, create);
common_decl!(
$module,
$vtab,
$aux,
$cursor,
$connect,
$best_index,
$disconnect,
$destroy,
$open,
$close,
$filter,
$next,
$eof,
$column,
$rowid
);
};
} // 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
}
let mut conn = VTabConnection(db);
let aux = aux as *mut T::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 T::create(&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 T = 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(Error::SqliteFailure(err, s)) => {
if let Some(s) = s {
*err_msg = mprintf(&s);
}
err.extended_code
}
Err(err) => {
*err_msg = mprintf(err.description());
ffi::SQLITE_ERROR
}
}
}
};
} // create_or_connect macro end
Err(Error::SqliteFailure(err, s)) => {
if let Some(s) = s {
*err_msg = mprintf(&s);
}
err.extended_code
}
Err(err) => {
*err_msg = mprintf(err.description());
ffi::SQLITE_ERROR
}
}
}
#[macro_export]
macro_rules! common_decl {
(
$module:ident,
$vtab:ident,
$aux:ty,
$cursor:ty,
$connect:ident,
$best_index:ident,
$disconnect:ident,
$destroy:expr,
$open:ident,
$close:ident,
$filter:ident,
$next:ident,
$eof:ident,
$column:ident,
$rowid:ident
) => {
create_or_connect!($module, $vtab, $aux, $connect, connect);
unsafe extern "C" fn $best_index(
vtab: *mut ffi::sqlite3_vtab,
info: *mut ffi::sqlite3_index_info,
) -> c_int {
use std::error::Error as StdError;
use $crate::vtab::set_err_msg;
let vt = vtab as *mut $vtab;
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);
unsafe extern "C" fn rust_connect<T>(
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
where T: VTab {
use std::error::Error as StdError;
use std::ffi::CStr;
use std::slice;
let mut conn = VTabConnection(db);
let aux = aux as *mut T::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 T::connect(&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 T = 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.extended_code
}
Err(err) => {
set_err_msg(vtab, err.description());
*err_msg = mprintf(err.description());
ffi::SQLITE_ERROR
}
}
}
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> c_int {
let vtab = vtab as *mut $vtab;
let _: Box<$vtab> = Box::from_raw(vtab);
Err(Error::SqliteFailure(err, s)) => {
if let Some(s) = s {
*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
}
unsafe extern "C" fn $open(
vtab: *mut ffi::sqlite3_vtab,
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(Error::SqliteFailure(err, s)) => {
if let Some(err_msg) = s {
set_err_msg(vtab, &err_msg);
}
err.extended_code
}
unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
let cr = cursor as *mut $cursor;
let _: Box<$cursor> = Box::from_raw(cr);
ffi::SQLITE_OK
Err(err) => {
set_err_msg(vtab, err.description());
ffi::SQLITE_ERROR
}
}
}
unsafe extern "C" fn $filter(
cursor: *mut ffi::sqlite3_vtab_cursor,
idx_num: c_int,
idx_str: *const c_char,
argc: c_int,
argv: *mut *mut ffi::sqlite3_value,
) -> c_int {
use std::ffi::CStr;
use std::slice;
use std::str;
use $crate::vtab::{cursor_error, Values};
let idx_name = if idx_str.is_null() {
None
} else {
let c_slice = CStr::from_ptr(idx_str).to_bytes();
Some(str::from_utf8_unchecked(c_slice))
};
let args = slice::from_raw_parts_mut(argv, argc as usize);
let values = Values { args: args };
let cr = cursor as *mut $cursor;
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
}
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
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),
}
}
unsafe extern "C" fn rust_close<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
where C: VTabCursor {
let cr = cursor as *mut C;
let _: Box<C> = Box::from_raw(cr);
ffi::SQLITE_OK
}
unsafe extern "C" fn rust_filter<C>(
cursor: *mut ffi::sqlite3_vtab_cursor,
idx_num: c_int,
idx_str: *const c_char,
argc: c_int,
argv: *mut *mut ffi::sqlite3_value,
) -> c_int
where C: VTabCursor {
use std::ffi::CStr;
use std::slice;
use std::str;
let idx_name = if idx_str.is_null() {
None
} else {
let c_slice = CStr::from_ptr(idx_str).to_bytes();
Some(str::from_utf8_unchecked(c_slice))
};
} // 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`.
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`.
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() {
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
/// 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;
match result {
Ok(_) => ffi::SQLITE_OK,

View File

@ -1,64 +1,21 @@
//! generate series virtual table.
//! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c).
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 types::Type;
use vtab::{self, Context, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
use {Connection, Error, Result};
use vtab::{eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
use {Connection, Result};
/// Register the "generate_series" module.
pub fn load_module(conn: &Connection) -> Result<()> {
let aux: Option<()> = None;
conn.create_module("generate_series", Series(&SERIES_MODULE), aux)
conn.create_module::<SeriesTab>("generate_series", &SERIES_MODULE, aux)
}
eponymous_module!(
SERIES_MODULE,
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,
))
}
lazy_static! {
static ref SERIES_MODULE: Module<SeriesTab> = eponymous_only_module::<SeriesTab>();
}
// Column numbers
@ -91,8 +48,23 @@ struct SeriesTab {
}
impl VTab for SeriesTab {
type Aux = ();
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<()> {
// The query plan bitmask
let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();
@ -106,7 +78,7 @@ impl VTab for SeriesTab {
if !constraint.is_usable() {
continue;
}
if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
continue;
}
match constraint.column() {

View File

@ -1,7 +1,6 @@
//! Ensure Virtual tables can be declared outside `rusqlite` crate.
#[cfg(feature = "vtab")]
#[macro_use]
extern crate rusqlite;
extern crate libsqlite3_sys as ffi;
@ -9,40 +8,21 @@ extern crate libsqlite3_sys as ffi;
#[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};
use rusqlite::vtab::{eponymous_only_module, Context, IndexInfo, VTab, VTabConnection, VTabCursor, Values};
use rusqlite::{Connection, Result};
use std::os::raw::c_int;
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
);
let module = eponymous_only_module::<DummyTab>();
#[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 Table = DummyTab;
fn as_ptr(&self) -> *const ffi::sqlite3_module {
self.0
}
type Cursor = DummyTabCursor;
fn connect(
_: &mut VTabConnection,
@ -54,16 +34,6 @@ fn test_dummy_module() {
};
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.);
@ -116,9 +86,7 @@ fn test_dummy_module() {
let db = Connection::open_in_memory().unwrap();
let module = DummyModule(&DUMMY_MODULE);
db.create_module("dummy", module, None).unwrap();
db.create_module::<DummyTab>("dummy", &module, None).unwrap();
let version = unsafe { ffi::sqlite3_libversion_number() };
if version < 3008012 {