mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-26 12:34:22 +01:00
This commit adds suport for DROP INDEX. Bytecode produced by this commit differs from SQLITE's bytecode, main reason we don't do autovacuum or repacking of pages like SQLITE does. Closes #1280 Closes #1444
221 lines
7.8 KiB
Rust
221 lines
7.8 KiB
Rust
//! The VDBE bytecode code generator.
|
|
//!
|
|
//! This module is responsible for translating the SQL AST into a sequence of
|
|
//! instructions for the VDBE. The VDBE is a register-based virtual machine that
|
|
//! executes bytecode instructions. This code generator is responsible for taking
|
|
//! the SQL AST and generating the corresponding VDBE instructions. For example,
|
|
//! a SELECT statement will be translated into a sequence of instructions that
|
|
//! will read rows from the database and filter them according to a WHERE clause.
|
|
|
|
pub(crate) mod aggregation;
|
|
pub(crate) mod delete;
|
|
pub(crate) mod emitter;
|
|
pub(crate) mod expr;
|
|
pub(crate) mod group_by;
|
|
pub(crate) mod index;
|
|
pub(crate) mod insert;
|
|
pub(crate) mod main_loop;
|
|
pub(crate) mod optimizer;
|
|
pub(crate) mod order_by;
|
|
pub(crate) mod plan;
|
|
pub(crate) mod planner;
|
|
pub(crate) mod pragma;
|
|
pub(crate) mod result_row;
|
|
pub(crate) mod schema;
|
|
pub(crate) mod select;
|
|
pub(crate) mod subquery;
|
|
pub(crate) mod transaction;
|
|
pub(crate) mod update;
|
|
|
|
use crate::fast_lock::SpinLock;
|
|
use crate::schema::Schema;
|
|
use crate::storage::pager::Pager;
|
|
use crate::storage::sqlite3_ondisk::DatabaseHeader;
|
|
use crate::translate::delete::translate_delete;
|
|
use crate::vdbe::builder::{ProgramBuilder, ProgramBuilderOpts, QueryMode};
|
|
use crate::vdbe::Program;
|
|
use crate::{bail_parse_error, Connection, LimboError, Result, SymbolTable};
|
|
use fallible_iterator::FallibleIterator as _;
|
|
use index::{translate_create_index, translate_drop_index};
|
|
use insert::translate_insert;
|
|
use limbo_sqlite3_parser::ast::{self, Delete, Insert};
|
|
use limbo_sqlite3_parser::lexer::sql::Parser;
|
|
use schema::{
|
|
translate_create_table, translate_create_virtual_table, translate_drop_table, ParseSchema,
|
|
SQLITE_TABLEID,
|
|
};
|
|
use select::translate_select;
|
|
use std::rc::{Rc, Weak};
|
|
use std::sync::Arc;
|
|
use transaction::{translate_tx_begin, translate_tx_commit};
|
|
use update::translate_update;
|
|
|
|
/// Translate SQL statement into bytecode program.
|
|
pub fn translate(
|
|
schema: &Schema,
|
|
stmt: ast::Stmt,
|
|
database_header: Arc<SpinLock<DatabaseHeader>>,
|
|
pager: Rc<Pager>,
|
|
connection: Weak<Connection>,
|
|
syms: &SymbolTable,
|
|
query_mode: QueryMode,
|
|
) -> Result<Program> {
|
|
let mut change_cnt_on = false;
|
|
|
|
let program = match stmt {
|
|
ast::Stmt::AlterTable(a) => {
|
|
let (table_name, alter_table) = a.as_ref();
|
|
|
|
match alter_table {
|
|
ast::AlterTableBody::RenameTo(name) => {
|
|
let rename = &name.0;
|
|
let name = &table_name.name.0;
|
|
|
|
let Some(table) = schema.tables.get(name) else {
|
|
return Err(LimboError::ParseError(format!("no such table: {name}")));
|
|
};
|
|
|
|
if schema.tables.contains_key(rename) {
|
|
return Err(LimboError::ParseError(format!(
|
|
"there is already another table or index with this name: {rename}"
|
|
)));
|
|
};
|
|
|
|
let Some(btree) = table.btree() else { todo!() };
|
|
|
|
let mut btree = (*btree).clone();
|
|
btree.name = rename.clone();
|
|
|
|
let sql = btree.to_sql();
|
|
|
|
let stmt = format!(
|
|
r#"
|
|
UPDATE {SQLITE_TABLEID}
|
|
SET name = '{rename}'
|
|
, tbl_name = '{rename}'
|
|
, sql = '{sql}'
|
|
WHERE tbl_name = '{name}'
|
|
"#,
|
|
);
|
|
|
|
let mut parser = Parser::new(stmt.as_bytes());
|
|
let Some(ast::Cmd::Stmt(ast::Stmt::Update(mut update))) = parser.next()? else {
|
|
unreachable!();
|
|
};
|
|
|
|
translate_update(
|
|
QueryMode::Normal,
|
|
schema,
|
|
&mut update,
|
|
syms,
|
|
ParseSchema::Reload,
|
|
)?
|
|
}
|
|
_ => todo!(),
|
|
}
|
|
}
|
|
ast::Stmt::Analyze(_) => bail_parse_error!("ANALYZE not supported yet"),
|
|
ast::Stmt::Attach { .. } => bail_parse_error!("ATTACH not supported yet"),
|
|
ast::Stmt::Begin(tx_type, tx_name) => translate_tx_begin(tx_type, tx_name)?,
|
|
ast::Stmt::Commit(tx_name) => translate_tx_commit(tx_name)?,
|
|
ast::Stmt::CreateIndex {
|
|
unique,
|
|
if_not_exists,
|
|
idx_name,
|
|
tbl_name,
|
|
columns,
|
|
..
|
|
} => {
|
|
change_cnt_on = true;
|
|
translate_create_index(
|
|
query_mode,
|
|
(unique, if_not_exists),
|
|
&idx_name.name.0,
|
|
&tbl_name.0,
|
|
&columns,
|
|
schema,
|
|
)?
|
|
}
|
|
ast::Stmt::CreateTable {
|
|
temporary,
|
|
if_not_exists,
|
|
tbl_name,
|
|
body,
|
|
} => translate_create_table(
|
|
query_mode,
|
|
tbl_name,
|
|
temporary,
|
|
*body,
|
|
if_not_exists,
|
|
schema,
|
|
)?,
|
|
ast::Stmt::CreateTrigger { .. } => bail_parse_error!("CREATE TRIGGER not supported yet"),
|
|
ast::Stmt::CreateView { .. } => bail_parse_error!("CREATE VIEW not supported yet"),
|
|
ast::Stmt::CreateVirtualTable(vtab) => {
|
|
translate_create_virtual_table(*vtab, schema, query_mode, &syms)?
|
|
}
|
|
ast::Stmt::Delete(delete) => {
|
|
let Delete {
|
|
tbl_name,
|
|
where_clause,
|
|
limit,
|
|
..
|
|
} = *delete;
|
|
change_cnt_on = true;
|
|
translate_delete(query_mode, schema, &tbl_name, where_clause, limit, syms)?
|
|
}
|
|
ast::Stmt::Detach(_) => bail_parse_error!("DETACH not supported yet"),
|
|
ast::Stmt::DropIndex {
|
|
if_exists,
|
|
idx_name,
|
|
} => translate_drop_index(query_mode, &idx_name.name.0, if_exists, schema)?,
|
|
ast::Stmt::DropTable {
|
|
if_exists,
|
|
tbl_name,
|
|
} => translate_drop_table(query_mode, tbl_name, if_exists, schema)?,
|
|
ast::Stmt::DropTrigger { .. } => bail_parse_error!("DROP TRIGGER not supported yet"),
|
|
ast::Stmt::DropView { .. } => bail_parse_error!("DROP VIEW not supported yet"),
|
|
ast::Stmt::Pragma(name, body) => pragma::translate_pragma(
|
|
query_mode,
|
|
schema,
|
|
&name,
|
|
body.map(|b| *b),
|
|
database_header.clone(),
|
|
pager,
|
|
)?,
|
|
ast::Stmt::Reindex { .. } => bail_parse_error!("REINDEX not supported yet"),
|
|
ast::Stmt::Release(_) => bail_parse_error!("RELEASE not supported yet"),
|
|
ast::Stmt::Rollback { .. } => bail_parse_error!("ROLLBACK not supported yet"),
|
|
ast::Stmt::Savepoint(_) => bail_parse_error!("SAVEPOINT not supported yet"),
|
|
ast::Stmt::Select(select) => translate_select(query_mode, schema, *select, syms)?,
|
|
ast::Stmt::Update(mut update) => {
|
|
translate_update(query_mode, schema, &mut update, syms, ParseSchema::None)?
|
|
}
|
|
ast::Stmt::Vacuum(_, _) => bail_parse_error!("VACUUM not supported yet"),
|
|
ast::Stmt::Insert(insert) => {
|
|
let Insert {
|
|
with,
|
|
or_conflict,
|
|
tbl_name,
|
|
columns,
|
|
body,
|
|
returning,
|
|
} = *insert;
|
|
change_cnt_on = true;
|
|
translate_insert(
|
|
query_mode,
|
|
schema,
|
|
&with,
|
|
&or_conflict,
|
|
&tbl_name,
|
|
&columns,
|
|
&body,
|
|
&returning,
|
|
syms,
|
|
)?
|
|
}
|
|
};
|
|
|
|
Ok(program.build(database_header, connection, change_cnt_on))
|
|
}
|