Merge 'Simplify PseudoCursor implementation' from Levy A.

Removes unnecessary allocations and unreachable code paths.

Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>

Closes #1903
This commit is contained in:
Pere Diaz Bou
2025-07-01 16:21:05 +02:00
11 changed files with 28 additions and 125 deletions

View File

@@ -136,7 +136,6 @@ impl Schema {
#[derive(Clone, Debug)]
pub enum Table {
BTree(Rc<BTreeTable>),
Pseudo(Rc<PseudoTable>),
Virtual(Rc<VirtualTable>),
FromClauseSubquery(FromClauseSubquery),
}
@@ -145,7 +144,6 @@ impl Table {
pub fn get_root_page(&self) -> usize {
match self {
Table::BTree(table) => table.root_page,
Table::Pseudo(_) => unimplemented!(),
Table::Virtual(_) => unimplemented!(),
Table::FromClauseSubquery(_) => unimplemented!(),
}
@@ -154,7 +152,6 @@ impl Table {
pub fn get_name(&self) -> &str {
match self {
Self::BTree(table) => &table.name,
Self::Pseudo(_) => "",
Self::Virtual(table) => &table.name,
Self::FromClauseSubquery(from_clause_subquery) => &from_clause_subquery.name,
}
@@ -163,7 +160,6 @@ impl Table {
pub fn get_column_at(&self, index: usize) -> Option<&Column> {
match self {
Self::BTree(table) => table.columns.get(index),
Self::Pseudo(table) => table.columns.get(index),
Self::Virtual(table) => table.columns.get(index),
Self::FromClauseSubquery(from_clause_subquery) => {
from_clause_subquery.columns.get(index)
@@ -174,7 +170,6 @@ impl Table {
pub fn columns(&self) -> &Vec<Column> {
match self {
Self::BTree(table) => &table.columns,
Self::Pseudo(table) => &table.columns,
Self::Virtual(table) => &table.columns,
Self::FromClauseSubquery(from_clause_subquery) => &from_clause_subquery.columns,
}
@@ -183,7 +178,6 @@ impl Table {
pub fn btree(&self) -> Option<Rc<BTreeTable>> {
match self {
Self::BTree(table) => Some(table.clone()),
Self::Pseudo(_) => None,
Self::Virtual(_) => None,
Self::FromClauseSubquery(_) => None,
}
@@ -201,7 +195,6 @@ impl PartialEq for Table {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::BTree(a), Self::BTree(b)) => Rc::ptr_eq(a, b),
(Self::Pseudo(a), Self::Pseudo(b)) => Rc::ptr_eq(a, b),
(Self::Virtual(a), Self::Virtual(b)) => Rc::ptr_eq(a, b),
_ => false,
}
@@ -291,41 +284,20 @@ impl BTreeTable {
}
}
#[derive(Debug, Default)]
pub struct PseudoTable {
pub columns: Vec<Column>,
#[derive(Debug, Default, Clone, Copy)]
pub struct PseudoCursorType {
pub column_count: usize,
}
impl PseudoTable {
impl PseudoCursorType {
pub fn new() -> Self {
Self { columns: vec![] }
Self { column_count: 0 }
}
pub fn new_with_columns(columns: Vec<Column>) -> Self {
Self { columns }
}
pub fn add_column(&mut self, name: &str, ty: Type, primary_key: bool) {
self.columns.push(Column {
name: Some(normalize_ident(name)),
ty,
ty_str: ty.to_string().to_uppercase(),
primary_key,
is_rowid_alias: false,
notnull: false,
default: None,
unique: false,
collation: None,
});
}
pub fn get_column(&self, name: &str) -> Option<(usize, &Column)> {
let name = normalize_ident(name);
for (i, column) in self.columns.iter().enumerate() {
if column.name.as_ref().map_or(false, |n| *n == name) {
return Some((i, column));
}
pub fn new_with_columns(columns: impl AsRef<[Column]>) -> Self {
Self {
column_count: columns.as_ref().len(),
}
None
}
}
@@ -580,8 +552,8 @@ fn create_table(
})
}
pub fn _build_pseudo_table(columns: &[ResultColumn]) -> PseudoTable {
let table = PseudoTable::new();
pub fn _build_pseudo_table(columns: &[ResultColumn]) -> PseudoCursorType {
let table = PseudoCursorType::new();
for column in columns {
match column {
ResultColumn::Expr(expr, _as_name) => {

View File

@@ -321,9 +321,7 @@ impl ToSqlString for JoinedTable {
) -> String {
let table_or_subquery =
match &self.table {
Table::BTree(..) | Table::Pseudo(..) | Table::Virtual(..) => {
self.table.get_name().to_string()
}
Table::BTree(..) | Table::Virtual(..) => self.table.get_name().to_string(),
Table::FromClauseSubquery(from_clause_subquery) => {
// Could possibly merge the contexts together here
format!(

View File

@@ -1930,7 +1930,6 @@ pub fn translate_expr(
});
Ok(target_register)
}
Table::Pseudo(_) => panic!("Column access on pseudo table"),
}
}
ast::Expr::RowId {

View File

@@ -1,12 +1,10 @@
use std::rc::Rc;
use turso_sqlite3_parser::ast;
use crate::translate::expr::{walk_expr, WalkControl};
use crate::translate::plan::ResultSetColumn;
use crate::{
function::AggFunc,
schema::{Column, PseudoTable},
schema::PseudoCursorType,
translate::collate::CollationSeq,
util::exprs_are_equivalent,
vdbe::{
@@ -304,30 +302,11 @@ pub fn group_by_create_pseudo_table(
program: &mut ProgramBuilder,
sorter_column_count: usize,
) -> usize {
// Create pseudo-columns for the pseudo-table
// (these are placeholders as we only care about structure, not semantics)
let ty = crate::schema::Type::Null;
let pseudo_columns = (0..sorter_column_count)
.map(|_| Column {
name: None,
primary_key: false,
ty,
ty_str: ty.to_string().to_uppercase(),
is_rowid_alias: false,
notnull: false,
default: None,
unique: false,
collation: None,
})
.collect::<Vec<_>>();
// Create a pseudo-table to read one row at a time from the sorter
// This allows us to use standard table access operations on the sorted data
let pseudo_table = Rc::new(PseudoTable {
columns: pseudo_columns,
});
program.alloc_cursor_id(CursorType::Pseudo(pseudo_table.clone()))
program.alloc_cursor_id(CursorType::Pseudo(PseudoCursorType {
column_count: sorter_column_count,
}))
}
/// In case sorting is needed for GROUP BY, sorts the rows in the GROUP BY sorter

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use crate::vdbe::insn::CmpInsFlags;
use crate::{
schema::{BTreeTable, Column, Index, IndexColumn, PseudoTable, Schema},
schema::{BTreeTable, Column, Index, IndexColumn, PseudoCursorType, Schema},
storage::pager::CreateBTreeFlags,
util::normalize_ident,
vdbe::{
@@ -83,8 +83,9 @@ pub fn translate_create_index(
let btree_cursor_id = program.alloc_cursor_id(CursorType::BTreeIndex(idx.clone()));
let table_cursor_id = program.alloc_cursor_id(CursorType::BTreeTable(tbl.clone()));
let sorter_cursor_id = program.alloc_cursor_id(CursorType::Sorter);
let pseudo_table = PseudoTable::new_with_columns(tbl.columns.clone());
let pseudo_cursor_id = program.alloc_cursor_id(CursorType::Pseudo(pseudo_table.into()));
let pseudo_cursor_id = program.alloc_cursor_id(CursorType::Pseudo(PseudoCursorType {
column_count: tbl.columns.len(),
}));
// Create a new B-Tree and store the root page index in a register
let root_page_reg = program.alloc_register();

View File

@@ -563,7 +563,6 @@ pub fn open_loop(
end_offset: loop_end,
});
}
Table::Pseudo(_) => panic!("Pseudo tables should not loop"),
}
if let Some(table_cursor_id) = table_cursor_id {
@@ -1042,7 +1041,6 @@ pub fn close_loop(
target_pc: loop_labels.loop_start,
});
}
other => unreachable!("Unsupported table reference type: {:?}", other),
}
program.preassign_label_to_next_insn(loop_labels.loop_end);
}

View File

@@ -1,9 +1,7 @@
use std::rc::Rc;
use turso_sqlite3_parser::ast::{self, SortOrder};
use crate::{
schema::{Column, PseudoTable},
schema::PseudoCursorType,
translate::collate::CollationSeq,
util::exprs_are_equivalent,
vdbe::{
@@ -87,54 +85,17 @@ pub fn emit_order_by(
let sort_loop_start_label = program.allocate_label();
let sort_loop_next_label = program.allocate_label();
let sort_loop_end_label = program.allocate_label();
let mut pseudo_columns = vec![];
for _ in 0..order_by.len() {
let ty = crate::schema::Type::Null;
pseudo_columns.push(Column {
// Names don't matter. We are tracking which result column is in which position in the ORDER BY clause in m.result_column_indexes_in_orderby_sorter.
name: None,
primary_key: false,
ty,
ty_str: ty.to_string().to_uppercase(),
is_rowid_alias: false,
notnull: false,
default: None,
unique: false,
collation: None,
});
}
for i in 0..result_columns.len() {
// If any result columns are not in the ORDER BY sorter, it's because they are equal to a sort key and were already added to the pseudo columns above.
if let Some(ref v) = t_ctx.result_columns_to_skip_in_orderby_sorter {
if v.contains(&i) {
continue;
}
}
let ty = crate::schema::Type::Null;
pseudo_columns.push(Column {
name: None,
primary_key: false,
ty,
ty_str: ty.to_string().to_uppercase(),
is_rowid_alias: false,
notnull: false,
default: None,
unique: false,
collation: None,
});
}
let num_columns_in_sorter = order_by.len() + result_columns.len()
let sorter_column_count = order_by.len() + result_columns.len()
- t_ctx
.result_columns_to_skip_in_orderby_sorter
.as_ref()
.map(|v| v.len())
.unwrap_or(0);
let pseudo_table = Rc::new(PseudoTable {
columns: pseudo_columns,
});
let pseudo_cursor = program.alloc_cursor_id(CursorType::Pseudo(pseudo_table.clone()));
let pseudo_cursor = program.alloc_cursor_id(CursorType::Pseudo(PseudoCursorType {
column_count: sorter_column_count,
}));
let SortMetadata {
sort_cursor,
reg_sorter_data,
@@ -143,7 +104,7 @@ pub fn emit_order_by(
program.emit_insn(Insn::OpenPseudo {
cursor_id: pseudo_cursor,
content_reg: reg_sorter_data,
num_fields: num_columns_in_sorter,
num_fields: sorter_column_count,
});
program.emit_insn(Insn::SorterSort {

View File

@@ -989,7 +989,6 @@ impl JoinedTable {
let index_cursor_id = None;
Ok((table_cursor_id, index_cursor_id))
}
Table::Pseudo(_) => Ok((None, None)),
Table::FromClauseSubquery(..) => Ok((None, None)),
}
}

View File

@@ -749,7 +749,6 @@ pub fn translate_drop_table(
db: 0, // TODO change this for multiple databases
});
}
Table::Pseudo(..) => unimplemented!(),
Table::FromClauseSubquery(..) => panic!("FromClauseSubquery can't be dropped"),
};

View File

@@ -6,7 +6,7 @@ use turso_sqlite3_parser::ast::{self, TableInternalId};
use crate::{
numeric::Numeric,
parameters::Parameters,
schema::{BTreeTable, Index, PseudoTable, Table},
schema::{BTreeTable, Index, PseudoCursorType, Table},
translate::{
collate::CollationSeq,
emitter::TransactionMode,
@@ -116,7 +116,7 @@ pub struct ProgramBuilder {
pub enum CursorType {
BTreeTable(Rc<BTreeTable>),
BTreeIndex(Arc<Index>),
Pseudo(Rc<PseudoTable>),
Pseudo(PseudoCursorType),
Sorter,
VirtualTable(Rc<VirtualTable>),
}

View File

@@ -539,10 +539,7 @@ pub fn insn_to_str(
let name = &index.columns.get(*column).unwrap().name;
Some(name)
}
CursorType::Pseudo(pseudo_table) => {
let name = pseudo_table.columns.get(*column).unwrap().name.as_ref();
name
}
CursorType::Pseudo(_) => None,
CursorType::Sorter => None,
CursorType::VirtualTable(v) => v.columns.get(*column).unwrap().name.as_ref(),
};