1 //! `feature = "array"` Array Virtual Table.
2 //!
3 //! Note: `rarray`, not `carray` is the name of the table valued function we
4 //! define.
5 //!
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
8 //!
9 //! # Example
10 //!
11 //! ```rust,no_run
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(?);")?;
21 //! let rows = stmt.query_map(params![values], |row| row.get::<_, i64>(0))?;
22 //! for value in rows {
23 //! println!("{}", value?);
24 //! }
25 //! Ok(())
26 //! }
27 //! ```
28
29 use std::default::Default;
30 use std::marker::PhantomData;
31 use std::os::raw::{c_char, c_int, c_void};
32 use std::rc::Rc;
33
34 use crate::ffi;
35 use crate::types::{ToSql, ToSqlOutput, Value};
36 use crate::vtab::{
37 eponymous_only_module, Context, IndexConstraintOp, IndexInfo, VTab, VTabConnection, VTabCursor,
38 Values,
39 };
40 use crate::{Connection, Result};
41
42 // http://sqlite.org/bindptr.html
43
44 pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;
45
free_array(p: *mut c_void)46 pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
47 let _: Array = Rc::from_raw(p as *const Vec<Value>);
48 }
49
50 /// Array parameter / pointer
51 pub type Array = Rc<Vec<Value>>;
52
53 impl ToSql for Array {
to_sql(&self) -> Result<ToSqlOutput<'_>>54 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
55 Ok(ToSqlOutput::Array(self.clone()))
56 }
57 }
58
59 /// `feature = "array"` Register the "rarray" module.
load_module(conn: &Connection) -> Result<()>60 pub fn load_module(conn: &Connection) -> Result<()> {
61 let aux: Option<()> = None;
62 conn.create_module("rarray", eponymous_only_module::<ArrayTab>(), aux)
63 }
64
65 // Column numbers
66 // const CARRAY_COLUMN_VALUE : c_int = 0;
67 const CARRAY_COLUMN_POINTER: c_int = 1;
68
69 /// An instance of the Array virtual table
70 #[repr(C)]
71 struct ArrayTab {
72 /// Base class. Must be first
73 base: ffi::sqlite3_vtab,
74 }
75
76 unsafe impl<'vtab> VTab<'vtab> for ArrayTab {
77 type Aux = ();
78 type Cursor = ArrayTabCursor<'vtab>;
79
connect( _: &mut VTabConnection, _aux: Option<&()>, _args: &[&[u8]], ) -> Result<(String, ArrayTab)>80 fn connect(
81 _: &mut VTabConnection,
82 _aux: Option<&()>,
83 _args: &[&[u8]],
84 ) -> Result<(String, ArrayTab)> {
85 let vtab = ArrayTab {
86 base: ffi::sqlite3_vtab::default(),
87 };
88 Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
89 }
90
best_index(&self, info: &mut IndexInfo) -> Result<()>91 fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
92 // Index of the pointer= constraint
93 let mut ptr_idx = None;
94 for (i, constraint) in info.constraints().enumerate() {
95 if !constraint.is_usable() {
96 continue;
97 }
98 if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
99 continue;
100 }
101 if let CARRAY_COLUMN_POINTER = constraint.column() {
102 ptr_idx = Some(i);
103 }
104 }
105 if let Some(ptr_idx) = ptr_idx {
106 {
107 let mut constraint_usage = info.constraint_usage(ptr_idx);
108 constraint_usage.set_argv_index(1);
109 constraint_usage.set_omit(true);
110 }
111 info.set_estimated_cost(1f64);
112 info.set_estimated_rows(100);
113 info.set_idx_num(1);
114 } else {
115 info.set_estimated_cost(2_147_483_647f64);
116 info.set_estimated_rows(2_147_483_647);
117 info.set_idx_num(0);
118 }
119 Ok(())
120 }
121
open(&self) -> Result<ArrayTabCursor<'_>>122 fn open(&self) -> Result<ArrayTabCursor<'_>> {
123 Ok(ArrayTabCursor::new())
124 }
125 }
126
127 /// A cursor for the Array virtual table
128 #[repr(C)]
129 struct ArrayTabCursor<'vtab> {
130 /// Base class. Must be first
131 base: ffi::sqlite3_vtab_cursor,
132 /// The rowid
133 row_id: i64,
134 /// Pointer to the array of values ("pointer")
135 ptr: Option<Array>,
136 phantom: PhantomData<&'vtab ArrayTab>,
137 }
138
139 impl ArrayTabCursor<'_> {
new<'vtab>() -> ArrayTabCursor<'vtab>140 fn new<'vtab>() -> ArrayTabCursor<'vtab> {
141 ArrayTabCursor {
142 base: ffi::sqlite3_vtab_cursor::default(),
143 row_id: 0,
144 ptr: None,
145 phantom: PhantomData,
146 }
147 }
148
len(&self) -> i64149 fn len(&self) -> i64 {
150 match self.ptr {
151 Some(ref a) => a.len() as i64,
152 _ => 0,
153 }
154 }
155 }
156 unsafe impl VTabCursor for ArrayTabCursor<'_> {
filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()>157 fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
158 if idx_num > 0 {
159 self.ptr = args.get_array(0)?;
160 } else {
161 self.ptr = None;
162 }
163 self.row_id = 1;
164 Ok(())
165 }
166
next(&mut self) -> Result<()>167 fn next(&mut self) -> Result<()> {
168 self.row_id += 1;
169 Ok(())
170 }
171
eof(&self) -> bool172 fn eof(&self) -> bool {
173 self.row_id > self.len()
174 }
175
column(&self, ctx: &mut Context, i: c_int) -> Result<()>176 fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
177 match i {
178 CARRAY_COLUMN_POINTER => Ok(()),
179 _ => {
180 if let Some(ref array) = self.ptr {
181 let value = &array[(self.row_id - 1) as usize];
182 ctx.set_result(&value)
183 } else {
184 Ok(())
185 }
186 }
187 }
188 }
189
rowid(&self) -> Result<i64>190 fn rowid(&self) -> Result<i64> {
191 Ok(self.row_id)
192 }
193 }
194
195 #[cfg(test)]
196 mod test {
197 use crate::types::Value;
198 use crate::vtab::array;
199 use crate::Connection;
200 use std::rc::Rc;
201
202 #[test]
test_array_module()203 fn test_array_module() {
204 let db = Connection::open_in_memory().unwrap();
205 array::load_module(&db).unwrap();
206
207 let v = vec![1i64, 2, 3, 4];
208 let values: Vec<Value> = v.into_iter().map(Value::from).collect();
209 let ptr = Rc::new(values);
210 {
211 let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap();
212
213 let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0)).unwrap();
214 assert_eq!(2, Rc::strong_count(&ptr));
215 let mut count = 0;
216 for (i, value) in rows.enumerate() {
217 assert_eq!(i as i64, value.unwrap() - 1);
218 count += 1;
219 }
220 assert_eq!(4, count);
221 }
222 assert_eq!(1, Rc::strong_count(&ptr));
223 }
224 }
225