From d653f550e28e3acfe6a17afbdcdfa8b6d36c726b Mon Sep 17 00:00:00 2001 From: jussisaurio Date: Sun, 25 Aug 2024 10:43:52 +0300 Subject: [PATCH 1/2] Don't do any I/O if top level operator is Nothing --- core/translate/optimizer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/translate/optimizer.rs b/core/translate/optimizer.rs index 4cf598cf9..4859e6df8 100644 --- a/core/translate/optimizer.rs +++ b/core/translate/optimizer.rs @@ -16,7 +16,13 @@ pub fn optimize_plan(mut select_plan: Plan) -> Result { &mut select_plan.root_operator, &select_plan.referenced_tables, )?; - eliminate_constants(&mut select_plan.root_operator)?; + let viable_plan = eliminate_constants(&mut select_plan.root_operator)?; + if !viable_plan { + return Ok(Plan { + root_operator: Operator::Nothing, + referenced_tables: vec![], + }); + } use_indexes( &mut select_plan.root_operator, &select_plan.referenced_tables, From d1059da9f19320c1bc800a84dfd10ec4d21f1164 Mon Sep 17 00:00:00 2001 From: jussisaurio Date: Sun, 25 Aug 2024 11:15:47 +0300 Subject: [PATCH 2/2] Return enum instead of bool for better documentation --- core/translate/optimizer.rs | 82 +++++++++++++++++++++++-------------- core/translate/plan.rs | 1 + 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/core/translate/optimizer.rs b/core/translate/optimizer.rs index 4859e6df8..7b9d68b01 100644 --- a/core/translate/optimizer.rs +++ b/core/translate/optimizer.rs @@ -16,8 +16,9 @@ pub fn optimize_plan(mut select_plan: Plan) -> Result { &mut select_plan.root_operator, &select_plan.referenced_tables, )?; - let viable_plan = eliminate_constants(&mut select_plan.root_operator)?; - if !viable_plan { + if eliminate_constants(&mut select_plan.root_operator)? + == ConstantConditionEliminationResult::ImpossibleCondition + { return Ok(Plan { root_operator: Operator::Nothing, referenced_tables: vec![], @@ -122,9 +123,15 @@ fn use_indexes( } } +#[derive(Debug, PartialEq, Clone)] +enum ConstantConditionEliminationResult { + Continue, + ImpossibleCondition, +} + // removes predicates that are always true -// returns false if there is an impossible predicate that is always false -fn eliminate_constants(operator: &mut Operator) -> Result { +// returns a ConstantEliminationResult indicating whether any predicates are always false +fn eliminate_constants(operator: &mut Operator) -> Result { match operator { Operator::Filter { source, predicates, .. @@ -135,7 +142,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result { if predicate.is_always_true()? { predicates.remove(i); } else if predicate.is_always_false()? { - return Ok(false); + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } else { i += 1; } @@ -148,7 +155,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result { eliminate_constants(source)?; } - return Ok(true); + return Ok(ConstantConditionEliminationResult::Continue); } Operator::Join { left, @@ -157,15 +164,19 @@ fn eliminate_constants(operator: &mut Operator) -> Result { outer, .. } => { - if !eliminate_constants(left)? { - return Ok(false); + if eliminate_constants(left)? == ConstantConditionEliminationResult::ImpossibleCondition + { + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } - if !eliminate_constants(right)? && !*outer { - return Ok(false); + if eliminate_constants(right)? + == ConstantConditionEliminationResult::ImpossibleCondition + && !*outer + { + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } if predicates.is_none() { - return Ok(true); + return Ok(ConstantConditionEliminationResult::Continue); } let predicates = predicates.as_mut().unwrap(); @@ -176,20 +187,22 @@ fn eliminate_constants(operator: &mut Operator) -> Result { if predicate.is_always_true()? { predicates.remove(i); } else if predicate.is_always_false()? && !*outer { - return Ok(false); + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } else { i += 1; } } - return Ok(true); + return Ok(ConstantConditionEliminationResult::Continue); } Operator::Aggregate { source, .. } => { - let ok = eliminate_constants(source)?; - if !ok { + if eliminate_constants(source)? + == ConstantConditionEliminationResult::ImpossibleCondition + { *source = Box::new(Operator::Nothing); } - return Ok(ok); + // Aggregation operator can return a row even if the source is empty e.g. count(1) from users where 0 + return Ok(ConstantConditionEliminationResult::Continue); } Operator::SeekRowid { rowid_predicate, @@ -203,7 +216,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result { if predicate.is_always_true()? { predicates.remove(i); } else if predicate.is_always_false()? { - return Ok(false); + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } else { i += 1; } @@ -211,31 +224,38 @@ fn eliminate_constants(operator: &mut Operator) -> Result { } if rowid_predicate.is_always_false()? { - return Ok(false); + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } - return Ok(true); + return Ok(ConstantConditionEliminationResult::Continue); } Operator::Limit { source, .. } => { - let ok = eliminate_constants(source)?; - if !ok { + let constant_elimination_result = eliminate_constants(source)?; + if constant_elimination_result + == ConstantConditionEliminationResult::ImpossibleCondition + { *operator = Operator::Nothing; } - return Ok(ok); + return Ok(constant_elimination_result); } Operator::Order { source, .. } => { - let ok = eliminate_constants(source)?; - if !ok { + if eliminate_constants(source)? + == ConstantConditionEliminationResult::ImpossibleCondition + { *operator = Operator::Nothing; + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } - return Ok(true); + return Ok(ConstantConditionEliminationResult::Continue); } Operator::Projection { source, .. } => { - let ok = eliminate_constants(source)?; - if !ok { + if eliminate_constants(source)? + == ConstantConditionEliminationResult::ImpossibleCondition + { *operator = Operator::Nothing; + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } - return Ok(ok); + + return Ok(ConstantConditionEliminationResult::Continue); } Operator::Scan { predicates, .. } => { if let Some(ps) = predicates { @@ -245,7 +265,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result { if predicate.is_always_true()? { ps.remove(i); } else if predicate.is_always_false()? { - return Ok(false); + return Ok(ConstantConditionEliminationResult::ImpossibleCondition); } else { i += 1; } @@ -255,9 +275,9 @@ fn eliminate_constants(operator: &mut Operator) -> Result { *predicates = None; } } - return Ok(true); + return Ok(ConstantConditionEliminationResult::Continue); } - Operator::Nothing => return Ok(true), + Operator::Nothing => return Ok(ConstantConditionEliminationResult::Continue), } } diff --git a/core/translate/plan.rs b/core/translate/plan.rs index e38ae3ff6..eb9f321ca 100644 --- a/core/translate/plan.rs +++ b/core/translate/plan.rs @@ -8,6 +8,7 @@ use sqlite3_parser::ast; use crate::{function::AggFunc, schema::BTreeTable, util::normalize_ident, Result}; +#[derive(Debug)] pub struct Plan { pub root_operator: Operator, pub referenced_tables: Vec<(Rc, String)>,