mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-25 02:21:37 +08:00
Remove macros
This commit is contained in:
parent
b19d050e39
commit
e2df03f474
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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() {
|
||||||
|
@ -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<()> {
|
||||||
|
654
src/vtab/mod.rs
654
src/vtab/mod.rs
@ -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,
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user