• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! 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, Error, Result};
17 
18 /// 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         // output in ascending order
42         const ASC  = 16;
43         // Both start and stop
44         const BOTH  = QueryPlanFlags::START.bits | QueryPlanFlags::STOP.bits;
45     }
46 }
47 
48 /// An instance of the Series virtual table
49 #[repr(C)]
50 struct SeriesTab {
51     /// Base class. Must be first
52     base: ffi::sqlite3_vtab,
53 }
54 
55 unsafe impl<'vtab> VTab<'vtab> for SeriesTab {
56     type Aux = ();
57     type Cursor = SeriesTabCursor<'vtab>;
58 
connect( _: &mut VTabConnection, _aux: Option<&()>, _args: &[&[u8]], ) -> Result<(String, SeriesTab)>59     fn connect(
60         _: &mut VTabConnection,
61         _aux: Option<&()>,
62         _args: &[&[u8]],
63     ) -> Result<(String, SeriesTab)> {
64         let vtab = SeriesTab {
65             base: ffi::sqlite3_vtab::default(),
66         };
67         Ok((
68             "CREATE TABLE x(value,start hidden,stop hidden,step hidden)".to_owned(),
69             vtab,
70         ))
71     }
72 
best_index(&self, info: &mut IndexInfo) -> Result<()>73     fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
74         // The query plan bitmask
75         let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();
76         // Mask of unusable constraints
77         let mut unusable_mask: QueryPlanFlags = QueryPlanFlags::empty();
78         // Constraints on start, stop, and step
79         let mut a_idx: [Option<usize>; 3] = [None, None, None];
80         for (i, constraint) in info.constraints().enumerate() {
81             if constraint.column() < SERIES_COLUMN_START {
82                 continue;
83             }
84             let (i_col, i_mask) = match constraint.column() {
85                 SERIES_COLUMN_START => (0, QueryPlanFlags::START),
86                 SERIES_COLUMN_STOP => (1, QueryPlanFlags::STOP),
87                 SERIES_COLUMN_STEP => (2, QueryPlanFlags::STEP),
88                 _ => {
89                     unreachable!()
90                 }
91             };
92             if !constraint.is_usable() {
93                 unusable_mask |= i_mask;
94             } else if constraint.operator() == IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
95                 idx_num |= i_mask;
96                 a_idx[i_col] = Some(i);
97             }
98         }
99         // Number of arguments that SeriesTabCursor::filter expects
100         let mut n_arg = 0;
101         for j in a_idx.iter().flatten() {
102             n_arg += 1;
103             let mut constraint_usage = info.constraint_usage(*j);
104             constraint_usage.set_argv_index(n_arg);
105             constraint_usage.set_omit(true);
106         }
107         if !(unusable_mask & !idx_num).is_empty() {
108             return Err(Error::SqliteFailure(
109                 ffi::Error::new(ffi::SQLITE_CONSTRAINT),
110                 None,
111             ));
112         }
113         if idx_num.contains(QueryPlanFlags::BOTH) {
114             // Both start= and stop= boundaries are available.
115             info.set_estimated_cost(f64::from(
116                 2 - if idx_num.contains(QueryPlanFlags::STEP) {
117                     1
118                 } else {
119                     0
120                 },
121             ));
122             info.set_estimated_rows(1000);
123             let order_by_consumed = {
124                 let mut order_bys = info.order_bys();
125                 if let Some(order_by) = order_bys.next() {
126                     if order_by.column() == 0 {
127                         if order_by.is_order_by_desc() {
128                             idx_num |= QueryPlanFlags::DESC;
129                         } else {
130                             idx_num |= QueryPlanFlags::ASC;
131                         }
132                         true
133                     } else {
134                         false
135                     }
136                 } else {
137                     false
138                 }
139             };
140             if order_by_consumed {
141                 info.set_order_by_consumed(true);
142             }
143         } else {
144             // If either boundary is missing, we have to generate a huge span
145             // of numbers.  Make this case very expensive so that the query
146             // planner will work hard to avoid it.
147             info.set_estimated_rows(2_147_483_647);
148         }
149         info.set_idx_num(idx_num.bits());
150         Ok(())
151     }
152 
open(&self) -> Result<SeriesTabCursor<'_>>153     fn open(&self) -> Result<SeriesTabCursor<'_>> {
154         Ok(SeriesTabCursor::new())
155     }
156 }
157 
158 /// A cursor for the Series virtual table
159 #[repr(C)]
160 struct SeriesTabCursor<'vtab> {
161     /// Base class. Must be first
162     base: ffi::sqlite3_vtab_cursor,
163     /// True to count down rather than up
164     is_desc: bool,
165     /// The rowid
166     row_id: i64,
167     /// Current value ("value")
168     value: i64,
169     /// Minimum value ("start")
170     min_value: i64,
171     /// Maximum value ("stop")
172     max_value: i64,
173     /// Increment ("step")
174     step: i64,
175     phantom: PhantomData<&'vtab SeriesTab>,
176 }
177 
178 impl SeriesTabCursor<'_> {
new<'vtab>() -> SeriesTabCursor<'vtab>179     fn new<'vtab>() -> SeriesTabCursor<'vtab> {
180         SeriesTabCursor {
181             base: ffi::sqlite3_vtab_cursor::default(),
182             is_desc: false,
183             row_id: 0,
184             value: 0,
185             min_value: 0,
186             max_value: 0,
187             step: 0,
188             phantom: PhantomData,
189         }
190     }
191 }
192 #[allow(clippy::comparison_chain)]
193 unsafe impl VTabCursor for SeriesTabCursor<'_> {
filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()>194     fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
195         let mut idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
196         let mut i = 0;
197         if idx_num.contains(QueryPlanFlags::START) {
198             self.min_value = args.get(i)?;
199             i += 1;
200         } else {
201             self.min_value = 0;
202         }
203         if idx_num.contains(QueryPlanFlags::STOP) {
204             self.max_value = args.get(i)?;
205             i += 1;
206         } else {
207             self.max_value = 0xffff_ffff;
208         }
209         if idx_num.contains(QueryPlanFlags::STEP) {
210             self.step = args.get(i)?;
211             if self.step == 0 {
212                 self.step = 1;
213             } else if self.step < 0 {
214                 self.step = -self.step;
215                 if !idx_num.contains(QueryPlanFlags::ASC) {
216                     idx_num |= QueryPlanFlags::DESC;
217                 }
218             }
219         } else {
220             self.step = 1;
221         };
222         for arg in args.iter() {
223             if arg.data_type() == Type::Null {
224                 // If any of the constraints have a NULL value, then return no rows.
225                 self.min_value = 1;
226                 self.max_value = 0;
227                 break;
228             }
229         }
230         self.is_desc = idx_num.contains(QueryPlanFlags::DESC);
231         if self.is_desc {
232             self.value = self.max_value;
233             if self.step > 0 {
234                 self.value -= (self.max_value - self.min_value) % self.step;
235             }
236         } else {
237             self.value = self.min_value;
238         }
239         self.row_id = 1;
240         Ok(())
241     }
242 
next(&mut self) -> Result<()>243     fn next(&mut self) -> Result<()> {
244         if self.is_desc {
245             self.value -= self.step;
246         } else {
247             self.value += self.step;
248         }
249         self.row_id += 1;
250         Ok(())
251     }
252 
eof(&self) -> bool253     fn eof(&self) -> bool {
254         if self.is_desc {
255             self.value < self.min_value
256         } else {
257             self.value > self.max_value
258         }
259     }
260 
column(&self, ctx: &mut Context, i: c_int) -> Result<()>261     fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
262         let x = match i {
263             SERIES_COLUMN_START => self.min_value,
264             SERIES_COLUMN_STOP => self.max_value,
265             SERIES_COLUMN_STEP => self.step,
266             _ => self.value,
267         };
268         ctx.set_result(&x)
269     }
270 
rowid(&self) -> Result<i64>271     fn rowid(&self) -> Result<i64> {
272         Ok(self.row_id)
273     }
274 }
275 
276 #[cfg(test)]
277 mod test {
278     use crate::ffi;
279     use crate::vtab::series;
280     use crate::{Connection, Result};
281     use fallible_iterator::FallibleIterator;
282 
283     #[test]
test_series_module() -> Result<()>284     fn test_series_module() -> Result<()> {
285         let version = unsafe { ffi::sqlite3_libversion_number() };
286         if version < 3_008_012 {
287             return Ok(());
288         }
289 
290         let db = Connection::open_in_memory()?;
291         series::load_module(&db)?;
292 
293         let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)")?;
294 
295         let series = s.query_map([], |row| row.get::<_, i32>(0))?;
296 
297         let mut expected = 0;
298         for value in series {
299             assert_eq!(expected, value?);
300             expected += 5;
301         }
302 
303         let mut s =
304             db.prepare("SELECT * FROM generate_series WHERE start=1 AND stop=9 AND step=2")?;
305         let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
306         assert_eq!(vec![1, 3, 5, 7, 9], series);
307         let mut s = db.prepare("SELECT * FROM generate_series LIMIT 5")?;
308         let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
309         assert_eq!(vec![0, 1, 2, 3, 4], series);
310         let mut s = db.prepare("SELECT * FROM generate_series(0,32,5) ORDER BY value DESC")?;
311         let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
312         assert_eq!(vec![30, 25, 20, 15, 10, 5, 0], series);
313 
314         Ok(())
315     }
316 }
317