Store cursor type (table,index,pseudo,sorter) when allocating cursor

This commit is contained in:
Jussi Saurio 2025-01-11 14:04:14 +02:00
parent 3e2993f5f5
commit 9909539b9d
10 changed files with 108 additions and 57 deletions

View file

@ -135,6 +135,14 @@ impl Table {
Self::Pseudo(_) => unimplemented!(),
}
}
pub fn btree(&self) -> Option<Rc<BTreeTable>> {
match self {
Self::BTree(table) => Some(table.clone()),
Self::Index(_) => None,
Self::Pseudo(_) => None,
}
}
}
impl PartialEq for Table {

View file

@ -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,

View file

@ -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<DistinctNames>,
values: &[Vec<Expr>],
) -> Result<Vec<ColumnMapping<'a>>> {
@ -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);

View file

@ -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 => {

View file

@ -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,

View file

@ -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,

View file

@ -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<Rc<BTreeTable>> {
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(

View file

@ -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<Insn>,
next_insn_label: Option<BranchOffset>,
// Cursors that are referenced by the program. Indexed by CursorID.
pub cursor_ref: Vec<(Option<String>, Option<Table>)>,
pub cursor_ref: Vec<(Option<String>, CursorType)>,
// Hashmap of label to insn reference. Resolved in build().
label_to_resolved_offset: HashMap<i32, u32>,
// Bitmask of cursors that have emitted a SeekRowid instruction.
@ -27,6 +31,20 @@ pub struct ProgramBuilder {
comments: HashMap<InsnReference, &'static str>,
}
#[derive(Debug, Clone)]
pub enum CursorType {
BTreeTable(Rc<BTreeTable>),
BTreeIndex(Rc<Index>),
Pseudo(Rc<PseudoTable>),
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<String>,
table: Option<Table>,
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
}

View file

@ -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))
),
)
}

View file

@ -211,7 +211,7 @@ impl ProgramState {
pub struct Program {
pub max_registers: usize,
pub insns: Vec<Insn>,
pub cursor_ref: Vec<(Option<String>, Option<Table>)>,
pub cursor_ref: Vec<(Option<String>, CursorType)>,
pub database_header: Rc<RefCell<DatabaseHeader>>,
pub comments: HashMap<InsnReference, &'static str>,
pub connection: Weak<Connection>,