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