mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 00:39:20 +08:00
Introduce context module
To make `set_result` and `report_error` in functions module visible to vtab module.
This commit is contained in:
parent
f4beb18904
commit
6cbeb6ef59
@ -38,7 +38,7 @@ script:
|
|||||||
- cargo test --features bundled
|
- cargo test --features bundled
|
||||||
- cargo test --features sqlcipher
|
- cargo test --features sqlcipher
|
||||||
- cargo test --features "unlock_notify bundled"
|
- 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"
|
||||||
- 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 buildtime_bindgen"
|
||||||
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled"
|
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled"
|
||||||
|
@ -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 = ["functions", "libsqlite3-sys/min_sqlite_version_3_7_7"]
|
vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
||||||
csvtab = ["csv", "vtab"]
|
csvtab = ["csv", "vtab"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
12
src/error.rs
12
src/error.rs
@ -68,6 +68,10 @@ pub enum Error {
|
|||||||
/// to the requested type.
|
/// to the requested type.
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
InvalidFunctionParameterType(usize, Type),
|
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.,
|
/// An error case available for implementors of custom user functions (e.g.,
|
||||||
/// `create_scalar_function`).
|
/// `create_scalar_function`).
|
||||||
@ -135,6 +139,10 @@ impl fmt::Display for Error {
|
|||||||
Error::InvalidFunctionParameterType(i, ref t) => {
|
Error::InvalidFunctionParameterType(i, ref t) => {
|
||||||
write!(f, "Invalid function parameter type {} at index {}", t, i)
|
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")]
|
#[cfg(feature = "functions")]
|
||||||
Error::UserFunctionError(ref err) => err.fmt(f),
|
Error::UserFunctionError(ref err) => err.fmt(f),
|
||||||
Error::ToSqlConversionFailure(ref err) => err.fmt(f),
|
Error::ToSqlConversionFailure(ref err) => err.fmt(f),
|
||||||
@ -165,6 +173,8 @@ impl error::Error for Error {
|
|||||||
|
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
|
Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
|
||||||
|
#[cfg(feature = "vtab")]
|
||||||
|
Error::InvalidFilterParameterType(_, _) => "invalid filter parameter type",
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::UserFunctionError(ref err) => err.description(),
|
Error::UserFunctionError(ref err) => err.description(),
|
||||||
Error::ToSqlConversionFailure(ref err) => err.description(),
|
Error::ToSqlConversionFailure(ref err) => err.description(),
|
||||||
@ -192,6 +202,8 @@ impl error::Error for Error {
|
|||||||
|
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::InvalidFunctionParameterType(_, _) => None,
|
Error::InvalidFunctionParameterType(_, _) => None,
|
||||||
|
#[cfg(feature = "vtab")]
|
||||||
|
Error::InvalidFilterParameterType(_, _) => None,
|
||||||
|
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::UserFunctionError(ref err) => Some(&**err),
|
Error::UserFunctionError(ref err) => Some(&**err),
|
||||||
|
125
src/functions.rs
125
src/functions.rs
@ -49,138 +49,19 @@
|
|||||||
//! assert!(is_match);
|
//! assert!(is_match);
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
use std::error::Error as StdError;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
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;
|
||||||
use ffi::sqlite3_context;
|
use ffi::sqlite3_context;
|
||||||
use ffi::sqlite3_value;
|
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};
|
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) {
|
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);
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,8 @@ pub use hooks::*;
|
|||||||
mod unlock_notify;
|
mod unlock_notify;
|
||||||
#[cfg(feature = "vtab")]
|
#[cfg(feature = "vtab")]
|
||||||
pub mod vtab;
|
pub mod vtab;
|
||||||
|
#[cfg(any(feature = "functions", feature = "vtab"))]
|
||||||
|
mod context;
|
||||||
|
|
||||||
// Number of cached prepared statements we'll hold on to.
|
// Number of cached prepared statements we'll hold on to.
|
||||||
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
||||||
|
@ -6,9 +6,9 @@ use std::os::raw::{c_char, c_int, c_void};
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
use context::{report_error, set_result};
|
||||||
use error::error_from_sqlite_code;
|
use error::error_from_sqlite_code;
|
||||||
use ffi;
|
use ffi;
|
||||||
use functions::{report_error, set_result};
|
|
||||||
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};
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ impl IndexInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Estimated number of rows returned
|
/// 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) {
|
pub fn set_estimated_rows(&mut self, estimated_rows: i64) {
|
||||||
unsafe {
|
unsafe {
|
||||||
(*self.0).estimatedRows = estimated_rows;
|
(*self.0).estimatedRows = estimated_rows;
|
||||||
@ -204,7 +204,6 @@ pub trait VTabCursor: Sized {
|
|||||||
fn rowid(&self) -> Result<i64>;
|
fn rowid(&self) -> Result<i64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME clash with functions::Context
|
|
||||||
pub struct Context(*mut ffi::sqlite3_context);
|
pub struct Context(*mut ffi::sqlite3_context);
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
@ -234,9 +233,7 @@ impl<'a> Values<'a> {
|
|||||||
let arg = self.args[idx];
|
let arg = self.args[idx];
|
||||||
let value = unsafe { ValueRef::from_value(arg) };
|
let value = unsafe { ValueRef::from_value(arg) };
|
||||||
FromSql::column_result(value).map_err(|err| match err {
|
FromSql::column_result(value).map_err(|err| match err {
|
||||||
FromSqlError::InvalidType => {
|
FromSqlError::InvalidType => Error::InvalidFilterParameterType(idx, value.data_type()),
|
||||||
Error::InvalidFunctionParameterType(idx, value.data_type())
|
|
||||||
}
|
|
||||||
FromSqlError::Other(err) => {
|
FromSqlError::Other(err) => {
|
||||||
Error::FromSqlConversionFailure(idx, value.data_type(), err)
|
Error::FromSqlConversionFailure(idx, value.data_type(), err)
|
||||||
}
|
}
|
||||||
@ -474,7 +471,7 @@ macro_rules! eponymous_module {
|
|||||||
iVersion: 1,
|
iVersion: 1,
|
||||||
xCreate: $create, /* For eponymous-only virtual tables, the xCreate method is NULL */
|
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
|
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),
|
xBestIndex: Some($best_index),
|
||||||
xDisconnect: Some($disconnect),
|
xDisconnect: Some($disconnect),
|
||||||
xDestroy: $destroy,
|
xDestroy: $destroy,
|
||||||
@ -763,7 +760,7 @@ pub fn mprintf(err_msg: &str) -> *mut c_char {
|
|||||||
pub mod csvtab;
|
pub mod csvtab;
|
||||||
pub mod int_array;
|
pub mod int_array;
|
||||||
#[cfg(feature = "bundled")]
|
#[cfg(feature = "bundled")]
|
||||||
pub mod series;
|
pub mod series; // SQLite >= 3.9.0
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -65,11 +65,7 @@ impl VTab for SeriesTab {
|
|||||||
type Aux = ();
|
type Aux = ();
|
||||||
type Cursor = SeriesTabCursor;
|
type Cursor = SeriesTabCursor;
|
||||||
|
|
||||||
unsafe fn connect(
|
unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result<SeriesTab> {
|
||||||
db: *mut ffi::sqlite3,
|
|
||||||
_aux: *mut (),
|
|
||||||
_args: &[&[u8]],
|
|
||||||
) -> Result<SeriesTab> {
|
|
||||||
let vtab = SeriesTab {
|
let vtab = SeriesTab {
|
||||||
base: Default::default(),
|
base: Default::default(),
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user