mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-07 01:04:26 +01:00
refactor: use walk_expr() in resolving vtab constraints
This commit is contained in:
@@ -365,6 +365,7 @@ pub fn open_loop(
|
||||
.filter_map(|(i, p)| {
|
||||
// Build ConstraintInfo from the predicates
|
||||
convert_where_to_vtab_constraint(p, table_index, i)
|
||||
.unwrap_or(None)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// TODO: get proper order_by information to pass to the vtab.
|
||||
|
||||
@@ -161,14 +161,14 @@ pub fn convert_where_to_vtab_constraint(
|
||||
term: &WhereTerm,
|
||||
table_index: usize,
|
||||
pred_idx: usize,
|
||||
) -> Option<ConstraintInfo> {
|
||||
) -> Result<Option<ConstraintInfo>> {
|
||||
if term.from_outer_join.is_some() {
|
||||
return None;
|
||||
return Ok(None);
|
||||
}
|
||||
let Expr::Binary(lhs, op, rhs) = &term.expr else {
|
||||
return None;
|
||||
return Ok(None);
|
||||
};
|
||||
let expr_is_ready = |e: &Expr| -> bool { can_pushdown_predicate(e, table_index) };
|
||||
let expr_is_ready = |e: &Expr| -> Result<bool> { can_pushdown_predicate(e, table_index) };
|
||||
let (vcol_idx, op_for_vtab, usable, is_rhs) = match (&**lhs, &**rhs) {
|
||||
(
|
||||
Expr::Column {
|
||||
@@ -186,7 +186,7 @@ pub fn convert_where_to_vtab_constraint(
|
||||
let vtab_on_l = *tbl_l == table_index;
|
||||
let vtab_on_r = *tbl_r == table_index;
|
||||
if vtab_on_l == vtab_on_r {
|
||||
return None; // either both or none -> not convertible
|
||||
return Ok(None); // either both or none -> not convertible
|
||||
}
|
||||
|
||||
if vtab_on_l {
|
||||
@@ -203,26 +203,30 @@ pub fn convert_where_to_vtab_constraint(
|
||||
(
|
||||
column,
|
||||
op,
|
||||
expr_is_ready(other), // literal / earlier‑table / deterministic func ?
|
||||
expr_is_ready(other)?, // literal / earlier‑table / deterministic func ?
|
||||
false,
|
||||
)
|
||||
}
|
||||
(other, Expr::Column { table, column, .. }) if *table == table_index => (
|
||||
column,
|
||||
&reverse_operator(op).unwrap_or(*op),
|
||||
expr_is_ready(other),
|
||||
expr_is_ready(other)?,
|
||||
true,
|
||||
),
|
||||
|
||||
_ => return None, // does not involve the virtual table at all
|
||||
_ => return Ok(None), // does not involve the virtual table at all
|
||||
};
|
||||
|
||||
Some(ConstraintInfo {
|
||||
let Some(op) = to_ext_constraint_op(op_for_vtab) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
Ok(Some(ConstraintInfo {
|
||||
column_index: *vcol_idx as u32,
|
||||
op: to_ext_constraint_op(op_for_vtab)?,
|
||||
op,
|
||||
usable,
|
||||
plan_info: ConstraintInfo::pack_plan_info(pred_idx as u32, is_rhs),
|
||||
})
|
||||
}))
|
||||
}
|
||||
/// The loop index where to evaluate the condition.
|
||||
/// For example, in `SELECT * FROM u JOIN p WHERE u.id = 5`, the condition can already be evaluated at the first loop (idx 0),
|
||||
|
||||
53
core/util.rs
53
core/util.rs
@@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
function::Func,
|
||||
schema::{self, Column, Schema, Type},
|
||||
translate::collate::CollationSeq,
|
||||
translate::{collate::CollationSeq, expr::walk_expr},
|
||||
types::{Value, ValueType},
|
||||
LimboError, OpenFlags, Result, Statement, StepResult, SymbolTable, IO,
|
||||
};
|
||||
@@ -584,35 +583,27 @@ pub fn columns_from_create_table_body(body: &ast::CreateTableBody) -> crate::Res
|
||||
|
||||
/// 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, table_idx: usize) -> bool {
|
||||
match expr {
|
||||
Expr::Literal(_) => true,
|
||||
Expr::Column { table, .. } => *table <= table_idx,
|
||||
Expr::Binary(lhs, _, rhs) => {
|
||||
can_pushdown_predicate(lhs, table_idx) && can_pushdown_predicate(rhs, table_idx)
|
||||
}
|
||||
Expr::Parenthesized(exprs) => can_pushdown_predicate(exprs.first().unwrap(), table_idx),
|
||||
Expr::Unary(_, expr) => can_pushdown_predicate(expr, table_idx),
|
||||
Expr::FunctionCall { args, name, .. } => {
|
||||
let function = crate::function::Func::resolve_function(
|
||||
&name.0,
|
||||
args.as_ref().map_or(0, |a| a.len()),
|
||||
);
|
||||
// is deterministic
|
||||
matches!(function, Ok(Func::Scalar(_)))
|
||||
}
|
||||
Expr::Like { lhs, rhs, .. } => {
|
||||
can_pushdown_predicate(lhs, table_idx) && can_pushdown_predicate(rhs, table_idx)
|
||||
}
|
||||
Expr::Between {
|
||||
lhs, start, end, ..
|
||||
} => {
|
||||
can_pushdown_predicate(lhs, table_idx)
|
||||
&& can_pushdown_predicate(start, table_idx)
|
||||
&& can_pushdown_predicate(end, table_idx)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
pub fn can_pushdown_predicate(top_level_expr: &Expr, table_idx: usize) -> Result<bool> {
|
||||
let mut can_pushdown = true;
|
||||
walk_expr(top_level_expr, &mut |expr: &Expr| -> Result<()> {
|
||||
match expr {
|
||||
Expr::Column { table, .. } | Expr::RowId { table, .. } => {
|
||||
can_pushdown &= *table <= table_idx;
|
||||
}
|
||||
Expr::FunctionCall { args, name, .. } => {
|
||||
let function = crate::function::Func::resolve_function(
|
||||
&name.0,
|
||||
args.as_ref().map_or(0, |a| a.len()),
|
||||
)?;
|
||||
// is deterministic
|
||||
can_pushdown &= function.is_deterministic();
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(can_pushdown)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
|
||||
Reference in New Issue
Block a user