Support PostgreSQL 18’s new RTE kinds in Citus deparser to fix OID 0 and “*GROUP*” errors
Fixes #8020
PostgreSQL 18 introduces two new, pseudo rangetable‐entry kinds that Citus’ downstream deparser must recognize:
- Pulled-up shard RTE clones (
CITUS_RTE_SHARDwithrelid == InvalidOid) - Grouping-step RTE (
RTE_GROUP, alias*GROUP*, not actually in the FROM clause)
Without special handling, Citus crashes or emits invalid SQL when running against PG 18beta1:
ERROR: could not open relation with OID 0Citus was unconditionally callingrelation_open(rte->relid,…)on entries whoserelidis 0.ERROR: missing FROM-clause entry for table "*GROUP*"Citus’set_rtable_names()assigned the synthetic*GROUP*alias but never printed a matching FROM item.
This PR teaches Citus’ ruleutils_18.c to skip catalog lookups for RTEs without valid OIDs and to suppress the grouping-RTE alias, restoring compatibility with both PG 17 and PG 18.
Background
- Upstream commit [247dea8](https://github.com/postgres/postgres/commit/247dea89f7616fdf06b7272b74abafc29e8e5860)
Introduced
RTE_GROUPfor the grouping step so that multiple subqueries inGROUP BY/HAVINGcan be deduplicated and planned correctly. - Citus PR [#6428](https://github.com/citusdata/citus/pull/6428)
Added initial support for treating shard RTEs like real relations—calling
relation_open()to pick up renamed-column fixes. Worked fine on PG 11–17, but PG 18’s pull-up logic clones those shard RTEs withrelid=0, leading to OID 0 crashes.
Changes
-
Guard
relation_open()Inset_relation_column_names(), only callrelation_open(rte->relid, …)whenOidIsValid(rte->relid)Prevents the “could not open relation with OID 0” crash on both pulled-up shards and synthetic RTEs.
-
Handle pulled-up shards (
CITUS_RTE_SHARDwithrelid=0) Copy column names directly fromrte->eref->colnamesinstead of hitting the catalog. -
Handle grouping RTE (
RTE_GROUP)-
In
set_relation_column_names(): fallback torte->eref->colnamesforRTE_GROUP. -
In
set_rtable_names(): explicitly assignrefname = NULL; /* never show *GROUP* in FROM */so that no
*GROUP*alias is ever printed.
Why this is required: PostgreSQL 18’s parser now represents the grouping step with a synthetic RTE whose alias is always
*GROUP*—and that RTE is never actually listed in theFROMclause. If Citus’ deparser assigns and emits*GROUP*as a table reference, the pushed-down SQL becomes:SELECT *GROUP*.mygroupcol … -- but there is no “*GROUP*” in the FROM listWorkers then fail:
ERROR: missing FROM-clause entry for table "*GROUP*"By setting
refname = NULLforRTE_GROUPinset_rtable_names(), the deparser prints just the column name unqualified, exactly matching upstream PG 18’s behavior and yielding valid SQL on the workers. -
-
Maintain existing behavior on PG 15–17
- Shard RTEs with valid
relidstill open the catalog to pick up renamed-column fixes. - No impact on other RTE kinds or versions prior to PG 18.
- Shard RTEs with valid
Codecov Report
All modified and coverable lines are covered by tests :white_check_mark:
Project coverage is 89.19%. Comparing base (
5005be3) to head (6c23b2f). Report is 1 commits behind head on main.
Additional details and impacted files
@@ Coverage Diff @@
## main #8023 +/- ##
==========================================
+ Coverage 88.69% 89.19% +0.50%
==========================================
Files 284 284
Lines 61521 61522 +1
Branches 7697 7696 -1
==========================================
+ Hits 54565 54877 +312
+ Misses 4669 4438 -231
+ Partials 2287 2207 -80
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
https://github.com/citusdata/citus/actions/runs/16324450268
Ran with the pg18 configuration, and the intended error is still successfully fixed.