mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 09:09:19 +08:00
Add series table-valued-function.
This commit is contained in:
parent
3b5bd7abab
commit
424a6c0cc8
@ -20,9 +20,11 @@ pub fn load_module(conn: &Connection) -> Result<()> {
|
|||||||
init_module!(CSV_MODULE,
|
init_module!(CSV_MODULE,
|
||||||
CSVTab,
|
CSVTab,
|
||||||
CSVTabCursor,
|
CSVTabCursor,
|
||||||
csv_create,
|
Some(csv_connect),
|
||||||
|
csv_connect,
|
||||||
csv_best_index,
|
csv_best_index,
|
||||||
csv_destroy,
|
csv_disconnect,
|
||||||
|
Some(csv_disconnect),
|
||||||
csv_open,
|
csv_open,
|
||||||
csv_close,
|
csv_close,
|
||||||
csv_filter,
|
csv_filter,
|
||||||
@ -56,7 +58,7 @@ impl CSVTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VTab<CSVTabCursor> for CSVTab {
|
impl VTab<CSVTabCursor> for CSVTab {
|
||||||
fn create(db: *mut ffi::sqlite3, _aux: *mut libc::c_void, args: &[&[u8]]) -> Result<CSVTab> {
|
fn connect(db: *mut ffi::sqlite3, _aux: *mut libc::c_void, args: &[&[u8]]) -> Result<CSVTab> {
|
||||||
if args.len() < 4 {
|
if args.len() < 4 {
|
||||||
return Err(Error::ModuleError("no CSV file specified".to_owned()));
|
return Err(Error::ModuleError("no CSV file specified".to_owned()));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Int array virtual table.
|
//! Int array virtual table.
|
||||||
|
//! Port of C ["intarray"](http://www.sqlite.org/cgi/src/finfo?name=src/test_intarray.h).
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@ -39,9 +40,11 @@ pub fn drop_int_array(conn: &Connection, name: &str) -> Result<()> {
|
|||||||
init_module!(INT_ARRAY_MODULE,
|
init_module!(INT_ARRAY_MODULE,
|
||||||
IntArrayVTab,
|
IntArrayVTab,
|
||||||
IntArrayVTabCursor,
|
IntArrayVTabCursor,
|
||||||
int_array_create,
|
Some(int_array_connect),
|
||||||
|
int_array_connect,
|
||||||
int_array_best_index,
|
int_array_best_index,
|
||||||
int_array_destroy,
|
int_array_disconnect,
|
||||||
|
Some(int_array_disconnect),
|
||||||
int_array_open,
|
int_array_open,
|
||||||
int_array_close,
|
int_array_close,
|
||||||
int_array_filter,
|
int_array_filter,
|
||||||
@ -58,7 +61,7 @@ struct IntArrayVTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VTab<IntArrayVTabCursor> for IntArrayVTab {
|
impl VTab<IntArrayVTabCursor> for IntArrayVTab {
|
||||||
fn create(db: *mut ffi::sqlite3,
|
fn connect(db: *mut ffi::sqlite3,
|
||||||
aux: *mut libc::c_void,
|
aux: *mut libc::c_void,
|
||||||
_args: &[&[u8]])
|
_args: &[&[u8]])
|
||||||
-> Result<IntArrayVTab> {
|
-> Result<IntArrayVTab> {
|
||||||
|
@ -44,7 +44,7 @@ use types::FromSql;
|
|||||||
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 create(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<()>;
|
||||||
/// Create a new cursor used for accessing a virtual table.
|
/// Create a new cursor used for accessing a virtual table.
|
||||||
@ -90,7 +90,7 @@ impl IndexInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// True if this constraint is usable
|
/// True if this constraint is usable
|
||||||
pub fn constraint_usable(&self, constraint_idx: usize) -> bool {
|
pub fn is_constraint_usable(&self, constraint_idx: usize) -> bool {
|
||||||
use std::slice;
|
use std::slice;
|
||||||
unsafe {
|
unsafe {
|
||||||
let constraints = slice::from_raw_parts((*self.0).aConstraint,
|
let constraints = slice::from_raw_parts((*self.0).aConstraint,
|
||||||
@ -288,18 +288,19 @@ unsafe extern "C" fn free_boxed_value<T>(p: *mut libc::c_void) {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! init_module {
|
macro_rules! init_module {
|
||||||
($module_name: ident, $vtab: ident, $cursor: ty,
|
($module_name: ident, $vtab: ident, $cursor: ty,
|
||||||
$create: ident, $best_index: ident, $destroy: ident,
|
$create: expr, $connect: ident, $best_index: ident,
|
||||||
|
$disconnect: ident, $destroy: expr,
|
||||||
$open: ident, $close: ident,
|
$open: ident, $close: ident,
|
||||||
$filter: ident, $next: ident, $eof: ident,
|
$filter: ident, $next: ident, $eof: ident,
|
||||||
$column: ident, $rowid: ident) => {
|
$column: ident, $rowid: ident) => {
|
||||||
|
|
||||||
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
||||||
iVersion: 1,
|
iVersion: 1,
|
||||||
xCreate: Some($create),
|
xCreate: $create,
|
||||||
xConnect: Some($create), /* 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($destroy),
|
xDisconnect: Some($disconnect),
|
||||||
xDestroy: Some($destroy),
|
xDestroy: $destroy,
|
||||||
xOpen: Some($open),
|
xOpen: Some($open),
|
||||||
xClose: Some($close),
|
xClose: Some($close),
|
||||||
xFilter: Some($filter),
|
xFilter: Some($filter),
|
||||||
@ -319,7 +320,7 @@ static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
|
|||||||
xRollbackTo: None,
|
xRollbackTo: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe extern "C" fn $create(db: *mut ffi::sqlite3,
|
unsafe extern "C" fn $connect(db: *mut ffi::sqlite3,
|
||||||
aux: *mut libc::c_void,
|
aux: *mut libc::c_void,
|
||||||
argc: libc::c_int,
|
argc: libc::c_int,
|
||||||
argv: *const *const libc::c_char,
|
argv: *const *const libc::c_char,
|
||||||
@ -334,7 +335,7 @@ unsafe extern "C" fn $create(db: *mut ffi::sqlite3,
|
|||||||
let vec = args.iter().map(|cs| {
|
let vec = args.iter().map(|cs| {
|
||||||
CStr::from_ptr(*cs).to_bytes()
|
CStr::from_ptr(*cs).to_bytes()
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
match $vtab::create(db, aux, &vec[..]) {
|
match $vtab::connect(db, aux, &vec[..]) {
|
||||||
Ok(vtab) => {
|
Ok(vtab) => {
|
||||||
let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab));
|
let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab));
|
||||||
*pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
|
*pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
|
||||||
@ -375,7 +376,7 @@ unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab,
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
unsafe extern "C" fn $destroy(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int {
|
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int {
|
||||||
let vtab = vtab as *mut $vtab;
|
let vtab = vtab as *mut $vtab;
|
||||||
let _: Box<$vtab> = Box::from_raw(vtab);
|
let _: Box<$vtab> = Box::from_raw(vtab);
|
||||||
ffi::SQLITE_OK
|
ffi::SQLITE_OK
|
||||||
@ -536,3 +537,4 @@ pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char {
|
|||||||
pub mod int_array;
|
pub mod int_array;
|
||||||
#[cfg(feature = "csvtab")]
|
#[cfg(feature = "csvtab")]
|
||||||
pub mod csvtab;
|
pub mod csvtab;
|
||||||
|
pub mod series;
|
||||||
|
244
src/vtab/series.rs
Normal file
244
src/vtab/series.rs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
//! 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;
|
||||||
|
|
||||||
|
use {Connection, Error, Result};
|
||||||
|
use ffi;
|
||||||
|
use vtab::{self, declare_vtab, Context, IndexInfo, Values, VTab, VTabCursor};
|
||||||
|
|
||||||
|
/// Register the "generate_series" module.
|
||||||
|
pub fn load_module(conn: &Connection) -> Result<()> {
|
||||||
|
let aux: Option<()> = None;
|
||||||
|
conn.create_module("generate_series", &SERIES_MODULE, aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init_module!(SERIES_MODULE,
|
||||||
|
SeriesTab,
|
||||||
|
SeriesTabCursor,
|
||||||
|
None,
|
||||||
|
series_connect,
|
||||||
|
series_best_index,
|
||||||
|
series_disconnect,
|
||||||
|
None,
|
||||||
|
series_open,
|
||||||
|
series_close,
|
||||||
|
series_filter,
|
||||||
|
series_next,
|
||||||
|
series_eof,
|
||||||
|
series_column,
|
||||||
|
series_rowid);
|
||||||
|
|
||||||
|
// Column numbers
|
||||||
|
// const SERIES_COLUMN_VALUE : libc::c_int = 0;
|
||||||
|
const SERIES_COLUMN_START: libc::c_int = 1;
|
||||||
|
const SERIES_COLUMN_STOP: libc::c_int = 2;
|
||||||
|
const SERIES_COLUMN_STEP: libc::c_int = 3;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[repr(C)]
|
||||||
|
flags QueryPlanFlags: ::libc::c_int {
|
||||||
|
// start = $value -- constraint exists
|
||||||
|
const START = 1,
|
||||||
|
// stop = $value -- constraint exists
|
||||||
|
const STOP = 2,
|
||||||
|
// step = $value -- constraint exists
|
||||||
|
const STEP = 4,
|
||||||
|
// output in descending order
|
||||||
|
const DESC = 8,
|
||||||
|
// Both start and stop
|
||||||
|
const BOTH = START.bits | STOP.bits,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// An instance of the Series virtual table
|
||||||
|
#[repr(C)]
|
||||||
|
struct SeriesTab {
|
||||||
|
/// Base class. Must be first
|
||||||
|
base: ffi::sqlite3_vtab,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl VTab<SeriesTabCursor> for SeriesTab {
|
||||||
|
fn connect(db: *mut ffi::sqlite3,
|
||||||
|
_aux: *mut libc::c_void,
|
||||||
|
_args: &[&[u8]])
|
||||||
|
-> Result<SeriesTab> {
|
||||||
|
let vtab = SeriesTab { base: Default::default() };
|
||||||
|
try!(declare_vtab(db,
|
||||||
|
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)"));
|
||||||
|
Ok(vtab)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
|
||||||
|
// The query plan bitmask
|
||||||
|
let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();
|
||||||
|
// Index of the start= constraint
|
||||||
|
let mut start_idx = None;
|
||||||
|
// Index of the stop= constraint
|
||||||
|
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) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if info.constraint_operator(i) != vtab::SQLITE_INDEX_CONSTRAINT_EQ {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match info.constraint_column(i) {
|
||||||
|
SERIES_COLUMN_START => {
|
||||||
|
start_idx = Some(i);
|
||||||
|
idx_num |= START;
|
||||||
|
}
|
||||||
|
SERIES_COLUMN_STOP => {
|
||||||
|
stop_idx = Some(i);
|
||||||
|
idx_num |= STOP;
|
||||||
|
}
|
||||||
|
SERIES_COLUMN_STEP => {
|
||||||
|
step_idx = Some(i);
|
||||||
|
idx_num |= STEP;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut num_of_arg = 0;
|
||||||
|
if let Some(start_idx) = start_idx {
|
||||||
|
num_of_arg += 1;
|
||||||
|
info.set_argv_index(start_idx, num_of_arg);
|
||||||
|
info.set_omit(start_idx, true);
|
||||||
|
}
|
||||||
|
if let Some(stop_idx) = stop_idx {
|
||||||
|
num_of_arg += 1;
|
||||||
|
info.set_argv_index(stop_idx, num_of_arg);
|
||||||
|
info.set_omit(stop_idx, true);
|
||||||
|
}
|
||||||
|
if let Some(step_idx) = step_idx {
|
||||||
|
num_of_arg += 1;
|
||||||
|
info.set_argv_index(step_idx, num_of_arg);
|
||||||
|
info.set_omit(step_idx, true);
|
||||||
|
}
|
||||||
|
if idx_num.contains(BOTH) {
|
||||||
|
// Both start= and stop= boundaries are available.
|
||||||
|
info.set_estimated_cost((2 - if idx_num.contains(STEP) { 1 } else { 0 }) as f64);
|
||||||
|
info.set_estimated_rows(1000);
|
||||||
|
if info.num_of_order_by() == 1 {
|
||||||
|
if info.is_order_by_desc(0) {
|
||||||
|
idx_num |= DESC;
|
||||||
|
}
|
||||||
|
info.set_order_by_consumed(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info.set_estimated_cost(2147483647f64);
|
||||||
|
info.set_estimated_rows(2147483647);
|
||||||
|
}
|
||||||
|
info.set_idx_num(idx_num.bits());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&self) -> Result<SeriesTabCursor> {
|
||||||
|
Ok(SeriesTabCursor::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A cursor for the Series virtual table
|
||||||
|
#[derive(Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct SeriesTabCursor {
|
||||||
|
/// Base class. Must be first
|
||||||
|
base: ffi::sqlite3_vtab_cursor,
|
||||||
|
/// True to count down rather than up
|
||||||
|
is_desc: bool,
|
||||||
|
/// The rowid
|
||||||
|
row_id: i64,
|
||||||
|
/// Current value ("value")
|
||||||
|
value: i64,
|
||||||
|
/// Mimimum value ("start")
|
||||||
|
min_value: i64,
|
||||||
|
/// Maximum value ("stop")
|
||||||
|
max_value: i64,
|
||||||
|
/// Increment ("step")
|
||||||
|
step: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeriesTabCursor {
|
||||||
|
fn new() -> SeriesTabCursor {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl VTabCursor<SeriesTab> for SeriesTabCursor {
|
||||||
|
fn vtab(&self) -> &mut SeriesTab {
|
||||||
|
unsafe { &mut *(self.base.pVtab as *mut SeriesTab) }
|
||||||
|
}
|
||||||
|
fn filter(&mut self,
|
||||||
|
idx_num: libc::c_int,
|
||||||
|
_idx_str: Option<&str>,
|
||||||
|
args: &Values)
|
||||||
|
-> Result<()> {
|
||||||
|
let idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
|
||||||
|
let mut i = 0;
|
||||||
|
if idx_num.contains(START) {
|
||||||
|
self.min_value = try!(args.get(i));
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
self.min_value = 0;
|
||||||
|
}
|
||||||
|
if idx_num.contains(STOP) {
|
||||||
|
self.max_value = try!(args.get(i));
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
self.max_value = 0xffffffff;
|
||||||
|
}
|
||||||
|
if idx_num.contains(STEP) {
|
||||||
|
self.step = try!(args.get(i));
|
||||||
|
if self.step < 1 {
|
||||||
|
self.step = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.step = 1;
|
||||||
|
};
|
||||||
|
self.is_desc = idx_num.contains(DESC);
|
||||||
|
if self.is_desc {
|
||||||
|
self.value = self.max_value;
|
||||||
|
if self.step > 1 {
|
||||||
|
self.value -= (self.max_value - self.min_value) % self.step;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.value = self.min_value;
|
||||||
|
}
|
||||||
|
self.row_id = 0;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn next(&mut self) -> Result<()> {
|
||||||
|
if self.is_desc {
|
||||||
|
self.value -= self.step;
|
||||||
|
} else {
|
||||||
|
self.value += self.step;
|
||||||
|
}
|
||||||
|
self.row_id += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn eof(&self) -> bool {
|
||||||
|
if self.is_desc {
|
||||||
|
self.value < self.min_value
|
||||||
|
} else {
|
||||||
|
self.value > self.max_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()> {
|
||||||
|
let x = match i {
|
||||||
|
SERIES_COLUMN_START => self.min_value,
|
||||||
|
SERIES_COLUMN_STOP => self.max_value,
|
||||||
|
SERIES_COLUMN_STEP => self.step,
|
||||||
|
_ => self.value,
|
||||||
|
};
|
||||||
|
ctx.set_result(&x);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn rowid(&self) -> Result<i64> {
|
||||||
|
Ok(self.row_id)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user