1use std::default::Default;
30use std::marker::PhantomData;
31use std::os::raw::{c_char, c_int, c_void};
32use std::rc::Rc;
33
34use crate::ffi;
35use crate::types::{ToSql, ToSqlOutput, Value};
36use crate::vtab::{
37 eponymous_only_module, Context, IndexConstraintOp, IndexInfo, VTab, VTabConnection, VTabCursor,
38 Values,
39};
40use crate::{Connection, Result};
41
42pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;
45
46pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
47 let _: Array = Rc::from_raw(p as *const Vec<Value>);
48}
49
50pub type Array = Rc<Vec<Value>>;
52
53impl ToSql for Array {
54 #[inline]
55 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
56 Ok(ToSqlOutput::Array(self.clone()))
57 }
58}
59
60pub fn load_module(conn: &Connection) -> Result<()> {
62 let aux: Option<()> = None;
63 conn.create_module("rarray", eponymous_only_module::<ArrayTab>(), aux)
64}
65
66const CARRAY_COLUMN_POINTER: c_int = 1;
69
70#[repr(C)]
72struct ArrayTab {
73 base: ffi::sqlite3_vtab,
75}
76
77unsafe 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 let mut ptr_idx = None;
95 for (i, constraint) in info.constraints().enumerate() {
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 = Some(i);
104 }
105 }
106 if let Some(ptr_idx) = ptr_idx {
107 {
108 let mut constraint_usage = info.constraint_usage(ptr_idx);
109 constraint_usage.set_argv_index(1);
110 constraint_usage.set_omit(true);
111 }
112 info.set_estimated_cost(1f64);
113 info.set_estimated_rows(100);
114 info.set_idx_num(1);
115 } else {
116 info.set_estimated_cost(2_147_483_647f64);
117 info.set_estimated_rows(2_147_483_647);
118 info.set_idx_num(0);
119 }
120 Ok(())
121 }
122
123 fn open(&self) -> Result<ArrayTabCursor<'_>> {
124 Ok(ArrayTabCursor::new())
125 }
126}
127
128#[repr(C)]
130struct ArrayTabCursor<'vtab> {
131 base: ffi::sqlite3_vtab_cursor,
133 row_id: i64,
135 ptr: Option<Array>,
137 phantom: PhantomData<&'vtab ArrayTab>,
138}
139
140impl ArrayTabCursor<'_> {
141 fn new<'vtab>() -> ArrayTabCursor<'vtab> {
142 ArrayTabCursor {
143 base: ffi::sqlite3_vtab_cursor::default(),
144 row_id: 0,
145 ptr: None,
146 phantom: PhantomData,
147 }
148 }
149
150 fn len(&self) -> i64 {
151 match self.ptr {
152 Some(ref a) => a.len() as i64,
153 _ => 0,
154 }
155 }
156}
157unsafe impl VTabCursor for ArrayTabCursor<'_> {
158 fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
159 if idx_num > 0 {
160 self.ptr = args.get_array(0)?;
161 } else {
162 self.ptr = None;
163 }
164 self.row_id = 1;
165 Ok(())
166 }
167
168 fn next(&mut self) -> Result<()> {
169 self.row_id += 1;
170 Ok(())
171 }
172
173 fn eof(&self) -> bool {
174 self.row_id > self.len()
175 }
176
177 fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
178 match i {
179 CARRAY_COLUMN_POINTER => Ok(()),
180 _ => {
181 if let Some(ref array) = self.ptr {
182 let value = &array[(self.row_id - 1) as usize];
183 ctx.set_result(&value)
184 } else {
185 Ok(())
186 }
187 }
188 }
189 }
190
191 fn rowid(&self) -> Result<i64> {
192 Ok(self.row_id)
193 }
194}
195
196#[cfg(test)]
197mod test {
198 use crate::types::Value;
199 use crate::vtab::array;
200 use crate::{Connection, Result};
201 use std::rc::Rc;
202
203 #[test]
204 fn test_array_module() -> Result<()> {
205 let db = Connection::open_in_memory()?;
206 array::load_module(&db)?;
207
208 let v = vec![1i64, 2, 3, 4];
209 let values: Vec<Value> = v.into_iter().map(Value::from).collect();
210 let ptr = Rc::new(values);
211 {
212 let mut stmt = db.prepare("SELECT value from rarray(?);")?;
213
214 let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0))?;
215 assert_eq!(2, Rc::strong_count(&ptr));
216 let mut count = 0;
217 for (i, value) in rows.enumerate() {
218 assert_eq!(i as i64, value? - 1);
219 count += 1;
220 }
221 assert_eq!(4, count);
222 }
223 assert_eq!(1, Rc::strong_count(&ptr));
224 Ok(())
225 }
226}