fivenines
6/25

Guided Problem

MiniPG Build 03: Give One Query One Authorized Meaning

Time
25m
Level
intermediate
Artifacts
not specified
Progress0%

Build Your Own PostgreSQL

Binding Names, Types, and Schemas

Time
6 min
Prerequisites
Parsing SQL Into Trees

What You Will Learn

  • Resolve parsed names through scopes, schemas, and PostgreSQL catalogs
  • Explain how types select concrete operators, functions, and coercions
  • Distinguish an analyzed query tree from both a raw parse tree and a plan
bindingscopeschemasearch pathcatalogtype coercionoperator resolutionprivilege

Syntax meets the catalog

A bookstore query asks for `price` from `books`. Parsing proves that both are valid names in a valid select statement. It cannot prove which `books` relation the session means, whether `price` is a column of it, which comparison operator accepts its type, or whether this user may read it. Binding—PostgreSQL's parse analysis—connects grammatical structure to the live database.

The catalog is the database's record of itself. It stores tables, columns, indexes, functions, operators, types, schemas, constraints, privileges, and many other facts. In PostgreSQL, the catalog is not a side document. It is stored in relations and consulted by the engine. The binding invariant is that every remaining reference in the analyzed tree identifies a specific allowed object and has a type-correct interpretation.

Trace a bound predicate

Consider `SELECT title FROM store.books WHERE price > 20`. The schema-qualified relation resolves to a catalog identity and introduces its columns into the query scope. `title` and `price` bind to columns of that range-table entry. The untyped literal `20` acquires a compatible numeric type from context. With both input types known, analysis chooses the exact greater-than operator and records any required coercion. Finally, privileges are checked against the resolved relation and columns.

The result is not an execution strategy. It does not choose an index, scan direction, join order, or buffer. It is an analyzed query tree whose object identities and expression types no longer depend on textual guesses. This boundary lets planning ask 'which equivalent route is cheapest?' without repeatedly asking 'what did the author mean?'

Scope comes before strategy

Name resolution starts with scope. A query can mention relations in a `from` clause, aliases for those relations, columns exposed by subqueries, common table expressions, and names from outer queries. A column reference such as `id` is valid only if exactly one visible relation provides that name. If two do, the reference is ambiguous. If none do, it is unknown. These errors are not syntax errors. They are failures to attach the parsed tree to available objects.

Schemas add another layer. The table name `accounts` may resolve through a search path, while `billing.accounts` names a specific namespace. Search paths are convenient, but they are also part of the execution context. Changing the path can change which object a query means. Binding freezes that choice for the statement by replacing loose names with object identities from the catalog.

Binding pipeline

Rendering diagram…

Types are chosen, not assumed

Types turn a tree of expressions into something executable. The text `1` does not always start with a final type. It may become an integer, numeric, or another compatible type depending on context. The operator `+` is not one operation either. It is a name resolved against input types. Integer addition, numeric addition, interval addition, and user-defined additions can all share a surface symbol. The analyzer must choose the exact operator implementation or reject the expression.

A dynamically typed design pushes this work later. Values carry loose runtime tags, and operators decide what to do at execution. That can make analysis smaller, but it delays errors and weakens planning. If the planner does not know whether a predicate compares integers or text, it cannot reason well about indexes, selectivity, or function volatility. Early type resolution gives later stages a firmer world.

The opposite mistake is to over-resolve too soon. Some statements are prepared with parameters whose values arrive later. A placeholder may have an inferred type from context, or it may need explicit typing from the client. The analyzer must preserve enough parameter information for later binding while still rejecting statements that cannot possibly be made meaningful. This balance is one reason prepared statements are more than cached strings.

Security rides on meaning

Privileges belong in this layer because they depend on meaning. The server cannot decide whether a user may read `balance` until it knows which column named `balance` is being referenced. It cannot decide whether a function call is allowed until it resolves the function. Authorization checks ride on top of successful binding. This keeps the security boundary attached to real catalog objects rather than textual guesses.

Views and rules make binding more interesting. A view looks like a table to the user, but it expands into a stored query. The analyzer may replace a view reference with the view's underlying query tree, adjusted for aliases and permissions. This creates a layered kind of meaning. The user's query binds to the view, and the view binds to its base objects. The system must preserve both the semantic result and the security rules that came with the view.

The output of analysis is still not a physical plan. It is an enriched query tree. Names have become object identifiers. Column references point to positions in range tables. Operators and functions are selected. Types are known. Coercions may have been inserted. Permissions have been checked. The tree now says not merely what the SQL looked like, but what it means in this database at this moment.

This stage prevents a great deal of confusion from leaking downstream. The planner should not wonder whether `accounts` exists. The executor should not discover halfway through a scan that `balance > 'abc'` has no valid operator. Binding settles meaning early so later layers can focus on strategy and movement.

Into planning

There is still a major gap between meaning and execution. An analyzed query may describe joins, filters, grouping, and ordering, but it does not say which relation to scan first or whether a predicate should be applied before or after a join. To answer that, the database translates meaning into a more algebraic form where equivalent routes can be compared.

Next step

See what actually stuck.

Take the practice scenarios now.