From c905d521c1cefa2c7a2273aec0263ec6c55bb213 Mon Sep 17 00:00:00 2001 From: Nisha Moond Date: Wed, 21 Oct 2026 13:56:51 +0530 Subject: [PATCH v82_1 4/4] Document and comment that replica identity rendering can still exceed 1GB The 64kB cap on replica identity values is checked after a key value's text is produced, so in rare cases the text itself can exceed 1GB and conflict logging fails with an error. Mention this in the docs, and add an XXX comment in build_index_key_json() listing when it can happen. --- doc/src/sgml/logical-replication.sgml | 6 +++++- src/backend/replication/logical/conflict.c | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml index 8bc0e9e2e9c..e11bae53e5f 100644 --- a/doc/src/sgml/logical-replication.sgml +++ b/doc/src/sgml/logical-replication.sgml @@ -2483,7 +2483,11 @@ SELECT * FROM pg_conflict.pg_conflict_log_16392 WHERE has_omitted_values; A value omitted this way cannot be recovered from the conflict log table. Some limit is needed because replica_identity is a json value, which cannot exceed 1GB, and the text of a value - can be much longer than the value itself. + can be much longer than the value itself. However, because the 64kB + limit is checked after a key value's text representation is formed, in + rare cases where a single key value's text representation on the + subscriber itself exceeds 1GB, conflict logging will still fail with an + error. diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c index a2037ba716a..20cee95dd50 100644 --- a/src/backend/replication/logical/conflict.c +++ b/src/backend/replication/logical/conflict.c @@ -1115,8 +1115,9 @@ build_index_key_json(EState *estate, Relation localrel, * a json representation -- so its result is unrelated to the size of the * value we received, and a tiny key could render a json value above the * 1GB limit and error out the apply worker. Using the output function - * keeps us to the same representation the publisher already produced, as - * the server log does in BuildIndexValueDescription(). + * keeps us to the same representation the publisher already produced + * (when the column types and output settings match, see below), as the + * server log does in BuildIndexValueDescription(). * * Only the key attributes are recorded. FormIndexDatum() above also * fills in any non-key (INCLUDE) columns, but those are not part of the @@ -1155,6 +1156,15 @@ build_index_key_json(EState *estate, Relation localrel, * instead index's tupleDesc. */ getTypeOutputInfo(indexDesc->rd_opcintype[i], &outfuncoid, &typisvarlena); + + /* + * XXX The cap below is checked only after the value is rendered. + * The publisher rendered the same value to send it, which bounds + * its length, unless the column types differ, binary = true, or + * the apply worker's bytea_output or quote_all_identifiers + * differs. Then the render can exceed 1GB and error out the apply + * worker. + */ outputstr = OidOutputFunctionCall(outfuncoid, values[i]); outputlen = strlen(outputstr); -- 2.54.0 (Apple Git-157)