mirror of
https://github.com/isar/rusqlite.git
synced 2025-01-21 01:00:51 +08:00
Make index constraints and values iterable
This commit is contained in:
parent
1d342ef5f3
commit
b11a4b1c73
125
src/vtab/mod.rs
125
src/vtab/mod.rs
@ -4,13 +4,14 @@ use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use libc;
|
||||
|
||||
use {Connection, Error, Result, InnerConnection, str_to_cstring};
|
||||
use error::error_from_sqlite_code;
|
||||
use ffi;
|
||||
use functions::ToResult;
|
||||
use types::FromSql;
|
||||
use types::{FromSql, ValueRef};
|
||||
|
||||
// let conn: Connection = ...;
|
||||
// let mod: Module = ...; // VTab builder
|
||||
@ -43,7 +44,8 @@ use types::FromSql;
|
||||
/// Virtual table instance trait.
|
||||
pub trait VTab<C: VTabCursor<Self>>: Sized {
|
||||
/// 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>;
|
||||
/// Determine the best way to access the virtual table.
|
||||
fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
|
||||
@ -67,36 +69,10 @@ bitflags! {
|
||||
pub struct IndexInfo(*mut ffi::sqlite3_index_info);
|
||||
|
||||
impl IndexInfo {
|
||||
/// Number of constraints
|
||||
pub fn num_of_constraint(&self) -> usize {
|
||||
unsafe { (*self.0).nConstraint as usize }
|
||||
}
|
||||
/// 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
|
||||
}
|
||||
pub fn constraints(&self) -> IndexConstraintIter {
|
||||
let constraints =
|
||||
unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
|
||||
IndexConstraintIter { iter: constraints.iter() }
|
||||
}
|
||||
|
||||
/// Number of terms in the ORDER BY clause
|
||||
@ -105,7 +81,6 @@ impl IndexInfo {
|
||||
}
|
||||
/// Column number
|
||||
pub fn order_by_column(&self, order_by_idx: usize) -> libc::c_int {
|
||||
use std::slice;
|
||||
unsafe {
|
||||
let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize);
|
||||
order_bys[order_by_idx].iColumn
|
||||
@ -113,7 +88,6 @@ impl IndexInfo {
|
||||
}
|
||||
/// True for DESC. False for ASC.
|
||||
pub fn is_order_by_desc(&self, order_by_idx: usize) -> bool {
|
||||
use std::slice;
|
||||
unsafe {
|
||||
let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize);
|
||||
order_bys[order_by_idx].desc != 0
|
||||
@ -122,7 +96,6 @@ impl IndexInfo {
|
||||
|
||||
/// 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) {
|
||||
use std::slice;
|
||||
unsafe {
|
||||
let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage,
|
||||
(*self.0).nConstraint as usize);
|
||||
@ -131,7 +104,6 @@ impl IndexInfo {
|
||||
}
|
||||
/// if `omit`, do not code a test for this constraint
|
||||
pub fn set_omit(&mut self, constraint_idx: usize, omit: bool) {
|
||||
use std::slice;
|
||||
unsafe {
|
||||
let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage,
|
||||
(*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.
|
||||
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<()>;
|
||||
/// Advance cursor to the next row of a result set initiated by `filter`.
|
||||
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;
|
||||
/// 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`.
|
||||
fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()>;
|
||||
/// 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);
|
||||
|
||||
impl Context {
|
||||
pub fn set_result(&mut self, value: &ToResult) {
|
||||
pub fn set_result<T: ToResult>(&mut self, value: &T) {
|
||||
unsafe {
|
||||
value.set_result(self.0);
|
||||
}
|
||||
@ -206,7 +212,6 @@ impl<'a> Values<'a> {
|
||||
}
|
||||
|
||||
pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
|
||||
use types::ValueRef;
|
||||
let arg = self.args[idx];
|
||||
let value = unsafe { ValueRef::from_value(arg) };
|
||||
FromSql::column_result(value).map_err(|err| match err {
|
||||
@ -214,6 +219,35 @@ impl<'a> Values<'a> {
|
||||
_ => 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 {
|
||||
@ -297,7 +331,8 @@ macro_rules! init_module {
|
||||
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 */
|
||||
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,
|
||||
@ -495,8 +530,9 @@ pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
|
||||
(*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.
|
||||
pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> libc::c_int {
|
||||
/// To raise an error, the `column` method should use this method to set the error message
|
||||
/// 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;
|
||||
match result {
|
||||
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 {
|
||||
let c_format = CString::new("%s").unwrap();
|
||||
let c_err = CString::new(err_msg).unwrap();
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! generate_series virtual table.
|
||||
//! Port of C [generate_series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c).
|
||||
//! generate series virtual table.
|
||||
//! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c).
|
||||
use std::default::Default;
|
||||
use libc;
|
||||
|
||||
@ -81,14 +81,14 @@ impl VTab<SeriesTabCursor> for SeriesTab {
|
||||
let mut stop_idx = None;
|
||||
// Index of the step= constraint
|
||||
let mut step_idx = None;
|
||||
for i in 0..info.num_of_constraint() {
|
||||
if !info.is_constraint_usable(i) {
|
||||
for (i, constraint) in info.constraints().enumerate() {
|
||||
if !constraint.is_usable() {
|
||||
continue;
|
||||
}
|
||||
if info.constraint_operator(i) != vtab::SQLITE_INDEX_CONSTRAINT_EQ {
|
||||
if constraint.operator() != vtab::SQLITE_INDEX_CONSTRAINT_EQ {
|
||||
continue;
|
||||
}
|
||||
match info.constraint_column(i) {
|
||||
match constraint.column() {
|
||||
SERIES_COLUMN_START => {
|
||||
start_idx = Some(i);
|
||||
idx_num |= START;
|
||||
|
Loading…
x
Reference in New Issue
Block a user