feat(hint): H2 Phase C batch 2 — DDL tier-3 hints (ADR-0053)
Per-form hints for the schema-shaping commands: create table, create m:n, add column/index/constraint, drop table/column/relationship/ index/constraint, rename column, change column (add_relationship was the Phase-B exemplar). Examples verified against the canonical usage templates. hint_ids wired on CREATE/CREATE_M2N/DROP/RENAME/CHANGE; catalogue + keys.rs registered. +2 spot tests (incl. multi-form DROP disambiguation); 2491 pass / 1 ignored, clippy clean.
This commit is contained in:
+20
@@ -5862,6 +5862,26 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Phase C batch 2: DDL hints render (incl. multi-form DROP) ──
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f1_on_create_table_renders_its_hint_block() {
|
||||||
|
let mut app = App::new();
|
||||||
|
type_str(&mut app, "create table Customers with pk id(serial)");
|
||||||
|
f1(&mut app);
|
||||||
|
assert!(output_contains(&app, "Create a new table"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f1_disambiguates_drop_forms() {
|
||||||
|
let mut app = App::new();
|
||||||
|
type_str(&mut app, "drop index idx_email");
|
||||||
|
f1(&mut app);
|
||||||
|
// Resolves drop_index, not drop_table/column/etc.
|
||||||
|
assert!(output_contains(&app, "Remove an index by name"));
|
||||||
|
assert!(!output_contains(&app, "Remove a table"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn messages_command_toggles_verbosity_and_reports() {
|
fn messages_command_toggles_verbosity_and_reports() {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
|
|||||||
+11
-5
@@ -968,7 +968,13 @@ pub static DROP: CommandNode = CommandNode {
|
|||||||
shape: DROP_SHAPE,
|
shape: DROP_SHAPE,
|
||||||
ast_builder: build_drop,
|
ast_builder: build_drop,
|
||||||
help_id: Some("ddl.drop"),
|
help_id: Some("ddl.drop"),
|
||||||
hint_ids: &[],
|
hint_ids: &[
|
||||||
|
"drop_table",
|
||||||
|
"drop_column",
|
||||||
|
"drop_relationship",
|
||||||
|
"drop_index",
|
||||||
|
"drop_constraint",
|
||||||
|
],
|
||||||
usage_ids: &[
|
usage_ids: &[
|
||||||
"parse.usage.drop_table",
|
"parse.usage.drop_table",
|
||||||
"parse.usage.drop_column",
|
"parse.usage.drop_column",
|
||||||
@@ -1004,7 +1010,7 @@ pub static RENAME: CommandNode = CommandNode {
|
|||||||
shape: RENAME_COLUMN,
|
shape: RENAME_COLUMN,
|
||||||
ast_builder: build_rename_column,
|
ast_builder: build_rename_column,
|
||||||
help_id: Some("ddl.rename"),
|
help_id: Some("ddl.rename"),
|
||||||
hint_ids: &[],
|
hint_ids: &["rename_column"],
|
||||||
usage_ids: &["parse.usage.rename_column"],};
|
usage_ids: &["parse.usage.rename_column"],};
|
||||||
|
|
||||||
pub static CHANGE: CommandNode = CommandNode {
|
pub static CHANGE: CommandNode = CommandNode {
|
||||||
@@ -1012,7 +1018,7 @@ pub static CHANGE: CommandNode = CommandNode {
|
|||||||
shape: CHANGE_COLUMN,
|
shape: CHANGE_COLUMN,
|
||||||
ast_builder: build_change_column,
|
ast_builder: build_change_column,
|
||||||
help_id: Some("ddl.change"),
|
help_id: Some("ddl.change"),
|
||||||
hint_ids: &[],
|
hint_ids: &["change_column"],
|
||||||
usage_ids: &["parse.usage.change_column"],};
|
usage_ids: &["parse.usage.change_column"],};
|
||||||
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
@@ -1373,7 +1379,7 @@ pub static CREATE: CommandNode = CommandNode {
|
|||||||
shape: CREATE_TABLE,
|
shape: CREATE_TABLE,
|
||||||
ast_builder: build_create_table,
|
ast_builder: build_create_table,
|
||||||
help_id: Some("ddl.create"),
|
help_id: Some("ddl.create"),
|
||||||
hint_ids: &[],
|
hint_ids: &["create_table"],
|
||||||
usage_ids: &["parse.usage.create_table"],};
|
usage_ids: &["parse.usage.create_table"],};
|
||||||
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
@@ -1442,7 +1448,7 @@ pub static CREATE_M2N: CommandNode = CommandNode {
|
|||||||
shape: CREATE_M2N_SHAPE,
|
shape: CREATE_M2N_SHAPE,
|
||||||
ast_builder: build_create_m2n,
|
ast_builder: build_create_m2n,
|
||||||
help_id: Some("ddl.create_m2n"),
|
help_id: Some("ddl.create_m2n"),
|
||||||
hint_ids: &[],
|
hint_ids: &["create_m2n"],
|
||||||
usage_ids: &["parse.usage.create_m2n"],
|
usage_ids: &["parse.usage.create_m2n"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -917,9 +917,12 @@ mod hint_key_tests {
|
|||||||
hint_key_for_input_in_mode("insert into T values (1)", Mode::Simple),
|
hint_key_for_input_in_mode("insert into T values (1)", Mode::Simple),
|
||||||
Some("insert")
|
Some("insert")
|
||||||
);
|
);
|
||||||
// A node with no hint_ids yet → None (tier-2 fallback).
|
// Multi-form DROP disambiguates to the typed form too.
|
||||||
assert_eq!(hint_key_for_input_in_mode("drop table T", Mode::Simple), None);
|
assert_eq!(
|
||||||
// Unknown entry word → None.
|
hint_key_for_input_in_mode("drop table T", Mode::Simple),
|
||||||
|
Some("drop_table")
|
||||||
|
);
|
||||||
|
// Unknown entry word → None (tier-2 fallback).
|
||||||
assert_eq!(hint_key_for_input_in_mode("zzz", Mode::Simple), None);
|
assert_eq!(hint_key_for_input_in_mode("zzz", Mode::Simple), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,6 +269,43 @@ pub const KEYS_AND_PLACEHOLDERS: &[(&str, &[&str])] = &[
|
|||||||
("hint.cmd.redo.example", &[]),
|
("hint.cmd.redo.example", &[]),
|
||||||
("hint.cmd.copy.what", &[]),
|
("hint.cmd.copy.what", &[]),
|
||||||
("hint.cmd.copy.example", &[]),
|
("hint.cmd.copy.example", &[]),
|
||||||
|
// Phase C batch 2 — DDL command hints.
|
||||||
|
("hint.cmd.create_table.what", &[]),
|
||||||
|
("hint.cmd.create_table.example", &[]),
|
||||||
|
("hint.cmd.create_table.concept", &[]),
|
||||||
|
("hint.cmd.create_m2n.what", &[]),
|
||||||
|
("hint.cmd.create_m2n.example", &[]),
|
||||||
|
("hint.cmd.create_m2n.concept", &[]),
|
||||||
|
("hint.cmd.add_column.what", &[]),
|
||||||
|
("hint.cmd.add_column.example", &[]),
|
||||||
|
("hint.cmd.add_column.concept", &[]),
|
||||||
|
("hint.cmd.add_index.what", &[]),
|
||||||
|
("hint.cmd.add_index.example", &[]),
|
||||||
|
("hint.cmd.add_index.concept", &[]),
|
||||||
|
("hint.cmd.add_constraint.what", &[]),
|
||||||
|
("hint.cmd.add_constraint.example", &[]),
|
||||||
|
("hint.cmd.add_constraint.concept", &[]),
|
||||||
|
("hint.cmd.drop_table.what", &[]),
|
||||||
|
("hint.cmd.drop_table.example", &[]),
|
||||||
|
("hint.cmd.drop_table.concept", &[]),
|
||||||
|
("hint.cmd.drop_column.what", &[]),
|
||||||
|
("hint.cmd.drop_column.example", &[]),
|
||||||
|
("hint.cmd.drop_column.concept", &[]),
|
||||||
|
("hint.cmd.drop_relationship.what", &[]),
|
||||||
|
("hint.cmd.drop_relationship.example", &[]),
|
||||||
|
("hint.cmd.drop_relationship.concept", &[]),
|
||||||
|
("hint.cmd.drop_index.what", &[]),
|
||||||
|
("hint.cmd.drop_index.example", &[]),
|
||||||
|
("hint.cmd.drop_index.concept", &[]),
|
||||||
|
("hint.cmd.drop_constraint.what", &[]),
|
||||||
|
("hint.cmd.drop_constraint.example", &[]),
|
||||||
|
("hint.cmd.drop_constraint.concept", &[]),
|
||||||
|
("hint.cmd.rename_column.what", &[]),
|
||||||
|
("hint.cmd.rename_column.example", &[]),
|
||||||
|
("hint.cmd.rename_column.concept", &[]),
|
||||||
|
("hint.cmd.change_column.what", &[]),
|
||||||
|
("hint.cmd.change_column.example", &[]),
|
||||||
|
("hint.cmd.change_column.concept", &[]),
|
||||||
(
|
(
|
||||||
"hint.ambient_invalid_ident",
|
"hint.ambient_invalid_ident",
|
||||||
&["kind", "found"],
|
&["kind", "found"],
|
||||||
|
|||||||
@@ -456,6 +456,55 @@ hint:
|
|||||||
copy:
|
copy:
|
||||||
what: "Copy the output panel to the clipboard — all of it, or just the last command's output."
|
what: "Copy the output panel to the clipboard — all of it, or just the last command's output."
|
||||||
example: "copy last"
|
example: "copy last"
|
||||||
|
# DDL — schema-shaping commands (Phase C batch 2).
|
||||||
|
create_table:
|
||||||
|
what: "Create a new table — its columns, their types, and a primary key."
|
||||||
|
example: "create table Customers with pk id(serial), name(text), email(text)"
|
||||||
|
concept: "A table is a set of rows that share the same columns. The primary key uniquely identifies each row; a `serial` key numbers the rows for you."
|
||||||
|
create_m2n:
|
||||||
|
what: "Create a junction table linking two tables many-to-many."
|
||||||
|
example: "create m:n relationship from Students to Courses"
|
||||||
|
concept: "A many-to-many link (a student takes many courses; a course has many students) can't live in either table, so it gets its own junction table holding a foreign key to each side."
|
||||||
|
add_column:
|
||||||
|
what: "Add a new column to an existing table."
|
||||||
|
example: "add column Customers: phone (text)"
|
||||||
|
concept: "Existing rows take the column's default, or null. A `not null` column with no default can't be added to a table that already has rows — there'd be nothing to put in them."
|
||||||
|
add_index:
|
||||||
|
what: "Create an index on one or more columns to speed up lookups."
|
||||||
|
example: "add index as idx_email on Customers (email)"
|
||||||
|
concept: "An index is a sorted side-structure that makes a lookup like `where email = …` fast, at the cost of a little space and slightly slower writes."
|
||||||
|
add_constraint:
|
||||||
|
what: "Add a constraint — not null, unique, default, or check — to an existing column."
|
||||||
|
example: "add constraint not null to Customers.email"
|
||||||
|
concept: "A constraint is a rule the database enforces on every row. Adding one fails if existing rows already break it, so you fix the data first."
|
||||||
|
drop_table:
|
||||||
|
what: "Remove a table and all of its rows."
|
||||||
|
example: "drop table Customers"
|
||||||
|
concept: "If other tables reference this one through a relationship, drop those relationships (or their child rows) first — the database won't orphan them."
|
||||||
|
drop_column:
|
||||||
|
what: "Remove a column from a table."
|
||||||
|
example: "drop column Customers: phone"
|
||||||
|
concept: "The column's values are lost. You can't drop a primary-key column, or one a relationship depends on."
|
||||||
|
drop_relationship:
|
||||||
|
what: "Remove a relationship between two tables."
|
||||||
|
example: "drop relationship customer_orders"
|
||||||
|
concept: "This drops the foreign-key link and stops the database enforcing it; the tables and their rows stay. The foreign-key column itself remains unless you also drop it."
|
||||||
|
drop_index:
|
||||||
|
what: "Remove an index by name."
|
||||||
|
example: "drop index idx_email"
|
||||||
|
concept: "Only the lookup shortcut goes — the data is untouched. Queries still work, just without that speed-up."
|
||||||
|
drop_constraint:
|
||||||
|
what: "Remove a constraint from a column."
|
||||||
|
example: "drop constraint not null from Customers.email"
|
||||||
|
concept: "The rule stops being enforced from now on; rows already stored are left as they are."
|
||||||
|
rename_column:
|
||||||
|
what: "Rename a column, keeping its values and type."
|
||||||
|
example: "rename column Customers: email to contact_email"
|
||||||
|
concept: "Only the name changes — the stored data is the same. References to the column are reconciled so nothing breaks."
|
||||||
|
change_column:
|
||||||
|
what: "Change a column's type, converting the existing values."
|
||||||
|
example: "change column Customers: status (int)"
|
||||||
|
concept: "The database converts each stored value to the new type; if a value can't convert it refuses the change, so you don't silently lose data. Flags let you force or skip the conversion."
|
||||||
err:
|
err:
|
||||||
foreign_key:
|
foreign_key:
|
||||||
child_side:
|
child_side:
|
||||||
|
|||||||
Reference in New Issue
Block a user