From 072d4c4240572980b9c101ff5d2b85e656e79275 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 14 Sep 2026 10:00:31 +0900 Subject: [PATCH] Refactor some TOAST value ID code to use Oid8 instead of Oid This is a mechanical update to change most of the TOAST code paths that assume TOAST value IDs to be Oid variables to become Oid8 variables, easing an the introduction of an upcoming patch to allow larger TOAST values, at 8 bytes. The areas touched by the change are related to table AM, amcheck and logical decoding's reorder buffer. A good chunk of the changes involve switching printf() markers from %u to OID8_FORMAT. For the sake of the release notes in terms of compatibility (perhaps worth mentioning, or not), note that this changes some callback definitions in the table AM section, upgrading some TOAST callbacks to use Oid8 instead of Oid when referring to a TOAST value. Author: Michael Paquier Reviewed-by: Greg Burd Reviewed-by: Bharath Rupireddy Reviewed-by: Yugo Nagata Discussion: https://postgr.es/m/af19kUjwjhaoUTLn@paquier.xy --- contrib/amcheck/verify_heapam.c | 55 +++++++++++-------- src/backend/access/common/toast_internals.c | 8 +-- src/backend/access/heap/heaptoast.c | 12 ++-- .../replication/logical/reorderbuffer.c | 14 +++-- src/include/access/heaptoast.h | 2 +- src/include/access/tableam.h | 4 +- 6 files changed, 52 insertions(+), 43 deletions(-) diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c index bbaaec89f8a..33b00fa4dc5 100644 --- a/contrib/amcheck/verify_heapam.c +++ b/contrib/amcheck/verify_heapam.c @@ -1564,9 +1564,11 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, int32 chunksize; int32 expected_size; int32 max_chunk_size; + Oid8 toast_valueid; max_chunk_size = TOAST_OID_MAX_CHUNK_SIZE; last_chunk_seq = (extsize - 1) / max_chunk_size; + toast_valueid = ta->toast_pointer.va_valueid; /* Sanity-check the sequence number. */ chunk_seq = DatumGetInt32(fastgetattr(toasttup, 2, @@ -1574,16 +1576,16 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, if (isnull) { report_toast_corruption(ctx, ta, - psprintf("toast value %u has toast chunk with null sequence number", - ta->toast_pointer.va_valueid)); + psprintf("toast value " OID8_FORMAT " has toast chunk with null sequence number", + toast_valueid)); return; } if (chunk_seq != *expected_chunk_seq) { /* Either the TOAST index is corrupt, or we don't have all chunks. */ report_toast_corruption(ctx, ta, - psprintf("toast value %u index scan returned chunk %d when expecting chunk %d", - ta->toast_pointer.va_valueid, + psprintf("toast value " OID8_FORMAT " index scan returned chunk %d when expecting chunk %d", + toast_valueid, chunk_seq, *expected_chunk_seq)); } *expected_chunk_seq = chunk_seq + 1; @@ -1594,8 +1596,8 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, if (isnull) { report_toast_corruption(ctx, ta, - psprintf("toast value %u chunk %d has null data", - ta->toast_pointer.va_valueid, + psprintf("toast value " OID8_FORMAT " chunk %d has null data", + toast_valueid, chunk_seq)); return; } @@ -1614,8 +1616,8 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, uint32 header = ((varattrib_4b *) chunk)->va_4byte.va_header; report_toast_corruption(ctx, ta, - psprintf("toast value %u chunk %d has invalid varlena header %0x", - ta->toast_pointer.va_valueid, + psprintf("toast value " OID8_FORMAT " chunk %d has invalid varlena header %0x", + toast_valueid, chunk_seq, header)); return; } @@ -1626,8 +1628,8 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, if (chunk_seq > last_chunk_seq) { report_toast_corruption(ctx, ta, - psprintf("toast value %u chunk %d follows last expected chunk %d", - ta->toast_pointer.va_valueid, + psprintf("toast value " OID8_FORMAT " chunk %d follows last expected chunk %d", + toast_valueid, chunk_seq, last_chunk_seq)); return; } @@ -1637,8 +1639,8 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, if (chunksize != expected_size) report_toast_corruption(ctx, ta, - psprintf("toast value %u chunk %d has size %u, but expected size %u", - ta->toast_pointer.va_valueid, + psprintf("toast value " OID8_FORMAT " chunk %d has size %u, but expected size %u", + toast_valueid, chunk_seq, chunksize, expected_size)); } @@ -1669,6 +1671,7 @@ check_tuple_attribute(HeapCheckContext *ctx) varlena *attr; char *tp; /* pointer to the tuple data */ uint16 infomask; + Oid8 toast_pointer_valueid; CompactAttribute *thisatt; varatt_external_oid toast_pointer; @@ -1777,12 +1780,13 @@ check_tuple_attribute(HeapCheckContext *ctx) * Must copy attr into toast_pointer for alignment considerations */ VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + toast_pointer_valueid = toast_pointer.va_valueid; /* Toasted attributes too large to be untoasted should never be stored */ if (toast_pointer.va_rawsize > VARLENA_SIZE_LIMIT) report_corruption(ctx, - psprintf("toast value %u rawsize %d exceeds limit %d", - toast_pointer.va_valueid, + psprintf("toast value " OID8_FORMAT " rawsize %d exceeds limit %d", + toast_pointer_valueid, toast_pointer.va_rawsize, VARLENA_SIZE_LIMIT)); @@ -1809,16 +1813,16 @@ check_tuple_attribute(HeapCheckContext *ctx) } if (!valid) report_corruption(ctx, - psprintf("toast value %u has invalid compression method id %d", - toast_pointer.va_valueid, cmid)); + psprintf("toast value " OID8_FORMAT " has invalid compression method id %d", + toast_pointer_valueid, cmid)); } /* The tuple header better claim to contain toasted values */ if (!(infomask & HEAP_HASEXTERNAL)) { report_corruption(ctx, - psprintf("toast value %u is external but tuple header flag HEAP_HASEXTERNAL not set", - toast_pointer.va_valueid)); + psprintf("toast value " OID8_FORMAT " is external but tuple header flag HEAP_HASEXTERNAL not set", + toast_pointer_valueid)); return true; } @@ -1826,8 +1830,8 @@ check_tuple_attribute(HeapCheckContext *ctx) if (!ctx->rel->rd_rel->reltoastrelid) { report_corruption(ctx, - psprintf("toast value %u is external but relation has no toast relation", - toast_pointer.va_valueid)); + psprintf("toast value " OID8_FORMAT " is external but relation has no toast relation", + toast_pointer_valueid)); return true; } @@ -1873,6 +1877,7 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta) int32 expected_chunk_seq = 0; int32 last_chunk_seq; int32 max_chunk_size = TOAST_OID_MAX_CHUNK_SIZE; + Oid8 toast_valueid; extsize = VARATT_EXTERNAL_OID_GET_EXTSIZE(ta->toast_pointer); last_chunk_seq = (extsize - 1) / max_chunk_size; @@ -1903,14 +1908,16 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta) } systable_endscan_ordered(toastscan); + toast_valueid = ta->toast_pointer.va_valueid; + if (!found_toasttup) report_toast_corruption(ctx, ta, - psprintf("toast value %u not found in toast table", - ta->toast_pointer.va_valueid)); + psprintf("toast value " OID8_FORMAT " not found in toast table", + toast_valueid)); else if (expected_chunk_seq <= last_chunk_seq) report_toast_corruption(ctx, ta, - psprintf("toast value %u was expected to end at chunk %d, but ended while expecting chunk %d", - ta->toast_pointer.va_valueid, + psprintf("toast value " OID8_FORMAT " was expected to end at chunk %d, but ended while expecting chunk %d", + toast_valueid, last_chunk_seq, expected_chunk_seq)); } diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c index 0f234661bfa..ca5ed8dfabc 100644 --- a/src/backend/access/common/toast_internals.c +++ b/src/backend/access/common/toast_internals.c @@ -26,8 +26,8 @@ #include "utils/rel.h" #include "utils/snapmgr.h" -static bool toastrel_valueid_exists(Relation toastrel, Oid valueid); -static bool toastid_valueid_exists(Oid toastrelid, Oid valueid); +static bool toastrel_valueid_exists(Relation toastrel, Oid8 valueid); +static bool toastid_valueid_exists(Oid toastrelid, Oid8 valueid); /* ---------- * toast_compress_datum - @@ -447,7 +447,7 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative) * ---------- */ static bool -toastrel_valueid_exists(Relation toastrel, Oid valueid) +toastrel_valueid_exists(Relation toastrel, Oid8 valueid) { bool result = false; ScanKeyData toastkey; @@ -495,7 +495,7 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid) * ---------- */ static bool -toastid_valueid_exists(Oid toastrelid, Oid valueid) +toastid_valueid_exists(Oid toastrelid, Oid8 valueid) { bool result; Relation toastrel; diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 3b7c658a90e..81154c17376 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -623,7 +623,7 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, * result is the varlena into which the results should be written. */ void -heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, +heap_fetch_toast_slice(Relation toastrel, Oid8 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, varlena *result) { @@ -729,7 +729,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, else { /* should never happen */ - elog(ERROR, "found toasted toast chunk for toast value %u in %s", + elog(ERROR, "found toasted toast chunk for toast value " OID8_FORMAT " in %s", valueid, RelationGetRelationName(toastrel)); chunksize = 0; /* keep compiler quiet */ chunkdata = NULL; @@ -741,13 +741,13 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, if (curchunk != expectedchunk) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), - errmsg_internal("unexpected chunk number %d (expected %d) for toast value %u in %s", + errmsg_internal("unexpected chunk number %d (expected %d) for toast value " OID8_FORMAT " in %s", curchunk, expectedchunk, valueid, RelationGetRelationName(toastrel)))); if (curchunk > endchunk) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), - errmsg_internal("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s", + errmsg_internal("unexpected chunk number %d (out of range %d..%d) for toast value " OID8_FORMAT " in %s", curchunk, startchunk, endchunk, valueid, RelationGetRelationName(toastrel)))); @@ -756,7 +756,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, if (chunksize != expected_size) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), - errmsg_internal("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s", + errmsg_internal("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value " OID8_FORMAT " in %s", chunksize, expected_size, curchunk, totalchunks, valueid, RelationGetRelationName(toastrel)))); @@ -785,7 +785,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, if (expectedchunk != (endchunk + 1)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), - errmsg_internal("missing chunk number %d for toast value %u in %s", + errmsg_internal("missing chunk number %d for toast value " OID8_FORMAT " in %s", expectedchunk, valueid, RelationGetRelationName(toastrel)))); diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 457d544eec9..534fe338b38 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -177,7 +177,7 @@ typedef struct ReorderBufferIterTXNState /* toast datastructures */ typedef struct ReorderBufferToastEnt { - Oid chunk_id; /* toast_table.chunk_id */ + Oid8 chunk_id; /* toast_table.chunk_id */ int32 last_chunk_seq; /* toast_table.chunk_seq of the last chunk we * have seen */ Size num_chunks; /* number of chunks we've already seen */ @@ -5010,7 +5010,7 @@ ReorderBufferToastInitHash(ReorderBuffer *rb, ReorderBufferTXN *txn) Assert(txn->toast_hash == NULL); - hash_ctl.keysize = sizeof(Oid); + hash_ctl.keysize = sizeof(Oid8); hash_ctl.entrysize = sizeof(ReorderBufferToastEnt); hash_ctl.hcxt = rb->context; txn->toast_hash = hash_create("ReorderBufferToastHash", 5, &hash_ctl, @@ -5034,7 +5034,7 @@ ReorderBufferToastAppendChunk(ReorderBuffer *rb, ReorderBufferTXN *txn, bool isnull; Pointer chunk; TupleDesc desc = RelationGetDescr(relation); - Oid chunk_id; + Oid8 chunk_id; int32 chunk_seq; if (txn->toast_hash == NULL) @@ -5061,11 +5061,11 @@ ReorderBufferToastAppendChunk(ReorderBuffer *rb, ReorderBufferTXN *txn, dlist_init(&ent->chunks); if (chunk_seq != 0) - elog(ERROR, "got sequence entry %d for toast chunk %u instead of seq 0", + elog(ERROR, "got sequence entry %d for toast chunk " OID8_FORMAT " instead of seq 0", chunk_seq, chunk_id); } else if (found && chunk_seq != ent->last_chunk_seq + 1) - elog(ERROR, "got sequence entry %d for toast chunk %u instead of seq %d", + elog(ERROR, "got sequence entry %d for toast chunk " OID8_FORMAT " instead of seq %d", chunk_seq, chunk_id, ent->last_chunk_seq + 1); chunk = DatumGetPointer(fastgetattr(newtup, 3, desc, &isnull)); @@ -5174,6 +5174,7 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn, varlena *reconstructed; dlist_iter it; Size data_done = 0; + Oid8 toast_valueid; if (attr->attisdropped) continue; @@ -5194,13 +5195,14 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn, continue; VARATT_EXTERNAL_GET_POINTER(toast_pointer, varlena_pointer); + toast_valueid = toast_pointer.va_valueid; /* * Check whether the toast tuple changed, replace if so. */ ent = (ReorderBufferToastEnt *) hash_search(txn->toast_hash, - &toast_pointer.va_valueid, + &toast_valueid, HASH_FIND, NULL); if (ent == NULL) diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h index b2346fd75bd..aaae6e0fef6 100644 --- a/src/include/access/heaptoast.h +++ b/src/include/access/heaptoast.h @@ -142,7 +142,7 @@ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc, * Fetch a slice from a toast value stored in a heap table. * ---------- */ -extern void heap_fetch_toast_slice(Relation toastrel, Oid valueid, +extern void heap_fetch_toast_slice(Relation toastrel, Oid8 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, varlena *result); diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index ff03a2b816f..ebec31a22e4 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -783,7 +783,7 @@ typedef struct TableAmRoutine * table implemented by this AM. See table_relation_fetch_toast_slice() * for more details. */ - void (*relation_fetch_toast_slice) (Relation toastrel, Oid valueid, + void (*relation_fetch_toast_slice) (Relation toastrel, Oid8 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, @@ -1987,7 +1987,7 @@ table_relation_toast_am(Relation rel) * stored. */ static inline void -table_relation_fetch_toast_slice(Relation toastrel, Oid valueid, +table_relation_fetch_toast_slice(Relation toastrel, Oid8 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, varlena *result) {