inillucent_sql/vtab.rs
1//! What the front end has to know about a virtual table.
2//!
3//! Invariant: this file holds the *data* of the virtual-table contract and none
4//! of its behaviour. The planner has to be able to say "here are the
5//! constraints I can offer and the order I would like", and the catalog has to
6//! be able to hold the columns a module declared, and neither of those can wait
7//! until the layer that owns modules. The traits a module implements live above
8//! this, in `inillucent-ext`, where a pager can be named.
9//!
10//! The split is not bureaucratic: it is what lets a bound statement stay a pure
11//! function of its SQL and one catalog generation. A planner that had to call
12//! into a module to describe a plan would be a planner whose output depended on
13//! run-time state.
14
15use inillucent_base::DbResult;
16use inillucent_value::{Affinity, Value};
17
18/// The comparison a constraint applies.
19///
20/// `Match`, `Like`, `Glob` and `Regexp` are here because an operator a module
21/// understands is the reason virtual tables exist: `t MATCH 'x'` means nothing
22/// to the engine and everything to FTS5.
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum ConstraintOp {
25 /// `=`
26 Eq,
27 /// `>`
28 Gt,
29 /// `<=`
30 Le,
31 /// `<`
32 Lt,
33 /// `>=`
34 Ge,
35 /// `MATCH`
36 Match,
37 /// `LIKE`
38 Like,
39 /// `GLOB`
40 Glob,
41 /// `REGEXP`
42 Regexp,
43 /// `!=`
44 Ne,
45 /// `IS NOT`
46 IsNot,
47 /// `IS NOT NULL`
48 IsNotNull,
49 /// `IS NULL`
50 IsNull,
51 /// `IS`
52 Is,
53}
54
55impl ConstraintOp {
56 /// Returns the number the C surface gives this operator.
57 pub fn code(self) -> i32 {
58 match self {
59 ConstraintOp::Eq => 2,
60 ConstraintOp::Gt => 4,
61 ConstraintOp::Le => 8,
62 ConstraintOp::Lt => 16,
63 ConstraintOp::Ge => 32,
64 ConstraintOp::Match => 64,
65 ConstraintOp::Like => 65,
66 ConstraintOp::Glob => 66,
67 ConstraintOp::Regexp => 67,
68 ConstraintOp::Ne => 68,
69 ConstraintOp::IsNot => 69,
70 ConstraintOp::IsNotNull => 70,
71 ConstraintOp::IsNull => 71,
72 ConstraintOp::Is => 72,
73 }
74 }
75
76 /// Returns whether the operator has a right-hand value to pass on.
77 ///
78 /// `IS NULL` and `IS NOT NULL` do not, which is why they are offered to a
79 /// module without an argument position ever being filled.
80 pub fn has_value(self) -> bool {
81 !matches!(self, ConstraintOp::IsNull | ConstraintOp::IsNotNull)
82 }
83}
84
85/// The column number that names the rowid rather than a declared column.
86pub const ROWID_COLUMN: i32 = -1;
87
88/// One constraint the query offers the module.
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub struct ConstraintSpec {
91 /// Which column, or [`ROWID_COLUMN`].
92 pub column: i32,
93 /// The comparison.
94 pub op: ConstraintOp,
95 /// Whether the value is available at the time this loop runs.
96 ///
97 /// A constraint against a table the loop has not reached yet is offered but
98 /// not usable, which is how one `best_index` answer serves every position
99 /// the term could take in the join order.
100 pub usable: bool,
101}
102
103/// One `ORDER BY` term the query offers the module.
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub struct OrderSpec {
106 /// Which column, or [`ROWID_COLUMN`].
107 pub column: i32,
108 /// Whether the term is descending.
109 pub descending: bool,
110}
111
112/// What the module decided to do with one constraint.
113#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
114pub struct ConstraintUsage {
115 /// The one-based position the value is passed to `filter` in, or zero when
116 /// the constraint is not used.
117 pub argument: usize,
118 /// Whether the engine may stop testing this constraint itself.
119 ///
120 /// The module promising, not the engine assuming. A module that sets this
121 /// and then does not apply the constraint returns wrong rows, which is why
122 /// the default is to test it twice.
123 pub omit: bool,
124}
125
126/// The question put to a module's `best_index`, and the answer written back.
127#[derive(Clone, Debug, PartialEq)]
128pub struct IndexQuery {
129 /// The constraints the query can offer.
130 pub constraints: Vec<ConstraintSpec>,
131 /// The ordering the query would like.
132 pub order_by: Vec<OrderSpec>,
133 /// What the module decided about each constraint, in the same order.
134 pub usage: Vec<ConstraintUsage>,
135 /// The plan number the module chose, passed back to `filter`.
136 pub index_number: i32,
137 /// The plan string the module chose, passed back to `filter`.
138 pub index_string: String,
139 /// Whether the rows will already be in the requested order.
140 pub ordered: bool,
141 /// What the module thinks the scan will cost.
142 pub estimated_cost: f64,
143 /// How many rows the module thinks it will produce.
144 pub estimated_rows: i64,
145}
146
147impl IndexQuery {
148 /// Returns a question with every answer at its default.
149 pub fn new(constraints: Vec<ConstraintSpec>, order_by: Vec<OrderSpec>) -> IndexQuery {
150 let usage = vec![ConstraintUsage::default(); constraints.len()];
151 IndexQuery {
152 constraints,
153 order_by,
154 usage,
155 index_number: 0,
156 index_string: String::new(),
157 ordered: false,
158 // SQLite's own default, which is deliberately enormous: a module
159 // that says nothing about cost should not win a join order it has
160 // no claim to.
161 estimated_cost: 5.0e98,
162 estimated_rows: 25,
163 }
164 }
165
166 /// Marks one constraint as used, taking the next argument position.
167 pub fn use_constraint(&mut self, index: usize, omit: bool) -> usize {
168 let next = self
169 .usage
170 .iter()
171 .map(|usage| usage.argument)
172 .max()
173 .unwrap_or(0)
174 .saturating_add(1);
175 if let Some(usage) = self.usage.get_mut(index) {
176 usage.argument = next;
177 usage.omit = omit;
178 }
179 next
180 }
181
182 /// Returns the constraint positions that feed `filter`, in argument order.
183 pub fn argument_order(&self) -> Vec<usize> {
184 let mut claimed: Vec<(usize, usize)> = self
185 .usage
186 .iter()
187 .enumerate()
188 .filter(|(_, usage)| usage.argument > 0)
189 .map(|(index, usage)| (usage.argument, index))
190 .collect();
191 claimed.sort_unstable();
192 claimed.into_iter().map(|(_, index)| index).collect()
193 }
194}
195
196/// What `filter` is told to do.
197#[derive(Clone, Debug)]
198pub struct FilterPlan {
199 /// The plan number `best_index` chose.
200 pub index_number: i32,
201 /// The plan string `best_index` chose.
202 pub index_string: String,
203 /// The constraint values, in the argument order `best_index` assigned.
204 pub arguments: Vec<Value<'static>>,
205}
206
207/// One column a module declares.
208#[derive(Clone, Debug, PartialEq, Eq)]
209pub struct DeclaredColumn {
210 /// The column name.
211 pub name: Vec<u8>,
212 /// The declared type, exactly as the module wrote it.
213 pub declared_type: Vec<u8>,
214 /// The affinity that type maps to.
215 pub affinity: Affinity,
216 /// The folded name of the column's collation.
217 pub collation: Vec<u8>,
218 /// Whether the column is hidden from `SELECT *` and from an `INSERT` with
219 /// no column list.
220 ///
221 /// A hidden column is how a table-valued function takes its arguments:
222 /// `json_each('[1]')` is `SELECT * FROM json_each WHERE json = '[1]'`, and
223 /// `json` is a hidden column. It is the whole mechanism, not a display
224 /// preference.
225 pub hidden: bool,
226}
227
228impl DeclaredColumn {
229 /// Returns an ordinary visible column with no declared type.
230 pub fn visible(name: &str) -> DeclaredColumn {
231 DeclaredColumn {
232 name: name.as_bytes().to_vec(),
233 declared_type: Vec::new(),
234 affinity: Affinity::Blob,
235 collation: b"binary".to_vec(),
236 hidden: false,
237 }
238 }
239
240 /// Returns a hidden column, which is how an argument is declared.
241 pub fn hidden(name: &str) -> DeclaredColumn {
242 DeclaredColumn {
243 hidden: true,
244 ..DeclaredColumn::visible(name)
245 }
246 }
247
248 /// Returns the column with a declared type and the affinity it implies.
249 pub fn typed(mut self, declared: &str) -> DeclaredColumn {
250 self.declared_type = declared.as_bytes().to_vec();
251 self.affinity = inillucent_value::affinity::for_column(declared.as_bytes());
252 self
253 }
254}
255
256/// What a module says its table looks like.
257#[derive(Clone, Debug, PartialEq, Eq)]
258pub struct Declaration {
259 /// The columns, in the order `SELECT *` and `column` number them.
260 pub columns: Vec<DeclaredColumn>,
261 /// Whether the table has no rowid of its own.
262 pub without_rowid: bool,
263}
264
265/// The root page of one shadow table, by the suffix that names it.
266#[derive(Clone, Debug, PartialEq, Eq)]
267pub struct ShadowRoot {
268 /// The suffix after the virtual table's own name, such as `data`.
269 pub suffix: Vec<u8>,
270 /// The root page of the shadow table's b-tree.
271 pub root: u32,
272}
273
274/// Everything a module is handed when it is connected.
275#[derive(Clone, Debug, Default, PartialEq, Eq)]
276pub struct ModuleArguments {
277 /// The database the table lives in.
278 pub database: usize,
279 /// The schema name, for messages.
280 pub schema: Vec<u8>,
281 /// The virtual table's own name.
282 pub table: Vec<u8>,
283 /// The module's name as written.
284 pub module: Vec<u8>,
285 /// The arguments inside the parentheses, as written source slices.
286 pub arguments: Vec<Vec<u8>>,
287 /// The roots of the shadow tables the schema already holds.
288 pub shadows: Vec<ShadowRoot>,
289}
290
291impl ModuleArguments {
292 /// Returns the root page of one shadow table.
293 pub fn shadow(&self, suffix: &[u8]) -> Option<u32> {
294 self.shadows
295 .iter()
296 .find(|shadow| shadow.suffix == suffix)
297 .map(|shadow| shadow.root)
298 }
299}
300
301/// What a module needs created before it can be connected.
302#[derive(Clone, Debug, PartialEq, Eq)]
303pub struct ShadowTable {
304 /// The suffix after the virtual table's own name.
305 pub suffix: Vec<u8>,
306 /// The `CREATE` statement, with `%` standing for the table's own name.
307 pub create_sql: String,
308 /// The table these shadows belong to, when it is not the module's own.
309 ///
310 /// **How a module reads another table's storage.** `fts5vocab(f, 'row')`
311 /// is a view over the index `f` built, and the whole of what it needs is
312 /// read access to `f`'s shadow tables - which the module contract
313 /// deliberately does not give it, because "a module sees only what it was
314 /// handed" is what makes a hostile module a bounded problem.
315 ///
316 /// So it is handed them, explicitly and by name. A module that names an
317 /// owner is asking for shadows that **already exist**: they are looked up
318 /// rather than created, and a name the catalog does not have is a refusal
319 /// rather than a fresh table. The module still sees only the roots it was
320 /// given, and still cannot resolve a name for itself.
321 pub owner: Option<Vec<u8>>,
322}
323
324/// One row a write asks a module to make.
325#[derive(Clone, Debug)]
326pub enum Change {
327 /// Remove the row with this rowid or primary key.
328 Delete(Value<'static>),
329 /// Add a row.
330 Insert {
331 /// The rowid to use, or NULL for one the module chooses.
332 rowid: Value<'static>,
333 /// One value per declared column, hidden columns included.
334 values: Vec<Value<'static>>,
335 },
336 /// Replace a row.
337 Update {
338 /// The row being replaced.
339 old_rowid: Value<'static>,
340 /// The rowid it should have afterwards, which a statement may change.
341 new_rowid: Value<'static>,
342 /// One value per declared column, hidden columns included.
343 values: Vec<Value<'static>>,
344 },
345}
346
347/// The module one virtual table is implemented by.
348#[derive(Clone, Debug, Default, PartialEq, Eq)]
349pub struct ModuleRef {
350 /// The module name as written.
351 pub name: Vec<u8>,
352 /// The ASCII-folded lookup key.
353 pub folded: Vec<u8>,
354 /// The arguments inside the parentheses, as written.
355 pub arguments: Vec<Vec<u8>>,
356}
357
358/// The rows of a module's shadow tables, whatever engine holds them.
359///
360/// **This is the seam the TDD's "shadow tables become ordinary trees" needs.**
361/// FTS5 and the R-Tree keep their whole state in shadow tables and reach them
362/// only through `ShadowTables`, so the modules themselves say nothing about
363/// pages, cursors or b-trees - which is what lets the same module code run over
364/// the old engine's `sqlite_master` b-trees and the new engine's PAX trees. A
365/// module that had reached a pager directly would have to be written twice.
366///
367/// Every method names a *root*, because a module is handed the roots of its own
368/// shadow tables and nothing else. There is no name resolution here and no
369/// catalog: a module that wanted to read somebody else's table would have to be
370/// given it.
371///
372/// The rowid methods are for a rowid table, where the first value of a row *is*
373/// its rowid; the keyed ones are for a `WITHOUT ROWID` table, whose whole row is
374/// its key. FTS5 uses both.
375pub trait ShadowStore {
376 /// Reads one row by rowid, or nothing when there is not one.
377 ///
378 /// @param root - the shadow table's root
379 /// @param rowid - the row's key
380 fn read_row(&mut self, root: u32, rowid: i64) -> DbResult<Option<Vec<Value<'static>>>>;
381
382 /// Writes one row by rowid, replacing whatever was there.
383 ///
384 /// @param root - the shadow table's root
385 /// @param rowid - the row's key
386 /// @param values - the row, its rowid first
387 fn write_row(&mut self, root: u32, rowid: i64, values: &[Value<'static>]) -> DbResult<()>;
388
389 /// Removes one row by rowid, reporting nothing when there was not one.
390 ///
391 /// @param root - the shadow table's root
392 /// @param rowid - the row's key
393 fn delete_row(&mut self, root: u32, rowid: i64) -> DbResult<()>;
394
395 /// Returns the largest rowid one shadow table holds.
396 ///
397 /// @param root - the shadow table's root
398 fn max_rowid(&mut self, root: u32) -> DbResult<i64>;
399
400 /// Runs a body over every row, in rowid order, stopping when it says so.
401 ///
402 /// @param root - the shadow table's root
403 /// @param body - what to do with each row
404 fn scan(
405 &mut self,
406 root: u32,
407 body: &mut dyn FnMut(i64, &[Value<'static>]) -> DbResult<bool>,
408 ) -> DbResult<()>;
409
410 /// Runs a body over every row whose rowid is at least `from`, in rowid
411 /// order, stopping when it says so.
412 ///
413 /// **A seek, not a scan with a filter, when an implementor has one.** A
414 /// rowid table's rows are already in key order, so a caller that only
415 /// wants what is above a watermark - a delta log's `deltas_above`, chief
416 /// among them - does not need every row below it decoded and thrown
417 /// away; it needs the store to descend to `from` once and walk right
418 /// from there.
419 ///
420 /// **The default is correct rather than fast, and that is deliberate.**
421 /// It is [`Self::scan`] with a callback that skips what is below `from`,
422 /// which costs the whole table exactly as a hand-written filter would -
423 /// so an implementor with no cheap way to position by key is still right
424 /// by doing nothing, and one that can descend directly to a key
425 /// overrides this with that descent. Every rowid tree the new engine
426 /// keeps can; the retired engine's b-trees, reached only from the
427 /// differential suites that still exercise it, are left on the default
428 /// because a seek there is not worth building for a store on its way out.
429 ///
430 /// @param root - the shadow table's root
431 /// @param from - the smallest rowid to visit
432 /// @param body - what to do with each row
433 fn scan_from(
434 &mut self,
435 root: u32,
436 from: i64,
437 body: &mut dyn FnMut(i64, &[Value<'static>]) -> DbResult<bool>,
438 ) -> DbResult<()> {
439 self.scan(root, &mut |rowid, values| {
440 if rowid < from {
441 return Ok(true);
442 }
443 body(rowid, values)
444 })
445 }
446
447 /// Reads one row of a keyed shadow table, or nothing when there is not one.
448 ///
449 /// @param root - the shadow table's root
450 /// @param key - the leading columns that identify it
451 /// @param columns - how many columns to return, `usize::MAX` for all
452 fn read_keyed(
453 &mut self,
454 root: u32,
455 key: &[Value<'static>],
456 columns: usize,
457 ) -> DbResult<Option<Vec<Value<'static>>>>;
458
459 /// Writes one row of a keyed shadow table, replacing whatever was there.
460 ///
461 /// @param root - the shadow table's root
462 /// @param key_columns - how many leading columns form the key
463 /// @param values - the whole row
464 fn write_keyed(
465 &mut self,
466 root: u32,
467 key_columns: usize,
468 values: &[Value<'static>],
469 ) -> DbResult<()>;
470
471 /// Removes one row of a keyed shadow table.
472 ///
473 /// @param root - the shadow table's root
474 /// @param key - the leading columns that identify it
475 fn delete_keyed(&mut self, root: u32, key: &[Value<'static>]) -> DbResult<()>;
476
477 /// Runs a body over every row of a keyed shadow table, in key order.
478 ///
479 /// @param root - the shadow table's root
480 /// @param key_columns - how many leading columns form the key
481 /// @param body - what to do with each row
482 fn scan_keyed(
483 &mut self,
484 root: u32,
485 key_columns: usize,
486 body: &mut dyn FnMut(&[Value<'static>]) -> DbResult<bool>,
487 ) -> DbResult<()>;
488
489 /// Runs a body over every row of a keyed shadow table whose key sorts at
490 /// or after `from`, in key order, stopping when it says so.
491 ///
492 /// **The keyed twin of [`Self::scan_from`], for a key of more than one
493 /// column.** FTS5's `%_idx` is keyed `(segid, term)`, so a caller that
494 /// wants one segment's terms starting at a prefix - a term-major seek to
495 /// `(segid, prefix)`, once per live segment - needs to position by a key
496 /// that is not the whole row, the same shape a rowid seek already had and
497 /// a single-column keyed seek would not need a new method for.
498 ///
499 /// **The default is correct rather than fast, and that is deliberate**,
500 /// for the reason [`Self::scan_from`]'s default is: it is
501 /// [`Self::scan_keyed`] with a callback that skips whatever sorts below
502 /// `from`, so an implementor with no cheap way to position by key is
503 /// still right by doing nothing, and one that can descend directly to a
504 /// key overrides this with that descent.
505 ///
506 /// @param root - the shadow table's root
507 /// @param key_columns - how many leading columns form the key
508 /// @param from - the key to start at, compared column by column
509 /// @param body - what to do with each row
510 fn scan_keyed_from(
511 &mut self,
512 root: u32,
513 key_columns: usize,
514 from: &[Value<'static>],
515 body: &mut dyn FnMut(&[Value<'static>]) -> DbResult<bool>,
516 ) -> DbResult<()> {
517 self.scan_keyed(root, key_columns, &mut |values| {
518 if key_sorts_below(values, from) {
519 return Ok(true);
520 }
521 body(values)
522 })
523 }
524}
525
526/// Returns whether a keyed row's leading columns sort before `from`, compared
527/// column by column under [`inillucent_value::compare::compare_values`] with
528/// `BINARY` collation - what every shadow table's key compares under, since
529/// none of them declares a column collation of its own.
530///
531/// Shared by [`ShadowStore::scan_keyed_from`]'s default implementation and by
532/// a real implementor's seek, which still has to discard whatever a leaf
533/// below the seek's target key holds - `PagedTree::visit_range` positions at
534/// the leaf that *could* hold the key, not necessarily past everything
535/// smaller than it.
536///
537/// @param row - the row read back
538/// @param from - the key a caller asked to start at
539pub fn key_sorts_below(row: &[Value<'static>], from: &[Value<'static>]) -> bool {
540 use std::cmp::Ordering;
541 for (left, right) in row.iter().zip(from.iter()) {
542 match inillucent_value::compare::compare_values(
543 left,
544 right,
545 inillucent_value::Collation::Binary,
546 ) {
547 Ordering::Less => return true,
548 Ordering::Greater => return false,
549 Ordering::Equal => continue,
550 }
551 }
552 false
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558
559 /// The operator numbers are the ones the C surface publishes, because a
560 /// module written against the header compares against these constants.
561 #[test]
562 fn operator_codes_match_the_published_constants() {
563 assert_eq!(ConstraintOp::Eq.code(), 2);
564 assert_eq!(ConstraintOp::Gt.code(), 4);
565 assert_eq!(ConstraintOp::Le.code(), 8);
566 assert_eq!(ConstraintOp::Lt.code(), 16);
567 assert_eq!(ConstraintOp::Ge.code(), 32);
568 assert_eq!(ConstraintOp::Match.code(), 64);
569 assert_eq!(ConstraintOp::Is.code(), 72);
570 }
571
572 /// Argument positions are handed out in the order they are claimed, and
573 /// read back in that same order.
574 #[test]
575 fn argument_positions_are_claimed_in_order() {
576 let mut query = IndexQuery::new(
577 vec![
578 ConstraintSpec {
579 column: 0,
580 op: ConstraintOp::Eq,
581 usable: true,
582 },
583 ConstraintSpec {
584 column: 1,
585 op: ConstraintOp::Gt,
586 usable: true,
587 },
588 ],
589 Vec::new(),
590 );
591 assert_eq!(query.use_constraint(1, true), 1);
592 assert_eq!(query.use_constraint(0, false), 2);
593 assert_eq!(query.argument_order(), vec![1, 0]);
594 assert!(query.usage[1].omit);
595 assert!(!query.usage[0].omit);
596 }
597
598 /// A module that says nothing about cost must not win a join order.
599 #[test]
600 fn the_default_cost_is_deliberately_enormous() {
601 let query = IndexQuery::new(Vec::new(), Vec::new());
602 assert!(query.estimated_cost > 1.0e90);
603 }
604
605 /// The two null tests carry no value, so nothing is passed for them.
606 #[test]
607 fn the_null_tests_carry_no_value() {
608 assert!(!ConstraintOp::IsNull.has_value());
609 assert!(!ConstraintOp::IsNotNull.has_value());
610 assert!(ConstraintOp::Eq.has_value());
611 }
612}