AggJoin Architecture
June 19, 2026 ยท View on GitHub
This extension is organized around four layers:
- extension registration
- logical/planner analysis and rewrites
- physical sink/source execution
- shared runtime/state utilities
Module map
Extension entry
src/aggjoin_extension.cppsrc/include/aggjoin_extension.hpp
Registers the optimizer extension and test-only hooks.
Optimizer entry
src/aggjoin_optimizer.cpp
Owns optimizer registration, env/test knobs, and the thin entry point into the rewrite pipeline.
Logical planning and rewrites
src/aggjoin_logical.cppsrc/aggjoin_rewrites.cppsrc/aggjoin_rewrite_agg_propagation.cppsrc/aggjoin_rewrite_build.cppsrc/aggjoin_rewrite_mixed.cppsrc/include/aggjoin_rewrites_internal.hppsrc/include/aggjoin_stats.hpp
Responsibilities:
- pattern matching on aggregate-over-join shapes
- cost/gating logic
- aggregate-propagation rewrites for high-blowup acyclic join trees
- native build-preagg rewrites
- native mixed-side rewrites
- planner statistics helpers for key domains, fanout estimates, and no-op filter proof
- construction of the logical extension operator
Physical operator shell
src/aggjoin_physical.cppsrc/include/aggjoin_physical.hpp
Owns the PhysicalAggJoin declaration, constructor, operator-state factory, pipeline wiring, and the physical-plan factory.
Sink side
src/aggjoin_sink.cpp
Responsibilities:
- build-side hash table population
- build-side aggregate accumulation
- fast-path mode selection and initialization
- bloom/range/build-slot/string lookup setup
Source side
src/aggjoin_source.cppsrc/aggjoin_source_segmented.cppsrc/aggjoin_source_direct.cppsrc/aggjoin_source_result_hash.cppsrc/include/aggjoin_source_internal.hpp
Responsibilities:
- source-side dispatch
- segmented direct execution
- flat direct execution
- generic result-hash/native fallback execution
Emit side
src/aggjoin_emit.cppsrc/aggjoin_emit_direct.cppsrc/include/aggjoin_emit_internal.hpp
Responsibilities:
- final result production
- direct/segmented/build-slot emission
- native aggregate hash table scan emission
- flat result-hash emission
Shared runtime and state
src/include/aggjoin_optimizer_shared.hppsrc/include/aggjoin_runtime.hppsrc/include/aggjoin_state.hpp
Responsibilities:
- shared planner/runtime metadata
- build/result hash table implementations
- key payload handling and equality
- compression metadata helpers
- sink/source state structures
Execution model
PhysicalAggJoin is a DuckDB CachingPhysicalOperator with a sink/source lifecycle:
-
Sink:
- consume build rows
- populate
build_ht - optionally precompute build-side aggregates
- choose execution mode
-
ExecuteInternal:
- consume probe chunks
- choose one of the execution families:
- segmented direct
- flat direct
- build-slot hash
- generic result-hash
- native aggregate HT
-
GetData:
- emit grouped or ungrouped results from the selected execution state
Rewrite families
The optimizer currently has three major native-lowering families:
- aggregate propagation over high-blowup acyclic join trees
- build-side native preaggregation
- mixed-side native preaggregation
These are kept separate because they have different planner envelopes and benchmarks.
Fast-path families
The runtime currently has four meaningful fast-path families:
- segmented direct
- flat direct
- build-slot hash
- generic result-hash/native fallback
They are intentionally split by mode because they have different data layout assumptions and different maintenance risk.
Testing layout
The sqllogictest suite lives in test/sql/aggjoin_*.test and is run by:
make testscripts/run_sqllogictests.sh
Themes are split by behavior family instead of keeping one monolithic test file.
Current design intent
- keep rewrite families benchmark-driven and narrow
- keep execution families separated by data-layout assumptions
- prefer explicit module boundaries over generic abstraction in hot loops
- treat broader varlen support and latency/parallelism work as larger projects, not small local tweaks