From 3ba63c5e5d0b82b7cd36dd86c3ca0651afa8a1ef Mon Sep 17 00:00:00 2001 From: Devrim Gunduz Date: Sat, 11 Jul 2026 14:07:43 +0300 Subject: [PATCH] Fix postgres-decoderbufs build against PostgreSQL 19. PG19 introduced several source-incompatible changes that this patch addresses in src/decoderbufs.c: 1. `AssertVariableIsOfType` was renamed to `StaticAssertVariableIsOfType` (upstream commit "Rename AssertVariableIsOfType to StaticAssertVariableIsOfType"). The old name no longer exists, so _PG_output_plugin_init() originally failed with an implicit-declaration error. However, simply switching to StaticAssertVariableIsOfType() trades that error for a new one: PG19's c.h defines that macro in terms of the bare `typeof` keyword, which only exists under a GNU C dialect or C23 -- it is not available under strict `-std=c11`/`-std=c17`, which is the dialect PGXS's own CFLAGS select on this build. So the upstream macro itself fails to compile here ("unknown type name 'typeof'"), independent of anything in decoderbufs. Fixed by performing the same compile-time type check ourselves on PG19+, using the double-underscored `__typeof__` GNU extension (which, unlike bare `typeof`, remains available in strict-ANSI modes on both gcc and clang) together with c.h's own `StaticAssertDecl` and `__builtin_types_compatible_p`. This check is purely a compile-time sanity check with no effect on behavior, so it is simply skipped (via an inner #if) on any toolchain lacking the builtin, rather than trying to fail the build. 2. `VARATT_IS_EXTERNAL_ONDISK()` now takes a `const void *` instead of silently accepting a `Datum`. tuple_to_tuple_msg() was passing the raw Datum (`origval`), which is an integer type on this platform, causing "makes pointer from integer without a cast". Fixed by wrapping the argument with DatumGetPointer(), which is correct and a no-op change on all supported PG versions. 3. `ReorderBufferTXN` no longer has an `xact_time` member. Upstream commit "Make some use of anonymous unions [reorderbuffer xact_time]" turned the `xact_time` union into an anonymous union embedded directly in ReorderBufferTXN, so `commit_time` (as well as `prepare_time` / `abort_time`) is once again accessed directly as txn->commit_time, the same way it was before PG15 introduced the named `xact_time` union. This affected pg_decode_begin_txn(), pg_decode_commit_txn(), and pg_decode_change(). Fixed by adding a `PG_VERSION_NUM >= 190000` branch that uses txn->commit_time directly, while keeping the txn->xact_time.commit_time path for PG15-PG18 and the pre-PG15 txn->commit_time path unchanged. No behavioral changes for PG15-PG18 or earlier; this is purely a compatibility fix so the extension builds on PG19 as well. --- src/decoderbufs.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/decoderbufs.c b/src/decoderbufs.c index 944c471..fda0ac0 100644 --- a/src/decoderbufs.c +++ b/src/decoderbufs.c @@ -98,7 +98,26 @@ void _PG_init(void) {} /* specify output plugin callbacks */ void _PG_output_plugin_init(OutputPluginCallbacks *cb) { +#if PG_VERSION_NUM >= 190000 + /* + * PG19 renamed AssertVariableIsOfType() to StaticAssertVariableIsOfType(), + * but that macro's definition in c.h uses the bare `typeof` keyword, which + * is only available under a GNU C dialect or C23 -- it is not available + * under strict `-std=c11`/`-std=c17`, which is what PGXS's CFLAGS use here. + * That makes the upstream macro itself fail to compile in this + * configuration. Perform the same compile-time type check ourselves using + * the double-underscored `__typeof__` GNU extension instead, which (unlike + * bare `typeof`) remains available in strict-ANSI modes on gcc and clang. + * This is a compile-time-only sanity check with no effect on behavior, so + * it's fine to skip it entirely on toolchains lacking the builtin. + */ +#if defined(HAVE__BUILTIN_TYPES_COMPATIBLE_P) || defined(__GNUC__) || defined(__clang__) + StaticAssertDecl(__builtin_types_compatible_p(__typeof__(&_PG_output_plugin_init), LogicalOutputPluginInit), + "_PG_output_plugin_init does not have type LogicalOutputPluginInit"); +#endif +#else AssertVariableIsOfType(&_PG_output_plugin_init, LogicalOutputPluginInit); +#endif cb->startup_cb = pg_decode_startup; cb->begin_cb = pg_decode_begin_txn; cb->change_cb = pg_decode_change; @@ -432,7 +451,7 @@ static void tuple_to_tuple_msg(Decoderbufs__DatumMessage **tmsg, /* query output function */ getTypeOutputInfo(attr->atttypid, &typoutput, &typisvarlena); if (!isnull) { - if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval)) { + if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(origval))) { datum_msg.datum_missing = true; datum_msg.datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_MISSING; elog(DEBUG1, "Not handling external on disk varlena at the moment."); @@ -506,7 +525,10 @@ static void pg_decode_begin_txn(LogicalDecodingContext *ctx, rmsg.has_op = true; rmsg.transaction_id = txn->xid; rmsg.has_transaction_id = true; -#if PG_VERSION_NUM >= 150000 +#if PG_VERSION_NUM >= 190000 + /* PG19 made the commit/prepare/abort_time union anonymous again */ + rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); +#elif PG_VERSION_NUM >= 150000 rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->xact_time.commit_time); #else rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); @@ -548,7 +570,10 @@ static void pg_decode_commit_txn(LogicalDecodingContext *ctx, rmsg.has_op = true; rmsg.transaction_id = txn->xid; rmsg.has_transaction_id = true; -#if PG_VERSION_NUM >= 150000 +#if PG_VERSION_NUM >= 190000 + /* PG19 made the commit/prepare/abort_time union anonymous again */ + rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); +#elif PG_VERSION_NUM >= 150000 rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->xact_time.commit_time); #else rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); @@ -604,7 +629,10 @@ static void pg_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, /* set common fields */ rmsg.transaction_id = txn->xid; rmsg.has_transaction_id = true; -#if PG_VERSION_NUM >= 150000 +#if PG_VERSION_NUM >= 190000 + /* PG19 made the commit/prepare/abort_time union anonymous again */ + rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); +#elif PG_VERSION_NUM >= 150000 rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->xact_time.commit_time); #else rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time);