1 //! `feature = "series"` Generate series virtual table.
2 //!
3 //! Port of C [generate series
4 //! "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c):
5 //! https://www.sqlite.org/series.html
6 use std::default::Default;
7 use std::os::raw::c_int;
8 
9 use crate::ffi;
10 use crate::types::Type;
11 use crate::vtab::{
12     eponymous_only_module, Context, IndexConstraintOp, IndexInfo, VTab, VTabConnection, VTabCursor,
13     Values,
14 };
15 use crate::{Connection, Result};
16 
17 /// `feature = "series"` Register the "generate_series" module.
load_module(conn: &Connection) -> Result<()>18 pub fn load_module(conn: &Connection) -> Result<()> {
19     let aux: Option<()> = None;
20     conn.create_module("generate_series", eponymous_only_module::<SeriesTab>(), aux)
21 }
22 
23 // Column numbers
24 // const SERIES_COLUMN_VALUE : c_int = 0;
25 const SERIES_COLUMN_START: c_int = 1;
26 const SERIES_COLUMN_STOP: c_int = 2;
27 const SERIES_COLUMN_STEP: c_int = 3;
28 
29 bitflags::bitflags! {
30     #[repr(C)]
31     struct QueryPlanFlags: ::std::os::raw::c_int {
32         // start = $value  -- constraint exists
33         const START = 1;
34         // stop = $value   -- constraint exists
35         const STOP  = 2;
36         // step = $value   -- constraint exists
37         const STEP  = 4;
38         // output in descending order
39         const DESC  = 8;
40         // Both start and stop
41         const BOTH  = QueryPlanFlags::START.bits | QueryPlanFlags::STOP.bits;
42     }
43 }
44 
45 /// An instance of the Series virtual table
46 #[repr(C)]
47 struct SeriesTab {
48     /// Base class. Must be first
49     base: ffi::sqlite3_vtab,
50 }
51 
52 unsafe impl VTab for SeriesTab {
53     type Aux = ();
54     type Cursor = SeriesTabCursor;
55 
connect( _: &mut VTabConnection, _aux: Option<&()>, _args: &[&[u8]], ) -> Result<(String, SeriesTab)>56     fn connect(
57         _: &mut VTabConnection,
58         _aux: Option<&()>,
59         _args: &[&[u8]],
60     ) -> Result<(String, SeriesTab)> {
61         let vtab = SeriesTab {
62             base: ffi::sqlite3_vtab::default(),
63         };
64         Ok((
65             "CREATE TABLE x(value,start hidden,stop hidden,step hidden)".to_owned(),
66             vtab,
67         ))
68     }
69 
best_index(&self, info: &mut IndexInfo) -> Result<()>70     fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
71         // The query plan bitmask
72         let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();
73         // Index of the start= constraint
74         let mut start_idx = None;
75         // Index of the stop= constraint
76         let mut stop_idx = None;
77         // Index of the step= constraint
78         let mut step_idx = None;
79         for (i, constraint) in info.constraints().enumerate() {
80             if !constraint.is_usable() {
81                 continue;
82             }
83             if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
84                 continue;
85             }
86             match constraint.column() {
87                 SERIES_COLUMN_START => {
88                     start_idx = Some(i);
89                     idx_num |= QueryPlanFlags::START;
90                 }
91                 SERIES_COLUMN_STOP => {
92                     stop_idx = Some(i);
93                     idx_num |= QueryPlanFlags::STOP;
94                 }
95                 SERIES_COLUMN_STEP => {
96                     step_idx = Some(i);
97                     idx_num |= QueryPlanFlags::STEP;
98                 }
99                 _ => {}
100             };
101         }
102 
103         let mut num_of_arg = 0;
104         if let Some(start_idx) = start_idx {
105             num_of_arg += 1;
106             let mut constraint_usage = info.constraint_usage(start_idx);
107             constraint_usage.set_argv_index(num_of_arg);
108             constraint_usage.set_omit(true);
109         }
110         if let Some(stop_idx) = stop_idx {
111             num_of_arg += 1;
112             let mut constraint_usage = info.constraint_usage(stop_idx);
113             constraint_usage.set_argv_index(num_of_arg);
114             constraint_usage.set_omit(true);
115         }
116         if let Some(step_idx) = step_idx {
117             num_of_arg += 1;
118             let mut constraint_usage = info.constraint_usage(step_idx);
119             constraint_usage.set_argv_index(num_of_arg);
120             constraint_usage.set_omit(true);
121         }
122         if idx_num.contains(QueryPlanFlags::BOTH) {
123             // Both start= and stop= boundaries are available.
124             info.set_estimated_cost(f64::from(
125                 2 - if idx_num.contains(QueryPlanFlags::STEP) {
126                     1
127                 } else {
128                     0
129                 },
130             ));
131             info.set_estimated_rows(1000);
132             let order_by_consumed = {
133                 let mut order_bys = info.order_bys();
134                 if let Some(order_by) = order_bys.next() {
135                     if order_by.is_order_by_desc() {
136                         idx_num |= QueryPlanFlags::DESC;
137                     }
138                     true
139                 } else {
140                     false
141                 }
142             };
143             if order_by_consumed {
144                 info.set_order_by_consumed(true);
145             }
146         } else {
147             info.set_estimated_cost(2_147_483_647f64);
148             info.set_estimated_rows(2_147_483_647);
149         }
150         info.set_idx_num(idx_num.bits());
151         Ok(())
152     }
153 
open(&self) -> Result<SeriesTabCursor>154     fn open(&self) -> Result<SeriesTabCursor> {
155         Ok(SeriesTabCursor::new())
156     }
157 }
158 
159 /// A cursor for the Series virtual table
160 #[derive(Default)]
161 #[repr(C)]
162 struct SeriesTabCursor {
163     /// Base class. Must be first
164     base: ffi::sqlite3_vtab_cursor,
165     /// True to count down rather than up
166     is_desc: bool,
167     /// The rowid
168     row_id: i64,
169     /// Current value ("value")
170     value: i64,
171     /// Mimimum value ("start")
172     min_value: i64,
173     /// Maximum value ("stop")
174     max_value: i64,
175     /// Increment ("step")
176     step: i64,
177 }
178 
179 impl SeriesTabCursor {
new() -> SeriesTabCursor180     fn new() -> SeriesTabCursor {
181         SeriesTabCursor::default()
182     }
183 }
184 unsafe impl VTabCursor for SeriesTabCursor {
filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()>185     fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
186         let idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
187         let mut i = 0;
188         if idx_num.contains(QueryPlanFlags::START) {
189             self.min_value = args.get(i)?;
190             i += 1;
191         } else {
192             self.min_value = 0;
193         }
194         if idx_num.contains(QueryPlanFlags::STOP) {
195             self.max_value = args.get(i)?;
196             i += 1;
197         } else {
198             self.max_value = 0xffff_ffff;
199         }
200         if idx_num.contains(QueryPlanFlags::STEP) {
201             self.step = args.get(i)?;
202             if self.step < 1 {
203                 self.step = 1;
204             }
205         } else {
206             self.step = 1;
207         };
208         for arg in args.iter() {
209             if arg.data_type() == Type::Null {
210                 // If any of the constraints have a NULL value, then return no rows.
211                 self.min_value = 1;
212                 self.max_value = 0;
213                 break;
214             }
215         }
216         self.is_desc = idx_num.contains(QueryPlanFlags::DESC);
217         if self.is_desc {
218             self.value = self.max_value;
219             if self.step > 0 {
220                 self.value -= (self.max_value - self.min_value) % self.step;
221             }
222         } else {
223             self.value = self.min_value;
224         }
225         self.row_id = 1;
226         Ok(())
227     }
228 
next(&mut self) -> Result<()>229     fn next(&mut self) -> Result<()> {
230         if self.is_desc {
231             self.value -= self.step;
232         } else {
233             self.value += self.step;
234         }
235         self.row_id += 1;
236         Ok(())
237     }
238 
eof(&self) -> bool239     fn eof(&self) -> bool {
240         if self.is_desc {
241             self.value < self.min_value
242         } else {
243             self.value > self.max_value
244         }
245     }
246 
column(&self, ctx: &mut Context, i: c_int) -> Result<()>247     fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
248         let x = match i {
249             SERIES_COLUMN_START => self.min_value,
250             SERIES_COLUMN_STOP => self.max_value,
251             SERIES_COLUMN_STEP => self.step,
252             _ => self.value,
253         };
254         ctx.set_result(&x)
255     }
256 
rowid(&self) -> Result<i64>257     fn rowid(&self) -> Result<i64> {
258         Ok(self.row_id)
259     }
260 }
261 
262 #[cfg(test)]
263 mod test {
264     use crate::ffi;
265     use crate::vtab::series;
266     use crate::{Connection, NO_PARAMS};
267 
268     #[test]
test_series_module()269     fn test_series_module() {
270         let version = unsafe { ffi::sqlite3_libversion_number() };
271         if version < 3_008_012 {
272             return;
273         }
274 
275         let db = Connection::open_in_memory().unwrap();
276         series::load_module(&db).unwrap();
277 
278         let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)").unwrap();
279 
280         let series = s.query_map(NO_PARAMS, |row| row.get::<_, i32>(0)).unwrap();
281 
282         let mut expected = 0;
283         for value in series {
284             assert_eq!(expected, value.unwrap());
285             expected += 5;
286         }
287     }
288 }
289