From 6cbeb6ef59599c59aaf5550c077cb9eadd86af78 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 13 May 2018 11:28:56 +0200 Subject: [PATCH] Introduce context module To make `set_result` and `report_error` in functions module visible to vtab module. --- .travis.yml | 2 +- Cargo.toml | 2 +- src/error.rs | 12 +++++ src/functions.rs | 125 ++------------------------------------------- src/lib.rs | 2 + src/vtab/mod.rs | 13 ++--- src/vtab/series.rs | 6 +-- 7 files changed, 25 insertions(+), 137 deletions(-) diff --git a/.travis.yml b/.travis.yml index ffe7022..b0baae8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ script: - cargo test --features bundled - cargo test --features sqlcipher - cargo test --features "unlock_notify bundled" - - cargo test --features "csvtab functions vtab" + - cargo test --features "csvtab vtab" - cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab" - cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab buildtime_bindgen" - cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled" diff --git a/Cargo.toml b/Cargo.toml index 99dba8c..8bab3c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ limits = [] hooks = [] sqlcipher = ["libsqlite3-sys/sqlcipher"] unlock_notify = ["libsqlite3-sys/unlock_notify"] -vtab = ["functions", "libsqlite3-sys/min_sqlite_version_3_7_7"] +vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"] csvtab = ["csv", "vtab"] [dependencies] diff --git a/src/error.rs b/src/error.rs index b60ba33..44c04de 100644 --- a/src/error.rs +++ b/src/error.rs @@ -68,6 +68,10 @@ pub enum Error { /// to the requested type. #[cfg(feature = "functions")] InvalidFunctionParameterType(usize, Type), + /// Error returned by `vtab::Values::get` when the filter argument cannot be converted + /// to the requested type. + #[cfg(feature = "vtab")] + InvalidFilterParameterType(usize, Type), /// An error case available for implementors of custom user functions (e.g., /// `create_scalar_function`). @@ -135,6 +139,10 @@ impl fmt::Display for Error { Error::InvalidFunctionParameterType(i, ref t) => { write!(f, "Invalid function parameter type {} at index {}", t, i) } + #[cfg(feature = "vtab")] + Error::InvalidFilterParameterType(i, ref t) => { + write!(f, "Invalid filter parameter type {} at index {}", t, i) + } #[cfg(feature = "functions")] Error::UserFunctionError(ref err) => err.fmt(f), Error::ToSqlConversionFailure(ref err) => err.fmt(f), @@ -165,6 +173,8 @@ impl error::Error for Error { #[cfg(feature = "functions")] Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type", + #[cfg(feature = "vtab")] + Error::InvalidFilterParameterType(_, _) => "invalid filter parameter type", #[cfg(feature = "functions")] Error::UserFunctionError(ref err) => err.description(), Error::ToSqlConversionFailure(ref err) => err.description(), @@ -192,6 +202,8 @@ impl error::Error for Error { #[cfg(feature = "functions")] Error::InvalidFunctionParameterType(_, _) => None, + #[cfg(feature = "vtab")] + Error::InvalidFilterParameterType(_, _) => None, #[cfg(feature = "functions")] Error::UserFunctionError(ref err) => Some(&**err), diff --git a/src/functions.rs b/src/functions.rs index 6e8b7c5..f3778dc 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -49,138 +49,19 @@ //! assert!(is_match); //! } //! ``` -use std::error::Error as StdError; -use std::ffi::CStr; use std::ptr; use std::slice; -use std::os::raw::{c_int, c_char, c_void}; +use std::os::raw::{c_int, c_void}; use ffi; use ffi::sqlite3_context; use ffi::sqlite3_value; -use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef}; +use context::{report_error, set_result}; +use types::{ToSql, FromSql, FromSqlError, ValueRef}; use {Result, Error, Connection, str_to_cstring, InnerConnection}; -pub unsafe fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) { - let value = match *result { - ToSqlOutput::Borrowed(v) => v, - ToSqlOutput::Owned(ref v) => ValueRef::from(v), - - #[cfg(feature = "blob")] - ToSqlOutput::ZeroBlob(len) => { - return ffi::sqlite3_result_zeroblob(ctx, len); - } - }; - - match value { - ValueRef::Null => ffi::sqlite3_result_null(ctx), - ValueRef::Integer(i) => ffi::sqlite3_result_int64(ctx, i), - ValueRef::Real(r) => ffi::sqlite3_result_double(ctx, r), - ValueRef::Text(s) => { - let length = s.len(); - if length > ::std::i32::MAX as usize { - ffi::sqlite3_result_error_toobig(ctx); - } else { - let c_str = match str_to_cstring(s) { - Ok(c_str) => c_str, - // TODO sqlite3_result_error - Err(_) => return ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE), - }; - let destructor = if length > 0 { - ffi::SQLITE_TRANSIENT() - } else { - ffi::SQLITE_STATIC() - }; - ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor); - } - }, - ValueRef::Blob(b) => { - let length = b.len(); - if length > ::std::i32::MAX as usize { - ffi::sqlite3_result_error_toobig(ctx); - } else if length == 0 { - ffi::sqlite3_result_zeroblob(ctx, 0) - } else { - ffi::sqlite3_result_blob(ctx, - b.as_ptr() as *const c_void, - length as c_int, - ffi::SQLITE_TRANSIENT()); - } - }, - } -} - -pub unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) { - // Extended constraint error codes were added in SQLite 3.7.16. We don't have an explicit - // feature check for that, and this doesn't really warrant one. We'll use the extended code - // if we're on the bundled version (since it's at least 3.17.0) and the normal constraint - // error code if not. - #[cfg(feature = "bundled")] - fn constraint_error_code() -> i32 { - ffi::SQLITE_CONSTRAINT_FUNCTION - } - #[cfg(not(feature = "bundled"))] - fn constraint_error_code() -> i32 { - ffi::SQLITE_CONSTRAINT - } - - match *err { - Error::SqliteFailure(ref err, ref s) => { - ffi::sqlite3_result_error_code(ctx, err.extended_code); - if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) { - ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1); - } - } - _ => { - ffi::sqlite3_result_error_code(ctx, constraint_error_code()); - if let Ok(cstr) = str_to_cstring(err.description()) { - ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1); - } - } - } -} - -impl<'a> ValueRef<'a> { - pub unsafe fn from_value(value: *mut sqlite3_value) -> ValueRef<'a> { - use std::slice::from_raw_parts; - - match ffi::sqlite3_value_type(value) { - ffi::SQLITE_NULL => ValueRef::Null, - ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)), - ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)), - ffi::SQLITE_TEXT => { - let text = ffi::sqlite3_value_text(value); - assert!(!text.is_null(), - "unexpected SQLITE_TEXT value type with NULL data"); - let s = CStr::from_ptr(text as *const c_char); - - // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine. - let s = s.to_str() - .expect("sqlite3_value_text returned invalid UTF-8"); - ValueRef::Text(s) - } - ffi::SQLITE_BLOB => { - let (blob, len) = (ffi::sqlite3_value_blob(value), ffi::sqlite3_value_bytes(value)); - - assert!(len >= 0, - "unexpected negative return from sqlite3_value_bytes"); - if len > 0 { - assert!(!blob.is_null(), - "unexpected SQLITE_BLOB value type with NULL data"); - ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize)) - } else { - // The return value from sqlite3_value_blob() for a zero-length BLOB - // is a NULL pointer. - ValueRef::Blob(&[]) - } - } - _ => unreachable!("sqlite3_value_type returned invalid value"), - } - } -} - unsafe extern "C" fn free_boxed_value(p: *mut c_void) { let _: Box = Box::from_raw(p as *mut T); } diff --git a/src/lib.rs b/src/lib.rs index 6d69fbf..6710bbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,6 +128,8 @@ pub use hooks::*; mod unlock_notify; #[cfg(feature = "vtab")] pub mod vtab; +#[cfg(any(feature = "functions", feature = "vtab"))] +mod context; // Number of cached prepared statements we'll hold on to. const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; diff --git a/src/vtab/mod.rs b/src/vtab/mod.rs index 8db0dba..9a139db 100644 --- a/src/vtab/mod.rs +++ b/src/vtab/mod.rs @@ -6,9 +6,9 @@ use std::os::raw::{c_char, c_int, c_void}; use std::ptr; use std::slice; +use context::{report_error, set_result}; use error::error_from_sqlite_code; use ffi; -use functions::{report_error, set_result}; use types::{FromSql, FromSqlError, ToSql, ValueRef}; use {str_to_cstring, Connection, Error, InnerConnection, Result}; @@ -131,7 +131,7 @@ impl IndexInfo { } /// Estimated number of rows returned - #[cfg(feature = "bundled")] + #[cfg(feature = "bundled")] // SQLite >= 3.8.2 pub fn set_estimated_rows(&mut self, estimated_rows: i64) { unsafe { (*self.0).estimatedRows = estimated_rows; @@ -204,7 +204,6 @@ pub trait VTabCursor: Sized { fn rowid(&self) -> Result; } -// FIXME clash with functions::Context pub struct Context(*mut ffi::sqlite3_context); impl Context { @@ -234,9 +233,7 @@ impl<'a> Values<'a> { let arg = self.args[idx]; let value = unsafe { ValueRef::from_value(arg) }; FromSql::column_result(value).map_err(|err| match err { - FromSqlError::InvalidType => { - Error::InvalidFunctionParameterType(idx, value.data_type()) - } + FromSqlError::InvalidType => Error::InvalidFilterParameterType(idx, value.data_type()), FromSqlError::Other(err) => { Error::FromSqlConversionFailure(idx, value.data_type(), err) } @@ -474,7 +471,7 @@ macro_rules! eponymous_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 */ + the exact same function as the xConnect method */ xBestIndex: Some($best_index), xDisconnect: Some($disconnect), xDestroy: $destroy, @@ -763,7 +760,7 @@ pub fn mprintf(err_msg: &str) -> *mut c_char { pub mod csvtab; pub mod int_array; #[cfg(feature = "bundled")] -pub mod series; +pub mod series; // SQLite >= 3.9.0 #[cfg(test)] mod test { diff --git a/src/vtab/series.rs b/src/vtab/series.rs index 1fecb4c..167be5d 100644 --- a/src/vtab/series.rs +++ b/src/vtab/series.rs @@ -65,11 +65,7 @@ impl VTab for SeriesTab { type Aux = (); type Cursor = SeriesTabCursor; - unsafe fn connect( - db: *mut ffi::sqlite3, - _aux: *mut (), - _args: &[&[u8]], - ) -> Result { + unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result { let vtab = SeriesTab { base: Default::default(), };