sqlite3_ext/vtab/index_info.rs
1use crate::{ffi, sqlite3_match_version, sqlite3_require_version, types::*, value::*};
2use std::{ffi::CStr, ptr};
3
4/// Information about a query plan.
5///
6/// This struct contains all of the information about a query plan that SQLite is attempting on
7/// the virtual table. The struct will be passed to
8/// [VTab::best_index](super::VTab::best_index).
9///
10/// This struct is both an input and an output. The virtual table implementation should examine
11/// the [constraints](Self::constraints) and [order_by](Self::order_by) fields, decide on the
12/// best query plan, and then set the results using [IndexInfoConstraint::set_argv_index],
13/// [set_estimated_cost](Self::set_estimated_cost), and the other methods.
14#[repr(transparent)]
15pub struct IndexInfo {
16 base: ffi::sqlite3_index_info,
17}
18
19impl IndexInfo {
20 pub fn constraints(&self) -> IndexInfoConstraintIterator {
21 IndexInfoConstraintIterator::new(self)
22 }
23
24 pub fn order_by(&self) -> IndexInfoOrderByIterator {
25 IndexInfoOrderByIterator::new(self)
26 }
27
28 /// Determine if a query is DISTINCT.
29 ///
30 /// Requires SQLite 3.38.0. On earlier versions, this function will always return
31 /// [DistinctMode::Ordered].
32 pub fn distinct_mode(&self) -> DistinctMode {
33 sqlite3_match_version! {
34 3_038_000 => {
35 let ret = unsafe { ffi::sqlite3_vtab_distinct(&self.base as *const _ as _) };
36 DistinctMode::from_sqlite(ret)
37 },
38 _ => DistinctMode::Ordered,
39 }
40 }
41
42 /// Retrieve the value previously set by
43 /// [set_index_num](Self::set_index_num).
44 pub fn index_num(&self) -> i32 {
45 self.base.idxNum
46 }
47
48 /// Set the index number of this query plan. This is an arbitrary value which will be
49 /// passed to [VTabCursor::filter](super::VTabCursor::filter).
50 pub fn set_index_num(&mut self, val: i32) {
51 self.base.idxNum = val;
52 }
53
54 /// Retrieve the value previously set by
55 /// [set_index_str](Self::set_index_str).
56 pub fn index_str(&self) -> Option<&str> {
57 if self.base.idxStr.is_null() {
58 None
59 } else {
60 let cstr = unsafe { CStr::from_ptr(self.base.idxStr) };
61 cstr.to_str().ok()
62 }
63 }
64
65 /// Set the index string of this query plan. This is an arbitrary value which will be
66 /// passed to [VTabCursor::filter](super::VTabCursor::filter).
67 ///
68 /// This function can fail if SQLite is not able to allocate memory for the string.
69 pub fn set_index_str(&mut self, val: Option<&str>) -> Result<()> {
70 if self.base.needToFreeIdxStr != 0 {
71 unsafe { ffi::sqlite3_free(self.base.idxStr as _) };
72 }
73 match val {
74 None => {
75 self.base.idxStr = ptr::null_mut();
76 self.base.needToFreeIdxStr = 0;
77 }
78 Some(x) => {
79 self.base.idxStr = ffi::str_to_sqlite3(x)?;
80 self.base.needToFreeIdxStr = 1;
81 }
82 }
83 Ok(())
84 }
85
86 /// Set the index string without copying.
87 pub fn set_index_str_static(&mut self, val: &'static CStr) {
88 if self.base.needToFreeIdxStr != 0 {
89 unsafe { ffi::sqlite3_free(self.base.idxStr as _) };
90 }
91 self.base.idxStr = val.as_ptr() as _;
92 self.base.needToFreeIdxStr = 0;
93 }
94
95 /// Retrieve the value previously set by
96 /// [set_order_by_consumed](Self::set_order_by_consumed).
97 pub fn order_by_consumed(&self) -> bool {
98 self.base.orderByConsumed != 0
99 }
100
101 /// Indicate that the virtual table fully understands the requirements of the
102 /// [order_by](Self::order_by) and [distinct_mode](Self::distinct_mode) fields. If this
103 /// is the case, then SQLite can omit reordering the results of the query, which may
104 /// improve performance. It is never necessary to use the order_by information, but
105 /// virtual tables may opt to use it as a performance optimization.
106 pub fn set_order_by_consumed(&mut self, val: bool) {
107 self.base.orderByConsumed = val as _;
108 }
109
110 /// Retrieve the value previously set by
111 /// [set_estimated_cost](Self::set_estimated_cost).
112 pub fn estimated_cost(&self) -> f64 {
113 self.base.estimatedCost
114 }
115
116 pub fn set_estimated_cost(&mut self, val: f64) {
117 self.base.estimatedCost = val;
118 }
119
120 /// Retrieve the value previously set by
121 /// [set_estimated_rows](Self::set_estimated_rows).
122 ///
123 /// Requires SQLite 3.8.2.
124 pub fn estimated_rows(&self) -> Result<i64> {
125 sqlite3_require_version!(3_008_002, Ok(self.base.estimatedRows))
126 }
127
128 /// Requires SQLite 3.8.2. On earlier versions of SQLite, this function is a harmless
129 /// no-op.
130 pub fn set_estimated_rows(&mut self, val: i64) {
131 let _ = val;
132 sqlite3_match_version! {
133 3_008_220 => self.base.estimatedRows = val,
134 _ => (),
135 }
136 }
137
138 /// Retrieve the value previously set by
139 /// [set_scan_flags](Self::set_scan_flags).
140 ///
141 /// Requires SQLite 3.9.0.
142 pub fn scan_flags(&self) -> Result<usize> {
143 sqlite3_require_version!(3_009_000, Ok(self.base.idxFlags as _))
144 }
145
146 /// Requires SQLite 3.9.0. On earlier versions of SQLite, this function is a harmless
147 /// no-op.
148 pub fn set_scan_flags(&mut self, val: usize) -> () {
149 let _ = val;
150 sqlite3_match_version! {
151 3_009_000 => self.base.idxFlags = val as _,
152 _ => (),
153 }
154 }
155
156 /// Requires SQLite 3.10.0.
157 pub fn columns_used(&self) -> Result<u64> {
158 sqlite3_require_version!(3_010_000, Ok(self.base.colUsed))
159 }
160}
161
162#[derive(Copy, Clone)]
163pub struct IndexInfoConstraint<'a> {
164 index_info: &'a IndexInfo,
165 position: usize,
166}
167
168impl IndexInfoConstraint<'_> {
169 fn constraint(&self) -> &ffi::sqlite3_index_info_sqlite3_index_constraint {
170 unsafe { &*self.index_info.base.aConstraint.offset(self.position as _) }
171 }
172
173 fn usage(&self) -> &mut ffi::sqlite3_index_info_sqlite3_index_constraint_usage {
174 unsafe {
175 &mut *self
176 .index_info
177 .base
178 .aConstraintUsage
179 .offset(self.position as _)
180 }
181 }
182
183 /// Return the column being constrained. The value is a 0-based index of columns as declared by
184 /// [connect](super::VTab::connect) / [create](super::CreateVTab::create). The rowid column is
185 /// index -1.
186 pub fn column(&self) -> i32 {
187 self.constraint().iColumn as _
188 }
189
190 /// Return the type of constraint.
191 pub fn op(&self) -> ConstraintOp {
192 ConstraintOp::from_sqlite(self.constraint().op)
193 }
194
195 /// [IndexInfo::constraints] contains information about all constraints that apply to
196 /// the virtual table, but some of the constraints might not be usable because of the
197 /// way tables are ordered in a join. The best_index method must therefore only
198 /// consider constraints that for which this method returns true.
199 pub fn usable(&self) -> bool {
200 self.constraint().usable != 0
201 }
202
203 /// Returns the right-hand side of the constraint.
204 ///
205 /// This routine attempts to retrieve the value of the right-hand operand of the constraint if
206 /// that operand is known. If the operand is not known, then Err([SQLITE_NOTFOUND]) is returned.
207 /// This method can return another error type if something goes wrong.
208 ///
209 /// This method is usually only successful if the right-hand operand of a constraint is a
210 /// literal value in the original SQL statement. If the right-hand operand is an expression or
211 /// a reference to some other column or a host parameter, then this method will probably return
212 /// Err(SQLITE_NOTFOUND).
213 ///
214 /// Some constraints, such as [ConstraintOp::IsNull], have no right-hand operand. For such
215 /// constraints, this method always returns Err(SQLITE_NOTFOUND).
216 ///
217 /// Requires SQLite 3.38.0. On earlier versions of SQLite, Err(SQLITE_NOTFOUND) is always
218 /// returned.
219 pub fn rhs(&self) -> Result<&ValueRef> {
220 sqlite3_match_version! {
221 3_038_000 => unsafe {
222 let mut ret: *mut ffi::sqlite3_value = ptr::null_mut();
223 Error::from_sqlite(ffi::sqlite3_vtab_rhs_value(
224 &self.index_info.base as *const _ as _,
225 self.position as _,
226 &mut ret,
227 ))?;
228 Ok(&*(ret as *const crate::value::ValueRef))
229 },
230 _ => Err(SQLITE_NOTFOUND),
231 }
232 }
233
234 /// Return the collation to use for text comparisons on this column.
235 ///
236 /// See [the SQLite documentation](https://www.sqlite.org/c3ref/vtab_collation.html)
237 /// for more details.
238 pub fn collation(&self) -> Result<&str> {
239 sqlite3_require_version!(3_022_000, {
240 let ret = unsafe {
241 CStr::from_ptr(ffi::sqlite3_vtab_collation(
242 &self.index_info.base as *const _ as _,
243 self.position as _,
244 ))
245 };
246 Ok(ret.to_str()?)
247 })
248 }
249
250 /// Retrieve the value previously set using [set_argv_index](Self::set_argv_index).
251 pub fn argv_index(&self) -> Option<u32> {
252 match self.usage().argvIndex {
253 0 => None,
254 x => Some((x - 1) as _),
255 }
256 }
257
258 /// Set the desired index for [filter](super::VTabCursor::filter)'s argv.
259 ///
260 /// Exactly one entry in the IndexInfo should be set to 0, another to 1, another to 2,
261 /// and so forth up to as many or as few as the best_index method wants. The EXPR of
262 /// the corresponding constraints will then be passed in as the argv[] parameters to
263 /// filter.
264 pub fn set_argv_index(&mut self, idx: Option<u32>) {
265 self.usage().argvIndex = match idx {
266 None => 0,
267 Some(i) => (i + 1) as _,
268 };
269 }
270
271 /// Retrieve the value previously set by [set_omit](Self::set_omit).
272 pub fn omit(&self) -> bool {
273 self.usage().omit != 0
274 }
275
276 /// Advise SQLite that this constraint is validated by the virtual table
277 /// implementation. SQLite may skip performing its own check in some cases. It is
278 /// generally a hint and not a requirement, but a notable exception is for
279 /// [ConstraintOp::Offset], which is always honored. See [the SQLite
280 /// documentation](https://www.sqlite.org/vtab.html#omit_constraint_checking_in_bytecode)
281 /// for more details.
282 pub fn set_omit(&mut self, val: bool) {
283 self.usage().omit = val as _;
284 }
285
286 /// Check if all values in this IN constraint are able to be processed simultaneously.
287 /// If this method returns true, then a call to
288 /// [set_value_list_wanted](Self::set_value_list_wanted) would also return true.
289 ///
290 /// Requires SQLite 3.38.0. On earlier versions, this function will always return
291 /// false.
292 pub fn value_list_available(&self) -> bool {
293 sqlite3_match_version! {
294 3_038_000 => unsafe {
295 ffi::sqlite3_vtab_in(
296 &self.index_info.base as *const _ as _,
297 self.position as _,
298 -1,
299 ) != 0
300 },
301 _ => false,
302 }
303 }
304
305 /// Instruct SQLite to return all values in an IN constraint simultaneously.
306 ///
307 /// A constraint on a virtual table in the form of "column IN (...)" is communicated to
308 /// [VTab::best_index](super::VTab::best_index) as a [ConstraintOp::Eq] constraint. If
309 /// the virtual table wants to use this constraint, it must use
310 /// [set_argv_index](Self::set_argv_index) to assign the constraint to an argument.
311 /// Then, SQLite will invoke [VTabCursor::filter](super::VTabCursor::filter) once for
312 /// each value on the right-hand side of the IN operator. Thus, the virtual table only
313 /// sees a single value from the right-hand side of the IN operator at a time.
314 ///
315 /// In some cases, however, it would be advantageous for the virtual table to see all values on the right-hand of the IN operator all at once. This method enables this feature.
316 ///
317 /// Calling this method with true will request [ValueList](crate::ValueList)
318 /// processing. In order for ValueList processing to work:
319 ///
320 /// 1. this constraint must be assigned an argv index using
321 /// [set_argv_index](Self::set_argv_index);
322 /// 2. this method is called with true; and
323 /// 3. SQLite is able to provide all values simultaneously.
324 ///
325 /// If all of these criteria are met, then the corresponding argument passed to
326 /// [VTabCursor::filter](super::VTabCursor::filter) will appear to be SQL NULL, but
327 /// accessible using [ValueList](crate::ValueList). If this facility is requested but
328 /// this method returns false, then VTabCursor::filter will be invoked multiple times
329 /// with each different value of the constraint, as normal.
330 ///
331 /// This method always returns the same value that
332 /// [value_list_available](Self::value_list_available) would. Calling this method with
333 /// false cancels a previous request for a ValueList.
334 ///
335 /// See [the SQLite documentation](https://www.sqlite.org/c3ref/vtab_in.html) for more
336 /// details.
337 ///
338 /// Requires SQLite 3.38.0. On earlier versions, this function will always return
339 /// false.
340 pub fn set_value_list_wanted(&mut self, val: bool) -> bool {
341 let _ = val;
342 sqlite3_match_version! {
343 3_038_000 => unsafe {
344 ffi::sqlite3_vtab_in(
345 &self.index_info.base as *const _ as _,
346 self.position as _,
347 if val { 1 } else { 0 },
348 ) != 0
349 },
350 _ => false,
351 }
352 }
353}
354
355#[derive(Copy, Clone)]
356pub struct IndexInfoOrderBy<'a> {
357 index_info: &'a IndexInfo,
358 position: usize,
359}
360
361impl IndexInfoOrderBy<'_> {
362 fn base(&self) -> &ffi::sqlite3_index_info_sqlite3_index_orderby {
363 unsafe { &*self.index_info.base.aOrderBy.offset(self.position as _) }
364 }
365
366 pub fn column(&self) -> i32 {
367 self.base().iColumn as _
368 }
369
370 pub fn desc(&self) -> bool {
371 self.base().desc != 0
372 }
373}
374
375macro_rules! make_iterator(
376 ($t:ident, $n:ident) => {
377 paste::paste! {
378 pub struct [<$t Iterator>]<'a> {
379 current: $t<'a>,
380 }
381
382 impl<'a> [<$t Iterator>]<'a> {
383 fn new(index_info: &'a IndexInfo) -> Self {
384 Self {
385 current: $t {
386 index_info,
387 position: usize::MAX,
388 },
389 }
390 }
391 }
392
393 impl<'a> Iterator for [<$t Iterator>]<'a> {
394 type Item = $t<'a>;
395
396 fn next(&mut self) -> Option<Self::Item> {
397 let pos = self.current.position.wrapping_add(1);
398 if pos < self.current.index_info.base.$n as usize {
399 self.current.position = pos;
400 Some(self.current)
401 } else {
402 None
403 }
404 }
405
406 fn size_hint(&self) -> (usize, Option<usize>) {
407 let remaining = self.current.index_info.base.$n as usize
408 - self.current.position.wrapping_add(1);
409 (remaining, Some(remaining))
410 }
411 }
412 }
413 }
414);
415
416make_iterator!(IndexInfoConstraint, nConstraint);
417make_iterator!(IndexInfoOrderBy, nOrderBy);
418
419#[derive(Debug, Eq, PartialEq, Clone, Copy)]
420pub enum ConstraintOp {
421 Eq,
422 GT,
423 LE,
424 LT,
425 GE,
426 Match,
427 Like, /* 3.10.0 and later */
428 Glob, /* 3.10.0 and later */
429 Regexp, /* 3.10.0 and later */
430 NE, /* 3.21.0 and later */
431 IsNot, /* 3.21.0 and later */
432 IsNotNull, /* 3.21.0 and later */
433 IsNull, /* 3.21.0 and later */
434 Is, /* 3.21.0 and later */
435 Limit, /* 3.38.0 and later */
436 Offset, /* 3.38.0 and later */
437 Function(u8), /* 3.25.0 and later */
438}
439
440impl ConstraintOp {
441 pub(crate) fn assert_valid_function_constraint(&self) {
442 if let ConstraintOp::Function(val) = *self {
443 if val >= 150 {
444 return;
445 }
446 }
447 panic!("invalid function constraint")
448 }
449
450 fn from_sqlite(val: u8) -> ConstraintOp {
451 match val as _ {
452 2 => ConstraintOp::Eq,
453 4 => ConstraintOp::GT,
454 8 => ConstraintOp::LE,
455 16 => ConstraintOp::LT,
456 32 => ConstraintOp::GE,
457 64 => ConstraintOp::Match,
458 65 => ConstraintOp::Like,
459 66 => ConstraintOp::Glob,
460 67 => ConstraintOp::Regexp,
461 68 => ConstraintOp::NE,
462 69 => ConstraintOp::IsNot,
463 70 => ConstraintOp::IsNotNull,
464 71 => ConstraintOp::IsNull,
465 72 => ConstraintOp::Is,
466 73 => ConstraintOp::Limit,
467 74 => ConstraintOp::Offset,
468 150..=255 => ConstraintOp::Function(val),
469 _ => panic!("invalid constraint op"),
470 }
471 }
472}
473
474/// Describes the requirements of the virtual table query.
475///
476/// This value is retured by [IndexInfo::distinct_mode]. It allows the virtual table
477/// implementation to decide if it is safe to consume the [order_by](IndexInfo::order_by)
478/// fields using [IndexInfo::set_order_by_consumed].
479///
480/// The levels described here are progressively less demanding. If the virtual table
481/// implementation meets the requirements of [DistinctMode::Ordered], then it is always safe to
482/// consume the order_by fields.
483///
484/// For the purposes of comparing virtual table output values to see if the values are same
485/// value for sorting purposes, two NULL values are considered to be the same. In other words,
486/// the comparison operator is "IS" (or "IS NOT DISTINCT FROM") and not "==".
487#[derive(Debug, Eq, PartialEq, Clone, Copy)]
488pub enum DistinctMode {
489 /// The virtual table must return all rows in the correct order according to the
490 /// [order_by](IndexInfo::order_by) fields.
491 Ordered,
492 /// The virtual table may return rows in any order, but all rows that are in the same
493 /// group must be adjacent to one another. A group is defined as all rows for which all
494 /// of the columns in [order_by](IndexInfo::order_by) are equal. This is the mode used
495 /// when planning a GROUP BY query.
496 Grouped,
497 /// The same as [DistinctMode::Grouped], however the virtual table is allowed (but not
498 /// required) to skip all but a single row within each group. This is the mode used
499 /// when planning a DISTINCT query.
500 Distinct,
501}
502
503impl DistinctMode {
504 #[cfg(modern_sqlite)]
505 fn from_sqlite(val: i32) -> Self {
506 match val {
507 0 => Self::Ordered,
508 1 => Self::Grouped,
509 2 => Self::Distinct,
510 _ => panic!("invalid distinct mode"),
511 }
512 }
513}
514
515impl std::fmt::Debug for IndexInfo {
516 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
517 let mut ds = f.debug_struct("IndexInfo");
518 ds.field(
519 "constraints",
520 &self.constraints().collect::<Vec<_>>().as_slice(),
521 )
522 .field("order_by", &self.order_by().collect::<Vec<_>>().as_slice())
523 .field("index_num", &self.index_num())
524 .field("index_str", &self.index_str())
525 .field("order_by_consumed", &self.order_by_consumed())
526 .field("estimated_cost", &self.estimated_cost());
527 self.estimated_rows()
528 .map(|v| ds.field("estimated_rows", &v))
529 .ok();
530 self.scan_flags().map(|v| ds.field("scan_flags", &v)).ok();
531 self.columns_used()
532 .map(|v| ds.field("columns_used", &v))
533 .ok();
534 ds.finish()
535 }
536}
537
538impl std::fmt::Debug for IndexInfoConstraint<'_> {
539 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
540 let mut ds = f.debug_struct("IndexInfoConstraint");
541 ds.field("column", &self.column())
542 .field("op", &self.op())
543 .field("usable", &self.usable());
544 sqlite3_match_version! {
545 3_038_000 => {
546 ds.field("rhs", &self.rhs());
547 }
548 _ => (),
549 }
550 sqlite3_match_version! {
551 3_022_000 => {
552 ds.field("collation", &self.collation());
553 }
554 _ => (),
555 }
556 ds.field("argv_index", &self.argv_index())
557 .field("omit", &self.omit())
558 .finish()
559 }
560}
561
562impl std::fmt::Debug for IndexInfoOrderBy<'_> {
563 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
564 f.debug_struct("IndexInfoOrderBy")
565 .field("column", &self.column())
566 .field("desc", &self.desc())
567 .finish()
568 }
569}