Open-Source Wikis

/

DuckDB

/

Systems

/

Planner

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 handling

Key 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:

  1. Preprocessing. statement_preprocessor.cpp applies syntactic rewrites that are easier to do before binding (e.g., expanding SELECT *, normalizing IN (...)).
  2. Statement dispatch. Binder calls BindSelectNode, BindInsert, BindCreate, etc. (src/planner/binder/statement/ and src/planner/binder/query_node/).
  3. Scope tracking. BindContext accumulates visible tables and columns. Subqueries get nested contexts. CTEs are added by BindCTE.
  4. Expression binding. ExpressionBinder (and contextual subclasses in expression_binder/) resolves each ParsedExpression into a bound Expression. It handles function overload resolution via FunctionBinder, applies implicit casts, and propagates types.
  5. Plan construction. As statements bind, they emit LogicalOperator nodes. For example, a SELECT a FROM t WHERE b > 0 becomes:
LogicalProjection(a)
  └── LogicalFilter(b > 0)
        └── LogicalGet(t)
  1. Output. BoundStatement carries the root LogicalOperator and 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: SQLStatement from parser.
  • Output: BoundStatement consumed by src/planner/planner.cpp callers (which is the optimizer pipeline in src/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 SQLStatement subclass (parser), then a binder in src/planner/binder/statement/, then optionally a logical operator in src/planner/operator/.
  • Adding type promotion rules: edit src/function/cast_rules.cpp and the cast functions in src/function/cast/.
  • Tightening or relaxing what is allowed in an expression context: subclass or modify the relevant *Binder in src/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.

Planner – DuckDB wiki | Factory