mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
ephemeral table for update when rowid is being update
This commit is contained in:
parent
edc6eb9a36
commit
74beac5ea8
6 changed files with 230 additions and 91 deletions
|
@ -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::<Vec<_>>(),
|
||||
);
|
||||
let regs = key
|
||||
.iter()
|
||||
.map(|col| Register::Value(Value::Integer(*col)))
|
||||
.collect::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
cursor.seek(
|
||||
SeekKey::IndexKey(&ImmutableRecord::from_registers(
|
||||
&key.iter()
|
||||
.map(|col| Register::Value(Value::Integer(*col)))
|
||||
.collect::<Vec<_>>(),
|
||||
)),
|
||||
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<u8> = 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<u8> = 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<u8> = 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<u8> = 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),
|
||||
|
|
|
@ -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<CursorID>,
|
||||
) -> 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
|
||||
}
|
||||
|
|
|
@ -357,6 +357,7 @@ pub fn open_loop(
|
|||
table_references: &TableReferences,
|
||||
join_order: &[JoinOrderMember],
|
||||
predicates: &[WhereTerm],
|
||||
temp_cursor_id: Option<CursorID>,
|
||||
) -> 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<CursorID>,
|
||||
) -> 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 {
|
||||
|
|
|
@ -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<Item = &'a Register> + 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());
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -522,7 +522,8 @@ fn get_new_rowid<R: Rng>(cursor: &mut BTreeCursor, mut rng: R) -> Result<CursorR
|
|||
}
|
||||
|
||||
fn make_record(registers: &[Register], start_reg: &usize, count: &usize) -> 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)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue