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