Merge 'Don't do any I/O if top level operator is Nothing' from Jussi Saurio

Small tweak

Before:

```
limbo> explain select * from users where 0;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     21    0                    0   Start at 21
1     OpenReadAsync      0     2     0                    0   table=users, root=2
2     OpenReadAwait      0     0     0                    0
3     RewindAsync        0     0     0                    0
4     RewindAwait        0     20    0                    0   Rewind table users
5       Integer          0     1     0                    0   r[1]=0
6       IfNot            1     18    1                    0   if !r[1] goto 18
7       RowId            0     2     0                    0   r[2]=users.rowid
8       Column           0     1     3                    0   r[3]=users.first_name
9       Column           0     2     4                    0   r[4]=users.last_name
10      Column           0     3     5                    0   r[5]=users.email
11      Column           0     4     6                    0   r[6]=users.phone_number
12      Column           0     5     7                    0   r[7]=users.address
13      Column           0     6     8                    0   r[8]=users.city
14      Column           0     7     9                    0   r[9]=users.state
15      Column           0     8     10                   0   r[10]=users.zipcode
16      Column           0     9     11                   0   r[11]=users.age
17      ResultRow        2     10    0                    0   output=r[2..11]
18    NextAsync          0     0     0                    0
19    NextAwait          0     4     0                    0
20    Halt               0     0     0                    0
21    Transaction        0     0     0                    0
22    Goto               0     1     0                    0
```

After:

```
limbo> explain select * from users where 0;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     2     0                    0   Start at 2
1     Halt               0     0     0                    0
2     Transaction        0     0     0                    0
3     Goto               0     1     0                    0
```

Closes #306
This commit is contained in:
Pekka Enberg
2024-08-25 13:23:20 +03:00
2 changed files with 57 additions and 30 deletions

View File

@@ -16,7 +16,14 @@ pub fn optimize_plan(mut select_plan: Plan) -> Result<Plan> {
&mut select_plan.root_operator,
&select_plan.referenced_tables,
)?;
eliminate_constants(&mut select_plan.root_operator)?;
if eliminate_constants(&mut select_plan.root_operator)?
== ConstantConditionEliminationResult::ImpossibleCondition
{
return Ok(Plan {
root_operator: Operator::Nothing,
referenced_tables: vec![],
});
}
use_indexes(
&mut select_plan.root_operator,
&select_plan.referenced_tables,
@@ -116,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<bool> {
// returns a ConstantEliminationResult indicating whether any predicates are always false
fn eliminate_constants(operator: &mut Operator) -> Result<ConstantConditionEliminationResult> {
match operator {
Operator::Filter {
source, predicates, ..
@@ -129,7 +142,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
if predicate.is_always_true()? {
predicates.remove(i);
} else if predicate.is_always_false()? {
return Ok(false);
return Ok(ConstantConditionEliminationResult::ImpossibleCondition);
} else {
i += 1;
}
@@ -142,7 +155,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
eliminate_constants(source)?;
}
return Ok(true);
return Ok(ConstantConditionEliminationResult::Continue);
}
Operator::Join {
left,
@@ -151,15 +164,19 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
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();
@@ -170,20 +187,22 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
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,
@@ -197,7 +216,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
if predicate.is_always_true()? {
predicates.remove(i);
} else if predicate.is_always_false()? {
return Ok(false);
return Ok(ConstantConditionEliminationResult::ImpossibleCondition);
} else {
i += 1;
}
@@ -205,31 +224,38 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
}
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 {
@@ -239,7 +265,7 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
if predicate.is_always_true()? {
ps.remove(i);
} else if predicate.is_always_false()? {
return Ok(false);
return Ok(ConstantConditionEliminationResult::ImpossibleCondition);
} else {
i += 1;
}
@@ -249,9 +275,9 @@ fn eliminate_constants(operator: &mut Operator) -> Result<bool> {
*predicates = None;
}
}
return Ok(true);
return Ok(ConstantConditionEliminationResult::Continue);
}
Operator::Nothing => return Ok(true),
Operator::Nothing => return Ok(ConstantConditionEliminationResult::Continue),
}
}

View File

@@ -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<BTreeTable>, String)>,