1 //! Array Virtual Table.
3 //! Note: `rarray`, not `carray` is the name of the table valued function we
6 //! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c)
7 //! C extension: `https://www.sqlite.org/carray.html`
12 //! # use rusqlite::{types::Value, Connection, Result, params};
13 //! # use std::rc::Rc;
14 //! fn example(db: &Connection) -> Result<()> {
15 //! // Note: This should be done once (usually when opening the DB).
16 //! rusqlite::vtab::array::load_module(&db)?;
17 //! let v = [1i64, 2, 3, 4];
18 //! // Note: A `Rc<Vec<Value>>` must be used as the parameter.
19 //! let values = Rc::new(v.iter().copied().map(Value::from).collect::<Vec<Value>>());
20 //! let mut stmt = db.prepare("SELECT value from rarray(?1);")?;
21 //! let rows = stmt.query_map([values], |row| row.get::<_, i64>(0))?;
22 //! for value in rows {
23 //! println!("{}", value?);
29 use std
::default::Default
;
30 use std
::marker
::PhantomData
;
31 use std
::os
::raw
::{c_char, c_int, c_void}
;
35 use crate::types
::{ToSql, ToSqlOutput, Value}
;
37 eponymous_only_module
, Context
, IndexConstraintOp
, IndexInfo
, VTab
, VTabConnection
, VTabCursor
,
40 use crate::{Connection, Result}
;
42 // http://sqlite.org/bindptr.html
44 pub(crate) const ARRAY_TYPE
: *const c_char
= (b
"rarray\0" as *const u8).cast
::<c_char
>();
46 pub(crate) unsafe extern "C" fn free_array(p
: *mut c_void
) {
47 drop(Rc
::from_raw(p
as *const Vec
<Value
>));
50 /// Array parameter / pointer
51 pub type Array
= Rc
<Vec
<Value
>>;
53 impl ToSql
for Array
{
55 fn to_sql(&self) -> Result
<ToSqlOutput
<'_
>> {
56 Ok(ToSqlOutput
::Array(self.clone()))
60 /// Register the "rarray" module.
61 pub fn load_module(conn
: &Connection
) -> Result
<()> {
62 let aux
: Option
<()> = None
;
63 conn
.create_module("rarray", eponymous_only_module
::<ArrayTab
>(), aux
)
67 // const CARRAY_COLUMN_VALUE : c_int = 0;
68 const CARRAY_COLUMN_POINTER
: c_int
= 1;
70 /// An instance of the Array virtual table
73 /// Base class. Must be first
74 base
: ffi
::sqlite3_vtab
,
77 unsafe impl<'vtab
> VTab
<'vtab
> for ArrayTab
{
79 type Cursor
= ArrayTabCursor
<'vtab
>;
82 _
: &mut VTabConnection
,
85 ) -> Result
<(String
, ArrayTab
)> {
87 base
: ffi
::sqlite3_vtab
::default(),
89 Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab
))
92 fn best_index(&self, info
: &mut IndexInfo
) -> Result
<()> {
93 // Index of the pointer= constraint
94 let mut ptr_idx
= false;
95 for (constraint
, mut constraint_usage
) in info
.constraints_and_usages() {
96 if !constraint
.is_usable() {
99 if constraint
.operator() != IndexConstraintOp
::SQLITE_INDEX_CONSTRAINT_EQ
{
102 if let CARRAY_COLUMN_POINTER
= constraint
.column() {
104 constraint_usage
.set_argv_index(1);
105 constraint_usage
.set_omit(true);
109 info
.set_estimated_cost(1_f64);
110 info
.set_estimated_rows(100);
113 info
.set_estimated_cost(2_147_483_647_f64);
114 info
.set_estimated_rows(2_147_483_647);
120 fn open(&mut self) -> Result
<ArrayTabCursor
<'_
>> {
121 Ok(ArrayTabCursor
::new())
125 /// A cursor for the Array virtual table
127 struct ArrayTabCursor
<'vtab
> {
128 /// Base class. Must be first
129 base
: ffi
::sqlite3_vtab_cursor
,
132 /// Pointer to the array of values ("pointer")
134 phantom
: PhantomData
<&'vtab ArrayTab
>,
137 impl ArrayTabCursor
<'_
> {
138 fn new
<'vtab
>() -> ArrayTabCursor
<'vtab
> {
140 base
: ffi
::sqlite3_vtab_cursor
::default(),
143 phantom
: PhantomData
,
147 fn len(&self) -> i64 {
149 Some(ref a
) => a
.len() as i64,
154 unsafe impl VTabCursor
for ArrayTabCursor
<'_
> {
155 fn filter(&mut self, idx_num
: c_int
, _idx_str
: Option
<&str>, args
: &Values
<'_
>) -> Result
<()> {
157 self.ptr
= args
.get_array(0);
165 fn next(&mut self) -> Result
<()> {
170 fn eof(&self) -> bool
{
171 self.row_id
> self.len()
174 fn column(&self, ctx
: &mut Context
, i
: c_int
) -> Result
<()> {
176 CARRAY_COLUMN_POINTER
=> Ok(()),
178 if let Some(ref array
) = self.ptr
{
179 let value
= &array
[(self.row_id
- 1) as usize];
180 ctx
.set_result(&value
)
188 fn rowid(&self) -> Result
<i64> {
195 use crate::types
::Value
;
196 use crate::vtab
::array
;
197 use crate::{Connection, Result}
;
201 fn test_array_module() -> Result
<()> {
202 let db
= Connection
::open_in_memory()?
;
203 array
::load_module(&db
)?
;
205 let v
= vec
![1i64, 2, 3, 4];
206 let values
: Vec
<Value
> = v
.into_iter().map(Value
::from
).collect();
207 let ptr
= Rc
::new(values
);
209 let mut stmt
= db
.prepare("SELECT value from rarray(?1);")?
;
211 let rows
= stmt
.query_map([&ptr
], |row
| row
.get
::<_
, i64>(0))?
;
212 assert_eq
!(2, Rc
::strong_count(&ptr
));
214 for (i
, value
) in rows
.enumerate() {
215 assert_eq
!(i
as i64, value?
- 1);
218 assert_eq
!(4, count
);
220 assert_eq
!(1, Rc
::strong_count(&ptr
));