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