constraints: add constraint / drop constraint on existing columns (ADR-0029 §2.2)
Adds the two commands for modifying a column's constraints after creation, completing ADR-0029's §2.2 surface. Grammar (dsl/grammar/ddl.rs): `add constraint <constraint> to <T>.<col>` reuses the §2.1 COLUMN_CONSTRAINT choice; `drop constraint <kind> from <T>.<col>` names only the kind. Both join the `add` / `drop` choices, discriminated by the `constraint` form word. AST (dsl/command.rs): `Command::AddConstraint` / `DropConstraint` plus the `Constraint` / `ConstraintKind` enums. Worker (db.rs): `do_add_constraint` / `do_drop_constraint` apply the change through the rebuild-table primitive. `add` runs the §5 dry-run first — `not null` / `unique` / `check` against a populated column are refused, before any write, with a pretty-printed table of offending rows. §9 redundant-on-PK declarations and §6 `default` on an auto-generated column are friendly refusals; dropping a constraint the column does not carry is likewise refused. Also fixes schema_to_ddl, which suppressed UNIQUE for every PK column — a compound-PK member is not individually unique, so an explicit UNIQUE on it must survive the rebuild. 23 tests added (6 grammar, 17 worker); 3 completion-test and 3 matrix snapshots updated for the new `constraint` subcommand.
This commit is contained in:
+19
-10
@@ -844,17 +844,19 @@ mod tests {
|
||||
fn multi_candidate_position_offers_add_subcommands() {
|
||||
// After `add ` the parser expects `column` (for
|
||||
// `add column ...`), `index` (for `add index ...`,
|
||||
// ADR-0025), and `1` (the opener for
|
||||
// ADR-0025), `constraint` (for `add constraint ...`,
|
||||
// ADR-0029 §2.2), and `1` (the opener for
|
||||
// `add 1:n relationship ...`). The completion engine
|
||||
// sections keyword candidates (`column`, `index`)
|
||||
// ahead of the `1:n` composite literal, so the literal
|
||||
// sorts last even though `add 1:n` is declared second.
|
||||
// sections keyword candidates ahead of the `1:n`
|
||||
// composite literal, so the literal sorts last even
|
||||
// though `add 1:n` is declared second.
|
||||
let cs = cands("add ", 4);
|
||||
assert_eq!(
|
||||
cs,
|
||||
vec![
|
||||
"column".to_string(),
|
||||
"index".to_string(),
|
||||
"constraint".to_string(),
|
||||
"1:n".to_string(),
|
||||
]
|
||||
);
|
||||
@@ -1113,10 +1115,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_offers_all_four_subcommands() {
|
||||
fn drop_offers_all_five_subcommands() {
|
||||
// `drop` branches: column / relationship / table / index
|
||||
// (ADR-0025). Candidates follow grammar declaration
|
||||
// order, so `index` — added last — appears last.
|
||||
// (ADR-0025) / constraint (ADR-0029 §2.2). Candidates
|
||||
// follow grammar declaration order, so `constraint` —
|
||||
// added last — appears last.
|
||||
let cs = cands("drop ", 5);
|
||||
assert_eq!(
|
||||
cs,
|
||||
@@ -1125,6 +1128,7 @@ mod tests {
|
||||
"relationship".to_string(),
|
||||
"table".to_string(),
|
||||
"index".to_string(),
|
||||
"constraint".to_string(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -1686,15 +1690,20 @@ mod tests {
|
||||
c.sort_by(|a, b| a.text.cmp(&b.text));
|
||||
c
|
||||
}
|
||||
// `add ` exposes `column`, `1:n` and `index` — the
|
||||
// alphabetic ranker reorders them.
|
||||
// `add ` exposes `column`, `1:n`, `index` and
|
||||
// `constraint` — the alphabetic ranker reorders them.
|
||||
let cache = SchemaCache::default();
|
||||
let comp = candidates_at_cursor_with("add ", 4, &cache, alphabetic_ranker)
|
||||
.expect("some completion");
|
||||
let texts: Vec<String> = comp.candidates.into_iter().map(|c| c.text).collect();
|
||||
assert_eq!(
|
||||
texts,
|
||||
vec!["1:n".to_string(), "column".to_string(), "index".to_string()]
|
||||
vec![
|
||||
"1:n".to_string(),
|
||||
"column".to_string(),
|
||||
"constraint".to_string(),
|
||||
"index".to_string(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user