SchemaRetrieverNode
Overview
- Retrieves schema context for a sub‑query using staged vector search and authoritative snapshots.
- Exists to constrain planning to relevant tables/columns while remaining schema‑grounded.
- Sits at the entry of the SQL agent subgraph.
- Class:
SchemaRetrieverNode - Source:
packages/nl2sql/src/nl2sql/pipeline/nodes/schema_retriever/node.py
Responsibilities
- Build a semantic query from sub‑query intent, filters, group_by, expected_schema, metrics.
- Retrieve candidate tables/columns/relationships from the run's pinned
Retriever. - Resolve authoritative tables from
SchemaStore. - Fall back to full schema snapshot when retrieval returns no candidates.
- Rank the datasource's
preferred_tablesfirst and setpreferred=Trueon them, on the full-snapshot path and the retrieval path; see Preferred tables. - Hide every table the caller may not read, on every path. The caller's
Scope(ctx.policy_engine.scope(user_context, datasource_id), the one the logical validator refuses with) filters the snapshot's tables before anything is chosen: the full-snapshot path sends the readable tables only (and compares their count, not the snapshot's, withfull_snapshot_max_tables), and the vector searches run withtables=<the readable tables>, so the topkis filled with readable ones. A hidden table's name, columns, keys, statistics and data never reach the prompt or the run trace, and a relationship from a readable table into a hidden one is dropped. See Security Model. - The tables it returns keep every statistic, but the planner and refiner
prompts render them with
render_schema_for_prompt(schema.py), which sends onlysample_valuesand, for date, time and numeric columns other than keys, arangeof[min_value, max_value]. See AST Planner: prompt layout.
Position in Execution Graph
Upstream: - SQL agent subgraph entry.
Downstream:
- ASTPlannerNode
Trigger conditions: - Always executed as the subgraph entry node.
flowchart LR
SchemaRetriever[SchemaRetrieverNode] --> Planner[ASTPlannerNode]
Inputs
From SubgraphExecutionState:
sub_query(required): includes intent, filters, group_by, expected_schema, metrics, schema_version.
From Runtime:
vector_store(optional)state(the state store; required for authoritative resolution)ds_registry(optional):get_preferred_tables(datasource_id)
Validation performed:
- If
sub_querymissing, returns emptyrelevant_tables. - If no candidates from vector store, falls back to full snapshot.
Outputs
Mutations to SubgraphExecutionState:
relevant_tables(list ofTable)reasoningandwarningsin fallback paths
Side effects:
- Vector store queries.
- Schema store reads.
Internal Flow (Step-by-Step)
- Build semantic query with
_build_semantic_query(). - Retrieve table/metric chunks via
retrieve_schema_context(),k = settings.retrieval.table_k(8). - If no table chunks, retrieve columns via
retrieve_column_candidates(),k = table_k. - If tables are identified, retrieve columns/relationships via
retrieve_planning_context(),k = settings.retrieval.planning_k(12). - Resolve schema snapshot via
_resolve_snapshot(). - Build
Tableobjects with_build_tables_from_snapshot(). - If no tables were found, return full snapshot with warning.
- On exception, log error and return empty
relevant_tables.
Preferred tables
A datasource's preferred_tables (in datasources.yaml, see
Datasources) are
matched against snapshot tables by bare name or schema.table, case ignored.
- Full snapshot: every preferred table is in the list, first.
- Retrieval: preferred tables the
tablessearch picked come first. A preferred table the search scored but did not pick is added (before theplanningsearch, so its columns and relationships are fetched too) when its similarity is at least the least similar pick's minusPREFERRED_MARGIN(0.05). - Each preferred
Tablehaspreferred=True.render_schema_for_promptsorts preferred tables first, then by name, and prints"preferred":trueon them only; with no preferred tables the block is byte-identical to before. Planner instruction 14 tells the model to plan against a preferred table when it answers the question. - Being preferred never shows a hidden table: a preferred table the caller's scope denies is left out like any other.
Contracts & Interfaces
Implements a LangGraph node callable:
Key contracts:
Table/Column(node schema)SchemaSnapshot(authoritative source)
Determinism Guarantees
- Deterministic snapshot resolution for a fixed
schema_version. - Vector retrieval ranking may be non‑deterministic (MMR).
Error Handling
- Exceptions are caught and logged via
logger.error. - Returns empty
relevant_tableswith error reasoning; noPipelineErroris emitted.
Retry + Idempotency
- No internal retry logic.
- Idempotent for fixed vector retrieval output; vector ranking may vary.
Performance Characteristics
- Depends on vector store retrieval latency and schema store reads.
- In‑memory processing of candidates and snapshot tables.
Observability
- Logger:
schema_retriever - Emits reasoning and warnings on fallback paths.
- Returns a
retrievalrecord in its update (not a state field, so LangGraph drops it; the run trace keeps it): the query text embedded, each search's pool with scores, the MMR picks in order and what was dropped, and the tables and columns sent to the planner; or, when the schema is small enough to send whole,skipped: trueand why. Column entries of a table the role cannot read keep their name and rank but lose their scores (their embedded text holds sample values). See Debugging a run.
Configuration
settings.retrieval.full_snapshot_max_tables,table_kandplanning_k(see Settings).- Vector store configuration is set in
Runtime.
Extension Points
- Modify semantic query construction to include additional signals.
- Replace node in
build_sql_agent_graph()for alternative retrieval strategies.
Known Limitations
- No tenant scoping in vector retrieval.
- No caching of retrieved tables across retries.
- Fallback to full schema can be expensive for large schemas.
Related Code
packages/nl2sql/src/nl2sql/pipeline/nodes/schema_retriever/node.pypackages/nl2sql/src/nl2sql/indexing/retrieval.py