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