Make index constraints and values iterable

This commit is contained in:
gwenn 2016-08-20 09:55:25 +02:00
parent 1d342ef5f3
commit b11a4b1c73
2 changed files with 87 additions and 50 deletions

View File

@ -4,13 +4,14 @@ use std::borrow::Cow::{self, Borrowed, Owned};
use std::ffi::CString; use std::ffi::CString;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use std::slice;
use libc; use libc;
use {Connection, Error, Result, InnerConnection, str_to_cstring}; use {Connection, Error, Result, InnerConnection, str_to_cstring};
use error::error_from_sqlite_code; use error::error_from_sqlite_code;
use ffi; use ffi;
use functions::ToResult; use functions::ToResult;
use types::FromSql; use types::{FromSql, ValueRef};
// let conn: Connection = ...; // let conn: Connection = ...;
// let mod: Module = ...; // VTab builder // let mod: Module = ...; // VTab builder
@ -43,7 +44,8 @@ use types::FromSql;
/// Virtual table instance trait. /// Virtual table instance trait.
pub trait VTab<C: VTabCursor<Self>>: Sized { pub trait VTab<C: VTabCursor<Self>>: Sized {
/// 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 CREATE VIRTUAL TABLE statement. /// The `db` parameter is a pointer to the SQLite database connection that is executing
/// the CREATE VIRTUAL TABLE statement.
fn connect(db: *mut ffi::sqlite3, aux: *mut libc::c_void, args: &[&[u8]]) -> Result<Self>; fn connect(db: *mut ffi::sqlite3, aux: *mut libc::c_void, args: &[&[u8]]) -> Result<Self>;
/// 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<()>;
@ -67,36 +69,10 @@ bitflags! {
pub struct IndexInfo(*mut ffi::sqlite3_index_info); pub struct IndexInfo(*mut ffi::sqlite3_index_info);
impl IndexInfo { impl IndexInfo {
/// Number of constraints pub fn constraints(&self) -> IndexConstraintIter {
pub fn num_of_constraint(&self) -> usize { let constraints =
unsafe { (*self.0).nConstraint as usize } unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
} IndexConstraintIter { iter: constraints.iter() }
/// Column constrained. -1 for ROWID
pub fn constraint_column(&self, constraint_idx: usize) -> libc::c_int {
use std::slice;
unsafe {
let constraints = slice::from_raw_parts((*self.0).aConstraint,
(*self.0).nConstraint as usize);
constraints[constraint_idx].iColumn
}
}
/// Constraint operator
pub fn constraint_operator(&self, constraint_idx: usize) -> IndexConstraintOp {
use std::slice;
unsafe {
let constraints = slice::from_raw_parts((*self.0).aConstraint,
(*self.0).nConstraint as usize);
IndexConstraintOp::from_bits_truncate(constraints[constraint_idx].op)
}
}
/// True if this constraint is usable
pub fn is_constraint_usable(&self, constraint_idx: usize) -> bool {
use std::slice;
unsafe {
let constraints = slice::from_raw_parts((*self.0).aConstraint,
(*self.0).nConstraint as usize);
constraints[constraint_idx].usable != 0
}
} }
/// Number of terms in the ORDER BY clause /// Number of terms in the ORDER BY clause
@ -105,7 +81,6 @@ impl IndexInfo {
} }
/// Column number /// Column number
pub fn order_by_column(&self, order_by_idx: usize) -> libc::c_int { pub fn order_by_column(&self, order_by_idx: usize) -> libc::c_int {
use std::slice;
unsafe { unsafe {
let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize); let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize);
order_bys[order_by_idx].iColumn order_bys[order_by_idx].iColumn
@ -113,7 +88,6 @@ impl IndexInfo {
} }
/// True for DESC. False for ASC. /// True for DESC. False for ASC.
pub fn is_order_by_desc(&self, order_by_idx: usize) -> bool { pub fn is_order_by_desc(&self, order_by_idx: usize) -> bool {
use std::slice;
unsafe { unsafe {
let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize); let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize);
order_bys[order_by_idx].desc != 0 order_bys[order_by_idx].desc != 0
@ -122,7 +96,6 @@ impl IndexInfo {
/// if `argv_index` > 0, constraint is part of argv to xFilter /// if `argv_index` > 0, constraint is part of argv to xFilter
pub fn set_argv_index(&mut self, constraint_idx: usize, argv_index: libc::c_int) { pub fn set_argv_index(&mut self, constraint_idx: usize, argv_index: libc::c_int) {
use std::slice;
unsafe { unsafe {
let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage, let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage,
(*self.0).nConstraint as usize); (*self.0).nConstraint as usize);
@ -131,7 +104,6 @@ impl IndexInfo {
} }
/// if `omit`, do not code a test for this constraint /// if `omit`, do not code a test for this constraint
pub fn set_omit(&mut self, constraint_idx: usize, omit: bool) { pub fn set_omit(&mut self, constraint_idx: usize, omit: bool) {
use std::slice;
unsafe { unsafe {
let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage, let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage,
(*self.0).nConstraint as usize); (*self.0).nConstraint as usize);
@ -163,6 +135,38 @@ impl IndexInfo {
} }
} }
} }
pub struct IndexConstraintIter<'a> {
iter: slice::Iter<'a, ffi::Struct_sqlite3_index_constraint>,
}
impl<'a> Iterator for IndexConstraintIter<'a> {
type Item = IndexConstraint<'a>;
fn next(&mut self) -> Option<IndexConstraint<'a>> {
self.iter.next().map(|raw| IndexConstraint(raw))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub struct IndexConstraint<'a>(&'a ffi::Struct_sqlite3_index_constraint);
impl<'a> IndexConstraint<'a> {
/// Column constrained. -1 for ROWID
pub fn column(&self) -> libc::c_int {
self.0.iColumn
}
/// Constraint operator
pub fn operator(&self) -> IndexConstraintOp {
IndexConstraintOp::from_bits_truncate(self.0.op)
}
/// True if this constraint is usable
pub fn is_usable(&self) -> bool {
self.0.usable != 0
}
}
/// Virtual table cursor trait. /// Virtual table cursor trait.
pub trait VTabCursor<V: VTab<Self>>: Sized { pub trait VTabCursor<V: VTab<Self>>: Sized {
@ -172,9 +176,11 @@ pub trait VTabCursor<V: VTab<Self>>: Sized {
fn filter(&mut self, idx_num: libc::c_int, idx_str: Option<&str>, args: &Values) -> Result<()>; fn filter(&mut self, idx_num: libc::c_int, idx_str: Option<&str>, args: &Values) -> Result<()>;
/// Advance cursor to the next row of a result set initiated by `filter`. /// Advance cursor to the next row of a result set initiated by `filter`.
fn next(&mut self) -> Result<()>; fn next(&mut self) -> Result<()>;
/// Must return `false` if the cursor currently points to a valid row of data, or `true` otherwise. /// Must return `false` if the cursor currently points to a valid row of data,
/// or `true` otherwise.
fn eof(&self) -> bool; fn eof(&self) -> bool;
/// Find the value for the `i`-th column of the current row. `i` is zero-based so the first column is numbered 0. /// Find the value for the `i`-th column of the current row.
/// `i` is zero-based so the first column is numbered 0.
/// May return its result back to SQLite using one of the specified `ctx`. /// May return its result back to SQLite using one of the specified `ctx`.
fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()>; fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()>;
/// Return the rowid of row that the cursor is currently pointing at. /// Return the rowid of row that the cursor is currently pointing at.
@ -185,7 +191,7 @@ pub trait VTabCursor<V: VTab<Self>>: Sized {
pub struct Context(*mut ffi::sqlite3_context); pub struct Context(*mut ffi::sqlite3_context);
impl Context { impl Context {
pub fn set_result(&mut self, value: &ToResult) { pub fn set_result<T: ToResult>(&mut self, value: &T) {
unsafe { unsafe {
value.set_result(self.0); value.set_result(self.0);
} }
@ -206,7 +212,6 @@ impl<'a> Values<'a> {
} }
pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> { pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
use types::ValueRef;
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 {
@ -214,6 +219,35 @@ impl<'a> Values<'a> {
_ => err, _ => err,
}) })
} }
pub fn iter(&self) -> ValueIter {
ValueIter { iter: self.args.iter() }
}
}
impl<'a> IntoIterator for &'a Values<'a> {
type Item = ValueRef<'a>;
type IntoIter = ValueIter<'a>;
fn into_iter(self) -> ValueIter<'a> {
self.iter()
}
}
pub struct ValueIter<'a> {
iter: slice::Iter<'a, *mut ffi::sqlite3_value>,
}
impl<'a> Iterator for ValueIter<'a> {
type Item = ValueRef<'a>;
fn next(&mut self) -> Option<ValueRef<'a>> {
self.iter.next().map(|raw| { unsafe { ValueRef::from_value(*raw) } })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
} }
impl Connection { impl Connection {
@ -297,7 +331,8 @@ macro_rules! init_module {
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { static $module_name: ffi::sqlite3_module = ffi::sqlite3_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 the exact same function as the xConnect method */ 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), xBestIndex: Some($best_index),
xDisconnect: Some($disconnect), xDisconnect: Some($disconnect),
xDestroy: $destroy, xDestroy: $destroy,
@ -495,8 +530,9 @@ pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
(*vtab).zErrMsg = mprintf(err_msg); (*vtab).zErrMsg = mprintf(err_msg);
} }
/// To raise an error, the `column` method should use this method to set the error message and return the error code. /// To raise an error, the `column` method should use this method to set the error message
pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> libc::c_int { /// and return the error code.
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> libc::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,
@ -527,7 +563,8 @@ pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>)
} }
} }
// Space to hold this error message string must be obtained from an SQLite memory allocation function. // Space to hold this error message string must be obtained
// from an SQLite memory allocation function.
pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char { pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char {
let c_format = CString::new("%s").unwrap(); let c_format = CString::new("%s").unwrap();
let c_err = CString::new(err_msg).unwrap(); let c_err = CString::new(err_msg).unwrap();

View File

@ -1,5 +1,5 @@
//! 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 libc; use libc;
@ -81,14 +81,14 @@ impl VTab<SeriesTabCursor> for SeriesTab {
let mut stop_idx = None; let mut stop_idx = None;
// Index of the step= constraint // Index of the step= constraint
let mut step_idx = None; let mut step_idx = None;
for i in 0..info.num_of_constraint() { for (i, constraint) in info.constraints().enumerate() {
if !info.is_constraint_usable(i) { if !constraint.is_usable() {
continue; continue;
} }
if info.constraint_operator(i) != vtab::SQLITE_INDEX_CONSTRAINT_EQ { if constraint.operator() != vtab::SQLITE_INDEX_CONSTRAINT_EQ {
continue; continue;
} }
match info.constraint_column(i) { match constraint.column() {
SERIES_COLUMN_START => { SERIES_COLUMN_START => {
start_idx = Some(i); start_idx = Some(i);
idx_num |= START; idx_num |= START;