diff --git a/core/translate/emitter.rs b/core/translate/emitter.rs index 12db50a95..ef559a16c 100644 --- a/core/translate/emitter.rs +++ b/core/translate/emitter.rs @@ -1,8 +1,6 @@ // This module contains code for emitting bytecode instructions for SQL query execution. // It handles translating high-level SQL operations into low-level bytecode that can be executed by the virtual machine. -use std::collections::HashMap; - use sqlite3_parser::ast::{self}; use crate::function::Func; @@ -76,11 +74,12 @@ pub struct TranslateCtx<'a> { pub meta_group_by: Option, // metadata for the order by operator pub meta_sort: Option, - // mapping between Join operator id and associated metadata (for left joins only) - pub meta_left_joins: HashMap, + /// mapping between table loop index and associated metadata (for left joins only) + /// this metadata exists for the right table in a given left join + pub meta_left_joins: Vec>, // We need to emit result columns in the order they are present in the SELECT, but they may not be in the same order in the ORDER BY sorter. // This vector holds the indexes of the result columns in the ORDER BY sorter. - pub result_column_indexes_in_orderby_sorter: HashMap, + pub result_column_indexes_in_orderby_sorter: Vec, // We might skip adding a SELECT result column into the ORDER BY sorter if it is an exact match in the ORDER BY keys. // This vector holds the indexes of the result columns that we need to skip. pub result_columns_to_skip_in_orderby_sorter: Option>, @@ -101,6 +100,8 @@ pub enum OperationMode { fn prologue<'a>( program: &mut ProgramBuilder, syms: &'a SymbolTable, + table_count: usize, + result_column_count: usize, ) -> Result<(TranslateCtx<'a>, BranchOffset, BranchOffset)> { let init_label = program.allocate_label(); @@ -111,7 +112,7 @@ fn prologue<'a>( let start_offset = program.offset(); let t_ctx = TranslateCtx { - labels_main_loop: Vec::new(), + labels_main_loop: (0..table_count).map(|_| LoopLabels::new(program)).collect(), label_main_loop_end: None, reg_agg_start: None, reg_limit: None, @@ -119,9 +120,9 @@ fn prologue<'a>( reg_limit_offset_sum: None, reg_result_cols_start: None, meta_group_by: None, - meta_left_joins: HashMap::new(), + meta_left_joins: (0..table_count).map(|_| None).collect(), meta_sort: None, - result_column_indexes_in_orderby_sorter: HashMap::new(), + result_column_indexes_in_orderby_sorter: (0..result_column_count).collect(), result_columns_to_skip_in_orderby_sorter: None, resolver: Resolver::new(syms), }; @@ -167,7 +168,12 @@ fn emit_program_for_select( mut plan: SelectPlan, syms: &SymbolTable, ) -> Result<()> { - let (mut t_ctx, init_label, start_offset) = prologue(program, syms)?; + let (mut t_ctx, init_label, start_offset) = prologue( + program, + syms, + plan.table_references.len(), + plan.result_columns.len(), + )?; // Trivial exit on LIMIT 0 if let Some(limit) = plan.limit { @@ -274,7 +280,12 @@ fn emit_program_for_delete( mut plan: DeletePlan, syms: &SymbolTable, ) -> Result<()> { - let (mut t_ctx, init_label, start_offset) = prologue(program, syms)?; + let (mut t_ctx, init_label, start_offset) = prologue( + program, + syms, + plan.table_references.len(), + plan.result_columns.len(), + )?; // No rows will be read from source table loops if there is a constant false condition eg. WHERE 0 let after_main_loop_label = program.allocate_label(); diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index 1df0efb80..98e0e3938 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -43,6 +43,16 @@ pub struct LoopLabels { loop_end: BranchOffset, } +impl LoopLabels { + pub fn new(program: &mut ProgramBuilder) -> Self { + Self { + loop_start: program.allocate_label(), + next: program.allocate_label(), + loop_end: program.allocate_label(), + } + } +} + /// Initialize resources needed for the source operators (tables, joins, etc) pub fn init_loop( program: &mut ProgramBuilder, @@ -51,13 +61,6 @@ pub fn init_loop( mode: &OperationMode, ) -> Result<()> { for (table_index, table) in tables.iter().enumerate() { - let loop_labels = LoopLabels { - next: program.allocate_label(), - loop_start: program.allocate_label(), - loop_end: program.allocate_label(), - }; - t_ctx.labels_main_loop.push(loop_labels); - // Initialize bookkeeping for OUTER JOIN if let Some(join_info) = table.join_info.as_ref() { if join_info.outer { @@ -66,7 +69,7 @@ pub fn init_loop( label_match_flag_set_true: program.allocate_label(), label_match_flag_check_value: program.allocate_label(), }; - t_ctx.meta_left_joins.insert(table_index, lj_metadata); + t_ctx.meta_left_joins[table_index] = Some(lj_metadata); } } match &table.op { @@ -181,7 +184,7 @@ pub fn open_loop( // This is used to determine whether to emit actual columns or NULLs for the columns of the right table. if let Some(join_info) = table.join_info.as_ref() { if join_info.outer { - let lj_meta = t_ctx.meta_left_joins.get(&table_index).unwrap(); + let lj_meta = t_ctx.meta_left_joins[table_index].as_ref().unwrap(); program.emit_insn(Insn::Integer { value: 0, dest: lj_meta.reg_match_flag, @@ -465,7 +468,7 @@ pub fn open_loop( // for the right table's cursor. if let Some(join_info) = table.join_info.as_ref() { if join_info.outer { - let lj_meta = t_ctx.meta_left_joins.get(&table_index).unwrap(); + let lj_meta = t_ctx.meta_left_joins[table_index].as_ref().unwrap(); program.resolve_label(lj_meta.label_match_flag_set_true, program.offset()); program.emit_insn(Insn::Integer { value: 1, @@ -731,7 +734,7 @@ pub fn close_loop( // and emit a row with NULLs for the right table, and then jump back to the next row of the left table. if let Some(join_info) = table.join_info.as_ref() { if join_info.outer { - let lj_meta = t_ctx.meta_left_joins.get(&table_index).unwrap(); + let lj_meta = t_ctx.meta_left_joins[table_index].as_ref().unwrap(); // The left join match flag is set to 1 when there is any match on the right table // (e.g. SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a). // If the left join match flag has been set to 1, we jump to the next row on the outer table, diff --git a/core/translate/order_by.rs b/core/translate/order_by.rs index a908da571..06e411175 100644 --- a/core/translate/order_by.rs +++ b/core/translate/order_by.rs @@ -142,7 +142,7 @@ pub fn emit_order_by( let reg = start_reg + i; program.emit_insn(Insn::Column { cursor_id, - column: t_ctx.result_column_indexes_in_orderby_sorter[&i], + column: t_ctx.result_column_indexes_in_orderby_sorter[i], dest: reg, }); } diff --git a/core/translate/subquery.rs b/core/translate/subquery.rs index e66acc88e..1730312be 100644 --- a/core/translate/subquery.rs +++ b/core/translate/subquery.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use crate::{ vdbe::{builder::ProgramBuilder, insn::Insn}, Result, @@ -7,6 +5,7 @@ use crate::{ use super::{ emitter::{emit_query, Resolver, TranslateCtx}, + main_loop::LoopLabels, plan::{Operation, SelectPlan, SelectQueryType, TableReference}, }; @@ -68,14 +67,16 @@ pub fn emit_subquery<'a>( } let end_coroutine_label = program.allocate_label(); let mut metadata = TranslateCtx { - labels_main_loop: vec![], + labels_main_loop: (0..plan.table_references.len()) + .map(|_| LoopLabels::new(program)) + .collect(), label_main_loop_end: None, meta_group_by: None, - meta_left_joins: HashMap::new(), + meta_left_joins: (0..plan.table_references.len()).map(|_| None).collect(), meta_sort: None, reg_agg_start: None, reg_result_cols_start: None, - result_column_indexes_in_orderby_sorter: HashMap::new(), + result_column_indexes_in_orderby_sorter: (0..plan.result_columns.len()).collect(), result_columns_to_skip_in_orderby_sorter: None, reg_limit: plan.limit.map(|_| program.alloc_register()), reg_offset: plan.offset.map(|_| program.alloc_register()),