diff --git a/core/schema.rs b/core/schema.rs index 829320585..e28a1546d 100644 --- a/core/schema.rs +++ b/core/schema.rs @@ -135,6 +135,14 @@ impl Table { Self::Pseudo(_) => unimplemented!(), } } + + pub fn btree(&self) -> Option> { + match self { + Self::BTree(table) => Some(table.clone()), + Self::Index(_) => None, + Self::Pseudo(_) => None, + } + } } impl PartialEq for Table { diff --git a/core/translate/group_by.rs b/core/translate/group_by.rs index c20466f27..2b6e7afb3 100644 --- a/core/translate/group_by.rs +++ b/core/translate/group_by.rs @@ -4,9 +4,13 @@ use sqlite3_parser::ast; use crate::{ function::AggFunc, - schema::{Column, PseudoTable, Table}, + schema::{Column, PseudoTable}, types::{OwnedRecord, OwnedValue}, - vdbe::{builder::ProgramBuilder, insn::Insn, BranchOffset}, + vdbe::{ + builder::{CursorType, ProgramBuilder}, + insn::Insn, + BranchOffset, + }, Result, }; @@ -50,7 +54,7 @@ pub fn init_group_by( ) -> Result<()> { let num_aggs = aggregates.len(); - let sort_cursor = program.alloc_cursor_id(None, None); + let sort_cursor = program.alloc_cursor_id(None, CursorType::Sorter); let reg_abort_flag = program.alloc_register(); let reg_group_exprs_cmp = program.alloc_registers(group_by.exprs.len()); @@ -175,7 +179,7 @@ pub fn emit_group_by<'a>( columns: pseudo_columns, }); - let pseudo_cursor = program.alloc_cursor_id(None, Some(Table::Pseudo(pseudo_table.clone()))); + let pseudo_cursor = program.alloc_cursor_id(None, CursorType::Pseudo(pseudo_table.clone())); program.emit_insn(Insn::OpenPseudo { cursor_id: pseudo_cursor, diff --git a/core/translate/insert.rs b/core/translate/insert.rs index b9f73ba8c..2dec74248 100644 --- a/core/translate/insert.rs +++ b/core/translate/insert.rs @@ -6,13 +6,18 @@ use sqlite3_parser::ast::{ }; use crate::error::SQLITE_CONSTRAINT_PRIMARYKEY; +use crate::schema::BTreeTable; use crate::util::normalize_ident; use crate::vdbe::BranchOffset; use crate::{ - schema::{Column, Schema, Table}, + schema::{Column, Schema}, storage::sqlite3_ondisk::DatabaseHeader, translate::expr::translate_expr, - vdbe::{builder::ProgramBuilder, insn::Insn, Program}, + vdbe::{ + builder::{CursorType, ProgramBuilder}, + insn::Insn, + Program, + }, SymbolTable, }; use crate::{Connection, Result}; @@ -53,20 +58,15 @@ pub fn translate_insert( Some(table) => table, None => crate::bail_corrupt_error!("Parse error: no such table: {}", table_name), }; - let table = Rc::new(Table::BTree(table)); - if !table.has_rowid() { + if !table.has_rowid { crate::bail_parse_error!("INSERT into WITHOUT ROWID table is not supported"); } let cursor_id = program.alloc_cursor_id( Some(table_name.0.clone()), - Some(table.clone().deref().clone()), + CursorType::BTreeTable(table.clone()), ); - let root_page = match table.as_ref() { - Table::BTree(btree) => btree.root_page, - Table::Index(index) => index.root_page, - Table::Pseudo(_) => todo!(), - }; + let root_page = table.root_page; let values = match body { InsertBody::Select(select, None) => match &select.body.select.deref() { sqlite3_parser::ast::OneSelect::Values(values) => values, @@ -77,9 +77,9 @@ pub fn translate_insert( let column_mappings = resolve_columns_for_insert(&table, columns, values)?; // Check if rowid was provided (through INTEGER PRIMARY KEY as a rowid alias) - let rowid_alias_index = table.columns().iter().position(|c| c.is_rowid_alias); + let rowid_alias_index = table.columns.iter().position(|c| c.is_rowid_alias); let has_user_provided_rowid = { - assert!(column_mappings.len() == table.columns().len()); + assert!(column_mappings.len() == table.columns.len()); if let Some(index) = rowid_alias_index { column_mappings[index].value_index.is_some() } else { @@ -89,7 +89,7 @@ pub fn translate_insert( // allocate a register for each column in the table. if not provided by user, they will simply be set as null. // allocate an extra register for rowid regardless of whether user provided a rowid alias column. - let num_cols = table.columns().len(); + let num_cols = table.columns.len(); let rowid_reg = program.alloc_registers(num_cols + 1); let column_registers_start = rowid_reg + 1; let rowid_alias_reg = { @@ -215,14 +215,14 @@ pub fn translate_insert( target_pc: make_record_label, }); let rowid_column_name = if let Some(index) = rowid_alias_index { - table.column_index_to_name(index).unwrap() + &table.columns.get(index).unwrap().name } else { "rowid" }; program.emit_insn(Insn::Halt { err_code: SQLITE_CONSTRAINT_PRIMARYKEY, - description: format!("{}.{}", table.get_name(), rowid_column_name), + description: format!("{}.{}", table_name.0, rowid_column_name), }); program.resolve_label(make_record_label, program.offset()); @@ -293,7 +293,7 @@ struct ColumnMapping<'a> { /// - Named columns map to their corresponding value index /// - Unspecified columns map to None fn resolve_columns_for_insert<'a>( - table: &'a Table, + table: &'a BTreeTable, columns: &Option, values: &[Vec], ) -> Result>> { @@ -301,7 +301,7 @@ fn resolve_columns_for_insert<'a>( crate::bail_parse_error!("no values to insert"); } - let table_columns = table.columns(); + let table_columns = &table.columns; // Case 1: No columns specified - map values to columns in order if columns.is_none() { @@ -309,7 +309,7 @@ fn resolve_columns_for_insert<'a>( if num_values > table_columns.len() { crate::bail_parse_error!( "table {} has {} columns but {} values were supplied", - table.get_name(), + &table.name, table_columns.len(), num_values ); @@ -350,11 +350,7 @@ fn resolve_columns_for_insert<'a>( .position(|c| c.name.eq_ignore_ascii_case(&column_name)); if table_index.is_none() { - crate::bail_parse_error!( - "table {} has no column named {}", - table.get_name(), - column_name - ); + crate::bail_parse_error!("table {} has no column named {}", &table.name, column_name); } mappings[table_index.unwrap()].value_index = Some(value_index); diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index e95b3dc22..33f76a84e 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -1,9 +1,12 @@ use sqlite3_parser::ast; use crate::{ - schema::Table, translate::result_row::emit_select_result, - vdbe::{builder::ProgramBuilder, insn::Insn, BranchOffset}, + vdbe::{ + builder::{CursorType, ProgramBuilder}, + insn::Insn, + BranchOffset, + }, Result, }; @@ -81,7 +84,7 @@ pub fn init_loop( } => { let cursor_id = program.alloc_cursor_id( Some(table_reference.table_identifier.clone()), - Some(table_reference.table.clone()), + CursorType::BTreeTable(table_reference.btree().unwrap().clone()), ); let root_page = table_reference.table.get_root_page(); @@ -114,7 +117,7 @@ pub fn init_loop( } => { let table_cursor_id = program.alloc_cursor_id( Some(table_reference.table_identifier.clone()), - Some(table_reference.table.clone()), + CursorType::BTreeTable(table_reference.btree().unwrap().clone()), ); match mode { @@ -138,8 +141,10 @@ pub fn init_loop( } if let Search::IndexSearch { index, .. } = search { - let index_cursor_id = program - .alloc_cursor_id(Some(index.name.clone()), Some(Table::Index(index.clone()))); + let index_cursor_id = program.alloc_cursor_id( + Some(index.name.clone()), + CursorType::BTreeIndex(index.clone()), + ); match mode { OperationMode::SELECT => { diff --git a/core/translate/mod.rs b/core/translate/mod.rs index 92b661ce1..fdbbc47e0 100644 --- a/core/translate/mod.rs +++ b/core/translate/mod.rs @@ -27,6 +27,7 @@ use crate::storage::pager::Pager; use crate::storage::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE}; use crate::translate::delete::translate_delete; use crate::util::PRIMARY_KEY_AUTOMATIC_INDEX_NAME_PREFIX; +use crate::vdbe::builder::CursorType; use crate::vdbe::{builder::ProgramBuilder, insn::Insn, Program}; use crate::{bail_parse_error, Connection, LimboError, Result, SymbolTable}; use insert::translate_insert; @@ -463,9 +464,10 @@ fn translate_create_table( let table_id = "sqlite_schema".to_string(); let table = schema.get_table(&table_id).unwrap(); - let table = crate::schema::Table::BTree(table.clone()); - let sqlite_schema_cursor_id = - program.alloc_cursor_id(Some(table_id.to_owned()), Some(table.to_owned())); + let sqlite_schema_cursor_id = program.alloc_cursor_id( + Some(table_id.to_owned()), + CursorType::BTreeTable(table.clone()), + ); program.emit_insn(Insn::OpenWriteAsync { cursor_id: sqlite_schema_cursor_id, root_page: 1, diff --git a/core/translate/order_by.rs b/core/translate/order_by.rs index b58e7ec21..1d02639d2 100644 --- a/core/translate/order_by.rs +++ b/core/translate/order_by.rs @@ -3,10 +3,13 @@ use std::rc::Rc; use sqlite3_parser::ast; use crate::{ - schema::{Column, PseudoTable, Table}, + schema::{Column, PseudoTable}, types::{OwnedRecord, OwnedValue}, util::exprs_are_equivalent, - vdbe::{builder::ProgramBuilder, insn::Insn}, + vdbe::{ + builder::{CursorType, ProgramBuilder}, + insn::Insn, + }, Result, }; @@ -32,7 +35,7 @@ pub fn init_order_by( t_ctx: &mut TranslateCtx, order_by: &[(ast::Expr, Direction)], ) -> Result<()> { - let sort_cursor = program.alloc_cursor_id(None, None); + let sort_cursor = program.alloc_cursor_id(None, CursorType::Sorter); t_ctx.meta_sort = Some(SortMetadata { sort_cursor, reg_sorter_data: program.alloc_register(), @@ -93,12 +96,10 @@ pub fn emit_order_by( .map(|v| v.len()) .unwrap_or(0); - let pseudo_cursor = program.alloc_cursor_id( - None, - Some(Table::Pseudo(Rc::new(PseudoTable { - columns: pseudo_columns, - }))), - ); + let pseudo_table = Rc::new(PseudoTable { + columns: pseudo_columns, + }); + let pseudo_cursor = program.alloc_cursor_id(None, CursorType::Pseudo(pseudo_table.clone())); let SortMetadata { sort_cursor, reg_sorter_data, diff --git a/core/translate/plan.rs b/core/translate/plan.rs index b773d1eec..bffd384d3 100644 --- a/core/translate/plan.rs +++ b/core/translate/plan.rs @@ -7,7 +7,7 @@ use std::{ use crate::{ function::AggFunc, - schema::{Column, Index, Table}, + schema::{BTreeTable, Column, Index, Table}, vdbe::BranchOffset, Result, }; @@ -255,6 +255,12 @@ pub struct TableReference { } impl TableReference { + pub fn btree(&self) -> Option> { + match self.reference_type { + TableReferenceType::BTreeTable => self.table.btree(), + TableReferenceType::Subquery { .. } => None, + } + } pub fn new_subquery(identifier: String, table_index: usize, plan: &SelectPlan) -> Self { Self { table: Table::Pseudo(Rc::new(PseudoTable::new_with_columns( diff --git a/core/vdbe/builder.rs b/core/vdbe/builder.rs index 40f936cd1..7acc4be6f 100644 --- a/core/vdbe/builder.rs +++ b/core/vdbe/builder.rs @@ -4,9 +4,13 @@ use std::{ rc::{Rc, Weak}, }; -use crate::{storage::sqlite3_ondisk::DatabaseHeader, Connection}; +use crate::{ + schema::{BTreeTable, Index, PseudoTable}, + storage::sqlite3_ondisk::DatabaseHeader, + Connection, +}; -use super::{BranchOffset, CursorID, Insn, InsnReference, Program, Table}; +use super::{BranchOffset, CursorID, Insn, InsnReference, Program}; #[allow(dead_code)] pub struct ProgramBuilder { @@ -18,7 +22,7 @@ pub struct ProgramBuilder { constant_insns: Vec, next_insn_label: Option, // Cursors that are referenced by the program. Indexed by CursorID. - pub cursor_ref: Vec<(Option, Option)>, + pub cursor_ref: Vec<(Option, CursorType)>, // Hashmap of label to insn reference. Resolved in build(). label_to_resolved_offset: HashMap, // Bitmask of cursors that have emitted a SeekRowid instruction. @@ -27,6 +31,20 @@ pub struct ProgramBuilder { comments: HashMap, } +#[derive(Debug, Clone)] +pub enum CursorType { + BTreeTable(Rc), + BTreeIndex(Rc), + Pseudo(Rc), + Sorter, +} + +impl CursorType { + pub fn is_index(&self) -> bool { + matches!(self, CursorType::BTreeIndex(_)) + } +} + impl ProgramBuilder { pub fn new() -> Self { Self { @@ -58,11 +76,11 @@ impl ProgramBuilder { pub fn alloc_cursor_id( &mut self, table_identifier: Option, - table: Option
, + cursor_type: CursorType, ) -> usize { let cursor = self.next_free_cursor_id; self.next_free_cursor_id += 1; - self.cursor_ref.push((table_identifier, table)); + self.cursor_ref.push((table_identifier, cursor_type)); assert!(self.cursor_ref.len() == self.next_free_cursor_id); cursor } diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 133172b78..40fda1a28 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1,3 +1,5 @@ +use crate::vdbe::builder::CursorType; + use super::{Insn, InsnReference, OwnedValue, Program}; use std::rc::Rc; @@ -387,7 +389,19 @@ pub fn insn_to_str( column, dest, } => { - let (table_identifier, table) = &program.cursor_ref[*cursor_id]; + let (table_identifier, cursor_type) = &program.cursor_ref[*cursor_id]; + let column_name = match cursor_type { + CursorType::BTreeTable(table) => { + Some(&table.columns.get(*column).unwrap().name) + } + CursorType::BTreeIndex(index) => { + Some(&index.columns.get(*column).unwrap().name) + } + CursorType::Pseudo(pseudo_table) => { + Some(&pseudo_table.columns.get(*column).unwrap().name) + } + CursorType::Sorter => None, + }; ( "Column", *cursor_id as i32, @@ -401,10 +415,7 @@ pub fn insn_to_str( table_identifier .as_ref() .unwrap_or(&format!("cursor {}", cursor_id)), - table - .as_ref() - .and_then(|x| x.column_index_to_name(*column)) - .unwrap_or(format!("column {}", *column).as_str()) + column_name.unwrap_or(&format!("column {}", *column)) ), ) } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 64c41b3bd..bceaeac4c 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -211,7 +211,7 @@ impl ProgramState { pub struct Program { pub max_registers: usize, pub insns: Vec, - pub cursor_ref: Vec<(Option, Option
)>, + pub cursor_ref: Vec<(Option, CursorType)>, pub database_header: Rc>, pub comments: HashMap, pub connection: Weak,