mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-08 09:44:21 +01:00
preallocate loop metadata according to table/column count and prefer vec over hashmap
This commit is contained in:
@@ -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<GroupByMetadata>,
|
||||
// metadata for the order by operator
|
||||
pub meta_sort: Option<SortMetadata>,
|
||||
// mapping between Join operator id and associated metadata (for left joins only)
|
||||
pub meta_left_joins: HashMap<usize, LeftJoinMetadata>,
|
||||
/// 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<Option<LeftJoinMetadata>>,
|
||||
// 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<usize, usize>,
|
||||
pub result_column_indexes_in_orderby_sorter: Vec<usize>,
|
||||
// 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<Vec<usize>>,
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
Reference in New Issue
Block a user