From 5d2cb6535cb0fe698552baf2f2d15ba3f4a227d1 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
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<<i))
+			size += feature_descs[i].length;
+
+	return MAXALIGN(size);
+}
+
+
+/* does a specific page have a feature? */
+static inline bool PageHasFeature(Page page, PageFeature feature)
+{
+	Assert(feature >= 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<<feature);
+}
+
+
+/*
+ * Get the page offset for the given feature given the page, flags, and
+ * feature id.  Returns NULL if the feature is not enabled.
+ */
+
+char *
+PageGetFeatureOffset(Page page, PageFeature feature_id)
+{
+	uint16 size = 0;
+	int i;
+	PageFeatureSet enabled_features;
+
+	Assert(page != NULL);
+
+	/* short circuit if page does not have extended features or is not using
+	 * this specific feature */
+
+	if (!PageHasFeature(page, feature_id))
+		return (char*)0;
+
+	enabled_features = ((PGAlignedBlock*)page)->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<<i))
+			size += feature_descs[i].length;
+
+	/* size is now the offset from the start of the reserved page space */
+	return (char*)((char *)page + BLCKSZ - reserved_page_size + size);
+}
+
+/* expose the given feature flags as boolean yes/no GUCs */
+void
+SetExtendedFeatureConfigOptions(PageFeatureSet features)
+{
+#ifndef FRONTEND
+	int i;
+
+	for (i = 0; i < PF_MAX_FEATURE; i++)
+		SetConfigOption(feature_descs[i].guc_name, (features & (1<<i)) ? "yes" : "no",
+						PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT);
+#endif
+	cluster_page_features = features;
+}
+
+/* add a named feature to the feature set */
+PageFeatureSet
+PageFeatureSetAddFeatureByName(PageFeatureSet features, const char *feat_name)
+{
+	int i;
+
+	for (i = 0; i < PF_MAX_FEATURE; i++)
+		if (!strcmp(feat_name, feature_descs[i].guc_name))
+			return features | (1<<i);
+	return features;
+}
+
+/* add feature to the feature set by identifier */
+PageFeatureSet
+PageFeatureSetAddFeature(PageFeatureSet features, PageFeature feature)
+{
+	Assert(feature >= 0 && feature < PF_MAX_FEATURE);
+	return features | (1<<feature);
+}
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 6bd2b8f3c2..e040d54fdd 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -19,6 +19,7 @@
 #include "access/tupdesc.h"
 #include "access/tupmacs.h"
 #include "storage/bufpage.h"
+#include "common/pagefeat.h"
 
 /*
  * MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 06368e2366..c6559a377b 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -19,7 +19,7 @@
 #include "access/xlogdefs.h"
 #include "pgtime.h"				/* for pg_time_t */
 #include "port/pg_crc32c.h"
-
+#include "common/pagefeat.h"
 
 /* Version identifier for this pg_control format */
 #define PG_CONTROL_VERSION	1300
@@ -219,6 +219,9 @@ typedef struct ControlFileData
 	/* Are data pages protected by checksums? Zero if no checksum version */
 	uint32		data_checksum_version;
 
+	/* What extended page features are we using? */
+	PageFeatureSet page_features;
+
 	/*
 	 * Random nonce, used in authentication requests that need to proceed
 	 * based on values that are cluster-unique, like a SASL exchange that
diff --git a/src/include/common/pagefeat.h b/src/include/common/pagefeat.h
new file mode 100644
index 0000000000..cf0f04ecdb
--- /dev/null
+++ b/src/include/common/pagefeat.h
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pagefeat.h
+ *	  POSTGRES page feature support
+ *
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/common/pagefeat.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PAGEFEAT_H
+#define PAGEFEAT_H
+
+/* revealed for GUCs */
+extern int reserved_page_size;
+
+/* forward declaration to avoid circular includes */
+typedef Pointer Page;
+typedef uint8 PageFeatureSet;
+
+extern PageFeatureSet cluster_page_features;
+
+#define SizeOfPageReservedSpace() reserved_page_size
+
+/* bit offset for features flags */
+typedef enum {
+	PF_MAX_FEATURE /* must be last */
+} PageFeature;
+
+/* Limit for total number of features we will support.  Since we are storing a
+ * single status byte, we are reserving the top bit here to be set to indicate
+ * for whether there are more than 7 features; used for future extensibility.
+ * This should not be increased as part of normal feature development, only
+ * when adding said mechanisms */
+
+#define PF_MAX_POSSIBLE_FEATURE_CUTOFF 7
+
+StaticAssertDecl(PF_MAX_FEATURE <= PF_MAX_POSSIBLE_FEATURE_CUTOFF,
+				 "defined more features than will fit in one byte");
+
+/* prototypes */
+void SetExtendedFeatureConfigOptions(PageFeatureSet features);
+uint16 PageFeatureSetCalculateSize(PageFeatureSet features);
+PageFeatureSet PageFeatureSetAddFeatureByName(PageFeatureSet features, const char *feat_name);
+PageFeatureSet PageFeatureSetAddFeature(PageFeatureSet features, PageFeature feature);
+
+/* macros dealing with the current cluster's page features */
+char *PageGetFeatureOffset(Page page, PageFeature feature);
+#define PageFeatureSetHasFeature(fs,f) (fs&(1<<f))
+#define ClusterPageFeatureInit(features) cluster_page_features = features;
+
+#endif							/* PAGEFEAT_H */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index e1bd22441b..853ebc7c93 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -17,6 +17,7 @@
 #include "storage/block.h"
 #include "storage/buf.h"
 #include "storage/bufpage.h"
+#include "common/pagefeat.h"
 #include "storage/relfilelocator.h"
 #include "utils/relcache.h"
 #include "utils/snapmgr.h"
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index ea0dfc2645..9afd2a74e4 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -18,6 +18,7 @@
 #include "storage/block.h"
 #include "storage/item.h"
 #include "storage/off.h"
+#include "common/pagefeat.h"
 
 extern int reserved_page_size;
 
@@ -194,8 +195,8 @@ typedef PageHeaderData *PageHeader;
 #define PD_PAGE_FULL		0x0002	/* not enough free space for new tuple? */
 #define PD_ALL_VISIBLE		0x0004	/* all tuples on page are visible to
 									 * everyone */
-
-#define PD_VALID_FLAG_BITS	0x0007	/* OR of all valid pd_flags bits */
+#define PD_EXTENDED_FEATS	0x0008	/* this page uses extended page features */
+#define PD_VALID_FLAG_BITS	0x000F	/* OR of all valid pd_flags bits */
 
 /*
  * Page layout version number 0 is for pre-7.3 Postgres releases.
@@ -311,6 +312,26 @@ PageSetPageSizeAndVersion(Page page, Size size, uint8 version)
 	((PageHeader) page)->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

