From 5d2cb6535cb0fe698552baf2f2d15ba3f4a227d1 Mon Sep 17 00:00:00 2001 From: David Christensen Date: Tue, 1 Nov 2022 17:50:13 -0400 Subject: [PATCH v2 3/5] Add pagefeat, plus new page status bit and page feature byte at trailer --- contrib/bloom/blutils.c | 2 +- src/backend/access/brin/brin_bloom.c | 1 + src/backend/access/brin/brin_pageops.c | 2 +- src/backend/access/gin/ginutil.c | 2 +- src/backend/access/gist/gistutil.c | 2 +- src/backend/access/hash/hashpage.c | 2 +- src/backend/access/heap/heapam.c | 8 +- src/backend/access/heap/hio.c | 4 +- src/backend/access/heap/rewriteheap.c | 2 +- src/backend/access/heap/visibilitymap.c | 4 +- src/backend/access/nbtree/nbtpage.c | 2 +- src/backend/access/spgist/spgutils.c | 2 +- src/backend/access/transam/xlog.c | 10 ++ src/backend/bootstrap/bootstrap.c | 19 ++- src/backend/commands/sequence.c | 4 +- src/backend/storage/freespace/freespace.c | 6 +- src/backend/storage/page/bufpage.c | 22 +++- src/backend/utils/init/globals.c | 3 - src/bin/pg_controldata/pg_controldata.c | 3 + src/common/Makefile | 1 + src/common/pagefeat.c | 143 ++++++++++++++++++++++ src/include/access/htup_details.h | 1 + src/include/catalog/pg_control.h | 5 +- src/include/common/pagefeat.h | 55 +++++++++ src/include/storage/bufmgr.h | 1 + src/include/storage/bufpage.h | 29 ++++- 26 files changed, 300 insertions(+), 35 deletions(-) create mode 100644 src/common/pagefeat.c create mode 100644 src/include/common/pagefeat.h diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c index a434cf93ef..be623b7bee 100644 --- a/contrib/bloom/blutils.c +++ b/contrib/bloom/blutils.c @@ -408,7 +408,7 @@ BloomInitPage(Page page, uint16 flags) { BloomPageOpaque opaque; - PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData)); + PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData)), cluster_page_features); opaque = BloomPageGetOpaque(page); opaque->flags = flags; diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c index fee460fb29..fbff776e75 100644 --- a/src/backend/access/brin/brin_bloom.c +++ b/src/backend/access/brin/brin_bloom.c @@ -125,6 +125,7 @@ #include "access/stratnum.h" #include "catalog/pg_type.h" #include "catalog/pg_amop.h" +#include "common/pagefeat.h" #include "utils/builtins.h" #include "utils/datum.h" #include "utils/lsyscache.h" diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c index f17aad51b6..e179f54c31 100644 --- a/src/backend/access/brin/brin_pageops.c +++ b/src/backend/access/brin/brin_pageops.c @@ -475,7 +475,7 @@ brin_doinsert(Relation idxrel, BlockNumber pagesPerRange, void brin_page_init(Page page, uint16 type) { - PageInit(page, BLCKSZ, sizeof(BrinSpecialSpace)); + PageInit(page, BLCKSZ, sizeof(BrinSpecialSpace), cluster_page_features); BrinPageType(page) = type; } diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 6df7f2eaeb..1516c4fd13 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -345,7 +345,7 @@ GinInitPage(Page page, uint32 f, Size pageSize) { GinPageOpaque opaque; - PageInit(page, pageSize, sizeof(GinPageOpaqueData)); + PageInit(page, pageSize, sizeof(GinPageOpaqueData), cluster_page_features); opaque = GinPageGetOpaque(page); opaque->flags = f; diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 1532462317..5bd0046671 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -758,7 +758,7 @@ gistinitpage(Page page, uint32 f) { GISTPageOpaque opaque; - PageInit(page, BLCKSZ, sizeof(GISTPageOpaqueData)); + PageInit(page, BLCKSZ, sizeof(GISTPageOpaqueData), cluster_page_features); opaque = GistPageGetOpaque(page); opaque->rightlink = InvalidBlockNumber; diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index d2edcd4617..c76de815d0 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -595,7 +595,7 @@ _hash_init_metabuffer(Buffer buf, double num_tuples, RegProcedure procid, void _hash_pageinit(Page page, Size size) { - PageInit(page, size, sizeof(HashPageOpaqueData)); + PageInit(page, size, sizeof(HashPageOpaqueData), cluster_page_features); } /* diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 265ac277ab..7ffae9d9d1 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8890,7 +8890,7 @@ heap_xlog_visible(XLogReaderState *record) /* initialize the page if it was read as zeros */ if (PageIsNew(vmpage)) - PageInit(vmpage, BLCKSZ, 0); + PageInit(vmpage, BLCKSZ, 0, cluster_page_features); /* * XLogReadBufferForRedoExtended locked the buffer. But @@ -9126,7 +9126,7 @@ heap_xlog_insert(XLogReaderState *record) { buffer = XLogInitBufferForRedo(record, 0); page = BufferGetPage(buffer); - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), 0, cluster_page_features); action = BLK_NEEDS_REDO; } else @@ -9250,7 +9250,7 @@ heap_xlog_multi_insert(XLogReaderState *record) { buffer = XLogInitBufferForRedo(record, 0); page = BufferGetPage(buffer); - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), 0, cluster_page_features); action = BLK_NEEDS_REDO; } else @@ -9468,7 +9468,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update) { nbuffer = XLogInitBufferForRedo(record, 0); page = (Page) BufferGetPage(nbuffer); - PageInit(page, BufferGetPageSize(nbuffer), 0); + PageInit(page, BufferGetPageSize(nbuffer), 0, cluster_page_features); newaction = BLK_NEEDS_REDO; } else diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index 4a525fb451..ee823743ac 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -525,7 +525,7 @@ loop: */ if (PageIsNew(page)) { - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), 0, cluster_page_features); MarkBufferDirty(buffer); } @@ -635,7 +635,7 @@ loop: BufferGetBlockNumber(buffer), RelationGetRelationName(relation)); - PageInit(page, BufferGetPageSize(buffer), 0); + PageInit(page, BufferGetPageSize(buffer), 0, cluster_page_features); MarkBufferDirty(buffer); /* diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c index 5b1d8bb184..42ae7e84ed 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -703,7 +703,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup) if (!state->rs_buffer_valid) { /* Initialize a new empty page */ - PageInit(page, BLCKSZ, 0); + PageInit(page, BLCKSZ, 0, cluster_page_features); state->rs_buffer_valid = true; } diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c index d62761728b..8c506b3ebf 100644 --- a/src/backend/access/heap/visibilitymap.c +++ b/src/backend/access/heap/visibilitymap.c @@ -601,7 +601,7 @@ vm_readbuf(Relation rel, BlockNumber blkno, bool extend) { LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); if (PageIsNew(BufferGetPage(buf))) - PageInit(BufferGetPage(buf), BLCKSZ, 0); + PageInit(BufferGetPage(buf), BLCKSZ, 0, cluster_page_features); LockBuffer(buf, BUFFER_LOCK_UNLOCK); } return buf; @@ -618,7 +618,7 @@ vm_extend(Relation rel, BlockNumber vm_nblocks) PGAlignedBlock pg; SMgrRelation reln; - PageInit((Page) pg.data, BLCKSZ, 0); + PageInit((Page) pg.data, BLCKSZ, 0, cluster_page_features); /* * We use the relation extension lock to lock out other backends trying to diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 8b96708b3e..a77e9719c5 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -1140,7 +1140,7 @@ _bt_upgradelockbufcleanup(Relation rel, Buffer buf) void _bt_pageinit(Page page, Size size) { - PageInit(page, size, sizeof(BTPageOpaqueData)); + PageInit(page, size, sizeof(BTPageOpaqueData), cluster_page_features); } /* diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index 2c661fcf96..cd1f1c7006 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -689,7 +689,7 @@ SpGistInitPage(Page page, uint16 f) { SpGistPageOpaque opaque; - PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData)); + PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData), cluster_page_features); opaque = SpGistPageGetOpaque(page); opaque->flags = f; opaque->spgist_page_id = SPGIST_PAGE_ID; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index be54c23187..27953367aa 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -69,6 +69,7 @@ #include "catalog/pg_database.h" #include "common/controldata_utils.h" #include "common/file_utils.h" +#include "common/pagefeat.h" #include "executor/instrument.h" #include "miscadmin.h" #include "pg_trace.h" @@ -89,6 +90,7 @@ #include "storage/ipc.h" #include "storage/large_object.h" #include "storage/latch.h" +#include "common/pagefeat.h" #include "storage/pmsignal.h" #include "storage/predicate.h" #include "storage/proc.h" @@ -109,6 +111,7 @@ #include "utils/varlena.h" extern uint32 bootstrap_data_checksum_version; +extern PageFeatureSet bootstrap_page_features; /* timeline ID to be used when bootstrapping */ #define BootstrapTimeLineID 1 @@ -3898,6 +3901,7 @@ InitControlFile(uint64 sysidentifier) ControlFile->wal_log_hints = wal_log_hints; ControlFile->track_commit_timestamp = track_commit_timestamp; ControlFile->data_checksum_version = bootstrap_data_checksum_version; + ControlFile->page_features = bootstrap_page_features; } static void @@ -4182,9 +4186,15 @@ ReadControlFile(void) CalculateCheckpointSegments(); + /* set our page-level space reservation from ControlFile if any extended feature flags are set*/ + reserved_page_size = PageFeatureSetCalculateSize(ControlFile->page_features); + Assert(reserved_page_size == MAXALIGN(reserved_page_size)); + /* Make the initdb settings visible as GUC variables, too */ SetConfigOption("data_checksums", DataChecksumsEnabled() ? "yes" : "no", PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + + SetExtendedFeatureConfigOptions(ControlFile->page_features); } /* diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 661ebacb0c..959771b1b1 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -46,7 +46,7 @@ #include "utils/relmapper.h" uint32 bootstrap_data_checksum_version = 0; /* No checksum */ - +PageFeatureSet bootstrap_page_features = 0; /* No special features */ static void CheckerModeMain(void); static void bootstrap_signals(void); @@ -221,7 +221,7 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) argv++; argc--; - while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:X:-:")) != -1) + while ((flag = getopt(argc, argv, "B:c:d:D:e:Fkr:X:-:")) != -1) { switch (flag) { @@ -244,6 +244,19 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) pfree(debugstr); } break; + case 'e': + { + /* enable specific features */ + PageFeatureSet features_tmp; + + features_tmp = PageFeatureSetAddFeatureByName(bootstrap_page_features, optarg); + if (features_tmp == bootstrap_page_features) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Unrecognized page feature requested: \"%s\"", optarg))); + bootstrap_page_features = features_tmp; + } + break; case 'F': SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); break; @@ -299,6 +312,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) } } + ClusterPageFeatureInit(bootstrap_page_features); + if (argc != optind) { write_stderr("%s: invalid command-line arguments\n", progname); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 99c9f91cba..06ec51d8bd 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -382,7 +382,7 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum) page = BufferGetPage(buf); - PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic)); + PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic), cluster_page_features); sm = (sequence_magic *) PageGetSpecialPointer(page); sm->magic = SEQ_MAGIC; @@ -1871,7 +1871,7 @@ seq_redo(XLogReaderState *record) */ localpage = (Page) palloc(BufferGetPageSize(buffer)); - PageInit(localpage, BufferGetPageSize(buffer), sizeof(sequence_magic)); + PageInit(localpage, BufferGetPageSize(buffer), sizeof(sequence_magic), cluster_page_features); sm = (sequence_magic *) PageGetSpecialPointer(localpage); sm->magic = SEQ_MAGIC; diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c index b880f059fe..b12bd1cc01 100644 --- a/src/backend/storage/freespace/freespace.c +++ b/src/backend/storage/freespace/freespace.c @@ -217,7 +217,7 @@ XLogRecordPageWithFreeSpace(RelFileLocator rlocator, BlockNumber heapBlk, page = BufferGetPage(buf); if (PageIsNew(page)) - PageInit(page, BLCKSZ, 0); + PageInit(page, BLCKSZ, 0, cluster_page_features); if (fsm_set_avail(page, slot, new_cat)) MarkBufferDirtyHint(buf, false); @@ -593,7 +593,7 @@ fsm_readbuf(Relation rel, FSMAddress addr, bool extend) { LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); if (PageIsNew(BufferGetPage(buf))) - PageInit(BufferGetPage(buf), BLCKSZ, 0); + PageInit(BufferGetPage(buf), BLCKSZ, 0, cluster_page_features); LockBuffer(buf, BUFFER_LOCK_UNLOCK); } return buf; @@ -611,7 +611,7 @@ fsm_extend(Relation rel, BlockNumber fsm_nblocks) PGAlignedBlock pg; SMgrRelation reln; - PageInit((Page) pg.data, BLCKSZ, 0); + PageInit((Page) pg.data, BLCKSZ, 0, cluster_page_features); /* * We use the relation extension lock to lock out other backends trying to diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c index cf4b9a3bfa..0433aade03 100644 --- a/src/backend/storage/page/bufpage.c +++ b/src/backend/storage/page/bufpage.c @@ -39,11 +39,18 @@ bool ignore_checksum_failure = false; * until it's time to write. */ void -PageInit(Page page, Size pageSize, Size specialSize) +PageInit(Page page, Size pageSize, Size specialSize, PageFeatureSet features) { PageHeader p = (PageHeader) page; - specialSize = MAXALIGN(specialSize) + reserved_page_size; + specialSize = MAXALIGN(specialSize); + + if (features) + { + Size reserved_size = PageFeatureSetCalculateSize(features); + Assert(reserved_size == MAXALIGN(reserved_size)); + specialSize += reserved_size; + } Assert(pageSize == BLCKSZ); Assert(pageSize > specialSize + SizeOfPageHeaderData); @@ -51,7 +58,14 @@ PageInit(Page page, Size pageSize, Size specialSize) /* Make sure all fields of page are zero, as well as unused space */ MemSet(p, 0, pageSize); - p->pd_flags = 0; + if (features) + { + /* initialize our page trailer with a copy of the cluster flags */ + p->pd_flags = PD_EXTENDED_FEATS; + ((PGAlignedBlock*)page)->data[BLCKSZ - 1] = (uint8)features; + } + else + p->pd_flags = 0; /* redundant w/MemSet? */ p->pd_lower = SizeOfPageHeaderData; p->pd_upper = pageSize - specialSize; p->pd_special = pageSize - specialSize; @@ -407,7 +421,7 @@ PageGetTempPageCopySpecial(Page page) pageSize = PageGetPageSize(page); temp = (Page) palloc(pageSize); - PageInit(temp, pageSize, PageGetSpecialSize(page)); + PageInit(temp, pageSize, PageGetSpecialSize(page), PageGetPageFeatures(page)); memcpy(PageGetSpecialPointer(temp), PageGetSpecialPointer(page), PageGetSpecialSize(page)); diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index 879c054ae0..00bceec8fa 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -151,6 +151,3 @@ int64 VacuumPageDirty = 0; int VacuumCostBalance = 0; /* working state for vacuum */ bool VacuumCostActive = false; - -int reserved_page_size = 0; /* how much page space to reserve for extended unencrypted metadata */ - diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index c390ec51ce..c1006ad5d8 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -26,6 +26,7 @@ #include "catalog/pg_control.h" #include "common/controldata_utils.h" #include "common/logging.h" +#include "common/pagefeat.h" #include "getopt_long.h" #include "pg_getopt.h" @@ -328,5 +329,7 @@ main(int argc, char *argv[]) ControlFile->data_checksum_version); printf(_("Mock authentication nonce: %s\n"), mock_auth_nonce_str); + printf(_("Reserved page size for features: %d\n"), + PageFeatureSetCalculateSize(ControlFile->page_features)); return 0; } diff --git a/src/common/Makefile b/src/common/Makefile index e9af7346c9..79ffa4dc9a 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -65,6 +65,7 @@ OBJS_COMMON = \ kwlookup.o \ link-canary.o \ md5_common.o \ + pagefeat.o \ pg_get_line.o \ pg_lzcompress.o \ pg_prng.o \ diff --git a/src/common/pagefeat.c b/src/common/pagefeat.c new file mode 100644 index 0000000000..75d5bffce2 --- /dev/null +++ b/src/common/pagefeat.c @@ -0,0 +1,143 @@ +/*------------------------------------------------------------------------- + * + * pagefeat.c + * POSTGRES optional page features + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/common/pagefeat.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "common/pagefeat.h" +#include "utils/guc.h" + +/* global variables */ +int reserved_page_size; +PageFeatureSet cluster_page_features; + +/* + * A "page feature" is an optional cluster-defined additional data field that + * is stored in the "reserved_page_size" area in the footer of a given Page. + * These features are set at initdb time and are static for the life of the cluster. + * + * Page features are identified by flags, each corresponding to a blob of data + * with a fixed length and content. For a given cluster, these features will + * globally exist or not, and can be queried for feature existence. You can + * also get the data/length for a given feature using accessors. + */ + +typedef struct PageFeatureDesc +{ + uint16 length; + char *guc_name; +} PageFeatureDesc; + +/* These are the fixed widths for each feature type, indexed by feature. This + * is also used to lookup page features by the bootstrap process and expose + * the state of this page feature as a readonly boolean GUC, so when adding a + * named feature here ensure you also update the guc_tables file to add this, + * or the attempt to set the GUC will fail. */ + +static PageFeatureDesc feature_descs[PF_MAX_FEATURE] = { +}; + + +/* Return the size for a given set of feature flags */ +uint16 +PageFeatureSetCalculateSize(PageFeatureSet features) +{ + uint16 size = 1; /* trailer byte */ + int i; + + if (!features) + return 0; + + for (i = 0; i < PF_MAX_FEATURE; i++) + if (features & (1<= 0 && feature < PF_MAX_FEATURE); + + /* We are checking out extended feat flag, and if set, checking the + * trailer byte for presence of said feature. */ + return ((PageHeader) page)->pd_flags & PD_EXTENDED_FEATS && \ + ((PGAlignedBlock*)page)->data[BLCKSZ - 1] & (1<data[BLCKSZ - 1]; + + /* we need to find the offsets of all previous features to skip */ + for (i = PF_MAX_FEATURE; i > feature_id; i--) + if (enabled_features & (1<= 0 && feature < PF_MAX_FEATURE); + return features | (1<pd_pagesize_version = size | version; } + +/* + * Return any extended page features set on the page. + */ +static inline PageFeatureSet PageGetPageFeatures(Page page) +{ + return ((PageHeader) page)->pd_flags & PD_EXTENDED_FEATS \ + ? (PageFeatureSet)(((PGAlignedBlock*)page)->data[BLCKSZ - 1]) + : 0; +} + +/* + * Return the size of space allocated for page features. + */ +static inline Size +PageGetFeatureSize(Page page) +{ + return PageFeatureSetCalculateSize(PageGetPageFeatures(page)); +} + /* ---------------- * page special data functions * ---------------- @@ -322,7 +343,7 @@ PageSetPageSizeAndVersion(Page page, Size size, uint8 version) static inline uint16 PageGetSpecialSize(Page page) { - return (PageGetPageSize(page) - ((PageHeader) page)->pd_special - reserved_page_size); + return (PageGetPageSize(page) - ((PageHeader) page)->pd_special - PageGetFeatureSize(page)); } /* @@ -494,7 +515,7 @@ do { \ StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)), "BLCKSZ has to be a multiple of sizeof(size_t)"); -extern void PageInit(Page page, Size pageSize, Size specialSize); +extern void PageInit(Page page, Size pageSize, Size specialSize, PageFeatureSet features); extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags); extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size, OffsetNumber offsetNumber, int flags); -- 2.31.1