A PostgreSQL 18 / 19 extension that plugs the ORCA query optimizer (originally from Greenplum / Apache Cloudberry) into a standard single-node PostgreSQL instance.
ORCA is a Cascades-style cost-based optimizer: instead of a fixed bottom-up dynamic-programming pass, it stores all equivalent plan alternatives in a memo and explores them by firing transformation rules (xforms), so logical rewrites and physical implementation choices are made together and priced by the same cost model. It operates on an intermediate representation called DXL (Data eXchange Language). It was designed for massively-parallel processing (MPP) databases but contains a powerful optimization engine that is useful in single-node mode as well.
That architecture lets ORCA address several known limits of the built-in PostgreSQL planner:
- Correlated subqueries. PG pulls up only a limited set of sublink shapes; the
rest stay as SubPlans re-executed per outer row. ORCA models them as
Applyand decorrelates them into regular joins through xforms, so the subquery side participates in join ordering and costing like any other relation. - Join ordering beyond the collapse limits. PG stops exhaustive search at
join_collapse_limit/from_collapse_limit(8 by default) and falls back to the syntactic order or GEQO. ORCA enumerates join orders as part of the search itself (optimizer_join_order:exhaustive2by default, orexhaustive/greedy/query), withpg_orca.join_order_dynamic_thresholddownshifting to a cheaper strategy only when the query is genuinely large. - Rewrites the planner does not attempt. Eager aggregation (pushing a
GROUP BYbelow a join), CTE inlining as a cost-based decision rather than a syntactic rule, aggregate splitting, and inferred/transitive predicates pushed across join boundaries. - Unified search space. Because rewriting and implementation live in the same memo, a rewrite that only pays off with a particular physical shape (index scan, merge join, partition pruning) can be chosen on cost instead of being applied — or skipped — ahead of the plan search.
The trade-off is planning time: exploring the full space costs more than PG's planner, which is why ORCA is opt-in per session and any query it cannot handle falls back to the standard planner automatically.
This project extracts ORCA's four core libraries and the PostgreSQL integration layer from Apache Cloudberry, adapts them for PG18 and PG19, and packages the result as a CREATE EXTENSION-installable plugin.
The ORCA optimizer is the work of many people from the Greenplum, Pivotal/VMware, and Apache Cloudberry projects. See CONTRIBUTORS.md for acknowledgments.
| Directory | Description |
|---|---|
libgpos/ |
ORCA memory pool, error handling, concurrency primitives |
libnaucrates/ |
DXL parser/serializer, metadata abstractions |
libgpopt/ |
Core optimizer: search, transformation rules, cost model |
libgpdbcost/ |
GPDB-specific cost model implementation |
gpopt/ |
PostgreSQL ↔ DXL translation layer (relcache, planner bridge) |
compat/ |
Stub headers replacing MPP-only Cloudberry types |
pg_orca.cpp |
Extension entry point, planner_hook, GUC definitions |
- PostgreSQL 18 (primary target), or PostgreSQL 19 beta
- xerces-c (XML parsing for DXL)
- CMake ≥ 3.20
- C++17 compiler (clang or gcc)
The same source tree builds against both majors — version differences are handled
with #if PG_VERSION_NUM >= 190000 guards, there is no separate branch. CI builds
PG18 on Ubuntu (gcc/clang, Debug/Release), Ubuntu arm64, CentOS Stream 9, Rocky 9
and macOS, and builds + runs the ORCA regression tests against PG19 on Rocky 9.
See PostgreSQL 19 for what is PG19-specific.
On macOS with Homebrew:
brew install xerces-c cmakeCMake needs pg_config from the PostgreSQL installation you are building
against to find headers, libraries, and install paths. Either add the PG bin/
directory to PATH:
export PATH="/path/to/pg/bin:$PATH"
# verify
pg_config --version # should print "PostgreSQL 18.x" or "PostgreSQL 19..."Or pass it explicitly on the CMake command line with -DPG_CONFIG=... (see below).
Two generators are supported. Pick whichever suits your environment.
mkdir build && cd build
# pg_config is on PATH
cmake .. -DCMAKE_BUILD_TYPE=Debug
# pg_config NOT on PATH — pass it explicitly
cmake .. -DPG_CONFIG=/path/to/pg/bin/pg_config -DCMAKE_BUILD_TYPE=Debugcmake --build . --target install -j$(nproc)To rebuild after source changes:
cmake --build build -j$(nproc)mkdir build && cd build
cmake .. -DPG_CONFIG=/path/to/pg/bin/pg_config -DCMAKE_BUILD_TYPE=Debug -GNinjaninja -j$(nproc)
ninja installTo rebuild after source changes:
ninja -j$(nproc) -C buildReplace Debug with Release in either generator. Example with Ninja:
mkdir build-release && cd build-release
cmake .. -DPG_CONFIG=/path/to/pg/bin/pg_config -DCMAKE_BUILD_TYPE=Release -GNinja
ninja -j$(nproc)
ninja installOr with plain CMake:
mkdir build-release && cd build-release
cmake .. -DPG_CONFIG=/path/to/pg/bin/pg_config -DCMAKE_BUILD_TYPE=Release
cmake --build . --target install -j$(nproc)Release mode enables -O3 -DNDEBUG and disables ORCA internal assertions (GPOS_DEBUG).
-- 1. Install the extension in the target database. This LOADs the
-- shared library into the current session so pg_orca.* GUCs and
-- the planner_hook are live immediately:
CREATE EXTENSION pg_orca;
-- 2. (Recommended) Have every new connection to this database
-- auto-load pg_orca. Per-database scope; no restart, no cluster
-- GUC change, takes effect for subsequent connections:
ALTER DATABASE mydb SET session_preload_libraries = 'pg_orca';
-- 3. Enable ORCA per session (or persistently via
-- ALTER DATABASE mydb SET pg_orca.enable_orca = on):
SET pg_orca.enable_orca = on;
-- 4. Run a query — ORCA optimizes it.
EXPLAIN SELECT * FROM t WHERE id > 100;Existing sessions are unaffected by step 2 until they reconnect. If ORCA cannot handle a query (unsupported feature or internal error) it falls back to the standard PostgreSQL planner automatically.
Alternative scopes for the preload setting:
-- Cluster-wide (every database, every role):
ALTER SYSTEM SET session_preload_libraries = 'pg_orca';
SELECT pg_reload_conf();
-- Single role only:
ALTER ROLE bench SET session_preload_libraries = 'pg_orca';ALTER DATABASE ... SET and ALTER SYSTEM SET overwrite the value;
if a sibling library was already present (pg_stat_statements, etc.),
include it explicitly:
ALTER DATABASE mydb SET session_preload_libraries = 'pg_orca,pg_stat_statements';Roll back:
ALTER DATABASE mydb RESET session_preload_libraries;
DROP EXTENSION pg_orca;| Parameter | Default | Description |
|---|---|---|
pg_orca.enable_orca |
off |
Enable ORCA optimizer |
pg_orca.trace_fallback |
off |
Log a message on fallback to standard planner |
optimizer_segments |
1 |
Number of segments for cost estimation |
optimizer_sort_factor |
1.0 |
Cost scaling factor for sort operations |
optimizer_metadata_caching |
on |
Cache relation metadata between calls |
optimizer_mdcache_size |
16384 |
Metadata cache size (KB) |
optimizer_search_strategy_path |
"" |
Path to custom search strategy XML (empty = built-in) |
See testing.md for the full guide. Quick start:
export PG_CONFIG=/Users/jianghua/pg-install/bin/pg_config
export PG_REGRESS_SQL=/Users/jianghua/code/postgresql/src/test/regress
# pg_orca's own regression tests
test/test.sh --orca-tests
# PostgreSQL standard suite with ORCA loaded (--ignore-plans suppresses plan-shape diffs)
test/test.sh --pg-tests --ignore-plansCloudberry's translation layer references many MPP-only types (Motion, PlanSlice, DynamicSeqScan, etc.). These are stubbed in compat/cdb/cdb_plan_nodes.h so the code compiles. ORCA will never generate these nodes in single-node mode.
Many ORCA configuration knobs were GPDB-specific GUCs. They are re-defined as real GUCs in pg_orca.cpp under the optimizer.* prefix, so existing ORCA code referencing them continues to work.
PG19 is still a moving target, so treat it as preview support: it builds and passes
test/test.sh --orca-tests, but the PG18 path is the one that gets the benchmark and
regression mileage. Everything version-dependent is behind #if PG_VERSION_NUM >= 190000:
planner_hooksignature — PG19 passes an extraExplainState *argument; the hook and bothstandard_planner()fallback paths are conditionally compiled (pg_orca.cpp).RESPECT/IGNORE NULLSwindow functions (SQL:2003, new in PG19) — supported natively rather than falling back: the null treatment is carried onCScalarWindowFuncand round-tripped through DXL.- SQL/PGQ
GRAPH_TABLE— the PG rewriter expands it into a LATERAL-joined subquery beforeplanner_hookruns, so these queries go through ORCA on the normal LATERAL path. A defensive check falls back cleanly if anRTE_GRAPH_TABLEever reaches the translator. Int8GetDatum()— removed frompostgres.hin PG19;gpdbwrappers.cppsupplies a byte-identical#ifndef-guarded replacement so call sites are unchanged on both versions.