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
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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');
/* 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;
xl_heap_update *xlrec;
ReorderBufferChange *change;
char *data;
+ size_t remlen = r->xl_len;
xlrec = (xl_heap_update *) buf->record_data;
/* caution, remaining data in record is not aligned */
data = buf->record_data + SizeOfHeapUpdate;
+ remlen -= SizeOfHeapUpdate;
if (xlrec->flags & XLOG_HEAP_CONTAINS_NEW_TUPLE)
{
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;
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)
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);
DecodeXLogTuple(data, datalen, change->data.tp.oldtuple);
#ifdef NOT_USED
data += datalen;
+ remlen -= datalen;
#endif
}