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