From 41269f27015ebf4dae011ecc11c7bca417bcad2c Mon Sep 17 00:00:00 2001 From: Devrim Gunduz Date: Thu, 25 Jun 2026 15:29:29 +0200 Subject: [PATCH] Fix wal2mongo build failures against PostgreSQL 17, 18, and 19. PG 17 / 18: - change->data.tp.newtuple and oldtuple changed type from ReorderBufferTupleBuf * (requiring ->tuple dereference to obtain HeapTupleData) to bare HeapTuple. Drop the ->tuple member access and pass the pointer directly to tuple_to_stringinfo() for all five call sites (INSERT, UPDATE old/new/pkAttrs, DELETE). PG 19: - RepOriginId renamed to ReplOriginId; InvalidRepOriginId renamed to InvalidReplOriginId. Update forward declaration, function definition, and the only_local guard accordingly. - AssertVariableIsOfType() removed; replace with StaticAssertVariableIsOfType() which performs the same check at compile time. - VARATT_IS_EXTERNAL_ONDISK(), VARSIZE_ANY_EXHDR(), and VARDATA_ANY() now take const void * instead of accepting Datum implicitly. Wrap the Datum arguments with DatumGetPointer() at the three affected call sites. All versions: - Remove always-true (valptr+1) != NULL guard in print_w2m_literal(). Pointer arithmetic on a non-null pointer never yields NULL; the compiler correctly warns about this. The *(valptr+1) read is safe regardless: the loop iterates while *valptr != '\0', so valptr+1 is always within the string or pointing at the null terminator, neither of which requires a NULL check. All changes are guarded with appropriate #if PG_VERSION_NUM >= NNNNNN blocks to preserve backwards compatibility with PG 13-16. --- wal2mongo.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/wal2mongo.c b/wal2mongo.c index 398bf17..03ea45c 100644 --- a/wal2mongo.c +++ b/wal2mongo.c @@ -80,7 +80,11 @@ static void pg_w2m_decode_truncate(LogicalDecodingContext *ctx, ReorderBufferChange *change); static bool pg_w2m_decode_filter(LogicalDecodingContext *ctx, +#if PG_VERSION_NUM >= 190000 + ReplOriginId origin_id); +#else RepOriginId origin_id); +#endif static void pg_w2m_decode_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, XLogRecPtr message_lsn, @@ -101,7 +105,11 @@ _PG_init(void) void _PG_output_plugin_init(OutputPluginCallbacks *cb) { +#if PG_VERSION_NUM >= 190000 + StaticAssertVariableIsOfType(&_PG_output_plugin_init, LogicalOutputPluginInit); +#else AssertVariableIsOfType(&_PG_output_plugin_init, LogicalOutputPluginInit); +#endif cb->startup_cb = pg_w2m_decode_startup; cb->begin_cb = pg_w2m_decode_begin_txn; @@ -355,11 +363,19 @@ pg_w2m_decode_commit_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, /* Generic message callback */ static bool pg_w2m_decode_filter(LogicalDecodingContext *ctx, +#if PG_VERSION_NUM >= 190000 + ReplOriginId origin_id) +#else RepOriginId origin_id) +#endif { Wal2MongoData *data = ctx->output_plugin_private; +#if PG_VERSION_NUM >= 190000 + if (data->only_local && origin_id != InvalidReplOriginId) +#else if (data->only_local && origin_id != InvalidRepOriginId) +#endif return true; return false; } @@ -578,7 +594,7 @@ print_w2m_literal(StringInfo s, Oid typid, char *outputstr) for (valptr = outputstr; *valptr; valptr++) { char ch = *valptr; - if(ch == '\\' && (valptr+1) != NULL && *(valptr+1) == '"' ) + if(ch == '\\' && *(valptr+1) == '"' ) { /* replace all \" with " */ appendStringInfoChar(s, '"'); @@ -696,7 +712,11 @@ tuple_to_stringinfo(StringInfo s, TupleDesc tupdesc, HeapTuple tuple, bool skip_ /* print data */ if (isnull) appendStringInfoString(s, "null"); +#if PG_VERSION_NUM >= 190000 + else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(origval))) +#else else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval)) +#endif appendStringInfoString(s, "unchanged-toast-datum"); else if (!typisvarlena) { @@ -720,10 +740,17 @@ tuple_to_stringinfo(StringInfo s, TupleDesc tupdesc, HeapTuple tuple, bool skip_ if (typid == BYTEAOID ) { /* Print hex format */ +#if PG_VERSION_NUM >= 190000 + rp = result = palloc(VARSIZE_ANY_EXHDR(DatumGetPointer(val)) * 2 + 2 + 1); + *rp++ = '\\'; + *rp++ = 'x'; + rp += hex_encode(VARDATA_ANY(DatumGetPointer(val)), VARSIZE_ANY_EXHDR(DatumGetPointer(val)), rp); +#else rp = result = palloc(VARSIZE_ANY_EXHDR(val) * 2 + 2 + 1); *rp++ = '\\'; *rp++ = 'x'; rp += hex_encode(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val), rp); +#endif *rp = '\0'; print_w2m_literal(s, typid, result); } @@ -806,7 +833,11 @@ pg_w2m_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, appendStringInfoString(ctx->out, " (no-tuple-data)"); else tuple_to_stringinfo(ctx->out, tupdesc, +#if PG_VERSION_NUM >= 170000 + change->data.tp.newtuple, +#else &change->data.tp.newtuple->tuple, +#endif true, NULL); appendStringInfoString(ctx->out, " );"); break; @@ -822,7 +853,11 @@ pg_w2m_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, pkAttrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_PRIMARY_KEY); tuple_to_stringinfo(ctx->out, tupdesc, +#if PG_VERSION_NUM >= 170000 + change->data.tp.oldtuple, +#else &change->data.tp.oldtuple->tuple, +#endif true, pkAttrs); bms_free(pkAttrs); } @@ -844,7 +879,11 @@ pg_w2m_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, else { tuple_to_stringinfo(ctx->out, tupdesc, +#if PG_VERSION_NUM >= 170000 + change->data.tp.newtuple, +#else &change->data.tp.newtuple->tuple, +#endif true, pkAttrs); bms_free(pkAttrs); } @@ -855,7 +894,11 @@ pg_w2m_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, { appendStringInfoString(ctx->out, ", \{ $set: "); tuple_to_stringinfo(ctx->out, tupdesc, +#if PG_VERSION_NUM >= 170000 + change->data.tp.newtuple, +#else &change->data.tp.newtuple->tuple, +#endif false, NULL); appendStringInfoString(ctx->out, " }"); } @@ -876,7 +919,11 @@ pg_w2m_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, pkAttrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_PRIMARY_KEY); tuple_to_stringinfo(ctx->out, tupdesc, +#if PG_VERSION_NUM >= 170000 + change->data.tp.oldtuple, +#else &change->data.tp.oldtuple->tuple, +#endif true, pkAttrs); bms_free(pkAttrs); }