This file provides guidance to coding agents when working with code in this repository.
DuckDB is a high-performance analytical database system designed to be fast, reliable, portable, and easy to use. It is an analytical database management system with a rich SQL dialect, vectorized execution engine, and columnar storage format.
make debug # Builds debug version with sanitizers and assertions
make reldebug # Builds optimized release version with debug symbols
FORCE_DEBUG=1 make relassert # Builds optimized version with sanitizers and assertionsbuild/reldebug/test/unittest # Fast unit tests# Run specific test file
build/reldebug/test/unittest test/sql/order/test_limit.test
# Run all tests including slow tests
build/reldebug/test/unittest "*"It is recommended to use make reldebug and build/reldebug/test/unittest unless a good reason exists to use the debug build - the debug build is much slower than the reldebug build.
Use the max_execution_time setting (milliseconds, 0 = no limit) to abort a query that runs too long:
SET max_execution_time=5000;
SELECT count(*) FROM range(100000000) t1, range(1000) t2;
-- INTERRUPT Error: Query exceeded maximum execution timeThe setting is LOCAL by default; use SET GLOBAL max_execution_time=... to apply it to all connections. From the shell:
build/reldebug/duckdb -c "SET max_execution_time=5000; SELECT ..."The deadline is enforced at ClientContext::InterruptCheck() call sites, so it only fires once execution reaches one of them. Queries that check in frequently abort close to the limit, but time spent in planning or inside a long non-interruptible stretch is not bounded by it.
Tests use the sqllogictest format (.test files). Example structure:
# name: test/sql/order/test_limit.test
# description: Test LIMIT keyword
# group: [order]
statement ok
CREATE TABLE test (a INTEGER, b INTEGER);
query I
SELECT a FROM test LIMIT 1
----
11
statement error
SELECT a FROM test LIMIT a
----
<REGEX>:Binder Error:.*not found.*Test directives:
statement ok- Statement should succeedstatement error- Statement should failquery I- Query returning INTEGER columnquery II- Query returning two columns----- Separates query from expected results<REGEX>:- Expected error message patternrequire-env VAR- Test requires environment variable
Slow tests should use .test_slow extension instead of .test.
Do not add PRAGMA enable_verification to tests - it should no longer be used in new tests.
make format-fix # Format all code (clang-format + black)
make generate-files # Generate files + format all codeEnsure you run formatting before committing.
Below is a set of tests that should be run in order to make sure a changeset passes extensive tests in CI. If the user is asking you to fix CI make sure that the below commands succeed.
make allunit
FORCE_DEBUG=1 FORCE_ASSERT=1 make reldebug && build/reldebug/test/unittest
make test_configs
make test_vectorSQL String
↓
[PARSER] - Uses a PEG parser to parse SQL into AST
↓
SQLStatement tree (ParsedExpression, TableRef objects)
↓
[PLANNER/BINDER] - Binds symbols to catalog, creates logical plan
↓
Logical Plan (LogicalOperator tree with bound Expressions)
↓
[OPTIMIZER] - Applies rule-based and cost-based optimizations
↓
Optimized Logical Plan
↓
[PHYSICAL PLAN GENERATOR] - Converts to physical operators
↓
Physical Plan (PhysicalOperator tree)
↓
[EXECUTOR] - Executes with vectorized, parallel pipelines
↓
Results
Parser (src/parser/)
- Converts SQL strings to Abstract Syntax Tree (AST)
- Uses a PEG-based parser
- The grammar is located in
*.gramfiles and generated usingscripts/build_grammar.sh - Outputs:
SQLStatement,ParsedExpression,TableRefobjects - Key subdirectories:
expression/,statement/,tableref/,peg/
For more details on adding new grammar, see the README located at src/parser/peg/README.md.
Each new grammar rule must have a corresponding transformer rule, located at peg/transformer.
Planner (src/planner/)
- Binds symbols to catalog entries and resolves types
- Creates logical query execution plan
- Key classes:
Binder,LogicalOperator, boundExpressiontypes - Subdirectories:
binder/,expression/,subquery/
Optimizer (src/optimizer/)
- Transforms logical plans without changing semantics
- Applies predicate pushdown, join ordering, expression rewriting, etc.
- Subdirectories:
join_order/,statistics/,rule/,pushdown/
Execution Engine (src/execution/)
- Converts logical plan to physical plan and executes
- Push-based vectorized execution model
- Processes data in batches (typically 2048 rows)
- Key subdirectories:
operator/(scan, join, filter, aggregate, etc.),expression_executor/
Storage (src/storage/)
- Manages persistent data storage and buffer management
- Block-based storage with compression
- Includes WAL (Write-Ahead Log) for durability
- Subdirectories:
buffer/,compression/,checkpoint/,table/
Catalog (src/catalog/)
- Metadata management for tables, schemas, functions, types, etc.
- Single source of truth for database metadata
- Key classes:
Catalog,CatalogEntry,SchemaCatalogEntry
Transaction Manager (src/transaction/)
- ACID transaction management with MVCC
- Coordinates concurrent access to data
- Key files:
transaction_manager.cpp,undo_buffer.cpp,wal_write_state.cpp
Parallel Execution (src/parallel/)
- Multi-threaded execution with task scheduling
- Pipeline-based parallelism
- Key files:
executor.cpp,pipeline_executor.cpp,task_scheduler.cpp
Functions (src/function/)
- Built-in function implementations
- Types:
scalar/,aggregate/,table/,window/,pragma/
/duckdb
├── src/ # Core C++ source code
│ ├── include/duckdb/ # Public headers
│ ├── parser/ # SQL parsing
│ ├── planner/ # Logical planning
│ ├── optimizer/ # Query optimization
│ ├── execution/ # Physical execution
│ ├── storage/ # Data storage
│ ├── catalog/ # Metadata management
│ ├── transaction/ # Transaction management
│ ├── parallel/ # Parallelization
│ ├── function/ # Built-in functions
│ ├── common/ # Shared utilities and types
│ └── main/ # Database/connection management
├── extension/ # In-tree extensions (parquet, json, icu, etc.)
├── test/ # Test framework and test cases
│ ├── sql/ # SQL regression tests (.test files)
│ └── api/ # C/C++ API tests
├── tools/ # Language bindings (pythonpkg, shell, etc.)
├── benchmark/ # Benchmark suites (TPC-H, TPC-DS, etc.)
├── scripts/ # Build and utility scripts
└── third_party/ # Third-party dependencies
DuckDB supports two types of extensions:
In-Tree Extensions (in extension/ directory):
- Extensions are located in-tree
- Full list in
.github/config/in_tree_extensions.cmake - Code can be edited directly and checked into the repository.
Out-of-Tree Extensions:
- Extensions are located in a separate git repository
- Full list in
.github/config/out_of_tree_extensions.cmake - When changes have to be made, they have to be made in patch files stored in
.github/patches
Building with extensions:
# build all extensions
BUILD_ALL_EXT=1 make
# build specific extensions
DUCKDB_EXTENSIONS='json;icu' make- Vectorized Processing: Data processed in columnar batches (not row-by-row), typically 2048 rows per batch
- Vector class: Represents a columnar batch of data
- ColumnBinding: Unique identifier
(table_index, column_index)for columns throughout planning/execution
ParsedExpression- From parser, unboundExpression- Bound with type informationExpressionExecutor- Vectorized execution of expressions
- Prefer
unique_ptr<T>for exclusive ownership - Use
shared_ptr<T>only when necessary optional_ptr<T>for nullable references,reference<T>for non-nullable references- Never use raw pointers
LogicalType- Abstract data type representation- Type promotion rules in
src/function/cast_rules.cpp - Custom types supported via extension system
- Visitor Pattern: For tree traversal (e.g.,
LogicalOperatorVisitor,ExpressionIterator) - Factory Pattern:
Deserialize()methods for object creation - Class Hierarchy: Base classes like
*Operator,*Entry,*Expressionwith typed subclasses
- Use tabs for indentation, spaces for alignment
- Lines should not exceed 120 columns (run formatter)
- Use
[u]int(8|16|32|64)_tinstead ofint,long, etc. - Use
idx_tinstead ofsize_tfor offsets/indices/counts - Use
constreferences for non-trivial objects - Use C++11 range-based for loops when possible
- Always use braces for if statements and loops
- Never use
const_cast
Try to keep comments short. In general, comments should be one short line. Only in exceptional situations should comments be more than one short line. Code should be mostly self-descriptive and too many large comments make code harder to read and understand.
Avoid adding comments specific to how a change was made to the code that relates to a specific issue. For example, a comment like "add +1 to fix an off-by-one error" is not relevant to understanding the code. Such comments related to specific issues that were addressed belong in a PR description or commit message, not in the code itself.
- Files:
snake_case(e.g.,abstract_operator.cpp) - Types:
PascalCase(e.g.,LogicalOperator) - Variables:
snake_case(e.g.,chunk_size) - Functions:
PascalCase(e.g.,GetChunk)
class MyClass {
public:
MyClass();
int my_public_variable;
public:
void MyFunction();
private:
void MyPrivateFunction();
private:
int my_private_variable;
};- Use exceptions for query-terminating errors (parser error, table not found, out-of-memory, etc.)
- Use return values for errors that are recoverable during a query
- Use
D_ASSERTfor programmer errors (never triggered by user input) - Assert liberally with clear comments
- Prefer sqllogictest framework (
.testfiles) over C++ tests - Test with different types (numerics, strings, nested types)
- Test unexpected/incorrect usage, not just happy path
- Slow tests should use
.test_slowextension - All tests must pass before submitting PR (
make allunit) - Aim for high code coverage
- Entry point:
src/main/database.cpp(DatabaseInstance) - Query execution coordinator:
src/main/client_context.cpp - SQL parsing:
src/parser/parser.cpp - Logical planning:
src/planner/binder/query_planner.cpp - Optimization orchestration:
src/optimizer/optimizer.cpp - Physical plan generation:
src/execution/physical_plan/physical_plan_generator.cpp - Execution orchestration:
src/parallel/executor.cpp
- Use
greporripgrepfor code search - Function definitions typically in
.cppfiles - Class declarations in
src/include/duckdb/headers - Test cases in
test/sql/by functionality
- Find test cases in
test/sql/to see usage examples - Trace from parser → planner → optimizer → execution
- Look for corresponding
*Statement,*Operator,*Expressionclasses - Check function registration in catalog
Some files are auto-generated. After modifying their sources, run:
make generate-filesThis regenerates:
- C API bindings
- Function registration
- Settings
- Serialization code
- Storage info
- Metric enums
- Enum utilities
- Main docs: https://duckdb.org/docs/
- Development docs: https://duckdb.org/dev/
- Build guide: https://duckdb.org/docs/dev/building/overview
- Testing docs: https://duckdb.org/dev/testing
Makefile- Main build configurationCMakeLists.txt- CMake configurationCONTRIBUTING.md- Contribution guidelinestest/README.md- Testing documentationextension/extension_config.cmake- Extension configurationscripts/format.py- Code formatterscripts/generate_*.py- Code generation scripts