Grammar: with-pk column specs use name(type), matching add column
`create table … with pk` parsed column types as `name:type`,
while `add column` uses `name(type)`. Unify on the parens
form so column-type syntax is consistent across the DSL:
create table T with pk id(serial), name(text)
Only `COL_SPEC` changes (`:` → `( … )`); `build_create_table`
reads columns by role, so it is unaffected. The `:` that
separates table from column in `add column` / `drop column`
is unchanged. Sweeps the test suite, the typing-surface
matrix (two `after_colon` cells renamed to `after_paren`,
4 snapshots regenerated), the friendly catalog's usage
templates, ADR-0009's example, and requirements.md.
1039 passing / 0 failing / 1 ignored; clippy clean.
This commit is contained in:
@@ -109,7 +109,7 @@ fn parse_errors_use_no_engine_vocabulary() {
|
||||
// tiny-win recipe from handoff-5).
|
||||
"change column Tag in Customers: Tag (text)",
|
||||
// unknown type token.
|
||||
"create table T with pk id:varchar",
|
||||
"create table T with pk id(varchar)",
|
||||
// mutually exclusive flags on change column.
|
||||
"change column T: c (int) --force-conversion --dont-convert",
|
||||
// missing required clause.
|
||||
|
||||
@@ -70,7 +70,7 @@ fn create_table_writes_yaml_and_history() {
|
||||
ColumnSpec { name: "Name".to_string(), ty: Type::Text },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -84,7 +84,7 @@ fn create_table_writes_yaml_and_history() {
|
||||
|
||||
let history = read_history(&path);
|
||||
assert_eq!(history.len(), 1, "expected one history line; got {history:?}");
|
||||
assert!(history[0].ends_with("|ok|create table Customers with pk id:serial"));
|
||||
assert!(history[0].ends_with("|ok|create table Customers with pk id(serial)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -100,7 +100,7 @@ fn insert_writes_csv_and_history() {
|
||||
ColumnSpec { name: "Name".to_string(), ty: Type::Text },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -136,7 +136,7 @@ fn drop_table_removes_its_csv() {
|
||||
"Customers".to_string(),
|
||||
vec![ColumnSpec { name: "id".to_string(), ty: Type::Serial }],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -174,7 +174,7 @@ fn delete_with_cascade_rewrites_both_csvs() {
|
||||
"Customers".to_string(),
|
||||
vec![ColumnSpec { name: "id".to_string(), ty: Type::Serial }],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -185,7 +185,7 @@ fn delete_with_cascade_rewrites_both_csvs() {
|
||||
ColumnSpec { name: "CustId".to_string(), ty: Type::Int },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Orders with pk id:serial, CustId:int".to_string()),
|
||||
Some("create table Orders with pk id(serial), CustId(int)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -266,7 +266,7 @@ fn create_table_does_not_write_csv_for_empty_table() {
|
||||
ColumnSpec { name: "Name".to_string(), ty: Type::Text },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -295,7 +295,7 @@ fn delete_all_rows_removes_csv() {
|
||||
ColumnSpec { name: "Name".to_string(), ty: Type::Text },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -335,7 +335,7 @@ fn show_table_appends_history_only() {
|
||||
"Customers".to_string(),
|
||||
vec![ColumnSpec { name: "id".to_string(), ty: Type::Serial }],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -368,7 +368,7 @@ fn failed_command_does_not_append_history_or_change_yaml() {
|
||||
"Customers".to_string(),
|
||||
vec![ColumnSpec { name: "id".to_string(), ty: Type::Serial }],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -380,7 +380,7 @@ fn failed_command_does_not_append_history_or_change_yaml() {
|
||||
"Customers".to_string(),
|
||||
vec![ColumnSpec { name: "id".to_string(), ty: Type::Serial }],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.expect_err("must fail");
|
||||
|
||||
@@ -48,7 +48,7 @@ fn rebuild_restores_schema_only_project() {
|
||||
ColumnSpec { name: "Name".to_string(), ty: Type::Text },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -414,7 +414,7 @@ fn rebuild_restores_indexes() {
|
||||
ColumnSpec { name: "Email".to_string(), ty: Type::Text },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -303,7 +303,7 @@ fn end_to_end_export_then_import_real_project() {
|
||||
ColumnSpec { name: "Name".to_string(), ty: Type::Text },
|
||||
],
|
||||
vec!["id".to_string()],
|
||||
Some("create table Customers with pk id:serial".to_string()),
|
||||
Some("create table Customers with pk id(serial)".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -95,7 +95,7 @@ fn replay_three_lines_dispatches_three_commands() {
|
||||
write_script(
|
||||
project.path(),
|
||||
"seed.commands",
|
||||
"create table T with pk id:int\n\
|
||||
"create table T with pk id(int)\n\
|
||||
add column T: name (text)\n\
|
||||
insert into T (1, 'Alice')\n",
|
||||
);
|
||||
@@ -122,7 +122,7 @@ fn replay_skips_blank_lines_and_comments() {
|
||||
"seed.commands",
|
||||
"# this is a comment\n\
|
||||
\n\
|
||||
create table T with pk id:int\n\
|
||||
create table T with pk id(int)\n\
|
||||
\n\
|
||||
# another comment\n\
|
||||
# comment with leading whitespace\n\
|
||||
@@ -192,7 +192,7 @@ fn replay_aborts_on_first_parse_failure_and_reports_line() {
|
||||
"bad.commands",
|
||||
// Line 1: ok. Line 2: ok. Line 3: parse error
|
||||
// (`broken keyword X` — not a recognised command).
|
||||
"create table T with pk id:int\n\
|
||||
"create table T with pk id(int)\n\
|
||||
add column T: name (text)\n\
|
||||
this is not a command\n\
|
||||
insert into T (1, 'should not happen')\n",
|
||||
@@ -248,7 +248,7 @@ fn replay_rejects_typed_slot_violation_at_parse_time() {
|
||||
write_script(
|
||||
project.path(),
|
||||
"typed.commands",
|
||||
"create table T with pk id:int\n\
|
||||
"create table T with pk id(int)\n\
|
||||
add column T: count (int)\n\
|
||||
insert into T values (1, 'not a number')\n",
|
||||
);
|
||||
@@ -285,7 +285,7 @@ fn replay_aborts_on_first_runtime_failure_and_reports_line() {
|
||||
"bad.commands",
|
||||
// Line 2 references a table that doesn't exist; the
|
||||
// engine refuses, replay stops and reports line 2.
|
||||
"create table T with pk id:int\n\
|
||||
"create table T with pk id(int)\n\
|
||||
add column NotATable: x (text)\n\
|
||||
insert into T (1)\n",
|
||||
);
|
||||
@@ -300,11 +300,11 @@ fn replay_aborts_on_first_runtime_failure_and_reports_line() {
|
||||
fn replay_refuses_nested_replay() {
|
||||
let data = tempdir();
|
||||
let (project, db) = open_project_db(data.path());
|
||||
write_script(project.path(), "inner.commands", "create table T with pk id:int\n");
|
||||
write_script(project.path(), "inner.commands", "create table T with pk id(int)\n");
|
||||
write_script(
|
||||
project.path(),
|
||||
"outer.commands",
|
||||
"create table U with pk id:int\nreplay inner.commands\n",
|
||||
"create table U with pk id(int)\nreplay inner.commands\n",
|
||||
);
|
||||
|
||||
let events = rt().block_on(async {
|
||||
@@ -332,7 +332,7 @@ fn replay_history_log_records_subcommands_only() {
|
||||
write_script(
|
||||
project.path(),
|
||||
"seed.commands",
|
||||
"create table T with pk id:int\nadd column T: name (text)\n",
|
||||
"create table T with pk id(int)\nadd column T: name (text)\n",
|
||||
);
|
||||
|
||||
let events = rt().block_on(async {
|
||||
@@ -344,7 +344,7 @@ fn replay_history_log_records_subcommands_only() {
|
||||
.expect("history.log exists");
|
||||
// Per-command entries landed.
|
||||
assert!(
|
||||
history.lines().any(|l| l.contains("create table T with pk id:int")),
|
||||
history.lines().any(|l| l.contains("create table T with pk id(int)")),
|
||||
"history.log missing create line:\n{history}"
|
||||
);
|
||||
assert!(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Matrix coverage for `create table T with pk [<col>:<type>[, ...]]`
|
||||
//! Matrix coverage for `create table T with pk [<col>(<type>)[, ...]]`
|
||||
//! (ADR-0005, ADR-0009).
|
||||
|
||||
use crate::typing_surface::*;
|
||||
@@ -73,7 +73,7 @@ fn after_pk_word_does_not_re_offer_pk() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_pk_space_with_col_name_typed_expects_colon() {
|
||||
fn after_pk_space_with_col_name_typed_expects_paren() {
|
||||
let schema = schema_empty();
|
||||
let a = assess_at_end("create table Customers with pk Code", &schema);
|
||||
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
||||
@@ -81,10 +81,10 @@ fn after_pk_space_with_col_name_typed_expects_colon() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_colon_expects_type_candidates() {
|
||||
fn after_paren_expects_type_candidates() {
|
||||
let schema = schema_empty();
|
||||
let a = assess_at_end(
|
||||
"create table Customers with pk Code:",
|
||||
"create table Customers with pk Code(",
|
||||
&schema,
|
||||
);
|
||||
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
||||
@@ -92,14 +92,14 @@ fn after_colon_expects_type_candidates() {
|
||||
&a,
|
||||
&["text", "int", "serial", "shortid", "bool"],
|
||||
);
|
||||
crate::snap!("after_colon", a);
|
||||
crate::snap!("after_paren", a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_table_with_explicit_pk_parses() {
|
||||
let schema = schema_empty();
|
||||
let a = assess_at_end(
|
||||
"create table Customers with pk Code:text",
|
||||
"create table Customers with pk Code(text)",
|
||||
&schema,
|
||||
);
|
||||
assert!(matches!(a.state, InputState::Valid));
|
||||
@@ -110,7 +110,7 @@ fn create_table_with_explicit_pk_parses() {
|
||||
fn create_table_with_compound_pk_parses() {
|
||||
let schema = schema_empty();
|
||||
let a = assess_at_end(
|
||||
"create table Memberships with pk UserId:int, GroupId:int",
|
||||
"create table Memberships with pk UserId(int), GroupId(int)",
|
||||
&schema,
|
||||
);
|
||||
assert!(matches!(a.state, InputState::Valid));
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
---
|
||||
source: tests/typing_surface/create_table.rs
|
||||
description: "input=\"create table Customers with pk Code:\" cursor=36"
|
||||
description: "input=\"create table Customers with pk Code(\" cursor=36"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create table Customers with pk Code:",
|
||||
input: "create table Customers with pk Code(",
|
||||
cursor: 36,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
+1
-1
@@ -9,7 +9,7 @@ Assessment {
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Next: `:`",
|
||||
"Next: `(`",
|
||||
),
|
||||
),
|
||||
completion: None,
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
---
|
||||
source: tests/typing_surface/create_table.rs
|
||||
description: "input=\"create table Memberships with pk UserId:int, GroupId:int\" cursor=56"
|
||||
description: "input=\"create table Memberships with pk UserId(int), GroupId(int)\" cursor=58"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create table Memberships with pk UserId:int, GroupId:int",
|
||||
cursor: 56,
|
||||
input: "create table Memberships with pk UserId(int), GroupId(int)",
|
||||
cursor: 58,
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Prose(
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
---
|
||||
source: tests/typing_surface/create_table.rs
|
||||
description: "input=\"create table Customers with pk Code:text\" cursor=40"
|
||||
description: "input=\"create table Customers with pk Code(text)\" cursor=41"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "create table Customers with pk Code:text",
|
||||
cursor: 40,
|
||||
input: "create table Customers with pk Code(text)",
|
||||
cursor: 41,
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Prose(
|
||||
|
||||
Reference in New Issue
Block a user