mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-31 13:54:27 +01:00
refactor
This commit is contained in:
368
core/translate/alter.rs
Normal file
368
core/translate/alter.rs
Normal file
@@ -0,0 +1,368 @@
|
||||
use fallible_iterator::FallibleIterator as _;
|
||||
use limbo_sqlite3_parser::{ast, lexer::sql::Parser};
|
||||
|
||||
use crate::{
|
||||
function::{AlterTableFunc, Func},
|
||||
schema::{Column, Schema},
|
||||
util::normalize_ident,
|
||||
vdbe::{
|
||||
builder::{ProgramBuilder, QueryMode},
|
||||
insn::{Insn, RegisterOrLiteral},
|
||||
},
|
||||
LimboError, Result, SymbolTable,
|
||||
};
|
||||
|
||||
use super::{
|
||||
emitter::TransactionMode, schema::SQLITE_TABLEID, update::translate_update_with_after,
|
||||
};
|
||||
|
||||
pub fn translate_alter_table(
|
||||
alter: Box<(ast::QualifiedName, ast::AlterTableBody)>,
|
||||
syms: &SymbolTable,
|
||||
schema: &Schema,
|
||||
mut program: ProgramBuilder,
|
||||
) -> Result<ProgramBuilder> {
|
||||
let (table_name, alter_table) = *alter;
|
||||
let ast::Name(table_name) = table_name.name;
|
||||
|
||||
let Some(original_btree) = schema
|
||||
.get_table(&table_name)
|
||||
.and_then(|table| table.btree())
|
||||
else {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"no such table: {table_name}"
|
||||
)));
|
||||
};
|
||||
|
||||
let mut btree = (*original_btree).clone();
|
||||
|
||||
Ok(match alter_table {
|
||||
ast::AlterTableBody::DropColumn(column_name) => {
|
||||
let ast::Name(column_name) = column_name;
|
||||
|
||||
// Tables always have at least one column.
|
||||
assert_ne!(btree.columns.len(), 0);
|
||||
|
||||
if btree.columns.len() == 1 {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"cannot drop column \"{column_name}\": no other columns exist"
|
||||
)));
|
||||
}
|
||||
|
||||
let (dropped_index, column) = btree.get_column(&column_name).ok_or_else(|| {
|
||||
LimboError::ParseError(format!("no such column: \"{column_name}\""))
|
||||
})?;
|
||||
|
||||
if column.primary_key {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"cannot drop column \"{column_name}\": PRIMARY KEY"
|
||||
)));
|
||||
}
|
||||
|
||||
if column.unique
|
||||
|| btree.unique_sets.as_ref().is_some_and(|set| {
|
||||
set.iter().any(|set| {
|
||||
set.iter()
|
||||
.any(|(name, _)| name == &normalize_ident(&column_name))
|
||||
})
|
||||
})
|
||||
{
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"cannot drop column \"{column_name}\": UNIQUE"
|
||||
)));
|
||||
}
|
||||
|
||||
btree.columns.remove(dropped_index);
|
||||
|
||||
let sql = btree.to_sql();
|
||||
|
||||
let stmt = format!(
|
||||
r#"
|
||||
UPDATE {SQLITE_TABLEID}
|
||||
SET sql = '{sql}'
|
||||
WHERE name = '{table_name}' COLLATE NOCASE AND type = 'table'
|
||||
"#,
|
||||
);
|
||||
|
||||
let mut parser = Parser::new(stmt.as_bytes());
|
||||
let Some(ast::Cmd::Stmt(ast::Stmt::Update(mut update))) = parser.next().unwrap() else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
translate_update_with_after(
|
||||
QueryMode::Normal,
|
||||
schema,
|
||||
&mut update,
|
||||
syms,
|
||||
program,
|
||||
|program| {
|
||||
let column_count = btree.columns.len();
|
||||
let root_page = btree.root_page;
|
||||
let table_name = btree.name.clone();
|
||||
|
||||
let cursor_id = program.alloc_cursor_id(
|
||||
crate::vdbe::builder::CursorType::BTreeTable(original_btree),
|
||||
);
|
||||
|
||||
program.emit_insn(Insn::OpenWrite {
|
||||
cursor_id,
|
||||
root_page: RegisterOrLiteral::Literal(root_page),
|
||||
name: table_name.clone(),
|
||||
});
|
||||
|
||||
program.cursor_loop(cursor_id, |program, rowid| {
|
||||
let first_column = program.alloc_registers(column_count);
|
||||
|
||||
let mut iter = first_column;
|
||||
|
||||
for i in 0..(column_count + 1) {
|
||||
if i == dropped_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
program.emit_column(cursor_id, i, iter);
|
||||
|
||||
iter += 1;
|
||||
}
|
||||
|
||||
let record = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: first_column,
|
||||
count: column_count,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: cursor_id,
|
||||
key_reg: rowid,
|
||||
record_reg: record,
|
||||
flag: 0,
|
||||
table_name: table_name.clone(),
|
||||
});
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
})
|
||||
},
|
||||
)?
|
||||
}
|
||||
ast::AlterTableBody::AddColumn(col_def) => {
|
||||
let column = Column::from(col_def);
|
||||
|
||||
if let Some(default) = &column.default {
|
||||
if !matches!(
|
||||
default,
|
||||
ast::Expr::Literal(
|
||||
ast::Literal::Null
|
||||
| ast::Literal::Blob(_)
|
||||
| ast::Literal::Numeric(_)
|
||||
| ast::Literal::String(_)
|
||||
)
|
||||
) {
|
||||
// TODO: This is slightly inaccurate since sqlite returns a `Runtime
|
||||
// error`.
|
||||
return Err(LimboError::ParseError(
|
||||
"Cannot add a column with non-constant default".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
btree.columns.push(column);
|
||||
|
||||
let sql = btree.to_sql();
|
||||
let mut escaped = String::with_capacity(sql.len());
|
||||
|
||||
for ch in sql.chars() {
|
||||
match ch {
|
||||
'\'' => escaped.push_str("''"),
|
||||
ch => escaped.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
let stmt = format!(
|
||||
r#"
|
||||
UPDATE {SQLITE_TABLEID}
|
||||
SET sql = '{escaped}'
|
||||
WHERE name = '{table_name}' COLLATE NOCASE AND type = 'table'
|
||||
"#,
|
||||
);
|
||||
|
||||
let mut parser = Parser::new(stmt.as_bytes());
|
||||
let Some(ast::Cmd::Stmt(ast::Stmt::Update(mut update))) = parser.next().unwrap() else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
translate_update_with_after(
|
||||
QueryMode::Normal,
|
||||
schema,
|
||||
&mut update,
|
||||
syms,
|
||||
program,
|
||||
|program| {
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
});
|
||||
},
|
||||
)?
|
||||
}
|
||||
ast::AlterTableBody::RenameColumn { old, new } => {
|
||||
let ast::Name(rename_from) = old;
|
||||
let ast::Name(rename_to) = new;
|
||||
|
||||
if btree.get_column(&rename_from).is_none() {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"no such column: \"{rename_from}\""
|
||||
)));
|
||||
};
|
||||
|
||||
let sqlite_schema = schema
|
||||
.get_btree_table(SQLITE_TABLEID)
|
||||
.expect("sqlite_schema should be on schema");
|
||||
|
||||
let cursor_id = program.alloc_cursor_id(crate::vdbe::builder::CursorType::BTreeTable(
|
||||
sqlite_schema.clone(),
|
||||
));
|
||||
|
||||
program.emit_insn(Insn::OpenWrite {
|
||||
cursor_id,
|
||||
root_page: RegisterOrLiteral::Literal(sqlite_schema.root_page),
|
||||
name: sqlite_schema.name.clone(),
|
||||
});
|
||||
|
||||
program.cursor_loop(cursor_id, |program, rowid| {
|
||||
let first_column = program.alloc_registers(5);
|
||||
|
||||
for i in 0..5 {
|
||||
program.emit_column(cursor_id, i, first_column + i);
|
||||
}
|
||||
|
||||
program.emit_string8_new_reg(table_name.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
program.emit_string8_new_reg(rename_from.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
program.emit_string8_new_reg(rename_to.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
let out = program.alloc_registers(5);
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: first_column,
|
||||
dest: out,
|
||||
func: crate::function::FuncCtx {
|
||||
func: Func::AlterTable(AlterTableFunc::RenameColumn),
|
||||
arg_count: 8,
|
||||
},
|
||||
});
|
||||
|
||||
let record = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: out,
|
||||
count: 5,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: cursor_id,
|
||||
key_reg: rowid,
|
||||
record_reg: record,
|
||||
flag: 0,
|
||||
table_name: table_name.clone(),
|
||||
});
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
});
|
||||
|
||||
program.epilogue(TransactionMode::Write);
|
||||
|
||||
program
|
||||
}
|
||||
ast::AlterTableBody::RenameTo(new_name) => {
|
||||
let ast::Name(new_name) = new_name;
|
||||
|
||||
if schema.get_table(&new_name).is_some() {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"there is already another table or index with this name: {new_name}"
|
||||
)));
|
||||
};
|
||||
|
||||
let sqlite_schema = schema
|
||||
.get_btree_table(SQLITE_TABLEID)
|
||||
.expect("sqlite_schema should be on schema");
|
||||
|
||||
let cursor_id = program.alloc_cursor_id(crate::vdbe::builder::CursorType::BTreeTable(
|
||||
sqlite_schema.clone(),
|
||||
));
|
||||
|
||||
program.emit_insn(Insn::OpenWrite {
|
||||
cursor_id,
|
||||
root_page: RegisterOrLiteral::Literal(sqlite_schema.root_page),
|
||||
name: sqlite_schema.name.clone(),
|
||||
});
|
||||
|
||||
program.cursor_loop(cursor_id, |program, rowid| {
|
||||
let first_column = program.alloc_registers(5);
|
||||
|
||||
for i in 0..5 {
|
||||
program.emit_column(cursor_id, i, first_column + i);
|
||||
}
|
||||
|
||||
program.emit_string8_new_reg(table_name.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
program.emit_string8_new_reg(new_name.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
let out = program.alloc_registers(5);
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: first_column,
|
||||
dest: out,
|
||||
func: crate::function::FuncCtx {
|
||||
func: Func::AlterTable(AlterTableFunc::RenameTable),
|
||||
arg_count: 7,
|
||||
},
|
||||
});
|
||||
|
||||
let record = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: out,
|
||||
count: 5,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: cursor_id,
|
||||
key_reg: rowid,
|
||||
record_reg: record,
|
||||
flag: 0,
|
||||
table_name: table_name.clone(),
|
||||
});
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
});
|
||||
|
||||
program.epilogue(TransactionMode::Write);
|
||||
|
||||
program
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
//! will read rows from the database and filter them according to a WHERE clause.
|
||||
|
||||
pub(crate) mod aggregation;
|
||||
pub(crate) mod alter;
|
||||
pub(crate) mod collate;
|
||||
pub(crate) mod delete;
|
||||
pub(crate) mod display;
|
||||
@@ -31,31 +32,24 @@ pub(crate) mod update;
|
||||
mod values;
|
||||
|
||||
use crate::fast_lock::SpinLock;
|
||||
use crate::function::{AlterTableFunc, Func};
|
||||
use crate::schema::{Column, Schema};
|
||||
use crate::schema::Schema;
|
||||
use crate::storage::pager::Pager;
|
||||
use crate::storage::sqlite3_ondisk::DatabaseHeader;
|
||||
use crate::translate::delete::translate_delete;
|
||||
use crate::util::normalize_ident;
|
||||
use crate::vdbe::builder::{ProgramBuilder, ProgramBuilderOpts, QueryMode};
|
||||
use crate::vdbe::insn::{Insn, RegisterOrLiteral};
|
||||
use crate::vdbe::Program;
|
||||
use crate::{bail_parse_error, Connection, LimboError, Result, SymbolTable};
|
||||
use emitter::TransactionMode;
|
||||
use fallible_iterator::FallibleIterator as _;
|
||||
use crate::{bail_parse_error, Connection, Result, SymbolTable};
|
||||
use alter::translate_alter_table;
|
||||
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, SQLITE_TABLEID,
|
||||
};
|
||||
use schema::{translate_create_table, translate_create_virtual_table, translate_drop_table};
|
||||
use select::translate_select;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::Arc;
|
||||
use tracing::{instrument, Level};
|
||||
use transaction::{translate_tx_begin, translate_tx_commit};
|
||||
use update::{translate_update, translate_update_with_after};
|
||||
use update::translate_update;
|
||||
|
||||
#[instrument(skip_all, level = Level::TRACE)]
|
||||
pub fn translate(
|
||||
@@ -111,359 +105,10 @@ pub fn translate_inner(
|
||||
stmt: ast::Stmt,
|
||||
syms: &SymbolTable,
|
||||
query_mode: QueryMode,
|
||||
mut program: ProgramBuilder,
|
||||
program: ProgramBuilder,
|
||||
) -> Result<ProgramBuilder> {
|
||||
let program = match stmt {
|
||||
ast::Stmt::AlterTable(a) => {
|
||||
let (table_name, alter_table) = *a;
|
||||
let ast::Name(table_name) = table_name.name;
|
||||
|
||||
let Some(original_btree) = schema
|
||||
.get_table(&table_name)
|
||||
.and_then(|table| table.btree())
|
||||
else {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"no such table: {table_name}"
|
||||
)));
|
||||
};
|
||||
|
||||
let mut btree = (*original_btree).clone();
|
||||
|
||||
match alter_table {
|
||||
ast::AlterTableBody::DropColumn(column_name) => {
|
||||
let ast::Name(column_name) = column_name;
|
||||
|
||||
// Tables always have at least one column.
|
||||
assert_ne!(btree.columns.len(), 0);
|
||||
|
||||
if btree.columns.len() == 1 {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"cannot drop column \"{column_name}\": no other columns exist"
|
||||
)));
|
||||
}
|
||||
|
||||
let (dropped_index, column) =
|
||||
btree.get_column(&column_name).ok_or_else(|| {
|
||||
LimboError::ParseError(format!("no such column: \"{column_name}\""))
|
||||
})?;
|
||||
|
||||
if column.primary_key {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"cannot drop column \"{column_name}\": PRIMARY KEY"
|
||||
)));
|
||||
}
|
||||
|
||||
if column.unique
|
||||
|| btree.unique_sets.as_ref().is_some_and(|set| {
|
||||
set.iter().any(|set| {
|
||||
set.iter()
|
||||
.any(|(name, _)| name == &normalize_ident(&column_name))
|
||||
})
|
||||
})
|
||||
{
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"cannot drop column \"{column_name}\": UNIQUE"
|
||||
)));
|
||||
}
|
||||
|
||||
btree.columns.remove(dropped_index);
|
||||
|
||||
let sql = btree.to_sql();
|
||||
|
||||
let stmt = format!(
|
||||
r#"
|
||||
UPDATE {SQLITE_TABLEID}
|
||||
SET sql = '{sql}'
|
||||
WHERE name = '{table_name}' COLLATE NOCASE AND type = 'table'
|
||||
"#,
|
||||
);
|
||||
|
||||
let mut parser = Parser::new(stmt.as_bytes());
|
||||
let Some(ast::Cmd::Stmt(ast::Stmt::Update(mut update))) =
|
||||
parser.next().unwrap()
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
translate_update_with_after(
|
||||
QueryMode::Normal,
|
||||
schema,
|
||||
&mut update,
|
||||
syms,
|
||||
program,
|
||||
|program| {
|
||||
let column_count = btree.columns.len();
|
||||
let root_page = btree.root_page;
|
||||
let table_name = btree.name.clone();
|
||||
|
||||
let cursor_id = program.alloc_cursor_id(
|
||||
crate::vdbe::builder::CursorType::BTreeTable(original_btree),
|
||||
);
|
||||
|
||||
program.emit_insn(Insn::OpenWrite {
|
||||
cursor_id,
|
||||
root_page: RegisterOrLiteral::Literal(root_page),
|
||||
name: table_name.clone(),
|
||||
});
|
||||
|
||||
program.cursor_loop(cursor_id, |program, rowid| {
|
||||
let first_column = program.alloc_registers(column_count);
|
||||
|
||||
let mut iter = first_column;
|
||||
|
||||
for i in 0..(column_count + 1) {
|
||||
if i == dropped_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
program.emit_column(cursor_id, i, iter);
|
||||
|
||||
iter += 1;
|
||||
}
|
||||
|
||||
let record = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: first_column,
|
||||
count: column_count,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: cursor_id,
|
||||
key_reg: rowid,
|
||||
record_reg: record,
|
||||
flag: 0,
|
||||
table_name: table_name.clone(),
|
||||
});
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
})
|
||||
},
|
||||
)?
|
||||
}
|
||||
ast::AlterTableBody::AddColumn(col_def) => {
|
||||
let column = Column::from(col_def);
|
||||
|
||||
if let Some(default) = &column.default {
|
||||
if !matches!(
|
||||
default,
|
||||
ast::Expr::Literal(
|
||||
ast::Literal::Null
|
||||
| ast::Literal::Blob(_)
|
||||
| ast::Literal::Numeric(_)
|
||||
| ast::Literal::String(_)
|
||||
)
|
||||
) {
|
||||
// TODO: This is slightly inaccurate since sqlite returns a `Runtime
|
||||
// error`.
|
||||
return Err(LimboError::ParseError(
|
||||
"Cannot add a column with non-constant default".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
btree.columns.push(column);
|
||||
|
||||
let sql = btree.to_sql();
|
||||
let mut escaped = String::with_capacity(sql.len());
|
||||
|
||||
for ch in sql.chars() {
|
||||
match ch {
|
||||
'\'' => escaped.push_str("''"),
|
||||
ch => escaped.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
let stmt = format!(
|
||||
r#"
|
||||
UPDATE {SQLITE_TABLEID}
|
||||
SET sql = '{escaped}'
|
||||
WHERE name = '{table_name}' COLLATE NOCASE AND type = 'table'
|
||||
"#,
|
||||
);
|
||||
|
||||
let mut parser = Parser::new(stmt.as_bytes());
|
||||
let Some(ast::Cmd::Stmt(ast::Stmt::Update(mut update))) =
|
||||
parser.next().unwrap()
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
translate_update_with_after(
|
||||
QueryMode::Normal,
|
||||
schema,
|
||||
&mut update,
|
||||
syms,
|
||||
program,
|
||||
|program| {
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
});
|
||||
},
|
||||
)?
|
||||
}
|
||||
ast::AlterTableBody::RenameColumn { old, new } => {
|
||||
let ast::Name(rename_from) = old;
|
||||
let ast::Name(rename_to) = new;
|
||||
|
||||
if btree.get_column(&rename_from).is_none() {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"no such column: \"{rename_from}\""
|
||||
)));
|
||||
};
|
||||
|
||||
let sqlite_schema = schema
|
||||
.get_btree_table(SQLITE_TABLEID)
|
||||
.expect("sqlite_schema should be on schema");
|
||||
|
||||
let cursor_id = program.alloc_cursor_id(
|
||||
crate::vdbe::builder::CursorType::BTreeTable(sqlite_schema.clone()),
|
||||
);
|
||||
|
||||
program.emit_insn(Insn::OpenWrite {
|
||||
cursor_id,
|
||||
root_page: RegisterOrLiteral::Literal(sqlite_schema.root_page),
|
||||
name: sqlite_schema.name.clone(),
|
||||
});
|
||||
|
||||
program.cursor_loop(cursor_id, |program, rowid| {
|
||||
let first_column = program.alloc_registers(5);
|
||||
|
||||
for i in 0..5 {
|
||||
program.emit_column(cursor_id, i, first_column + i);
|
||||
}
|
||||
|
||||
program.emit_string8_new_reg(table_name.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
program.emit_string8_new_reg(rename_from.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
program.emit_string8_new_reg(rename_to.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
let out = program.alloc_registers(5);
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: first_column,
|
||||
dest: out,
|
||||
func: crate::function::FuncCtx {
|
||||
func: Func::AlterTable(AlterTableFunc::RenameColumn),
|
||||
arg_count: 8,
|
||||
},
|
||||
});
|
||||
|
||||
let record = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: out,
|
||||
count: 5,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: cursor_id,
|
||||
key_reg: rowid,
|
||||
record_reg: record,
|
||||
flag: 0,
|
||||
table_name: table_name.clone(),
|
||||
});
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
});
|
||||
|
||||
program.epilogue(TransactionMode::Write);
|
||||
|
||||
program
|
||||
}
|
||||
ast::AlterTableBody::RenameTo(new_name) => {
|
||||
let ast::Name(new_name) = new_name;
|
||||
|
||||
if schema.get_table(&new_name).is_some() {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"there is already another table or index with this name: {new_name}"
|
||||
)));
|
||||
};
|
||||
|
||||
let sqlite_schema = schema
|
||||
.get_btree_table(SQLITE_TABLEID)
|
||||
.expect("sqlite_schema should be on schema");
|
||||
|
||||
let cursor_id = program.alloc_cursor_id(
|
||||
crate::vdbe::builder::CursorType::BTreeTable(sqlite_schema.clone()),
|
||||
);
|
||||
|
||||
program.emit_insn(Insn::OpenWrite {
|
||||
cursor_id,
|
||||
root_page: RegisterOrLiteral::Literal(sqlite_schema.root_page),
|
||||
name: sqlite_schema.name.clone(),
|
||||
});
|
||||
|
||||
program.cursor_loop(cursor_id, |program, rowid| {
|
||||
let first_column = program.alloc_registers(5);
|
||||
|
||||
for i in 0..5 {
|
||||
program.emit_column(cursor_id, i, first_column + i);
|
||||
}
|
||||
|
||||
program.emit_string8_new_reg(table_name.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
program.emit_string8_new_reg(new_name.clone());
|
||||
program.mark_last_insn_constant();
|
||||
|
||||
let out = program.alloc_registers(5);
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: first_column,
|
||||
dest: out,
|
||||
func: crate::function::FuncCtx {
|
||||
func: Func::AlterTable(AlterTableFunc::RenameTable),
|
||||
arg_count: 7,
|
||||
},
|
||||
});
|
||||
|
||||
let record = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: out,
|
||||
count: 5,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: cursor_id,
|
||||
key_reg: rowid,
|
||||
record_reg: record,
|
||||
flag: 0,
|
||||
table_name: table_name.clone(),
|
||||
});
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::ParseSchema {
|
||||
db: usize::MAX, // TODO: This value is unused, change when we do something with it
|
||||
where_clause: None,
|
||||
});
|
||||
|
||||
program.epilogue(TransactionMode::Write);
|
||||
|
||||
program
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::Stmt::AlterTable(alter) => translate_alter_table(alter, syms, schema, program)?,
|
||||
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, program)?,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#![no_main]
|
||||
use core::fmt;
|
||||
use std::{error::Error, num::NonZero, sync::Arc};
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
||||
use arbitrary::Arbitrary;
|
||||
use libfuzzer_sys::{fuzz_target, Corpus};
|
||||
use limbo_core::{Value, IO as _};
|
||||
use rusqlite::ffi::SQLITE_STATIC;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Id(String);
|
||||
@@ -71,7 +69,6 @@ impl fmt::Display for ColumnDef {
|
||||
let ColumnDef {
|
||||
name,
|
||||
r#type,
|
||||
unique,
|
||||
} = self;
|
||||
write!(f, "{name} {type}",)?;
|
||||
|
||||
@@ -92,7 +89,6 @@ impl<'a> Arbitrary<'a> for Columns {
|
||||
out.push(ColumnDef {
|
||||
name: Id(format!("c{i}")),
|
||||
r#type: u.arbitrary()?,
|
||||
unique: u.arbitrary()?,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,7 +141,7 @@ impl fmt::Display for IndexDef {
|
||||
|
||||
write!(f, "CREATE INDEX {name} ON {table}(")?;
|
||||
|
||||
for (i, column) in self.columns.iter().enumerate() {
|
||||
for (i, column) in columns.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user