diff --git a/core/translate/insert.rs b/core/translate/insert.rs new file mode 100644 index 000000000..851384a66 --- /dev/null +++ b/core/translate/insert.rs @@ -0,0 +1,96 @@ +use std::{ops::Deref, rc::Rc}; + +use sqlite3_parser::ast::{ + DistinctNames, InsertBody, Name, QualifiedName, ResolveType, ResultColumn, Select, With, +}; + +use crate::Result; +use crate::{ + schema::{self, Schema, Table}, + translate::expr::resolve_ident_qualified, + vdbe::{builder::ProgramBuilder, Insn, Program}, +}; + +pub fn translate_insert( + schema: &Schema, + with: &Option, + or_conflict: &Option, + tbl_name: &QualifiedName, + columns: &Option, + body: &InsertBody, + returning: &Option>, +) -> Result { + assert!(with.is_none()); + assert!(or_conflict.is_none()); + let mut program = ProgramBuilder::new(); + let init_label = program.allocate_label(); + program.emit_insn_with_label_dependency( + Insn::Init { + target_pc: init_label, + }, + init_label, + ); + let start_offset = program.offset(); + + dbg!(tbl_name); + dbg!(columns); + dbg!(returning); + dbg!(with); + dbg!(body); + + let yield_reg = program.alloc_register(); + let jump_on_definition_label = program.allocate_label(); + program.emit_insn(Insn::InitCoroutine { + yield_reg, + jump_on_definition: jump_on_definition_label, + start_offset: program.offset() + 1, + }); + match body { + InsertBody::Select(select, None) => match &select.body.select { + sqlite3_parser::ast::OneSelect::Select { + distinctness: _, + columns: _, + from: _, + where_clause: _, + group_by: _, + window_clause: _, + } => todo!(), + sqlite3_parser::ast::OneSelect::Values(values) => {} + }, + InsertBody::DefaultValues => todo!("default values not yet supported"), + _ => todo!(), + } + program.emit_insn(Insn::EndCoroutine { yield_reg }); + + // open table + let table_name = &tbl_name.name; + + let table = match schema.get_table(table_name.0.as_str()) { + Some(table) => table, + None => crate::bail_corrupt_error!("Parse error: no such table: {}", table_name), + }; + let table = Rc::new(Table::BTree(table)); + let cursor_id = program.alloc_cursor_id( + Some(table_name.0.clone()), + Some(table.clone().deref().clone()), + ); + let root_page = match table.as_ref() { + Table::BTree(btree) => btree.root_page, + Table::Pseudo(_) => todo!(), + }; + program.emit_insn(Insn::OpenWriteAsync { + cursor_id, + root_page, + }); + program.emit_insn(Insn::OpenWriteAwait {}); + + program.emit_insn(Insn::Halt); + program.resolve_label(init_label, program.offset()); + program.emit_insn(Insn::Transaction); + program.emit_constant_insns(); + program.emit_insn(Insn::Goto { + target_pc: start_offset, + }); + program.resolve_deferred_labels(); + Ok(program.build()) +} diff --git a/core/translate/mod.rs b/core/translate/mod.rs index 53c288ab9..48ebe1746 100644 --- a/core/translate/mod.rs +++ b/core/translate/mod.rs @@ -8,6 +8,7 @@ //! will read rows from the database and filter them according to a WHERE clause. pub(crate) mod expr; +pub(crate) mod insert; pub(crate) mod select; pub(crate) mod where_clause; @@ -20,6 +21,7 @@ use crate::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE}; use crate::util::normalize_ident; use crate::vdbe::{builder::ProgramBuilder, Insn, Program}; use crate::{bail_parse_error, Result}; +use insert::translate_insert; use select::{prepare_select, translate_select}; use sqlite3_parser::ast; @@ -49,7 +51,6 @@ pub fn translate( ast::Stmt::DropTable { .. } => bail_parse_error!("DROP TABLE not supported yet"), ast::Stmt::DropTrigger { .. } => bail_parse_error!("DROP TRIGGER not supported yet"), ast::Stmt::DropView { .. } => bail_parse_error!("DROP VIEW not supported yet"), - ast::Stmt::Insert { .. } => bail_parse_error!("INSERT not supported yet"), ast::Stmt::Pragma(name, body) => translate_pragma(&name, body, database_header, pager), ast::Stmt::Reindex { .. } => bail_parse_error!("REINDEX not supported yet"), ast::Stmt::Release(_) => bail_parse_error!("RELEASE not supported yet"), @@ -61,6 +62,22 @@ pub fn translate( } ast::Stmt::Update { .. } => bail_parse_error!("UPDATE not supported yet"), ast::Stmt::Vacuum(_, _) => bail_parse_error!("VACUUM not supported yet"), + ast::Stmt::Insert { + with, + or_conflict, + tbl_name, + columns, + body, + returning, + } => translate_insert( + schema, + &with, + &or_conflict, + &tbl_name, + &columns, + &body, + &returning, + ), } } diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 8f29c360b..f13aaf00a 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -507,6 +507,137 @@ pub fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: 0, format!("r[{}]=func(r[{}..])", dest, start_reg), ), + Insn::InitCoroutine { + yield_reg, + jump_on_definition, + start_offset, + } => ( + "InitCoroutine", + *yield_reg as i32, + *jump_on_definition as i32, + *start_offset as i32, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::EndCoroutine { yield_reg } => ( + "EndCoroutine", + *yield_reg as i32, + 0, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::Yield { + yield_reg, + end_offset, + } => ( + "Yield", + *yield_reg as i32, + *end_offset as i32, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::InsertAsync { + cursor, + key_reg, + record_reg, + flag, + } => ( + "InsertAsync", + *cursor as i32, + *record_reg as i32, + *key_reg as i32, + OwnedValue::Text(Rc::new("".to_string())), + *flag as u16, + format!(""), + ), + Insn::InsertAwait {} => ( + "InsertAwait", + 0, + 0, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::NewRowid { reg } => ( + "NewRowId", + 0, + *reg as i32, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::MustBeInt { reg } => ( + "MustBeInt", + *reg as i32, + 0, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::SoftNull { reg } => ( + "SoftNull", + *reg as i32, + 0, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::NotExists { + cursor, + rowid_reg, + target_pc, + } => ( + "NotExists", + *cursor as i32, + *target_pc as i32, + *rowid_reg as i32, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::OpenWriteAsync { + cursor_id, + root_page, + } => ( + "OpenWriteAsync", + *cursor_id as i32, + *root_page as i32, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::OpenWriteAwait {} => ( + "OpenWriteAsync", + 0, + 0, + 0, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), + Insn::Copy { + src_reg, + dst_reg, + amount, + } => ( + "Copy", + *src_reg as i32, + *dst_reg as i32, + *amount as i32, + OwnedValue::Text(Rc::new("".to_string())), + 0, + format!(""), + ), }; format!( "{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}", diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 587536bed..437d57523 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -279,6 +279,61 @@ pub enum Insn { dest: usize, // P3 func: ScalarFunc, // P4 }, + + InitCoroutine { + yield_reg: usize, + jump_on_definition: BranchOffset, + start_offset: BranchOffset, + }, + + EndCoroutine { + yield_reg: usize, + }, + + Yield { + yield_reg: usize, + end_offset: BranchOffset, + }, + + InsertAsync { + cursor: CursorID, + key_reg: usize, // Must be int. + record_reg: usize, // Blob of record data. + flag: usize, // Flags used by insert, for now not used. + }, + + InsertAwait {}, + + NewRowid { + reg: usize, + }, + + MustBeInt { + reg: usize, + }, + + SoftNull { + reg: usize, + }, + + NotExists { + cursor: CursorID, + rowid_reg: usize, + target_pc: BranchOffset, + }, + + OpenWriteAsync { + cursor_id: CursorID, + root_page: PageIdx, + }, + + OpenWriteAwait {}, + + Copy { + src_reg: usize, + dst_reg: usize, + amount: usize, // 0 amount means we include src_reg, dst_reg..=dst_reg+amount = src_reg..=src_reg+amount + }, } // Index of insn in list of insns @@ -1198,6 +1253,45 @@ impl Program { state.pc += 1; } }, + Insn::InitCoroutine { + yield_reg, + jump_on_definition, + start_offset, + } => todo!(), + Insn::EndCoroutine { yield_reg } => todo!(), + Insn::Yield { + yield_reg, + end_offset, + } => todo!(), + Insn::InsertAsync { + cursor, + key_reg, + record_reg, + flag, + } => todo!(), + Insn::InsertAwait {} => todo!(), + Insn::NewRowid { reg } => todo!(), + Insn::MustBeInt { reg } => todo!(), + Insn::SoftNull { reg } => todo!(), + Insn::NotExists { + cursor, + rowid_reg, + target_pc, + } => todo!(), + Insn::OpenWriteAsync { + cursor_id, + root_page, + } => todo!(), + Insn::OpenWriteAwait {} => todo!(), + Insn::Copy { + src_reg, + dst_reg, + amount, + } => { + for i in 0..=*amount { + state.registers[*dst_reg + i] = state.registers[*src_reg + i].clone(); + } + } } } }