Add some utilities to constraint related structs

This commit is contained in:
Jussi Saurio
2025-05-12 15:36:34 +03:00
parent 71ab3d57d8
commit 625cf005fd
4 changed files with 46 additions and 54 deletions

View File

@@ -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"),

View File

@@ -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<Arc<Index>>,
@@ -104,16 +140,6 @@ pub struct TableConstraints {
pub candidates: Vec<ConstraintUseCandidate>,
}
/// 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.

View File

@@ -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

View File

@@ -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<Arc<Index>>,