support using a INSERT SELECT that references the same table in both statements

This commit is contained in:
pedrocarlo
2025-05-25 00:02:12 -03:00
parent 90e3c8483d
commit e3fd1e589e
3 changed files with 251 additions and 57 deletions

View File

@@ -10,7 +10,7 @@ use limbo_sqlite3_parser::ast::{self, TableInternalId};
use crate::{
fast_lock::SpinLock,
parameters::Parameters,
schema::{BTreeTable, Index, PseudoTable},
schema::{BTreeTable, Index, PseudoTable, Schema, Table},
storage::sqlite3_ondisk::DatabaseHeader,
translate::{
collate::CollationSeq,
@@ -37,7 +37,10 @@ impl TableRefIdCounter {
}
}
use super::{BranchOffset, CursorID, Insn, InsnFunction, InsnReference, JumpTarget, Program};
use super::{
insn::RegisterOrLiteral, BranchOffset, CursorID, Insn, InsnFunction, InsnReference, JumpTarget,
Program,
};
#[allow(dead_code)]
pub struct ProgramBuilder {
pub table_reference_counter: TableRefIdCounter,
@@ -682,6 +685,66 @@ impl ProgramBuilder {
}
}
/// Checks whether `table` or any of its indices has been opened in the program
pub fn is_table_open(&self, table: &Table, schema: &Schema) -> bool {
let btree = table.btree();
let vtab = table.virtual_table();
for (insn, ..) in self.insns.iter() {
match insn {
Insn::OpenRead {
cursor_id,
root_page,
..
} => {
if let Some(btree) = &btree {
if btree.root_page == *root_page {
return true;
}
}
let name = self.cursor_ref[*cursor_id].0.as_ref();
if name.is_none() {
continue;
}
let name = name.unwrap();
let indices = schema.get_indices(name);
for index in indices {
if index.root_page == *root_page {
return true;
}
}
}
Insn::OpenWrite {
root_page, name, ..
} => {
let RegisterOrLiteral::Literal(root_page) = root_page else {
unreachable!("root page can only be a literal");
};
if let Some(btree) = &btree {
if btree.root_page == *root_page {
return true;
}
}
let indices = schema.get_indices(name);
for index in indices {
if index.root_page == *root_page {
return true;
}
}
}
Insn::VOpen { cursor_id, .. } => {
if let Some(vtab) = &vtab {
let name = self.cursor_ref[*cursor_id].0.as_ref().unwrap();
if vtab.name == *name {
return true;
}
}
}
_ => {}
}
}
false
}
pub fn build(
mut self,
database_header: Arc<SpinLock<DatabaseHeader>>,