From 06f7cc7dba4b7b69ce91f05ab2527ac79dd5289c Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Fri, 5 Jan 2024 15:39:43 -0500
Subject: [PATCH v3 16/28] feature: Calculate all blocksize constants in a
 common location

These variables are calculated once on server startup or in front-end code which
utilizes these constants.

This adds the BlockSizeInit() and BlockSizeInitControl() routines to handle the
bootstrapping of the cluster constants depending on if we have a control file or
not.

Also move the declaration of ReservedPageSize variable into this module, since
we need the same sort of initialization if we are using these constants in the
frontend code as well.
---
 src/backend/access/transam/xlog.c  | 12 ++--
 src/backend/storage/page/bufpage.c |  1 -
 src/common/Makefile                |  1 +
 src/common/blocksize.c             | 88 ++++++++++++++++++++++++++++++
 src/common/meson.build             |  1 +
 src/include/common/blocksize.h     | 37 +++++++++++++
 src/include/common/blocksize_int.h | 42 ++++++++++++++
 src/include/storage/bufpage.h      |  6 +-
 8 files changed, 176 insertions(+), 12 deletions(-)
 create mode 100644 src/common/blocksize.c
 create mode 100644 src/include/common/blocksize.h
 create mode 100644 src/include/common/blocksize_int.h

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f9defc70f4..5be782db92 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4082,8 +4082,8 @@ WriteControlFile(void)
 	ControlFile->nameDataLen = NAMEDATALEN;
 	ControlFile->indexMaxKeys = INDEX_MAX_KEYS;
 
-	ControlFile->toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
-	ControlFile->loblksize = LOBLKSIZE;
+	ControlFile->toast_max_chunk_size = ClusterToastMaxChunkSize;
+	ControlFile->loblksize = ClusterLargeObjectBlockSize;
 
 	ControlFile->float8ByVal = FLOAT8PASSBYVAL;
 
@@ -4273,19 +4273,19 @@ ReadControlFile(void)
 						   " but the server was compiled with INDEX_MAX_KEYS %d.",
 						   ControlFile->indexMaxKeys, INDEX_MAX_KEYS),
 				 errhint("It looks like you need to recompile or initdb.")));
-	if (ControlFile->toast_max_chunk_size != TOAST_MAX_CHUNK_SIZE)
+	if (ControlFile->toast_max_chunk_size != ClusterToastMaxChunkSize)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
 				 errdetail("The database cluster was initialized with ClusterToastMaxChunkSize %d,"
 						   " but the server was configured with ClusterToastMaxChunkSize %d.",
 						   ControlFile->toast_max_chunk_size, (int) ClusterToastMaxChunkSize),
 				 errhint("It looks like you need to recompile or initdb.")));
-	if (ControlFile->loblksize != LOBLKSIZE)
+	if (ControlFile->loblksize != ClusterLargeObjectBlockSize)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
 				 errdetail("The database cluster was initialized with LOBLKSIZE %d,"
-						   " but the server was compiled with LOBLKSIZE %d.",
-						   ControlFile->loblksize, (int) LOBLKSIZE),
+						   " but the server was configured with LOBLKSIZE %d.",
+						   ControlFile->loblksize, (int) ClusterLargeObjectBlockSize),
 				 errhint("It looks like you need to recompile or initdb.")));
 
 #ifdef USE_FLOAT8_BYVAL
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index d54651d703..f84343e6ce 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -26,7 +26,6 @@
 /* GUC variable */
 bool		ignore_checksum_failure = false;
 
-int ReservedPageSize = 0;
 /* ----------------------------------------------------------------
  *						Page support functions
  * ----------------------------------------------------------------
diff --git a/src/common/Makefile b/src/common/Makefile
index 2ba5069dca..ca63835382 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -48,6 +48,7 @@ OBJS_COMMON = \
 	base64.o \
 	binaryheap.o \
 	blkreftable.o \
+	blocksize.o \
 	checksum_helper.o \
 	compression.o \
 	config_info.o \
diff --git a/src/common/blocksize.c b/src/common/blocksize.c
new file mode 100644
index 0000000000..1dc3e0bbe0
--- /dev/null
+++ b/src/common/blocksize.c
@@ -0,0 +1,88 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.c
+ *		This file contains methods to calculate blocksize-related variables
+ *
+ *
+ * Copyright (c) 2024, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/common/blocksize.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "common/blocksize_int.h"
+#include "access/heaptoast.h"
+#include "access/htup_details.h"
+#include "access/itup.h"
+#include "access/nbtree_int.h"
+#include "common/controldata_utils.h"
+#include "storage/large_object.h"
+
+/* These variables are effectively constants, but are initialized by BlockSizeInit() */
+
+Size ReservedPageSize;
+
+Size ClusterMaxTIDsPerBTreePage = 0;
+Size ClusterLargeObjectBlockSize = 0;
+Size ClusterMaxHeapTupleSize = 0;
+Size ClusterMaxHeapTuplesPerPage = 0;
+Size ClusterMaxIndexTuplesPerPage = 0;
+Size ClusterToastMaxChunkSize = 0;
+
+/*
+ * This routine will calculate and cache the necessary constants. This should
+ * be called once very very early in the process (as soon as the native block
+ * size is known, so after reading ControlFile, or using BlockSizeInitControl).
+ */
+
+static bool initialized = false;
+
+void
+BlockSizeInit(Size rawblocksize, Size reservedsize)
+{
+	Assert(rawblocksize == BLCKSZ);
+	Assert(IsValidReservedPageSize(reservedsize));
+
+	if (initialized)
+		return;
+
+	ReservedPageSize = reservedsize;
+	ClusterMaxTIDsPerBTreePage = CalcMaxTIDsPerBTreePage(PageUsableSpace);
+	ClusterLargeObjectBlockSize = LOBLKSIZE;		 /* TODO: calculate? */
+	ClusterMaxHeapTupleSize = CalcMaxHeapTupleSize(PageUsableSpace);
+	ClusterMaxHeapTuplesPerPage = CalcMaxHeapTuplesPerPage(PageUsableSpace);
+	ClusterMaxIndexTuplesPerPage = CalcMaxIndexTuplesPerPage(PageUsableSpace);
+	ClusterToastMaxChunkSize = CalcToastMaxChunkSize(PageUsableSpace);
+
+	initialized = true;
+}
+
+/*
+ * Init the BlockSize using values from the given control file pointer.  If
+ * this is nil, then load and use the control file pointed to by the pgdata
+ * path and perform said operations.
+ */
+void
+BlockSizeInitControl(ControlFileData *control, const char *pgdata)
+{
+	bool crc_ok = true;
+
+	Assert(pgdata);
+
+	if (!control)
+		control = get_controlfile(pgdata, &crc_ok);
+
+	Assert(crc_ok);
+
+	if (control)
+	{
+		BlockSizeInit(control->blcksz, control->reserved_page_size);
+		return;
+	}
+
+	/* panic */
+}
diff --git a/src/common/meson.build b/src/common/meson.build
index 4eb16024cb..5369fd2960 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -5,6 +5,7 @@ common_sources = files(
   'base64.c',
   'binaryheap.c',
   'blkreftable.c',
+  'blocksize.c',
   'checksum_helper.c',
   'compression.c',
   'controldata_utils.c',
diff --git a/src/include/common/blocksize.h b/src/include/common/blocksize.h
new file mode 100644
index 0000000000..ff9e0cd3d6
--- /dev/null
+++ b/src/include/common/blocksize.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.h
+ *	  definitions for cluster-specific limits/structure defs
+ *
+ *
+ * Copyright (c) 2024, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION: src/include/common/blocksize.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef BLOCKSIZE_H
+#define BLOCKSIZE_H
+
+#include "catalog/pg_control.h"
+
+void BlockSizeInit(Size rawblocksize, Size reservedsize);
+void BlockSizeInitControl(ControlFileData *ControlFile, const char *DataDir);
+
+/* These constants are initialized at runtime but are effective constants for callers */
+
+const extern PGDLLIMPORT Size ReservedPageSize;
+const extern PGDLLIMPORT Size ClusterMaxTIDsPerBTreePage;
+const extern PGDLLIMPORT Size ClusterLargeObjectBlockSize;
+const extern PGDLLIMPORT Size ClusterMaxHeapTupleSize;
+const extern PGDLLIMPORT Size ClusterMaxHeapTuplesPerPage;
+const extern PGDLLIMPORT Size ClusterMaxIndexTuplesPerPage;
+const extern PGDLLIMPORT Size ClusterToastMaxChunkSize;
+
+#define MaxReservedPageSize 256
+
+/* between 0 and MaxReservedPageSize and multiple of 8 */
+#define IsValidReservedPageSize(s) ((s) >= 0 && (s) <= MaxReservedPageSize && (((s)&0x7) == 0))
+
+#endif
diff --git a/src/include/common/blocksize_int.h b/src/include/common/blocksize_int.h
new file mode 100644
index 0000000000..a7213878f7
--- /dev/null
+++ b/src/include/common/blocksize_int.h
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize_int.h
+ *	  internal defintions for cluster-specific limits/structure defs
+ *
+ * Copyright (c) 2024, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION: src/include/common/blocksize_int.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * Note: We use the same identifier here as in blocksize.h, due to blocksize.c
+ * (the only consumer of this header file) needing to see these definitions;
+ * other subsequent header files will pull in blocksize.h, so without using
+ * the same symbol you get conflicting defintion errors.
+ */
+
+#ifndef BLOCKSIZE_H
+#define BLOCKSIZE_H
+
+/* forward declaration */
+typedef struct ControlFileData ControlFileData;
+
+void BlockSizeInit(Size rawblocksize, Size reservedsize);
+void BlockSizeInitControl(ControlFileData *ControlFile, const char *DataDir);
+
+extern PGDLLIMPORT Size ReservedPageSize;
+extern PGDLLIMPORT Size ClusterMaxTIDsPerBTreePage;
+extern PGDLLIMPORT Size ClusterLargeObjectBlockSize;
+extern PGDLLIMPORT Size ClusterMaxHeapTupleSize;
+extern PGDLLIMPORT Size ClusterMaxHeapTuplesPerPage;
+extern PGDLLIMPORT Size ClusterMaxIndexTuplesPerPage;
+extern PGDLLIMPORT Size ClusterToastMaxChunkSize;
+
+#define MaxReservedPageSize 256
+
+/* between 0 and MaxReservedPageSize and multiple of 8 */
+#define IsValidReservedPageSize(s) ((s) >= 0 && (s) <= MaxReservedPageSize && (((s)&0x7) == 0))
+
+#endif
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index c21bb0d86f..a233049b70 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -15,6 +15,7 @@
 #define BUFPAGE_H
 
 #include "access/xlogdefs.h"
+#include "common/blocksize.h"
 #include "storage/block.h"
 #include "storage/item.h"
 #include "storage/off.h"
@@ -213,11 +214,6 @@ typedef PageHeaderData *PageHeader;
  */
 #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
 
-/*
- * how much space is left after smgr's bookkeeping, etc; should be MAXALIGN
- */
-extern int ReservedPageSize;
-
 /* ignore page usable space */
 #define PageUsableSpaceMax (BLCKSZ - SizeOfPageHeaderData)
 #define PageUsableSpace (BLCKSZ - SizeOfPageHeaderData - ReservedPageSize)
-- 
2.40.1

