Introduce context module

To make `set_result` and `report_error` in functions module
visible to vtab module.
This commit is contained in:
gwenn 2018-05-13 11:28:56 +02:00
parent f4beb18904
commit 6cbeb6ef59
7 changed files with 25 additions and 137 deletions

View File

@ -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"

View File

@ -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]

View File

@ -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),

View File

@ -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<T>(p: *mut c_void) {
let _: Box<T> = Box::from_raw(p as *mut T);
}

View File

@ -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;

View File

@ -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<i64>;
}
// 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 {

View File

@ -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<SeriesTab> {
unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result<SeriesTab> {
let vtab = SeriesTab {
base: Default::default(),
};