feat: ADR-0035 4g — ALTER TABLE add/drop constraint + add FK
ALTER TABLE <T> ADD [CONSTRAINT <name>] (CHECK | UNIQUE | FOREIGN KEY)
and DROP CONSTRAINT <name>. ADD = table-CHECK + composite UNIQUE + FK
(ADD PRIMARY KEY and a named UNIQUE refused — composite UNIQUE is
anonymous in our model). Each ADD reuses a low-level path with a dry-run
guard (table-CHECK/UNIQUE rebuild; FK -> add_relationship, bare
REFERENCES -> parent single PK). DROP CONSTRAINT resolves the name to a
named table-CHECK then a child-side FK, else refuses. One undo step each.
Named table-CHECKs round-trip: a nullable `name` column on
__rdbms_playground_table_checks (rebuild-only arrival; a named add on a
pre-4g project is refused with a "rebuild first" hint) plus a project.yaml
check_constraints {expr, name} extension (bare-string form still reads).
The internal-__rdbms_* guard was folded into do_add_constraint /
do_add_relationship, completing that guard class.
Grammar: the action Choice keeps one branch per verb (add/drop/rename/
alter) with an inner Choice fanning out on the distinct second keyword,
since the walker's Choice does not backtrack between same-led branches.
Tests: 7 Tier-1 parse + 2 yaml round-trip + 1 internal-guard + 9 Tier-3
e2e. Help/usage refreshed; ADR-0035 §13 4g + README + requirements.md in
lockstep.
This commit is contained in:
@@ -33,6 +33,7 @@ use crate::db::{
|
||||
Database, DbError, DeleteResult, DropColumnResult, DropIndexOutcome, DropOutcome, InsertResult,
|
||||
QueryPlan, TableDescription, UpdateResult,
|
||||
};
|
||||
use crate::dsl::command::TableConstraint;
|
||||
use crate::dsl::{AlterTableAction, ChangeColumnMode, Command, ColumnSpec};
|
||||
use crate::dsl::walker::Severity;
|
||||
use crate::event::AppEvent;
|
||||
@@ -2124,6 +2125,30 @@ async fn execute_command_typed(
|
||||
.change_column_type(table, column, ty, ChangeColumnMode::ForceConversion, src)
|
||||
.await
|
||||
.map(CommandOutcome::ChangeColumn),
|
||||
// `ADD [CONSTRAINT <name>] (CHECK | UNIQUE | FOREIGN KEY)`
|
||||
// (ADR-0035 §4g) — each reuses an existing low-level executor
|
||||
// (the FK via the relationship machinery `add 1:n
|
||||
// relationship` uses); one undo step each.
|
||||
AlterTableAction::AddTableConstraint { name, constraint } => match *constraint {
|
||||
TableConstraint::Check { expr_sql } => database
|
||||
.alter_add_table_check(table, name, expr_sql, src)
|
||||
.await
|
||||
.map(|d| CommandOutcome::Schema(Some(d))),
|
||||
TableConstraint::Unique { columns } => database
|
||||
.alter_add_unique(table, columns, src)
|
||||
.await
|
||||
.map(|d| CommandOutcome::Schema(Some(d))),
|
||||
TableConstraint::ForeignKey(fk) => database
|
||||
.alter_add_foreign_key(table, name, fk, src)
|
||||
.await
|
||||
.map(|d| CommandOutcome::Schema(Some(d))),
|
||||
},
|
||||
// `DROP CONSTRAINT <name>` — a named table-CHECK or a named
|
||||
// FK, resolved by the executor (ADR-0035 §4g).
|
||||
AlterTableAction::DropConstraint { name } => database
|
||||
.alter_drop_constraint(table, name, src)
|
||||
.await
|
||||
.map(CommandOutcome::Schema),
|
||||
},
|
||||
Command::AddConstraint {
|
||||
table,
|
||||
|
||||
Reference in New Issue
Block a user