diff --git a/core/lib.rs b/core/lib.rs index 0addaffc5..a3532237e 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -331,7 +331,7 @@ impl Connection { *select, &self.db.syms.borrow(), )?; - optimize_plan(&mut plan)?; + optimize_plan(&mut plan, &self.schema.borrow())?; println!("{}", plan); } _ => todo!(), diff --git a/core/translate/delete.rs b/core/translate/delete.rs index 8741e1e62..063aaad21 100644 --- a/core/translate/delete.rs +++ b/core/translate/delete.rs @@ -18,7 +18,7 @@ pub fn translate_delete( syms: &SymbolTable, ) -> Result<()> { let mut delete_plan = prepare_delete_plan(schema, tbl_name, where_clause, limit)?; - optimize_plan(&mut delete_plan)?; + optimize_plan(&mut delete_plan, schema)?; emit_program(program, delete_plan, syms) } @@ -55,7 +55,6 @@ pub fn prepare_delete_plan( order_by: None, limit: resolved_limit, offset: resolved_offset, - available_indexes: vec![], contains_constant_false_condition: false, }; diff --git a/core/translate/optimizer.rs b/core/translate/optimizer.rs index be93248a5..cfa66e9fc 100644 --- a/core/translate/optimizer.rs +++ b/core/translate/optimizer.rs @@ -1,18 +1,21 @@ -use std::rc::Rc; +use std::{collections::HashMap, rc::Rc}; use sqlite3_parser::ast; -use crate::{schema::Index, Result}; +use crate::{ + schema::{Index, Schema}, + Result, +}; use super::plan::{ DeletePlan, Direction, IterationDirection, Operation, Plan, Search, SelectPlan, TableReference, WhereTerm, }; -pub fn optimize_plan(plan: &mut Plan) -> Result<()> { +pub fn optimize_plan(plan: &mut Plan, schema: &Schema) -> Result<()> { match plan { - Plan::Select(plan) => optimize_select_plan(plan), - Plan::Delete(plan) => optimize_delete_plan(plan), + Plan::Select(plan) => optimize_select_plan(plan, schema), + Plan::Delete(plan) => optimize_delete_plan(plan, schema), } } @@ -21,8 +24,8 @@ pub fn optimize_plan(plan: &mut Plan) -> Result<()> { * TODO: these could probably be done in less passes, * but having them separate makes them easier to understand */ -fn optimize_select_plan(plan: &mut SelectPlan) -> Result<()> { - optimize_subqueries(plan)?; +fn optimize_select_plan(plan: &mut SelectPlan, schema: &Schema) -> Result<()> { + optimize_subqueries(plan, schema)?; rewrite_exprs_select(plan)?; if let ConstantConditionEliminationResult::ImpossibleCondition = eliminate_constant_conditions(&mut plan.where_clause)? @@ -33,16 +36,16 @@ fn optimize_select_plan(plan: &mut SelectPlan) -> Result<()> { use_indexes( &mut plan.table_references, - &plan.available_indexes, + &schema.indexes, &mut plan.where_clause, )?; - eliminate_unnecessary_orderby(plan)?; + eliminate_unnecessary_orderby(plan, schema)?; Ok(()) } -fn optimize_delete_plan(plan: &mut DeletePlan) -> Result<()> { +fn optimize_delete_plan(plan: &mut DeletePlan, schema: &Schema) -> Result<()> { rewrite_exprs_delete(plan)?; if let ConstantConditionEliminationResult::ImpossibleCondition = eliminate_constant_conditions(&mut plan.where_clause)? @@ -53,17 +56,17 @@ fn optimize_delete_plan(plan: &mut DeletePlan) -> Result<()> { use_indexes( &mut plan.table_references, - &plan.available_indexes, + &schema.indexes, &mut plan.where_clause, )?; Ok(()) } -fn optimize_subqueries(plan: &mut SelectPlan) -> Result<()> { +fn optimize_subqueries(plan: &mut SelectPlan, schema: &Schema) -> Result<()> { for table in plan.table_references.iter_mut() { if let Operation::Subquery { plan, .. } = &mut table.op { - optimize_select_plan(&mut *plan)?; + optimize_select_plan(&mut *plan, schema)?; } } @@ -73,7 +76,7 @@ fn optimize_subqueries(plan: &mut SelectPlan) -> Result<()> { fn query_is_already_ordered_by( table_references: &[TableReference], key: &mut ast::Expr, - available_indexes: &Vec>, + available_indexes: &HashMap>>, ) -> Result { let first_table = table_references.first(); if first_table.is_none() { @@ -86,10 +89,9 @@ fn query_is_already_ordered_by( Search::RowidEq { .. } => Ok(key.is_rowid_alias_of(0)), Search::RowidSearch { .. } => Ok(key.is_rowid_alias_of(0)), Search::IndexSearch { index, .. } => { - let index_idx = key.check_index_scan(0, &table_reference, available_indexes)?; - let index_is_the_same = index_idx - .map(|i| Rc::ptr_eq(&available_indexes[i], index)) - .unwrap_or(false); + let index_rc = key.check_index_scan(0, &table_reference, available_indexes)?; + let index_is_the_same = + index_rc.map(|irc| Rc::ptr_eq(index, &irc)).unwrap_or(false); Ok(index_is_the_same) } }, @@ -97,7 +99,7 @@ fn query_is_already_ordered_by( } } -fn eliminate_unnecessary_orderby(plan: &mut SelectPlan) -> Result<()> { +fn eliminate_unnecessary_orderby(plan: &mut SelectPlan, schema: &Schema) -> Result<()> { if plan.order_by.is_none() { return Ok(()); } @@ -115,7 +117,7 @@ fn eliminate_unnecessary_orderby(plan: &mut SelectPlan) -> Result<()> { let (key, direction) = o.first_mut().unwrap(); let already_ordered = - query_is_already_ordered_by(&plan.table_references, key, &plan.available_indexes)?; + query_is_already_ordered_by(&plan.table_references, key, &schema.indexes)?; if already_ordered { push_scan_direction(&mut plan.table_references[0], direction); @@ -136,7 +138,7 @@ fn eliminate_unnecessary_orderby(plan: &mut SelectPlan) -> Result<()> { */ fn use_indexes( table_references: &mut [TableReference], - available_indexes: &Vec>, + available_indexes: &HashMap>>, where_clause: &mut Vec, ) -> Result<()> { if where_clause.is_empty() { @@ -274,8 +276,8 @@ pub trait Optimizable { &mut self, table_index: usize, table_reference: &TableReference, - available_indexes: &[Rc], - ) -> Result>; + available_indexes: &HashMap>>, + ) -> Result>>; } impl Optimizable for ast::Expr { @@ -293,19 +295,23 @@ impl Optimizable for ast::Expr { &mut self, table_index: usize, table_reference: &TableReference, - available_indexes: &[Rc], - ) -> Result> { + available_indexes: &HashMap>>, + ) -> Result>> { match self { Self::Column { table, column, .. } => { if *table != table_index { return Ok(None); } - for (idx, index) in available_indexes.iter().enumerate() { - if index.table_name == table_reference.table.get_name() { - let column = table_reference.table.get_column_at(*column); - if index.columns.first().unwrap().name == column.name { - return Ok(Some(idx)); - } + let available_indexes_for_table = + available_indexes.get(table_reference.table.get_name()); + if available_indexes_for_table.is_none() { + return Ok(None); + } + let available_indexes_for_table = available_indexes_for_table.unwrap(); + for index in available_indexes_for_table.iter() { + let column = table_reference.table.get_column_at(*column); + if index.columns.first().unwrap().name == column.name { + return Ok(Some(index.clone())); } } Ok(None) @@ -489,7 +495,7 @@ pub fn try_extract_index_search_expression( cond: &mut WhereTerm, table_index: usize, table_reference: &TableReference, - available_indexes: &[Rc], + available_indexes: &HashMap>>, ) -> Result> { if cond.eval_at_loop != table_index { return Ok(None); @@ -556,7 +562,7 @@ pub fn try_extract_index_search_expression( } } - if let Some(index_index) = + if let Some(index_rc) = lhs.check_index_scan(table_index, &table_reference, available_indexes)? { match operator { @@ -567,7 +573,7 @@ pub fn try_extract_index_search_expression( | ast::Operator::LessEquals => { let rhs_owned = rhs.take_ownership(); return Ok(Some(Search::IndexSearch { - index: available_indexes[index_index].clone(), + index: index_rc, cmp_op: *operator, cmp_expr: WhereTerm { expr: rhs_owned, @@ -580,7 +586,7 @@ pub fn try_extract_index_search_expression( } } - if let Some(index_index) = + if let Some(index_rc) = rhs.check_index_scan(table_index, &table_reference, available_indexes)? { match operator { @@ -591,7 +597,7 @@ pub fn try_extract_index_search_expression( | ast::Operator::LessEquals => { let lhs_owned = lhs.take_ownership(); return Ok(Some(Search::IndexSearch { - index: available_indexes[index_index].clone(), + index: index_rc, cmp_op: opposite_cmp_op(*operator), cmp_expr: WhereTerm { expr: lhs_owned, diff --git a/core/translate/plan.rs b/core/translate/plan.rs index 9ac3a5b78..6209d37a8 100644 --- a/core/translate/plan.rs +++ b/core/translate/plan.rs @@ -89,8 +89,6 @@ pub struct SelectPlan { pub limit: Option, /// offset clause pub offset: Option, - /// all the indexes available - pub available_indexes: Vec>, /// query contains a constant condition that is always false pub contains_constant_false_condition: bool, /// query type (top level or subquery) @@ -112,8 +110,6 @@ pub struct DeletePlan { pub limit: Option, /// offset clause pub offset: Option, - /// all the indexes available - pub available_indexes: Vec>, /// query contains a constant condition that is always false pub contains_constant_false_condition: bool, } diff --git a/core/translate/select.rs b/core/translate/select.rs index b0494e931..d86897096 100644 --- a/core/translate/select.rs +++ b/core/translate/select.rs @@ -21,7 +21,7 @@ pub fn translate_select( syms: &SymbolTable, ) -> Result<()> { let mut select_plan = prepare_select_plan(schema, select, syms)?; - optimize_plan(&mut select_plan)?; + optimize_plan(&mut select_plan, schema)?; emit_program(program, select_plan, syms) } @@ -78,7 +78,6 @@ pub fn prepare_select_plan( aggregates: vec![], limit: None, offset: None, - available_indexes: schema.indexes.clone().into_values().flatten().collect(), contains_constant_false_condition: false, query_type: SelectQueryType::TopLevel, };