duckdb/duckdb
Planner
Active contributors: Mark Raasveldt, Mark, Mytherin
Purpose
src/planner/ resolves names against the catalog, infers types, and produces a LogicalOperator tree (the logical plan). The output is what the optimizer consumes.
Directory layout
src/planner/
├── planner.cpp Top-level Planner: SQLStatement -> LogicalOperator
├── binder.cpp Binder: walks an SQLStatement and binds it
├── bind_context.cpp Tracks which columns/tables are visible in scope
├── expression_binder.cpp Binds individual expressions
├── expression_iterator.cpp Visitor for bound expression trees
├── logical_operator.cpp Base class for LogicalOperator
├── logical_operator_visitor.cpp Visitor for logical-plan rewriting
├── statement_preprocessor.cpp Pre-binding rewrites (e.g., expand SELECT *)
├── joinside.cpp Determines which side of a join an expression belongs to
├── table_filter*.cpp Push-down-friendly filter representation
├── binder/ Per-statement-kind binders
├── operator/ LogicalOperator subclasses
├── expression/ Bound Expression subclasses
├── expression_binder/ Binders for specialized expression contexts
├── filter/ TableFilter subclasses
└── subquery/ Correlated subquery handlingKey abstractions
| Type | File | Role |
|---|---|---|
Planner |
src/planner/planner.cpp |
Entry point. Planner::CreatePlan(unique_ptr<SQLStatement>) produces a BoundStatement. |
Binder |
src/planner/binder.cpp |
Walks a SQLStatement and a BindContext, dispatching to specialized binders per statement kind. |
BindContext |
src/planner/bind_context.cpp |
The current name-resolution scope: visible tables, aliases, column bindings. |
LogicalOperator |
src/include/duckdb/planner/logical_operator.hpp |
A node in the logical plan. ~30 subclasses including LogicalProjection, LogicalFilter, LogicalAggregate, LogicalJoin, LogicalGet, LogicalSet. |
Expression |
src/include/duckdb/planner/expression.hpp |
A bound expression with LogicalType. Subclasses include BoundColumnRefExpression, BoundFunctionExpression, BoundAggregateExpression, BoundCastExpression. |
ColumnBinding |
src/include/duckdb/planner/column_binding.hpp |
(table_index, column_index) — uniquely identifies a column across the plan. |
BoundStatement |
src/include/duckdb/planner/bound_statement.hpp |
The output: a LogicalOperator plus column metadata. |
TableFilter |
src/planner/filter/, src/planner/table_filter_set.cpp |
Pushdown-friendly conjunctions of filters on a single column, handed to scans. |
How it works
graph TD
Stmt[SQLStatement] --> StmtPreproc[StatementPreprocessor: rewrites]
StmtPreproc --> StmtBinder[Statement-specific Binder]
StmtBinder --> ExprBinder[ExpressionBinder per scope]
ExprBinder --> Catalog[Catalog lookups]
StmtBinder --> Logical[LogicalOperator tree]
Logical --> BoundStmt[BoundStatement]The flow:
- Preprocessing.
statement_preprocessor.cppapplies syntactic rewrites that are easier to do before binding (e.g., expandingSELECT *, normalizingIN (...)). - Statement dispatch.
BindercallsBindSelectNode,BindInsert,BindCreate, etc. (src/planner/binder/statement/andsrc/planner/binder/query_node/). - Scope tracking.
BindContextaccumulates visible tables and columns. Subqueries get nested contexts. CTEs are added byBindCTE. - Expression binding.
ExpressionBinder(and contextual subclasses inexpression_binder/) resolves eachParsedExpressioninto a boundExpression. It handles function overload resolution viaFunctionBinder, applies implicit casts, and propagates types. - Plan construction. As statements bind, they emit
LogicalOperatornodes. For example, aSELECT a FROM t WHERE b > 0becomes:
LogicalProjection(a)
└── LogicalFilter(b > 0)
└── LogicalGet(t)- Output.
BoundStatementcarries the rootLogicalOperatorand the schema of the result (column names +LogicalTypes).
ColumnBinding
The single most important invariant of the planner is that every reference to a column uses a ColumnBinding(table_index, column_index). Table indices are assigned incrementally during binding; downstream stages (optimizer, physical-plan generator, executor) propagate these bindings unchanged. The ColumnBindingResolver (src/execution/column_binding_resolver.cpp) eventually translates them to physical column indices.
Subqueries
Correlated subqueries are handled in src/planner/subquery/ via decorrelation rules. Uncorrelated subqueries are wrapped in BoundSubqueryExpression and become a separate logical plan that the optimizer may reuse. The Deliminator optimizer (src/optimizer/deliminator.cpp) eliminates remaining duplicate-eliminating cross products.
Specialized expression binders
src/planner/expression_binder/ has many subclasses that constrain what is allowed in each context:
| Binder | When it is used |
|---|---|
WhereBinder |
WHERE clauses (no aggregates, no window functions). |
HavingBinder |
HAVING clauses (aggregates required, no windows). |
SelectBinder |
SELECT list (aggregates, windows, expressions). |
OrderBinder |
ORDER BY items (can refer to output column aliases). |
GroupBinder |
GROUP BY items (must be column refs or expressions in the SELECT). |
IndexBinder |
CREATE INDEX expressions (no subqueries, no functions with side effects). |
LateralBinder |
LATERAL joins (allows correlated references to the left side). |
InsertBinder, UpdateBinder, ConstraintBinder |
Their respective DML and DDL contexts. |
Each one rejects expression shapes that are illegal in its context with a clear BinderException.
Integration points
- Input:
SQLStatementfrom parser. - Output:
BoundStatementconsumed bysrc/planner/planner.cppcallers (which is the optimizer pipeline insrc/main/client_context.cpp). - Catalog: Catalog lookups go through
CatalogEntryRetriever(src/catalog/catalog_entry_retriever.cpp), which handles search-path resolution and dependency tracking. - Functions: Overload resolution goes through
FunctionBinder(src/function/function_binder.cpp).
Entry points for modification
- Supporting a new statement: add a
SQLStatementsubclass (parser), then a binder insrc/planner/binder/statement/, then optionally a logical operator insrc/planner/operator/. - Adding type promotion rules: edit
src/function/cast_rules.cppand the cast functions insrc/function/cast/. - Tightening or relaxing what is allowed in an expression context: subclass or modify the relevant
*Binderinsrc/planner/expression_binder/. - Improving error messages: many bind errors flow through
BinderException. Provide source positions where possible.
Key source files
| File | Purpose |
|---|---|
src/planner/planner.cpp |
Planner::CreatePlan. |
src/planner/binder.cpp |
Binder dispatch and BindContext interaction. |
src/planner/expression_binder.cpp |
Generic expression binding. |
src/planner/expression_binder/*.cpp |
Per-context binders. |
src/planner/operator/*.cpp |
Concrete LogicalOperator types. |
src/planner/expression/*.cpp |
Concrete bound expression types. |
src/planner/subquery/*.cpp |
Subquery flattening / decorrelation. |
src/planner/statement_preprocessor.cpp |
Pre-bind rewrites. |
src/planner/table_filter_set.cpp |
Pushdown filter representation. |
Continue to optimizer for what happens to the logical plan next.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.