DDL grammar: writes_table on table-name slots for column narrowing
Handoff-12 §2.2: the DDL TABLE_NAME_EXISTING slot and the relationship-endpoint table idents didn't set writes_table, so column-name slots downstream (drop/rename/change column; relationship qualified columns) couldn't narrow to the active table — candidates leaked from every table. Set writes_table: true on TABLE_NAME_EXISTING and on DR_PARENT/DR_CHILD/AR_PARENT/AR_CHILD table idents. The deliberately-documenting completion test now asserts per-table narrowing.
This commit is contained in:
+10
-8
@@ -1177,8 +1177,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn drop_column_from_offers_only_current_table_columns() {
|
fn drop_column_from_offers_only_current_table_columns() {
|
||||||
// The drop-column path also writes_table → narrowed
|
// The DDL `TABLE_NAME_EXISTING` slot now sets
|
||||||
// columns should appear here too.
|
// `writes_table` (handoff-13 §2.2 fix), so the
|
||||||
|
// column-name slot after the table name narrows to that
|
||||||
|
// table's columns. `OrderTotal` belongs to no table in
|
||||||
|
// this cache's `table_columns`, so it must not leak.
|
||||||
use crate::dsl::types::Type;
|
use crate::dsl::types::Type;
|
||||||
let mut cache = schema_with_table(
|
let mut cache = schema_with_table(
|
||||||
"Customers",
|
"Customers",
|
||||||
@@ -1187,13 +1190,12 @@ mod tests {
|
|||||||
cache.columns.push("OrderTotal".to_string());
|
cache.columns.push("OrderTotal".to_string());
|
||||||
let cs =
|
let cs =
|
||||||
cands_with("drop column from Customers: ", 28, &cache);
|
cands_with("drop column from Customers: ", 28, &cache);
|
||||||
// Note: drop column's table-name slot doesn't set
|
|
||||||
// writes_table today (DDL paths don't carry Phase D
|
|
||||||
// table-column resolution yet). Falls back to global
|
|
||||||
// cache.columns, which is the documented schemaless
|
|
||||||
// fallback. Either narrowed-or-flat is acceptable; the
|
|
||||||
// test just confirms valid columns appear.
|
|
||||||
assert!(cs.contains(&"Email".to_string()), "got {cs:?}");
|
assert!(cs.contains(&"Email".to_string()), "got {cs:?}");
|
||||||
|
assert!(cs.contains(&"id".to_string()), "got {cs:?}");
|
||||||
|
assert!(
|
||||||
|
!cs.contains(&"OrderTotal".to_string()),
|
||||||
|
"OrderTotal (not a Customers column) must not leak: got {cs:?}",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+21
-5
@@ -34,12 +34,19 @@ const TABLE_NAME_NEW: Node = Node::Ident {
|
|||||||
writes_user_listed_column: false,
|
writes_user_listed_column: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `writes_table: true` so that the column-name slots that
|
||||||
|
// follow the table name in `drop column` / `rename column` /
|
||||||
|
// `change column` / `add column` can narrow their candidates to
|
||||||
|
// this table's columns (handoff-12 §2.2). The walker writes
|
||||||
|
// `current_table` / `current_table_columns` on match; the
|
||||||
|
// completion engine reads the snapshot. `drop table` has no
|
||||||
|
// downstream column slot, so the write is harmless there.
|
||||||
const TABLE_NAME_EXISTING: Node = Node::Ident {
|
const TABLE_NAME_EXISTING: Node = Node::Ident {
|
||||||
source: IdentSource::Tables,
|
source: IdentSource::Tables,
|
||||||
role: "table_name",
|
role: "table_name",
|
||||||
validator: None,
|
validator: None,
|
||||||
highlight_override: None,
|
highlight_override: None,
|
||||||
writes_table: false,
|
writes_table: true,
|
||||||
writes_column: false,
|
writes_column: false,
|
||||||
writes_user_listed_column: false,
|
writes_user_listed_column: false,
|
||||||
};
|
};
|
||||||
@@ -118,13 +125,19 @@ const DROP_COLUMN: Node = Node::Seq(DROP_COLUMN_NODES);
|
|||||||
// drop_relationship — `drop relationship (endpoints | name)`
|
// drop_relationship — `drop relationship (endpoints | name)`
|
||||||
// =================================================================
|
// =================================================================
|
||||||
|
|
||||||
|
// `writes_table: true` on each endpoint's table ident so the
|
||||||
|
// `.<col>` slot that follows narrows to that table's columns
|
||||||
|
// (handoff-13 §2.2 follow-up). The two endpoints are walked
|
||||||
|
// sequentially, so `current_table` is correctly the parent
|
||||||
|
// table while walking `parent_column` and the child table
|
||||||
|
// while walking `child_column`.
|
||||||
const DR_PARENT_NODES: &[Node] = &[
|
const DR_PARENT_NODES: &[Node] = &[
|
||||||
Node::Ident {
|
Node::Ident {
|
||||||
source: IdentSource::Tables,
|
source: IdentSource::Tables,
|
||||||
role: "parent_table",
|
role: "parent_table",
|
||||||
validator: None,
|
validator: None,
|
||||||
highlight_override: None,
|
highlight_override: None,
|
||||||
writes_table: false,
|
writes_table: true,
|
||||||
writes_column: false,
|
writes_column: false,
|
||||||
writes_user_listed_column: false,
|
writes_user_listed_column: false,
|
||||||
},
|
},
|
||||||
@@ -147,7 +160,7 @@ const DR_CHILD_NODES: &[Node] = &[
|
|||||||
role: "child_table",
|
role: "child_table",
|
||||||
validator: None,
|
validator: None,
|
||||||
highlight_override: None,
|
highlight_override: None,
|
||||||
writes_table: false,
|
writes_table: true,
|
||||||
writes_column: false,
|
writes_column: false,
|
||||||
writes_user_listed_column: false,
|
writes_user_listed_column: false,
|
||||||
},
|
},
|
||||||
@@ -212,13 +225,16 @@ const ADD_COLUMN: Node = Node::Seq(ADD_COLUMN_NODES);
|
|||||||
// [--create-fk]`
|
// [--create-fk]`
|
||||||
// =================================================================
|
// =================================================================
|
||||||
|
|
||||||
|
// `writes_table: true` on each endpoint's table ident so the
|
||||||
|
// `.<col>` slot narrows to that table's columns (handoff-13
|
||||||
|
// §2.2 follow-up — mirrors DR_PARENT / DR_CHILD).
|
||||||
const AR_PARENT_NODES: &[Node] = &[
|
const AR_PARENT_NODES: &[Node] = &[
|
||||||
Node::Ident {
|
Node::Ident {
|
||||||
source: IdentSource::Tables,
|
source: IdentSource::Tables,
|
||||||
role: "parent_table",
|
role: "parent_table",
|
||||||
validator: None,
|
validator: None,
|
||||||
highlight_override: None,
|
highlight_override: None,
|
||||||
writes_table: false,
|
writes_table: true,
|
||||||
writes_column: false,
|
writes_column: false,
|
||||||
writes_user_listed_column: false,
|
writes_user_listed_column: false,
|
||||||
},
|
},
|
||||||
@@ -241,7 +257,7 @@ const AR_CHILD_NODES: &[Node] = &[
|
|||||||
role: "child_table",
|
role: "child_table",
|
||||||
validator: None,
|
validator: None,
|
||||||
highlight_override: None,
|
highlight_override: None,
|
||||||
writes_table: false,
|
writes_table: true,
|
||||||
writes_column: false,
|
writes_column: false,
|
||||||
writes_user_listed_column: false,
|
writes_user_listed_column: false,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user