Skip to content

Commit 5b87424

Browse files
committed
Preserve foreign key clauses when adding columns via ALTER TABLE
1 parent 2c49c47 commit 5b87424

File tree

5 files changed

+102
-7
lines changed

5 files changed

+102
-7
lines changed

core/translate/alter.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use turso_parser::{
66

77
use crate::{
88
function::{AlterTableFunc, Func},
9-
schema::{Column, Table, RESERVED_TABLE_PREFIXES},
9+
schema::{Column, ForeignKey, Table, RESERVED_TABLE_PREFIXES},
1010
translate::{
1111
emitter::Resolver,
1212
expr::{walk_expr, WalkControl},
@@ -308,8 +308,63 @@ pub fn translate_alter_table(
308308
));
309309
}
310310

311+
let mut column_foreign_keys = Vec::new();
312+
for constraint in &col_def.constraints {
313+
if let ast::ColumnConstraint::ForeignKey {
314+
clause,
315+
defer_clause,
316+
} = &constraint.constraint
317+
{
318+
let foreign_key = ForeignKey {
319+
child_columns: vec![new_column_name.clone()],
320+
parent_table: normalize_ident(clause.tbl_name.as_str()),
321+
parent_columns: clause
322+
.columns
323+
.iter()
324+
.map(|c| normalize_ident(c.col_name.as_str()))
325+
.collect(),
326+
on_delete: clause
327+
.args
328+
.iter()
329+
.find_map(|arg| {
330+
if let ast::RefArg::OnDelete(act) = arg {
331+
Some(*act)
332+
} else {
333+
None
334+
}
335+
})
336+
.unwrap_or(ast::RefAct::NoAction),
337+
on_update: clause
338+
.args
339+
.iter()
340+
.find_map(|arg| {
341+
if let ast::RefArg::OnUpdate(act) = arg {
342+
Some(*act)
343+
} else {
344+
None
345+
}
346+
})
347+
.unwrap_or(ast::RefAct::NoAction),
348+
deferred: match defer_clause {
349+
Some(defer_clause) => {
350+
defer_clause.deferrable
351+
&& matches!(
352+
defer_clause.init_deferred,
353+
Some(ast::InitDeferredPred::InitiallyDeferred)
354+
)
355+
}
356+
None => false,
357+
},
358+
};
359+
column_foreign_keys.push(foreign_key);
360+
}
361+
}
362+
311363
// TODO: All quoted ids will be quoted with `[]`, we should store some info from the parsed AST
312364
btree.columns.push(column.clone());
365+
for foreign_key in &column_foreign_keys {
366+
btree.foreign_keys.push(Arc::new(foreign_key.clone()));
367+
}
313368

314369
let sql = btree.to_sql();
315370
let mut escaped = String::with_capacity(sql.len());
@@ -351,6 +406,7 @@ pub fn translate_alter_table(
351406
program.emit_insn(Insn::AddColumn {
352407
table: table_name.to_owned(),
353408
column,
409+
foreign_keys: column_foreign_keys,
354410
});
355411
},
356412
)?

core/vdbe/execute.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8561,7 +8561,14 @@ pub fn op_add_column(
85618561
pager: &Arc<Pager>,
85628562
mv_store: Option<&Arc<MvStore>>,
85638563
) -> Result<InsnFunctionStepResult> {
8564-
load_insn!(AddColumn { table, column }, insn);
8564+
load_insn!(
8565+
AddColumn {
8566+
table,
8567+
column,
8568+
foreign_keys
8569+
},
8570+
insn
8571+
);
85658572

85668573
let conn = program.connection.clone();
85678574

@@ -8578,7 +8585,10 @@ pub fn op_add_column(
85788585
};
85798586

85808587
let btree = Arc::make_mut(btree);
8581-
btree.columns.push(column.clone())
8588+
btree.columns.push(column.clone());
8589+
for fk in foreign_keys {
8590+
btree.foreign_keys.push(Arc::new(fk.clone()));
8591+
}
85828592
});
85838593

85848594
state.pc += 1;

core/vdbe/explain.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,14 +1742,21 @@ pub fn insn_to_row(
17421742
0,
17431743
format!("drop_column({table}, {column_index})"),
17441744
),
1745-
Insn::AddColumn { table, column } => (
1745+
Insn::AddColumn {
1746+
table,
1747+
column,
1748+
foreign_keys,
1749+
} => (
17461750
"AddColumn",
17471751
0,
17481752
0,
17491753
0,
17501754
Value::build_text(""),
17511755
0,
1752-
format!("add_column({table}, {column:?})"),
1756+
format!(
1757+
"add_column({table}, {column:?}, fks={})",
1758+
foreign_keys.len()
1759+
),
17531760
),
17541761
Insn::AlterColumn { table, column_index, definition: column, rename } => (
17551762
"AlterColumn",

core/vdbe/insn.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55

66
use super::{execute, AggFunc, BranchOffset, CursorID, FuncCtx, InsnFunction, PageIdx};
77
use crate::{
8-
schema::{Affinity, BTreeTable, Column, Index},
8+
schema::{Affinity, BTreeTable, Column, ForeignKey, Index},
99
storage::{pager::CreateBTreeFlags, wal::CheckpointMode},
1010
translate::{collate::CollationSeq, emitter::TransactionMode},
1111
types::KeyInfo,
@@ -1149,6 +1149,7 @@ pub enum Insn {
11491149
AddColumn {
11501150
table: String,
11511151
column: Column,
1152+
foreign_keys: Vec<ForeignKey>,
11521153
},
11531154
AlterColumn {
11541155
table: String,

testing/alter_table.test

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,27 @@ do_execsql_test_on_specific_db {:memory:} alter-table-add-quoted-column {
9090
"CREATE TABLE test (a, [b c])"
9191
}
9292

93+
do_execsql_test_on_specific_db {:memory:} alter-table-add-column-foreign-key {
94+
CREATE TABLE parent(a PRIMARY KEY);
95+
CREATE TABLE child(a);
96+
97+
ALTER TABLE child ADD COLUMN b REFERENCES parent(a);
98+
SELECT sql FROM sqlite_schema WHERE name = 'child';
99+
PRAGMA foreign_keys = ON;
100+
INSERT INTO parent VALUES (1);
101+
INSERT INTO child VALUES (1, 1);
102+
} {
103+
"CREATE TABLE child (a, b, FOREIGN KEY (b) REFERENCES parent(a))"
104+
}
105+
106+
do_execsql_test_in_memory_error alter-table-add-column-foreign-key-enforced {
107+
PRAGMA foreign_keys = ON;
108+
CREATE TABLE parent(a PRIMARY KEY);
109+
CREATE TABLE child(a);
110+
ALTER TABLE child ADD COLUMN b REFERENCES parent(a);
111+
INSERT INTO child VALUES (2, 2);
112+
} {FOREIGN KEY constraint failed}
113+
93114
do_execsql_test_on_specific_db {:memory:} alter-table-drop-column {
94115
CREATE TABLE t (a, b);
95116
INSERT INTO t VALUES (1, 1), (2, 2), (3, 3);
@@ -219,4 +240,4 @@ do_execsql_test_on_specific_db {:memory:} drop-column-regression {
219240
ALTER TABLE t ADD COLUMN col3 BLOB;
220241
ALTER TABLE t DROP COLUMN col1;
221242
SELECT col3 FROM t;
222-
} {{}}
243+
} {{}}

0 commit comments

Comments
 (0)