diff --git a/core/schema.rs b/core/schema.rs index 7ebe249be..c60b8ff5f 100644 --- a/core/schema.rs +++ b/core/schema.rs @@ -90,7 +90,7 @@ impl Table { None => None, }, Table::Pseudo(table) => match table.columns.get(index) { - Some(column) => Some(&column.name), + Some(_) => None, None => None, }, } diff --git a/core/translate/emitter.rs b/core/translate/emitter.rs index fe23832db..b9bf5767a 100644 --- a/core/translate/emitter.rs +++ b/core/translate/emitter.rs @@ -6,7 +6,6 @@ use sqlite3_parser::ast; use crate::schema::{Column, PseudoTable, Table}; use crate::storage::sqlite3_ondisk::DatabaseHeader; -use crate::translate::expr::resolve_ident_pseudo_table; use crate::translate::plan::{IterationDirection, Search}; use crate::types::{OwnedRecord, OwnedValue}; use crate::vdbe::builder::ProgramBuilder; @@ -14,41 +13,12 @@ use crate::vdbe::{BranchOffset, Insn, Program}; use crate::{Connection, Result}; use super::expr::{ - translate_aggregation, translate_condition_expr, translate_expr, translate_table_columns, + translate_aggregation, translate_aggregation_groupby, translate_condition_expr, translate_expr, ConditionMetadata, }; use super::plan::{Aggregate, BTreeTableReference, Direction, Plan}; use super::plan::{ResultSetColumn, SourceOperator}; -/** - * The Emitter trait is used to emit bytecode instructions for a given operator in the query plan. - * - * - step: perform a single step of the operator, emitting bytecode instructions as needed, - and returning a result indicating whether the operator is ready to emit a result row -*/ -// pub trait Emitter { -// fn step( -// &mut self, -// pb: &mut ProgramBuilder, -// m: &mut Metadata, -// referenced_tables: &[BTreeTableReference], -// ) -> Result; -// fn result_columns( -// &self, -// program: &mut ProgramBuilder, -// referenced_tables: &[BTreeTableReference], -// metadata: &mut Metadata, -// cursor_override: Option<&SortCursorOverride>, -// ) -> Result; -// fn result_row( -// &mut self, -// program: &mut ProgramBuilder, -// referenced_tables: &[BTreeTableReference], -// metadata: &mut Metadata, -// cursor_override: Option<&SortCursorOverride>, -// ) -> Result<()>; -// } - #[derive(Debug)] pub struct LeftJoinMetadata { // integer register that holds a flag that is set to true if the current row has a match for the left join @@ -127,1500 +97,22 @@ pub struct Metadata { next_row_labels: HashMap, // labels for the Rewind instructions. scan_loop_body_labels: Vec, - // mapping between Aggregation operator id and the register that holds the start of the aggregation result - aggregation_start_registers: HashMap, - // mapping between Aggregation operator id and associated metadata (if the aggregation has a group by clause) - group_bys: HashMap, + // metadata for the group by operator + group_by_metadata: Option, // mapping between Order operator id and associated metadata sorts: HashMap, // mapping between Join operator id and associated metadata (for left joins only) left_joins: HashMap, - // register holding the start of the result set - result_set_register_start: usize, + // First register of the aggregation results + pub aggregation_start_register: Option, + // 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, + // 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>, } -// /// Emitters return one of three possible results from the step() method: -// /// - Continue: the operator is not yet ready to emit a result row -// /// - ReadyToEmit: the operator is ready to emit a result row -// /// - Done: the operator has completed execution -// /// For example, a Scan operator will return Continue until it has opened a cursor, rewound it and applied any predicates. -// /// At that point, it will return ReadyToEmit. -// /// Finally, when the Scan operator has emitted a Next instruction, it will return Done. -// /// -// /// Parent operators are free to make decisions based on the result a child operator's step() method. -// /// -// /// When the root operator of a Plan returns ReadyToEmit, a ResultRow will always be emitted. -// /// When the root operator returns Done, the bytecode plan is complete. -// #[derive(Debug, PartialEq)] -// pub enum OpStepResult { -// Continue, -// ReadyToEmit, -// Done, -// } - -// impl Emitter for SourceOperator { -// fn step( -// &mut self, -// program: &mut ProgramBuilder, -// m: &mut Metadata, -// referenced_tables: &[BTreeTableReference], -// ) -> Result { -// let current_operator_column_count = self.column_count(referenced_tables); -// match self { -// SourceOperator::Scan { -// table_reference, -// id, -// step, -// predicates, -// iter_dir, -// } => { -// *step += 1; -// const SCAN_OPEN_READ: usize = 1; -// const SCAN_BODY: usize = 2; -// const SCAN_NEXT: usize = 3; -// let reverse = iter_dir -// .as_ref() -// .is_some_and(|iter_dir| *iter_dir == IterationDirection::Backwards); -// match *step { -// SCAN_OPEN_READ => { -// let cursor_id = program.alloc_cursor_id( -// Some(table_reference.table_identifier.clone()), -// Some(Table::BTree(table_reference.table.clone())), -// ); -// let root_page = table_reference.table.root_page; -// let next_row_label = program.allocate_label(); -// m.next_row_labels.insert(*id, next_row_label); -// program.emit_insn(Insn::OpenReadAsync { -// cursor_id, -// root_page, -// }); -// program.emit_insn(Insn::OpenReadAwait); - -// Ok(OpStepResult::Continue) -// } -// SCAN_BODY => { -// let cursor_id = -// program.resolve_cursor_id(&table_reference.table_identifier, None); -// if reverse { -// program.emit_insn(Insn::LastAsync { cursor_id }); -// } else { -// program.emit_insn(Insn::RewindAsync { cursor_id }); -// } -// let scan_loop_body_label = program.allocate_label(); -// let halt_label = m.termination_label_stack.last().unwrap(); -// program.emit_insn_with_label_dependency( -// if reverse { -// Insn::LastAwait { -// cursor_id, -// pc_if_empty: *halt_label, -// } -// } else { -// Insn::RewindAwait { -// cursor_id, -// pc_if_empty: *halt_label, -// } -// }, -// *halt_label, -// ); -// m.scan_loop_body_labels.push(scan_loop_body_label); -// program.defer_label_resolution( -// scan_loop_body_label, -// program.offset() as usize, -// ); - -// let jump_label = m.next_row_labels.get(id).unwrap_or(halt_label); -// if let Some(preds) = predicates { -// for expr in preds { -// let jump_target_when_true = program.allocate_label(); -// let condition_metadata = ConditionMetadata { -// jump_if_condition_is_true: false, -// jump_target_when_true, -// jump_target_when_false: *jump_label, -// }; -// translate_condition_expr( -// program, -// referenced_tables, -// expr, -// None, -// condition_metadata, -// m.result_set_register_start, -// )?; -// program.resolve_label(jump_target_when_true, program.offset()); -// } -// } - -// Ok(OpStepResult::ReadyToEmit) -// } -// SCAN_NEXT => { -// let cursor_id = -// program.resolve_cursor_id(&table_reference.table_identifier, None); -// program -// .resolve_label(*m.next_row_labels.get(id).unwrap(), program.offset()); -// if reverse { -// program.emit_insn(Insn::PrevAsync { cursor_id }); -// } else { -// program.emit_insn(Insn::NextAsync { cursor_id }); -// } -// let jump_label = m.scan_loop_body_labels.pop().unwrap(); - -// if reverse { -// program.emit_insn_with_label_dependency( -// Insn::PrevAwait { -// cursor_id, -// pc_if_next: jump_label, -// }, -// jump_label, -// ); -// } else { -// program.emit_insn_with_label_dependency( -// Insn::NextAwait { -// cursor_id, -// pc_if_next: jump_label, -// }, -// jump_label, -// ); -// } -// Ok(OpStepResult::Done) -// } -// _ => Ok(OpStepResult::Done), -// } -// } -// SourceOperator::Search { -// table_reference, -// search, -// predicates, -// step, -// id, -// .. -// } => { -// *step += 1; -// const SEARCH_OPEN_READ: usize = 1; -// const SEARCH_BODY: usize = 2; -// const SEARCH_NEXT: usize = 3; -// match *step { -// SEARCH_OPEN_READ => { -// let table_cursor_id = program.alloc_cursor_id( -// Some(table_reference.table_identifier.clone()), -// Some(Table::BTree(table_reference.table.clone())), -// ); - -// let next_row_label = program.allocate_label(); - -// if !matches!(search, Search::PrimaryKeyEq { .. }) { -// // Primary key equality search is handled with a SeekRowid instruction which does not loop, since it is a single row lookup. -// m.next_row_labels.insert(*id, next_row_label); -// } - -// let scan_loop_body_label = program.allocate_label(); -// m.scan_loop_body_labels.push(scan_loop_body_label); -// program.emit_insn(Insn::OpenReadAsync { -// cursor_id: table_cursor_id, -// root_page: table_reference.table.root_page, -// }); -// program.emit_insn(Insn::OpenReadAwait); - -// if let Search::IndexSearch { index, .. } = search { -// let index_cursor_id = program.alloc_cursor_id( -// Some(index.name.clone()), -// Some(Table::Index(index.clone())), -// ); -// program.emit_insn(Insn::OpenReadAsync { -// cursor_id: index_cursor_id, -// root_page: index.root_page, -// }); -// program.emit_insn(Insn::OpenReadAwait); -// } -// Ok(OpStepResult::Continue) -// } -// SEARCH_BODY => { -// let table_cursor_id = -// program.resolve_cursor_id(&table_reference.table_identifier, None); - -// // Open the loop for the index search. -// // Primary key equality search is handled with a SeekRowid instruction which does not loop, since it is a single row lookup. -// if !matches!(search, Search::PrimaryKeyEq { .. }) { -// let index_cursor_id = if let Search::IndexSearch { index, .. } = search -// { -// Some(program.resolve_cursor_id(&index.name, None)) -// } else { -// None -// }; -// let scan_loop_body_label = *m.scan_loop_body_labels.last().unwrap(); -// let cmp_reg = program.alloc_register(); -// let (cmp_expr, cmp_op) = match search { -// Search::IndexSearch { -// cmp_expr, cmp_op, .. -// } => (cmp_expr, cmp_op), -// Search::PrimaryKeySearch { cmp_expr, cmp_op } => (cmp_expr, cmp_op), -// Search::PrimaryKeyEq { .. } => unreachable!(), -// }; -// // TODO this only handles ascending indexes -// match cmp_op { -// ast::Operator::Equals -// | ast::Operator::Greater -// | ast::Operator::GreaterEquals => { -// translate_expr( -// program, -// Some(referenced_tables), -// cmp_expr, -// cmp_reg, -// None, -// m.result_set_register_start, -// )?; -// } -// ast::Operator::Less | ast::Operator::LessEquals => { -// program.emit_insn(Insn::Null { -// dest: cmp_reg, -// dest_end: None, -// }); -// } -// _ => unreachable!(), -// } -// program.emit_insn_with_label_dependency( -// match cmp_op { -// ast::Operator::Equals | ast::Operator::GreaterEquals => { -// Insn::SeekGE { -// is_index: index_cursor_id.is_some(), -// cursor_id: index_cursor_id.unwrap_or(table_cursor_id), -// start_reg: cmp_reg, -// num_regs: 1, -// target_pc: *m.termination_label_stack.last().unwrap(), -// } -// } -// ast::Operator::Greater -// | ast::Operator::Less -// | ast::Operator::LessEquals => Insn::SeekGT { -// is_index: index_cursor_id.is_some(), -// cursor_id: index_cursor_id.unwrap_or(table_cursor_id), -// start_reg: cmp_reg, -// num_regs: 1, -// target_pc: *m.termination_label_stack.last().unwrap(), -// }, -// _ => unreachable!(), -// }, -// *m.termination_label_stack.last().unwrap(), -// ); -// if *cmp_op == ast::Operator::Less -// || *cmp_op == ast::Operator::LessEquals -// { -// translate_expr( -// program, -// Some(referenced_tables), -// cmp_expr, -// cmp_reg, -// None, -// m.result_set_register_start, -// )?; -// } - -// program.defer_label_resolution( -// scan_loop_body_label, -// program.offset() as usize, -// ); -// // TODO: We are currently only handling ascending indexes. -// // For conditions like index_key > 10, we have already seeked to the first key greater than 10, and can just scan forward. -// // For conditions like index_key < 10, we are at the beginning of the index, and will scan forward and emit IdxGE(10) with a conditional jump to the end. -// // For conditions like index_key = 10, we have already seeked to the first key greater than or equal to 10, and can just scan forward and emit IdxGT(10) with a conditional jump to the end. -// // For conditions like index_key >= 10, we have already seeked to the first key greater than or equal to 10, and can just scan forward. -// // For conditions like index_key <= 10, we are at the beginning of the index, and will scan forward and emit IdxGT(10) with a conditional jump to the end. -// // For conditions like index_key != 10, TODO. probably the optimal way is not to use an index at all. -// // -// // For primary key searches we emit RowId and then compare it to the seek value. - -// let abort_jump_target = *m -// .next_row_labels -// .get(id) -// .unwrap_or(m.termination_label_stack.last().unwrap()); -// match cmp_op { -// ast::Operator::Equals | ast::Operator::LessEquals => { -// if let Some(index_cursor_id) = index_cursor_id { -// program.emit_insn_with_label_dependency( -// Insn::IdxGT { -// cursor_id: index_cursor_id, -// start_reg: cmp_reg, -// num_regs: 1, -// target_pc: abort_jump_target, -// }, -// abort_jump_target, -// ); -// } else { -// let rowid_reg = program.alloc_register(); -// program.emit_insn(Insn::RowId { -// cursor_id: table_cursor_id, -// dest: rowid_reg, -// }); -// program.emit_insn_with_label_dependency( -// Insn::Gt { -// lhs: rowid_reg, -// rhs: cmp_reg, -// target_pc: abort_jump_target, -// }, -// abort_jump_target, -// ); -// } -// } -// ast::Operator::Less => { -// if let Some(index_cursor_id) = index_cursor_id { -// program.emit_insn_with_label_dependency( -// Insn::IdxGE { -// cursor_id: index_cursor_id, -// start_reg: cmp_reg, -// num_regs: 1, -// target_pc: abort_jump_target, -// }, -// abort_jump_target, -// ); -// } else { -// let rowid_reg = program.alloc_register(); -// program.emit_insn(Insn::RowId { -// cursor_id: table_cursor_id, -// dest: rowid_reg, -// }); -// program.emit_insn_with_label_dependency( -// Insn::Ge { -// lhs: rowid_reg, -// rhs: cmp_reg, -// target_pc: abort_jump_target, -// }, -// abort_jump_target, -// ); -// } -// } -// _ => {} -// } - -// if let Some(index_cursor_id) = index_cursor_id { -// program.emit_insn(Insn::DeferredSeek { -// index_cursor_id, -// table_cursor_id, -// }); -// } -// } - -// let jump_label = m -// .next_row_labels -// .get(id) -// .unwrap_or(m.termination_label_stack.last().unwrap()); - -// if let Search::PrimaryKeyEq { cmp_expr } = search { -// let src_reg = program.alloc_register(); -// translate_expr( -// program, -// Some(referenced_tables), -// cmp_expr, -// src_reg, -// None, -// m.result_set_register_start, -// )?; -// program.emit_insn_with_label_dependency( -// Insn::SeekRowid { -// cursor_id: table_cursor_id, -// src_reg, -// target_pc: *jump_label, -// }, -// *jump_label, -// ); -// } -// if let Some(predicates) = predicates { -// for predicate in predicates.iter() { -// let jump_target_when_true = program.allocate_label(); -// let condition_metadata = ConditionMetadata { -// jump_if_condition_is_true: false, -// jump_target_when_true, -// jump_target_when_false: *jump_label, -// }; -// translate_condition_expr( -// program, -// referenced_tables, -// predicate, -// None, -// condition_metadata, -// m.result_set_register_start, -// )?; -// program.resolve_label(jump_target_when_true, program.offset()); -// } -// } - -// Ok(OpStepResult::ReadyToEmit) -// } -// SEARCH_NEXT => { -// if matches!(search, Search::PrimaryKeyEq { .. }) { -// // Primary key equality search is handled with a SeekRowid instruction which does not loop, so there is no need to emit a NextAsync instruction. -// return Ok(OpStepResult::Done); -// } -// let cursor_id = match search { -// Search::IndexSearch { index, .. } => { -// program.resolve_cursor_id(&index.name, None) -// } -// Search::PrimaryKeySearch { .. } => { -// program.resolve_cursor_id(&table_reference.table_identifier, None) -// } -// Search::PrimaryKeyEq { .. } => unreachable!(), -// }; -// program -// .resolve_label(*m.next_row_labels.get(id).unwrap(), program.offset()); -// program.emit_insn(Insn::NextAsync { cursor_id }); -// let jump_label = m.scan_loop_body_labels.pop().unwrap(); -// program.emit_insn_with_label_dependency( -// Insn::NextAwait { -// cursor_id, -// pc_if_next: jump_label, -// }, -// jump_label, -// ); -// Ok(OpStepResult::Done) -// } -// _ => Ok(OpStepResult::Done), -// } -// } -// SourceOperator::Join { -// left, -// right, -// outer, -// predicates, -// step, -// id, -// .. -// } => { -// *step += 1; -// const JOIN_INIT: usize = 1; -// const JOIN_DO_JOIN: usize = 2; -// const JOIN_END: usize = 3; -// match *step { -// JOIN_INIT => { -// if *outer { -// let lj_metadata = LeftJoinMetadata { -// match_flag_register: program.alloc_register(), -// set_match_flag_true_label: program.allocate_label(), -// check_match_flag_label: program.allocate_label(), -// on_match_jump_to_label: program.allocate_label(), -// }; -// m.left_joins.insert(*id, lj_metadata); -// } -// left.step(program, m, referenced_tables)?; -// right.step(program, m, referenced_tables)?; - -// Ok(OpStepResult::Continue) -// } -// JOIN_DO_JOIN => { -// left.step(program, m, referenced_tables)?; - -// let mut jump_target_when_false = *m -// .next_row_labels -// .get(&right.id()) -// .or(m.next_row_labels.get(&left.id())) -// .unwrap_or(m.termination_label_stack.last().unwrap()); - -// if *outer { -// let lj_meta = m.left_joins.get(id).unwrap(); -// program.emit_insn(Insn::Integer { -// value: 0, -// dest: lj_meta.match_flag_register, -// }); -// jump_target_when_false = lj_meta.check_match_flag_label; -// } -// m.next_row_labels.insert(right.id(), jump_target_when_false); - -// right.step(program, m, referenced_tables)?; - -// if let Some(predicates) = predicates { -// let jump_target_when_true = program.allocate_label(); -// let condition_metadata = ConditionMetadata { -// jump_if_condition_is_true: false, -// jump_target_when_true, -// jump_target_when_false, -// }; -// for predicate in predicates.iter() { -// translate_condition_expr( -// program, -// referenced_tables, -// predicate, -// None, -// condition_metadata, -// m.result_set_register_start, -// )?; -// } -// program.resolve_label(jump_target_when_true, program.offset()); -// } - -// if *outer { -// let lj_meta = m.left_joins.get(id).unwrap(); -// program.defer_label_resolution( -// lj_meta.set_match_flag_true_label, -// program.offset() as usize, -// ); -// program.emit_insn(Insn::Integer { -// value: 1, -// dest: lj_meta.match_flag_register, -// }); -// } - -// Ok(OpStepResult::ReadyToEmit) -// } -// JOIN_END => { -// right.step(program, m, referenced_tables)?; - -// if *outer { -// let lj_meta = m.left_joins.get(id).unwrap(); -// // If the left join match flag has been set to 1, we jump to the next row on the outer table (result row has been emitted already) -// program.resolve_label(lj_meta.check_match_flag_label, program.offset()); -// program.emit_insn_with_label_dependency( -// Insn::IfPos { -// reg: lj_meta.match_flag_register, -// target_pc: lj_meta.on_match_jump_to_label, -// decrement_by: 0, -// }, -// lj_meta.on_match_jump_to_label, -// ); -// // If not, we set the right table cursor's "pseudo null bit" on, which means any Insn::Column will return NULL -// let right_cursor_id = match right.as_ref() { -// SourceOperator::Scan { -// table_reference, .. -// } => program -// .resolve_cursor_id(&table_reference.table_identifier, None), -// SourceOperator::Search { -// table_reference, .. -// } => program -// .resolve_cursor_id(&table_reference.table_identifier, None), -// _ => unreachable!(), -// }; -// program.emit_insn(Insn::NullRow { -// cursor_id: right_cursor_id, -// }); -// // Jump to setting the left join match flag to 1 again, but this time the right table cursor will set everything to null -// program.emit_insn_with_label_dependency( -// Insn::Goto { -// target_pc: lj_meta.set_match_flag_true_label, -// }, -// lj_meta.set_match_flag_true_label, -// ); -// } -// let next_row_label = if *outer { -// m.left_joins.get(id).unwrap().on_match_jump_to_label -// } else { -// *m.next_row_labels.get(&right.id()).unwrap() -// }; -// // This points to the NextAsync instruction of the left table -// program.resolve_label(next_row_label, program.offset()); -// left.step(program, m, referenced_tables)?; - -// Ok(OpStepResult::Done) -// } -// _ => Ok(OpStepResult::Done), -// } -// } -// SourceOperator::Projection { -// id, -// source, -// expressions, -// aggregates, -// group_by, -// step, -// .. -// } => { -// *step += 1; - -// if !aggregates.is_empty() && group_by.is_none() { -// const PROJECTION_WAIT_UNTIL_SOURCE_READY: usize = 1; -// const PROJECTION_FINALIZE_SOURCE: usize = 2; -// match *step { -// PROJECTION_WAIT_UNTIL_SOURCE_READY => loop { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Continue => continue, -// OpStepResult::ReadyToEmit | OpStepResult::Done => { -// return Ok(OpStepResult::ReadyToEmit); -// } -// } -// }, -// PROJECTION_FINALIZE_SOURCE => { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Done => return Ok(OpStepResult::Done), -// _ => unreachable!(), -// } -// } -// _ => return Ok(OpStepResult::Done), -// } -// } - -// // Group by aggregation eg. SELECT a, b, sum(c) FROM t GROUP BY a, b -// if let Some(group_by) = group_by { -// const GROUP_BY_INIT: usize = 1; -// const GROUP_BY_INSERT_INTO_SORTER: usize = 2; -// const GROUP_BY_SORT_AND_COMPARE: usize = 3; -// const GROUP_BY_PREPARE_ROW: usize = 4; -// const GROUP_BY_CLEAR_ACCUMULATOR_SUBROUTINE: usize = 5; -// match *step { -// GROUP_BY_INIT => { -// let agg_final_label = program.allocate_label(); -// m.termination_label_stack.push(agg_final_label); -// let num_aggs = aggregates.len(); - -// let sort_cursor = program.alloc_cursor_id(None, None); - -// let abort_flag_register = program.alloc_register(); -// let data_in_accumulator_indicator_register = program.alloc_register(); -// let group_exprs_comparison_register = -// program.alloc_registers(group_by.len()); -// let group_exprs_accumulator_register = -// program.alloc_registers(group_by.len()); -// let agg_exprs_start_reg = program.alloc_registers(num_aggs); -// m.aggregation_start_registers -// .insert(*id, agg_exprs_start_reg); -// let sorter_key_register = program.alloc_register(); - -// let subroutine_accumulator_clear_label = program.allocate_label(); -// let subroutine_accumulator_output_label = program.allocate_label(); -// let sorter_data_label = program.allocate_label(); -// let grouping_done_label = program.allocate_label(); - -// let mut order = Vec::new(); -// const ASCENDING: i64 = 0; -// for _ in group_by.iter() { -// order.push(OwnedValue::Integer(ASCENDING)); -// } -// program.emit_insn(Insn::SorterOpen { -// cursor_id: sort_cursor, -// columns: current_operator_column_count, -// order: OwnedRecord::new(order), -// }); - -// program.add_comment(program.offset(), "clear group by abort flag"); -// program.emit_insn(Insn::Integer { -// value: 0, -// dest: abort_flag_register, -// }); - -// program.add_comment( -// program.offset(), -// "initialize group by comparison registers to NULL", -// ); -// program.emit_insn(Insn::Null { -// dest: group_exprs_comparison_register, -// dest_end: if group_by.len() > 1 { -// Some(group_exprs_comparison_register + group_by.len() - 1) -// } else { -// None -// }, -// }); - -// program.add_comment( -// program.offset(), -// "go to clear accumulator subroutine", -// ); - -// let subroutine_accumulator_clear_return_offset_register = -// program.alloc_register(); -// program.emit_insn_with_label_dependency( -// Insn::Gosub { -// target_pc: subroutine_accumulator_clear_label, -// return_reg: subroutine_accumulator_clear_return_offset_register, -// }, -// subroutine_accumulator_clear_label, -// ); - -// m.group_bys.insert( -// *id, -// GroupByMetadata { -// sort_cursor, -// subroutine_accumulator_clear_label, -// subroutine_accumulator_clear_return_offset_register, -// subroutine_accumulator_output_label, -// subroutine_accumulator_output_return_offset_register: program -// .alloc_register(), -// accumulator_indicator_set_true_label: program.allocate_label(), -// sorter_data_label, -// grouping_done_label, -// abort_flag_register, -// data_in_accumulator_indicator_register, -// group_exprs_accumulator_register, -// group_exprs_comparison_register, -// sorter_key_register, -// }, -// ); - -// loop { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Continue => continue, -// OpStepResult::ReadyToEmit => { -// return Ok(OpStepResult::Continue); -// } -// OpStepResult::Done => { -// return Ok(OpStepResult::Done); -// } -// } -// } -// } -// GROUP_BY_INSERT_INTO_SORTER => { -// let sort_keys_count = group_by.len(); -// let start_reg = program.alloc_registers(current_operator_column_count); -// for (i, expr) in group_by.iter().enumerate() { -// let key_reg = start_reg + i; -// translate_expr( -// program, -// Some(referenced_tables), -// expr, -// key_reg, -// None, -// m.result_set_register_start, -// )?; -// } -// for (i, agg) in aggregates.iter().enumerate() { -// // TODO it's a hack to assume aggregate functions have exactly one argument. -// // Counterpoint e.g. GROUP_CONCAT(expr, separator). -// // -// // Here we are collecting scalars for the group by sorter, which will include -// // both the group by expressions and the aggregate arguments. -// // e.g. in `select u.first_name, sum(u.age) from users group by u.first_name` -// // the sorter will have two scalars: u.first_name and u.age. -// // these are then sorted by u.first_name, and for each u.first_name, we sum the u.age. -// // the actual aggregation is done later in GROUP_BY_SORT_AND_COMPARE below. -// // -// // This is why we take the first argument of each aggregate function currently. -// // It's mostly an artifact of the current architecture being a bit poor; we should recognize -// // which scalars are dependencies of aggregate functions and explicitly collect those. -// let expr = &agg.args[0]; -// let agg_reg = start_reg + sort_keys_count + i; -// translate_expr( -// program, -// Some(referenced_tables), -// expr, -// agg_reg, -// None, -// m.result_set_register_start, -// )?; -// } - -// let group_by_metadata = m.group_bys.get(id).unwrap(); - -// program.emit_insn(Insn::MakeRecord { -// start_reg, -// count: current_operator_column_count, -// dest_reg: group_by_metadata.sorter_key_register, -// }); - -// let group_by_metadata = m.group_bys.get(id).unwrap(); -// program.emit_insn(Insn::SorterInsert { -// cursor_id: group_by_metadata.sort_cursor, -// record_reg: group_by_metadata.sorter_key_register, -// }); - -// return Ok(OpStepResult::Continue); -// } -// #[allow(clippy::never_loop)] -// GROUP_BY_SORT_AND_COMPARE => { -// loop { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Done => { -// break; -// } -// _ => unreachable!(), -// } -// } - -// let group_by_metadata = m.group_bys.get_mut(id).unwrap(); - -// let GroupByMetadata { -// group_exprs_comparison_register: comparison_register, -// subroutine_accumulator_output_return_offset_register, -// subroutine_accumulator_output_label, -// subroutine_accumulator_clear_return_offset_register, -// subroutine_accumulator_clear_label, -// data_in_accumulator_indicator_register, -// accumulator_indicator_set_true_label, -// group_exprs_accumulator_register: group_exprs_start_register, -// abort_flag_register, -// sorter_key_register, -// .. -// } = *group_by_metadata; -// let halt_label = *m.termination_label_stack.first().unwrap(); - -// let mut column_names = -// Vec::with_capacity(current_operator_column_count); -// for expr in group_by -// .iter() -// .chain(aggregates.iter().map(|agg| &agg.args[0])) -// // FIXME: just blindly taking the first arg is a hack -// { -// // Sorter column names for group by are now just determined by stringifying the expression, since the group by -// // columns and aggregations can be practically anything. -// // FIXME: either come up with something more robust, or make this something like expr.to_canonical_string() so that we can handle -// // things like `count(1)` and `COUNT(1)` the same way -// column_names.push(expr.to_string()); -// } -// let pseudo_columns = column_names -// .iter() -// .map(|name| Column { -// name: name.clone(), -// primary_key: false, -// ty: crate::schema::Type::Null, -// }) -// .collect::>(); - -// let pseudo_table = Rc::new(PseudoTable { -// columns: pseudo_columns, -// }); - -// let pseudo_cursor = program -// .alloc_cursor_id(None, Some(Table::Pseudo(pseudo_table.clone()))); - -// program.emit_insn(Insn::OpenPseudo { -// cursor_id: pseudo_cursor, -// content_reg: sorter_key_register, -// num_fields: current_operator_column_count, -// }); - -// let group_by_metadata = m.group_bys.get(id).unwrap(); -// program.emit_insn_with_label_dependency( -// Insn::SorterSort { -// cursor_id: group_by_metadata.sort_cursor, -// pc_if_empty: group_by_metadata.grouping_done_label, -// }, -// group_by_metadata.grouping_done_label, -// ); - -// program.defer_label_resolution( -// group_by_metadata.sorter_data_label, -// program.offset() as usize, -// ); -// program.emit_insn(Insn::SorterData { -// cursor_id: group_by_metadata.sort_cursor, -// dest_reg: group_by_metadata.sorter_key_register, -// pseudo_cursor, -// }); - -// let groups_start_reg = program.alloc_registers(group_by.len()); -// for (i, expr) in group_by.iter().enumerate() { -// let sorter_column_index = -// resolve_ident_pseudo_table(&expr.to_string(), &pseudo_table)?; -// let group_reg = groups_start_reg + i; -// program.emit_insn(Insn::Column { -// cursor_id: pseudo_cursor, -// column: sorter_column_index, -// dest: group_reg, -// }); -// } - -// program.emit_insn(Insn::Compare { -// start_reg_a: comparison_register, -// start_reg_b: groups_start_reg, -// count: group_by.len(), -// }); - -// let agg_step_label = program.allocate_label(); - -// program.add_comment( -// program.offset(), -// "start new group if comparison is not equal", -// ); -// program.emit_insn_with_label_dependency( -// Insn::Jump { -// target_pc_lt: program.offset() + 1, -// target_pc_eq: agg_step_label, -// target_pc_gt: program.offset() + 1, -// }, -// agg_step_label, -// ); - -// program.emit_insn(Insn::Move { -// source_reg: groups_start_reg, -// dest_reg: comparison_register, -// count: group_by.len(), -// }); - -// program.add_comment( -// program.offset(), -// "check if ended group had data, and output if so", -// ); -// program.emit_insn_with_label_dependency( -// Insn::Gosub { -// target_pc: subroutine_accumulator_output_label, -// return_reg: -// subroutine_accumulator_output_return_offset_register, -// }, -// subroutine_accumulator_output_label, -// ); - -// program.add_comment(program.offset(), "check abort flag"); -// program.emit_insn_with_label_dependency( -// Insn::IfPos { -// reg: abort_flag_register, -// target_pc: halt_label, -// decrement_by: 0, -// }, -// m.termination_label_stack[0], -// ); - -// program -// .add_comment(program.offset(), "goto clear accumulator subroutine"); -// program.emit_insn_with_label_dependency( -// Insn::Gosub { -// target_pc: subroutine_accumulator_clear_label, -// return_reg: subroutine_accumulator_clear_return_offset_register, -// }, -// subroutine_accumulator_clear_label, -// ); - -// program.resolve_label(agg_step_label, program.offset()); -// let start_reg = m.aggregation_start_registers.get(id).unwrap(); -// for (i, agg) in aggregates.iter().enumerate() { -// let agg_result_reg = start_reg + i; -// translate_aggregation( -// program, -// referenced_tables, -// agg, -// agg_result_reg, -// Some(pseudo_cursor), -// )?; -// } - -// program.add_comment( -// program.offset(), -// "don't emit group columns if continuing existing group", -// ); -// program.emit_insn_with_label_dependency( -// Insn::If { -// target_pc: accumulator_indicator_set_true_label, -// reg: data_in_accumulator_indicator_register, -// null_reg: 0, // unused in this case -// }, -// accumulator_indicator_set_true_label, -// ); - -// for (i, expr) in group_by.iter().enumerate() { -// let key_reg = group_exprs_start_register + i; -// let sorter_column_index = -// resolve_ident_pseudo_table(&expr.to_string(), &pseudo_table)?; -// program.emit_insn(Insn::Column { -// cursor_id: pseudo_cursor, -// column: sorter_column_index, -// dest: key_reg, -// }); -// } - -// program.resolve_label( -// accumulator_indicator_set_true_label, -// program.offset(), -// ); -// program.add_comment(program.offset(), "indicate data in accumulator"); -// program.emit_insn(Insn::Integer { -// value: 1, -// dest: data_in_accumulator_indicator_register, -// }); - -// return Ok(OpStepResult::Continue); -// } -// GROUP_BY_PREPARE_ROW => { -// let group_by_metadata = m.group_bys.get(id).unwrap(); -// program.emit_insn_with_label_dependency( -// Insn::SorterNext { -// cursor_id: group_by_metadata.sort_cursor, -// pc_if_next: group_by_metadata.sorter_data_label, -// }, -// group_by_metadata.sorter_data_label, -// ); - -// program.resolve_label( -// group_by_metadata.grouping_done_label, -// program.offset(), -// ); - -// program.add_comment(program.offset(), "emit row for final group"); -// program.emit_insn_with_label_dependency( -// Insn::Gosub { -// target_pc: group_by_metadata -// .subroutine_accumulator_output_label, -// return_reg: group_by_metadata -// .subroutine_accumulator_output_return_offset_register, -// }, -// group_by_metadata.subroutine_accumulator_output_label, -// ); - -// program.add_comment(program.offset(), "group by finished"); -// let termination_label = -// m.termination_label_stack[m.termination_label_stack.len() - 2]; -// program.emit_insn_with_label_dependency( -// Insn::Goto { -// target_pc: termination_label, -// }, -// termination_label, -// ); -// program.emit_insn(Insn::Integer { -// value: 1, -// dest: group_by_metadata.abort_flag_register, -// }); -// program.emit_insn(Insn::Return { -// return_reg: group_by_metadata -// .subroutine_accumulator_output_return_offset_register, -// }); - -// program.resolve_label( -// group_by_metadata.subroutine_accumulator_output_label, -// program.offset(), -// ); - -// program.add_comment( -// program.offset(), -// "output group by row subroutine start", -// ); -// let termination_label = *m.termination_label_stack.last().unwrap(); -// program.emit_insn_with_label_dependency( -// Insn::IfPos { -// reg: group_by_metadata.data_in_accumulator_indicator_register, -// target_pc: termination_label, -// decrement_by: 0, -// }, -// termination_label, -// ); -// program.emit_insn(Insn::Return { -// return_reg: group_by_metadata -// .subroutine_accumulator_output_return_offset_register, -// }); - -// return Ok(OpStepResult::ReadyToEmit); -// } -// GROUP_BY_CLEAR_ACCUMULATOR_SUBROUTINE => { -// let group_by_metadata = m.group_bys.get(id).unwrap(); -// program.emit_insn(Insn::Return { -// return_reg: group_by_metadata -// .subroutine_accumulator_output_return_offset_register, -// }); - -// program.add_comment( -// program.offset(), -// "clear accumulator subroutine start", -// ); -// program.resolve_label( -// group_by_metadata.subroutine_accumulator_clear_label, -// program.offset(), -// ); -// let start_reg = group_by_metadata.group_exprs_accumulator_register; -// program.emit_insn(Insn::Null { -// dest: start_reg, -// dest_end: Some(start_reg + group_by.len() + aggregates.len() - 1), -// }); - -// program.emit_insn(Insn::Integer { -// value: 0, -// dest: group_by_metadata.data_in_accumulator_indicator_register, -// }); -// program.emit_insn(Insn::Return { -// return_reg: group_by_metadata -// .subroutine_accumulator_clear_return_offset_register, -// }); -// } -// _ => { -// return Ok(OpStepResult::Done); -// } -// } -// } - -// // Non-grouped aggregation e.g. SELECT COUNT(*) FROM t - -// const AGGREGATE_INIT: usize = 1; -// const AGGREGATE_WAIT_UNTIL_SOURCE_READY: usize = 2; -// match *step { -// AGGREGATE_INIT => { -// let agg_final_label = program.allocate_label(); -// m.termination_label_stack.push(agg_final_label); -// let num_aggs = aggregates.len(); -// let start_reg = program.alloc_registers(num_aggs); -// m.aggregation_start_registers.insert(*id, start_reg); - -// Ok(OpStepResult::Continue) -// } -// AGGREGATE_WAIT_UNTIL_SOURCE_READY => loop { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Continue => {} -// OpStepResult::ReadyToEmit => { -// let start_reg = m.aggregation_start_registers.get(id).unwrap(); -// for (i, agg) in aggregates.iter().enumerate() { -// let agg_result_reg = start_reg + i; -// translate_aggregation( -// program, -// referenced_tables, -// agg, -// agg_result_reg, -// None, -// )?; -// } -// } -// OpStepResult::Done => { -// return Ok(OpStepResult::ReadyToEmit); -// } -// } -// }, -// _ => Ok(OpStepResult::Done), -// } -// } -// SourceOperator::Filter { .. } => unreachable!("predicates have been pushed down"), -// SourceOperator::Limit { source, step, .. } => { -// *step += 1; -// loop { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Continue => continue, -// OpStepResult::ReadyToEmit => { -// return Ok(OpStepResult::ReadyToEmit); -// } -// OpStepResult::Done => return Ok(OpStepResult::Done), -// } -// } -// } -// SourceOperator::Order { -// id, -// source, -// key, -// step, -// } => { -// *step += 1; -// const ORDER_INIT: usize = 1; -// const ORDER_INSERT_INTO_SORTER: usize = 2; -// const ORDER_SORT_AND_OPEN_LOOP: usize = 3; -// const ORDER_NEXT: usize = 4; -// match *step { -// ORDER_INIT => { -// m.termination_label_stack.push(program.allocate_label()); -// let sort_cursor = program.alloc_cursor_id(None, None); -// m.sorts.insert( -// *id, -// SortMetadata { -// sort_cursor, -// pseudo_table_cursor: usize::MAX, // will be set later -// sorter_data_register: program.alloc_register(), -// sorter_data_label: program.allocate_label(), -// done_label: program.allocate_label(), -// }, -// ); -// let mut order = Vec::new(); -// for (_, direction) in key.iter() { -// order.push(OwnedValue::Integer(*direction as i64)); -// } -// program.emit_insn(Insn::SorterOpen { -// cursor_id: sort_cursor, -// columns: key.len(), -// order: OwnedRecord::new(order), -// }); - -// loop { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Continue => continue, -// OpStepResult::ReadyToEmit => { -// return Ok(OpStepResult::Continue); -// } -// OpStepResult::Done => { -// return Ok(OpStepResult::Done); -// } -// } -// } -// } -// ORDER_INSERT_INTO_SORTER => { -// let sort_keys_count = key.len(); -// let source_cols_count = source.column_count(referenced_tables); -// let start_reg = program.alloc_registers(sort_keys_count); -// source.result_columns(program, referenced_tables, m, None)?; - -// for (i, (expr, _)) in key.iter().enumerate() { -// let key_reg = start_reg + i; -// translate_expr( -// program, -// Some(referenced_tables), -// expr, -// key_reg, -// None, -// m.result_set_register_start, -// )?; -// } - -// let sort_metadata = m.sorts.get_mut(id).unwrap(); -// program.emit_insn(Insn::MakeRecord { -// start_reg, -// count: sort_keys_count + source_cols_count, -// dest_reg: sort_metadata.sorter_data_register, -// }); - -// program.emit_insn(Insn::SorterInsert { -// cursor_id: sort_metadata.sort_cursor, -// record_reg: sort_metadata.sorter_data_register, -// }); - -// Ok(OpStepResult::Continue) -// } -// #[allow(clippy::never_loop)] -// ORDER_SORT_AND_OPEN_LOOP => { -// loop { -// match source.step(program, m, referenced_tables)? { -// OpStepResult::Done => { -// break; -// } -// _ => unreachable!(), -// } -// } -// program.resolve_label( -// m.termination_label_stack.pop().unwrap(), -// program.offset(), -// ); -// let column_names = source.column_names(); -// let mut pseudo_columns = vec![]; -// for (i, _) in key.iter().enumerate() { -// pseudo_columns.push(Column { -// name: format!("sort_key_{}", i), -// primary_key: false, -// ty: crate::schema::Type::Null, -// }); -// } -// for name in column_names { -// pseudo_columns.push(Column { -// name: name.clone(), -// primary_key: false, -// ty: crate::schema::Type::Null, -// }); -// } - -// let num_fields = pseudo_columns.len(); - -// let pseudo_cursor = program.alloc_cursor_id( -// None, -// Some(Table::Pseudo(Rc::new(PseudoTable { -// columns: pseudo_columns, -// }))), -// ); -// let sort_metadata = m.sorts.get(id).unwrap(); - -// program.emit_insn(Insn::OpenPseudo { -// cursor_id: pseudo_cursor, -// content_reg: sort_metadata.sorter_data_register, -// num_fields, -// }); - -// program.emit_insn_with_label_dependency( -// Insn::SorterSort { -// cursor_id: sort_metadata.sort_cursor, -// pc_if_empty: sort_metadata.done_label, -// }, -// sort_metadata.done_label, -// ); - -// program.defer_label_resolution( -// sort_metadata.sorter_data_label, -// program.offset() as usize, -// ); -// program.emit_insn(Insn::SorterData { -// cursor_id: sort_metadata.sort_cursor, -// dest_reg: sort_metadata.sorter_data_register, -// pseudo_cursor, -// }); - -// let sort_metadata = m.sorts.get_mut(id).unwrap(); - -// sort_metadata.pseudo_table_cursor = pseudo_cursor; - -// Ok(OpStepResult::ReadyToEmit) -// } -// ORDER_NEXT => { -// let sort_metadata = m.sorts.get(id).unwrap(); -// program.emit_insn_with_label_dependency( -// Insn::SorterNext { -// cursor_id: sort_metadata.sort_cursor, -// pc_if_next: sort_metadata.sorter_data_label, -// }, -// sort_metadata.sorter_data_label, -// ); - -// program.resolve_label(sort_metadata.done_label, program.offset()); - -// Ok(OpStepResult::Done) -// } -// _ => unreachable!(), -// } -// } -// SourceOperator::Nothing => Ok(OpStepResult::Done), -// } -// } -// fn result_columns( -// &self, -// program: &mut ProgramBuilder, -// referenced_tables: &[BTreeTableReference], -// m: &mut Metadata, -// cursor_override: Option<&SortCursorOverride>, -// ) -> Result { -// let col_count = self.column_count(referenced_tables); -// match self { -// SourceOperator::Scan { -// table_reference, .. -// } => { -// let start_reg = program.alloc_registers(col_count); -// let table = cursor_override -// .map(|c| c.pseudo_table.clone()) -// .unwrap_or_else(|| Table::BTree(table_reference.table.clone())); -// let cursor_id = cursor_override.map(|c| c.cursor_id).unwrap_or_else(|| { -// program.resolve_cursor_id(&table_reference.table_identifier, None) -// }); -// let start_column_offset = cursor_override.map(|c| c.sort_key_len).unwrap_or(0); -// translate_table_columns(program, cursor_id, &table, start_column_offset, start_reg); - -// Ok(start_reg) -// } -// SourceOperator::Search { -// table_reference, .. -// } => { -// let start_reg = program.alloc_registers(col_count); -// let table = cursor_override -// .map(|c| c.pseudo_table.clone()) -// .unwrap_or_else(|| Table::BTree(table_reference.table.clone())); -// let cursor_id = cursor_override.map(|c| c.cursor_id).unwrap_or_else(|| { -// program.resolve_cursor_id(&table_reference.table_identifier, None) -// }); -// let start_column_offset = cursor_override.map(|c| c.sort_key_len).unwrap_or(0); -// translate_table_columns(program, cursor_id, &table, start_column_offset, start_reg); - -// Ok(start_reg) -// } -// SourceOperator::Join { left, right, .. } => { -// let left_start_reg = -// left.result_columns(program, referenced_tables, m, cursor_override)?; -// right.result_columns(program, referenced_tables, m, cursor_override)?; - -// Ok(left_start_reg) -// } -// SourceOperator::Projection { -// id, -// expressions, -// aggregates, -// group_by, -// .. -// } => { -// if aggregates.is_empty() && group_by.is_none() { -// let expr_count = expressions.len(); -// let start_reg = program.alloc_registers(expr_count); -// let mut cur_reg = start_reg; -// m.result_set_register_start = start_reg; -// for expr in expressions { -// translate_expr( -// program, -// Some(referenced_tables), -// expr, -// cur_reg, -// cursor_override.map(|c| c.cursor_id), -// m.result_set_register_start, -// )?; -// cur_reg += 1; -// } - -// return Ok(start_reg); -// } -// let agg_start_reg = m.aggregation_start_registers.get(id).unwrap(); -// program.resolve_label(m.termination_label_stack.pop().unwrap(), program.offset()); -// for (i, agg) in aggregates.iter().enumerate() { -// let agg_result_reg = *agg_start_reg + i; -// program.emit_insn(Insn::AggFinal { -// register: agg_result_reg, -// func: agg.func.clone(), -// }); -// } - -// if let Some(group_by) = group_by { -// let output_row_start_reg = -// program.alloc_registers(aggregates.len() + group_by.len()); -// let group_by_metadata = m.group_bys.get(id).unwrap(); -// program.emit_insn(Insn::Copy { -// src_reg: group_by_metadata.group_exprs_accumulator_register, -// dst_reg: output_row_start_reg, -// amount: group_by.len() - 1, -// }); -// program.emit_insn(Insn::Copy { -// src_reg: *agg_start_reg, -// dst_reg: output_row_start_reg + group_by.len(), -// amount: aggregates.len() - 1, -// }); - -// Ok(output_row_start_reg) -// } else { -// Ok(*agg_start_reg) -// } -// } -// SourceOperator::Filter { .. } => unreachable!("predicates have been pushed down"), -// SourceOperator::Limit { .. } => { -// unimplemented!() -// } -// SourceOperator::Order { id, key, .. } => { -// let cursor_id = m.sorts.get(id).unwrap().pseudo_table_cursor; -// let pseudo_table = program.resolve_cursor_to_table(cursor_id).unwrap(); -// let start_column_offset = key.len(); -// let column_count = pseudo_table.columns().len() - start_column_offset; -// let start_reg = program.alloc_registers(column_count); -// translate_table_columns( -// program, -// cursor_id, -// &pseudo_table, -// start_column_offset, -// start_reg, -// ); - -// Ok(start_reg) -// } -// SourceOperator::Projection { -// expressions, id, .. -// } => { -// let expr_count = expressions.len(); -// let start_reg = program.alloc_registers(expr_count); -// let mut cur_reg = start_reg; -// m.result_set_register_start = start_reg; -// for expr in expressions { -// translate_expr( -// program, -// Some(referenced_tables), -// expr, -// cur_reg, -// cursor_override.map(|c| c.cursor_id), -// m.result_set_register_start, -// )?; -// cur_reg += 1; -// } - -// Ok(start_reg) -// } -// SourceOperator::Nothing => unimplemented!(), -// } -// } -// fn result_row( -// &mut self, -// program: &mut ProgramBuilder, -// referenced_tables: &[BTreeTableReference], -// m: &mut Metadata, -// cursor_override: Option<&SortCursorOverride>, -// ) -> Result<()> { -// match self { -// SourceOperator::Limit { source, limit, .. } => { -// source.result_row(program, referenced_tables, m, cursor_override)?; -// let limit_reg = program.alloc_register(); -// program.emit_insn(Insn::Integer { -// value: *limit as i64, -// dest: limit_reg, -// }); -// program.mark_last_insn_constant(); -// let jump_label = m.termination_label_stack.first().unwrap(); -// program.emit_insn_with_label_dependency( -// Insn::DecrJumpZero { -// reg: limit_reg, -// target_pc: *jump_label, -// }, -// *jump_label, -// ); - -// Ok(()) -// } -// operator => { -// let start_reg = -// operator.result_columns(program, referenced_tables, m, cursor_override)?; -// program.emit_insn(Insn::ResultRow { -// start_reg, -// count: operator.column_count(referenced_tables), -// }); -// Ok(()) -// } -// } -// } -// } - fn prologue() -> Result<(ProgramBuilder, Metadata, BranchOffset, BranchOffset)> { let mut program = ProgramBuilder::new(); let init_label = program.allocate_label(); @@ -1637,13 +129,14 @@ fn prologue() -> Result<(ProgramBuilder, Metadata, BranchOffset, BranchOffset)> let metadata = Metadata { termination_label_stack: vec![halt_label], - aggregation_start_registers: HashMap::new(), - group_bys: HashMap::new(), + group_by_metadata: None, left_joins: HashMap::new(), next_row_labels: HashMap::new(), scan_loop_body_labels: vec![], sorts: HashMap::new(), - result_set_register_start: 0, + aggregation_start_register: None, + result_column_indexes_in_orderby_sorter: HashMap::new(), + result_columns_to_skip_in_orderby_sorter: None, }; Ok((program, metadata, init_label, start_offset)) @@ -1655,10 +148,8 @@ fn epilogue( init_label: BranchOffset, start_offset: BranchOffset, ) -> Result<()> { - program.resolve_label( - metadata.termination_label_stack.pop().unwrap(), - program.offset(), - ); + let halt_label = metadata.termination_label_stack.pop().unwrap(); + program.resolve_label(halt_label, program.offset()); program.emit_insn(Insn::Halt { err_code: 0, description: String::new(), @@ -1684,8 +175,6 @@ pub fn emit_program( ) -> Result { let (mut program, mut metadata, init_label, start_offset) = prologue()?; - let mut order_by_necessary = plan.order_by.is_some(); - // OPEN CURSORS ETC if let Some(ref mut order_by) = plan.order_by { init_order_by(&mut program, order_by, &mut metadata)?; @@ -1716,13 +205,23 @@ pub fn emit_program( &plan.referenced_tables, )?; + let mut order_by_necessary = plan.order_by.is_some(); + // IF GROUP BY, SORT BY GROUPS AND DO AGGREGATION if let Some(ref mut group_by) = plan.group_by { - sort_group_by(&mut program, group_by, &mut metadata)?; - finalize_group_by(&mut program, group_by, &mut metadata)?; + group_by_emit( + &mut program, + &plan.result_columns, + group_by, + plan.order_by.as_ref(), + &plan.aggregates.as_ref().unwrap(), + plan.limit.clone(), + &plan.referenced_tables, + &mut metadata, + )?; } else if let Some(ref mut aggregates) = plan.aggregates { // Example: SELECT sum(x), count(*) FROM t; - finalize_agg_without_group_by(&mut program, aggregates, &mut metadata)?; + agg_without_group_by_emit(&mut program, aggregates, &mut metadata)?; // If we have an aggregate without a group by, we don't need an order by because currently // there can only be a single row result in those cases. order_by_necessary = false; @@ -1797,8 +296,6 @@ fn init_group_by( let group_exprs_comparison_register = program.alloc_registers(group_by.len()); let group_exprs_accumulator_register = program.alloc_registers(group_by.len()); let agg_exprs_start_reg = program.alloc_registers(num_aggs); - m.aggregation_start_registers - .insert(GROUP_BY_ID, agg_exprs_start_reg); let sorter_key_register = program.alloc_register(); let subroutine_accumulator_clear_label = program.allocate_label(); @@ -1847,24 +344,23 @@ fn init_group_by( subroutine_accumulator_clear_label, ); - m.group_bys.insert( - GROUP_BY_ID, - GroupByMetadata { - sort_cursor, - subroutine_accumulator_clear_label, - subroutine_accumulator_clear_return_offset_register, - subroutine_accumulator_output_label, - subroutine_accumulator_output_return_offset_register: program.alloc_register(), - accumulator_indicator_set_true_label: program.allocate_label(), - sorter_data_label, - grouping_done_label, - abort_flag_register, - data_in_accumulator_indicator_register, - group_exprs_accumulator_register, - group_exprs_comparison_register, - sorter_key_register, - }, - ); + m.aggregation_start_register = Some(agg_exprs_start_reg); + + m.group_by_metadata = Some(GroupByMetadata { + sort_cursor, + subroutine_accumulator_clear_label, + subroutine_accumulator_clear_return_offset_register, + subroutine_accumulator_output_label, + subroutine_accumulator_output_return_offset_register: program.alloc_register(), + accumulator_indicator_set_true_label: program.allocate_label(), + sorter_data_label, + grouping_done_label, + abort_flag_register, + data_in_accumulator_indicator_register, + group_exprs_accumulator_register, + group_exprs_comparison_register, + sorter_key_register, + }); Ok(()) } @@ -2017,7 +513,7 @@ fn open_loop( predicate, None, condition_metadata, - m.result_set_register_start, + None, )?; } program.resolve_label(jump_target_when_true, program.offset()); @@ -2089,7 +585,7 @@ fn open_loop( expr, None, condition_metadata, - m.result_set_register_start, + None, )?; program.resolve_label(jump_target_when_true, program.offset()); } @@ -2135,7 +631,7 @@ fn open_loop( cmp_expr, cmp_reg, None, - m.result_set_register_start, + None, )?; } ast::Operator::Less | ast::Operator::LessEquals => { @@ -2175,7 +671,7 @@ fn open_loop( cmp_expr, cmp_reg, None, - m.result_set_register_start, + None, )?; } @@ -2273,7 +769,7 @@ fn open_loop( cmp_expr, src_reg, None, - m.result_set_register_start, + None, )?; program.emit_insn_with_label_dependency( Insn::SeekRowid { @@ -2298,7 +794,7 @@ fn open_loop( predicate, None, condition_metadata, - m.result_set_register_start, + None, )?; program.resolve_label(jump_target_when_true, program.offset()); } @@ -2395,46 +891,31 @@ fn inner_loop_source_emit( } => { // TODO: DOESNT WORK YET let sort_keys_count = group_by.len(); - let column_count = sort_keys_count + aggregates.len(); + let aggregate_arguments_count = + aggregates.iter().map(|agg| agg.args.len()).sum::(); + let column_count = sort_keys_count + aggregate_arguments_count; let start_reg = program.alloc_registers(column_count); - for (i, expr) in group_by.iter().enumerate() { - let key_reg = start_reg + i; - translate_expr( - program, - Some(referenced_tables), - expr, - key_reg, - None, - m.result_set_register_start, - )?; + let mut cur_reg = start_reg; + for expr in group_by.iter() { + let key_reg = cur_reg; + cur_reg += 1; + translate_expr(program, Some(referenced_tables), expr, key_reg, None, None)?; } - for (i, agg) in aggregates.iter().enumerate() { - // TODO it's a hack to assume aggregate functions have exactly one argument. - // Counterpoint e.g. GROUP_CONCAT(expr, separator). - // + for agg in aggregates.iter() { // Here we are collecting scalars for the group by sorter, which will include // both the group by expressions and the aggregate arguments. // e.g. in `select u.first_name, sum(u.age) from users group by u.first_name` // the sorter will have two scalars: u.first_name and u.age. // these are then sorted by u.first_name, and for each u.first_name, we sum the u.age. - // the actual aggregation is done later in GROUP_BY_SORT_AND_COMPARE below. - // - // This is why we take the first argument of each aggregate function currently. - // It's mostly an artifact of the current architecture being a bit poor; we should recognize - // which scalars are dependencies of aggregate functions and explicitly collect those. - let expr = &agg.args[0]; - let agg_reg = start_reg + sort_keys_count + i; - translate_expr( - program, - Some(referenced_tables), - expr, - agg_reg, - None, - m.result_set_register_start, - )?; + // the actual aggregation is done later. + for expr in agg.args.iter() { + let agg_reg = cur_reg; + cur_reg += 1; + translate_expr(program, Some(referenced_tables), expr, agg_reg, None, None)?; + } } - let group_by_metadata = m.group_bys.get(&GROUP_BY_ID).unwrap(); + let group_by_metadata = m.group_by_metadata.as_ref().unwrap(); program.emit_insn(Insn::MakeRecord { start_reg, @@ -2442,7 +923,6 @@ fn inner_loop_source_emit( dest_reg: group_by_metadata.sorter_key_register, }); - let group_by_metadata = m.group_bys.get(&GROUP_BY_ID).unwrap(); program.emit_insn(Insn::SorterInsert { cursor_id: group_by_metadata.sort_cursor, record_reg: group_by_metadata.sorter_key_register, @@ -2451,42 +931,90 @@ fn inner_loop_source_emit( Ok(()) } InnerLoopEmitTarget::OrderBySorter { order_by } => { - // TODO: DOESNT WORK YET - let sort_keys_count = order_by.len(); - let source_cols_count = result_columns.len(); - let start_reg = program.alloc_registers(sort_keys_count + source_cols_count); + // We need to handle the case where we are emitting to sorter. + // In that case the first columns should be the sort key columns, and the rest is the result columns of the select. + // In case any of the sort keys are exactly equal to a result column, we need to skip emitting that result column. + // We need to do this before rewriting the result columns to registers because we need to know which columns to skip. + // Moreover, we need to keep track what index in the ORDER BY sorter the result columns have, because the result columns + // should be emitted in the SELECT clause order, not the ORDER BY clause order. + let mut result_columns_to_skip: Option> = None; + for (i, rc) in result_columns.iter().enumerate() { + match rc { + ResultSetColumn::Scalar(expr) => { + let found = order_by.iter().enumerate().find(|(_, (e, _))| e == expr); + if let Some((j, _)) = found { + if let Some(ref mut v) = result_columns_to_skip { + v.push(i); + } else { + result_columns_to_skip = Some(vec![i]); + } + m.result_column_indexes_in_orderby_sorter.insert(i, j); + } + } + ResultSetColumn::Agg(agg) => { + let found = order_by + .iter() + .enumerate() + .find(|(_, (expr, _))| expr == &agg.original_expr); + if let Some((j, _)) = found { + if let Some(ref mut v) = result_columns_to_skip { + v.push(i); + } else { + result_columns_to_skip = Some(vec![i]); + } + m.result_column_indexes_in_orderby_sorter.insert(i, j); + } + } + ResultSetColumn::ComputedAgg(_) => { + unreachable!( + "ComputedAgg should have been rewritten to a normal agg before emit" + ); + } + } + } + let order_by_len = order_by.len(); + let result_columns_to_skip_len = result_columns_to_skip + .as_ref() + .map(|v| v.len()) + .unwrap_or(0); + let orderby_sorter_column_count = + order_by_len + result_columns.len() - result_columns_to_skip_len; + let start_reg = program.alloc_registers(orderby_sorter_column_count); for (i, (expr, _)) in order_by.iter().enumerate() { let key_reg = start_reg + i; - translate_expr( - program, - Some(referenced_tables), - expr, - key_reg, - None, - m.result_set_register_start, - )?; + translate_expr(program, Some(referenced_tables), expr, key_reg, None, None)?; } - for (i, expr) in result_columns.iter().enumerate() { - match expr { + let mut cur_reg = start_reg + order_by_len; + let mut cur_idx_in_orderby_sorter = order_by_len; + for (i, rc) in result_columns.iter().enumerate() { + if let Some(ref v) = result_columns_to_skip { + if v.contains(&i) { + continue; + } + } + match rc { ResultSetColumn::Scalar(expr) => { - let reg = start_reg + sort_keys_count + i; translate_expr( program, Some(referenced_tables), expr, - reg, + cur_reg, + None, None, - m.result_set_register_start, )?; } - other => todo!("{:?}", other), + other => unreachable!("{:?}", other), } + m.result_column_indexes_in_orderby_sorter + .insert(i, cur_idx_in_orderby_sorter); + cur_idx_in_orderby_sorter += 1; + cur_reg += 1; } let sort_metadata = m.sorts.get_mut(&ORDER_BY_ID).unwrap(); program.emit_insn(Insn::MakeRecord { start_reg, - count: sort_keys_count + source_cols_count, + count: orderby_sorter_column_count, dest_reg: sort_metadata.sorter_data_register, }); @@ -2503,8 +1031,7 @@ fn inner_loop_source_emit( m.termination_label_stack.push(agg_final_label); let num_aggs = aggregates.len(); let start_reg = program.alloc_registers(result_columns.len()); - m.aggregation_start_registers - .insert(AGG_WITHOUT_GROUP_BY_ID, start_reg); + m.aggregation_start_register = Some(start_reg); for (i, agg) in aggregates.iter().enumerate() { let reg = start_reg + i; translate_aggregation(program, referenced_tables, agg, reg, None)?; @@ -2513,14 +1040,7 @@ fn inner_loop_source_emit( match expr { ResultSetColumn::Scalar(expr) => { let reg = start_reg + num_aggs + i; - translate_expr( - program, - Some(referenced_tables), - expr, - reg, - None, - m.result_set_register_start, - )?; + translate_expr(program, Some(referenced_tables), expr, reg, None, None)?; } ResultSetColumn::Agg(_) => { /* do nothing, aggregates are computed above */ } other => unreachable!("Unexpected non-scalar result column: {:?}", other), @@ -2535,16 +1055,12 @@ fn inner_loop_source_emit( match expr { ResultSetColumn::Scalar(expr) => { let reg = start_reg + i; - translate_expr( - program, - Some(referenced_tables), - expr, - reg, - None, - m.result_set_register_start, - )?; + translate_expr(program, Some(referenced_tables), expr, reg, None, None)?; } - other => unreachable!("Unexpected non-scalar result column: {:?}", other), + other => unreachable!( + "Unexpected non-scalar result column in inner loop: {:?}", + other + ), } } program.emit_insn(Insn::ResultRow { @@ -2703,44 +1219,461 @@ fn close_loop( Ok(()) } - SourceOperator::Nothing => { - unreachable!() - } + SourceOperator::Nothing => Ok(()), } } -fn sort_group_by( +fn group_by_emit( program: &mut ProgramBuilder, + result_columns: &Vec, group_by: &Vec, + order_by: Option<&Vec<(ast::Expr, Direction)>>, + aggregates: &Vec, + limit: Option, + referenced_tables: &[BTreeTableReference], m: &mut Metadata, ) -> Result<()> { - todo!() + let group_by_metadata = m.group_by_metadata.as_mut().unwrap(); + + let GroupByMetadata { + group_exprs_comparison_register: comparison_register, + subroutine_accumulator_output_return_offset_register, + subroutine_accumulator_output_label, + subroutine_accumulator_clear_return_offset_register, + subroutine_accumulator_clear_label, + data_in_accumulator_indicator_register, + accumulator_indicator_set_true_label, + group_exprs_accumulator_register: group_exprs_start_register, + abort_flag_register, + sorter_key_register, + .. + } = *group_by_metadata; + let halt_label = *m.termination_label_stack.first().unwrap(); + + // all group by columns and all arguments of agg functions are in the sorter. + // the sort keys are the group by columns (the aggregation within groups is done based on how long the sort keys remain the same) + let sorter_column_count = + group_by.len() + aggregates.iter().map(|agg| agg.args.len()).sum::(); + // sorter column names do not matter + let pseudo_columns = (0..sorter_column_count) + .map(|i| Column { + name: i.to_string(), + primary_key: false, + ty: crate::schema::Type::Null, + }) + .collect::>(); + + // A pseudo table is a "fake" table to which we read one row at a time from the sorter + let pseudo_table = Rc::new(PseudoTable { + columns: pseudo_columns, + }); + + let pseudo_cursor = program.alloc_cursor_id(None, Some(Table::Pseudo(pseudo_table.clone()))); + + program.emit_insn(Insn::OpenPseudo { + cursor_id: pseudo_cursor, + content_reg: sorter_key_register, + num_fields: sorter_column_count, + }); + + // Sort the sorter based on the group by columns + program.emit_insn_with_label_dependency( + Insn::SorterSort { + cursor_id: group_by_metadata.sort_cursor, + pc_if_empty: group_by_metadata.grouping_done_label, + }, + group_by_metadata.grouping_done_label, + ); + + program.defer_label_resolution( + group_by_metadata.sorter_data_label, + program.offset() as usize, + ); + // Read a row from the sorted data in the sorter into the pseudo cursor + program.emit_insn(Insn::SorterData { + cursor_id: group_by_metadata.sort_cursor, + dest_reg: group_by_metadata.sorter_key_register, + pseudo_cursor, + }); + + // Read the group by columns from the pseudo cursor + let groups_start_reg = program.alloc_registers(group_by.len()); + for (i, expr) in group_by.iter().enumerate() { + let sorter_column_index = i; + let group_reg = groups_start_reg + i; + program.emit_insn(Insn::Column { + cursor_id: pseudo_cursor, + column: sorter_column_index, + dest: group_reg, + }); + } + + // Compare the group by columns to the previous group by columns to see if we are at a new group or not + program.emit_insn(Insn::Compare { + start_reg_a: comparison_register, + start_reg_b: groups_start_reg, + count: group_by.len(), + }); + + let agg_step_label = program.allocate_label(); + + program.add_comment( + program.offset(), + "start new group if comparison is not equal", + ); + // If we are at a new group, continue. If we are at the same group, jump to the aggregation step (i.e. accumulate more values into the aggregations) + program.emit_insn_with_label_dependency( + Insn::Jump { + target_pc_lt: program.offset() + 1, + target_pc_eq: agg_step_label, + target_pc_gt: program.offset() + 1, + }, + agg_step_label, + ); + + // New group, move current group by columns into the comparison register + program.emit_insn(Insn::Move { + source_reg: groups_start_reg, + dest_reg: comparison_register, + count: group_by.len(), + }); + + program.add_comment( + program.offset(), + "check if ended group had data, and output if so", + ); + program.emit_insn_with_label_dependency( + Insn::Gosub { + target_pc: subroutine_accumulator_output_label, + return_reg: subroutine_accumulator_output_return_offset_register, + }, + subroutine_accumulator_output_label, + ); + + program.add_comment(program.offset(), "check abort flag"); + program.emit_insn_with_label_dependency( + Insn::IfPos { + reg: abort_flag_register, + target_pc: halt_label, + decrement_by: 0, + }, + m.termination_label_stack[0], + ); + + program.add_comment(program.offset(), "goto clear accumulator subroutine"); + program.emit_insn_with_label_dependency( + Insn::Gosub { + target_pc: subroutine_accumulator_clear_label, + return_reg: subroutine_accumulator_clear_return_offset_register, + }, + subroutine_accumulator_clear_label, + ); + + // Accumulate the values into the aggregations + program.resolve_label(agg_step_label, program.offset()); + let start_reg = m.aggregation_start_register.unwrap(); + let mut cursor_index = group_by.len(); + for (i, agg) in aggregates.iter().enumerate() { + let agg_result_reg = start_reg + i; + translate_aggregation_groupby( + program, + referenced_tables, + pseudo_cursor, + cursor_index, + agg, + agg_result_reg, + )?; + cursor_index += agg.args.len(); + } + + // We only emit the group by columns if we are going to start a new group (i.e. the prev group will not accumulate any more values into the aggregations) + program.add_comment( + program.offset(), + "don't emit group columns if continuing existing group", + ); + program.emit_insn_with_label_dependency( + Insn::If { + target_pc: accumulator_indicator_set_true_label, + reg: data_in_accumulator_indicator_register, + null_reg: 0, // unused in this case + }, + accumulator_indicator_set_true_label, + ); + + // Read the group by columns for a finished group + for (i, expr) in group_by.iter().enumerate() { + let key_reg = group_exprs_start_register + i; + let sorter_column_index = i; + program.emit_insn(Insn::Column { + cursor_id: pseudo_cursor, + column: sorter_column_index, + dest: key_reg, + }); + } + + program.resolve_label(accumulator_indicator_set_true_label, program.offset()); + program.add_comment(program.offset(), "indicate data in accumulator"); + program.emit_insn(Insn::Integer { + value: 1, + dest: data_in_accumulator_indicator_register, + }); + + program.emit_insn_with_label_dependency( + Insn::SorterNext { + cursor_id: group_by_metadata.sort_cursor, + pc_if_next: group_by_metadata.sorter_data_label, + }, + group_by_metadata.sorter_data_label, + ); + + program.resolve_label(group_by_metadata.grouping_done_label, program.offset()); + + program.add_comment(program.offset(), "emit row for final group"); + program.emit_insn_with_label_dependency( + Insn::Gosub { + target_pc: group_by_metadata.subroutine_accumulator_output_label, + return_reg: group_by_metadata.subroutine_accumulator_output_return_offset_register, + }, + group_by_metadata.subroutine_accumulator_output_label, + ); + + program.add_comment(program.offset(), "group by finished"); + let termination_label = m.termination_label_stack[m.termination_label_stack.len() - 2]; + program.emit_insn_with_label_dependency( + Insn::Goto { + target_pc: termination_label, + }, + termination_label, + ); + program.emit_insn(Insn::Integer { + value: 1, + dest: group_by_metadata.abort_flag_register, + }); + program.emit_insn(Insn::Return { + return_reg: group_by_metadata.subroutine_accumulator_output_return_offset_register, + }); + + program.resolve_label( + group_by_metadata.subroutine_accumulator_output_label, + program.offset(), + ); + + program.add_comment(program.offset(), "output group by row subroutine start"); + let termination_label = *m.termination_label_stack.last().unwrap(); + program.emit_insn_with_label_dependency( + Insn::IfPos { + reg: group_by_metadata.data_in_accumulator_indicator_register, + target_pc: termination_label, + decrement_by: 0, + }, + termination_label, + ); + program.emit_insn(Insn::Return { + return_reg: group_by_metadata.subroutine_accumulator_output_return_offset_register, + }); + + let agg_start_reg = m.aggregation_start_register.unwrap(); + program.resolve_label(m.termination_label_stack.pop().unwrap(), program.offset()); + for (i, agg) in aggregates.iter().enumerate() { + let agg_result_reg = agg_start_reg + i; + program.emit_insn(Insn::AggFinal { + register: agg_result_reg, + func: agg.func.clone(), + }); + } + + // TODO handle result column expressions like LENGTH(SUM(x)) + // we now have the group by columns in registers (group_exprs_start_register..group_exprs_start_register + group_by.len() - 1) + // and the agg results in (agg_start_reg..agg_start_reg + aggregates.len() - 1) + // we need to call translate_expr on each result column, but replace the expr with a register copy in case any part of the + // result column expression matches a) a group by column or b) an aggregation result. + let mut precomputed_exprs_to_register = Vec::with_capacity(aggregates.len() + group_by.len()); + for (i, expr) in group_by.iter().enumerate() { + precomputed_exprs_to_register.push((expr, group_exprs_start_register + i)); + } + for (i, agg) in aggregates.iter().enumerate() { + precomputed_exprs_to_register.push((&agg.original_expr, agg_start_reg + i)); + } + + // We need to handle the case where we are emitting to sorter. + // In that case the first columns should be the sort key columns, and the rest is the result columns of the select. + // In case any of the sort keys are exactly equal to a result column, we need to skip emitting that result column. + // We need to do this before rewriting the result columns to registers because we need to know which columns to skip. + // Moreover, we need to keep track what index in the ORDER BY sorter the result columns have, because the result columns + // should be emitted in the SELECT clause order, not the ORDER BY clause order. + let mut result_columns_to_skip: Option> = None; + if let Some(order_by) = order_by { + for (i, rc) in result_columns.iter().enumerate() { + match rc { + ResultSetColumn::Scalar(expr) => { + let found = order_by.iter().enumerate().find(|(_, (e, _))| e == expr); + if let Some((j, _)) = found { + if let Some(ref mut v) = result_columns_to_skip { + v.push(i); + } else { + result_columns_to_skip = Some(vec![i]); + } + m.result_column_indexes_in_orderby_sorter.insert(i, j); + } + } + ResultSetColumn::Agg(agg) => { + let found = order_by + .iter() + .enumerate() + .find(|(_, (expr, _))| expr == &agg.original_expr); + if let Some((j, _)) = found { + if let Some(ref mut v) = result_columns_to_skip { + v.push(i); + } else { + result_columns_to_skip = Some(vec![i]); + } + m.result_column_indexes_in_orderby_sorter.insert(i, j); + } + } + ResultSetColumn::ComputedAgg(_) => { + unreachable!( + "ComputedAgg should have been rewritten to a normal agg before emit" + ); + } + } + } + } + let order_by_len = order_by.as_ref().map(|v| v.len()).unwrap_or(0); + let result_columns_to_skip_len = result_columns_to_skip + .as_ref() + .map(|v| v.len()) + .unwrap_or(0); + let output_row_start_reg = + program.alloc_registers(result_columns.len() + order_by_len - result_columns_to_skip_len); + let mut cur_reg = output_row_start_reg; + if let Some(order_by) = order_by { + for (expr, _) in order_by.iter() { + translate_expr( + program, + Some(referenced_tables), + expr, + cur_reg, + None, + Some(&precomputed_exprs_to_register), + )?; + cur_reg += 1; + } + } + let mut res_col_idx_in_orderby_sorter = order_by_len; + for (i, rc) in result_columns.iter().enumerate() { + if let Some(ref v) = result_columns_to_skip { + if v.contains(&i) { + continue; + } + } + match rc { + ResultSetColumn::Scalar(expr) => { + translate_expr( + program, + Some(referenced_tables), + expr, + cur_reg, + None, + Some(&precomputed_exprs_to_register), + )?; + } + ResultSetColumn::Agg(agg) => { + let found = aggregates.iter().enumerate().find(|(_, a)| **a == *agg); + if let Some((i, _)) = found { + program.emit_insn(Insn::Copy { + src_reg: agg_start_reg + i, + dst_reg: cur_reg, + amount: 0, + }); + } else { + unreachable!("agg {:?} not found", agg); + } + } + ResultSetColumn::ComputedAgg(agg) => { + unreachable!( + "ComputedAgg should have been rewritten to a normal agg before emit: {:?}", + agg + ); + } + } + m.result_column_indexes_in_orderby_sorter + .insert(i, res_col_idx_in_orderby_sorter); + res_col_idx_in_orderby_sorter += 1; + cur_reg += 1; + } + + match order_by { + None => { + if let Some(limit) = limit { + let limit_reg = program.alloc_register(); + program.emit_insn(Insn::Integer { + value: limit as i64, + dest: limit_reg, + }); + program.mark_last_insn_constant(); + program.emit_insn(Insn::ResultRow { + start_reg: output_row_start_reg, + count: aggregates.len() + group_by.len(), + }); + program.emit_insn_with_label_dependency( + Insn::DecrJumpZero { + reg: limit_reg, + target_pc: *m.termination_label_stack.last().unwrap(), + }, + *m.termination_label_stack.last().unwrap(), + ); + } + } + Some(_) => { + program.emit_insn(Insn::MakeRecord { + start_reg: output_row_start_reg, + count: aggregates.len() + group_by.len(), + dest_reg: group_by_metadata.sorter_key_register, + }); + + program.emit_insn(Insn::SorterInsert { + cursor_id: m.sorts.get(&ORDER_BY_ID).unwrap().sort_cursor, + record_reg: group_by_metadata.sorter_key_register, + }); + } + } + + program.emit_insn(Insn::Return { + return_reg: group_by_metadata.subroutine_accumulator_output_return_offset_register, + }); + + program.add_comment(program.offset(), "clear accumulator subroutine start"); + program.resolve_label( + group_by_metadata.subroutine_accumulator_clear_label, + program.offset(), + ); + let start_reg = group_by_metadata.group_exprs_accumulator_register; + program.emit_insn(Insn::Null { + dest: start_reg, + dest_end: Some(start_reg + group_by.len() + aggregates.len() - 1), + }); + + program.emit_insn(Insn::Integer { + value: 0, + dest: group_by_metadata.data_in_accumulator_indicator_register, + }); + program.emit_insn(Insn::Return { + return_reg: group_by_metadata.subroutine_accumulator_clear_return_offset_register, + }); + + m.result_columns_to_skip_in_orderby_sorter = result_columns_to_skip; + + Ok(()) } -fn finalize_group_by( - program: &mut ProgramBuilder, - group_by: &Vec, - m: &mut Metadata, -) -> Result<()> { - todo!() -} - -enum FinalizeGroupByEmitTarget { - OrderBySorter(usize), - ResultRow, -} - -fn finalize_agg_without_group_by( +fn agg_without_group_by_emit( program: &mut ProgramBuilder, aggregates: &Vec, m: &mut Metadata, ) -> Result<()> { - let agg_start_reg = m - .aggregation_start_registers - .get(&AGG_WITHOUT_GROUP_BY_ID) - .unwrap(); + let agg_start_reg = m.aggregation_start_register.unwrap(); for (i, agg) in aggregates.iter().enumerate() { - let agg_result_reg = *agg_start_reg + i; + let agg_result_reg = agg_start_reg + i; program.emit_insn(Insn::AggFinal { register: agg_result_reg, func: agg.func.clone(), @@ -2748,7 +1681,7 @@ fn finalize_agg_without_group_by( } let output_reg = program.alloc_registers(aggregates.len()); program.emit_insn(Insn::Copy { - src_reg: *agg_start_reg, + src_reg: agg_start_reg, dst_reg: output_reg, amount: aggregates.len() - 1, }); @@ -2778,7 +1711,12 @@ fn sort_order_by( ty: crate::schema::Type::Null, }); } - for expr in result_columns.iter() { + for (i, expr) in result_columns.iter().enumerate() { + if let Some(ref v) = m.result_columns_to_skip_in_orderby_sorter { + if v.contains(&i) { + continue; + } + } pseudo_columns.push(Column { name: match expr { ResultSetColumn::Scalar(expr) => expr.to_string(), @@ -2790,7 +1728,11 @@ fn sort_order_by( }); } - let num_fields = pseudo_columns.len(); + let num_columns_in_sorter = order_by.len() + result_columns.len() + - m.result_columns_to_skip_in_orderby_sorter + .as_ref() + .map(|v| v.len()) + .unwrap_or(0); let pseudo_cursor = program.alloc_cursor_id( None, @@ -2803,7 +1745,7 @@ fn sort_order_by( program.emit_insn(Insn::OpenPseudo { cursor_id: pseudo_cursor, content_reg: sort_metadata.sorter_data_register, - num_fields, + num_fields: num_columns_in_sorter, }); program.emit_insn_with_label_dependency( @@ -2823,25 +1765,20 @@ fn sort_order_by( let sort_metadata = m.sorts.get_mut(&ORDER_BY_ID).unwrap(); - sort_metadata.pseudo_table_cursor = pseudo_cursor; - // EMIT COLUMNS FROM SORTER AND EMIT ROW let cursor_id = pseudo_cursor; - let pseudo_table = program.resolve_cursor_to_table(cursor_id).unwrap(); - let start_column_offset = order_by.len(); - let column_count = pseudo_table.columns().len() - start_column_offset; - let start_reg = program.alloc_registers(column_count); - for i in 0..column_count { + let start_reg = program.alloc_registers(result_columns.len()); + for i in 0..result_columns.len() { let reg = start_reg + i; program.emit_insn(Insn::Column { cursor_id, - column: start_column_offset + i, + column: m.result_column_indexes_in_orderby_sorter[&i], dest: reg, }); } program.emit_insn(Insn::ResultRow { start_reg, - count: column_count, + count: result_columns.len(), }); if let Some(limit) = limit { diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 01dcae16c..7f74ef509 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -23,7 +23,7 @@ pub fn translate_condition_expr( expr: &ast::Expr, cursor_hint: Option, condition_metadata: ConditionMetadata, - result_set_register_start: usize, + precomputed_exprs_to_registers: Option<&Vec<(&ast::Expr, usize)>>, ) -> Result<()> { match expr { ast::Expr::Between { .. } => todo!(), @@ -39,7 +39,7 @@ pub fn translate_condition_expr( jump_if_condition_is_true: false, ..condition_metadata }, - result_set_register_start, + precomputed_exprs_to_registers, ); let _ = translate_condition_expr( program, @@ -47,7 +47,7 @@ pub fn translate_condition_expr( rhs, cursor_hint, condition_metadata, - result_set_register_start, + precomputed_exprs_to_registers, ); } ast::Expr::Binary(lhs, ast::Operator::Or, rhs) => { @@ -63,7 +63,7 @@ pub fn translate_condition_expr( jump_target_when_false, ..condition_metadata }, - result_set_register_start, + precomputed_exprs_to_registers, ); program.resolve_label(jump_target_when_false, program.offset()); let _ = translate_condition_expr( @@ -72,7 +72,7 @@ pub fn translate_condition_expr( rhs, cursor_hint, condition_metadata, - result_set_register_start, + precomputed_exprs_to_registers, ); } ast::Expr::Binary(lhs, op, rhs) => { @@ -83,7 +83,7 @@ pub fn translate_condition_expr( lhs, lhs_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, ); if let ast::Expr::Literal(_) = lhs.as_ref() { program.mark_last_insn_constant() @@ -95,7 +95,7 @@ pub fn translate_condition_expr( rhs, rhs_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, ); if let ast::Expr::Literal(_) = rhs.as_ref() { program.mark_last_insn_constant() @@ -344,7 +344,7 @@ pub fn translate_condition_expr( lhs, lhs_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; let rhs = rhs.as_ref().unwrap(); @@ -374,7 +374,7 @@ pub fn translate_condition_expr( expr, rhs_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; // If this is not the last condition, we need to jump to the 'jump_target_when_true' label if the condition is true. if !last_condition { @@ -418,7 +418,7 @@ pub fn translate_condition_expr( expr, rhs_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; program.emit_insn_with_label_dependency( Insn::Eq { @@ -464,7 +464,7 @@ pub fn translate_condition_expr( lhs, column_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if let ast::Expr::Literal(_) = lhs.as_ref() { program.mark_last_insn_constant(); @@ -475,7 +475,7 @@ pub fn translate_condition_expr( rhs, pattern_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if let ast::Expr::Literal(_) = rhs.as_ref() { program.mark_last_insn_constant(); @@ -549,7 +549,7 @@ pub fn translate_condition_expr( expr, cursor_hint, condition_metadata, - result_set_register_start, + precomputed_exprs_to_registers, ); } } @@ -564,27 +564,40 @@ pub fn translate_expr( expr: &ast::Expr, target_register: usize, cursor_hint: Option, - result_set_register_start: usize, + precomputed_exprs_to_registers: Option<&Vec<(&ast::Expr, usize)>>, ) -> Result { + if let Some(precomputed_exprs_to_registers) = precomputed_exprs_to_registers { + for (precomputed_expr, reg) in precomputed_exprs_to_registers.iter() { + if expr == *precomputed_expr { + program.emit_insn(Insn::Copy { + src_reg: *reg, + dst_reg: target_register, + amount: 0, + }); + return Ok(target_register); + } + } + } match expr { - ast::Expr::AggRef { index } => todo!(), ast::Expr::Between { .. } => todo!(), ast::Expr::Binary(e1, op, e2) => { - let e1_reg = translate_expr( + let e1_reg = program.alloc_register(); + translate_expr( program, referenced_tables, e1, - target_register, + e1_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; - let e2_reg = translate_expr( + let e2_reg = program.alloc_register(); + translate_expr( program, referenced_tables, e2, - target_register, + e2_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; match op { @@ -708,7 +721,7 @@ pub fn translate_expr( expr, reg_expr, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; let reg_type = program.alloc_register(); program.emit_insn(Insn::String8 { @@ -781,7 +794,7 @@ pub fn translate_expr( &args[0], regs, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; program.emit_insn(Insn::Function { constant_mask: 0, @@ -808,7 +821,7 @@ pub fn translate_expr( arg, reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; } @@ -846,7 +859,7 @@ pub fn translate_expr( arg, target_register, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if index < args.len() - 1 { program.emit_insn_with_label_dependency( @@ -882,7 +895,7 @@ pub fn translate_expr( arg, reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; } program.emit_insn(Insn::Function { @@ -915,7 +928,7 @@ pub fn translate_expr( arg, reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; } program.emit_insn(Insn::Function { @@ -952,7 +965,7 @@ pub fn translate_expr( &args[0], temp_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; program.emit_insn(Insn::NotNull { reg: temp_reg, @@ -965,7 +978,7 @@ pub fn translate_expr( &args[1], temp_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; program.emit_insn(Insn::Copy { src_reg: temp_reg, @@ -998,7 +1011,7 @@ pub fn translate_expr( arg, reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if let ast::Expr::Literal(_) = arg { program.mark_last_insn_constant() @@ -1046,7 +1059,7 @@ pub fn translate_expr( &args[0], regs, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; program.emit_insn(Insn::Function { constant_mask: 0, @@ -1083,7 +1096,7 @@ pub fn translate_expr( arg, target_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; } } @@ -1121,7 +1134,7 @@ pub fn translate_expr( &args[0], str_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; translate_expr( program, @@ -1129,7 +1142,7 @@ pub fn translate_expr( &args[1], start_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if args.len() == 3 { translate_expr( @@ -1138,7 +1151,7 @@ pub fn translate_expr( &args[2], length_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; } @@ -1168,7 +1181,7 @@ pub fn translate_expr( &args[0], regs, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; program.emit_insn(Insn::Function { constant_mask: 0, @@ -1192,7 +1205,7 @@ pub fn translate_expr( &args[0], arg_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; start_reg = arg_reg; } @@ -1217,7 +1230,7 @@ pub fn translate_expr( arg, target_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; } } @@ -1257,7 +1270,7 @@ pub fn translate_expr( arg, reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if let ast::Expr::Literal(_) = arg { program.mark_last_insn_constant(); @@ -1290,7 +1303,7 @@ pub fn translate_expr( arg, reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if let ast::Expr::Literal(_) = arg { program.mark_last_insn_constant() @@ -1324,7 +1337,7 @@ pub fn translate_expr( arg, reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; if let ast::Expr::Literal(_) = arg { program.mark_last_insn_constant() @@ -1362,7 +1375,7 @@ pub fn translate_expr( &args[0], first_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; let second_reg = program.alloc_register(); translate_expr( @@ -1371,7 +1384,7 @@ pub fn translate_expr( &args[1], second_reg, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; program.emit_insn(Insn::Function { constant_mask: 0, @@ -1412,7 +1425,7 @@ pub fn translate_expr( database: _, table, column, - is_primary_key, + is_rowid_alias: is_primary_key, } => { let tbl_ref = referenced_tables.as_ref().unwrap().get(*table).unwrap(); let cursor_id = program.resolve_cursor_id(&tbl_ref.table_identifier, cursor_hint); @@ -1503,7 +1516,7 @@ pub fn translate_expr( &exprs[0], target_register, cursor_hint, - result_set_register_start, + precomputed_exprs_to_registers, )?; } else { // Parenthesized expressions with multiple arguments are reserved for special cases @@ -1557,19 +1570,6 @@ fn wrap_eval_jump_expr( program.preassign_label_to_next_insn(if_true_label); } -pub fn resolve_ident_pseudo_table(ident: &String, pseudo_table: &PseudoTable) -> Result { - let res = pseudo_table - .columns - .iter() - .enumerate() - .find(|(_, col)| col.name == *ident); - if res.is_some() { - let (idx, _) = res.unwrap(); - return Ok(idx); - } - crate::bail_parse_error!("column with name {} not found", ident.as_str()); -} - pub fn maybe_apply_affinity(col_type: Type, target_register: usize, program: &mut ProgramBuilder) { if col_type == crate::schema::Type::Real { program.emit_insn(Insn::RealAffinity { @@ -1578,35 +1578,6 @@ pub fn maybe_apply_affinity(col_type: Type, target_register: usize, program: &mu } } -pub fn translate_table_columns( - program: &mut ProgramBuilder, - cursor_id: usize, - table: &Table, - start_column_offset: usize, - start_reg: usize, -) -> usize { - let mut cur_reg = start_reg; - for i in start_column_offset..table.columns().len() { - let is_rowid = table.column_is_rowid_alias(table.get_column_at(i)); - let col_type = &table.get_column_at(i).ty; - if is_rowid { - program.emit_insn(Insn::RowId { - cursor_id, - dest: cur_reg, - }); - } else { - program.emit_insn(Insn::Column { - cursor_id, - column: i, - dest: cur_reg, - }); - } - maybe_apply_affinity(*col_type, cur_reg, program); - cur_reg += 1; - } - cur_reg -} - pub fn translate_aggregation( program: &mut ProgramBuilder, referenced_tables: &[BTreeTableReference], @@ -1627,7 +1598,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, )?; program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -1649,7 +1620,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, ); expr_reg }; @@ -1692,7 +1663,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, )?; translate_expr( program, @@ -1700,7 +1671,7 @@ pub fn translate_aggregation( &delimiter_expr, delimiter_reg, cursor_hint, - 0, + None, )?; program.emit_insn(Insn::AggStep { @@ -1724,7 +1695,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, )?; program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -1746,7 +1717,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, )?; program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -1783,7 +1754,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, )?; translate_expr( program, @@ -1791,7 +1762,7 @@ pub fn translate_aggregation( &delimiter_expr, delimiter_reg, cursor_hint, - 0, + None, )?; program.emit_insn(Insn::AggStep { @@ -1815,7 +1786,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, )?; program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -1837,7 +1808,7 @@ pub fn translate_aggregation( expr, expr_reg, cursor_hint, - 0, + None, )?; program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -1850,3 +1821,186 @@ pub fn translate_aggregation( }; Ok(dest) } + +pub fn translate_aggregation_groupby( + program: &mut ProgramBuilder, + referenced_tables: &[BTreeTableReference], + group_by_sorter_cursor_id: usize, + cursor_index: usize, + agg: &Aggregate, + target_register: usize, +) -> Result { + let emit_column = |program: &mut ProgramBuilder, expr_reg: usize| { + program.emit_insn(Insn::Column { + cursor_id: group_by_sorter_cursor_id, + column: cursor_index, + dest: expr_reg, + }); + }; + let dest = match agg.func { + AggFunc::Avg => { + if agg.args.len() != 1 { + crate::bail_parse_error!("avg bad number of arguments"); + } + let expr_reg = program.alloc_register(); + emit_column(program, expr_reg); + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: 0, + func: AggFunc::Avg, + }); + target_register + } + AggFunc::Count => { + let expr_reg = program.alloc_register(); + emit_column(program, expr_reg); + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: 0, + func: AggFunc::Count, + }); + target_register + } + AggFunc::GroupConcat => { + if agg.args.len() != 1 && agg.args.len() != 2 { + crate::bail_parse_error!("group_concat bad number of arguments"); + } + + let expr_reg = program.alloc_register(); + let delimiter_reg = program.alloc_register(); + + let delimiter_expr: ast::Expr; + + if agg.args.len() == 2 { + match &agg.args[1] { + ast::Expr::Column { .. } => { + delimiter_expr = agg.args[1].clone(); + } + ast::Expr::Literal(ast::Literal::String(s)) => { + delimiter_expr = ast::Expr::Literal(ast::Literal::String(s.to_string())); + } + _ => crate::bail_parse_error!("Incorrect delimiter parameter"), + }; + } else { + delimiter_expr = ast::Expr::Literal(ast::Literal::String(String::from("\",\""))); + } + + emit_column(program, expr_reg); + translate_expr( + program, + Some(referenced_tables), + &delimiter_expr, + delimiter_reg, + None, + None, + )?; + + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: delimiter_reg, + func: AggFunc::GroupConcat, + }); + + target_register + } + AggFunc::Max => { + if agg.args.len() != 1 { + crate::bail_parse_error!("max bad number of arguments"); + } + let expr_reg = program.alloc_register(); + emit_column(program, expr_reg); + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: 0, + func: AggFunc::Max, + }); + target_register + } + AggFunc::Min => { + if agg.args.len() != 1 { + crate::bail_parse_error!("min bad number of arguments"); + } + let expr_reg = program.alloc_register(); + emit_column(program, expr_reg); + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: 0, + func: AggFunc::Min, + }); + target_register + } + AggFunc::StringAgg => { + if agg.args.len() != 2 { + crate::bail_parse_error!("string_agg bad number of arguments"); + } + + let expr_reg = program.alloc_register(); + let delimiter_reg = program.alloc_register(); + + let delimiter_expr: ast::Expr; + + match &agg.args[1] { + ast::Expr::Column { .. } => { + delimiter_expr = agg.args[1].clone(); + } + ast::Expr::Literal(ast::Literal::String(s)) => { + delimiter_expr = ast::Expr::Literal(ast::Literal::String(s.to_string())); + } + _ => crate::bail_parse_error!("Incorrect delimiter parameter"), + }; + + emit_column(program, expr_reg); + translate_expr( + program, + Some(referenced_tables), + &delimiter_expr, + delimiter_reg, + None, + None, + )?; + + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: delimiter_reg, + func: AggFunc::StringAgg, + }); + + target_register + } + AggFunc::Sum => { + if agg.args.len() != 1 { + crate::bail_parse_error!("sum bad number of arguments"); + } + let expr_reg = program.alloc_register(); + emit_column(program, expr_reg); + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: 0, + func: AggFunc::Sum, + }); + target_register + } + AggFunc::Total => { + if agg.args.len() != 1 { + crate::bail_parse_error!("total bad number of arguments"); + } + let expr_reg = program.alloc_register(); + emit_column(program, expr_reg); + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: 0, + func: AggFunc::Total, + }); + target_register + } + }; + Ok(dest) +} diff --git a/core/translate/insert.rs b/core/translate/insert.rs index 8a5760516..ea890e994 100644 --- a/core/translate/insert.rs +++ b/core/translate/insert.rs @@ -98,7 +98,7 @@ pub fn translate_insert( expr, column_registers_start + col, None, - 0, + None, )?; } program.emit_insn(Insn::Yield { diff --git a/core/translate/optimizer.rs b/core/translate/optimizer.rs index cd08f619f..4635a933a 100644 --- a/core/translate/optimizer.rs +++ b/core/translate/optimizer.rs @@ -25,7 +25,14 @@ pub fn optimize_plan(mut select_plan: Plan) -> Result { { return Ok(Plan { source: SourceOperator::Nothing, - ..select_plan + aggregates: None, + result_columns: vec![], + where_clause: None, + group_by: None, + order_by: None, + limit: None, + referenced_tables: select_plan.referenced_tables, + available_indexes: select_plan.available_indexes, }); } use_indexes( @@ -478,7 +485,7 @@ impl Optimizable for ast::Expr { ast::Expr::Column { table, column, - is_primary_key, + is_rowid_alias: is_primary_key, .. } => *is_primary_key && *table == table_index, _ => false, diff --git a/core/translate/planner.rs b/core/translate/planner.rs index 3e6b73630..8641d3198 100644 --- a/core/translate/planner.rs +++ b/core/translate/planner.rs @@ -68,7 +68,6 @@ fn bind_column_references( referenced_tables: &[BTreeTableReference], ) -> Result<()> { match expr { - ast::Expr::AggRef { .. } => unreachable!(), ast::Expr::Id(id) => { let mut match_result = None; for (tbl_idx, table) in referenced_tables.iter().enumerate() { @@ -93,7 +92,7 @@ fn bind_column_references( database: None, // TODO: support different databases table: tbl_idx, column: col_idx, - is_primary_key, + is_rowid_alias: is_primary_key, }; Ok(()) } @@ -122,7 +121,7 @@ fn bind_column_references( database: None, // TODO: support different databases table: tbl_idx, column: col_idx.unwrap(), - is_primary_key: col.primary_key, + is_rowid_alias: col.primary_key, }; Ok(()) } @@ -276,7 +275,7 @@ pub fn prepare_select_plan<'a>(schema: &Schema, select: ast::Select) -> Result

(schema: &Schema, select: ast::Select) -> Result

(schema: &Schema, select: ast::Select) -> Result

{ resolve_aggregates(&expr, &mut aggregate_expressions); + // TODO: can be compound aggregate + plan.result_columns + .push(ResultSetColumn::Scalar(expr.clone())); } _ => {} } diff --git a/vendored/sqlite3-parser/src/parser/ast/fmt.rs b/vendored/sqlite3-parser/src/parser/ast/fmt.rs index 6b0271919..80f87eefb 100644 --- a/vendored/sqlite3-parser/src/parser/ast/fmt.rs +++ b/vendored/sqlite3-parser/src/parser/ast/fmt.rs @@ -638,7 +638,6 @@ impl ToTokens for Expr { } Self::Id(id) => id.to_tokens(s), Self::Column { .. } => Ok(()), - Self::AggRef { .. } => Ok(()), Self::InList { lhs, not, rhs } => { lhs.to_tokens(s)?; if *not { diff --git a/vendored/sqlite3-parser/src/parser/ast/mod.rs b/vendored/sqlite3-parser/src/parser/ast/mod.rs index ac45b5170..690f5e71c 100644 --- a/vendored/sqlite3-parser/src/parser/ast/mod.rs +++ b/vendored/sqlite3-parser/src/parser/ast/mod.rs @@ -335,13 +335,8 @@ pub enum Expr { table: usize, /// the z in `x.y.z`. index of the column in the table. column: usize, - /// is the column a primary key - is_primary_key: bool, - }, - /// AggRef is a reference to a computed aggregate - AggRef { - /// index of the aggregate in the aggregates vector parsed from the query - index: usize, + /// is the column a rowid alias + is_rowid_alias: bool, }, /// `IN` InList {