diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index 00c943b9b..86ce7415f 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -291,8 +291,7 @@ pub fn open_loop( }); } } else if let Some(vtab) = table.virtual_table() { - // Collect usable constraints and track which predicate each came from - // Virtual tables may be used either as VTab or TVF, distinguished by vtab.name. + // Virtual tables may be used either as VTab or TVF let (start_reg, count, maybe_idx_str, maybe_idx_int) = if vtab .kind .eq(&VTabKind::VirtualTable) @@ -308,13 +307,14 @@ pub fn open_loop( } let constraints: Vec<_> = converted_constraints.iter().map(|(c, _)| *c).collect(); - let order_by = vec![OrderByInfo { + let order_by = [OrderByInfo { column_index: *t_ctx .result_column_indexes_in_orderby_sorter .first() .unwrap_or(&0) as u32, desc: matches!(iter_dir, IterationDirection::Backwards), }]; + // Call xBestIndex method on the underlying vtable. let index_info = vtab.best_index(&constraints, &order_by); // Determine the number of VFilter arguments (constraints with an argv_index). @@ -330,7 +330,7 @@ pub fn open_loop( if let Some(argv_index) = usage.argv_index { if let Some((_, pred)) = converted_constraints.get(i) { if let ast::Expr::Binary(lhs, _, rhs) = &pred.expr { - let literal_expr = match (&**lhs, &**rhs) { + let expr = match (&**lhs, &**rhs) { (ast::Expr::Column { .. }, lit) => lit, (lit, ast::Expr::Column { .. }) => lit, _ => continue, @@ -340,7 +340,7 @@ pub fn open_loop( translate_expr( program, Some(tables), - literal_expr, + expr, target_reg, &t_ctx.resolver, )?; @@ -359,7 +359,6 @@ pub fn open_loop( } else { None }; - // Record (in t_ctx) the indices of predicates that best_index tells us to omit. // Here we insert directly into t_ctx.omit_predicates for (j, usage) in index_info.constraint_usages.iter().enumerate() { diff --git a/core/translate/plan.rs b/core/translate/plan.rs index faec051f1..3b60775a7 100644 --- a/core/translate/plan.rs +++ b/core/translate/plan.rs @@ -79,6 +79,10 @@ impl WhereTerm { use crate::ast::{Expr, Operator}; +// This function takes an operator and returns the operator you would obtain if the operands were swapped. +// e.g. "literal < column" +// which is not the canonical order for constraint pushdown. +// This function will return > so that the expression can be treated as if it were written "column > literal" fn reverse_operator(op: &Operator) -> Option { match op { Operator::Equals => Some(Operator::Equals), @@ -97,7 +101,7 @@ fn reverse_operator(op: &Operator) -> Option { /// It determines whether or not it involves the given table and whether or not it can /// be converted into a ConstraintInfo which can be passed to the vtab module's xBestIndex /// method, which will possibly calculate some information to improve the query plan, that we can send -/// back to it as arguments for the VFilter operation. Perhaps we should save the exact Expr for which a relevant column +/// back to it as arguments for the VFilter operation. /// is going to be filtered against: e.g: /// 'SELECT key, value FROM vtab WHERE key = 'some_key'; /// we need to send the OwnedValue('some_key') as an argument to VFilter, and possibly omit it from @@ -126,6 +130,8 @@ pub fn try_convert_to_constraint_info( if table != &table_index { return None; } + // if the column is on the rhs, swap the operands and possibly + // the operator if it's a logical comparison. (rhs, lhs, &reverse_operator(op).unwrap_or(*op)) } _ => { diff --git a/core/util.rs b/core/util.rs index 8ab86c70a..5d0010423 100644 --- a/core/util.rs +++ b/core/util.rs @@ -566,10 +566,11 @@ pub fn columns_from_create_table_body(body: &ast::CreateTableBody) -> crate::Res .collect::>()) } +/// This function checks if a given expression is a constant value that can be pushed down to the database engine. +/// It is expected to be called with the other half of a binary expression with an Expr::Column pub fn can_pushdown_predicate(expr: &Expr) -> bool { match expr { Expr::Literal(_) => true, - Expr::Column { .. } => true, Expr::Binary(lhs, _, rhs) => can_pushdown_predicate(lhs) && can_pushdown_predicate(rhs), Expr::Parenthesized(exprs) => can_pushdown_predicate(exprs.first().unwrap()), Expr::Unary(_, expr) => can_pushdown_predicate(expr), @@ -589,11 +590,6 @@ pub fn can_pushdown_predicate(expr: &Expr) -> bool { && can_pushdown_predicate(start) && can_pushdown_predicate(end) } - Expr::Id(_) => true, - Expr::Name(_) => true, - Expr::Qualified(_, _) => true, - Expr::DoublyQualified(_, _, _) => true, - Expr::InTable { lhs, .. } => can_pushdown_predicate(lhs), _ => false, } } diff --git a/extensions/core/src/vtabs.rs b/extensions/core/src/vtabs.rs index ae3fc7429..c2f6620d1 100644 --- a/extensions/core/src/vtabs.rs +++ b/extensions/core/src/vtabs.rs @@ -158,19 +158,30 @@ pub enum ConstraintOp { #[repr(C)] #[derive(Copy, Clone)] +/// Describes an ORDER BY clause in a query involving a virtual table. +/// Passed along with the constraints to xBestIndex. pub struct OrderByInfo { + /// The index of the column referenced in the ORDER BY clause. pub column_index: u32, + /// Whether or not the clause is in descending order. pub desc: bool, } +/// The internal (core) representation of an 'index' on a virtual table. +/// Returned from xBestIndex and then processed and passed to VFilter. #[derive(Debug, Clone)] pub struct IndexInfo { + /// The index number, used to identify the index internally by the VTab pub idx_num: i32, + /// Optional index name. these are passed to vfilter in a tuple (idx_num, idx_str) pub idx_str: Option, + /// Whether the index is used for order by pub order_by_consumed: bool, /// TODO: for eventual cost based query planning pub estimated_cost: f64, + /// Estimated number of rows that the query will return pub estimated_rows: u32, + /// List of constraints that can be used to optimize the query. pub constraint_usages: Vec, } impl Default for IndexInfo { @@ -245,6 +256,7 @@ impl IndexInfo { #[repr(C)] #[derive(Clone, Debug)] +/// FFI representation of IndexInfo. pub struct ExtIndexInfo { pub idx_num: i32, pub idx_str: *const u8, @@ -256,17 +268,27 @@ pub struct ExtIndexInfo { pub constraint_usage_len: usize, } +/// Returned from xBestIndex to describe how the virtual table +/// can use the constraints in the WHERE clause of a query. #[derive(Debug, Clone, Copy)] pub struct ConstraintUsage { - pub argv_index: Option, // 1-based index into VFilter args - pub omit: bool, // if true, core skips checking it again + /// 1 based index of the argument in the WHERE clause. + pub argv_index: Option, + /// If true, core can omit this constraint in the vdbe layer. + pub omit: bool, } #[derive(Clone, Copy, Debug)] #[repr(C)] +/// The primary argument to xBestIndex, which describes a constraint +/// in a query involving a virtual table. pub struct ConstraintInfo { + /// The index of the column referenced in the WHERE clause. pub column_index: u32, + /// The operator used in the clause. pub op: ConstraintOp, + /// Whether or not constraint is garaunteed to be enforced. pub usable: bool, + /// pub pred_idx: usize, }