From 625cf005fd1c39b8b277d849ad7b7d7625d7faa0 Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Mon, 12 May 2025 15:36:34 +0300 Subject: [PATCH] Add some utilities to constraint related structs --- core/translate/main_loop.rs | 8 +---- core/translate/optimizer/constraints.rs | 48 +++++++++++++++++++------ core/translate/optimizer/mod.rs | 42 ++++------------------ core/translate/plan.rs | 2 +- 4 files changed, 46 insertions(+), 54 deletions(-) diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index 5e8c5908a..bf30b8508 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -433,13 +433,7 @@ pub fn open_loop( // Rowid equality point lookups are handled with a SeekRowid instruction which does not loop, since it is a single row lookup. if let Search::RowidEq { cmp_expr } = search { let src_reg = program.alloc_register(); - translate_expr( - program, - Some(tables), - &cmp_expr.expr, - src_reg, - &t_ctx.resolver, - )?; + translate_expr(program, Some(tables), cmp_expr, src_reg, &t_ctx.resolver)?; program.emit_insn(Insn::SeekRowid { cursor_id: table_cursor_id .expect("Search::RowidEq requires a table cursor"), diff --git a/core/translate/optimizer/constraints.rs b/core/translate/optimizer/constraints.rs index bb150c19b..a7272ce86 100644 --- a/core/translate/optimizer/constraints.rs +++ b/core/translate/optimizer/constraints.rs @@ -50,6 +50,28 @@ pub struct Constraint { pub selectivity: f64, } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum BinaryExprSide { + Lhs, + Rhs, +} + +impl Constraint { + /// Get the constraining expression, e.g. '2+3' from 't.x = 2+3' + pub fn get_constraining_expr(&self, where_clause: &[WhereTerm]) -> ast::Expr { + let (idx, side) = self.where_clause_pos; + let where_term = &where_clause[idx]; + let Ok(Some((lhs, _, rhs))) = as_binary_components(&where_term.expr) else { + panic!("Expected a valid binary expression"); + }; + if side == BinaryExprSide::Lhs { + lhs.clone() + } else { + rhs.clone() + } + } +} + #[derive(Debug, Clone)] /// A reference to a [Constraint] in a [TableConstraints]. /// @@ -62,7 +84,20 @@ pub struct ConstraintRef { /// The sort order of the constrained column in the index. Always ascending for rowid indices. pub sort_order: SortOrder, } -#[derive(Debug, Clone)] + +impl ConstraintRef { + /// Convert the constraint to a column usable in a [crate::translate::plan::SeekDef::key]. + pub fn as_seek_key_column( + &self, + constraints: &[Constraint], + where_clause: &[WhereTerm], + ) -> (ast::Expr, SortOrder) { + let constraint = &constraints[self.constraint_vec_pos]; + let constraining_expr = constraint.get_constraining_expr(where_clause); + (constraining_expr, self.sort_order) + } +} + /// A collection of [ConstraintRef]s for a given index, or if index is None, for the table's rowid index. /// For example, given a table `T (x,y,z)` with an index `T_I (y desc,z)`, take the following query: /// ```sql @@ -87,6 +122,7 @@ pub struct ConstraintRef { /// ], /// } /// +#[derive(Debug)] pub struct ConstraintUseCandidate { /// The index that may be used to satisfy the constraints. If none, the table's rowid index is used. pub index: Option>, @@ -104,16 +140,6 @@ pub struct TableConstraints { pub candidates: Vec, } -/// Helper enum for [Constraint] to indicate which side of a binary comparison expression is being compared to the index column. -/// For example, if the where clause is "WHERE x = 10" and there's an index on x, -/// the [Constraint] for the where clause term "x = 10" will have a [BinaryExprSide::Rhs] -/// because the right hand side expression "10" is being compared to the index column "x". -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BinaryExprSide { - Lhs, - Rhs, -} - /// In lieu of statistics, we estimate that an equality filter will reduce the output set to 1% of its size. const SELECTIVITY_EQ: f64 = 0.01; /// In lieu of statistics, we estimate that a range filter will reduce the output set to 40% of its size. diff --git a/core/translate/optimizer/mod.rs b/core/translate/optimizer/mod.rs index 3eea86770..0353d7b84 100644 --- a/core/translate/optimizer/mod.rs +++ b/core/translate/optimizer/mod.rs @@ -1,8 +1,7 @@ use std::{cell::RefCell, cmp::Ordering, collections::HashMap, sync::Arc}; use constraints::{ - constraints_from_where_clause, usable_constraints_for_join_order, BinaryExprSide, Constraint, - ConstraintRef, + constraints_from_where_clause, usable_constraints_for_join_order, Constraint, ConstraintRef, }; use cost::Cost; use join::{compute_best_join_order, BestJoinOrderResult}; @@ -12,7 +11,7 @@ use order::{compute_order_target, plan_satisfies_order_target, EliminatesSort}; use crate::{ parameters::PARAM_PREFIX, schema::{Index, IndexColumn, Schema}, - translate::{expr::as_binary_components, plan::TerminationKey}, + translate::plan::TerminationKey, types::SeekOp, Result, }; @@ -311,21 +310,7 @@ fn optimize_table_access( [constraint_refs[0].constraint_vec_pos]; table_references[table_number].op = match constraint.operator { ast::Operator::Equals => Operation::Search(Search::RowidEq { - cmp_expr: { - let (idx, side) = constraint.where_clause_pos; - let Some((lhs, _, rhs)) = as_binary_components(&where_clause[idx].expr)? - else { - panic!("Expected a binary expression"); - }; - let where_term = WhereTerm { - expr: match side { - BinaryExprSide::Lhs => lhs.clone(), - BinaryExprSide::Rhs => rhs.clone(), - }, - from_outer_join: where_clause[idx].from_outer_join.clone(), - }; - where_term - }, + cmp_expr: constraint.get_constraining_expr(where_clause), }), _ => Operation::Search(Search::Seek { index: None, @@ -813,23 +798,10 @@ pub fn build_seek_def_from_constraints( "cannot build seek def from empty list of constraint refs" ); // Extract the key values and operators - let mut key = Vec::with_capacity(constraint_refs.len()); - - for cref in constraint_refs { - // Extract the other expression from the binary WhereTerm (i.e. the one being compared to the index column) - let constraint = &constraints[cref.constraint_vec_pos]; - let (where_idx, side) = constraint.where_clause_pos; - let where_term = &where_clause[where_idx]; - let Some((lhs, _, rhs)) = as_binary_components(&where_term.expr)? else { - panic!("Expected a binary expression"); - }; - let cmp_expr = if side == BinaryExprSide::Lhs { - lhs.clone() - } else { - rhs.clone() - }; - key.push((cmp_expr, cref.sort_order)); - } + let key = constraint_refs + .iter() + .map(|cref| cref.as_seek_key_column(constraints, where_clause)) + .collect(); // We know all but potentially the last term is an equality, so we can use the operator of the last term // to form the SeekOp diff --git a/core/translate/plan.rs b/core/translate/plan.rs index 50e325832..2a047b3a1 100644 --- a/core/translate/plan.rs +++ b/core/translate/plan.rs @@ -782,7 +782,7 @@ pub struct TerminationKey { #[derive(Clone, Debug)] pub enum Search { /// A rowid equality point lookup. This is a special case that uses the SeekRowid bytecode instruction and does not loop. - RowidEq { cmp_expr: WhereTerm }, + RowidEq { cmp_expr: ast::Expr }, /// A search on a table btree (via `rowid`) or a secondary index search. Uses bytecode instructions like SeekGE, SeekGT etc. Seek { index: Option>,