2020-04-07 02:04:45 +08:00
|
|
|
//! `feature = "array"` Array Virtual Table.
|
2020-04-06 13:15:27 +08:00
|
|
|
//!
|
|
|
|
//! Note: `rarray`, not `carray` is the name of the table valued function we
|
|
|
|
//! define.
|
2018-07-15 16:19:18 +08:00
|
|
|
//!
|
2020-04-07 02:04:45 +08:00
|
|
|
//! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c)
|
2020-11-07 19:32:41 +08:00
|
|
|
//! C extension: `https://www.sqlite.org/carray.html`
|
2020-04-06 13:15:27 +08:00
|
|
|
//!
|
|
|
|
//! # Example
|
|
|
|
//!
|
|
|
|
//! ```rust,no_run
|
|
|
|
//! # use rusqlite::{types::Value, Connection, Result, params};
|
|
|
|
//! # use std::rc::Rc;
|
|
|
|
//! fn example(db: &Connection) -> Result<()> {
|
|
|
|
//! // Note: This should be done once (usually when opening the DB).
|
|
|
|
//! rusqlite::vtab::array::load_module(&db)?;
|
|
|
|
//! let v = [1i64, 2, 3, 4];
|
|
|
|
//! // Note: A `Rc<Vec<Value>>` must be used as the parameter.
|
|
|
|
//! let values = Rc::new(v.iter().copied().map(Value::from).collect::<Vec<Value>>());
|
|
|
|
//! let mut stmt = db.prepare("SELECT value from rarray(?);")?;
|
|
|
|
//! let rows = stmt.query_map(params![values], |row| row.get::<_, i64>(0))?;
|
|
|
|
//! for value in rows {
|
|
|
|
//! println!("{}", value?);
|
|
|
|
//! }
|
|
|
|
//! Ok(())
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
use std::default::Default;
|
2020-06-01 15:48:49 +08:00
|
|
|
use std::marker::PhantomData;
|
2018-06-10 18:16:54 +08:00
|
|
|
use std::os::raw::{c_char, c_int, c_void};
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
2018-10-31 03:11:35 +08:00
|
|
|
use crate::ffi;
|
|
|
|
use crate::types::{ToSql, ToSqlOutput, Value};
|
|
|
|
use crate::vtab::{
|
2020-04-14 22:09:50 +08:00
|
|
|
eponymous_only_module, Context, IndexConstraintOp, IndexInfo, VTab, VTabConnection, VTabCursor,
|
|
|
|
Values,
|
2018-07-15 01:21:03 +08:00
|
|
|
};
|
2018-10-31 03:11:35 +08:00
|
|
|
use crate::{Connection, Result};
|
2018-06-10 18:16:54 +08:00
|
|
|
|
|
|
|
// http://sqlite.org/bindptr.html
|
|
|
|
|
|
|
|
pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;
|
|
|
|
|
|
|
|
pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
|
|
|
|
let _: Array = Rc::from_raw(p as *const Vec<Value>);
|
|
|
|
}
|
|
|
|
|
2020-05-17 17:21:10 +08:00
|
|
|
/// Array parameter / pointer
|
2018-06-10 18:16:54 +08:00
|
|
|
pub type Array = Rc<Vec<Value>>;
|
|
|
|
|
|
|
|
impl ToSql for Array {
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2018-12-08 04:57:04 +08:00
|
|
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
2018-06-10 18:16:54 +08:00
|
|
|
Ok(ToSqlOutput::Array(self.clone()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-06 13:15:27 +08:00
|
|
|
/// `feature = "array"` Register the "rarray" module.
|
2018-06-10 18:16:54 +08:00
|
|
|
pub fn load_module(conn: &Connection) -> Result<()> {
|
|
|
|
let aux: Option<()> = None;
|
2020-04-14 22:09:50 +08:00
|
|
|
conn.create_module("rarray", eponymous_only_module::<ArrayTab>(), aux)
|
2018-06-21 02:01:38 +08:00
|
|
|
}
|
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
// Column numbers
|
|
|
|
// const CARRAY_COLUMN_VALUE : c_int = 0;
|
|
|
|
const CARRAY_COLUMN_POINTER: c_int = 1;
|
|
|
|
|
|
|
|
/// An instance of the Array virtual table
|
|
|
|
#[repr(C)]
|
|
|
|
struct ArrayTab {
|
|
|
|
/// Base class. Must be first
|
|
|
|
base: ffi::sqlite3_vtab,
|
|
|
|
}
|
|
|
|
|
2020-06-01 15:48:49 +08:00
|
|
|
unsafe impl<'vtab> VTab<'vtab> for ArrayTab {
|
2018-07-15 00:47:52 +08:00
|
|
|
type Aux = ();
|
2020-06-01 15:48:49 +08:00
|
|
|
type Cursor = ArrayTabCursor<'vtab>;
|
2018-06-10 18:16:54 +08:00
|
|
|
|
2018-07-15 00:47:52 +08:00
|
|
|
fn connect(
|
|
|
|
_: &mut VTabConnection,
|
|
|
|
_aux: Option<&()>,
|
|
|
|
_args: &[&[u8]],
|
|
|
|
) -> Result<(String, ArrayTab)> {
|
|
|
|
let vtab = ArrayTab {
|
|
|
|
base: ffi::sqlite3_vtab::default(),
|
|
|
|
};
|
|
|
|
Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
|
|
|
|
}
|
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
|
|
|
|
// Index of the pointer= constraint
|
|
|
|
let mut ptr_idx = None;
|
|
|
|
for (i, constraint) in info.constraints().enumerate() {
|
|
|
|
if !constraint.is_usable() {
|
|
|
|
continue;
|
|
|
|
}
|
2018-07-15 00:47:52 +08:00
|
|
|
if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
|
2018-06-10 18:16:54 +08:00
|
|
|
continue;
|
|
|
|
}
|
2018-06-21 01:12:02 +08:00
|
|
|
if let CARRAY_COLUMN_POINTER = constraint.column() {
|
|
|
|
ptr_idx = Some(i);
|
2018-06-10 18:16:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(ptr_idx) = ptr_idx {
|
|
|
|
{
|
|
|
|
let mut constraint_usage = info.constraint_usage(ptr_idx);
|
|
|
|
constraint_usage.set_argv_index(1);
|
|
|
|
constraint_usage.set_omit(true);
|
|
|
|
}
|
|
|
|
info.set_estimated_cost(1f64);
|
|
|
|
info.set_estimated_rows(100);
|
|
|
|
info.set_idx_num(1);
|
|
|
|
} else {
|
|
|
|
info.set_estimated_cost(2_147_483_647f64);
|
|
|
|
info.set_estimated_rows(2_147_483_647);
|
|
|
|
info.set_idx_num(0);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-06-02 00:42:30 +08:00
|
|
|
fn open(&self) -> Result<ArrayTabCursor<'_>> {
|
2018-06-10 18:16:54 +08:00
|
|
|
Ok(ArrayTabCursor::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A cursor for the Array virtual table
|
|
|
|
#[repr(C)]
|
2020-06-01 15:48:49 +08:00
|
|
|
struct ArrayTabCursor<'vtab> {
|
2018-06-10 18:16:54 +08:00
|
|
|
/// Base class. Must be first
|
|
|
|
base: ffi::sqlite3_vtab_cursor,
|
|
|
|
/// The rowid
|
|
|
|
row_id: i64,
|
|
|
|
/// Pointer to the array of values ("pointer")
|
|
|
|
ptr: Option<Array>,
|
2020-06-01 15:48:49 +08:00
|
|
|
phantom: PhantomData<&'vtab ArrayTab>,
|
2018-06-10 18:16:54 +08:00
|
|
|
}
|
|
|
|
|
2020-06-01 15:48:49 +08:00
|
|
|
impl ArrayTabCursor<'_> {
|
|
|
|
fn new<'vtab>() -> ArrayTabCursor<'vtab> {
|
2018-06-10 18:16:54 +08:00
|
|
|
ArrayTabCursor {
|
2018-06-29 03:07:05 +08:00
|
|
|
base: ffi::sqlite3_vtab_cursor::default(),
|
2018-06-10 18:16:54 +08:00
|
|
|
row_id: 0,
|
|
|
|
ptr: None,
|
2020-06-01 15:48:49 +08:00
|
|
|
phantom: PhantomData,
|
2018-06-10 18:16:54 +08:00
|
|
|
}
|
|
|
|
}
|
2018-08-17 00:29:46 +08:00
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
fn len(&self) -> i64 {
|
|
|
|
match self.ptr {
|
|
|
|
Some(ref a) => a.len() as i64,
|
|
|
|
_ => 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-01 15:48:49 +08:00
|
|
|
unsafe impl VTabCursor for ArrayTabCursor<'_> {
|
2018-12-08 04:57:04 +08:00
|
|
|
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
|
2018-06-10 18:16:54 +08:00
|
|
|
if idx_num > 0 {
|
2018-10-31 03:11:35 +08:00
|
|
|
self.ptr = args.get_array(0)?;
|
2018-06-10 18:16:54 +08:00
|
|
|
} else {
|
|
|
|
self.ptr = None;
|
|
|
|
}
|
|
|
|
self.row_id = 1;
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-08-17 00:29:46 +08:00
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
fn next(&mut self) -> Result<()> {
|
|
|
|
self.row_id += 1;
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-08-17 00:29:46 +08:00
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
fn eof(&self) -> bool {
|
|
|
|
self.row_id > self.len()
|
|
|
|
}
|
2018-08-17 00:29:46 +08:00
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
|
|
|
|
match i {
|
|
|
|
CARRAY_COLUMN_POINTER => Ok(()),
|
|
|
|
_ => {
|
|
|
|
if let Some(ref array) = self.ptr {
|
2018-06-12 03:30:55 +08:00
|
|
|
let value = &array[(self.row_id - 1) as usize];
|
2018-06-10 18:16:54 +08:00
|
|
|
ctx.set_result(&value)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-17 00:29:46 +08:00
|
|
|
|
2018-06-10 18:16:54 +08:00
|
|
|
fn rowid(&self) -> Result<i64> {
|
|
|
|
Ok(self.row_id)
|
|
|
|
}
|
|
|
|
}
|
2018-06-12 03:30:55 +08:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2018-10-31 03:11:35 +08:00
|
|
|
use crate::types::Value;
|
|
|
|
use crate::vtab::array;
|
2020-11-06 05:14:00 +08:00
|
|
|
use crate::{Connection, Result};
|
2018-10-31 03:13:41 +08:00
|
|
|
use std::rc::Rc;
|
2018-06-12 03:30:55 +08:00
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_array_module() -> Result<()> {
|
|
|
|
let db = Connection::open_in_memory()?;
|
|
|
|
array::load_module(&db)?;
|
2018-06-12 03:30:55 +08:00
|
|
|
|
|
|
|
let v = vec![1i64, 2, 3, 4];
|
2020-04-01 12:07:26 +08:00
|
|
|
let values: Vec<Value> = v.into_iter().map(Value::from).collect();
|
2018-06-12 03:30:55 +08:00
|
|
|
let ptr = Rc::new(values);
|
2018-08-05 22:55:27 +08:00
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut stmt = db.prepare("SELECT value from rarray(?);")?;
|
2018-08-05 22:55:27 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0))?;
|
2018-08-05 22:55:27 +08:00
|
|
|
assert_eq!(2, Rc::strong_count(&ptr));
|
|
|
|
let mut count = 0;
|
|
|
|
for (i, value) in rows.enumerate() {
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(i as i64, value? - 1);
|
2018-08-05 22:55:27 +08:00
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
assert_eq!(4, count);
|
2018-06-12 03:30:55 +08:00
|
|
|
}
|
2018-06-12 03:39:08 +08:00
|
|
|
assert_eq!(1, Rc::strong_count(&ptr));
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2018-06-12 03:30:55 +08:00
|
|
|
}
|
|
|
|
}
|