diff options
-rw-r--r-- | contrib/test_decoding/expected/toast.out | 8 | ||||
-rw-r--r-- | contrib/test_decoding/sql/toast.sql | 3 | ||||
-rw-r--r-- | src/backend/access/heap/heapam.c | 8 | ||||
-rw-r--r-- | src/backend/replication/logical/decode.c | 19 |
4 files changed, 32 insertions, 6 deletions
diff --git a/contrib/test_decoding/expected/toast.out b/contrib/test_decoding/expected/toast.out index b7bae65ee82..c0a19878091 100644 --- a/contrib/test_decoding/expected/toast.out +++ b/contrib/test_decoding/expected/toast.out @@ -298,7 +298,13 @@ ALTER TABLE toasted_several REPLICA IDENTITY FULL; ALTER TABLE toasted_several ALTER COLUMN toasted_key SET STORAGE EXTERNAL; ALTER TABLE toasted_several ALTER COLUMN toasted_col1 SET STORAGE EXTERNAL; ALTER TABLE toasted_several ALTER COLUMN toasted_col2 SET STORAGE EXTERNAL; -INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 2000)); +INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000)); +SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several; + ?column? +---------- + t +(1 row) + SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); regexp_replace ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ diff --git a/contrib/test_decoding/sql/toast.sql b/contrib/test_decoding/sql/toast.sql index a333d99abce..428816ff498 100644 --- a/contrib/test_decoding/sql/toast.sql +++ b/contrib/test_decoding/sql/toast.sql @@ -274,7 +274,8 @@ ALTER TABLE toasted_several ALTER COLUMN toasted_key SET STORAGE EXTERNAL; ALTER TABLE toasted_several ALTER COLUMN toasted_col1 SET STORAGE EXTERNAL; ALTER TABLE toasted_several ALTER COLUMN toasted_col2 SET STORAGE EXTERNAL; -INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 2000)); +INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000)); +SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several; SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 1848bc5f2c0..26d96fd7fc0 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -7086,7 +7086,13 @@ log_heap_update(Relation reln, Buffer oldbuf, /* We need to log a tuple identity */ if (old_key_tuple) { - /* don't really need this, but its more comfy to decode */ + /* + * This isn't needed, and can't actually capture the contents of + * the tuple accurately (because t_len isn't guaranteed to be big + * enough to contain old tuples which can be up to 1 GB long). But + * previous versions of 9.4 used this, so we can't change the WAL + * format. + */ xlhdr_idx.header.t_infomask2 = old_key_tuple->t_data->t_infomask2; xlhdr_idx.header.t_infomask = old_key_tuple->t_data->t_infomask; xlhdr_idx.header.t_hoff = old_key_tuple->t_data->t_hoff; diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c index 3a6d6ffab1a..09a334232e1 100644 --- a/src/backend/replication/logical/decode.c +++ b/src/backend/replication/logical/decode.c @@ -653,6 +653,7 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) xl_heap_update *xlrec; ReorderBufferChange *change; char *data; + size_t remlen = r->xl_len; xlrec = (xl_heap_update *) buf->record_data; @@ -666,6 +667,7 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) /* caution, remaining data in record is not aligned */ data = buf->record_data + SizeOfHeapUpdate; + remlen -= SizeOfHeapUpdate; if (xlrec->flags & XLOG_HEAP_CONTAINS_NEW_TUPLE) { @@ -677,6 +679,7 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) memcpy(&xlhdr, data, sizeof(xlhdr)); data += offsetof(xl_heap_header_len, header); + remlen -= offsetof(xl_heap_header_len, header); datalen = xlhdr.t_len + SizeOfHeapHeader; tuplelen = xlhdr.t_len; @@ -687,8 +690,10 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) DecodeXLogTuple(data, datalen, change->data.tp.newtuple); /* skip over the rest of the tuple header */ data += SizeOfHeapHeader; + remlen -= SizeOfHeapHeader; /* skip over the tuple data */ data += xlhdr.t_len; + remlen -= xlhdr.t_len; } if (xlrec->flags & XLOG_HEAP_CONTAINS_OLD) @@ -699,10 +704,17 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) memcpy(&xlhdr, data, sizeof(xlhdr)); data += offsetof(xl_heap_header_len, header); + remlen -= offsetof(xl_heap_header_len, header); - /* t_len is inconsistent with other cases, see log_heap_update */ - tuplelen = xlhdr.t_len - offsetof(HeapTupleHeaderData, t_bits); - datalen = tuplelen + SizeOfHeapHeader; + /* + * NB: Even though xl_heap_header_len contains the tuple's length, + * it's length field is not wide enough. Use the whole record length + * minus the new tuple's length instead. We can't remove the record + * length from the WAL record format in 9.4 due to compatibility + * concerns - later versions don't have it anyway. + */ + datalen = remlen; + tuplelen = datalen - SizeOfHeapHeader; change->data.tp.oldtuple = ReorderBufferGetTupleBuf(ctx->reorder, tuplelen); @@ -710,6 +722,7 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf) DecodeXLogTuple(data, datalen, change->data.tp.oldtuple); #ifdef NOT_USED data += datalen; + remlen -= datalen; #endif } |