From 3a562f734cfaa2768a361cf89dc5ce6e3f0cf3ce Mon Sep 17 00:00:00 2001 From: RS2007 Date: Sat, 8 Nov 2025 13:45:17 +0530 Subject: [PATCH] feat: alter table disallow generated columns, support foreign keys for alter table --- core/translate/alter.rs | 64 ++++++++++++++++++++++++++++++++++++++- testing/alter_column.test | 34 +++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/core/translate/alter.rs b/core/translate/alter.rs index 0ce64969e..5835884aa 100644 --- a/core/translate/alter.rs +++ b/core/translate/alter.rs @@ -6,7 +6,7 @@ use turso_parser::{ use crate::{ function::{AlterTableFunc, Func}, - schema::{Column, Table, RESERVED_TABLE_PREFIXES}, + schema::{Column, ForeignKey, Table, RESERVED_TABLE_PREFIXES}, translate::{ emitter::Resolver, expr::{walk_expr, WalkControl}, @@ -280,6 +280,16 @@ pub fn translate_alter_table( )? } ast::AlterTableBody::AddColumn(col_def) => { + if col_def + .constraints + .iter() + .any(|c| matches!(c.constraint, ast::ColumnConstraint::Generated { .. })) + { + return Err(LimboError::ParseError( + "Alter table does not support adding generated columns".to_string(), + )); + } + let constraints = col_def.constraints.clone(); let column = Column::from(&col_def); if let Some(default) = &column.default { @@ -310,6 +320,58 @@ pub fn translate_alter_table( // TODO: All quoted ids will be quoted with `[]`, we should store some info from the parsed AST btree.columns.push(column.clone()); + // Add foreign key constraints to the btree table + for constraint in constraints { + if let ast::ColumnConstraint::ForeignKey { + clause, + defer_clause, + } = constraint.constraint + { + let fk = ForeignKey { + parent_table: normalize_ident(clause.tbl_name.as_str()), + parent_columns: clause + .columns + .iter() + .map(|c| normalize_ident(c.col_name.as_str())) + .collect(), + on_delete: clause + .args + .iter() + .find_map(|arg| { + if let ast::RefArg::OnDelete(act) = arg { + Some(*act) + } else { + None + } + }) + .unwrap_or(ast::RefAct::NoAction), + on_update: clause + .args + .iter() + .find_map(|arg| { + if let ast::RefArg::OnUpdate(act) = arg { + Some(*act) + } else { + None + } + }) + .unwrap_or(ast::RefAct::NoAction), + child_columns: vec![new_column_name.to_string()], + deferred: match defer_clause { + Some(d) => { + d.deferrable + && matches!( + d.init_deferred, + Some(ast::InitDeferredPred::InitiallyDeferred) + ) + } + None => false, + }, + }; + btree.foreign_keys.push(Arc::new(fk)); + } + } + let sql = btree.to_sql(); let mut escaped = String::with_capacity(sql.len()); diff --git a/testing/alter_column.test b/testing/alter_column.test index ea667af2f..bc9ca0b30 100755 --- a/testing/alter_column.test +++ b/testing/alter_column.test @@ -299,3 +299,37 @@ do_execsql_test_on_specific_db {:memory:} rename-parent-3-composite { SELECT sql FROM sqlite_schema WHERE type='table' AND name='c3'; } {{CREATE TABLE c3 (x INTEGER PRIMARY KEY, fa INTEGER, fb INTEGER, FOREIGN KEY (fa, fb) REFERENCES p3_new (a, b))}} +# Adding a generated column via ALTER TABLE should error +do_execsql_test_in_memory_error_content alter-table-add-generated-column-error { + CREATE TABLE t(a); + ALTER TABLE t ADD COLUMN b AS (NULL); +} { + "Parse error: Alter table does not support adding generated columns" +} + +# Add column with a foreign key reference and verify schema SQL +do_execsql_test_on_specific_db {:memory:} alter-table-add-column-with-fk-updates-schema { + CREATE TABLE t(a); + CREATE TABLE s(a); + ALTER TABLE s ADD COLUMN b REFERENCES t(a); + SELECT sql FROM sqlite_schema WHERE name = 's'; +} { + "CREATE TABLE s (a, b, FOREIGN KEY (b) REFERENCES t(a))" +} + +do_execsql_test_on_specific_db {:memory:} alter-table-add-self-ref-fk-updates-schema { + CREATE TABLE s(a); + ALTER TABLE s ADD COLUMN b REFERENCES s(a); + SELECT sql FROM sqlite_schema WHERE name = 's'; +} { + "CREATE TABLE s (a, b, FOREIGN KEY (b) REFERENCES s(a))" +} + +do_execsql_test_on_specific_db {:memory:} alter-table-add-column-with-composite-fk-updates-schema { + CREATE TABLE t(a, c); + CREATE TABLE s(a); + ALTER TABLE s ADD COLUMN b REFERENCES t(a, c); + SELECT sql FROM sqlite_schema WHERE name = 's'; +} { + "CREATE TABLE s (a, b, FOREIGN KEY (b) REFERENCES t(a, c))" +}