mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
Store cursor type (table,index,pseudo,sorter) when allocating cursor
This commit is contained in:
parent
3e2993f5f5
commit
9909539b9d
10 changed files with 108 additions and 57 deletions
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue