walker: populate from_scope table bindings (ADR-0032 §10.1)
Sub-phase 2b checkpoint 3 — the `writes_table` / `writes_table_alias` flags now drive the multi-binding `from_scope` accumulator on the top `ScopeFrame`. Node::Ident gains `writes_table_alias: bool`. When set on an ident-name slot, the matched name lands on the most-recently- pushed `TableBinding`'s `alias`. All 46 existing Ident sites across the codebase are updated to `writes_table_alias: false` (mechanical — no behavioral change for DSL paths). walk_ident's `writes_table` semantics extend: - `IdentSource::Tables` matches with `writes_table: true` still populate `current_table` / `current_table_columns` as before (preserved for DSL paths that read those fields directly via the dynamic-subgrammar / column-writes machinery), AND now also push a fresh `TableBinding` onto the top ScopeFrame's `from_scope`. The two mechanisms coexist additively — current_table reflects the most-recent `writes_table` write (single-binding view, as before); from_scope is the authoritative multi-binding accumulator that SQL JOINs, subqueries, and CTE bodies use. sql_select.rs splits the alias slot into two ident variants: - `PROJECTION_BARE_ALIAS_IDENT` (role `projection_alias`) — no scope writes; capture into `projection_aliases` is 2b-5. - `TABLE_SOURCE_BARE_ALIAS_IDENT` (role `table_alias`, `writes_table_alias: true`) — sets the top binding's alias. The `AS alias` form likewise splits into PROJECTION_AS_ALIAS and TABLE_SOURCE_AS_ALIAS so each path threads through the correct ident. The bare-alias lookahead factories return the projection or table-source ident accordingly. `TABLE_NAME_IDENT` in sql_select.rs gets `writes_table: true` so each FROM / JOIN table source pushes a binding. The schema-resolved columns are stored on the TableBinding for later use by qualified-prefix completion (2e) and the schema-existence diagnostic (2d). Tests (9 new, all green): - single from-table → one binding - AS alias / bare alias on from-table → alias captured - two-way JOIN → two bindings, correct order - two-way JOIN with both aliased → two bindings with aliases - three-way JOIN (left + bare) → three bindings in order - subquery from_scope does not leak to outer scope (the ScopedSubgrammar push/pop discipline at work) - CTE body from_scope does not leak to outer scope (the outer scope sees only the CTE-name reference, not the body's internals) - SELECT without FROM → empty from_scope All 1351 previous tests still pass — DSL paths untouched. Test totals: 1358 passing, 0 failed, 1 ignored. Clippy clean. Frame is_cte_body marker, body-projection harvest, and projection_aliases population are the remaining 2b work (2b-4 and 2b-5).
This commit is contained in:
@@ -42,6 +42,7 @@ const TABLE_NAME_NEW_IDENT: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
const TABLE_NAME_NEW: Node = Node::Hinted {
|
||||
mode: NEW_NAME_HINT,
|
||||
@@ -63,6 +64,7 @@ const TABLE_NAME_EXISTING: Node = Node::Ident {
|
||||
writes_table: true,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
|
||||
const COLUMN_NAME: Node = Node::Ident {
|
||||
@@ -73,6 +75,7 @@ const COLUMN_NAME: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
|
||||
const COLUMN_NAME_NEW_IDENT: Node = Node::Ident {
|
||||
@@ -83,6 +86,7 @@ const COLUMN_NAME_NEW_IDENT: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
const COLUMN_NAME_NEW: Node = Node::Hinted {
|
||||
mode: NEW_NAME_HINT,
|
||||
@@ -97,6 +101,7 @@ const RELATIONSHIP_NAME: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
|
||||
const RELATIONSHIP_NAME_NEW_IDENT: Node = Node::Ident {
|
||||
@@ -107,6 +112,7 @@ const RELATIONSHIP_NAME_NEW_IDENT: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
const RELATIONSHIP_NAME_NEW: Node = Node::Hinted {
|
||||
mode: NEW_NAME_HINT,
|
||||
@@ -121,6 +127,7 @@ const INDEX_NAME_EXISTING: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
|
||||
const INDEX_NAME_NEW_IDENT: Node = Node::Ident {
|
||||
@@ -131,6 +138,7 @@ const INDEX_NAME_NEW_IDENT: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
const INDEX_NAME_NEW: Node = Node::Hinted {
|
||||
mode: NEW_NAME_HINT,
|
||||
@@ -202,6 +210,7 @@ const DR_PARENT_NODES: &[Node] = &[
|
||||
writes_table: true,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
Node::Punct('.'),
|
||||
Node::Ident {
|
||||
@@ -212,6 +221,7 @@ const DR_PARENT_NODES: &[Node] = &[
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
];
|
||||
const DR_PARENT: Node = Node::Seq(DR_PARENT_NODES);
|
||||
@@ -225,6 +235,7 @@ const DR_CHILD_NODES: &[Node] = &[
|
||||
writes_table: true,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
Node::Punct('.'),
|
||||
Node::Ident {
|
||||
@@ -235,6 +246,7 @@ const DR_CHILD_NODES: &[Node] = &[
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
];
|
||||
const DR_CHILD: Node = Node::Seq(DR_CHILD_NODES);
|
||||
@@ -327,6 +339,7 @@ const AR_PARENT_NODES: &[Node] = &[
|
||||
writes_table: true,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
Node::Punct('.'),
|
||||
Node::Ident {
|
||||
@@ -337,6 +350,7 @@ const AR_PARENT_NODES: &[Node] = &[
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
];
|
||||
const AR_PARENT: Node = Node::Seq(AR_PARENT_NODES);
|
||||
@@ -350,6 +364,7 @@ const AR_CHILD_NODES: &[Node] = &[
|
||||
writes_table: true,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
Node::Punct('.'),
|
||||
Node::Ident {
|
||||
@@ -360,6 +375,7 @@ const AR_CHILD_NODES: &[Node] = &[
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
];
|
||||
const AR_CHILD: Node = Node::Seq(AR_CHILD_NODES);
|
||||
@@ -427,6 +443,7 @@ const NEW_COLUMN_NAME_IDENT: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
const NEW_COLUMN_NAME: Node = Node::Hinted {
|
||||
mode: NEW_NAME_HINT,
|
||||
@@ -890,6 +907,7 @@ const COL_NAME_IDENT: Node = Node::Ident {
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
};
|
||||
const COL_NAME: Node = Node::Hinted {
|
||||
mode: NEW_NAME_HINT,
|
||||
@@ -998,6 +1016,7 @@ const COL_SPEC_NODES: &[Node] = &[
|
||||
writes_table: false,
|
||||
writes_column: false,
|
||||
writes_user_listed_column: false,
|
||||
writes_table_alias: false,
|
||||
},
|
||||
Node::Punct(')'),
|
||||
COLUMN_CONSTRAINT_SUFFIX,
|
||||
|
||||
Reference in New Issue
Block a user