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