diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index b7bb20083..085068c32 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -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::>(); // TODO: get proper order_by information to pass to the vtab. diff --git a/core/translate/plan.rs b/core/translate/plan.rs index e84f6a28d..3b2554a48 100644 --- a/core/translate/plan.rs +++ b/core/translate/plan.rs @@ -161,14 +161,14 @@ pub fn convert_where_to_vtab_constraint( term: &WhereTerm, table_index: usize, pred_idx: usize, -) -> Option { +) -> Result> { 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 { 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), diff --git a/core/util.rs b/core/util.rs index 3415aefdc..0f71089ec 100644 --- a/core/util.rs +++ b/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 { + 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)]