mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-24 18:01:37 +08:00
Ensure Virtual tables can be declared outside rusqlite
crate
Not sure it is the way to go.
This commit is contained in:
parent
ce39b9a3c0
commit
823f3c96aa
@ -58,6 +58,9 @@ harness = false
|
|||||||
[[test]]
|
[[test]]
|
||||||
name = "deny_single_threaded_sqlite_config"
|
name = "deny_single_threaded_sqlite_config"
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "vtab"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = [ "backup", "blob", "chrono", "functions", "limits", "load_extension", "serde_json", "trace" ]
|
features = [ "backup", "blob", "chrono", "functions", "limits", "load_extension", "serde_json", "trace" ]
|
||||||
all-features = false
|
all-features = false
|
||||||
|
@ -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
|
||||||
|
@ -75,7 +75,7 @@ use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
|
|||||||
use std::os::raw::{c_int, c_char};
|
use std::os::raw::{c_int, c_char};
|
||||||
|
|
||||||
use types::{ToSql, ValueRef};
|
use types::{ToSql, ValueRef};
|
||||||
use error::{error_from_sqlite_code, error_from_handle};
|
use error::error_from_handle;
|
||||||
use raw_statement::RawStatement;
|
use raw_statement::RawStatement;
|
||||||
use cache::StatementCache;
|
use cache::StatementCache;
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}
|
|||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
pub use error::SqliteError;
|
pub use error::SqliteError;
|
||||||
pub use error::Error;
|
pub use error::{Error, error_from_sqlite_code};
|
||||||
pub use ffi::ErrorCode;
|
pub use ffi::ErrorCode;
|
||||||
|
|
||||||
pub use cache::CachedStatement;
|
pub use cache::CachedStatement;
|
||||||
|
@ -45,7 +45,7 @@ use {str_to_cstring, Connection, Error, InnerConnection, Result};
|
|||||||
// ffi::sqlite3_vtab => VTab
|
// ffi::sqlite3_vtab => VTab
|
||||||
// ffi::sqlite3_vtab_cursor => VTabCursor
|
// ffi::sqlite3_vtab_cursor => VTabCursor
|
||||||
|
|
||||||
pub struct VTabConnection(*mut ffi::sqlite3);
|
pub struct VTabConnection(pub *mut ffi::sqlite3);
|
||||||
|
|
||||||
impl VTabConnection {
|
impl VTabConnection {
|
||||||
/// Get access to the underlying SQLite database connection handle.
|
/// Get access to the underlying SQLite database connection handle.
|
||||||
@ -110,7 +110,7 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IndexInfo(*mut ffi::sqlite3_index_info);
|
pub struct IndexInfo(pub *mut ffi::sqlite3_index_info);
|
||||||
|
|
||||||
impl IndexInfo {
|
impl IndexInfo {
|
||||||
pub fn constraints(&self) -> IndexConstraintIter {
|
pub fn constraints(&self) -> IndexConstraintIter {
|
||||||
@ -265,7 +265,7 @@ pub trait VTabCursor: Sized {
|
|||||||
fn rowid(&self) -> Result<i64>;
|
fn rowid(&self) -> Result<i64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Context(*mut ffi::sqlite3_context);
|
pub struct Context(pub *mut ffi::sqlite3_context);
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
|
pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
|
||||||
@ -276,7 +276,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Values<'a> {
|
pub struct Values<'a> {
|
||||||
args: &'a [*mut ffi::sqlite3_value],
|
pub args: &'a [*mut ffi::sqlite3_value],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Values<'a> {
|
impl<'a> Values<'a> {
|
||||||
@ -585,6 +585,7 @@ macro_rules! eponymous_module {
|
|||||||
};
|
};
|
||||||
} // eponymous_module macro end
|
} // eponymous_module macro end
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
macro_rules! create_or_connect {
|
macro_rules! create_or_connect {
|
||||||
($module:ident, $vtab:ident, $aux:ty, $create_or_connect:ident, $module_func:ident) => {
|
($module:ident, $vtab:ident, $aux:ty, $create_or_connect:ident, $module_func:ident) => {
|
||||||
unsafe extern "C" fn $create_or_connect(
|
unsafe extern "C" fn $create_or_connect(
|
||||||
@ -598,7 +599,7 @@ macro_rules! create_or_connect {
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use vtab::mprintf;
|
use $crate::vtab::mprintf;
|
||||||
|
|
||||||
let mut conn = VTabConnection(db);
|
let mut conn = VTabConnection(db);
|
||||||
let aux = aux as *mut $aux;
|
let aux = aux as *mut $aux;
|
||||||
@ -643,6 +644,7 @@ macro_rules! create_or_connect {
|
|||||||
};
|
};
|
||||||
} // create_or_connect macro end
|
} // create_or_connect macro end
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
macro_rules! common_decl {
|
macro_rules! common_decl {
|
||||||
(
|
(
|
||||||
$module:ident,
|
$module:ident,
|
||||||
@ -667,7 +669,7 @@ macro_rules! common_decl {
|
|||||||
info: *mut ffi::sqlite3_index_info,
|
info: *mut ffi::sqlite3_index_info,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use vtab::set_err_msg;
|
use $crate::vtab::set_err_msg;
|
||||||
let vt = vtab as *mut $vtab;
|
let vt = vtab as *mut $vtab;
|
||||||
let mut idx_info = IndexInfo(info);
|
let mut idx_info = IndexInfo(info);
|
||||||
match (*vt).best_index(&mut idx_info) {
|
match (*vt).best_index(&mut idx_info) {
|
||||||
@ -695,7 +697,7 @@ macro_rules! common_decl {
|
|||||||
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
|
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use vtab::set_err_msg;
|
use $crate::vtab::set_err_msg;
|
||||||
let vt = vtab as *mut $vtab;
|
let vt = vtab as *mut $vtab;
|
||||||
match (*vt).open() {
|
match (*vt).open() {
|
||||||
Ok(cursor) => {
|
Ok(cursor) => {
|
||||||
@ -731,7 +733,7 @@ macro_rules! common_decl {
|
|||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::str;
|
use std::str;
|
||||||
use vtab::{cursor_error, Values};
|
use $crate::vtab::{cursor_error, Values};
|
||||||
let idx_name = if idx_str.is_null() {
|
let idx_name = if idx_str.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -744,7 +746,7 @@ macro_rules! common_decl {
|
|||||||
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
|
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
|
||||||
}
|
}
|
||||||
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
|
||||||
use vtab::cursor_error;
|
use $crate::vtab::cursor_error;
|
||||||
let cr = cursor as *mut $cursor;
|
let cr = cursor as *mut $cursor;
|
||||||
cursor_error(cursor, (*cr).next())
|
cursor_error(cursor, (*cr).next())
|
||||||
}
|
}
|
||||||
@ -757,7 +759,7 @@ macro_rules! common_decl {
|
|||||||
ctx: *mut ffi::sqlite3_context,
|
ctx: *mut ffi::sqlite3_context,
|
||||||
i: c_int,
|
i: c_int,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
use vtab::{result_error, Context};
|
use $crate::vtab::{result_error, Context};
|
||||||
let cr = cursor as *mut $cursor;
|
let cr = cursor as *mut $cursor;
|
||||||
let mut ctxt = Context(ctx);
|
let mut ctxt = Context(ctx);
|
||||||
result_error(ctx, (*cr).column(&mut ctxt, i))
|
result_error(ctx, (*cr).column(&mut ctxt, i))
|
||||||
@ -766,7 +768,7 @@ macro_rules! common_decl {
|
|||||||
cursor: *mut ffi::sqlite3_vtab_cursor,
|
cursor: *mut ffi::sqlite3_vtab_cursor,
|
||||||
p_rowid: *mut ffi::sqlite3_int64,
|
p_rowid: *mut ffi::sqlite3_int64,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
use vtab::cursor_error;
|
use $crate::vtab::cursor_error;
|
||||||
let cr = cursor as *mut $cursor;
|
let cr = cursor as *mut $cursor;
|
||||||
match (*cr).rowid() {
|
match (*cr).rowid() {
|
||||||
Ok(rowid) => {
|
Ok(rowid) => {
|
||||||
@ -798,7 +800,7 @@ pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Virtual tables methods can set an error message by assigning a string to `zErrMsg`.
|
/// Virtual tables methods can set an error message by assigning a string to `zErrMsg`.
|
||||||
unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
|
pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
|
||||||
if !(*vtab).zErrMsg.is_null() {
|
if !(*vtab).zErrMsg.is_null() {
|
||||||
ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void);
|
ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void);
|
||||||
}
|
}
|
||||||
@ -807,7 +809,7 @@ unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
|
|||||||
|
|
||||||
/// To raise an error, the `column` method should use this method to set the error message
|
/// To raise an error, the `column` method should use this method to set the error message
|
||||||
/// and return the error code.
|
/// and return the error code.
|
||||||
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
|
pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => ffi::SQLITE_OK,
|
Ok(_) => ffi::SQLITE_OK,
|
||||||
|
127
tests/vtab.rs
Normal file
127
tests/vtab.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
//! Ensure Virtual tables can be declared outside `rusqlite` crate.
|
||||||
|
|
||||||
|
#[cfg(feature = "vtab")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate rusqlite;
|
||||||
|
extern crate libsqlite3_sys as ffi;
|
||||||
|
|
||||||
|
#[cfg(feature = "vtab")]
|
||||||
|
#[test]
|
||||||
|
fn test_dummy_module() {
|
||||||
|
use ffi;
|
||||||
|
use rusqlite::vtab::{Context, IndexInfo, Module, VTab, VTabConnection, VTabCursor, Values};
|
||||||
|
use rusqlite::{error_from_sqlite_code, Connection, Error, Result};
|
||||||
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
|
|
||||||
|
eponymous_module!(
|
||||||
|
DUMMY_MODULE,
|
||||||
|
DummyModule,
|
||||||
|
DummyTab,
|
||||||
|
(),
|
||||||
|
DummyTabCursor,
|
||||||
|
None,
|
||||||
|
dummy_connect,
|
||||||
|
dummy_best_index,
|
||||||
|
dummy_disconnect,
|
||||||
|
None,
|
||||||
|
dummy_open,
|
||||||
|
dummy_close,
|
||||||
|
dummy_filter,
|
||||||
|
dummy_next,
|
||||||
|
dummy_eof,
|
||||||
|
dummy_column,
|
||||||
|
dummy_rowid
|
||||||
|
);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct DummyModule(&'static ffi::sqlite3_module);
|
||||||
|
|
||||||
|
impl Module for DummyModule {
|
||||||
|
type Aux = ();
|
||||||
|
type Table = DummyTab;
|
||||||
|
|
||||||
|
fn as_ptr(&self) -> *const ffi::sqlite3_module {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect(
|
||||||
|
_: &mut VTabConnection,
|
||||||
|
_aux: Option<&()>,
|
||||||
|
_args: &[&[u8]],
|
||||||
|
) -> Result<(String, DummyTab)> {
|
||||||
|
let vtab = DummyTab {
|
||||||
|
base: ffi::sqlite3_vtab::default(),
|
||||||
|
};
|
||||||
|
Ok(("CREATE TABLE x(value)".to_owned(), vtab))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct DummyTab {
|
||||||
|
/// Base class. Must be first
|
||||||
|
base: ffi::sqlite3_vtab,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VTab for DummyTab {
|
||||||
|
type Cursor = DummyTabCursor;
|
||||||
|
|
||||||
|
fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
|
||||||
|
info.set_estimated_cost(1.);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&self) -> Result<DummyTabCursor> {
|
||||||
|
Ok(DummyTabCursor::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct DummyTabCursor {
|
||||||
|
/// Base class. Must be first
|
||||||
|
base: ffi::sqlite3_vtab_cursor,
|
||||||
|
/// The rowid
|
||||||
|
row_id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VTabCursor for DummyTabCursor {
|
||||||
|
type Table = DummyTab;
|
||||||
|
|
||||||
|
fn vtab(&self) -> &DummyTab {
|
||||||
|
unsafe { &*(self.base.pVtab as *const DummyTab) }
|
||||||
|
}
|
||||||
|
fn filter(
|
||||||
|
&mut self,
|
||||||
|
_idx_num: c_int,
|
||||||
|
_idx_str: Option<&str>,
|
||||||
|
_args: &Values,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.row_id = 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn next(&mut self) -> Result<()> {
|
||||||
|
self.row_id += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn eof(&self) -> bool {
|
||||||
|
self.row_id > 1
|
||||||
|
}
|
||||||
|
fn column(&self, ctx: &mut Context, _: c_int) -> Result<()> {
|
||||||
|
ctx.set_result(&self.row_id)
|
||||||
|
}
|
||||||
|
fn rowid(&self) -> Result<i64> {
|
||||||
|
Ok(self.row_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = Connection::open_in_memory().unwrap();
|
||||||
|
|
||||||
|
let module = DummyModule(&DUMMY_MODULE);
|
||||||
|
|
||||||
|
db.create_module("dummy", module, None).unwrap();
|
||||||
|
|
||||||
|
let mut s = db.prepare("SELECT * FROM dummy()").unwrap();
|
||||||
|
|
||||||
|
let dummy = s.query_row(&[], |row| row.get::<_, i32>(0)).unwrap();
|
||||||
|
assert_eq!(1, dummy);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user