diff --git a/core/storage/btree.rs b/core/storage/btree.rs index 8721b767d..890b8b420 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -6610,7 +6610,8 @@ mod tests { let page = page.get(); let page = page.get_contents(); let header_size = 8; - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(1))]); + let regs = &[Register::Value(Value::Integer(1))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(1, 0, page, record, &conn); assert_eq!(page.cell_count(), 1); let free = compute_free_space(page, 4096); @@ -6639,8 +6640,8 @@ mod tests { let mut cells = Vec::new(); let usable_space = 4096; for i in 0..3 { - let record = - ImmutableRecord::from_registers(&[Register::Value(Value::Integer(i as i64))]); + let regs = &[Register::Value(Value::Integer(i as i64))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(i, i, page, record, &conn); assert_eq!(page.cell_count(), i + 1); let free = compute_free_space(page, usable_space); @@ -6928,10 +6929,8 @@ mod tests { pager.deref(), ) .unwrap(); - let value = ImmutableRecord::from_registers(&[Register::Value(Value::Blob(vec![ - 0; - *size - ]))]); + let regs = &[Register::Value(Value::Blob(vec![0; *size]))]; + let value = ImmutableRecord::from_registers(regs, regs.len()); tracing::info!("insert key:{}", key); run_until_done( || cursor.insert(&BTreeKey::new_table_rowid(*key, Some(&value)), true), @@ -7022,8 +7021,8 @@ mod tests { pager.deref(), ) .unwrap(); - let value = - ImmutableRecord::from_registers(&[Register::Value(Value::Blob(vec![0; size]))]); + let regs = &[Register::Value(Value::Blob(vec![0; size]))]; + let value = ImmutableRecord::from_registers(regs, regs.len()); let btree_before = if do_validate { format_btree(pager.clone(), root_page, 0) } else { @@ -7143,11 +7142,11 @@ mod tests { }; tracing::info!("insert {}/{}: {:?}", i + 1, inserts, key); keys.push(key.clone()); - let value = ImmutableRecord::from_registers( - &key.iter() - .map(|col| Register::Value(Value::Integer(*col))) - .collect::>(), - ); + let regs = key + .iter() + .map(|col| Register::Value(Value::Integer(*col))) + .collect::>(); + let value = ImmutableRecord::from_registers(®s, regs.len()); run_until_done( || { cursor.insert( @@ -7176,12 +7175,12 @@ mod tests { tracing::info!("seeking key {}/{}: {:?}", i + 1, keys.len(), key); let exists = run_until_done( || { + let regs = key + .iter() + .map(|col| Register::Value(Value::Integer(*col))) + .collect::>(); cursor.seek( - SeekKey::IndexKey(&ImmutableRecord::from_registers( - &key.iter() - .map(|col| Register::Value(Value::Integer(*col))) - .collect::>(), - )), + SeekKey::IndexKey(&ImmutableRecord::from_registers(®s, regs.len())), SeekOp::GE { eq_only: true }, ) }, @@ -7244,8 +7243,8 @@ mod tests { let usable_space = 4096; let total_cells = 10; for i in 0..total_cells { - let record = - ImmutableRecord::from_registers(&[Register::Value(Value::Integer(i as i64))]); + let regs = &[Register::Value(Value::Integer(i as i64))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(i, i, page, record, &conn); assert_eq!(page.cell_count(), i + 1); let free = compute_free_space(page, usable_space); @@ -7650,8 +7649,8 @@ mod tests { let mut cells = Vec::new(); let usable_space = 4096; for i in 0..3 { - let record = - ImmutableRecord::from_registers(&[Register::Value(Value::Integer(i as i64))]); + let regs = &[Register::Value(Value::Integer(i as i64))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(i, i, page, record, &conn); assert_eq!(page.cell_count(), i + 1); let free = compute_free_space(page, usable_space); @@ -7692,8 +7691,8 @@ mod tests { let usable_space = 4096; let total_cells = 10; for i in 0..total_cells { - let record = - ImmutableRecord::from_registers(&[Register::Value(Value::Integer(i as i64))]); + let regs = &[Register::Value(Value::Integer(i as i64))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(i, i, page, record, &conn); assert_eq!(page.cell_count(), i + 1); let free = compute_free_space(page, usable_space); @@ -7748,9 +7747,8 @@ mod tests { // allow appends with extra place to insert let cell_idx = rng.next_u64() as usize % (page.cell_count() + 1); let free = compute_free_space(page, usable_space); - let record = ImmutableRecord::from_registers(&[Register::Value( - Value::Integer(i as i64), - )]); + let regs = &[Register::Value(Value::Integer(i as i64))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let mut payload: Vec = Vec::new(); fill_cell_payload( page.page_type(), @@ -7827,9 +7825,8 @@ mod tests { // allow appends with extra place to insert let cell_idx = rng.next_u64() as usize % (page.cell_count() + 1); let free = compute_free_space(page, usable_space); - let record = ImmutableRecord::from_registers(&[Register::Value( - Value::Integer(i as i64), - )]); + let regs = &[Register::Value(Value::Integer(i as i64))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let mut payload: Vec = Vec::new(); fill_cell_payload( page.page_type(), @@ -7987,7 +7984,8 @@ mod tests { let header_size = 8; let usable_space = 4096; - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(0, 0, page, record, &conn); let free = compute_free_space(page, usable_space); assert_eq!(free, 4096 - payload.len() as u16 - 2 - header_size); @@ -8003,7 +8001,8 @@ mod tests { let page = page.get_contents(); let usable_space = 4096; - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(0, 0, page, record, &conn); assert_eq!(page.cell_count(), 1); @@ -8029,17 +8028,19 @@ mod tests { let page = page.get_contents(); let usable_space = 4096; - let record = ImmutableRecord::from_registers(&[ + let regs = &[ Register::Value(Value::Integer(0)), Register::Value(Value::Text(Text::new("aaaaaaaa"))), - ]); + ]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, 0, page, record, &conn); assert_eq!(page.cell_count(), 1); drop_cell(page, 0, usable_space).unwrap(); assert_eq!(page.cell_count(), 0); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(0, 0, page, record, &conn); assert_eq!(page.cell_count(), 1); @@ -8063,10 +8064,11 @@ mod tests { let page = page.get_contents(); let usable_space = 4096; - let record = ImmutableRecord::from_registers(&[ + let regs = &[ Register::Value(Value::Integer(0)), Register::Value(Value::Text(Text::new("aaaaaaaa"))), - ]); + ]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, 0, page, record, &conn); for _ in 0..100 { @@ -8074,7 +8076,8 @@ mod tests { drop_cell(page, 0, usable_space).unwrap(); assert_eq!(page.cell_count(), 0); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(0, 0, page, record, &conn); assert_eq!(page.cell_count(), 1); @@ -8099,11 +8102,14 @@ mod tests { let page = page.get_contents(); let usable_space = 4096; - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let payload = add_record(0, 0, page, record, &conn); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(1))]); + let regs = &[Register::Value(Value::Integer(1))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(1, 1, page, record, &conn); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(2))]); + let regs = &[Register::Value(Value::Integer(2))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(2, 2, page, record, &conn); drop_cell(page, 1, usable_space).unwrap(); @@ -8122,21 +8128,25 @@ mod tests { let page = page.get_contents(); let usable_space = 4096; - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, 0, page, record, &conn); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, 0, page, record, &conn); drop_cell(page, 0, usable_space).unwrap(); defragment_page(page, usable_space); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, 1, page, record, &conn); drop_cell(page, 0, usable_space).unwrap(); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, 1, page, record, &conn); } @@ -8148,7 +8158,8 @@ mod tests { let page = get_page(2); let usable_space = 4096; let insert = |pos, page| { - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, pos, page, record, &conn); }; let drop = |pos, page| { @@ -8188,7 +8199,8 @@ mod tests { let page = get_page(2); let usable_space = 4096; let insert = |pos, page| { - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let _ = add_record(0, pos, page, record, &conn); }; let drop = |pos, page| { @@ -8197,7 +8209,8 @@ mod tests { let defragment = |page| { defragment_page(page, usable_space); }; - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(0))]); + let regs = &[Register::Value(Value::Integer(0))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let mut payload: Vec = Vec::new(); fill_cell_payload( page.get().get_contents().page_type(), @@ -8231,7 +8244,8 @@ mod tests { for i in 0..10000 { let mut cursor = BTreeCursor::new_table(None, pager.clone(), root_page); tracing::info!("INSERT INTO t VALUES ({});", i,); - let value = ImmutableRecord::from_registers(&[Register::Value(Value::Integer(i))]); + let regs = &[Register::Value(Value::Integer(i))]; + let value = ImmutableRecord::from_registers(regs, regs.len()); tracing::trace!("before insert {}", i); run_until_done( || { @@ -8270,8 +8284,8 @@ mod tests { let page = get_page(2); let usable_space = 4096; - let record = - ImmutableRecord::from_registers(&[Register::Value(Value::Blob(vec![0; 3600]))]); + let regs = &[Register::Value(Value::Blob(vec![0; 3600]))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); let mut payload: Vec = Vec::new(); fill_cell_payload( page.get().get_contents().page_type(), @@ -8306,9 +8320,8 @@ mod tests { // Insert 10,000 records in to the BTree. for i in 1..=10000 { let mut cursor = BTreeCursor::new_table(None, pager.clone(), root_page); - let value = ImmutableRecord::from_registers(&[Register::Value(Value::Text( - Text::new("hello world"), - ))]); + let regs = &[Register::Value(Value::Text(Text::new("hello world")))]; + let value = ImmutableRecord::from_registers(regs, regs.len()); run_until_done( || { @@ -8385,10 +8398,11 @@ mod tests { for i in 0..iterations { let mut cursor = BTreeCursor::new_table(None, pager.clone(), root_page); tracing::info!("INSERT INTO t VALUES ({});", i,); - let value = ImmutableRecord::from_registers(&[Register::Value(Value::Text(Text { + let regs = &[Register::Value(Value::Text(Text { value: huge_texts[i].as_bytes().to_vec(), subtype: crate::types::TextSubtype::Text, - }))]); + }))]; + let value = ImmutableRecord::from_registers(regs, regs.len()); tracing::trace!("before insert {}", i); tracing::debug!( "=========== btree before ===========\n{}\n\n", @@ -8433,8 +8447,8 @@ mod tests { let offset = 2; // blobs data starts at offset 2 let initial_text = "hello world"; let initial_blob = initial_text.as_bytes().to_vec(); - let value = - ImmutableRecord::from_registers(&[Register::Value(Value::Blob(initial_blob.clone()))]); + let regs = &[Register::Value(Value::Blob(initial_blob.clone()))]; + let value = ImmutableRecord::from_registers(regs, regs.len()); run_until_done( || { @@ -8509,8 +8523,8 @@ mod tests { let mut large_blob = vec![b'A'; 40960 - 11]; // insert large blob. 40960 = 10 page long. let hello_world = b"hello world"; large_blob.extend_from_slice(hello_world); - let value = - ImmutableRecord::from_registers(&[Register::Value(Value::Blob(large_blob.clone()))]); + let regs = &[Register::Value(Value::Blob(large_blob.clone()))]; + let value = ImmutableRecord::from_registers(regs, regs.len()); run_until_done( || { @@ -8700,10 +8714,8 @@ mod tests { page_type: PageType, ) { let mut payload = Vec::new(); - let record = ImmutableRecord::from_registers(&[Register::Value(Value::Blob(vec![ - 0; - size as usize - ]))]); + let regs = &[Register::Value(Value::Blob(vec![0; size as usize]))]; + let record = ImmutableRecord::from_registers(regs, regs.len()); fill_cell_payload( page_type, Some(i as i64), diff --git a/core/translate/emitter.rs b/core/translate/emitter.rs index bb1d10db9..66fa7ef4c 100644 --- a/core/translate/emitter.rs +++ b/core/translate/emitter.rs @@ -22,13 +22,14 @@ use super::select::emit_simple_count; use super::subquery::emit_subqueries; use crate::error::SQLITE_CONSTRAINT_PRIMARYKEY; use crate::function::Func; -use crate::schema::Schema; +use crate::schema::{BTreeTable, Column, Index, IndexColumn, Schema, Table, Type}; use crate::translate::compound_select::emit_program_for_compound_select; use crate::translate::plan::{DeletePlan, Plan, Search}; use crate::translate::values::emit_values; use crate::util::exprs_are_equivalent; use crate::vdbe::builder::{CursorKey, CursorType, ProgramBuilder}; use crate::vdbe::insn::{CmpInsFlags, IdxInsertFlags, InsertFlags, RegisterOrLiteral}; +use crate::vdbe::CursorID; use crate::vdbe::{insn::Insn, BranchOffset}; use crate::{Result, SymbolTable}; @@ -348,13 +349,20 @@ pub fn emit_query<'a>( &plan.table_references, &plan.join_order, &mut plan.where_clause, + None, )?; // Process result columns and expressions in the inner loop emit_loop(program, t_ctx, plan)?; // Clean up and close the main execution loop - close_loop(program, t_ctx, &plan.table_references, &plan.join_order)?; + close_loop( + program, + t_ctx, + &plan.table_references, + &plan.join_order, + None, + )?; program.preassign_label_to_next_insn(after_main_loop_label); @@ -439,6 +447,7 @@ fn emit_program_for_delete( &plan.table_references, &[JoinOrderMember::default()], &mut plan.where_clause, + None, )?; emit_delete_insns(program, &mut t_ctx, &plan.table_references)?; @@ -449,6 +458,7 @@ fn emit_program_for_delete( &mut t_ctx, &plan.table_references, &[JoinOrderMember::default()], + None, )?; program.preassign_label_to_next_insn(after_main_loop_label); @@ -603,6 +613,29 @@ fn emit_program_for_update( }); } + let temp_cursor_id = { + // Sqlite determines we should create an ephemeral table if we do not have a FROM clause + // Difficult to say what items from the plan can be checked for this so currently just checking the where clause + // https://github.com/sqlite/sqlite/blob/master/src/update.c#L395 + // https://github.com/sqlite/sqlite/blob/master/src/update.c#L670 + if !plan.where_clause.is_empty() { + None + } else { + let table_ref = plan + .table_references + .joined_tables() + .first() + .expect("at least one table needs to be referenced for UPDATE"); + let columns = table_ref.columns(); + + let rowid_alias_used = plan.set_clauses.iter().fold(false, |accum, (idx, _)| { + accum || columns[*idx].is_rowid_alias + }); + + rowid_alias_used.then(|| emit_ephemeral(program, &table_ref.table)) + } + }; + init_loop( program, &mut t_ctx, @@ -643,14 +676,16 @@ fn emit_program_for_update( &plan.table_references, &[JoinOrderMember::default()], &mut plan.where_clause, + temp_cursor_id, )?; - emit_update_insns(&plan, &t_ctx, program, index_cursors)?; + emit_update_insns(&plan, &t_ctx, program, index_cursors, temp_cursor_id)?; close_loop( program, &mut t_ctx, &plan.table_references, &[JoinOrderMember::default()], + temp_cursor_id, )?; program.preassign_label_to_next_insn(after_main_loop_label); @@ -670,12 +705,13 @@ fn emit_update_insns( t_ctx: &TranslateCtx, program: &mut ProgramBuilder, index_cursors: Vec<(usize, usize)>, + temp_cursor_id: Option, ) -> crate::Result<()> { let table_ref = plan.table_references.joined_tables().first().unwrap(); let loop_labels = t_ctx.labels_main_loop.first().unwrap(); - let (cursor_id, index, is_virtual) = match &table_ref.op { + let cursor_id = program.resolve_cursor_id(&CursorKey::table(table_ref.internal_id)); + let (index, is_virtual) = match &table_ref.op { Operation::Scan { index, .. } => ( - program.resolve_cursor_id(&CursorKey::table(table_ref.internal_id)), index.as_ref().map(|index| { ( index.clone(), @@ -686,15 +722,10 @@ fn emit_update_insns( table_ref.virtual_table().is_some(), ), Operation::Search(search) => match search { - &Search::RowidEq { .. } | Search::Seek { index: None, .. } => ( - program.resolve_cursor_id(&CursorKey::table(table_ref.internal_id)), - None, - false, - ), + &Search::RowidEq { .. } | Search::Seek { index: None, .. } => (None, false), Search::Seek { index: Some(index), .. } => ( - program.resolve_cursor_id(&CursorKey::table(table_ref.internal_id)), Some(( index.clone(), program @@ -714,7 +745,7 @@ fn emit_update_insns( }, ); program.emit_insn(Insn::RowId { - cursor_id, + cursor_id: temp_cursor_id.unwrap_or(cursor_id), dest: beg, }); @@ -1107,3 +1138,77 @@ fn init_limit( }); } } + +/// Emits an ephemeral table that reads the rowids from `table` +fn emit_ephemeral(program: &mut ProgramBuilder, table: &Table) -> CursorID { + let cursor_type = CursorType::BTreeTable(table.btree().unwrap()); + let cursor_id = program.alloc_cursor_id(cursor_type); + + let simple_table_rc = Rc::new(BTreeTable { + root_page: 0, // Not relevant for ephemeral table definition + name: "ephemeral_scratch".to_string(), + has_rowid: true, + primary_key_columns: vec![], + columns: vec![Column { + name: Some("rowid".to_string()), + ty: Type::Integer, + ty_str: "INTEGER".to_string(), + primary_key: false, + is_rowid_alias: false, + notnull: false, + default: None, + unique: false, + collation: None, + }], + is_strict: false, + unique_sets: None, + }); + let temp_cursor_id = program.alloc_cursor_id(CursorType::BTreeTable(simple_table_rc)); + + let null_data_reg = program.alloc_register(); + let rowid_reg = program.alloc_register(); + program.emit_insn(Insn::Null { + dest: null_data_reg, + dest_end: Some(rowid_reg), + }); + + program.emit_insn(Insn::OpenEphemeral { + cursor_id: temp_cursor_id, + is_table: true, + }); + + program.emit_insn(Insn::OpenRead { + cursor_id, + root_page: table.get_root_page(), + }); + + let loop_labels = LoopLabels::new(program); + + program.emit_insn(Insn::Rewind { + cursor_id, + pc_if_empty: loop_labels.loop_end, + }); + + program.preassign_label_to_next_insn(loop_labels.loop_start); + + program.emit_insn(Insn::RowId { + cursor_id, + dest: rowid_reg, + }); + program.emit_insn(Insn::Insert { + cursor: temp_cursor_id, + key_reg: rowid_reg, + record_reg: null_data_reg, + flag: InsertFlags(0), // TODO: when we use the flags see if this needs to change + table_name: table.get_name().to_string(), + }); + + program.preassign_label_to_next_insn(loop_labels.next); + program.emit_insn(Insn::Next { + cursor_id, + pc_if_next: loop_labels.loop_start, + }); + program.preassign_label_to_next_insn(loop_labels.loop_end); + + temp_cursor_id +} diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index b5121ef13..cb27468b4 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -357,6 +357,7 @@ pub fn open_loop( table_references: &TableReferences, join_order: &[JoinOrderMember], predicates: &[WhereTerm], + temp_cursor_id: Option, ) -> Result<()> { for (join_index, join) in join_order.iter().enumerate() { let joined_table_index = join.original_idx; @@ -389,8 +390,11 @@ pub fn open_loop( Operation::Scan { iter_dir, .. } => { match &table.table { Table::BTree(_) => { - let iteration_cursor_id = index_cursor_id.unwrap_or_else(|| { - table_cursor_id.expect("Either index or table cursor must be opened") + let iteration_cursor_id = temp_cursor_id.unwrap_or_else(|| { + index_cursor_id.unwrap_or_else(|| { + table_cursor_id + .expect("Either index or table cursor must be opened") + }) }); if *iter_dir == IterationDirection::Backwards { program.emit_insn(Insn::Last { @@ -971,6 +975,7 @@ pub fn close_loop( t_ctx: &mut TranslateCtx, tables: &TableReferences, join_order: &[JoinOrderMember], + temp_cursor_id: Option, ) -> Result<()> { // We close the loops for all tables in reverse order, i.e. innermost first. // OPEN t1 @@ -995,8 +1000,11 @@ pub fn close_loop( program.resolve_label(loop_labels.next, program.offset()); match &table.table { Table::BTree(_) => { - let iteration_cursor_id = index_cursor_id.unwrap_or_else(|| { - table_cursor_id.expect("Either index or table cursor must be opened") + let iteration_cursor_id = temp_cursor_id.unwrap_or_else(|| { + index_cursor_id.unwrap_or_else(|| { + table_cursor_id + .expect("Either index or table cursor must be opened") + }) }); if *iter_dir == IterationDirection::Backwards { program.emit_insn(Insn::Prev { diff --git a/core/types.rs b/core/types.rs index 010b01202..2d3eddae6 100644 --- a/core/types.rs +++ b/core/types.rs @@ -859,15 +859,18 @@ impl ImmutableRecord { self.values.len() } - pub fn from_registers(registers: &[Register]) -> Self { - let mut values = Vec::with_capacity(registers.len()); - let mut serials = Vec::with_capacity(registers.len()); + pub fn from_registers<'a>( + registers: impl IntoIterator + Clone, + len: usize, + ) -> Self { + let mut values = Vec::with_capacity(len); + let mut serials = Vec::with_capacity(len); let mut size_header = 0; let mut size_values = 0; let mut serial_type_buf = [0; 9]; // write serial types - for value in registers { + for value in registers.clone() { let value = value.get_owned_value(); let serial_type = SerialType::from(value); let n = write_varint(&mut serial_type_buf[0..], serial_type.into()); diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 5216f51ad..cfa0daa93 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -4236,15 +4236,25 @@ pub fn op_insert( { let mut cursor = state.get_cursor(*cursor); let cursor = cursor.as_btree_mut(); - let record = match &state.registers[*record_reg] { - Register::Record(r) => r, - _ => unreachable!("Not a record! Cannot insert a non record value."), - }; + let key = match &state.registers[*key_reg].get_owned_value() { Value::Integer(i) => *i, _ => unreachable!("expected integer key"), }; - return_if_io!(cursor.insert(&BTreeKey::new_table_rowid(key, Some(record)), true)); + + let record = match &state.registers[*record_reg] { + Register::Record(r) => std::borrow::Cow::Borrowed(r), + Register::Value(value) => { + let x = 1; + let regs = &state.registers[*record_reg..*record_reg + 1]; + let new_regs = [&state.registers[*record_reg]]; + let record = ImmutableRecord::from_registers(new_regs, new_regs.len()); + std::borrow::Cow::Owned(record) + } + Register::Aggregate(..) => unreachable!("Cannot insert an aggregate value."), + }; + + return_if_io!(cursor.insert(&BTreeKey::new_table_rowid(key, Some(record.as_ref())), true)); // Only update last_insert_rowid for regular table inserts, not schema modifications if cursor.root_page() != 1 { if let Some(rowid) = return_if_io!(cursor.rowid()) { diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index e8f42ec5a..88ef7ecc1 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -522,7 +522,8 @@ fn get_new_rowid(cursor: &mut BTreeCursor, mut rng: R) -> Result ImmutableRecord { - ImmutableRecord::from_registers(®isters[*start_reg..*start_reg + *count]) + let regs = ®isters[*start_reg..*start_reg + *count]; + ImmutableRecord::from_registers(regs, regs.len()) } #[instrument(skip(program), level = Level::TRACE)]