mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-26 12:34:22 +01:00
Merge 'feat(optimizer): eliminate between statement' from KaguraMilet
Rewrite `Y BETWEEN X AND Z` as `X <= Y AND Y <= Z`. And due to the support of this optimization rule, limbo should now be able to execute the `BETWEEN AND` statement. Closes #490
This commit is contained in:
@@ -15,6 +15,7 @@ use super::plan::{
|
||||
* but having them separate makes them easier to understand
|
||||
*/
|
||||
pub fn optimize_plan(mut select_plan: Plan) -> Result<Plan> {
|
||||
eliminate_between(&mut select_plan.source, &mut select_plan.where_clause)?;
|
||||
if let ConstantConditionEliminationResult::ImpossibleCondition =
|
||||
eliminate_constants(&mut select_plan.source, &mut select_plan.where_clause)?
|
||||
{
|
||||
@@ -498,6 +499,46 @@ fn push_scan_direction(operator: &mut SourceOperator, direction: &Direction) {
|
||||
}
|
||||
}
|
||||
|
||||
fn eliminate_between(
|
||||
operator: &mut SourceOperator,
|
||||
where_clauses: &mut Option<Vec<ast::Expr>>,
|
||||
) -> Result<()> {
|
||||
if let Some(predicates) = where_clauses {
|
||||
*predicates = predicates.drain(..).map(convert_between_expr).collect();
|
||||
}
|
||||
|
||||
match operator {
|
||||
SourceOperator::Join {
|
||||
left,
|
||||
right,
|
||||
predicates,
|
||||
..
|
||||
} => {
|
||||
eliminate_between(left, where_clauses)?;
|
||||
eliminate_between(right, where_clauses)?;
|
||||
|
||||
if let Some(predicates) = predicates {
|
||||
*predicates = predicates.drain(..).map(convert_between_expr).collect();
|
||||
}
|
||||
}
|
||||
SourceOperator::Scan {
|
||||
predicates: Some(preds),
|
||||
..
|
||||
} => {
|
||||
*preds = preds.drain(..).map(convert_between_expr).collect();
|
||||
}
|
||||
SourceOperator::Search {
|
||||
predicates: Some(preds),
|
||||
..
|
||||
} => {
|
||||
*preds = preds.drain(..).map(convert_between_expr).collect();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ConstantPredicate {
|
||||
AlwaysTrue,
|
||||
@@ -808,6 +849,65 @@ pub fn try_extract_index_search_expression(
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_between_expr(expr: ast::Expr) -> ast::Expr {
|
||||
match expr {
|
||||
ast::Expr::Between {
|
||||
lhs,
|
||||
not,
|
||||
start,
|
||||
end,
|
||||
} => {
|
||||
// Convert `y NOT BETWEEN x AND z` to `x > y OR y > z`
|
||||
let (lower_op, upper_op) = if not {
|
||||
(ast::Operator::Greater, ast::Operator::Greater)
|
||||
} else {
|
||||
// Convert `y BETWEEN x AND z` to `x <= y AND y <= z`
|
||||
(ast::Operator::LessEquals, ast::Operator::LessEquals)
|
||||
};
|
||||
|
||||
let lower_bound = ast::Expr::Binary(start, lower_op, lhs.clone());
|
||||
let upper_bound = ast::Expr::Binary(lhs, upper_op, end);
|
||||
|
||||
if not {
|
||||
ast::Expr::Binary(
|
||||
Box::new(lower_bound),
|
||||
ast::Operator::Or,
|
||||
Box::new(upper_bound),
|
||||
)
|
||||
} else {
|
||||
ast::Expr::Binary(
|
||||
Box::new(lower_bound),
|
||||
ast::Operator::And,
|
||||
Box::new(upper_bound),
|
||||
)
|
||||
}
|
||||
}
|
||||
ast::Expr::Parenthesized(mut exprs) => {
|
||||
ast::Expr::Parenthesized(exprs.drain(..).map(convert_between_expr).collect())
|
||||
}
|
||||
// Process other expressions recursively
|
||||
ast::Expr::Binary(lhs, op, rhs) => ast::Expr::Binary(
|
||||
Box::new(convert_between_expr(*lhs)),
|
||||
op,
|
||||
Box::new(convert_between_expr(*rhs)),
|
||||
),
|
||||
ast::Expr::FunctionCall {
|
||||
name,
|
||||
distinctness,
|
||||
args,
|
||||
order_by,
|
||||
filter_over,
|
||||
} => ast::Expr::FunctionCall {
|
||||
name,
|
||||
distinctness,
|
||||
args: args.map(|args| args.into_iter().map(convert_between_expr).collect()),
|
||||
order_by,
|
||||
filter_over,
|
||||
},
|
||||
_ => expr,
|
||||
}
|
||||
}
|
||||
|
||||
trait TakeOwnership {
|
||||
fn take_ownership(&mut self) -> Self;
|
||||
}
|
||||
|
||||
@@ -317,3 +317,20 @@ do_execsql_test where-age-index-seek-regression-test {
|
||||
do_execsql_test where-age-index-seek-regression-test-2 {
|
||||
select count(1) from users where age > 0;
|
||||
} {10000}
|
||||
|
||||
do_execsql_test where-simple-between {
|
||||
SELECT * FROM products WHERE price BETWEEN 70 AND 100;
|
||||
} {1|hat|79.0
|
||||
2|cap|82.0
|
||||
5|sweatshirt|74.0
|
||||
6|shorts|70.0
|
||||
7|jeans|78.0
|
||||
8|sneakers|82.0
|
||||
11|accessories|81.0}
|
||||
|
||||
do_execsql_test between-price-range-with-names {
|
||||
SELECT * FROM products
|
||||
WHERE (price BETWEEN 70 AND 100)
|
||||
AND (name = 'sweatshirt' OR name = 'sneakers');
|
||||
} {5|sweatshirt|74.0
|
||||
8|sneakers|82.0}
|
||||
|
||||
Reference in New Issue
Block a user