feat: create m:n relationship convenience command (C4, ADR-0045)
`create m:n relationship from <T1> to <T2> [as <name>]` generates a
junction table with one FK column per parent PK column ({table}_{pkcol},
typed via fk_target_type), a compound PK over them, and two CASCADE 1:n
relationships -- all in one do_create_table call = one undo step.
Auto-named {T1}_{T2} (optional `as`), both modes, compound-parent PKs
supported (ADR-0043). Self-referential m:n / PK-less parent / internal
junction name / name collision all refused.
Wired across every surface: grammar (separate CREATE_M2N node), worker
executor, runtime dispatch, completion ("m:n" composite), hints,
highlighting, help + usage catalog + disambiguator, and the advanced-mode
DSL->SQL teaching echo (render_create_m2n, round-trips as valid SQL).
Generalized/fixed framework assumptions the build + two /runda passes
surfaced (all behaviour-preserving for existing commands):
- simple-mode dispatch committed simple.first() unconditionally -> tries
candidates, so `create table` no longer shadows `create m:n`.
- the completion continuation-merge was advanced-only -> runs in simple
mode too when an entry word has >1 DSL form (gated simple_count>1).
- do_create_table now rejects internal `__rdbms_*` names (closes a
pre-existing hole on the DSL create-table path too, not just m:n).
- usage disambiguator now recognizes the `m:n` opener.
Tests: 14 integration (tests/it/m2n.rs), 7 typing-surface matrix, echo /
highlight / usage / internal-name units. Closes C4.
2237 pass / 0 fail / 1 ignored. Clippy clean.
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: tests/typing_surface/create_m2n.rs
|
||||
assertion_line: 72
|
||||
description: "input=\"create m:n relationship from Customers to Orders as \" cursor=52"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create m:n relationship from Customers to Orders as ",
|
||||
cursor: 52,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a name",
|
||||
),
|
||||
),
|
||||
completion: None,
|
||||
parse_result: Err(
|
||||
"Invalid(at_eof)",
|
||||
),
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
source: tests/typing_surface/create_m2n.rs
|
||||
assertion_line: 16
|
||||
description: "input=\"create \" cursor=7"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create ",
|
||||
cursor: 7,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "table",
|
||||
kind: Keyword,
|
||||
mode: Simple,
|
||||
},
|
||||
Candidate {
|
||||
text: "m:n",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
7,
|
||||
7,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "table",
|
||||
kind: Keyword,
|
||||
mode: Simple,
|
||||
},
|
||||
Candidate {
|
||||
text: "m:n",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Err(
|
||||
"Invalid(at_eof)",
|
||||
),
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
source: tests/typing_surface/create_m2n.rs
|
||||
assertion_line: 34
|
||||
description: "input=\"create m:n relationship from \" cursor=29"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create m:n relationship from ",
|
||||
cursor: 29,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "Customers",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
Candidate {
|
||||
text: "Orders",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
29,
|
||||
29,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "Customers",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
Candidate {
|
||||
text: "Orders",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Err(
|
||||
"Invalid(at_eof)",
|
||||
),
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
source: tests/typing_surface/create_m2n.rs
|
||||
assertion_line: 43
|
||||
description: "input=\"create m:n relationship from Customers to \" cursor=42"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create m:n relationship from Customers to ",
|
||||
cursor: 42,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "Customers",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
Candidate {
|
||||
text: "Orders",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
42,
|
||||
42,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "Customers",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
Candidate {
|
||||
text: "Orders",
|
||||
kind: Identifier,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Err(
|
||||
"Invalid(at_eof)",
|
||||
),
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: tests/typing_surface/create_m2n.rs
|
||||
assertion_line: 52
|
||||
description: "input=\"create m:n relationship from Customers to Orders\" cursor=48"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create m:n relationship from Customers to Orders",
|
||||
cursor: 48,
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Submit with Enter",
|
||||
),
|
||||
),
|
||||
completion: None,
|
||||
parse_result: Ok(
|
||||
"CreateM2nRelationship",
|
||||
),
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: tests/typing_surface/create_m2n.rs
|
||||
assertion_line: 64
|
||||
description: "input=\"create m:n relationship from Customers to Orders as CustomerOrders\" cursor=66"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create m:n relationship from Customers to Orders as CustomerOrders",
|
||||
cursor: 66,
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a name",
|
||||
),
|
||||
),
|
||||
completion: None,
|
||||
parse_result: Ok(
|
||||
"CreateM2nRelationship",
|
||||
),
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
---
|
||||
source: tests/typing_surface/create_m2n.rs
|
||||
assertion_line: 25
|
||||
description: "input=\"create m:n relationship \" cursor=24"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create m:n relationship ",
|
||||
cursor: 24,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "from",
|
||||
kind: Keyword,
|
||||
mode: Simple,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
24,
|
||||
24,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "from",
|
||||
kind: Keyword,
|
||||
mode: Simple,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Err(
|
||||
"Invalid(at_eof)",
|
||||
),
|
||||
}
|
||||
+11
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tests/typing_surface/create_table.rs
|
||||
assertion_line: 13
|
||||
description: "input=\"create \" cursor=7"
|
||||
expression: "& a"
|
||||
---
|
||||
@@ -13,6 +14,11 @@ Assessment {
|
||||
Candidate {
|
||||
text: "table",
|
||||
kind: Keyword,
|
||||
mode: Simple,
|
||||
},
|
||||
Candidate {
|
||||
text: "m:n",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
@@ -30,6 +36,11 @@ Assessment {
|
||||
Candidate {
|
||||
text: "table",
|
||||
kind: Keyword,
|
||||
mode: Simple,
|
||||
},
|
||||
Candidate {
|
||||
text: "m:n",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
},
|
||||
],
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tests/typing_surface/create_table.rs
|
||||
assertion_line: 48
|
||||
description: "input=\"create table Customers with \" cursor=28"
|
||||
expression: "& a"
|
||||
---
|
||||
@@ -13,7 +14,7 @@ Assessment {
|
||||
Candidate {
|
||||
text: "pk",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
mode: Simple,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
@@ -30,7 +31,7 @@ Assessment {
|
||||
Candidate {
|
||||
text: "pk",
|
||||
kind: Keyword,
|
||||
mode: Both,
|
||||
mode: Simple,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user