Skip to content

Support building against PostgreSQL 19#2457

Open
jrgemignani wants to merge 1 commit into
apache:PG19from
jrgemignani:support_postgres_19
Open

Support building against PostgreSQL 19#2457
jrgemignani wants to merge 1 commit into
apache:PG19from
jrgemignani:support_postgres_19

Conversation

@jrgemignani

@jrgemignani jrgemignani commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Note: A big thank you to Alexander Kukushkin cyberdemn@gmail.com
who's work helped to make this possible.

Note: This is a beta branch for PostgreSQL 19. This PR is part of the work to get the PG19 branch up and running. Other items will follow.

Adapt AGE to PostgreSQL 19's C API changes so the extension compiles cleanly (verified with COPT=-Werror) and passes the full regression suite against 19beta1. Each prototype change was matched to how AGE actually uses the function rather than blindly appending arguments.

Access-method scans: table_beginscan() and index_beginscan() gained a trailing uint32 flags. Pass SO_NONE (no special scan options); table_beginscan() still ORs in SO_TYPE_SEQSCAN internally, so behavior is unchanged. Add access/genam.h to the files that use index_*/IndexScanDesc (previously reached only transitively through now-leaner PG headers).

Tuple-slot init: MakeTupleTableSlot() and ExecInitScanTupleSlot() gained a uint16 flags. Pass 0. AGE's DML executors are CustomScanState nodes whose input tuples are synthesized agtype rows, so -- like PostgreSQL's own nodeCustom.c and nodeSubqueryscan.c -- 0 is correct. The TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS hint that heap seq/index scans pass would be unsound here since these tuples are not guaranteed to obey a base relation's NOT NULL constraints.

DML tuple ops: heap_delete() added a uint32 options argument and dropped the trailing changingPart bool; table_tuple_update() added a uint32 options argument. Pass 0 in both (AGE used changingPart=false and no special options, so 0 preserves behavior).

ExecInsertIndexTuples() was reworked to
(resultRelInfo, estate, uint32 flags, slot, arbiterIndexes, specConflict) with flags EIIT_IS_UPDATE / EIIT_NO_DUPE_ERROR / EIIT_ONLY_SUMMARIZING. The old booleans map to flags: plain inserts (cypher_utils) use 0; SET uses EIIT_ONLY_SUMMARIZING when update_indexes == TU_Summarizing. Importantly, the bulk loader (age_load) previously passed noDupErr=true so it could detect the conflict list and raise its own error; this maps to EIIT_NO_DUPE_ERROR (not 0), preserving AGE's "Cannot insert duplicate vertex id" message instead of surfacing the raw unique-constraint violation.

CreateSchemaCommand() replaced its const char *queryString parameter with a ParseState *. Build one with make_parsestate(), set p_sourcetext to the generated command string, and free_parsestate() afterward (mirroring standard_ProcessUtility). The argument count is unchanged, so this mismatch only warns under default flags -- another reason the build uses -Werror.

Shared-memory/locks: LWLockNewTrancheId() now takes the tranche name directly and LWLockRegisterTranche() was removed; GetNamedDSMSegment() and its init callback gained a void *arg. Update the graph-version DSM setup accordingly, passing NULL for the unused callback argument.

Miscellaneous: add includes exposed by leaner PG19 headers (catalog/pg_type.h for TEXTOID/CHAROID, <time.h> for CLOCK_REALTIME, utils/tuplesort.h and utils/tuplestore.h, access/htup_details.h for GETSTRUCT/heap_form_tuple); PointerIsValid() was removed (use != NULL); VARDATA()/VARSIZE() now require a pointer, so wrap the Datum in DatumGetPointer(); replace the fall-through comment with the pg_fallthrough macro; and const-qualify JumbleState * in the post_parse_analyze hook signature.

Regression output: the only differences under PG19 are cosmetic and are reflected in the expected files. PostgreSQL 19 reworded undefined function/operator errors -- the "HINT: No function/operator matches the given name and argument types" line became "DETAIL: No function/operator of that name accepts...", and for a wholly unknown operator "DETAIL: There is no operator of that name."; the ERROR lines themselves are unchanged. This accounts for the expr, cypher_call, jsonb_operators and subgraph updates. Separately, a divergent-path query in cypher_match returned the same four paths in a different, non-deterministic order under PG19; add ORDER BY p to make it deterministic (matching the project's practice of ordering non-deterministic RETURN queries). The returned row multiset is unchanged -- it is purely a reordering, not a data difference.

Co-authored-by: Copilot copilot@github.com
Co-authored-by: Alexander Kukushkin cyberdemn@gmail.com

modified: regress/expected/cypher_call.out
modified: regress/expected/cypher_match.out
modified: regress/expected/expr.out
modified: regress/expected/jsonb_operators.out
modified: regress/expected/subgraph.out
modified: regress/sql/cypher_match.sql
modified: src/backend/catalog/ag_label.c
modified: src/backend/commands/graph_commands.c
modified: src/backend/executor/cypher_create.c
modified: src/backend/executor/cypher_delete.c
modified: src/backend/executor/cypher_merge.c
modified: src/backend/executor/cypher_set.c
modified: src/backend/executor/cypher_utils.c
modified: src/backend/parser/cypher_analyze.c
modified: src/backend/parser/cypher_expr.c
modified: src/backend/parser/cypher_keywords.c
modified: src/backend/utils/adt/age_global_graph.c
modified: src/backend/utils/adt/age_vle.c
modified: src/backend/utils/adt/agtype.c
modified: src/backend/utils/adt/agtype_ops.c
modified: src/backend/utils/adt/agtype_util.c
modified: src/backend/utils/load/age_load.c

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Apache AGE’s backend code to compile cleanly against PostgreSQL 19’s C API changes and aligns regression tests with PostgreSQL 19’s adjusted error text, while preserving runtime behavior (notably around scan options, tuple-slot flags, and index-insert conflict handling).

Changes:

  • Adjusts numerous PostgreSQL API callsites (scan APIs, tuple slot initialization, DML tuple ops, index tuple insertion, DSM callbacks/LWLock tranche handling, ParseState usage).
  • Adds headers that are no longer pulled in transitively by PostgreSQL 19’s leaner includes.
  • Updates regression SQL/expected outputs for PostgreSQL 19 wording changes and makes one previously non-deterministic path query deterministic via ORDER BY.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.github/workflows/installcheck.yaml Increases checkout fetch depth for CI installcheck workflow.
regress/expected/cypher_call.out Updates expected error output text to match PostgreSQL 19 diagnostics.
regress/expected/cypher_match.out Updates expected output and reflects deterministic ordering change.
regress/expected/expr.out Updates expected error output text to match PostgreSQL 19 diagnostics.
regress/expected/jsonb_operators.out Updates expected error output text to match PostgreSQL 19 diagnostics.
regress/expected/subgraph.out Updates expected error output text to match PostgreSQL 19 diagnostics.
regress/sql/cypher_match.sql Adds ORDER BY p to stabilize previously non-deterministic path ordering.
src/backend/catalog/ag_label.c Updates index/table scan calls for PostgreSQL 19 scan flags.
src/backend/commands/graph_commands.c Adapts schema creation to new ParseState-based CreateSchemaCommand; updates tuple slot creation flags.
src/backend/executor/cypher_create.c Updates tuple slot initialization to include new flags argument.
src/backend/executor/cypher_delete.c Updates tuple slot init, heap_delete signature, and scan calls for PostgreSQL 19 API changes.
src/backend/executor/cypher_merge.c Updates tuple slot initialization to include new flags argument.
src/backend/executor/cypher_set.c Updates tuple slot init, table_tuple_update signature, ExecInsertIndexTuples flags mapping, and scan calls.
src/backend/executor/cypher_utils.c Updates scan calls and ExecInsertIndexTuples call signature.
src/backend/parser/cypher_analyze.c Updates post-parse-analyze hook signature to const-qualify JumbleState.
src/backend/parser/cypher_expr.c Adds header needed for htup macros with PostgreSQL 19 headers.
src/backend/parser/cypher_keywords.c Adds pg_type header for OID macros now needed explicitly.
src/backend/utils/adt/age_global_graph.c Updates scan calls, DSM init callback signature, LWLock tranche API usage, and DSM segment API call signature.
src/backend/utils/adt/age_vle.c Adds header needed for htup macros with PostgreSQL 19 headers.
src/backend/utils/adt/agtype.c Adds missing headers; updates scan calls; replaces removed PointerIsValid; adjusts varlena access macros to pass pointers.
src/backend/utils/adt/agtype_ops.c Adjusts varlena access macros to pass pointers.
src/backend/utils/adt/agtype_util.c Replaces fallthrough comment with pg_fallthrough.
src/backend/utils/load/age_load.c Updates ExecInsertIndexTuples call to new signature and preserves “no duplicate” behavior via flags.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1758 to 1762
static void age_dsm_init_callback(void *ptr, void *arg)
{
GraphVersionState *state = (GraphVersionState *) ptr;

LWLockInitialize(&state->lock,
@gregfelice

Copy link
Copy Markdown
Contributor

Nice — diff is clean and CI's green on the PG19 branch. Two things before this lands:

  1. Unused arg in the DSM init callback (age_global_graph.c:1762, per Copilot). CI passes today, but now that CI: fail build on compiler and bison warnings #2398 turns compiler warnings into build failures, a -Wunused-parameter / COPT=-Werror build could trip on this. Worth marking it unused (e.g. (void) arg; or PG_USED_FOR_ASSERTS_ONLY) to keep the PG19 branch warning-clean under the stricter flags.

  2. Attribution to Support PostgreSQL 19 #2427. This is essentially the rebased/cleaned form of @CyberDem0n's PG19 port (Support PostgreSQL 19 #2427) — same callsite adaptations and regress updates. Since that's a month of external-contributor work, could we carry a Co-authored-by: trailer for @CyberDem0n (and/or land Support PostgreSQL 19 #2427 first / note it here)? Just so the origin isn't lost when this supersedes it.

Otherwise the PG19 API adaptations look right to me.

Note: A big thank you to Alexander Kukushkin <cyberdemn@gmail.com>
      who's work helped to make this possible.

Adapt AGE to PostgreSQL 19's C API changes so the extension compiles
cleanly (verified with COPT=-Werror) and passes the full regression
suite against 19beta1. Each prototype change was matched to how AGE
actually uses the function rather than blindly appending arguments.

Access-method scans: table_beginscan() and index_beginscan() gained a
trailing uint32 flags. Pass SO_NONE (no special scan options);
table_beginscan() still ORs in SO_TYPE_SEQSCAN internally, so behavior is
unchanged. Add access/genam.h to the files that use index_*/IndexScanDesc
(previously reached only transitively through now-leaner PG headers).

Tuple-slot init: MakeTupleTableSlot() and ExecInitScanTupleSlot() gained a
uint16 flags. Pass 0. AGE's DML executors are CustomScanState nodes whose
input tuples are synthesized agtype rows, so -- like PostgreSQL's own
nodeCustom.c and nodeSubqueryscan.c -- 0 is correct. The
TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS hint that heap seq/index scans pass
would be unsound here since these tuples are not guaranteed to obey a base
relation's NOT NULL constraints.

DML tuple ops: heap_delete() added a uint32 options argument and dropped
the trailing changingPart bool; table_tuple_update() added a uint32 options
argument. Pass 0 in both (AGE used changingPart=false and no special
options, so 0 preserves behavior).

ExecInsertIndexTuples() was reworked to
(resultRelInfo, estate, uint32 flags, slot, arbiterIndexes, specConflict)
with flags EIIT_IS_UPDATE / EIIT_NO_DUPE_ERROR / EIIT_ONLY_SUMMARIZING. The
old booleans map to flags: plain inserts (cypher_utils) use 0; SET uses
EIIT_ONLY_SUMMARIZING when update_indexes == TU_Summarizing. Importantly,
the bulk loader (age_load) previously passed noDupErr=true so it could
detect the conflict list and raise its own error; this maps to
EIIT_NO_DUPE_ERROR (not 0), preserving AGE's "Cannot insert duplicate
vertex id" message instead of surfacing the raw unique-constraint violation.

CreateSchemaCommand() replaced its const char *queryString parameter with a
ParseState *. Build one with make_parsestate(), set p_sourcetext to the
generated command string, and free_parsestate() afterward (mirroring
standard_ProcessUtility). The argument count is unchanged, so this mismatch
only warns under default flags -- another reason the build uses -Werror.

Shared-memory/locks: LWLockNewTrancheId() now takes the tranche name
directly and LWLockRegisterTranche() was removed; GetNamedDSMSegment() and
its init callback gained a void *arg. Update the graph-version DSM setup
accordingly, passing NULL for the unused callback argument.

Miscellaneous: add includes exposed by leaner PG19 headers (catalog/pg_type.h
for TEXTOID/CHAROID, <time.h> for CLOCK_REALTIME, utils/tuplesort.h and
utils/tuplestore.h, access/htup_details.h for GETSTRUCT/heap_form_tuple);
PointerIsValid() was removed (use != NULL); VARDATA()/VARSIZE() now require a
pointer, so wrap the Datum in DatumGetPointer(); replace the fall-through
comment with the pg_fallthrough macro; and const-qualify JumbleState * in the
post_parse_analyze hook signature.

Regression output: the only differences under PG19 are cosmetic and are
reflected in the expected files. PostgreSQL 19 reworded undefined
function/operator errors -- the "HINT: No function/operator matches the given
name and argument types" line became "DETAIL: No function/operator of that
name accepts...", and for a wholly unknown operator "DETAIL: There is no
operator of that name."; the ERROR lines themselves are unchanged. This
accounts for the expr, cypher_call, jsonb_operators and subgraph updates.
Separately, a divergent-path query in cypher_match returned the same four
paths in a different, non-deterministic order under PG19; add ORDER BY p to
make it deterministic (matching the project's practice of ordering
non-deterministic RETURN queries). The returned row multiset is unchanged --
it is purely a reordering, not a data difference.

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Alexander Kukushkin <cyberdemn@gmail.com>

modified:   regress/expected/cypher_call.out
modified:   regress/expected/cypher_match.out
modified:   regress/expected/expr.out
modified:   regress/expected/jsonb_operators.out
modified:   regress/expected/subgraph.out
modified:   regress/sql/cypher_match.sql
modified:   src/backend/catalog/ag_label.c
modified:   src/backend/commands/graph_commands.c
modified:   src/backend/executor/cypher_create.c
modified:   src/backend/executor/cypher_delete.c
modified:   src/backend/executor/cypher_merge.c
modified:   src/backend/executor/cypher_set.c
modified:   src/backend/executor/cypher_utils.c
modified:   src/backend/parser/cypher_analyze.c
modified:   src/backend/parser/cypher_expr.c
modified:   src/backend/parser/cypher_keywords.c
modified:   src/backend/utils/adt/age_global_graph.c
modified:   src/backend/utils/adt/age_vle.c
modified:   src/backend/utils/adt/agtype.c
modified:   src/backend/utils/adt/agtype_ops.c
modified:   src/backend/utils/adt/agtype_util.c
modified:   src/backend/utils/load/age_load.c
modified:   .github/workflows/installcheck.yaml

Resolved conflicts:  .github/workflows/installcheck.yaml
@jrgemignani jrgemignani force-pushed the support_postgres_19 branch from feb7e47 to 9f42230 Compare July 3, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants