mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 13:58:55 +08:00 
			
		
		
		
	Remove macros
This commit is contained in:
		| @@ -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 { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user