]> git.proxmox.com Git - rustc.git/blob - vendor/rusqlite/src/vtab/array.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / vendor / rusqlite / src / vtab / array.rs
1 //! 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(?1);")?;
21 //! let rows = stmt.query_map([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).cast::<c_char>();
45
46 pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
47 drop(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 {
54 #[inline]
55 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
56 Ok(ToSqlOutput::Array(self.clone()))
57 }
58 }
59
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)
64 }
65
66 // Column numbers
67 // const CARRAY_COLUMN_VALUE : c_int = 0;
68 const CARRAY_COLUMN_POINTER: c_int = 1;
69
70 /// An instance of the Array virtual table
71 #[repr(C)]
72 struct ArrayTab {
73 /// Base class. Must be first
74 base: ffi::sqlite3_vtab,
75 }
76
77 unsafe impl<'vtab> VTab<'vtab> for ArrayTab {
78 type Aux = ();
79 type Cursor = ArrayTabCursor<'vtab>;
80
81 fn connect(
82 _: &mut VTabConnection,
83 _aux: Option<&()>,
84 _args: &[&[u8]],
85 ) -> Result<(String, ArrayTab)> {
86 let vtab = ArrayTab {
87 base: ffi::sqlite3_vtab::default(),
88 };
89 Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
90 }
91
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() {
97 continue;
98 }
99 if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
100 continue;
101 }
102 if let CARRAY_COLUMN_POINTER = constraint.column() {
103 ptr_idx = true;
104 constraint_usage.set_argv_index(1);
105 constraint_usage.set_omit(true);
106 }
107 }
108 if ptr_idx {
109 info.set_estimated_cost(1_f64);
110 info.set_estimated_rows(100);
111 info.set_idx_num(1);
112 } else {
113 info.set_estimated_cost(2_147_483_647_f64);
114 info.set_estimated_rows(2_147_483_647);
115 info.set_idx_num(0);
116 }
117 Ok(())
118 }
119
120 fn open(&mut self) -> Result<ArrayTabCursor<'_>> {
121 Ok(ArrayTabCursor::new())
122 }
123 }
124
125 /// A cursor for the Array virtual table
126 #[repr(C)]
127 struct ArrayTabCursor<'vtab> {
128 /// Base class. Must be first
129 base: ffi::sqlite3_vtab_cursor,
130 /// The rowid
131 row_id: i64,
132 /// Pointer to the array of values ("pointer")
133 ptr: Option<Array>,
134 phantom: PhantomData<&'vtab ArrayTab>,
135 }
136
137 impl ArrayTabCursor<'_> {
138 fn new<'vtab>() -> ArrayTabCursor<'vtab> {
139 ArrayTabCursor {
140 base: ffi::sqlite3_vtab_cursor::default(),
141 row_id: 0,
142 ptr: None,
143 phantom: PhantomData,
144 }
145 }
146
147 fn len(&self) -> i64 {
148 match self.ptr {
149 Some(ref a) => a.len() as i64,
150 _ => 0,
151 }
152 }
153 }
154 unsafe impl VTabCursor for ArrayTabCursor<'_> {
155 fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
156 if idx_num > 0 {
157 self.ptr = args.get_array(0);
158 } else {
159 self.ptr = None;
160 }
161 self.row_id = 1;
162 Ok(())
163 }
164
165 fn next(&mut self) -> Result<()> {
166 self.row_id += 1;
167 Ok(())
168 }
169
170 fn eof(&self) -> bool {
171 self.row_id > self.len()
172 }
173
174 fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
175 match i {
176 CARRAY_COLUMN_POINTER => Ok(()),
177 _ => {
178 if let Some(ref array) = self.ptr {
179 let value = &array[(self.row_id - 1) as usize];
180 ctx.set_result(&value)
181 } else {
182 Ok(())
183 }
184 }
185 }
186 }
187
188 fn rowid(&self) -> Result<i64> {
189 Ok(self.row_id)
190 }
191 }
192
193 #[cfg(test)]
194 mod test {
195 use crate::types::Value;
196 use crate::vtab::array;
197 use crate::{Connection, Result};
198 use std::rc::Rc;
199
200 #[test]
201 fn test_array_module() -> Result<()> {
202 let db = Connection::open_in_memory()?;
203 array::load_module(&db)?;
204
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);
208 {
209 let mut stmt = db.prepare("SELECT value from rarray(?1);")?;
210
211 let rows = stmt.query_map([&ptr], |row| row.get::<_, i64>(0))?;
212 assert_eq!(2, Rc::strong_count(&ptr));
213 let mut count = 0;
214 for (i, value) in rows.enumerate() {
215 assert_eq!(i as i64, value? - 1);
216 count += 1;
217 }
218 assert_eq!(4, count);
219 }
220 assert_eq!(1, Rc::strong_count(&ptr));
221 Ok(())
222 }
223 }