From 3fe12335e9a0eb7beff9e04d82d7f853aeb6c30b Mon Sep 17 00:00:00 2001 From: Nikita Malakhov Date: Thu, 1 Dec 2022 23:56:25 +0300 Subject: [PATCH 1/2] 64-bit TOAST value id instead of 32-bit This patch introduces 64-bit TOAST value id to fix issue with expending all available TOAST value OIDs in TOAST table - this causes INSERT queries to hang and DB stalls. Also, TOAST tables have individual counters cached for fast access. --- contrib/amcheck/verify_heapam.c | 63 ++-- contrib/pageinspect/heapfuncs.c | 3 +- contrib/test_decoding/test_decoding.c | 2 +- src/backend/access/common/detoast.c | 38 +-- src/backend/access/common/toast_compression.c | 6 +- src/backend/access/common/toast_internals.c | 315 ++++++++++++++++-- src/backend/access/heap/heaptoast.c | 22 +- src/backend/access/table/toast_helper.c | 6 +- src/backend/catalog/toasting.c | 6 + src/backend/replication/logical/proto.c | 2 +- .../replication/logical/reorderbuffer.c | 8 +- src/backend/replication/pgoutput/pgoutput.c | 7 +- src/backend/utils/mmgr/aset.c | 32 +- src/backend/utils/mmgr/generation.c | 9 + src/backend/utils/mmgr/slab.c | 4 + src/bin/initdb/initdb.c | 7 +- src/include/access/detoast.h | 23 ++ src/include/access/heaptoast.h | 6 +- src/include/access/tableam.h | 6 +- src/include/access/toast_internals.h | 5 + src/include/postgres.h | 43 ++- src/test/regress/regress.c | 2 +- 22 files changed, 507 insertions(+), 108 deletions(-) diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c index b72a5c96d1..ac40749fb6 100644 --- a/contrib/amcheck/verify_heapam.c +++ b/contrib/amcheck/verify_heapam.c @@ -68,7 +68,7 @@ typedef enum SkipPages */ typedef struct ToastedAttribute { - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; BlockNumber blkno; /* block in main table */ OffsetNumber offnum; /* offset in main table */ AttrNumber attnum; /* attribute in main table */ @@ -1171,16 +1171,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 %ld has toast chunk with null sequence number", + get_uint64align32(&(ta->toast_pointer.va_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 %ld index scan returned chunk %d when expecting chunk %d", + get_uint64align32(&(ta->toast_pointer.va_valueid)), chunk_seq, *expected_chunk_seq)); } *expected_chunk_seq = chunk_seq + 1; @@ -1191,8 +1191,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 %ld chunk %d has null data", + get_uint64align32(&(ta->toast_pointer.va_valueid)), chunk_seq)); return; } @@ -1211,8 +1211,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 %ld chunk %d has invalid varlena header %0x", + get_uint64align32(&(ta->toast_pointer.va_valueid)), chunk_seq, header)); return; } @@ -1223,8 +1223,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 %ld chunk %d follows last expected chunk %d", + get_uint64align32(&(ta->toast_pointer.va_valueid)), chunk_seq, last_chunk_seq)); return; } @@ -1234,8 +1234,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 %ld chunk %d has size %u, but expected size %u", + get_uint64align32(&(ta->toast_pointer.va_valueid)), chunk_seq, chunksize, expected_size)); } @@ -1267,7 +1267,8 @@ check_tuple_attribute(HeapCheckContext *ctx) char *tp; /* pointer to the tuple data */ uint16 infomask; Form_pg_attribute thisatt; - struct varatt_external toast_pointer; +/* struct varatt_external toast_pointer; */ + struct varatt_long_external toast_pointer; infomask = ctx->tuphdr->t_infomask; thisatt = TupleDescAttr(RelationGetDescr(ctx->rel), ctx->attnum); @@ -1326,7 +1327,7 @@ check_tuple_attribute(HeapCheckContext *ctx) { uint8 va_tag = VARTAG_EXTERNAL(tp + ctx->offset); - if (va_tag != VARTAG_ONDISK) + if (va_tag != VARTAG_ONDISK && va_tag != VARTAG_LONG_EXTERNAL) { report_corruption(ctx, psprintf("toasted attribute has unexpected TOAST tag %u", @@ -1373,13 +1374,15 @@ check_tuple_attribute(HeapCheckContext *ctx) /* * Must copy attr into toast_pointer for alignment considerations */ - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); +/* XXX LONG */ + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); +/* VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); */ /* 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 %ld rawsize %d exceeds limit %d", + get_uint64align32(&toast_pointer.va_valueid), toast_pointer.va_rawsize, VARLENA_SIZE_LIMIT)); @@ -1406,16 +1409,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 %ld has invalid compression method id %d", + get_uint64align32(&toast_pointer.va_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 %ld is external but tuple header flag HEAP_HASEXTERNAL not set", + get_uint64align32(&toast_pointer.va_valueid))); return true; } @@ -1423,8 +1426,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 %ld is external but relation has no toast relation", + get_uint64align32(&toast_pointer.va_valueid))); return true; } @@ -1443,7 +1446,7 @@ check_tuple_attribute(HeapCheckContext *ctx) ta = (ToastedAttribute *) palloc0(sizeof(ToastedAttribute)); - VARATT_EXTERNAL_GET_POINTER(ta->toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(ta->toast_pointer, attr); ta->blkno = ctx->blkno; ta->offnum = ctx->offnum; ta->attnum = ctx->attnum; @@ -1477,10 +1480,16 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta) /* * Setup a scan key to find chunks in toast table with matching va_valueid */ +/* ScanKeyInit(&toastkey, (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ta->toast_pointer.va_valueid)); +*/ + ScanKeyInit(&toastkey, + (AttrNumber) 1, + BTEqualStrategyNumber, F_INT8EQ, + Int64GetDatum(get_uint64align32(&(ta->toast_pointer.va_valueid)))); /* * Check if any chunks for this toasted object exist in the toast table, @@ -1504,11 +1513,11 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta) if (!found_toasttup) report_toast_corruption(ctx, ta, psprintf("toast value %u not found in toast table", - ta->toast_pointer.va_valueid)); + get_uint64align32(&(ta->toast_pointer.va_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, + get_uint64align32(&(ta->toast_pointer.va_valueid)), last_chunk_seq, expected_chunk_seq)); } diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c index aed2753253..3fa6d0b0e1 100644 --- a/contrib/pageinspect/heapfuncs.c +++ b/contrib/pageinspect/heapfuncs.c @@ -364,7 +364,8 @@ tuple_data_split_internal(Oid relid, char *tupdata, */ if (VARATT_IS_EXTERNAL(tupdata + off) && !VARATT_IS_EXTERNAL_ONDISK(tupdata + off) && - !VARATT_IS_EXTERNAL_INDIRECT(tupdata + off)) + !VARATT_IS_EXTERNAL_INDIRECT(tupdata + off) && + !VARATT_IS_LONG_EXTERNAL(tupdata + off)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("first byte of varlena attribute is incorrect for attribute %d", i))); diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c index e0fd6f1765..e027ae2ac7 100644 --- a/contrib/test_decoding/test_decoding.c +++ b/contrib/test_decoding/test_decoding.c @@ -580,7 +580,7 @@ tuple_to_stringinfo(StringInfo s, TupleDesc tupdesc, HeapTuple tuple, bool skip_ /* print data */ if (isnull) appendStringInfoString(s, "null"); - else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval)) + else if (typisvarlena && (VARATT_IS_EXTERNAL_ONDISK(origval) || VARATT_IS_LONG_EXTERNAL(origval))) appendStringInfoString(s, "unchanged-toast-datum"); else if (!typisvarlena) print_literal(s, typid, diff --git a/src/backend/access/common/detoast.c b/src/backend/access/common/detoast.c index 92c9c658d3..990d4a8387 100644 --- a/src/backend/access/common/detoast.c +++ b/src/backend/access/common/detoast.c @@ -46,7 +46,7 @@ detoast_external_attr(struct varlena *attr) { struct varlena *result; - if (VARATT_IS_EXTERNAL_ONDISK(attr)) + if (VARATT_IS_EXTERNAL_ONDISK(attr) || VARATT_IS_LONG_EXTERNAL(attr)) { /* * This is an external stored plain value @@ -115,7 +115,7 @@ detoast_external_attr(struct varlena *attr) struct varlena * detoast_attr(struct varlena *attr) { - if (VARATT_IS_EXTERNAL_ONDISK(attr)) + if ((VARATT_IS_EXTERNAL_ONDISK(attr)) || VARATT_IS_LONG_EXTERNAL(attr)) { /* * This is an externally stored datum --- fetch it back from there @@ -223,11 +223,11 @@ detoast_attr_slice(struct varlena *attr, else if (pg_add_s32_overflow(sliceoffset, slicelength, &slicelimit)) slicelength = slicelimit = -1; - if (VARATT_IS_EXTERNAL_ONDISK(attr)) + if (VARATT_IS_EXTERNAL_ONDISK(attr) || VARATT_IS_LONG_EXTERNAL(attr)) { - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); /* fast path for non-compressed external datums */ if (!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer)) @@ -344,14 +344,14 @@ toast_fetch_datum(struct varlena *attr) { Relation toastrel; struct varlena *result; - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; int32 attrsize; - if (!VARATT_IS_EXTERNAL_ONDISK(attr)) + if (!VARATT_IS_EXTERNAL_ONDISK(attr) && !VARATT_IS_LONG_EXTERNAL(attr)) elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums"); /* Must copy to access aligned fields */ - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); attrsize = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer); @@ -372,7 +372,7 @@ toast_fetch_datum(struct varlena *attr) toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock); /* Fetch all chunks */ - table_relation_fetch_toast_slice(toastrel, toast_pointer.va_valueid, + table_relation_fetch_toast_slice(toastrel, get_uint64align32(&(toast_pointer.va_valueid)), attrsize, 0, attrsize, result); /* Close toast table */ @@ -398,14 +398,14 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, { Relation toastrel; struct varlena *result; - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; int32 attrsize; - if (!VARATT_IS_EXTERNAL_ONDISK(attr)) + if (!VARATT_IS_EXTERNAL_ONDISK(attr) && !VARATT_IS_LONG_EXTERNAL(attr)) elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums"); /* Must copy to access aligned fields */ - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); /* * It's nonsense to fetch slices of a compressed datum unless when it's a @@ -452,7 +452,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock); /* Fetch all chunks */ - table_relation_fetch_toast_slice(toastrel, toast_pointer.va_valueid, + table_relation_fetch_toast_slice(toastrel, get_uint64align32(&(toast_pointer.va_valueid)), attrsize, sliceoffset, slicelength, result); @@ -547,12 +547,12 @@ toast_raw_datum_size(Datum value) struct varlena *attr = (struct varlena *) DatumGetPointer(value); Size result; - if (VARATT_IS_EXTERNAL_ONDISK(attr)) + if (VARATT_IS_EXTERNAL_ONDISK(attr) || VARATT_IS_LONG_EXTERNAL(attr)) { /* va_rawsize is the size of the original datum -- including header */ - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); result = toast_pointer.va_rawsize; } else if (VARATT_IS_EXTERNAL_INDIRECT(attr)) @@ -603,16 +603,16 @@ toast_datum_size(Datum value) struct varlena *attr = (struct varlena *) DatumGetPointer(value); Size result; - if (VARATT_IS_EXTERNAL_ONDISK(attr)) + if (VARATT_IS_EXTERNAL_ONDISK(attr) || VARATT_IS_LONG_EXTERNAL(attr)) { /* * Attribute is stored externally - return the extsize whether * compressed or not. We do not count the size of the toast pointer * ... should we? */ - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); result = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer); } else if (VARATT_IS_EXTERNAL_INDIRECT(attr)) diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c index 7156ae9c47..5b27c84275 100644 --- a/src/backend/access/common/toast_compression.c +++ b/src/backend/access/common/toast_compression.c @@ -261,11 +261,11 @@ toast_get_compression_id(struct varlena *attr) * the external toast pointer. If compressed inline, fetch it from the * toast compression header. */ - if (VARATT_IS_EXTERNAL_ONDISK(attr)) + if (VARATT_IS_EXTERNAL_ONDISK(attr) || VARATT_IS_LONG_EXTERNAL(attr)) { - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer)) cmid = VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer); diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c index 576e585a89..278c7822f4 100644 --- a/src/backend/access/common/toast_internals.c +++ b/src/backend/access/common/toast_internals.c @@ -27,8 +27,168 @@ #include "utils/rel.h" #include "utils/snapmgr.h" +#include "access/htup_details.h" +#include "commands/defrem.h" +#include "lib/pairingheap.h" +#include "utils/builtins.h" +#include "utils/memutils.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#define InvalidInt64 0x0000000000000000 + static bool toastrel_valueid_exists(Relation toastrel, Oid valueid); static bool toastid_valueid_exists(Oid toastrelid, Oid valueid); +static bool toastrel_long_valueid_exists(Relation toastrel, uint64 valueid, bool long_ind); + +/* Toast Relation counters cache */ +typedef struct ToastCounterCacheEntry +{ + Oid toastrelOid; + uint64 counter; +} ToastCounterCacheEntry; + +static List *ToastCounterCache = NIL; + +uint64 +SearchMinUnusedValueid(Oid toastrelOid) +{ + ToastCounterCacheEntry *entry; + bool cnt_exists_ind = false; + Relation toast_rel; + uint64 valueid = 0; + uint64 hi_valueid = 0xffffffffffff; + uint64 lo_valueid = 0; + ScanKeyData toastkey; + SysScanDesc toastscan; + int num_indexes; + int validIndex; + Relation *toastidxs; + + + toast_rel = table_open(toastrelOid, AccessShareLock); + validIndex = toast_open_indexes(toast_rel, + AccessShareLock, + &toastidxs, + &num_indexes); + + ScanKeyInit(&toastkey, + (AttrNumber) 1, + BTEqualStrategyNumber, F_INT8EQ, + Int64GetDatum(lo_valueid + 1)); + toastscan = systable_beginscan(toast_rel, + RelationGetRelid(toastidxs[validIndex]), + true, SnapshotAny, 1, &toastkey); + + if (systable_getnext(toastscan) != NULL) + cnt_exists_ind = true; + + systable_endscan(toastscan); + if(!cnt_exists_ind) + goto out; + + while(true) //(hi_valueid - lo_valueid) > 0) + { + valueid = (lo_valueid + ((hi_valueid - lo_valueid) >> 1)); + cnt_exists_ind = false; + + ScanKeyInit(&toastkey, + (AttrNumber) 1, + BTEqualStrategyNumber, F_INT8EQ, + Int64GetDatum(valueid)); + toastscan = systable_beginscan(toast_rel, + RelationGetRelid(toastidxs[validIndex]), + true, SnapshotAny, 1, &toastkey); + + if (systable_getnext(toastscan) != NULL) + cnt_exists_ind = true; + + systable_endscan(toastscan); + + if( hi_valueid - lo_valueid == 0 ) + goto out; + + if(cnt_exists_ind) + lo_valueid = valueid + 1; + else + hi_valueid = valueid - 1; + } + + +out: + toast_close_indexes(toastidxs, num_indexes, NoLock); + + if(cnt_exists_ind) + valueid++; + + table_close(toast_rel, NoLock); + + entry = palloc(sizeof(*entry)); + + entry->toastrelOid = toastrelOid; + entry->counter = valueid; + + ToastCounterCache = lappend(ToastCounterCache, entry); + + return valueid; +} + +/* + * SearchTsrCache - get cached toaster routine, emits an error if toaster + * doesn't exist + */ +uint64 +SearchTrelCounterCache(Oid toastrelOid) +{ + ListCell *lc; + ToastCounterCacheEntry *entry; + MemoryContext ctx; + uint64 valueid = 0; + ctx = MemoryContextSwitchTo(CacheMemoryContext); + + foreach(lc, ToastCounterCache) + { + entry = (ToastCounterCacheEntry*)lfirst(lc); + if (entry->toastrelOid == toastrelOid) + { + if(entry->counter < 0xffffffffffffffff) + entry->counter++; + else + entry->counter = 1; + valueid = entry->counter; + goto out; + } + } + +out: + MemoryContextSwitchTo(ctx); + + return valueid; +} + +uint64 +InsertTrelCounterCache(Oid toastrelOid) +{ + ToastCounterCacheEntry *entry; + MemoryContext ctx; + uint64 valueid = 0; + ctx = MemoryContextSwitchTo(CacheMemoryContext); + + valueid = SearchMinUnusedValueid(toastrelOid); + + /* did not find entry, make a new one */ + entry = palloc(sizeof(*entry)); + + entry->toastrelOid = toastrelOid; + entry->counter = valueid; + ToastCounterCache = lappend(ToastCounterCache, entry); + + MemoryContextSwitchTo(ctx); + + return valueid; +} + +/**/ /* ---------- * toast_compress_datum - @@ -128,7 +288,7 @@ toast_save_datum(Relation rel, Datum value, bool t_isnull[3]; CommandId mycid = GetCurrentCommandId(true); struct varlena *result; - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; union { struct varlena hdr; @@ -144,6 +304,7 @@ toast_save_datum(Relation rel, Datum value, Pointer dval = DatumGetPointer(value); int num_indexes; int validIndex; + bool long_ind = false; Assert(!VARATT_IS_EXTERNAL(value)); @@ -212,6 +373,16 @@ toast_save_datum(Relation rel, Datum value, else toast_pointer.va_toastrelid = RelationGetRelid(toastrel); + if(TupleDescAttr(toasttupDesc, 0)->atttypid == OIDOID || TupleDescAttr(toasttupDesc, 0)->atttypid == INT4OID) + { +/* regular toast pointer*/ + long_ind = false; + } + else + { +/* 64-bit id */ + long_ind = true; + } /* * Choose an OID to use as the value ID for this toast value. * @@ -226,27 +397,38 @@ toast_save_datum(Relation rel, Datum value, */ if (!OidIsValid(rel->rd_toastoid)) { + uint64 valueid = 0; /* normal case: just choose an unused OID */ +/* toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, RelationGetRelid(toastidxs[validIndex]), (AttrNumber) 1); +*/ + valueid = SearchTrelCounterCache(rel->rd_rel->reltoastrelid); + if(valueid == 0) + valueid = InsertTrelCounterCache(rel->rd_rel->reltoastrelid); + set_uint64align32(&(toast_pointer.va_valueid), valueid); } else { + uint64 zero_int8 = 0; + uint64 valueid = 0; /* rewrite case: check to see if value was in old toast table */ - toast_pointer.va_valueid = InvalidOid; + /* toast_pointer.va_valueid = InvalidOid; */ + set_uint64align32(&(toast_pointer.va_valueid), zero_int8); if (oldexternal != NULL) { - struct varatt_external old_toast_pointer; + struct varatt_long_external old_toast_pointer; - Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal)); + Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal) || VARATT_IS_LONG_EXTERNAL(oldexternal)); /* Must copy to access aligned fields */ - VARATT_EXTERNAL_GET_POINTER(old_toast_pointer, oldexternal); + VARATT_EXTERNAL_GET_LONG_POINTER(old_toast_pointer, oldexternal); if (old_toast_pointer.va_toastrelid == rel->rd_toastoid) { /* This value came from the old toast table; reuse its OID */ - toast_pointer.va_valueid = old_toast_pointer.va_valueid; + /* toast_pointer.va_valueid = old_toast_pointer.va_valueid; */ + set_uint64align32(&(toast_pointer.va_valueid), get_uint64align32(&(old_toast_pointer.va_valueid))); /* * There is a corner case here: the table rewrite might have @@ -265,35 +447,50 @@ toast_save_datum(Relation rel, Datum value, * copies belonging to already-deleted heap tuples would not * be reclaimed by VACUUM. */ +/* if (toastrel_valueid_exists(toastrel, toast_pointer.va_valueid)) +*/ + if (toastrel_long_valueid_exists(toastrel, + get_uint64align32(&(toast_pointer.va_valueid)), long_ind)) { /* Match, so short-circuit the data storage loop below */ data_todo = 0; } } } - if (toast_pointer.va_valueid == InvalidOid) +/* if (toast_pointer.va_valueid == InvalidOid) */ + if (get_uint64align32(&(toast_pointer.va_valueid)) == zero_int8) { + bool exists_ind = false; /* * new value; must choose an OID that doesn't conflict in either * old or new toast table */ do { +/* toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, RelationGetRelid(toastidxs[validIndex]), (AttrNumber) 1); - } while (toastid_valueid_exists(rel->rd_toastoid, - toast_pointer.va_valueid)); +*/ + valueid = SearchTrelCounterCache(rel->rd_rel->reltoastrelid); + if(valueid == 0) + valueid = InsertTrelCounterCache(rel->rd_rel->reltoastrelid); +// set_uint64align32(&(toast_pointer.va_valueid), valueid); + +// set_uint64align32(&(toast_pointer.va_valueid), SearchTrelCounterCache(rel->rd_rel->reltoastrelid)); + exists_ind = toastrel_long_valueid_exists(toastrel, + get_uint64align32(&(toast_pointer.va_valueid)), long_ind); + } while (!exists_ind); + set_uint64align32(&(toast_pointer.va_valueid), valueid); } } - /* * Initialize constant parts of the tuple data */ - t_values[0] = ObjectIdGetDatum(toast_pointer.va_valueid); + t_values[0] = Int64GetDatum(get_uint64align32(&(toast_pointer.va_valueid))); //ObjectIdGetDatum(toast_pointer.va_valueid); t_values[2] = PointerGetDatum(&chunk_data); t_isnull[0] = false; t_isnull[1] = false; @@ -338,12 +535,14 @@ toast_save_datum(Relation rel, Datum value, { /* Only index relations marked as ready can be updated */ if (toastidxs[i]->rd_index->indisready) + { index_insert(toastidxs[i], t_values, t_isnull, &(toasttup->t_self), toastrel, toastidxs[i]->rd_index->indisunique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO, false, NULL); + } } /* @@ -365,14 +564,14 @@ toast_save_datum(Relation rel, Datum value, */ toast_close_indexes(toastidxs, num_indexes, NoLock); table_close(toastrel, NoLock); - /* * Create the TOAST pointer value that we'll return */ - result = (struct varlena *) palloc(TOAST_POINTER_SIZE); - SET_VARTAG_EXTERNAL(result, VARTAG_ONDISK); - memcpy(VARDATA_EXTERNAL(result), &toast_pointer, sizeof(toast_pointer)); - + // result = (struct varlena *) palloc(TOAST_POINTER_SIZE); + result = (struct varlena *) palloc(VARSIZE_LONG_EXTERNAL(toast_pointer)); +/* SET_VARTAG_EXTERNAL(result, VARTAG_ONDISK); */ + SET_VARTAG_EXTERNAL(result, VARTAG_LONG_EXTERNAL); + memcpy(VARDATA_EXTERNAL(result), &toast_pointer, VARATT_LONG_SIZE); return PointerGetDatum(result); } @@ -386,7 +585,7 @@ void toast_delete_datum(Relation rel, Datum value, bool is_speculative) { struct varlena *attr = (struct varlena *) DatumGetPointer(value); - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; Relation toastrel; Relation *toastidxs; ScanKeyData toastkey; @@ -396,11 +595,11 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative) int validIndex; SnapshotData SnapshotToast; - if (!VARATT_IS_EXTERNAL_ONDISK(attr)) + if (!VARATT_IS_EXTERNAL_ONDISK(attr) && !VARATT_IS_LONG_EXTERNAL(attr)) return; /* Must copy to access aligned fields */ - VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr); /* * Open the toast relation and its indexes @@ -416,10 +615,27 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative) /* * Setup a scan key to find chunks with matching va_valueid */ - ScanKeyInit(&toastkey, +/* if(VARATT_IS_LONG_EXTERNAL(attr)) + { */ + ScanKeyInit(&toastkey, + (AttrNumber) 1, + BTEqualStrategyNumber, F_INT8EQ, + Int64GetDatum(get_uint64align32(&toast_pointer.va_valueid))); +/* } + else + { + ScanKeyInit(&toastkey, + (AttrNumber) 1, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum((&toast_pointer.va_valueid)->lo)); +*/ +/* + ScanKeyInit(&toastkey, (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(toast_pointer.va_valueid)); +*/ +/* } */ /* * Find all the chunks. (We don't actually care whether we see them in @@ -450,6 +666,65 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative) table_close(toastrel, NoLock); } +static bool +toastrel_long_valueid_exists(Relation toastrel, uint64 valueid, bool long_ind) +{ + bool result = false; + ScanKeyData toastkey; + SysScanDesc toastscan; + int num_indexes; + int validIndex; + Relation *toastidxs; + TupleDesc tupDesc; + uint32 short_valueid; + + tupDesc = RelationGetDescr(toastrel); + /* Fetch a valid index relation */ + validIndex = toast_open_indexes(toastrel, + RowExclusiveLock, + &toastidxs, + &num_indexes); + + /* + * Setup a scan key to find chunks with matching va_valueid + */ +/* + if(long_ind) + {*/ + ScanKeyInit(&toastkey, + (AttrNumber) 1, + BTEqualStrategyNumber, F_INT8EQ, + Int64GetDatum(valueid)); +/* } + else + { + short_valueid = (uint32) (valueid & 0xffffffff); + + ScanKeyInit(&toastkey, + (AttrNumber) 1, + BTEqualStrategyNumber, F_OIDEQ, + Int32GetDatum(short_valueid)); + } +*/ + /* + * Is there any such chunk? + */ + toastscan = systable_beginscan(toastrel, + RelationGetRelid(toastidxs[validIndex]), + true, SnapshotAny, 1, &toastkey); + + if (systable_getnext(toastscan) != NULL) + result = true; + + systable_endscan(toastscan); + + /* Clean up */ + toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock); + + return result; +} + + /* ---------- * toastrel_valueid_exists - * diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 1575a81b01..d5d0042744 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -266,7 +266,6 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, biggest_attno = toast_tuple_find_biggest_attribute(&ttc, false, true); if (biggest_attno < 0) break; - toast_tuple_externalize(&ttc, biggest_attno, options); } @@ -329,9 +328,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, } else result_tuple = newtup; - toast_tuple_cleanup(&ttc); - return result_tuple; } @@ -623,7 +620,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, uint64 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, struct varlena *result) { @@ -652,10 +649,17 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, Assert(endchunk <= totalchunks); /* Set up a scan key to fetch from the index. */ +/* ScanKeyInit(&toastkey[0], (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(valueid)); +*/ + elog(NOTICE, "heap_fetch_toast_slice rel %u val %ld", toastrel->rd_id, valueid); + ScanKeyInit(&toastkey[0], + (AttrNumber) 1, + BTEqualStrategyNumber, F_INT8EQ, + Int64GetDatum(valueid)); /* * No additional condition if fetching all chunks. Otherwise, use an @@ -727,7 +731,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 %ld in %s", valueid, RelationGetRelationName(toastrel)); chunksize = 0; /* keep compiler quiet */ chunkdata = NULL; @@ -739,13 +743,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 %ld 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 %ld in %s", curchunk, startchunk, endchunk, valueid, RelationGetRelationName(toastrel)))); @@ -754,7 +758,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 %ld in %s", chunksize, expected_size, curchunk, totalchunks, valueid, RelationGetRelationName(toastrel)))); @@ -783,7 +787,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 %ld in %s", expectedchunk, valueid, RelationGetRelationName(toastrel)))); diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c index 74ba2189f0..91bf2c67c5 100644 --- a/src/backend/access/table/toast_helper.c +++ b/src/backend/access/table/toast_helper.c @@ -71,10 +71,10 @@ toast_tuple_init(ToastTupleContext *ttc) * we have to delete it later. */ if (att->attlen == -1 && !ttc->ttc_oldisnull[i] && - VARATT_IS_EXTERNAL_ONDISK(old_value)) + (VARATT_IS_EXTERNAL_ONDISK(old_value) || VARATT_IS_LONG_EXTERNAL(old_value))) { if (ttc->ttc_isnull[i] || - !VARATT_IS_EXTERNAL_ONDISK(new_value) || + !(VARATT_IS_EXTERNAL_ONDISK(new_value) || VARATT_IS_LONG_EXTERNAL(new_value)) || memcmp((char *) old_value, (char *) new_value, VARSIZE_EXTERNAL(old_value)) != 0) { @@ -330,7 +330,7 @@ toast_delete_external(Relation rel, Datum *values, bool *isnull, if (isnull[i]) continue; - else if (VARATT_IS_EXTERNAL_ONDISK(value)) + else if (VARATT_IS_EXTERNAL_ONDISK(value) || VARATT_IS_LONG_EXTERNAL(value)) toast_delete_datum(rel, value, is_speculative); } } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 9bc10729b0..866fb8aa82 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -204,10 +204,16 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, /* this is pretty painful... need a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(3); +/* TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, -1, 0); +*/ + TupleDescInitEntry(tupdesc, (AttrNumber) 1, + "chunk_id", + INT8OID, + -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "chunk_seq", INT4OID, diff --git a/src/backend/replication/logical/proto.c b/src/backend/replication/logical/proto.c index f5f2bc24d8..0f6eb2dbc5 100644 --- a/src/backend/replication/logical/proto.c +++ b/src/backend/replication/logical/proto.c @@ -814,7 +814,7 @@ logicalrep_write_tuple(StringInfo out, Relation rel, TupleTableSlot *slot, continue; } - if (att->attlen == -1 && VARATT_IS_EXTERNAL_ONDISK(values[i])) + if (att->attlen == -1 && (VARATT_IS_EXTERNAL_ONDISK(values[i]) || VARATT_IS_LONG_EXTERNAL(values[i]))) { /* * Unchanged toasted datum. (Note that we don't promise to detect diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index b567b8b59e..533f1f3f8e 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -4755,12 +4755,13 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn, struct varlena *varlena; /* va_rawsize is the size of the original datum -- including header */ - struct varatt_external toast_pointer; + struct varatt_long_external toast_pointer; struct varatt_indirect redirect_pointer; struct varlena *new_datum = NULL; struct varlena *reconstructed; dlist_iter it; Size data_done = 0; + uint64 valueid = 0; /* system columns aren't toasted */ if (attr->attnum < 0) @@ -4784,14 +4785,15 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn, if (!VARATT_IS_EXTERNAL(varlena)) continue; - VARATT_EXTERNAL_GET_POINTER(toast_pointer, varlena); + VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, varlena); /* * Check whether the toast tuple changed, replace if so. */ + valueid = get_uint64align32(&toast_pointer.va_valueid); ent = (ReorderBufferToastEnt *) hash_search(txn->toast_hash, - (void *) &toast_pointer.va_valueid, + (void *) &valueid, HASH_FIND, NULL); if (ent == NULL) diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index ca46fba3af..1b8e59ef7d 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1284,8 +1284,11 @@ pgoutput_row_filter(Relation relation, TupleTableSlot *old_slot, * VARTAG_INDIRECT. See ReorderBufferToastReplace. */ if (att->attlen == -1 && - VARATT_IS_EXTERNAL_ONDISK(new_slot->tts_values[i]) && - !VARATT_IS_EXTERNAL_ONDISK(old_slot->tts_values[i])) + ((VARATT_IS_EXTERNAL_ONDISK(new_slot->tts_values[i]) && + !VARATT_IS_EXTERNAL_ONDISK(old_slot->tts_values[i])) || + (VARATT_IS_LONG_EXTERNAL(new_slot->tts_values[i]) && + !(VARATT_IS_LONG_EXTERNAL(old_slot->tts_values[i]) || VARATT_IS_EXTERNAL_ONDISK(old_slot->tts_values[i]))) + )) { if (!tmp_new_slot) { diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index b6a8bbcd59..a821fcbcc9 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -1025,12 +1025,13 @@ AllocSetFree(void *pointer) #ifdef MEMORY_CONTEXT_CHECKING { Size chunk_size = block->endptr - (char *) pointer; - /* Test for someone scribbling on unused space in chunk */ Assert(chunk->requested_size < chunk_size); if (!sentinel_ok(pointer, chunk->requested_size)) - elog(WARNING, "detected write past chunk end in %s %p", - set->header.name, chunk); + elog(WARNING, "1 detected write past chunk end in %s %p req size %ld ch size %ld", + set->header.name, chunk, chunk->requested_size, chunk_size); +/* elog(WARNING, "detected write past chunk end in %s %p", + set->header.name, chunk); */ } #endif @@ -1072,8 +1073,11 @@ AllocSetFree(void *pointer) /* Test for someone scribbling on unused space in chunk */ if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx)) if (!sentinel_ok(pointer, chunk->requested_size)) - elog(WARNING, "detected write past chunk end in %s %p", - set->header.name, chunk); + elog(WARNING, "2 detected write past chunk end in %s %p size %ld f size %ld", + set->header.name, chunk, chunk->requested_size, GetChunkSizeFromFreeListIdx(fidx)); + +/* elog(WARNING, "detected write past chunk end in %s %p", + set->header.name, chunk); */ #endif #ifdef CLOBBER_FREED_MEMORY @@ -1148,8 +1152,12 @@ AllocSetRealloc(void *pointer, Size size) /* Test for someone scribbling on unused space in chunk */ Assert(chunk->requested_size < oldsize); if (!sentinel_ok(pointer, chunk->requested_size)) + elog(WARNING, "detected write past chunk end in %s %p size %ld oldsize %ld", + set->header.name, chunk, chunk->requested_size, oldsize); +/* elog(WARNING, "detected write past chunk end in %s %p", set->header.name, chunk); +*/ #endif #ifdef MEMORY_CONTEXT_CHECKING @@ -1246,10 +1254,11 @@ AllocSetRealloc(void *pointer, Size size) #ifdef MEMORY_CONTEXT_CHECKING /* Test for someone scribbling on unused space in chunk */ +// elog(NOTICE, "write past chunk 4 req size %ld oldsize %ld", chunk->requested_size, oldsize); if (chunk->requested_size < oldsize) if (!sentinel_ok(pointer, chunk->requested_size)) - elog(WARNING, "detected write past chunk end in %s %p", - set->header.name, chunk); + elog(WARNING, "detected write past chunk end in %s %p size %ld oldsize %ld", + set->header.name, chunk, chunk->requested_size, oldsize); #endif /* @@ -1603,9 +1612,12 @@ AllocSetCheck(MemoryContext context) */ if (dsize != InvalidAllocSize && dsize < chsize && !sentinel_ok(chunk, ALLOC_CHUNKHDRSZ + dsize)) - elog(WARNING, "problem in alloc set %s: detected write past chunk end in block %p, chunk %p", - name, block, chunk); - + elog(WARNING, "problem in alloc set %s: detected write past chunk end in block %p, chunk %p size %ld oldsize %ld", + name, block, chunk, dsize, chsize); +/* + elog(WARNING, "problem in alloc set %s: detected write past chunk end in block %p, chunk %p size %ld oldsize %ld", + name, block, chunk, dsize, chsize); +*/ /* * If chunk is allocated, disallow external access to private part * of chunk header. diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index b432a92be3..8a83861518 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -671,8 +671,13 @@ GenerationFree(void *pointer) /* Test for someone scribbling on unused space in chunk */ Assert(chunk->requested_size < chunksize); if (!sentinel_ok(pointer, chunk->requested_size)) + elog(WARNING, "detected write past chunk end in %s %p size %ld oldsize %ld", + ((MemoryContext) block->context)->name, chunk, chunk->requested_size, chunksize); + +/* elog(WARNING, "detected write past chunk end in %s %p", ((MemoryContext) block->context)->name, chunk); +*/ #endif #ifdef CLOBBER_FREED_MEMORY @@ -780,8 +785,12 @@ GenerationRealloc(void *pointer, Size size) /* Test for someone scribbling on unused space in chunk */ Assert(chunk->requested_size < oldsize); if (!sentinel_ok(pointer, chunk->requested_size)) + elog(WARNING, "detected write past chunk end in %s %p size %ld oldsize %ld", + ((MemoryContext) set)->name, chunk, chunk->requested_size, oldsize); +/* elog(WARNING, "detected write past chunk end in %s %p", ((MemoryContext) set)->name, chunk); +*/ #endif /* diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c index 6df0839b6a..be5ababf75 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -482,8 +482,12 @@ SlabFree(void *pointer) /* Test for someone scribbling on unused space in chunk */ Assert(slab->chunkSize < (slab->fullChunkSize - Slab_CHUNKHDRSZ)); if (!sentinel_ok(pointer, slab->chunkSize)) + elog(WARNING, "detected write past chunk end in %s %p size %ld oldsize %ld", + slab->header.name, chunk, slab->chunkSize, (slab->fullChunkSize - Slab_CHUNKHDRSZ)); +/* elog(WARNING, "detected write past chunk end in %s %p", slab->header.name, chunk); +*/ #endif /* compute index of the chunk with respect to block start */ diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 7c391aaf0b..880035f8a0 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2683,11 +2683,16 @@ initialize_data_directory(void) fputs(_("performing post-bootstrap initialization ... "), stdout); fflush(stdout); + snprintf(cmd, sizeof(cmd), + "\"%s\" %s %s template1 %s", + backend_exec, backend_options, extra_options, + debug ? "-d 5" : ""); +/* snprintf(cmd, sizeof(cmd), "\"%s\" %s %s template1 >%s", backend_exec, backend_options, extra_options, DEVNULL); - +*/ PG_CMD_OPEN; setup_auth(cmdfd); diff --git a/src/include/access/detoast.h b/src/include/access/detoast.h index b1d8ea09dd..f4381872bf 100644 --- a/src/include/access/detoast.h +++ b/src/include/access/detoast.h @@ -27,6 +27,29 @@ do { \ memcpy(&(toast_pointer), VARDATA_EXTERNAL(attre), sizeof(toast_pointer)); \ } while (0) +/* memcpy(&(toast_pointer), VARDATA_EXTERNAL(attre), sizeof(varatt_external)); \ */ +/* memcpy(&(toast_pointer) + sizeof(varatt_external), VARDATA_EXTERNAL(attre) + sizeof(varatt_external), sizeof(uint32)); \ */ +/* + Assert(VARSIZE_EXTERNAL(attre) == sizeof(toast_pointer) + VARHDRSZ_EXTERNAL); \ +*/ + // sizeof(varatt_long_external)) +#define LONG_TOAST_POINTER_SIZE \ + sizeof(varatt_long_external) + VARHDRSZ_EXTERNAL + +#define VARATT_EXTERNAL_GET_LONG_POINTER(toast_pointer, attr) \ +do { \ + varattrib_1b_e *attre = (varattrib_1b_e *) (attr); \ + Assert(VARATT_IS_EXTERNAL(attre)); \ + Assert(VARSIZE_EXTERNAL(attre) == LONG_TOAST_POINTER_SIZE); \ + if(VARATT_IS_LONG_EXTERNAL(attr)) \ + memcpy(&(toast_pointer), VARDATA_EXTERNAL(attre), sizeof(toast_pointer)); \ + else \ + { \ + memcpy(&(toast_pointer), VARDATA_EXTERNAL(attre), sizeof(varatt_external)); \ + memset(&(toast_pointer) + sizeof(varatt_external), 0, sizeof(uint32)); \ + } \ +} while (0) + /* Size of an EXTERNAL datum that contains a standard TOAST pointer */ #define TOAST_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_external)) diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h index a75699054a..44832861d8 100644 --- a/src/include/access/heaptoast.h +++ b/src/include/access/heaptoast.h @@ -80,11 +80,11 @@ #define EXTERN_TUPLES_PER_PAGE 4 /* tweak only this */ #define EXTERN_TUPLE_MAX_SIZE MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE) - +/* XXX long_external */ #define TOAST_MAX_CHUNK_SIZE \ (EXTERN_TUPLE_MAX_SIZE - \ MAXALIGN(SizeofHeapTupleHeader) - \ - sizeof(Oid) - \ + sizeof(uint64) - \ sizeof(int32) - \ VARHDRSZ) @@ -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, uint64 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, struct varlena *result); diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index 4d1ef405c2..4c587e93ff 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -724,7 +724,8 @@ 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, + /* Oid valueid, */ + void (*relation_fetch_toast_slice) (Relation toastrel, uint64 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, @@ -1885,8 +1886,9 @@ table_relation_toast_am(Relation rel) * result is caller-allocated space into which the fetched bytes should be * stored. */ + /* Oid valueid, */ static inline void -table_relation_fetch_toast_slice(Relation toastrel, Oid valueid, +table_relation_fetch_toast_slice(Relation toastrel, uint64 valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, struct varlena *result) { diff --git a/src/include/access/toast_internals.h b/src/include/access/toast_internals.h index 85e7dc0fc5..b92dd22df9 100644 --- a/src/include/access/toast_internals.h +++ b/src/include/access/toast_internals.h @@ -47,11 +47,16 @@ typedef struct toast_compress_header extern Datum toast_compress_datum(Datum value, char cmethod); extern Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock); +/* extern bool toastrel_long_valueid_exists(Relation toastrel, uint64 valueid, bool long_ind); */ extern void toast_delete_datum(Relation rel, Datum value, bool is_speculative); extern Datum toast_save_datum(Relation rel, Datum value, struct varlena *oldexternal, int options); +extern uint64 SearchMinUnusedValueid(Oid toastrelOid); +extern uint64 SearchTrelCounterCache(Oid toastrelOid); +extern uint64 InsertTrelCounterCache(Oid toastrelOid); + extern int toast_open_indexes(Relation toastrel, LOCKMODE lock, Relation **toastidxs, diff --git a/src/include/postgres.h b/src/include/postgres.h index 54730dfb38..f971bad233 100644 --- a/src/include/postgres.h +++ b/src/include/postgres.h @@ -72,10 +72,35 @@ typedef struct varatt_external int32 va_rawsize; /* Original data size (includes header) */ uint32 va_extinfo; /* External saved size (without header) and * compression method */ - Oid va_valueid; /* Unique ID of value within TOAST table */ Oid va_toastrelid; /* RelID of TOAST table containing it */ + Oid va_valueid; /* Unique ID of value within TOAST table */ } varatt_external; +typedef struct uint64align32 +{ + uint32 hi; + uint32 lo; +} uint64align32; + +#define set_uint64align32(p, v) \ + ( \ + (p)->hi = (v) >> 32, \ + (p)->lo = (v) & 0xffffffff \ + ) + +#define get_uint64align32(p) \ + (((uint64)((p)->hi)) << 32 | ((uint64)((p)->lo))) + +typedef struct varatt_long_external +{ + int32 va_rawsize; /* Original data size (includes header) */ + uint32 va_extinfo; /* External saved size (without header) and */ + /* compression method */ + Oid va_toastrelid; /* RelID of TOAST table containing it */ + uint64align32 va_valueid; /* Unique ID of value within TOAST table */ +/* char *inline_tail_data; */ +} varatt_long_external; + /* * These macros define the "saved size" portion of va_extinfo. Its remaining * two high-order bits identify the compression method. @@ -124,6 +149,7 @@ typedef enum vartag_external VARTAG_INDIRECT = 1, VARTAG_EXPANDED_RO = 2, VARTAG_EXPANDED_RW = 3, + VARTAG_LONG_EXTERNAL = 4, VARTAG_ONDISK = 18 } vartag_external; @@ -135,6 +161,7 @@ typedef enum vartag_external ((tag) == VARTAG_INDIRECT ? sizeof(varatt_indirect) : \ VARTAG_IS_EXPANDED(tag) ? sizeof(varatt_expanded) : \ (tag) == VARTAG_ONDISK ? sizeof(varatt_external) : \ + (tag) == VARTAG_LONG_EXTERNAL ? sizeof(varatt_long_external) : \ (AssertMacro(false), 0)) /* @@ -313,6 +340,13 @@ typedef struct * Other macros here should usually be used only by tuple assembly/disassembly * code and code that specifically wants to work with still-toasted Datums. */ +/* , datalen) (datalen)*/ +/* (datalen) + (datalen) */ +/* #define VARATT_LONG_SIZE ((Size) offsetof(varatt_long_external, inline_tail_data)) */ + +#define VARATT_LONG_SIZE ((Size) sizeof(varatt_long_external)) +#define VARSIZE_LONG_EXTERNAL(PTR) ((Size) VARHDRSZ_EXTERNAL + VARATT_LONG_SIZE) + #define VARDATA(PTR) VARDATA_4B(PTR) #define VARSIZE(PTR) VARSIZE_4B(PTR) @@ -320,7 +354,10 @@ typedef struct #define VARDATA_SHORT(PTR) VARDATA_1B(PTR) #define VARTAG_EXTERNAL(PTR) VARTAG_1B_E(PTR) -#define VARSIZE_EXTERNAL(PTR) (VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR))) +#define VARSIZE_EXTERNAL(PTR) (VARATT_IS_LONG_EXTERNAL(PTR) ? \ + VARSIZE_LONG_EXTERNAL(PTR) : \ + VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR))) + #define VARDATA_EXTERNAL(PTR) VARDATA_1B_E(PTR) #define VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR) @@ -339,6 +376,8 @@ typedef struct (VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR))) #define VARATT_IS_SHORT(PTR) VARATT_IS_1B(PTR) #define VARATT_IS_EXTENDED(PTR) (!VARATT_IS_4B_U(PTR)) +#define VARATT_IS_LONG_EXTERNAL(PTR) \ + (VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_LONG_EXTERNAL) #define SET_VARSIZE(PTR, len) SET_VARSIZE_4B(PTR, len) #define SET_VARSIZE_SHORT(PTR, len) SET_VARSIZE_1B(PTR, len) diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index 2977045cc7..fa59a11e70 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -606,7 +606,7 @@ make_tuple_indirect(PG_FUNCTION_ARGS) continue; /* copy datum, so it still lives later */ - if (VARATT_IS_EXTERNAL_ONDISK(attr)) + if (VARATT_IS_EXTERNAL_ONDISK(attr) || VARATT_IS_LONG_EXTERNAL(attr)) attr = detoast_external_attr(attr); else { -- 2.25.1 From 59444b9be33dc71bb3434f1d7517272c7802d098 Mon Sep 17 00:00:00 2001 From: Nikita Malakhov Date: Wed, 7 Dec 2022 13:25:42 +0300 Subject: [PATCH 2/2] 64-bit TOAST value ID - removed NOTICE message that failed lots of tests and returned initdb to original state. --- src/backend/access/heap/heaptoast.c | 1 - src/bin/initdb/initdb.c | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index d5d0042744..ded527c6d7 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -655,7 +655,6 @@ heap_fetch_toast_slice(Relation toastrel, uint64 valueid, int32 attrsize, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(valueid)); */ - elog(NOTICE, "heap_fetch_toast_slice rel %u val %ld", toastrel->rd_id, valueid); ScanKeyInit(&toastkey[0], (AttrNumber) 1, BTEqualStrategyNumber, F_INT8EQ, diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 880035f8a0..60ecdd334b 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2682,17 +2682,17 @@ initialize_data_directory(void) */ fputs(_("performing post-bootstrap initialization ... "), stdout); fflush(stdout); - +/* snprintf(cmd, sizeof(cmd), "\"%s\" %s %s template1 %s", backend_exec, backend_options, extra_options, debug ? "-d 5" : ""); -/* +*/ snprintf(cmd, sizeof(cmd), "\"%s\" %s %s template1 >%s", backend_exec, backend_options, extra_options, DEVNULL); -*/ + PG_CMD_OPEN; setup_auth(cmdfd); -- 2.25.1