mod columns;
mod constraints;
mod table_options;
mod validation;
use vibesql_ast::*;
use vibesql_storage::Database;
use crate::{errors::ExecutorError, privilege_checker::PrivilegeChecker};
pub struct AlterTableExecutor;
impl AlterTableExecutor {
pub fn execute(
stmt: &AlterTableStmt,
database: &mut Database,
) -> Result<String, ExecutorError> {
Self::execute_with_source(stmt, database, None)
}
pub fn execute_with_source(
stmt: &AlterTableStmt,
database: &mut Database,
alter_sql: Option<&str>,
) -> Result<String, ExecutorError> {
let table_name = match stmt {
AlterTableStmt::AddColumn(s) => &s.table_name,
AlterTableStmt::DropColumn(s) => &s.table_name,
AlterTableStmt::AlterColumn(s) => match s {
AlterColumnStmt::SetDefault { table_name, .. } => table_name,
AlterColumnStmt::DropDefault { table_name, .. } => table_name,
AlterColumnStmt::SetNotNull { table_name, .. } => table_name,
AlterColumnStmt::DropNotNull { table_name, .. } => table_name,
},
AlterTableStmt::AddConstraint(s) => &s.table_name,
AlterTableStmt::DropConstraint(s) => &s.table_name,
AlterTableStmt::RenameTable(s) => &s.table_name,
AlterTableStmt::RenameColumn(s) => &s.table_name,
AlterTableStmt::ModifyColumn(s) => &s.table_name,
AlterTableStmt::ChangeColumn(s) => &s.table_name,
};
PrivilegeChecker::check_alter(database, table_name)?;
let result = match stmt {
AlterTableStmt::AddColumn(add_column) => {
columns::execute_add_column(add_column, database)
}
AlterTableStmt::DropColumn(drop_column) => {
columns::execute_drop_column(drop_column, database)
}
AlterTableStmt::AlterColumn(alter_column) => {
columns::execute_alter_column(alter_column, database)
}
AlterTableStmt::AddConstraint(add_constraint) => {
constraints::execute_add_constraint(add_constraint, database)
}
AlterTableStmt::DropConstraint(drop_constraint) => {
constraints::execute_drop_constraint(drop_constraint, database)
}
AlterTableStmt::RenameTable(rename_table) => {
let old_name = &rename_table.table_name;
let new_name = &rename_table.new_table_name;
let result = table_options::execute_rename_table(rename_table, database);
if result.is_ok() {
database.invalidate_columnar_cache(old_name);
database.invalidate_columnar_cache(new_name);
}
return result;
}
AlterTableStmt::RenameColumn(rename_column) => {
columns::execute_rename_column(rename_column, database)
}
AlterTableStmt::ModifyColumn(modify_column) => {
columns::execute_modify_column(modify_column, database)
}
AlterTableStmt::ChangeColumn(change_column) => {
columns::execute_change_column(change_column, database)
}
};
if result.is_ok() {
database.invalidate_columnar_cache(table_name);
update_sql_source_after_alter(database, table_name, stmt, alter_sql);
sync_catalog_schema_from_storage(database, table_name);
}
result
}
}
fn sync_catalog_schema_from_storage(database: &mut Database, table_name: &str) {
let schema = match database.get_table(table_name) {
Some(table) => table.schema.clone(),
None => return,
};
database.catalog.replace_table_schema(table_name, schema);
}
fn update_sql_source_after_alter(
database: &mut Database,
table_name: &str,
stmt: &AlterTableStmt,
alter_sql: Option<&str>,
) {
let Some(table) = database.get_table_mut(table_name) else {
return;
};
let Some(current) = table.schema.sql_source.clone() else {
return;
};
let edited: Option<String> = match stmt {
AlterTableStmt::AddColumn(_) => alter_sql
.and_then(crate::alter_rewrite::extract_add_column_text)
.and_then(|coldef| crate::alter_rewrite::append_column(¤t, &coldef)),
AlterTableStmt::RenameColumn(rename) => crate::alter_rewrite::rename_column(
¤t,
&rename.old_column_name,
&rename.new_column_name,
),
AlterTableStmt::DropColumn(drop) => {
crate::alter_rewrite::drop_column(¤t, &drop.column_name)
}
_ => None,
};
match edited {
Some(text) => table.schema_mut().set_sql_source(text),
None => table.schema_mut().invalidate_sql_source(),
}
}