1 //! `feature = "series"` 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::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, VTabConnection, VTabCursor,
13 Values,
14 };
15 use crate::{Connection, Result};
16
17 /// `feature = "series"` 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 #[repr(C)]
31 struct QueryPlanFlags: ::std::os::raw::c_int {
32 // start = $value -- constraint exists
33 const START = 1;
34 // stop = $value -- constraint exists
35 const STOP = 2;
36 // step = $value -- constraint exists
37 const STEP = 4;
38 // output in descending order
39 const DESC = 8;
40 // Both start and stop
41 const BOTH = QueryPlanFlags::START.bits | QueryPlanFlags::STOP.bits;
42 }
43 }
44
45 /// An instance of the Series virtual table
46 #[repr(C)]
47 struct SeriesTab {
48 /// Base class. Must be first
49 base: ffi::sqlite3_vtab,
50 }
51
52 unsafe impl VTab for SeriesTab {
53 type Aux = ();
54 type Cursor = SeriesTabCursor;
55
connect( _: &mut VTabConnection, _aux: Option<&()>, _args: &[&[u8]], ) -> Result<(String, SeriesTab)>56 fn connect(
57 _: &mut VTabConnection,
58 _aux: Option<&()>,
59 _args: &[&[u8]],
60 ) -> Result<(String, SeriesTab)> {
61 let vtab = SeriesTab {
62 base: ffi::sqlite3_vtab::default(),
63 };
64 Ok((
65 "CREATE TABLE x(value,start hidden,stop hidden,step hidden)".to_owned(),
66 vtab,
67 ))
68 }
69
best_index(&self, info: &mut IndexInfo) -> Result<()>70 fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
71 // The query plan bitmask
72 let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();
73 // Index of the start= constraint
74 let mut start_idx = None;
75 // Index of the stop= constraint
76 let mut stop_idx = None;
77 // Index of the step= constraint
78 let mut step_idx = None;
79 for (i, constraint) in info.constraints().enumerate() {
80 if !constraint.is_usable() {
81 continue;
82 }
83 if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
84 continue;
85 }
86 match constraint.column() {
87 SERIES_COLUMN_START => {
88 start_idx = Some(i);
89 idx_num |= QueryPlanFlags::START;
90 }
91 SERIES_COLUMN_STOP => {
92 stop_idx = Some(i);
93 idx_num |= QueryPlanFlags::STOP;
94 }
95 SERIES_COLUMN_STEP => {
96 step_idx = Some(i);
97 idx_num |= QueryPlanFlags::STEP;
98 }
99 _ => {}
100 };
101 }
102
103 let mut num_of_arg = 0;
104 if let Some(start_idx) = start_idx {
105 num_of_arg += 1;
106 let mut constraint_usage = info.constraint_usage(start_idx);
107 constraint_usage.set_argv_index(num_of_arg);
108 constraint_usage.set_omit(true);
109 }
110 if let Some(stop_idx) = stop_idx {
111 num_of_arg += 1;
112 let mut constraint_usage = info.constraint_usage(stop_idx);
113 constraint_usage.set_argv_index(num_of_arg);
114 constraint_usage.set_omit(true);
115 }
116 if let Some(step_idx) = step_idx {
117 num_of_arg += 1;
118 let mut constraint_usage = info.constraint_usage(step_idx);
119 constraint_usage.set_argv_index(num_of_arg);
120 constraint_usage.set_omit(true);
121 }
122 if idx_num.contains(QueryPlanFlags::BOTH) {
123 // Both start= and stop= boundaries are available.
124 info.set_estimated_cost(f64::from(
125 2 - if idx_num.contains(QueryPlanFlags::STEP) {
126 1
127 } else {
128 0
129 },
130 ));
131 info.set_estimated_rows(1000);
132 let order_by_consumed = {
133 let mut order_bys = info.order_bys();
134 if let Some(order_by) = order_bys.next() {
135 if order_by.is_order_by_desc() {
136 idx_num |= QueryPlanFlags::DESC;
137 }
138 true
139 } else {
140 false
141 }
142 };
143 if order_by_consumed {
144 info.set_order_by_consumed(true);
145 }
146 } else {
147 info.set_estimated_cost(2_147_483_647f64);
148 info.set_estimated_rows(2_147_483_647);
149 }
150 info.set_idx_num(idx_num.bits());
151 Ok(())
152 }
153
open(&self) -> Result<SeriesTabCursor>154 fn open(&self) -> Result<SeriesTabCursor> {
155 Ok(SeriesTabCursor::new())
156 }
157 }
158
159 /// A cursor for the Series virtual table
160 #[derive(Default)]
161 #[repr(C)]
162 struct SeriesTabCursor {
163 /// Base class. Must be first
164 base: ffi::sqlite3_vtab_cursor,
165 /// True to count down rather than up
166 is_desc: bool,
167 /// The rowid
168 row_id: i64,
169 /// Current value ("value")
170 value: i64,
171 /// Mimimum value ("start")
172 min_value: i64,
173 /// Maximum value ("stop")
174 max_value: i64,
175 /// Increment ("step")
176 step: i64,
177 }
178
179 impl SeriesTabCursor {
new() -> SeriesTabCursor180 fn new() -> SeriesTabCursor {
181 SeriesTabCursor::default()
182 }
183 }
184 unsafe impl VTabCursor for SeriesTabCursor {
filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()>185 fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
186 let idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
187 let mut i = 0;
188 if idx_num.contains(QueryPlanFlags::START) {
189 self.min_value = args.get(i)?;
190 i += 1;
191 } else {
192 self.min_value = 0;
193 }
194 if idx_num.contains(QueryPlanFlags::STOP) {
195 self.max_value = args.get(i)?;
196 i += 1;
197 } else {
198 self.max_value = 0xffff_ffff;
199 }
200 if idx_num.contains(QueryPlanFlags::STEP) {
201 self.step = args.get(i)?;
202 if self.step < 1 {
203 self.step = 1;
204 }
205 } else {
206 self.step = 1;
207 };
208 for arg in args.iter() {
209 if arg.data_type() == Type::Null {
210 // If any of the constraints have a NULL value, then return no rows.
211 self.min_value = 1;
212 self.max_value = 0;
213 break;
214 }
215 }
216 self.is_desc = idx_num.contains(QueryPlanFlags::DESC);
217 if self.is_desc {
218 self.value = self.max_value;
219 if self.step > 0 {
220 self.value -= (self.max_value - self.min_value) % self.step;
221 }
222 } else {
223 self.value = self.min_value;
224 }
225 self.row_id = 1;
226 Ok(())
227 }
228
next(&mut self) -> Result<()>229 fn next(&mut self) -> Result<()> {
230 if self.is_desc {
231 self.value -= self.step;
232 } else {
233 self.value += self.step;
234 }
235 self.row_id += 1;
236 Ok(())
237 }
238
eof(&self) -> bool239 fn eof(&self) -> bool {
240 if self.is_desc {
241 self.value < self.min_value
242 } else {
243 self.value > self.max_value
244 }
245 }
246
column(&self, ctx: &mut Context, i: c_int) -> Result<()>247 fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
248 let x = match i {
249 SERIES_COLUMN_START => self.min_value,
250 SERIES_COLUMN_STOP => self.max_value,
251 SERIES_COLUMN_STEP => self.step,
252 _ => self.value,
253 };
254 ctx.set_result(&x)
255 }
256
rowid(&self) -> Result<i64>257 fn rowid(&self) -> Result<i64> {
258 Ok(self.row_id)
259 }
260 }
261
262 #[cfg(test)]
263 mod test {
264 use crate::ffi;
265 use crate::vtab::series;
266 use crate::{Connection, NO_PARAMS};
267
268 #[test]
test_series_module()269 fn test_series_module() {
270 let version = unsafe { ffi::sqlite3_libversion_number() };
271 if version < 3_008_012 {
272 return;
273 }
274
275 let db = Connection::open_in_memory().unwrap();
276 series::load_module(&db).unwrap();
277
278 let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)").unwrap();
279
280 let series = s.query_map(NO_PARAMS, |row| row.get::<_, i32>(0)).unwrap();
281
282 let mut expected = 0;
283 for value in series {
284 assert_eq!(expected, value.unwrap());
285 expected += 5;
286 }
287 }
288 }
289