Initdb-time block size specification

Started by David Christensenover 2 years ago45 messages
#1David Christensen
david.christensen@crunchydata.com
1 attachment(s)

Hi,

As discussed somewhat at PgCon, enclosed is v1 of a patch to provide
variable block sizes; basically instead of BLCKSZ being a compile-time
constant, a single set of binaries can support all of the block sizes
Pg can support, using the value stored in pg_control as the basis.
(Possible future plans would be to make this something even more
dynamic, such as configured per tablespace, but this is out of scope;
this just sets up the infrastructure for this.)

Whereas we had traditionally used BLCKSZ to indicate the compile-time selected
block size, this commit adjusted things so the cluster block size can be
selected at initdb time.

In order to code for this, we introduce a few new defines:

- CLUSTER_BLOCK_SIZE is the blocksize for this cluster itself. This is not
valid until BlockSizeInit() has been called in the given backend, which we do as
early as possible by parsing the ControlFile and using the blcksz field.

- MIN_BLOCK_SIZE and MAX_BLOCK_SIZE are the limits for the selectable block
size. It is required that CLUSTER_BLOCK_SIZE is a power of 2 between these two
constants.

- DEFAULT_BLOCK_SIZE is the moral equivalent of BLCKSZ; it is the built-in
default value. This is used in a few places that just needed a buffer of an
arbitrary size, but the dynamic value CLUSTER_BLOCK_SIZE should almost always be
used instead.

- CLUSTER_RELSEG_SIZE is used instead of RELSEG_SIZE, since internally we are
storing the segment size in terms of number of blocks. RELSEG_SIZE is still
kept, but is used in terms of the number of blocks of DEFAULT_BLOCK_SIZE;
CLUSTER_RELSEG_SIZE scales appropriately (and is the only thing used internally)
to keep the same target total segment size regardless of block size.

This patch uses a precalculated table to store the block size itself, as well as
additional derived values that have traditionally been compile-time
constants (example: MaxHeapTuplesPerPage). The traditional macro names are kept
so code that doesn't care about it should not need to change, however the
definition of these has changed (see the CalcXXX() routines in blocksize.h for
details).

A new function, BlockSizeInit() populates the appropriate values based on the
target block size. This should be called as early as possible in any code that
utilizes block sizes. This patch adds this in the appropriate place on the
handful of src/bin/ programs that used BLCKSZ, so this caveat mainly impacts new
code.

Code which had previously used BLCKZ should likely be able to get away with
changing these instances to CLUSTER_BLOCK_SIZE, unless you're using a structure
allocated on the stack. In these cases, the compiler will complain about
dynamic structure. The solution is to utilize an expression with MAX_BLOCK_SIZE
instead of BLCKSZ, ensuring enough stack space is allocated for the maximum
size. This also does require using CLUSTER_BLOCK_SIZE or an expression based on
it when actually using this structure, so in practice more stack space may be
allocated then used in principal; as long as there is plenty of stack this
should have no specific impacts on code.

Initial (basic) performance testing shows only minor changes with the pgbench -S
benchmark, though this is obviously an area that will need considerable
testing/verification across multiple workloads.

Thanks,

David

Attachments:

v1-0001-Introduce-initdb-selectable-block-sizes.patchapplication/octet-stream; name=v1-0001-Introduce-initdb-selectable-block-sizes.patchDownload
From b325d524c91be5860154516bb8b4ea2801e89552 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Thu, 1 Jun 2023 11:34:14 -0400
Subject: [PATCH v1] Introduce initdb-selectable block sizes

Whereas we had traditionally used BLCKSZ to indicate the compile-time selected
block size, this commit adjusted things so the cluster block size can be
selected at initdb time.

In order to code for this, we introduce a few new defines:

- CLUSTER_BLOCK_SIZE is the blocksize for this cluster itself.  This is not
valid until BlockSizeInit() has been called in the given backend, which we do as
early as possible by parsing the ControlFile and using the blcksz field.

- MIN_BLOCK_SIZE and MAX_BLOCK_SIZE are the limits for the selectable block
size. It is required that CLUSTER_BLOCK_SIZE is a power of 2 between these two
constants.

- DEFAULT_BLOCK_SIZE is the moral equivalent of BLCKSZ; it is the built-in
default value.  This is used in a few places that just needed a buffer of an
arbitrary size, but the dynamic value CLUSTER_BLOCK_SIZE should almost always be
used instead.

- CLUSTER_RELSEG_SIZE is used instead of RELSEG_SIZE, since internally we are
storing the segment size in terms of number of blocks.  RELSEG_SIZE is still
kept, but is used in terms of the number of blocks of DEFAULT_BLOCK_SIZE;
CLUSTER_RELSEG_SIZE scales appropriately (and is the only thing used internally)
to keep the same target total segment size regardless of block size.

This patch uses a precalculated table to store the block size itself, as well as
additional derived values that have traditionally been compile-time
constants (example: MaxHeapTuplesPerPage).  The traditional macro names are kept
so code that doesn't care about it should not need to change, however the
definition of these has changed (see the CalcXXX() routines in blocksize.h for
details).

A new function, BlockSizeInit() populates the appropriate values based on the
target block size.  This should be called as early as possible in any code that
utilizes block sizes.  This patch adds this in the appropriate place on the
handful of src/bin/ programs that used BLCKSZ, so this caveat mainly impacts new
code.

Code which had previously used BLCKZ should likely be able to get away with
changing these instances to CLUSTER_BLOCK_SIZE, unless you're using a structure
allocated on the stack.  In these cases, the compiler will complain about
dynamic structure.  The solution is to utilize an expression with MAX_BLOCK_SIZE
instead of BLCKSZ, ensuring enough stack space is allocated for the maximum
size. This also does require using CLUSTER_BLOCK_SIZE or an expression based on
it when actually using this structure, so in practice more stack space may be
allocated then used in principal; as long as there is plenty of stack this
should have no specific impacts on code.

Initial (basic) performance testing shows only minor changes with the pgbench -S
benchmark, though this is obviously an area that will need considerable
testing/verification across multiple workloads.
---
 configure                                   |  14 +-
 configure.ac                                |  25 +--
 contrib/amcheck/verify_heapam.c             |  14 +-
 contrib/amcheck/verify_nbtree.c             |   6 +-
 contrib/bloom/blinsert.c                    |   4 +-
 contrib/bloom/bloom.h                       |  23 +-
 contrib/bloom/blutils.c                     |   6 +-
 contrib/bloom/blvacuum.c                    |   2 +-
 contrib/file_fdw/file_fdw.c                 |   6 +-
 contrib/pageinspect/btreefuncs.c            |   6 +-
 contrib/pageinspect/expected/checksum.out   |   2 +-
 contrib/pageinspect/expected/checksum_1.out |   2 +-
 contrib/pageinspect/rawpage.c               |  12 +-
 contrib/pageinspect/sql/checksum.sql        |   2 +-
 contrib/pg_surgery/heap_surgery.c           |   2 +-
 contrib/pg_walinspect/pg_walinspect.c       |   6 +-
 contrib/pgstattuple/pgstatapprox.c          |   6 +-
 contrib/pgstattuple/pgstatindex.c           |   4 +-
 contrib/pgstattuple/pgstattuple.c           |  10 +-
 contrib/postgres_fdw/deparse.c              |   2 +-
 contrib/postgres_fdw/postgres_fdw.c         |   2 +-
 doc/src/sgml/catalogs.sgml                  |   4 +-
 doc/src/sgml/config.sgml                    |  39 ++--
 doc/src/sgml/limits.sgml                    |   2 +-
 doc/src/sgml/monitoring.sgml                |   4 +-
 doc/src/sgml/pgfreespacemap.sgml            |   3 +-
 doc/src/sgml/ref/initdb.sgml                |  16 ++
 doc/src/sgml/ref/pg_resetwal.sgml           |  19 ++
 meson.build                                 |  11 +-
 src/backend/access/brin/brin_bloom.c        |   2 +-
 src/backend/access/brin/brin_pageops.c      |   4 +-
 src/backend/access/common/bufmask.c         |   4 +-
 src/backend/access/common/reloptions.c      |  33 ++-
 src/backend/access/common/syncscan.c        |   2 +-
 src/backend/access/common/toast_internals.c |   2 +-
 src/backend/access/gin/ginbtree.c           |  12 +-
 src/backend/access/gin/gindatapage.c        |  18 +-
 src/backend/access/gin/ginfast.c            |   4 +-
 src/backend/access/gin/ginget.c             |   6 +-
 src/backend/access/gin/ginvacuum.c          |   2 +-
 src/backend/access/gin/ginxlog.c            |   4 +-
 src/backend/access/gist/gist.c              |   2 +-
 src/backend/access/gist/gistbuild.c         |  16 +-
 src/backend/access/gist/gistbuildbuffers.c  |  10 +-
 src/backend/access/gist/gistutil.c          |   2 +-
 src/backend/access/gist/gistvacuum.c        |   6 +-
 src/backend/access/hash/hash.c              |   4 +-
 src/backend/access/hash/hashinsert.c        |   2 +-
 src/backend/access/hash/hashovfl.c          |   8 +-
 src/backend/access/hash/hashpage.c          |   6 +-
 src/backend/access/heap/README.HOT          |   2 +-
 src/backend/access/heap/heapam.c            |  26 +--
 src/backend/access/heap/heapam_handler.c    |   8 +-
 src/backend/access/heap/heaptoast.c         |   2 +-
 src/backend/access/heap/hio.c               |   2 +-
 src/backend/access/heap/pruneheap.c         |  14 +-
 src/backend/access/heap/rewriteheap.c       |   6 +-
 src/backend/access/heap/vacuumlazy.c        |  18 +-
 src/backend/access/heap/visibilitymap.c     |   8 +-
 src/backend/access/nbtree/nbtdedup.c        |   8 +-
 src/backend/access/nbtree/nbtinsert.c       |   2 +-
 src/backend/access/nbtree/nbtpage.c         |  22 +-
 src/backend/access/nbtree/nbtree.c          |  10 +-
 src/backend/access/nbtree/nbtsort.c         |  12 +-
 src/backend/access/nbtree/nbtxlog.c         |   4 +-
 src/backend/access/rmgrdesc/xlogdesc.c      |   2 +-
 src/backend/access/spgist/spgdoinsert.c     |   6 +-
 src/backend/access/spgist/spginsert.c       |   2 +-
 src/backend/access/spgist/spgscan.c         |   4 +-
 src/backend/access/spgist/spgtextproc.c     |  10 +-
 src/backend/access/spgist/spgutils.c        |   4 +-
 src/backend/access/spgist/spgvacuum.c       |  22 +-
 src/backend/access/table/tableam.c          |   2 +-
 src/backend/access/transam/README           |   2 +-
 src/backend/access/transam/clog.c           |   6 +-
 src/backend/access/transam/commit_ts.c      |   6 +-
 src/backend/access/transam/generic_xlog.c   |  18 +-
 src/backend/access/transam/multixact.c      |  12 +-
 src/backend/access/transam/slru.c           |  22 +-
 src/backend/access/transam/subtrans.c       |   4 +-
 src/backend/access/transam/timeline.c       |   2 +-
 src/backend/access/transam/varsup.c         |   2 +-
 src/backend/access/transam/xlog.c           | 132 ++++++++++--
 src/backend/access/transam/xloginsert.c     |  24 +--
 src/backend/access/transam/xlogprefetcher.c |   2 +-
 src/backend/access/transam/xlogreader.c     |  28 +--
 src/backend/access/transam/xlogrecovery.c   |  10 +-
 src/backend/backup/basebackup.c             |  41 ++--
 src/backend/backup/basebackup_lz4.c         |   6 +-
 src/backend/backup/basebackup_zstd.c        |   6 +-
 src/backend/bootstrap/bootstrap.c           |  14 +-
 src/backend/catalog/storage.c               |   2 +-
 src/backend/commands/analyze.c              |   4 +-
 src/backend/commands/async.c                |   6 +-
 src/backend/commands/vacuumparallel.c       |   2 +-
 src/backend/executor/nodeAgg.c              |  16 +-
 src/backend/nodes/tidbitmap.c               |   6 +-
 src/backend/optimizer/path/costsize.c       |  12 +-
 src/backend/optimizer/util/plancat.c        |   2 +-
 src/backend/po/de.po                        |   8 +-
 src/backend/po/es.po                        |   6 +-
 src/backend/po/fr.po                        |   6 +-
 src/backend/po/id.po                        |   4 +-
 src/backend/po/it.po                        |   4 +-
 src/backend/po/ja.po                        |   8 +-
 src/backend/po/ko.po                        |   6 +-
 src/backend/po/pl.po                        |   4 +-
 src/backend/po/pt_BR.po                     |   2 +-
 src/backend/po/ru.po                        |   8 +-
 src/backend/po/sv.po                        |   4 +-
 src/backend/po/tr.po                        |   4 +-
 src/backend/po/uk.po                        |   4 +-
 src/backend/po/zh_CN.po                     |   4 +-
 src/backend/replication/logical/worker.c    |   2 +-
 src/backend/storage/buffer/buf_init.c       |   4 +-
 src/backend/storage/buffer/bufmgr.c         |  22 +-
 src/backend/storage/buffer/freelist.c       |   2 +-
 src/backend/storage/buffer/localbuf.c       |   8 +-
 src/backend/storage/file/buffile.c          |  14 +-
 src/backend/storage/file/copydir.c          |   2 +-
 src/backend/storage/file/fd.c               |   4 +-
 src/backend/storage/freespace/README        |   8 +-
 src/backend/storage/freespace/freespace.c   |  29 ++-
 src/backend/storage/freespace/indexfsm.c    |   6 +-
 src/backend/storage/large_object/inv_api.c  |   4 +-
 src/backend/storage/lmgr/predicate.c        |   2 +-
 src/backend/storage/page/bufpage.c          |  34 +--
 src/backend/storage/smgr/README             |  52 +++++
 src/backend/storage/smgr/md.c               | 126 +++++------
 src/backend/utils/adt/pgstatfuncs.c         |   4 +-
 src/backend/utils/adt/selfuncs.c            |   2 +-
 src/backend/utils/init/miscinit.c           |   6 +-
 src/backend/utils/misc/guc.c                |  27 ++-
 src/backend/utils/misc/guc_tables.c         |  39 +++-
 src/backend/utils/sort/logtape.c            |  48 ++---
 src/backend/utils/sort/sharedtuplestore.c   |  12 +-
 src/backend/utils/sort/tuplesort.c          |   6 +-
 src/bin/initdb/initdb.c                     |  60 ++++--
 src/bin/initdb/meson.build                  |   1 +
 src/bin/initdb/t/002_blocksize.pl           |  24 +++
 src/bin/pg_basebackup/pg_basebackup.c       |   7 +
 src/bin/pg_basebackup/pg_receivewal.c       |   4 +
 src/bin/pg_basebackup/streamutil.c          |  59 ++++++
 src/bin/pg_basebackup/streamutil.h          |   3 +
 src/bin/pg_checksums/pg_checksums.c         |  34 ++-
 src/bin/pg_resetwal/pg_resetwal.c           |  54 ++++-
 src/bin/pg_resetwal/t/002_corrupted.pl      |   4 +-
 src/bin/pg_rewind/filemap.c                 |   5 +-
 src/bin/pg_rewind/pg_rewind.c               |  12 +-
 src/bin/pg_upgrade/file.c                   |  17 +-
 src/bin/pg_upgrade/meson.build              |   2 +
 src/bin/pg_upgrade/pg_upgrade.c             |   3 +
 src/bin/pg_upgrade/pg_upgrade.h             |   2 +-
 src/bin/pg_waldump/pg_waldump.c             |  36 +++-
 src/bin/pg_waldump/t/002_save_fullpage.pl   |   1 +
 src/common/Makefile                         |   1 +
 src/common/blocksize.c                      |  56 +++++
 src/common/file_utils.c                     |   6 +-
 src/common/meson.build                      |   1 +
 src/include/access/brin_page.h              |   2 +-
 src/include/access/ginblock.h               |   6 +-
 src/include/access/gist.h                   |   2 +-
 src/include/access/gist_private.h           |   6 +-
 src/include/access/hash.h                   |   8 +-
 src/include/access/heapam.h                 |   2 +-
 src/include/access/heaptoast.h              |  18 +-
 src/include/access/htup_details.h           |  16 +-
 src/include/access/itup.h                   |   5 +-
 src/include/access/nbtree.h                 | 211 +------------------
 src/include/access/nbtree_int.h             | 220 ++++++++++++++++++++
 src/include/access/slru.h                   |   2 +-
 src/include/access/spgist_private.h         |  14 +-
 src/include/access/xlog_internal.h          |   1 +
 src/include/access/xlogrecord.h             |   6 +-
 src/include/backup/basebackup_sink.h        |   4 +-
 src/include/c.h                             |   6 +-
 src/include/common/blocksize.h              |  91 ++++++++
 src/include/pg_config.h.in                  |  12 +-
 src/include/pg_config_manual.h              |  10 +-
 src/include/postgres.h                      |   1 +
 src/include/storage/bufmgr.h                |   4 +-
 src/include/storage/bufpage.h               |  20 +-
 src/include/storage/checksum.h              |   2 +-
 src/include/storage/checksum_impl.h         |  13 +-
 src/include/storage/freespace.h             |   1 +
 src/include/storage/fsm_internals.h         |   4 +-
 src/include/storage/large_object.h          |   6 +-
 src/include/storage/off.h                   |   4 +-
 src/include/utils/guc_tables.h              |   1 +
 src/include/utils/rel.h                     |   4 +-
 src/test/modules/test_slru/test_slru.c      |   2 +-
 src/test/regress/expected/btree_index.out   |   4 +-
 src/test/regress/expected/largeobject.out   |   2 +-
 src/test/regress/expected/largeobject_1.out |   2 +-
 src/test/regress/sql/btree_index.sql        |   4 +-
 src/test/regress/sql/largeobject.sql        |   2 +-
 src/tools/msvc/Mkvcbuild.pm                 |   2 +-
 src/tools/msvc/Solution.pm                  |   2 +-
 src/tools/pgindent/typedefs.list            |   1 -
 199 files changed, 1666 insertions(+), 970 deletions(-)
 create mode 100644 src/bin/initdb/t/002_blocksize.pl
 create mode 100644 src/common/blocksize.c
 create mode 100644 src/include/access/nbtree_int.h
 create mode 100644 src/include/common/blocksize.h

diff --git a/configure b/configure
index 8e5006bd4f..a374762c02 100755
--- a/configure
+++ b/configure
@@ -3714,12 +3714,12 @@ fi
 
 
 case ${blocksize} in
-  1) BLCKSZ=1024;;
-  2) BLCKSZ=2048;;
-  4) BLCKSZ=4096;;
-  8) BLCKSZ=8192;;
- 16) BLCKSZ=16384;;
- 32) BLCKSZ=32768;;
+  1) DEFAULT_BLOCK_SIZE=1024;;
+  2) DEFAULT_BLOCK_SIZE=2048;;
+  4) DEFAULT_BLOCK_SIZE=4096;;
+  8) DEFAULT_BLOCK_SIZE=8192;;
+ 16) DEFAULT_BLOCK_SIZE=16384;;
+ 32) DEFAULT_BLOCK_SIZE=32768;;
   *) as_fn_error $? "Invalid block size. Allowed values are 1,2,4,8,16,32." "$LINENO" 5
 esac
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${blocksize}kB" >&5
@@ -3727,7 +3727,7 @@ $as_echo "${blocksize}kB" >&6; }
 
 
 cat >>confdefs.h <<_ACEOF
-#define BLCKSZ ${BLCKSZ}
+#define DEFAULT_BLOCK_SIZE ${DEFAULT_BLOCK_SIZE}
 _ACEOF
 
 
diff --git a/configure.ac b/configure.ac
index 0e8158a5e6..aa796ab9dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -258,28 +258,29 @@ PGAC_ARG_REQ(with, blocksize, [BLOCKSIZE], [set table block size in kB [8]],
              [blocksize=$withval],
              [blocksize=8])
 case ${blocksize} in
-  1) BLCKSZ=1024;;
-  2) BLCKSZ=2048;;
-  4) BLCKSZ=4096;;
-  8) BLCKSZ=8192;;
- 16) BLCKSZ=16384;;
- 32) BLCKSZ=32768;;
+  1) DEFAULT_BLOCK_SIZE=1024;;
+  2) DEFAULT_BLOCK_SIZE=2048;;
+  4) DEFAULT_BLOCK_SIZE=4096;;
+  8) DEFAULT_BLOCK_SIZE=8192;;
+ 16) DEFAULT_BLOCK_SIZE=16384;;
+ 32) DEFAULT_BLOCK_SIZE=32768;;
   *) AC_MSG_ERROR([Invalid block size. Allowed values are 1,2,4,8,16,32.])
 esac
 AC_MSG_RESULT([${blocksize}kB])
 
-AC_DEFINE_UNQUOTED([BLCKSZ], ${BLCKSZ}, [
+AC_DEFINE_UNQUOTED([DEFAULT_BLOCK_SIZE], ${DEFAULT_BLOCK_SIZE}, [
  Size of a disk block --- this also limits the size of a tuple.  You
  can set it bigger if you need bigger tuples (although TOAST should
  reduce the need to have large tuples, since fields can be spread
  across multiple tuples).
 
- BLCKSZ must be a power of 2.  The maximum possible value of BLCKSZ
+ DEFAULT_BLOCK_SIZE must be a power of 2.  The maximum possible value of DEFAULT_BLOCK_SIZE
  is currently 2^15 (32768).  This is determined by the 15-bit widths
  of the lp_off and lp_len fields in ItemIdData (see
  include/storage/itemid.h).
 
- Changing BLCKSZ requires an initdb.
+ Changing DEFAULT_BLOCK_SIZE requires an initdb, however an individual cluster
+ can have its block size set.
 ])
 
 #
@@ -313,10 +314,10 @@ fi
 
 AC_DEFINE_UNQUOTED([RELSEG_SIZE], ${RELSEG_SIZE}, [
  RELSEG_SIZE is the maximum number of blocks allowed in one disk file.
- Thus, the maximum size of a single file is RELSEG_SIZE * BLCKSZ;
+ Thus, the maximum size of a single file is RELSEG_SIZE * CLUSTER_BLOCK_SIZE;
  relations bigger than that are divided into multiple files.
 
- RELSEG_SIZE * BLCKSZ must be less than your OS' limit on file size.
+ RELSEG_SIZE * CLUSTER_BLOCK_SIZE must be less than your OS' limit on file size.
  This is often 2 GB or 4GB in a 32-bit operating system, unless you
  have large file support enabled.  By default, we make the limit 1 GB
  to avoid any possible integer-overflow problems within the OS.
@@ -350,7 +351,7 @@ esac
 AC_MSG_RESULT([${wal_blocksize}kB])
 
 AC_DEFINE_UNQUOTED([XLOG_BLCKSZ], ${XLOG_BLCKSZ}, [
- Size of a WAL file block.  This need have no particular relation to BLCKSZ.
+ Size of a WAL file block.  This need have no particular relation to CLUSTER_BLOCK_SIZE.
  XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O,
  XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O
  buffers, else direct I/O may fail.
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 97f3253522..b4453f1797 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -403,11 +403,11 @@ verify_heapam(PG_FUNCTION_ARGS)
 	for (ctx.blkno = first_block; ctx.blkno <= last_block; ctx.blkno++)
 	{
 		OffsetNumber maxoff;
-		OffsetNumber predecessor[MaxOffsetNumber];
-		OffsetNumber successor[MaxOffsetNumber];
-		bool		lp_valid[MaxOffsetNumber];
-		bool		xmin_commit_status_ok[MaxOffsetNumber];
-		XidCommitStatus xmin_commit_status[MaxOffsetNumber];
+		OffsetNumber predecessor[MaxOffsetNumberLimit];
+		OffsetNumber successor[MaxOffsetNumberLimit];
+		bool		lp_valid[MaxOffsetNumberLimit];
+		bool		xmin_commit_status_ok[MaxOffsetNumberLimit];
+		XidCommitStatus xmin_commit_status[MaxOffsetNumberLimit];
 
 		CHECK_FOR_INTERRUPTS();
 
@@ -540,13 +540,13 @@ verify_heapam(PG_FUNCTION_ARGS)
 										   (unsigned) MAXALIGN(SizeofHeapTupleHeader)));
 				continue;
 			}
-			if (ctx.lp_off + ctx.lp_len > BLCKSZ)
+			if (ctx.lp_off + ctx.lp_len > CLUSTER_BLOCK_SIZE)
 			{
 				report_corruption(&ctx,
 								  psprintf("line pointer to page offset %u with length %u ends beyond maximum page offset %u",
 										   ctx.lp_off,
 										   ctx.lp_len,
-										   (unsigned) BLCKSZ));
+										   (unsigned) CLUSTER_BLOCK_SIZE));
 				continue;
 			}
 
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 94a9759322..62276d108e 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -2962,7 +2962,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
 	BTPageOpaque opaque;
 	OffsetNumber maxoffset;
 
-	page = palloc(BLCKSZ);
+	page = palloc(CLUSTER_BLOCK_SIZE);
 
 	/*
 	 * We copy the page into local storage to avoid holding pin on the buffer
@@ -2979,7 +2979,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
 	_bt_checkpage(state->rel, buffer);
 
 	/* Only use copy of page in palloc()'d memory */
-	memcpy(page, BufferGetPage(buffer), BLCKSZ);
+	memcpy(page, BufferGetPage(buffer), CLUSTER_BLOCK_SIZE);
 	UnlockReleaseBuffer(buffer);
 
 	opaque = BTPageGetOpaque(page);
@@ -3163,7 +3163,7 @@ PageGetItemIdCareful(BtreeCheckState *state, BlockNumber block, Page page,
 	ItemId		itemid = PageGetItemId(page, offset);
 
 	if (ItemIdGetOffset(itemid) + ItemIdGetLength(itemid) >
-		BLCKSZ - MAXALIGN(sizeof(BTPageOpaqueData)))
+		CLUSTER_BLOCK_SIZE - MAXALIGN(sizeof(BTPageOpaqueData)))
 		ereport(ERROR,
 				(errcode(ERRCODE_INDEX_CORRUPTED),
 				 errmsg("line pointer points past end of tuple space in index \"%s\"",
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index b42b9e6c41..3c36c985f3 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -52,7 +52,7 @@ flushCachedPage(Relation index, BloomBuildState *buildstate)
 
 	state = GenericXLogStart(index);
 	page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
-	memcpy(page, buildstate->data.data, BLCKSZ);
+	memcpy(page, buildstate->data.data, CLUSTER_BLOCK_SIZE);
 	GenericXLogFinish(state);
 	UnlockReleaseBuffer(buffer);
 }
@@ -166,7 +166,7 @@ blbuildempty(Relation index)
 	Page		metapage;
 
 	/* Construct metapage. */
-	metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	metapage = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 	BloomFillMetapage(index, metapage);
 
 	/*
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index efdf9415d1..683ecb8a77 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -106,16 +106,9 @@ typedef struct BloomOptions
 											 * index key */
 } BloomOptions;
 
-/*
- * FreeBlockNumberArray - array of block numbers sized so that metadata fill
- * all space in metapage.
- */
-typedef BlockNumber FreeBlockNumberArray[
-										 MAXALIGN_DOWN(
-													   BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
-													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
-													   ) / sizeof(BlockNumber)
-];
+#define FREE_BLOCK_NUMBER_ELEMS CalcFreeBlockNumberElems(CLUSTER_BLOCK_SIZE)
+
+
 
 /* Metadata of bloom index */
 typedef struct BloomMetaPageData
@@ -124,14 +117,18 @@ typedef struct BloomMetaPageData
 	uint16		nStart;
 	uint16		nEnd;
 	BloomOptions opts;
-	FreeBlockNumberArray notFullPage;
+	BlockNumber notFullPage[FLEXIBLE_ARRAY_MEMBER];
 } BloomMetaPageData;
 
+#define SizeOfBloomMetaPageData (offsetof(BloomMetaPageData,notFullPage) + sizeof(BlockNumber) * BloomMetaBlockN)
+
 /* Magic number to distinguish bloom pages among anothers */
 #define BLOOM_MAGICK_NUMBER (0xDBAC0DED)
 
 /* Number of blocks numbers fit in BloomMetaPageData */
-#define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+#define BloomMetaBlockN		(MAXALIGN_DOWN(CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData)) \
+													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) \
+								  ) / sizeof(BlockNumber))
 
 #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
 
@@ -150,7 +147,7 @@ typedef struct BloomState
 } BloomState;
 
 #define BloomPageGetFreeSpace(state, page) \
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+	(CLUSTER_BLOCK_SIZE - MAXALIGN(SizeOfPageHeaderData) \
 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
 
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index d935ed8fbd..c8abdc86f6 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -400,7 +400,7 @@ BloomInitPage(Page page, uint16 flags)
 {
 	BloomPageOpaque opaque;
 
-	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+	PageInit(page, CLUSTER_BLOCK_SIZE, sizeof(BloomPageOpaqueData));
 
 	opaque = BloomPageGetOpaque(page);
 	opaque->flags = flags;
@@ -430,10 +430,10 @@ BloomFillMetapage(Relation index, Page metaPage)
 	 */
 	BloomInitPage(metaPage, BLOOM_META);
 	metadata = BloomPageGetMeta(metaPage);
-	memset(metadata, 0, sizeof(BloomMetaPageData));
+	memset(metadata, 0, SizeOfBloomMetaPageData);
 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
 	metadata->opts = *opts;
-	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+	((PageHeader) metaPage)->pd_lower += SizeOfBloomMetaPageData;
 
 	/* If this fails, probably FreeBlockNumberArray size calc is wrong: */
 	Assert(((PageHeader) metaPage)->pd_lower <= ((PageHeader) metaPage)->pd_upper);
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
index 2340d49e00..373227af98 100644
--- a/contrib/bloom/blvacuum.c
+++ b/contrib/bloom/blvacuum.c
@@ -37,7 +37,7 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	Relation	index = info->index;
 	BlockNumber blkno,
 				npages;
-	FreeBlockNumberArray notFullPage;
+	BlockNumber notFullPage[CalcFreeBlockNumberElems(MAX_BLOCK_SIZE)];
 	int			countPage = 0;
 	BloomState	state;
 	Buffer		buffer;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 9e330b9934..e8e159c81b 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -839,7 +839,7 @@ fileAnalyzeForeignTable(Relation relation,
 	 * Convert size to pages.  Must return at least 1 so that we can tell
 	 * later on that pg_class.relpages is not default.
 	 */
-	*totalpages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
+	*totalpages = (stat_buf.st_size + (CLUSTER_BLOCK_SIZE - 1)) / CLUSTER_BLOCK_SIZE;
 	if (*totalpages < 1)
 		*totalpages = 1;
 
@@ -1009,12 +1009,12 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel,
 	 * back to the default if using a program as the input.
 	 */
 	if (fdw_private->is_program || stat(fdw_private->filename, &stat_buf) < 0)
-		stat_buf.st_size = 10 * BLCKSZ;
+		stat_buf.st_size = 10 * CLUSTER_BLOCK_SIZE;
 
 	/*
 	 * Convert size to pages for use in I/O cost estimate later.
 	 */
-	pages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
+	pages = (stat_buf.st_size + (CLUSTER_BLOCK_SIZE - 1)) / CLUSTER_BLOCK_SIZE;
 	if (pages < 1)
 		pages = 1;
 	fdw_private->pages = pages;
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 9cdc8e182b..dd6115cbc7 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -116,7 +116,7 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
 
 	stat->blkno = blkno;
 
-	stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
+	stat->max_avail = CLUSTER_BLOCK_SIZE - (CLUSTER_BLOCK_SIZE - phdr->pd_special + SizeOfPageHeaderData);
 
 	stat->dead_items = stat->live_items = 0;
 
@@ -663,8 +663,8 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 
 		uargs = palloc(sizeof(ua_page_items));
 
-		uargs->page = palloc(BLCKSZ);
-		memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
+		uargs->page = palloc(CLUSTER_BLOCK_SIZE);
+		memcpy(uargs->page, BufferGetPage(buffer), CLUSTER_BLOCK_SIZE);
 
 		UnlockReleaseBuffer(buffer);
 		relation_close(rel, AccessShareLock);
diff --git a/contrib/pageinspect/expected/checksum.out b/contrib/pageinspect/expected/checksum.out
index a85388e158..2cebe89754 100644
--- a/contrib/pageinspect/expected/checksum.out
+++ b/contrib/pageinspect/expected/checksum.out
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	CLUSTER_BLOCK_SIZE	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pageinspect/expected/checksum_1.out b/contrib/pageinspect/expected/checksum_1.out
index 6fb1b1b04d..807a70dacd 100644
--- a/contrib/pageinspect/expected/checksum_1.out
+++ b/contrib/pageinspect/expected/checksum_1.out
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	CLUSTER_BLOCK_SIZE	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index b25a63cbd6..cb8bd8de91 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -179,8 +179,8 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 						blkno, RelationGetRelationName(rel))));
 
 	/* Initialize buffer to copy to */
-	raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
-	SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
+	raw_page = (bytea *) palloc(CLUSTER_BLOCK_SIZE + VARHDRSZ);
+	SET_VARSIZE(raw_page, CLUSTER_BLOCK_SIZE + VARHDRSZ);
 	raw_page_data = VARDATA(raw_page);
 
 	/* Take a verbatim copy of the page */
@@ -188,7 +188,7 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 	buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
 	LockBuffer(buf, BUFFER_LOCK_SHARE);
 
-	memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
+	memcpy(raw_page_data, BufferGetPage(buf), CLUSTER_BLOCK_SIZE);
 
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	ReleaseBuffer(buf);
@@ -219,12 +219,12 @@ get_page_from_raw(bytea *raw_page)
 
 	raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
 
-	if (raw_page_size != BLCKSZ)
+	if (raw_page_size != CLUSTER_BLOCK_SIZE)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("invalid page size"),
 				 errdetail("Expected %d bytes, got %d.",
-						   BLCKSZ, raw_page_size)));
+						   CLUSTER_BLOCK_SIZE, raw_page_size)));
 
 	page = palloc(raw_page_size);
 
@@ -357,7 +357,7 @@ page_checksum_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 	if (PageIsNew(page))
 		PG_RETURN_NULL();
 
-	PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
+	PG_RETURN_INT16(pg_checksum_page((char *) page, blkno, CLUSTER_BLOCK_SIZE));
 }
 
 Datum
diff --git a/contrib/pageinspect/sql/checksum.sql b/contrib/pageinspect/sql/checksum.sql
index b877db0611..98eb1c3b8c 100644
--- a/contrib/pageinspect/sql/checksum.sql
+++ b/contrib/pageinspect/sql/checksum.sql
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	CLUSTER_BLOCK_SIZE	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pg_surgery/heap_surgery.c b/contrib/pg_surgery/heap_surgery.c
index 88a40ab7d3..58c4819675 100644
--- a/contrib/pg_surgery/heap_surgery.c
+++ b/contrib/pg_surgery/heap_surgery.c
@@ -89,7 +89,7 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
 	Relation	rel;
 	OffsetNumber curr_start_ptr,
 				next_start_ptr;
-	bool		include_this_tid[MaxHeapTuplesPerPage];
+	bool		include_this_tid[MaxHeapTuplesPerPageLimit];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
diff --git a/contrib/pg_walinspect/pg_walinspect.c b/contrib/pg_walinspect/pg_walinspect.c
index 796a74f322..3a5a852989 100644
--- a/contrib/pg_walinspect/pg_walinspect.c
+++ b/contrib/pg_walinspect/pg_walinspect.c
@@ -386,9 +386,9 @@ GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record,
 						(errcode(ERRCODE_INTERNAL_ERROR),
 						 errmsg_internal("%s", record->errormsg_buf)));
 
-			block_fpi_data = (bytea *) palloc(BLCKSZ + VARHDRSZ);
-			SET_VARSIZE(block_fpi_data, BLCKSZ + VARHDRSZ);
-			memcpy(VARDATA(block_fpi_data), page, BLCKSZ);
+			block_fpi_data = (bytea *) palloc(CLUSTER_BLOCK_SIZE + VARHDRSZ);
+			SET_VARSIZE(block_fpi_data, CLUSTER_BLOCK_SIZE + VARHDRSZ);
+			memcpy(VARDATA(block_fpi_data), page, CLUSTER_BLOCK_SIZE);
 			values[i++] = PointerGetDatum(block_fpi_data);
 		}
 		else
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index f601dc6121..d961e91f09 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -94,7 +94,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		if (VM_ALL_VISIBLE(rel, blkno, &vmbuffer))
 		{
 			freespace = GetRecordedFreeSpace(rel, blkno);
-			stat->tuple_len += BLCKSZ - freespace;
+			stat->tuple_len += CLUSTER_BLOCK_SIZE - freespace;
 			stat->free_space += freespace;
 			continue;
 		}
@@ -113,7 +113,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		if (!PageIsNew(page))
 			stat->free_space += PageGetHeapFreeSpace(page);
 		else
-			stat->free_space += BLCKSZ - SizeOfPageHeaderData;
+			stat->free_space += CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData;
 
 		/* We may count the page as scanned even if it's new/empty */
 		scanned++;
@@ -182,7 +182,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		UnlockReleaseBuffer(buf);
 	}
 
-	stat->table_len = (uint64) nblocks * BLCKSZ;
+	stat->table_len = (uint64) nblocks * CLUSTER_BLOCK_SIZE;
 
 	/*
 	 * We don't know how many tuples are in the pages we didn't scan, so
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index d69ac1c93d..a0367184b8 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -297,7 +297,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 		{
 			int			max_avail;
 
-			max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
+			max_avail = CLUSTER_BLOCK_SIZE - (CLUSTER_BLOCK_SIZE - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
 			indexStat.max_avail += max_avail;
 			indexStat.free_space += PageGetFreeSpace(page);
 
@@ -342,7 +342,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 								indexStat.leaf_pages +
 								indexStat.internal_pages +
 								indexStat.deleted_pages +
-								indexStat.empty_pages) * BLCKSZ);
+								indexStat.empty_pages) * CLUSTER_BLOCK_SIZE);
 		values[j++] = psprintf("%u", indexStat.root_blkno);
 		values[j++] = psprintf(INT64_FORMAT, indexStat.internal_pages);
 		values[j++] = psprintf(INT64_FORMAT, indexStat.leaf_pages);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 93b7834b77..91f5e5dcfe 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -386,7 +386,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
 	table_endscan(scan);
 	relation_close(rel, AccessShareLock);
 
-	stat.table_len = (uint64) nblocks * BLCKSZ;
+	stat.table_len = (uint64) nblocks * CLUSTER_BLOCK_SIZE;
 
 	return build_pgstattuple_type(&stat, fcinfo);
 }
@@ -409,7 +409,7 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 	if (PageIsNew(page))
 	{
 		/* fully empty page */
-		stat->free_space += BLCKSZ;
+		stat->free_space += CLUSTER_BLOCK_SIZE;
 	}
 	else
 	{
@@ -419,7 +419,7 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 		if (P_IGNORE(opaque))
 		{
 			/* deleted or half-dead page */
-			stat->free_space += BLCKSZ;
+			stat->free_space += CLUSTER_BLOCK_SIZE;
 		}
 		else if (P_ISLEAF(opaque))
 		{
@@ -456,7 +456,7 @@ pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 		switch (opaque->hasho_flag & LH_PAGE_TYPE)
 		{
 			case LH_UNUSED_PAGE:
-				stat->free_space += BLCKSZ;
+				stat->free_space += CLUSTER_BLOCK_SIZE;
 				break;
 			case LH_BUCKET_PAGE:
 			case LH_OVERFLOW_PAGE:
@@ -531,7 +531,7 @@ pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn,
 		/* Quit if we've scanned the whole relation */
 		if (blkno >= nblocks)
 		{
-			stat.table_len = (uint64) nblocks * BLCKSZ;
+			stat.table_len = (uint64) nblocks * CLUSTER_BLOCK_SIZE;
 
 			break;
 		}
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..74f0d2d16d 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -2364,7 +2364,7 @@ deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
 
 	appendStringInfoString(buf, "SELECT pg_catalog.pg_relation_size(");
 	deparseStringLiteral(buf, relname.data);
-	appendStringInfo(buf, "::pg_catalog.regclass) / %d", BLCKSZ);
+	appendStringInfo(buf, "::pg_catalog.regclass) / %d", CLUSTER_BLOCK_SIZE);
 }
 
 /*
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..2acc44a421 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -755,7 +755,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 		{
 			baserel->pages = 10;
 			baserel->tuples =
-				(10 * BLCKSZ) / (baserel->reltarget->width +
+				(10 * CLUSTER_BLOCK_SIZE) / (baserel->reltarget->width +
 								 MAXALIGN(SizeofHeapTupleHeader));
 		}
 
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ed32ca0349..180d809249 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2022,7 +2022,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </para>
       <para>
        Size of the on-disk representation of this table in pages (of size
-       <symbol>BLCKSZ</symbol>).  This is only an estimate used by the
+       <symbol>CLUSTER_BLOCK_SIZE</symbol>).  This is only an estimate used by the
        planner.  It is updated by <link linkend="sql-vacuum"><command>VACUUM</command></link>,
        <link linkend="sql-analyze"><command>ANALYZE</command></link>, and a few DDL commands such as
        <link linkend="sql-createindex"><command>CREATE INDEX</command></link>.
@@ -4885,7 +4885,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
    segments or <quote>pages</quote> small enough to be conveniently stored as rows
    in <structname>pg_largeobject</structname>.
    The amount of data per page is defined to be <symbol>LOBLKSIZE</symbol> (which is currently
-   <literal>BLCKSZ/4</literal>, or typically 2 kB).
+   <literal>CLUSTER_BLOCK_SIZE/4</literal>, or typically 2 kB).
   </para>
 
   <para>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6262cb7bb2..6880976700 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1683,8 +1683,8 @@ include_dir 'conf.d'
         settings significantly higher than the minimum are usually needed
         for good performance.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
-        (Non-default values of <symbol>BLCKSZ</symbol> change the minimum
+        that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
+        (Non-default values of <symbol>CLUSTER_BLOCK_SIZE</symbol> change the minimum
         value.)
         This parameter can only be set at server start.
        </para>
@@ -1812,9 +1812,9 @@ include_dir 'conf.d'
         each database session.  These are session-local buffers used only
         for access to temporary tables.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
         The default is eight megabytes (<literal>8MB</literal>).
-        (If <symbol>BLCKSZ</symbol> is not 8kB, the default value scales
+        (If <symbol>CLUSTER_BLOCK_SIZE</symbol> is not 8kB, the default value scales
         proportionally to it.)
         This setting can be changed within individual
         sessions, but only before the first use of temporary tables
@@ -1829,7 +1829,7 @@ include_dir 'conf.d'
         buffers is only a buffer descriptor, or about 64 bytes, per
         increment in <varname>temp_buffers</varname>.  However if a buffer is
         actually used an additional 8192 bytes will be consumed for it
-        (or in general, <symbol>BLCKSZ</symbol> bytes).
+        (or in general, <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes).
        </para>
       </listitem>
      </varlistentry>
@@ -2486,11 +2486,11 @@ include_dir 'conf.d'
          cache, where performance might degrade.  This setting may have no
          effect on some platforms.
          If this value is specified without units, it is taken as blocks,
-         that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+         that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
          The valid range is between
          <literal>0</literal>, which disables forced writeback, and
          <literal>2MB</literal>.  The default is <literal>512kB</literal> on Linux,
-         <literal>0</literal> elsewhere.  (If <symbol>BLCKSZ</symbol> is not 8kB,
+         <literal>0</literal> elsewhere.  (If <symbol>CLUSTER_BLOCK_SIZE</symbol> is not 8kB,
          the default and maximum values scale proportionally to it.)
          This parameter can only be set in the <filename>postgresql.conf</filename>
          file or on the server command line.
@@ -2532,11 +2532,11 @@ include_dir 'conf.d'
          than the OS's page cache, where performance might degrade.  This
          setting may have no effect on some platforms.
          If this value is specified without units, it is taken as blocks,
-         that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+         that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
          The valid range is
          between <literal>0</literal>, which disables forced writeback,
          and <literal>2MB</literal>.  The default is <literal>0</literal>, i.e., no
-         forced writeback.  (If <symbol>BLCKSZ</symbol> is not 8kB,
+         forced writeback.  (If <symbol>CLUSTER_BLOCK_SIZE</symbol> is not 8kB,
          the maximum value scales proportionally to it.)
         </para>
        </listitem>
@@ -3539,11 +3539,11 @@ include_dir 'conf.d'
         than the OS's page cache, where performance might degrade.  This
         setting may have no effect on some platforms.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
         The valid range is
         between <literal>0</literal>, which disables forced writeback,
         and <literal>2MB</literal>.  The default is <literal>256kB</literal> on
-        Linux, <literal>0</literal> elsewhere.  (If <symbol>BLCKSZ</symbol> is not
+        Linux, <literal>0</literal> elsewhere.  (If <symbol>CLUSTER_BLOCK_SIZE</symbol> is not
         8kB, the default and maximum values scale proportionally to it.)
         This parameter can only be set in the <filename>postgresql.conf</filename>
         file or on the server command line.
@@ -5665,7 +5665,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         table, but when indexes are used the amount of table data
         scanned will normally be less.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
         The default is 8 megabytes (<literal>8MB</literal>).
        </para>
       </listitem>
@@ -5687,7 +5687,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         particular index can participate in a parallel vacuum.  See
         <xref linkend="sql-vacuum"/>.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
         The default is 512 kilobytes (<literal>512kB</literal>).
        </para>
       </listitem>
@@ -5719,9 +5719,9 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         purposes.  The system also does not assume data remains in
         the disk cache between queries.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>CLUSTER_BLOCK_SIZE</symbol> bytes, typically 8kB.
         The default is 4 gigabytes (<literal>4GB</literal>).
-        (If <symbol>BLCKSZ</symbol> is not 8kB, the default value scales
+        (If <symbol>CLUSTER_BLOCK_SIZE</symbol> is not 8kB, the default value scales
         proportionally to it.)
        </para>
       </listitem>
@@ -10676,12 +10676,13 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </term>
       <listitem>
        <para>
-        Reports the size of a disk block.  It is determined by the value
-        of <literal>BLCKSZ</literal> when building the server. The default
-        value is 8192 bytes.  The meaning of some configuration
+        Reports the size of a disk block for this cluster.
+        The default value is 8192 bytes.  The meaning of some configuration
         variables (such as <xref linkend="guc-shared-buffers"/>) is
         influenced by <varname>block_size</varname>. See <xref
-        linkend="runtime-config-resource"/> for information.
+        linkend="runtime-config-resource"/> for information on GUC configuration
+        or <xref linkend="app-initdb-blocksize"/> for more information on
+        selecting a cluster's block size.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/limits.sgml b/doc/src/sgml/limits.sgml
index d5b2b627dd..92194779da 100644
--- a/doc/src/sgml/limits.sgml
+++ b/doc/src/sgml/limits.sgml
@@ -46,7 +46,7 @@
     <row>
      <entry>relation size</entry>
      <entry>32 TB</entry>
-     <entry>with the default <symbol>BLCKSZ</symbol> of 8192 bytes</entry>
+     <entry>with the default <symbol>CLUSTER_BLOCK_SIZE</symbol> of 8192 bytes</entry>
     </row>
 
     <row>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 5cfdc70c03..8d44648c17 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3927,8 +3927,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        </para>
        <para>
         Relation data reads, writes, and extends are done in
-        <varname>block_size</varname> units, derived from the build-time
-        parameter <symbol>BLCKSZ</symbol>, which is <literal>8192</literal> by
+        <varname>block_size</varname> units, derived from the cluster init-time
+        parameter <symbol>CLUSTER_BLOCK_SIZE</symbol>, which is <literal>8192</literal> by
         default.
        </para>
       </entry>
diff --git a/doc/src/sgml/pgfreespacemap.sgml b/doc/src/sgml/pgfreespacemap.sgml
index 829ad60f32..1ac1561a74 100644
--- a/doc/src/sgml/pgfreespacemap.sgml
+++ b/doc/src/sgml/pgfreespacemap.sgml
@@ -60,7 +60,8 @@
 
   <para>
    The values stored in the free space map are not exact. They're rounded
-   to precision of 1/256th of <symbol>BLCKSZ</symbol> (32 bytes with default <symbol>BLCKSZ</symbol>), and
+   to precision of 1/256th of <symbol>CLUSTER_BLOCK_SIZE</symbol> (32 bytes
+   with default <symbol>CLUSTER_BLOCK_SIZE</symbol>), and
    they're not kept fully up-to-date as tuples are inserted and updated.
   </para>
 
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 22f1011781..6ba8fc9771 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -277,6 +277,22 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-blocksize" xreflabel="blocksize">
+      <term><option>-b <replaceable>blocksize</replaceable></option></term>
+      <term><option>--block-size=<replaceable>blocksize</replaceable></option></term>
+      <listitem>
+       <para>
+        Sets the cluster's database block size
+        to <replaceable>blocksize</replaceable> kilobytes.  This should be one
+        of 1, 2, 4, 8, 16, or 32.  Many database limits are defined relative
+        to this parameter.  If unspecified, defaults to 8.
+       </para>
+       <para>
+        This used to be the compile-time parameter <literal>BLCKSZ</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-option-locale">
       <term><option>--locale=<replaceable>locale</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml
index fd539f5604..54dd73bb12 100644
--- a/doc/src/sgml/ref/pg_resetwal.sgml
+++ b/doc/src/sgml/ref/pg_resetwal.sgml
@@ -136,6 +136,25 @@ PostgreSQL documentation
   </para>
 
   <variablelist>
+   <varlistentry>
+    <term><option>-b <replaceable class="parameter">blocksize</replaceable></option></term>
+    <term><option>--block-size=<replaceable class="parameter">blocksize</replaceable></option></term>
+    <listitem>
+     <para>
+      Sets the cluster's database block size
+      to <replaceable>blocksize</replaceable> kilobytes.  This should be one
+      of 1, 2, 4, 8, 16, or 32.  Many database limits are defined relative
+      to this parameter.
+     </para>
+     <para>
+      This <emphasis>must</emphasis> be the same value selected
+      at <command>initdb</command> time, or this will result in database
+      corruption.  This option should <emphasis>only</emphasis> be provided
+      when <filename>pg_control</filename> is corrupted or missing.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><option>-c <replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
     <term><option>--commit-timestamp-ids=<replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
diff --git a/meson.build b/meson.build
index e408967fd4..f7fd4a7c49 100644
--- a/meson.build
+++ b/meson.build
@@ -440,14 +440,15 @@ else
   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
 endif
 
-cdata.set('BLCKSZ', blocksize, description:
-'''Size of a disk block --- this also limits the size of a tuple. You can set
+cdata.set('DEFAULT_BLOCK_SIZE', blocksize, description:
+'''Default size of a disk block --- this also limits the size of a tuple. You can set
    it bigger if you need bigger tuples (although TOAST should reduce the need
    to have large tuples, since fields can be spread across multiple tuples).
-   BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
+   DEFAULT_BLOCK_SIZE must be a power of 2. The maximum possible value of DEFAULT_BLOCK_SIZE is
    currently 2^15 (32768). This is determined by the 15-bit widths of the
    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
-   Changing BLCKSZ requires an initdb.''')
+   Changing DEFAULT_BLOCK_SIZE requires an initdb, however individual clusters
+   can have different values for block size (CLUSTER_BLOCK_SIZE)''')
 
 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
 cdata.set('RELSEG_SIZE', segsize)
@@ -3311,7 +3312,7 @@ if meson.version().version_compare('>=0.57')
 
   summary(
     {
-      'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
+      'data block size': '@0@ kB'.format(cdata.get('DEFAULT_BLOCK_SIZE') / 1024),
       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
       'segment size': get_option('segsize_blocks') != 0 ?
         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index e4953a9d37..7e0976b2b0 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -212,7 +212,7 @@ typedef struct BloomOptions
  * be larger because the index has multiple columns.
  */
 #define BloomMaxFilterSize \
-	MAXALIGN_DOWN(BLCKSZ - \
+	MAXALIGN_DOWN(CLUSTER_BLOCK_SIZE - \
 				  (MAXALIGN(SizeOfPageHeaderData + \
 							sizeof(ItemIdData)) + \
 				   MAXALIGN(sizeof(BrinSpecialSpace)) + \
diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c
index b578d25954..a38605b02b 100644
--- a/src/backend/access/brin/brin_pageops.c
+++ b/src/backend/access/brin/brin_pageops.c
@@ -27,7 +27,7 @@
  * a single item per page, unlike other index AMs.
  */
 #define BrinMaxItemSize \
-	MAXALIGN_DOWN(BLCKSZ - \
+	MAXALIGN_DOWN(CLUSTER_BLOCK_SIZE - \
 				  (MAXALIGN(SizeOfPageHeaderData + \
 							sizeof(ItemIdData)) + \
 				   MAXALIGN(sizeof(BrinSpecialSpace))))
@@ -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, CLUSTER_BLOCK_SIZE, sizeof(BrinSpecialSpace));
 
 	BrinPageType(page) = type;
 }
diff --git a/src/backend/access/common/bufmask.c b/src/backend/access/common/bufmask.c
index 5e392dab1e..258b7110d9 100644
--- a/src/backend/access/common/bufmask.c
+++ b/src/backend/access/common/bufmask.c
@@ -76,7 +76,7 @@ mask_unused_space(Page page)
 
 	/* Sanity check */
 	if (pd_lower > pd_upper || pd_special < pd_upper ||
-		pd_lower < SizeOfPageHeaderData || pd_special > BLCKSZ)
+		pd_lower < SizeOfPageHeaderData || pd_special > CLUSTER_BLOCK_SIZE)
 	{
 		elog(ERROR, "invalid page pd_lower %u pd_upper %u pd_special %u",
 			 pd_lower, pd_upper, pd_special);
@@ -120,7 +120,7 @@ mask_page_content(Page page)
 {
 	/* Mask Page Content */
 	memset(page + SizeOfPageHeaderData, MASK_MARKER,
-		   BLCKSZ - SizeOfPageHeaderData);
+		   CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData);
 
 	/* Mask pd_lower and pd_upper */
 	memset(&((PageHeader) page)->pd_lower, MASK_MARKER,
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 11cc431677..36d59d095b 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -28,6 +28,7 @@
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
 #include "commands/view.h"
+#include "common/blocksize.h"
 #include "nodes/makefuncs.h"
 #include "postmaster/postmaster.h"
 #include "utils/array.h"
@@ -328,7 +329,8 @@ static relopt_int intRelOpts[] =
 			RELOPT_KIND_HEAP,
 			ShareUpdateExclusiveLock
 		},
-		TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
+		/* NOTE: these limits are dynamically updated */
+		0, 0, 0
 	},
 	{
 		{
@@ -563,6 +565,8 @@ static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 								int text_len, bool validate);
 
+static void update_dynamic_reloptions(void);
+
 /*
  * Get the length of a string reloption (either default or the user-defined
  * value).  This is used for allocation purposes when building a set of
@@ -572,6 +576,26 @@ static void parse_one_reloption(relopt_value *option, char *text_str,
 	((option).isset ? strlen((option).values.string_val) : \
 	 ((relopt_string *) (option).gen)->default_len)
 
+/*
+ * handle adjustments to the config table based on dynamic parameters' limits
+ */
+
+static void
+update_dynamic_reloptions(void)
+{
+	int i;
+
+	for (i = 0; intRelOpts[i].gen.name; i++)
+	{
+		if (strcmp("toast_tuple_target", intRelOpts[i].gen.name) == 0)
+		{
+			intRelOpts[i].min = 128;
+			intRelOpts[i].default_val = TOAST_TUPLE_TARGET;
+			intRelOpts[i].max = TOAST_TUPLE_TARGET_MAIN;
+		}
+	}
+}
+
 /*
  * initialize_reloptions
  *		initialization routine, must be called before parsing
@@ -584,6 +608,13 @@ initialize_reloptions(void)
 	int			i;
 	int			j;
 
+	/*
+	 * Set the dynamic limits based on block size; if we get multiple can make
+	 * more sophisticated.
+	 */
+
+	update_dynamic_reloptions();
+
 	j = 0;
 	for (i = 0; boolRelOpts[i].gen.name; i++)
 	{
diff --git a/src/backend/access/common/syncscan.c b/src/backend/access/common/syncscan.c
index 2bc6828883..21aa57d620 100644
--- a/src/backend/access/common/syncscan.c
+++ b/src/backend/access/common/syncscan.c
@@ -80,7 +80,7 @@ bool		trace_syncscan = false;
  * the buffer cache anyway, and on the other hand the page is most likely
  * still in the OS cache.
  */
-#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / BLCKSZ)
+#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / CLUSTER_BLOCK_SIZE)
 
 
 /*
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 588825ed85..e4f11bc35e 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -133,7 +133,7 @@ toast_save_datum(Relation rel, Datum value,
 	{
 		struct varlena hdr;
 		/* this is to make the union big enough for a chunk: */
-		char		data[TOAST_MAX_CHUNK_SIZE + VARHDRSZ];
+		char		data[TOAST_MAX_CHUNK_SIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			chunk_data;
diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c
index 35490c7283..c4352d39a2 100644
--- a/src/backend/access/gin/ginbtree.c
+++ b/src/backend/access/gin/ginbtree.c
@@ -510,7 +510,7 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
 			 * critical section yet.)
 			 */
 			newrootpg = PageGetTempPage(newrpage);
-			GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), BLCKSZ);
+			GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), CLUSTER_BLOCK_SIZE);
 
 			btree->fillRoot(btree, newrootpg,
 							BufferGetBlockNumber(lbuffer), newlpage,
@@ -567,15 +567,15 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
 		{
 			/* Splitting the root, three pages to update */
 			MarkBufferDirty(lbuffer);
-			memcpy(page, newrootpg, BLCKSZ);
-			memcpy(BufferGetPage(lbuffer), newlpage, BLCKSZ);
-			memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
+			memcpy(page, newrootpg, CLUSTER_BLOCK_SIZE);
+			memcpy(BufferGetPage(lbuffer), newlpage, CLUSTER_BLOCK_SIZE);
+			memcpy(BufferGetPage(rbuffer), newrpage, CLUSTER_BLOCK_SIZE);
 		}
 		else
 		{
 			/* Normal split, only two pages to update */
-			memcpy(page, newlpage, BLCKSZ);
-			memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
+			memcpy(page, newlpage, CLUSTER_BLOCK_SIZE);
+			memcpy(BufferGetPage(rbuffer), newrpage, CLUSTER_BLOCK_SIZE);
 		}
 
 		/* We also clear childbuf's INCOMPLETE_SPLIT flag, if passed */
diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
index 9caeac164a..a280136253 100644
--- a/src/backend/access/gin/gindatapage.c
+++ b/src/backend/access/gin/gindatapage.c
@@ -655,7 +655,7 @@ dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
 						break;
 					if (append)
 					{
-						if ((leaf->lsize - segsize) < (BLCKSZ * 3) / 4)
+						if ((leaf->lsize - segsize) < (CLUSTER_BLOCK_SIZE * 3) / 4)
 							break;
 					}
 
@@ -681,8 +681,8 @@ dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
 		/*
 		 * Now allocate a couple of temporary page images, and fill them.
 		 */
-		*newlpage = palloc(BLCKSZ);
-		*newrpage = palloc(BLCKSZ);
+		*newlpage = palloc(CLUSTER_BLOCK_SIZE);
+		*newrpage = palloc(CLUSTER_BLOCK_SIZE);
 
 		dataPlaceToPageLeafSplit(leaf, lbound, rbound,
 								 *newlpage, *newrpage);
@@ -887,7 +887,7 @@ computeLeafRecompressWALData(disassembledLeaf *leaf)
 
 	walbufbegin =
 		palloc(sizeof(ginxlogRecompressDataLeaf) +
-			   BLCKSZ +			/* max size needed to hold the segment data */
+			   CLUSTER_BLOCK_SIZE +			/* max size needed to hold the segment data */
 			   nmodified * 2	/* (segno + action) per action */
 		);
 	walbufend = walbufbegin;
@@ -1041,8 +1041,8 @@ dataPlaceToPageLeafSplit(disassembledLeaf *leaf,
 	leafSegmentInfo *seginfo;
 
 	/* Initialize temporary pages to hold the new left and right pages */
-	GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
-	GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
+	GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, CLUSTER_BLOCK_SIZE);
+	GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, CLUSTER_BLOCK_SIZE);
 
 	/*
 	 * Copy the segments that go to the left page.
@@ -1259,7 +1259,7 @@ dataSplitPageInternal(GinBtree btree, Buffer origbuf,
 	Page		lpage;
 	Page		rpage;
 	OffsetNumber separator;
-	PostingItem allitems[(BLCKSZ / sizeof(PostingItem)) + 1];
+	PostingItem allitems[(MAX_BLOCK_SIZE / sizeof(PostingItem)) + 1];
 
 	lpage = PageGetTempPage(oldpage);
 	rpage = PageGetTempPage(oldpage);
@@ -1779,8 +1779,8 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
 	bool		is_build = (buildStats != NULL);
 
 	/* Construct the new root page in memory first. */
-	tmppage = (Page) palloc(BLCKSZ);
-	GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
+	tmppage = (Page) palloc(CLUSTER_BLOCK_SIZE);
+	GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, CLUSTER_BLOCK_SIZE);
 	GinPageGetOpaque(tmppage)->rightlink = InvalidBlockNumber;
 
 	/*
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index ca7d770d86..f1128eef6b 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -39,7 +39,7 @@
 int			gin_pending_list_limit = 0;
 
 #define GIN_PAGE_FREESIZE \
-	( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
+	( CLUSTER_BLOCK_SIZE - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
 
 typedef struct KeyArray
 {
@@ -92,7 +92,7 @@ writeListPage(Relation index, Buffer buffer,
 		off++;
 	}
 
-	Assert(size <= BLCKSZ);		/* else we overran workspace */
+	Assert(size <= CLUSTER_BLOCK_SIZE);		/* else we overran workspace */
 
 	GinPageGetOpaque(page)->rightlink = rightlink;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index cb676a710f..e816b1b7d1 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1630,9 +1630,9 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
 	 */
 	for (;;)
 	{
-		Datum		datum[BLCKSZ / sizeof(IndexTupleData)];
-		GinNullCategory category[BLCKSZ / sizeof(IndexTupleData)];
-		bool		datumExtracted[BLCKSZ / sizeof(IndexTupleData)];
+		Datum		datum[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
+		GinNullCategory category[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
+		bool		datumExtracted[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
 
 		Assert(pos->lastOffset > pos->firstOffset);
 		memset(datumExtracted + pos->firstOffset - 1, 0,
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index e5d310d836..c7198d85c9 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -569,7 +569,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	BlockNumber blkno = GIN_ROOT_BLKNO;
 	GinVacuumState gvs;
 	Buffer		buffer;
-	BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
+	BlockNumber rootOfPostingTree[MAX_BLOCK_SIZE / (sizeof(IndexTupleData) + sizeof(ItemId))];
 	uint32		nRoot;
 
 	gvs.tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index f7c84beef8..761e6fedf0 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -146,7 +146,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
 			GinPostingList *plist;
 
 			plist = ginCompressPostingList(uncompressed, nuncompressed,
-										   BLCKSZ, &npacked);
+										   CLUSTER_BLOCK_SIZE, &npacked);
 			totalsize = SizeOfGinPostingList(plist);
 
 			Assert(npacked == nuncompressed);
@@ -236,7 +236,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
 			Assert(nnewitems == nolditems + nitems);
 
 			newseg = ginCompressPostingList(newitems, nnewitems,
-											BLCKSZ, &npacked);
+											CLUSTER_BLOCK_SIZE, &npacked);
 			Assert(npacked == nnewitems);
 
 			newsegsize = SizeOfGinPostingList(newseg);
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 516465f8b7..acaca88863 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -1642,7 +1642,7 @@ freeGISTstate(GISTSTATE *giststate)
 static void
 gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
 {
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				maxoff;
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index 5e0c1447f9..31d4cdf917 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -255,7 +255,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * Calculate target amount of free space to leave on pages.
 	 */
 	fillfactor = options ? options->fillfactor : GIST_DEFAULT_FILLFACTOR;
-	buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
+	buildstate.freespace = CLUSTER_BLOCK_SIZE * (100 - fillfactor) / 100;
 
 	/*
 	 * Build the index using the chosen strategy.
@@ -415,7 +415,7 @@ gist_indexsortbuild(GISTBuildState *state)
 	 * Write an empty page as a placeholder for the root page. It will be
 	 * replaced with the real root page at the end.
 	 */
-	page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
+	page = palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
 	smgrextend(RelationGetSmgr(state->indexrel), MAIN_FORKNUM, GIST_ROOT_BLKNO,
 			   page, true);
 	state->pages_allocated++;
@@ -510,7 +510,7 @@ gist_indexsortbuild_levelstate_add(GISTBuildState *state,
 
 		if (levelstate->pages[levelstate->current_page] == NULL)
 			levelstate->pages[levelstate->current_page] =
-				palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+				palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 
 		newPage = levelstate->pages[levelstate->current_page];
 		gistinitpage(newPage, old_page_flags);
@@ -580,7 +580,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
 
 		/* Create page and copy data */
 		data = (char *) (dist->list);
-		target = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
+		target = palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
 		gistinitpage(target, isleaf ? F_LEAF : 0);
 		for (int i = 0; i < dist->block.num; i++)
 		{
@@ -631,7 +631,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
 		if (parent == NULL)
 		{
 			parent = palloc0(sizeof(GistSortedBuildLevelState));
-			parent->pages[0] = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+			parent->pages[0] = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 			parent->parent = NULL;
 			gistinitpage(parent->pages[0], 0);
 
@@ -702,7 +702,7 @@ gistInitBuffering(GISTBuildState *buildstate)
 	int			levelStep;
 
 	/* Calc space of index page which is available for index tuples */
-	pageFreeSpace = BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
+	pageFreeSpace = CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
 		- sizeof(ItemIdData)
 		- buildstate->freespace;
 
@@ -799,7 +799,7 @@ gistInitBuffering(GISTBuildState *buildstate)
 			break;
 
 		/* each node in the lowest level of a subtree has one page in memory */
-		if (maxlowestlevelpages > ((double) maintenance_work_mem * 1024) / BLCKSZ)
+		if (maxlowestlevelpages > ((double) maintenance_work_mem * 1024) / CLUSTER_BLOCK_SIZE)
 			break;
 
 		/* Good, we can handle this levelStep. See if we can go one higher. */
@@ -858,7 +858,7 @@ calculatePagesPerBuffer(GISTBuildState *buildstate, int levelStep)
 	Size		pageFreeSpace;
 
 	/* Calc space of index page which is available for index tuples */
-	pageFreeSpace = BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
+	pageFreeSpace = CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
 		- sizeof(ItemIdData)
 		- buildstate->freespace;
 
diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c
index 1423b4b047..5f2c18a40f 100644
--- a/src/backend/access/gist/gistbuildbuffers.c
+++ b/src/backend/access/gist/gistbuildbuffers.c
@@ -187,11 +187,11 @@ gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb)
 	GISTNodeBufferPage *pageBuffer;
 
 	pageBuffer = (GISTNodeBufferPage *) MemoryContextAllocZero(gfbb->context,
-															   BLCKSZ);
+															   CLUSTER_BLOCK_SIZE);
 	pageBuffer->prev = InvalidBlockNumber;
 
 	/* Set page free space */
-	PAGE_FREE_SPACE(pageBuffer) = BLCKSZ - BUFFER_PAGE_DATA_OFFSET;
+	PAGE_FREE_SPACE(pageBuffer) = CLUSTER_BLOCK_SIZE - BUFFER_PAGE_DATA_OFFSET;
 	return pageBuffer;
 }
 
@@ -379,7 +379,7 @@ gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer,
 		 * the new page by storing its block number in the prev-link.
 		 */
 		PAGE_FREE_SPACE(nodeBuffer->pageBuffer) =
-			BLCKSZ - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
+			CLUSTER_BLOCK_SIZE - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
 		nodeBuffer->pageBuffer->prev = blkno;
 
 		/* We've just added one more page */
@@ -755,7 +755,7 @@ ReadTempFileBlock(BufFile *file, long blknum, void *ptr)
 {
 	if (BufFileSeekBlock(file, blknum) != 0)
 		elog(ERROR, "could not seek to block %ld in temporary file", blknum);
-	BufFileReadExact(file, ptr, BLCKSZ);
+	BufFileReadExact(file, ptr, CLUSTER_BLOCK_SIZE);
 }
 
 static void
@@ -763,5 +763,5 @@ WriteTempFileBlock(BufFile *file, long blknum, const void *ptr)
 {
 	if (BufFileSeekBlock(file, blknum) != 0)
 		elog(ERROR, "could not seek to block %ld in temporary file", blknum);
-	BufFileWrite(file, ptr, BLCKSZ);
+	BufFileWrite(file, ptr, CLUSTER_BLOCK_SIZE);
 }
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index f9f51152b8..7fa78eb002 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, CLUSTER_BLOCK_SIZE, sizeof(GISTPageOpaqueData));
 
 	opaque = GistPageGetOpaque(page);
 	opaque->rightlink = InvalidBlockNumber;
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index 3f60d3274d..53a29e55b3 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -309,7 +309,7 @@ restart:
 	}
 	else if (GistPageIsLeaf(page))
 	{
-		OffsetNumber todelete[MaxOffsetNumber];
+		OffsetNumber todelete[MaxOffsetNumberLimit];
 		int			ntodelete = 0;
 		int			nremain;
 		GISTPageOpaque opaque = GistPageGetOpaque(page);
@@ -476,8 +476,8 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistVacState *vstate)
 		Page		page;
 		OffsetNumber off,
 					maxoff;
-		OffsetNumber todelete[MaxOffsetNumber];
-		BlockNumber leafs_to_delete[MaxOffsetNumber];
+		OffsetNumber todelete[MaxOffsetNumberLimit];
+		BlockNumber leafs_to_delete[MaxOffsetNumberLimit];
 		int			ntodelete;
 		int			deleted;
 
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index fc5d97f606..f0a8ca0e4e 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -152,7 +152,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * one page.  Also, "initial index size" accounting does not include the
 	 * metapage, nor the first bitmap page.
 	 */
-	sort_threshold = (maintenance_work_mem * 1024L) / BLCKSZ;
+	sort_threshold = (maintenance_work_mem * 1024L) / CLUSTER_BLOCK_SIZE;
 	if (index->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
 		sort_threshold = Min(sort_threshold, NBuffers);
 	else
@@ -709,7 +709,7 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf,
 		OffsetNumber maxoffno;
 		Buffer		next_buf;
 		Page		page;
-		OffsetNumber deletable[MaxOffsetNumber];
+		OffsetNumber deletable[MaxOffsetNumberLimit];
 		int			ndeletable = 0;
 		bool		retain_pin = false;
 		bool		clear_dead_marking = false;
diff --git a/src/backend/access/hash/hashinsert.c b/src/backend/access/hash/hashinsert.c
index 22656b24e2..48d986272a 100644
--- a/src/backend/access/hash/hashinsert.c
+++ b/src/backend/access/hash/hashinsert.c
@@ -371,7 +371,7 @@ _hash_pgaddmultitup(Relation rel, Buffer buf, IndexTuple *itups,
 static void
 _hash_vacuum_one_page(Relation rel, Relation hrel, Buffer metabuf, Buffer buf)
 {
-	OffsetNumber deletable[MaxOffsetNumber];
+	OffsetNumber deletable[MaxOffsetNumberLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				maxoff;
diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.c
index 39bb2cb9f6..dd414c557e 100644
--- a/src/backend/access/hash/hashovfl.c
+++ b/src/backend/access/hash/hashovfl.c
@@ -866,10 +866,10 @@ _hash_squeezebucket(Relation rel,
 	{
 		OffsetNumber roffnum;
 		OffsetNumber maxroffnum;
-		OffsetNumber deletable[MaxOffsetNumber];
-		IndexTuple	itups[MaxIndexTuplesPerPage];
-		Size		tups_size[MaxIndexTuplesPerPage];
-		OffsetNumber itup_offsets[MaxIndexTuplesPerPage];
+		OffsetNumber deletable[MaxOffsetNumberLimit];
+		IndexTuple	itups[MaxIndexTuplesPerPageLimit];
+		Size		tups_size[MaxIndexTuplesPerPageLimit];
+		OffsetNumber itup_offsets[MaxIndexTuplesPerPageLimit];
 		uint16		ndeletable = 0;
 		uint16		nitups = 0;
 		Size		all_tups_size = 0;
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index af3a154266..3a4380bde7 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -1012,7 +1012,7 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
 	 * _hash_freeovflpage for similar usage.  We take care to make the special
 	 * space valid for the benefit of tools such as pageinspect.
 	 */
-	_hash_pageinit(page, BLCKSZ);
+	_hash_pageinit(page, CLUSTER_BLOCK_SIZE);
 
 	ovflopaque = HashPageGetOpaque(page);
 
@@ -1087,8 +1087,8 @@ _hash_splitbucket(Relation rel,
 	Page		npage;
 	HashPageOpaque oopaque;
 	HashPageOpaque nopaque;
-	OffsetNumber itup_offsets[MaxIndexTuplesPerPage];
-	IndexTuple	itups[MaxIndexTuplesPerPage];
+	OffsetNumber itup_offsets[MaxIndexTuplesPerPageLimit];
+	IndexTuple	itups[MaxIndexTuplesPerPageLimit];
 	Size		all_tups_size = 0;
 	int			i;
 	uint16		nitups = 0;
diff --git a/src/backend/access/heap/README.HOT b/src/backend/access/heap/README.HOT
index 6fd1767f70..242740552f 100644
--- a/src/backend/access/heap/README.HOT
+++ b/src/backend/access/heap/README.HOT
@@ -233,7 +233,7 @@ large enough to accept any extra maintenance burden for.
 The currently planned heuristic is to prune and defrag when first accessing
 a page that potentially has prunable tuples (as flagged by the pd_prune_xid
 page hint field) and that either has free space less than MAX(fillfactor
-target free space, BLCKSZ/10) *or* has recently had an UPDATE fail to
+target free space, CLUSTER_BLOCK_SIZE/10) *or* has recently had an UPDATE fail to
 find enough free space to store an updated tuple version.  (These rules
 are subject to change.)
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 7ed72abe59..59c020589c 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2061,7 +2061,7 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
 static int
 heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveFreeSpace)
 {
-	size_t		page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+	size_t		page_avail = CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - saveFreeSpace;
 	int			npages = 1;
 
 	for (int i = done; i < ntuples; i++)
@@ -2071,7 +2071,7 @@ heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveF
 		if (page_avail < tup_sz)
 		{
 			npages++;
-			page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+			page_avail = CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - saveFreeSpace;
 		}
 		page_avail -= tup_sz;
 	}
@@ -2332,7 +2332,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
 				scratchptr += datalen;
 			}
 			totaldatalen = scratchptr - tupledata;
-			Assert((scratchptr - scratch.data) < BLCKSZ);
+			Assert((scratchptr - scratch.data) < CLUSTER_BLOCK_SIZE);
 
 			if (need_tuple_data)
 				xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
@@ -6740,8 +6740,8 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
 	/* Now WAL-log freezing if necessary */
 	if (RelationNeedsWAL(rel))
 	{
-		xl_heap_freeze_plan plans[MaxHeapTuplesPerPage];
-		OffsetNumber offsets[MaxHeapTuplesPerPage];
+		xl_heap_freeze_plan plans[MaxHeapTuplesPerPageLimit];
+		OffsetNumber offsets[MaxHeapTuplesPerPageLimit];
 		int			nplans;
 		xl_heap_freeze_page xlrec;
 		XLogRecPtr	recptr;
@@ -7989,7 +7989,7 @@ index_delete_sort(TM_IndexDeleteOp *delstate)
 	 * Shellsort gap sequence (taken from Sedgewick-Incerpi paper).
 	 *
 	 * This implementation is fast with array sizes up to ~4500.  This covers
-	 * all supported BLCKSZ values.
+	 * all supported CLUSTER_BLOCK_SIZE values.
 	 */
 	const int	gaps[9] = {1968, 861, 336, 112, 48, 21, 7, 3, 1};
 
@@ -9019,7 +9019,7 @@ heap_xlog_visible(XLogReaderState *record)
 
 		/* initialize the page if it was read as zeros */
 		if (PageIsNew(vmpage))
-			PageInit(vmpage, BLCKSZ, 0);
+			PageInit(vmpage, CLUSTER_BLOCK_SIZE, 0);
 
 		/* remove VISIBILITYMAP_XLOG_* */
 		vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS;
@@ -9221,7 +9221,7 @@ heap_xlog_insert(XLogReaderState *record)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	HeapTupleHeader htup;
 	xl_heap_header xlhdr;
@@ -9324,7 +9324,7 @@ heap_xlog_insert(XLogReaderState *record)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
+	if (action == BLK_NEEDS_REDO && freespace < CLUSTER_BLOCK_SIZE / 5)
 		XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
 }
 
@@ -9343,7 +9343,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	HeapTupleHeader htup;
 	uint32		newlen;
@@ -9471,7 +9471,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
+	if (action == BLK_NEEDS_REDO && freespace < CLUSTER_BLOCK_SIZE / 5)
 		XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
 }
 
@@ -9500,7 +9500,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	xl_heap_header xlhdr;
 	uint32		newlen;
@@ -9746,7 +9746,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
+	if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < CLUSTER_BLOCK_SIZE / 5)
 		XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
 }
 
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 0755be8390..89c213ac91 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -1195,7 +1195,7 @@ heapam_index_build_range_scan(Relation heapRelation,
 	TransactionId OldestXmin;
 	BlockNumber previous_blkno = InvalidBlockNumber;
 	BlockNumber root_blkno = InvalidBlockNumber;
-	OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+	OffsetNumber root_offsets[MaxHeapTuplesPerPageLimit];
 
 	/*
 	 * sanity checks
@@ -1758,8 +1758,8 @@ heapam_index_validate_scan(Relation heapRelation,
 	EState	   *estate;
 	ExprContext *econtext;
 	BlockNumber root_blkno = InvalidBlockNumber;
-	OffsetNumber root_offsets[MaxHeapTuplesPerPage];
-	bool		in_index[MaxHeapTuplesPerPage];
+	OffsetNumber root_offsets[MaxHeapTuplesPerPageLimit];
+	bool		in_index[MaxHeapTuplesPerPageLimit];
 	BlockNumber previous_blkno = InvalidBlockNumber;
 
 	/* state variables for the merge */
@@ -2096,7 +2096,7 @@ heapam_relation_toast_am(Relation rel)
 #define HEAP_OVERHEAD_BYTES_PER_TUPLE \
 	(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
 #define HEAP_USABLE_BYTES_PER_PAGE \
-	(BLCKSZ - SizeOfPageHeaderData)
+	(CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData)
 
 static void
 heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 52ecd45654..8d38e375d9 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -770,7 +770,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 			chcpyend = (sliceoffset + slicelength - 1) % TOAST_MAX_CHUNK_SIZE;
 
 		memcpy(VARDATA(result) +
-			   (curchunk * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
+			   ((int32)(curchunk * TOAST_MAX_CHUNK_SIZE) - sliceoffset) + chcpystrt,
 			   chunkdata + chcpystrt,
 			   (chcpyend - chcpystrt) + 1);
 
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index c275b08494..d8fb0107ba 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -515,7 +515,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("row is too big: size %zu, maximum size %zu",
-						len, MaxHeapTupleSize)));
+						len, (Size)MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
 	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 47b9e20915..999ae8f063 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -54,9 +54,9 @@ typedef struct
 	int			ndead;
 	int			nunused;
 	/* arrays that accumulate indexes of items to be changed */
-	OffsetNumber redirected[MaxHeapTuplesPerPage * 2];
-	OffsetNumber nowdead[MaxHeapTuplesPerPage];
-	OffsetNumber nowunused[MaxHeapTuplesPerPage];
+	OffsetNumber redirected[MaxHeapTuplesPerPageLimit * 2];
+	OffsetNumber nowdead[MaxHeapTuplesPerPageLimit];
+	OffsetNumber nowunused[MaxHeapTuplesPerPageLimit];
 
 	/*
 	 * marked[i] is true if item i is entered in one of the above arrays.
@@ -64,7 +64,7 @@ typedef struct
 	 * This needs to be MaxHeapTuplesPerPage + 1 long as FirstOffsetNumber is
 	 * 1. Otherwise every access would need to subtract 1.
 	 */
-	bool		marked[MaxHeapTuplesPerPage + 1];
+	bool		marked[MaxHeapTuplesPerPageLimit + 1];
 
 	/*
 	 * Tuple visibility is only computed once for each tuple, for correctness
@@ -74,7 +74,7 @@ typedef struct
 	 *
 	 * Same indexing as ->marked.
 	 */
-	int8		htsv[MaxHeapTuplesPerPage + 1];
+	int8		htsv[MaxHeapTuplesPerPageLimit + 1];
 } PruneState;
 
 /* Local functions */
@@ -187,7 +187,7 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
 	 */
 	minfree = RelationGetTargetPageFreeSpace(relation,
 											 HEAP_DEFAULT_FILLFACTOR);
-	minfree = Max(minfree, BLCKSZ / 10);
+	minfree = Max(minfree, CLUSTER_BLOCK_SIZE / 10);
 
 	if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
 	{
@@ -598,7 +598,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
 	OffsetNumber latestdead = InvalidOffsetNumber,
 				maxoff = PageGetMaxOffsetNumber(dp),
 				offnum;
-	OffsetNumber chainitems[MaxHeapTuplesPerPage];
+	OffsetNumber chainitems[MaxHeapTuplesPerPageLimit];
 	int			nchain = 0,
 				i;
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 424958912c..23096b0149 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -255,7 +255,7 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm
 
 	state->rs_old_rel = old_heap;
 	state->rs_new_rel = new_heap;
-	state->rs_buffer = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	state->rs_buffer = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 	/* new_heap needn't be empty, just locked */
 	state->rs_blockno = RelationGetNumberOfBlocks(new_heap);
 	state->rs_buffer_valid = false;
@@ -657,7 +657,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("row is too big: size %zu, maximum size %zu",
-						len, MaxHeapTupleSize)));
+						len, (Size)MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
 	saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
@@ -702,7 +702,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 	if (!state->rs_buffer_valid)
 	{
 		/* Initialize a new empty page */
-		PageInit(page, BLCKSZ, 0);
+		PageInit(page, CLUSTER_BLOCK_SIZE, 0);
 		state->rs_buffer_valid = true;
 	}
 
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 4eb953f904..6037bfffd2 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -98,7 +98,7 @@
  * (Note that this is deliberately kept to a power-of-two, usually 2^19.)
  */
 #define FAILSAFE_EVERY_PAGES \
-	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / BLCKSZ))
+	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / CLUSTER_BLOCK_SIZE))
 
 /*
  * When a table has no indexes, vacuum the FSM after every 8GB, approximately
@@ -107,7 +107,7 @@
  * and we vacuum FSM after each index/heap cleaning pass.
  */
 #define VACUUM_FSM_EVERY_PAGES \
-	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / BLCKSZ))
+	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / CLUSTER_BLOCK_SIZE))
 
 /*
  * Before we consider skipping a page that's marked as clean in
@@ -749,9 +749,9 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
 			}
 			if (secs_dur > 0 || usecs_dur > 0)
 			{
-				read_rate = (double) BLCKSZ * PageMissOp / (1024 * 1024) /
+				read_rate = (double) CLUSTER_BLOCK_SIZE * PageMissOp / (1024 * 1024) /
 					(secs_dur + usecs_dur / 1000000.0);
-				write_rate = (double) BLCKSZ * PageDirtyOp / (1024 * 1024) /
+				write_rate = (double) CLUSTER_BLOCK_SIZE * PageDirtyOp / (1024 * 1024) /
 					(secs_dur + usecs_dur / 1000000.0);
 			}
 			appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
@@ -1439,7 +1439,7 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno,
 
 		if (GetRecordedFreeSpace(vacrel->rel, blkno) == 0)
 		{
-			freespace = BLCKSZ - SizeOfPageHeaderData;
+			freespace = CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData;
 
 			RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
 		}
@@ -1552,8 +1552,8 @@ lazy_scan_prune(LVRelState *vacrel,
 	int			nnewlpdead;
 	HeapPageFreeze pagefrz;
 	int64		fpi_before = pgWalUsage.wal_fpi;
-	OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
-	HeapTupleFreeze frozen[MaxHeapTuplesPerPage];
+	OffsetNumber deadoffsets[MaxHeapTuplesPerPageLimit];
+	HeapTupleFreeze frozen[MaxHeapTuplesPerPageLimit];
 
 	Assert(BufferGetBlockNumber(buf) == blkno);
 
@@ -1972,7 +1972,7 @@ lazy_scan_noprune(LVRelState *vacrel,
 	HeapTupleHeader tupleheader;
 	TransactionId NoFreezePageRelfrozenXid = vacrel->NewRelfrozenXid;
 	MultiXactId NoFreezePageRelminMxid = vacrel->NewRelminMxid;
-	OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
+	OffsetNumber deadoffsets[MaxHeapTuplesPerPageLimit];
 
 	Assert(BufferGetBlockNumber(buf) == blkno);
 
@@ -2504,7 +2504,7 @@ lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer,
 {
 	VacDeadItems *dead_items = vacrel->dead_items;
 	Page		page = BufferGetPage(buffer);
-	OffsetNumber unused[MaxHeapTuplesPerPage];
+	OffsetNumber unused[MaxHeapTuplesPerPageLimit];
 	int			nunused = 0;
 	TransactionId visibility_cutoff_xid;
 	bool		all_frozen;
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 7d54ec9c0f..24049e9291 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -105,7 +105,7 @@
  * extra headers, so the whole page minus the standard page header is
  * used for the bitmap.
  */
-#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData))
+#define MAPSIZE (CLUSTER_BLOCK_SIZE - MAXALIGN(SizeOfPageHeaderData))
 
 /* Number of heap blocks we can represent in one byte */
 #define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
@@ -414,8 +414,8 @@ visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_fro
 		 */
 		map = (uint64 *) PageGetContents(BufferGetPage(mapBuffer));
 
-		StaticAssertStmt(MAPSIZE % sizeof(uint64) == 0,
-						 "unsupported MAPSIZE");
+		Assert(MAPSIZE % sizeof(uint64) == 0);
+
 		if (all_frozen == NULL)
 		{
 			for (i = 0; i < MAPSIZE / sizeof(uint64); i++)
@@ -613,7 +613,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), CLUSTER_BLOCK_SIZE, 0);
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	}
 	return buf;
diff --git a/src/backend/access/nbtree/nbtdedup.c b/src/backend/access/nbtree/nbtdedup.c
index d4db0b28f2..35f12b434d 100644
--- a/src/backend/access/nbtree/nbtdedup.c
+++ b/src/backend/access/nbtree/nbtdedup.c
@@ -324,7 +324,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 	state = (BTDedupState) palloc(sizeof(BTDedupStateData));
 	state->deduplicate = true;
 	state->nmaxitems = 0;
-	state->maxpostingsize = BLCKSZ; /* We're not really deduplicating */
+	state->maxpostingsize = CLUSTER_BLOCK_SIZE; /* We're not really deduplicating */
 	state->base = NULL;
 	state->baseoff = InvalidOffsetNumber;
 	state->basetupsize = 0;
@@ -353,7 +353,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 	delstate.irel = rel;
 	delstate.iblknum = BufferGetBlockNumber(buf);
 	delstate.bottomup = true;
-	delstate.bottomupfreespace = Max(BLCKSZ / 16, newitemsz);
+	delstate.bottomupfreespace = Max(CLUSTER_BLOCK_SIZE / 16, newitemsz);
 	delstate.ndeltids = 0;
 	delstate.deltids = palloc(MaxTIDsPerBTreePage * sizeof(TM_IndexDelete));
 	delstate.status = palloc(MaxTIDsPerBTreePage * sizeof(TM_IndexStatus));
@@ -417,7 +417,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 		return true;
 
 	/* Don't dedup when we won't end up back here any time soon anyway */
-	return PageGetExactFreeSpace(page) >= Max(BLCKSZ / 24, newitemsz);
+	return PageGetExactFreeSpace(page) >= Max(CLUSTER_BLOCK_SIZE / 24, newitemsz);
 }
 
 /*
@@ -597,7 +597,7 @@ _bt_dedup_finish_pending(Page newpage, BTDedupState state)
 		spacesaving = state->phystupsize - (tuplesz + sizeof(ItemIdData));
 		/* Increment nintervals, since we wrote a new posting list tuple */
 		state->nintervals++;
-		Assert(spacesaving > 0 && spacesaving < BLCKSZ);
+		Assert(spacesaving > 0 && spacesaving < CLUSTER_BLOCK_SIZE);
 	}
 
 	/* Reset state for next pending posting list */
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index d33f814a93..eed23d35fd 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -2685,7 +2685,7 @@ _bt_delete_or_dedup_one_page(Relation rel, Relation heapRel,
 							 bool simpleonly, bool checkingunique,
 							 bool uniquedup, bool indexUnchanged)
 {
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				minoff,
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index d78971bfe8..43d6fe6c50 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -70,7 +70,7 @@ _bt_initmetapage(Page page, BlockNumber rootbknum, uint32 level,
 	BTMetaPageData *metad;
 	BTPageOpaque metaopaque;
 
-	_bt_pageinit(page, BLCKSZ);
+	_bt_pageinit(page, CLUSTER_BLOCK_SIZE);
 
 	metad = BTPageGetMeta(page);
 	metad->btm_magic = BTREE_MAGIC;
@@ -977,7 +977,7 @@ _bt_allocbuf(Relation rel, Relation heaprel)
 	 */
 	buf = ExtendBufferedRel(EB_REL(rel), MAIN_FORKNUM, NULL, EB_LOCK_FIRST);
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), CLUSTER_BLOCK_SIZE);
 
 	/* Initialize the new page before returning it */
 	page = BufferGetPage(buf);
@@ -1060,7 +1060,7 @@ _bt_lockbuf(Relation rel, Buffer buf, int access)
 	 * lock/pin held, though.
 	 */
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), CLUSTER_BLOCK_SIZE);
 }
 
 /*
@@ -1073,13 +1073,13 @@ _bt_unlockbuf(Relation rel, Buffer buf)
 	 * Buffer is pinned and locked, which means that it is expected to be
 	 * defined and addressable.  Check that proactively.
 	 */
-	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), BLCKSZ);
+	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), CLUSTER_BLOCK_SIZE);
 
 	/* LockBuffer() asserts that pin is held by this backend */
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_NOACCESS(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_NOACCESS(BufferGetPage(buf), CLUSTER_BLOCK_SIZE);
 }
 
 /*
@@ -1097,7 +1097,7 @@ _bt_conditionallockbuf(Relation rel, Buffer buf)
 		return false;
 
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), CLUSTER_BLOCK_SIZE);
 
 	return true;
 }
@@ -1112,7 +1112,7 @@ _bt_upgradelockbufcleanup(Relation rel, Buffer buf)
 	 * Buffer is pinned and locked, which means that it is expected to be
 	 * defined and addressable.  Check that proactively.
 	 */
-	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), BLCKSZ);
+	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), CLUSTER_BLOCK_SIZE);
 
 	/* LockBuffer() asserts that pin is held by this backend */
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
@@ -1160,7 +1160,7 @@ _bt_delitems_vacuum(Relation rel, Buffer buf,
 	bool		needswal = RelationNeedsWAL(rel);
 	char	   *updatedbuf = NULL;
 	Size		updatedbuflen = 0;
-	OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
+	OffsetNumber updatedoffsets[MaxIndexTuplesPerPageLimit];
 
 	/* Shouldn't be called unless there's something to do */
 	Assert(ndeletable > 0 || nupdatable > 0);
@@ -1291,7 +1291,7 @@ _bt_delitems_delete(Relation rel, Buffer buf,
 	bool		needswal = RelationNeedsWAL(rel);
 	char	   *updatedbuf = NULL;
 	Size		updatedbuflen = 0;
-	OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
+	OffsetNumber updatedoffsets[MaxIndexTuplesPerPageLimit];
 
 	/* Shouldn't be called unless there's something to do */
 	Assert(ndeletable > 0 || nupdatable > 0);
@@ -1524,8 +1524,8 @@ _bt_delitems_delete_check(Relation rel, Buffer buf, Relation heapRel,
 	OffsetNumber postingidxoffnum = InvalidOffsetNumber;
 	int			ndeletable = 0,
 				nupdatable = 0;
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
-	BTVacuumPosting updatable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
+	BTVacuumPosting updatable[MaxIndexTuplesPerPageLimit];
 
 	/* Use tableam interface to determine which tuples to delete first */
 	snapshotConflictHorizon = table_index_delete_tuples(heapRel, delstate);
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 4553aaee53..259663bca0 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -154,7 +154,7 @@ btbuildempty(Relation index)
 	Page		metapage;
 
 	/* Construct metapage. */
-	metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	metapage = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 	_bt_initmetapage(metapage, P_NONE, 0, _bt_allequalimage(index, false));
 
 	/*
@@ -425,8 +425,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
 	 */
 	if (scan->xs_want_itup && so->currTuples == NULL)
 	{
-		so->currTuples = (char *) palloc(BLCKSZ * 2);
-		so->markTuples = so->currTuples + BLCKSZ;
+		so->currTuples = (char *) palloc(CLUSTER_BLOCK_SIZE * 2);
+		so->markTuples = so->currTuples + CLUSTER_BLOCK_SIZE;
 	}
 
 	/*
@@ -1154,9 +1154,9 @@ backtrack:
 	}
 	else if (P_ISLEAF(opaque))
 	{
-		OffsetNumber deletable[MaxIndexTuplesPerPage];
+		OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 		int			ndeletable;
-		BTVacuumPosting updatable[MaxIndexTuplesPerPage];
+		BTVacuumPosting updatable[MaxIndexTuplesPerPageLimit];
 		int			nupdatable;
 		OffsetNumber offnum,
 					minoff,
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index c2665fce41..f584adc8c9 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -619,10 +619,10 @@ _bt_blnewpage(uint32 level)
 	Page		page;
 	BTPageOpaque opaque;
 
-	page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	page = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 
 	/* Zero the page and set up standard page header info */
-	_bt_pageinit(page, BLCKSZ);
+	_bt_pageinit(page, CLUSTER_BLOCK_SIZE);
 
 	/* Initialize BT opaque state */
 	opaque = BTPageGetOpaque(page);
@@ -660,7 +660,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
 	while (blkno > wstate->btws_pages_written)
 	{
 		if (!wstate->btws_zeropage)
-			wstate->btws_zeropage = (Page) palloc_aligned(BLCKSZ,
+			wstate->btws_zeropage = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE,
 														  PG_IO_ALIGN_SIZE,
 														  MCXT_ALLOC_ZERO);
 		/* don't set checksum for all-zero page */
@@ -715,7 +715,7 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
 	state->btps_level = level;
 	/* set "full" threshold based on level.  See notes at head of file. */
 	if (level > 0)
-		state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
+		state->btps_full = (CLUSTER_BLOCK_SIZE * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
 	else
 		state->btps_full = BTGetTargetPageFreeSpace(wstate->index);
 
@@ -1172,7 +1172,7 @@ _bt_uppershutdown(BTWriteState *wstate, BTPageState *state)
 	 * set to point to "P_NONE").  This changes the index to the "valid" state
 	 * by filling in a valid magic number in the metapage.
 	 */
-	metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	metapage = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 	_bt_initmetapage(metapage, rootblkno, rootlevel,
 					 wstate->inskey->allequalimage);
 	_bt_blwritepage(wstate, metapage, BTREE_METAPAGE);
@@ -1350,7 +1350,7 @@ _bt_load(BTWriteState *wstate, BTSpool *btspool, BTSpool *btspool2)
 				 * leaf pages full with few very large tuples doesn't seem
 				 * like a useful goal.)
 				 */
-				dstate->maxpostingsize = MAXALIGN_DOWN((BLCKSZ * 10 / 100)) -
+				dstate->maxpostingsize = MAXALIGN_DOWN((CLUSTER_BLOCK_SIZE * 10 / 100)) -
 					sizeof(ItemIdData);
 				Assert(dstate->maxpostingsize <= BTMaxItemSize(state->btps_page) &&
 					   dstate->maxpostingsize <= INDEX_SIZE_MASK);
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index c87e46ed66..7035665eaf 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -40,8 +40,8 @@ _bt_restore_page(Page page, char *from, int len)
 	IndexTupleData itupdata;
 	Size		itemsz;
 	char	   *end = from + len;
-	Item		items[MaxIndexTuplesPerPage];
-	uint16		itemsizes[MaxIndexTuplesPerPage];
+	Item		items[MaxIndexTuplesPerPageLimit];
+	uint16		itemsizes[MaxIndexTuplesPerPageLimit];
 	int			i;
 	int			nitems;
 
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index f390c177e4..de760439b0 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -271,7 +271,7 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 									 "" : " for WAL verification",
 									 XLogRecGetBlock(record, block_id)->hole_offset,
 									 XLogRecGetBlock(record, block_id)->hole_length,
-									 BLCKSZ -
+									 CLUSTER_BLOCK_SIZE -
 									 XLogRecGetBlock(record, block_id)->hole_length -
 									 XLogRecGetBlock(record, block_id)->bimg_len,
 									 method);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 3554edcc9a..12ec7cf9ee 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -135,7 +135,7 @@ spgPageIndexMultiDelete(SpGistState *state, Page page,
 						BlockNumber blkno, OffsetNumber offnum)
 {
 	OffsetNumber firstItem;
-	OffsetNumber sortednos[MaxIndexTuplesPerPage];
+	OffsetNumber sortednos[MaxIndexTuplesPerPageLimit];
 	SpGistDeadTuple tuple = NULL;
 	int			i;
 
@@ -341,8 +341,8 @@ checkSplitConditions(Relation index, SpGistState *state,
 	if (SpGistBlockIsRoot(current->blkno))
 	{
 		/* return impossible values to force split */
-		*nToSplit = BLCKSZ;
-		return BLCKSZ;
+		*nToSplit = CLUSTER_BLOCK_SIZE;
+		return CLUSTER_BLOCK_SIZE;
 	}
 
 	i = current->offnum;
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
index 72d2e1551c..1e1025128d 100644
--- a/src/backend/access/spgist/spginsert.c
+++ b/src/backend/access/spgist/spginsert.c
@@ -158,7 +158,7 @@ spgbuildempty(Relation index)
 	Page		page;
 
 	/* Construct metapage. */
-	page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	page = (Page) palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE, 0);
 	SpGistInitMetapage(page);
 
 	/*
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index cbfaf0c00a..8686916846 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -755,8 +755,8 @@ spgGetNextQueueItem(SpGistScanOpaque so)
 enum SpGistSpecialOffsetNumbers
 {
 	SpGistBreakOffsetNumber = InvalidOffsetNumber,
-	SpGistRedirectOffsetNumber = MaxOffsetNumber + 1,
-	SpGistErrorOffsetNumber = MaxOffsetNumber + 2
+	SpGistRedirectOffsetNumber = MaxOffsetNumberLimit + 1,
+	SpGistErrorOffsetNumber = MaxOffsetNumberLimit + 2
 };
 
 static OffsetNumber
diff --git a/src/backend/access/spgist/spgtextproc.c b/src/backend/access/spgist/spgtextproc.c
index 03a7afdbab..a741dd1614 100644
--- a/src/backend/access/spgist/spgtextproc.c
+++ b/src/backend/access/spgist/spgtextproc.c
@@ -53,20 +53,20 @@
  * In the worst case, an inner tuple in a text radix tree could have as many
  * as 258 nodes (one for each possible byte value, plus the two special
  * cases).  Each node can take 16 bytes on MAXALIGN=8 machines.  The inner
- * tuple must fit on an index page of size BLCKSZ.  Rather than assuming we
+ * tuple must fit on an index page of size CLUSTER_BLOCK_SIZE.  Rather than assuming we
  * know the exact amount of overhead imposed by page headers, tuple headers,
  * etc, we leave 100 bytes for that (the actual overhead should be no more
  * than 56 bytes at this writing, so there is slop in this number).
- * So we can safely create prefixes up to BLCKSZ - 258 * 16 - 100 bytes long.
+ * So we can safely create prefixes up to CLUSTER_BLOCK_SIZE - 258 * 16 - 100 bytes long.
  * Unfortunately, because 258 * 16 is over 4K, there is no safe prefix length
- * when BLCKSZ is less than 8K; it is always possible to get "SPGiST inner
+ * when CLUSTER_BLOCK_SIZE is less than 8K; it is always possible to get "SPGiST inner
  * tuple size exceeds maximum" if there are too many distinct next-byte values
  * at a given place in the tree.  Since use of nonstandard block sizes appears
  * to be negligible in the field, we just live with that fact for now,
- * choosing a max prefix size of 32 bytes when BLCKSZ is configured smaller
+ * choosing a max prefix size of 32 bytes when CLUSTER_BLOCK_SIZE is configured smaller
  * than default.
  */
-#define SPGIST_MAX_PREFIX_LENGTH	Max((int) (BLCKSZ - 258 * 16 - 100), 32)
+#define SPGIST_MAX_PREFIX_LENGTH	Max((int) (CLUSTER_BLOCK_SIZE - 258 * 16 - 100), 32)
 
 /*
  * Strategy for collation aware operator on text is equal to btree strategy
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 190e4f76a9..768b7128c5 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -681,7 +681,7 @@ SpGistInitPage(Page page, uint16 f)
 {
 	SpGistPageOpaque opaque;
 
-	PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
+	PageInit(page, CLUSTER_BLOCK_SIZE, sizeof(SpGistPageOpaqueData));
 	opaque = SpGistPageGetOpaque(page);
 	opaque->flags = f;
 	opaque->spgist_page_id = SPGIST_PAGE_ID;
@@ -693,7 +693,7 @@ SpGistInitPage(Page page, uint16 f)
 void
 SpGistInitBuffer(Buffer b, uint16 f)
 {
-	Assert(BufferGetPageSize(b) == BLCKSZ);
+	Assert(BufferGetPageSize(b) == CLUSTER_BLOCK_SIZE);
 	SpGistInitPage(BufferGetPage(b), f);
 }
 
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index 8a5b540c80..f561ff6e2b 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -128,14 +128,14 @@ vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer,
 {
 	Page		page = BufferGetPage(buffer);
 	spgxlogVacuumLeaf xlrec;
-	OffsetNumber toDead[MaxIndexTuplesPerPage];
-	OffsetNumber toPlaceholder[MaxIndexTuplesPerPage];
-	OffsetNumber moveSrc[MaxIndexTuplesPerPage];
-	OffsetNumber moveDest[MaxIndexTuplesPerPage];
-	OffsetNumber chainSrc[MaxIndexTuplesPerPage];
-	OffsetNumber chainDest[MaxIndexTuplesPerPage];
-	OffsetNumber predecessor[MaxIndexTuplesPerPage + 1];
-	bool		deletable[MaxIndexTuplesPerPage + 1];
+	OffsetNumber toDead[MaxIndexTuplesPerPageLimit];
+	OffsetNumber toPlaceholder[MaxIndexTuplesPerPageLimit];
+	OffsetNumber moveSrc[MaxIndexTuplesPerPageLimit];
+	OffsetNumber moveDest[MaxIndexTuplesPerPageLimit];
+	OffsetNumber chainSrc[MaxIndexTuplesPerPageLimit];
+	OffsetNumber chainDest[MaxIndexTuplesPerPageLimit];
+	OffsetNumber predecessor[MaxIndexTuplesPerPageLimit + 1];
+	bool		deletable[MaxIndexTuplesPerPageLimit + 1];
 	int			nDeletable;
 	OffsetNumber i,
 				max = PageGetMaxOffsetNumber(page);
@@ -408,7 +408,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
 {
 	Page		page = BufferGetPage(buffer);
 	spgxlogVacuumRoot xlrec;
-	OffsetNumber toDelete[MaxIndexTuplesPerPage];
+	OffsetNumber toDelete[MaxIndexTuplesPerPageLimit];
 	OffsetNumber i,
 				max = PageGetMaxOffsetNumber(page);
 
@@ -498,8 +498,8 @@ vacuumRedirectAndPlaceholder(Relation index, Relation heaprel, Buffer buffer)
 				firstPlaceholder = InvalidOffsetNumber;
 	bool		hasNonPlaceholder = false;
 	bool		hasUpdate = false;
-	OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPage];
-	OffsetNumber itemnos[MaxIndexTuplesPerPage];
+	OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPageLimit];
+	OffsetNumber itemnos[MaxIndexTuplesPerPageLimit];
 	spgxlogVacuumRedirect xlrec;
 	GlobalVisState *vistest;
 
diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
index 771438c8ce..d962334251 100644
--- a/src/backend/access/table/tableam.c
+++ b/src/backend/access/table/tableam.c
@@ -636,7 +636,7 @@ table_block_relation_size(Relation rel, ForkNumber forkNumber)
 	else
 		nblocks = smgrnblocks(RelationGetSmgr(rel), forkNumber);
 
-	return nblocks * BLCKSZ;
+	return nblocks * CLUSTER_BLOCK_SIZE;
 }
 
 /*
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index 22c8ae9755..51558efd2c 100644
--- a/src/backend/access/transam/README
+++ b/src/backend/access/transam/README
@@ -851,7 +851,7 @@ we won't be able to hint its outputs until the second xact is sync'd, up to
 three walwriter cycles later.  This argues for keeping N (the group size)
 as small as possible.  For the moment we are setting the group size to 32,
 which makes the LSN cache space the same size as the actual clog buffer
-space (independently of BLCKSZ).
+space (independently of CLUSTER_BLOCK_SIZE).
 
 It is useful that we can run both synchronous and asynchronous commit
 transactions concurrently, but the safety of this is perhaps not
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 4a431d5876..ffcc6e4056 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -45,7 +45,7 @@
 #include "storage/sync.h"
 
 /*
- * Defines for CLOG page sizes.  A page is the same BLCKSZ as is used
+ * Defines for CLOG page sizes.  A page is the same CLUSTER_BLOCK_SIZE as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
@@ -59,7 +59,7 @@
 /* We need two bits per xact, so four xacts fit in a byte */
 #define CLOG_BITS_PER_XACT	2
 #define CLOG_XACTS_PER_BYTE 4
-#define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
+#define CLOG_XACTS_PER_PAGE (CLUSTER_BLOCK_SIZE * CLOG_XACTS_PER_BYTE)
 #define CLOG_XACT_BITMASK	((1 << CLOG_BITS_PER_XACT) - 1)
 
 #define TransactionIdToPage(xid)	((xid) / (TransactionId) CLOG_XACTS_PER_PAGE)
@@ -802,7 +802,7 @@ TrimCLOG(void)
 		/* Zero so-far-unused positions in the current byte */
 		*byteptr &= (1 << bshift) - 1;
 		/* Zero the rest of the page */
-		MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
+		MemSet(byteptr + 1, 0, CLUSTER_BLOCK_SIZE - byteno - 1);
 
 		XactCtl->shared->page_dirty[slotno] = true;
 	}
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index b897fabc70..8d1c26951d 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -37,7 +37,7 @@
 #include "utils/timestamp.h"
 
 /*
- * Defines for CommitTs page sizes.  A page is the same BLCKSZ as is used
+ * Defines for CommitTs page sizes.  A page is the same CLUSTER_BLOCK_SIZE as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
@@ -63,7 +63,7 @@ typedef struct CommitTimestampEntry
 									sizeof(RepOriginId))
 
 #define COMMIT_TS_XACTS_PER_PAGE \
-	(BLCKSZ / SizeOfCommitTimestampEntry)
+	(CLUSTER_BLOCK_SIZE / SizeOfCommitTimestampEntry)
 
 #define TransactionIdToCTsPage(xid) \
 	((xid) / (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
@@ -898,7 +898,7 @@ AdvanceOldestCommitTsXid(TransactionId oldestXact)
  * Decide whether a commitTS page number is "older" for truncation purposes.
  * Analogous to CLOGPagePrecedes().
  *
- * At default BLCKSZ, (1 << 31) % COMMIT_TS_XACTS_PER_PAGE == 128.  This
+ * At default CLUSTER_BLOCK_SIZE, (1 << 31) % COMMIT_TS_XACTS_PER_PAGE == 128.  This
  * introduces differences compared to CLOG and the other SLRUs having (1 <<
  * 31) % per_page == 0.  This function never tests exactly
  * TransactionIdPrecedes(x-2^31, x).  When the system reaches xidStopLimit,
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 6c68191ca6..9050e449fe 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -45,7 +45,7 @@
  */
 #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
 #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
-#define MAX_DELTA_SIZE			(BLCKSZ + 2 * FRAGMENT_HEADER_SIZE)
+#define MAX_DELTA_SIZE			(MAX_BLOCK_SIZE + 2 * FRAGMENT_HEADER_SIZE)
 
 /* Struct of generic xlog data for single page */
 typedef struct
@@ -241,8 +241,8 @@ computeDelta(PageData *pageData, Page curpage, Page targetpage)
 					   0, curLower);
 	/* ... and for upper part, ignoring what's between */
 	computeRegionDelta(pageData, curpage, targetpage,
-					   targetUpper, BLCKSZ,
-					   curUpper, BLCKSZ);
+					   targetUpper, CLUSTER_BLOCK_SIZE,
+					   curUpper, CLUSTER_BLOCK_SIZE);
 
 	/*
 	 * If xlog debug is enabled, then check produced delta.  Result of delta
@@ -253,11 +253,11 @@ computeDelta(PageData *pageData, Page curpage, Page targetpage)
 	{
 		PGAlignedBlock tmp;
 
-		memcpy(tmp.data, curpage, BLCKSZ);
+		memcpy(tmp.data, curpage, CLUSTER_BLOCK_SIZE);
 		applyPageRedo(tmp.data, pageData->delta, pageData->deltaLen);
 		if (memcmp(tmp.data, targetpage, targetLower) != 0 ||
 			memcmp(tmp.data + targetUpper, targetpage + targetUpper,
-				   BLCKSZ - targetUpper) != 0)
+				   CLUSTER_BLOCK_SIZE - targetUpper) != 0)
 			elog(ERROR, "result of generic xlog apply does not match");
 	}
 #endif
@@ -311,7 +311,7 @@ GenericXLogRegisterBuffer(GenericXLogState *state, Buffer buffer, int flags)
 			/* Empty slot, so use it (there cannot be a match later) */
 			page->buffer = buffer;
 			page->flags = flags;
-			memcpy(page->image, BufferGetPage(buffer), BLCKSZ);
+			memcpy(page->image, BufferGetPage(buffer), CLUSTER_BLOCK_SIZE);
 			return (Page) page->image;
 		}
 		else if (page->buffer == buffer)
@@ -373,7 +373,7 @@ GenericXLogFinish(GenericXLogState *state)
 					   pageHeader->pd_upper - pageHeader->pd_lower);
 				memcpy(page + pageHeader->pd_upper,
 					   pageData->image + pageHeader->pd_upper,
-					   BLCKSZ - pageHeader->pd_upper);
+					   CLUSTER_BLOCK_SIZE - pageHeader->pd_upper);
 
 				XLogRegisterBuffer(i, pageData->buffer,
 								   REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
@@ -392,7 +392,7 @@ GenericXLogFinish(GenericXLogState *state)
 					   pageHeader->pd_upper - pageHeader->pd_lower);
 				memcpy(page + pageHeader->pd_upper,
 					   pageData->image + pageHeader->pd_upper,
-					   BLCKSZ - pageHeader->pd_upper);
+					   CLUSTER_BLOCK_SIZE - pageHeader->pd_upper);
 
 				XLogRegisterBuffer(i, pageData->buffer, REGBUF_STANDARD);
 				XLogRegisterBufData(i, pageData->delta, pageData->deltaLen);
@@ -426,7 +426,7 @@ GenericXLogFinish(GenericXLogState *state)
 				continue;
 			memcpy(BufferGetPage(pageData->buffer),
 				   pageData->image,
-				   BLCKSZ);
+				   CLUSTER_BLOCK_SIZE);
 			/* We don't worry about zeroing the "hole" in this case */
 			MarkBufferDirty(pageData->buffer);
 		}
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index abb022e067..0a3e7e882b 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -93,7 +93,7 @@
 
 
 /*
- * Defines for MultiXactOffset page sizes.  A page is the same BLCKSZ as is
+ * Defines for MultiXactOffset page sizes.  A page is the same CLUSTER_BLOCK_SIZE as is
  * used everywhere else in Postgres.
  *
  * Note: because MultiXactOffsets are 32 bits and wrap around at 0xFFFFFFFF,
@@ -106,7 +106,7 @@
  */
 
 /* We need four bytes per offset */
-#define MULTIXACT_OFFSETS_PER_PAGE (BLCKSZ / sizeof(MultiXactOffset))
+#define MULTIXACT_OFFSETS_PER_PAGE (CLUSTER_BLOCK_SIZE / sizeof(MultiXactOffset))
 
 #define MultiXactIdToOffsetPage(xid) \
 	((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
@@ -119,7 +119,7 @@
  * additional flag bits for each TransactionId.  To do this without getting
  * into alignment issues, we store four bytes of flags, and then the
  * corresponding 4 Xids.  Each such 5-word (20-byte) set we call a "group", and
- * are stored as a whole in pages.  Thus, with 8kB BLCKSZ, we keep 409 groups
+ * are stored as a whole in pages.  Thus, with 8kB CLUSTER_BLOCK_SIZE, we keep 409 groups
  * per page.  This wastes 12 bytes per page, but that's OK -- simplicity (and
  * performance) trumps space efficiency here.
  *
@@ -138,7 +138,7 @@
 /* size in bytes of a complete group */
 #define MULTIXACT_MEMBERGROUP_SIZE \
 	(sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)
-#define MULTIXACT_MEMBERGROUPS_PER_PAGE (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)
+#define MULTIXACT_MEMBERGROUPS_PER_PAGE (CLUSTER_BLOCK_SIZE / MULTIXACT_MEMBERGROUP_SIZE)
 #define MULTIXACT_MEMBERS_PER_PAGE	\
 	(MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
 
@@ -2072,7 +2072,7 @@ TrimMultiXact(void)
 		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 		offptr += entryno;
 
-		MemSet(offptr, 0, BLCKSZ - (entryno * sizeof(MultiXactOffset)));
+		MemSet(offptr, 0, CLUSTER_BLOCK_SIZE - (entryno * sizeof(MultiXactOffset)));
 
 		MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
 	}
@@ -2104,7 +2104,7 @@ TrimMultiXact(void)
 		xidptr = (TransactionId *)
 			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
-		MemSet(xidptr, 0, BLCKSZ - memberoff);
+		MemSet(xidptr, 0, CLUSTER_BLOCK_SIZE - memberoff);
 
 		/*
 		 * Note: we don't need to zero out the flag bits in the remaining
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 71ac70fb40..2030fd36e7 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -169,7 +169,7 @@ SimpleLruShmemSize(int nslots, int nlsns)
 	if (nlsns > 0)
 		sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));	/* group_lsn[] */
 
-	return BUFFERALIGN(sz) + BLCKSZ * nslots;
+	return BUFFERALIGN(sz) + CLUSTER_BLOCK_SIZE * nslots;
 }
 
 /*
@@ -251,7 +251,7 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
 			shared->page_dirty[slotno] = false;
 			shared->page_lru_count[slotno] = 0;
-			ptr += BLCKSZ;
+			ptr += CLUSTER_BLOCK_SIZE;
 		}
 
 		/* Should fit to estimated shmem size */
@@ -297,7 +297,7 @@ SimpleLruZeroPage(SlruCtl ctl, int pageno)
 	SlruRecentlyUsed(shared, slotno);
 
 	/* Set the buffer to zeroes */
-	MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+	MemSet(shared->page_buffer[slotno], 0, CLUSTER_BLOCK_SIZE);
 
 	/* Set the LSNs for this new page to zero */
 	SimpleLruZeroLSNs(ctl, slotno);
@@ -628,7 +628,7 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
 {
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	int			offset = rpageno * BLCKSZ;
+	int			offset = rpageno * CLUSTER_BLOCK_SIZE;
 	char		path[MAXPGPATH];
 	int			fd;
 	bool		result;
@@ -659,7 +659,7 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
 		SlruReportIOError(ctl, pageno, 0);
 	}
 
-	result = endpos >= (off_t) (offset + BLCKSZ);
+	result = endpos >= (off_t) (offset + CLUSTER_BLOCK_SIZE);
 
 	if (CloseTransientFile(fd) != 0)
 	{
@@ -687,7 +687,7 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
 	SlruShared	shared = ctl->shared;
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	off_t		offset = rpageno * BLCKSZ;
+	off_t		offset = rpageno * CLUSTER_BLOCK_SIZE;
 	char		path[MAXPGPATH];
 	int			fd;
 
@@ -713,13 +713,13 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
 		ereport(LOG,
 				(errmsg("file \"%s\" doesn't exist, reading as zeroes",
 						path)));
-		MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+		MemSet(shared->page_buffer[slotno], 0, CLUSTER_BLOCK_SIZE);
 		return true;
 	}
 
 	errno = 0;
 	pgstat_report_wait_start(WAIT_EVENT_SLRU_READ);
-	if (pg_pread(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	if (pg_pread(fd, shared->page_buffer[slotno], CLUSTER_BLOCK_SIZE, offset) != CLUSTER_BLOCK_SIZE)
 	{
 		pgstat_report_wait_end();
 		slru_errcause = SLRU_READ_FAILED;
@@ -759,7 +759,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
 	SlruShared	shared = ctl->shared;
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	off_t		offset = rpageno * BLCKSZ;
+	off_t		offset = rpageno * CLUSTER_BLOCK_SIZE;
 	char		path[MAXPGPATH];
 	int			fd = -1;
 
@@ -874,7 +874,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
 
 	errno = 0;
 	pgstat_report_wait_start(WAIT_EVENT_SLRU_WRITE);
-	if (pg_pwrite(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	if (pg_pwrite(fd, shared->page_buffer[slotno], CLUSTER_BLOCK_SIZE, offset) != CLUSTER_BLOCK_SIZE)
 	{
 		pgstat_report_wait_end();
 		/* if write didn't set errno, assume problem is no disk space */
@@ -933,7 +933,7 @@ SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
 {
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	int			offset = rpageno * BLCKSZ;
+	int			offset = rpageno * CLUSTER_BLOCK_SIZE;
 	char		path[MAXPGPATH];
 
 	SlruFileName(ctl, path, segno);
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index 62bb610167..a066be2672 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -36,7 +36,7 @@
 
 
 /*
- * Defines for SubTrans page sizes.  A page is the same BLCKSZ as is used
+ * Defines for SubTrans page sizes.  A page is the same CLUSTER_BLOCK_SIZE as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
@@ -49,7 +49,7 @@
  */
 
 /* We need four bytes per xact */
-#define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
+#define SUBTRANS_XACTS_PER_PAGE (CLUSTER_BLOCK_SIZE / sizeof(TransactionId))
 
 #define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
 #define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c
index 94e152694e..24319d1aeb 100644
--- a/src/backend/access/transam/timeline.c
+++ b/src/backend/access/transam/timeline.c
@@ -307,7 +307,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
 	char		path[MAXPGPATH];
 	char		tmppath[MAXPGPATH];
 	char		histfname[MAXFNAMELEN];
-	char		buffer[BLCKSZ];
+	char		buffer[DEFAULT_BLOCK_SIZE];
 	int			srcfd;
 	int			fd;
 	int			nbytes;
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 334adac09e..b95d23a4c3 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -370,7 +370,7 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	 * being significant compared to total XID space. (VACUUM requires an XID
 	 * if it truncates at wal_level!=minimal.  "VACUUM (ANALYZE)", which a DBA
 	 * might do by reflex, assigns an XID.  Hence, we had better be sure
-	 * there's lots of XIDs left...)  Also, at default BLCKSZ, this leaves two
+	 * there's lots of XIDs left...)  Also, at default CLUSTER_BLOCK_SIZE, this leaves two
 	 * completely-idle segments.  In the event of edge-case bugs involving
 	 * page or segment arithmetic, idle segments render the bugs unreachable
 	 * outside of single-user mode.
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 8b0710abe6..de6b0c193a 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -67,6 +67,7 @@
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_database.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_utils.h"
 #include "executor/instrument.h"
@@ -3900,8 +3901,8 @@ WriteControlFile(void)
 	ControlFile->maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile->floatFormat = FLOATFORMAT_VALUE;
 
-	ControlFile->blcksz = BLCKSZ;
-	ControlFile->relseg_size = RELSEG_SIZE;
+	ControlFile->blcksz = CLUSTER_BLOCK_SIZE;
+	ControlFile->relseg_size = CLUSTER_RELSEG_SIZE;
 	ControlFile->xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile->xlog_seg_size = wal_segment_size;
 
@@ -3967,14 +3968,104 @@ WriteControlFile(void)
 						XLOG_CONTROL_FILE)));
 }
 
+extern int block_size;
+
+/*
+ * This routine reads and returns the block size from the control file as
+ * needed; since this is the only field we care about and it needs to be
+ * handled early in the process, we don't worry about locks and the like; this
+ * is static for the life of the cluster.
+ */
+uint32
+ClusterBlockSize(void)
+{
+	pg_crc32c	crc;
+	int fd, r;
+	ControlFileData cluster_control;
+
+	if (block_size)
+		return block_size;
+
+	if (ControlFile)
+		return ControlFile->blcksz;
+
+	// TODO: check for bootstrap mode, since that's passed in the param?
+
+	/* neither shortcut worked, so go ahead and open the control file and
+	 * parse out only basic field */
+	fd = BasicOpenFile(XLOG_CONTROL_FILE,
+					   O_RDONLY | PG_BINARY);
+	if (fd < 0)
+		ereport(PANIC,
+				(errcode_for_file_access(),
+				 errmsg("could not open file \"%s\": %m",
+						XLOG_CONTROL_FILE)));
+
+	r = read(fd, &cluster_control, sizeof(ControlFileData));
+	if (r != sizeof(ControlFileData))
+	{
+		if (r < 0)
+			ereport(PANIC,
+					(errcode_for_file_access(),
+					 errmsg("could not read file \"%s\": %m",
+							XLOG_CONTROL_FILE)));
+		else
+			ereport(PANIC,
+					(errcode(ERRCODE_DATA_CORRUPTED),
+					 errmsg("could not read file \"%s\": read %d of %zu",
+							XLOG_CONTROL_FILE, r, sizeof(ControlFileData))));
+	}
+	close(fd);
+
+	/*
+	 * Check for expected pg_control format version.  If this is wrong, the
+	 * CRC check will likely fail because we'll be checking the wrong number
+	 * of bytes.  Complaining about wrong version will probably be more
+	 * enlightening than complaining about wrong CRC.
+	 */
+
+	if (cluster_control.pg_control_version != PG_CONTROL_VERSION && cluster_control.pg_control_version % 65536 == 0 && cluster_control.pg_control_version / 65536 != 0)
+		ereport(FATAL,
+				(errmsg("database files are incompatible with server"),
+				 errdetail("The database cluster was initialized with PG_CONTROL_VERSION %d (0x%08x),"
+						   " but the server was compiled with PG_CONTROL_VERSION %d (0x%08x).",
+						   cluster_control.pg_control_version, cluster_control.pg_control_version,
+						   PG_CONTROL_VERSION, PG_CONTROL_VERSION),
+				 errhint("This could be a problem of mismatched byte ordering.  It looks like you need to initdb.")));
+
+	if (cluster_control.pg_control_version != PG_CONTROL_VERSION)
+		ereport(FATAL,
+				(errmsg("database files are incompatible with server"),
+				 errdetail("The database cluster was initialized with PG_CONTROL_VERSION %d,"
+						   " but the server was compiled with PG_CONTROL_VERSION %d.",
+						   cluster_control.pg_control_version, PG_CONTROL_VERSION),
+				 errhint("It looks like you need to initdb.")));
+
+	/* Now check the CRC. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc,
+				(char *) &cluster_control,
+				offsetof(ControlFileData, crc));
+	FIN_CRC32C(crc);
+
+	if (!EQ_CRC32C(crc, cluster_control.crc))
+		ereport(FATAL,
+				(errmsg("incorrect checksum in control file")));
+
+	block_size = cluster_control.blcksz;
+
+	return block_size;
+}
+
 static void
 ReadControlFile(void)
 {
 	pg_crc32c	crc;
 	int			fd;
 	static char wal_segsz_str[20];
+	static char block_size_str[20];
 	int			r;
-
+	int block_size;
 	/*
 	 * Read data...
 	 */
@@ -4040,6 +4131,26 @@ ReadControlFile(void)
 		ereport(FATAL,
 				(errmsg("incorrect checksum in control file")));
 
+	/*
+	 * Block size computations affect a number of things that are later
+	 * checked, so ensure that we calculate as soon as CRC has been validated
+	 * before checking other things that may depend on it.
+	 */
+
+	block_size = ControlFile->blcksz;
+
+	if (!IsValidBlockSize(block_size))
+		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg_plural("Block size must be a power of two between 1k and 32k, but the control file specifies %d byte",
+									  "Block size must be a power of two between 1k and 32k, but the control file specifies %d bytes",
+									  block_size,
+									  block_size)));
+
+	BlockSizeInit(block_size);
+	snprintf(block_size_str, sizeof(block_size_str), "%d", block_size);
+	SetConfigOption("block_size", block_size_str, PGC_INTERNAL,
+					PGC_S_DYNAMIC_DEFAULT);
+
 	/*
 	 * Do compatibility checking immediately.  If the database isn't
 	 * compatible with the backend executable, we want to abort before we can
@@ -4064,19 +4175,12 @@ ReadControlFile(void)
 				(errmsg("database files are incompatible with server"),
 				 errdetail("The database cluster appears to use a different floating-point number format than the server executable."),
 				 errhint("It looks like you need to initdb.")));
-	if (ControlFile->blcksz != BLCKSZ)
-		ereport(FATAL,
-				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with BLCKSZ %d,"
-						   " but the server was compiled with BLCKSZ %d.",
-						   ControlFile->blcksz, BLCKSZ),
-				 errhint("It looks like you need to recompile or initdb.")));
-	if (ControlFile->relseg_size != RELSEG_SIZE)
+	if (ControlFile->relseg_size != CLUSTER_RELSEG_SIZE)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with RELSEG_SIZE %d,"
-						   " but the server was compiled with RELSEG_SIZE %d.",
-						   ControlFile->relseg_size, RELSEG_SIZE),
+				 errdetail("The database cluster was initialized with CLUSTER_RELSEG_SIZE %d,"
+						   " but the server was compiled with CLUSTER_RELSEG_SIZE %d.",
+						   ControlFile->relseg_size, CLUSTER_RELSEG_SIZE),
 				 errhint("It looks like you need to recompile or initdb.")));
 	if (ControlFile->xlog_blcksz != XLOG_BLCKSZ)
 		ereport(FATAL,
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 54247e1d81..8210eec724 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -46,18 +46,18 @@
  * backup block image.
  */
 #ifdef USE_LZ4
-#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(BLCKSZ)
+#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(MAX_BLOCK_SIZE)
 #else
 #define LZ4_MAX_BLCKSZ		0
 #endif
 
 #ifdef USE_ZSTD
-#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(BLCKSZ)
+#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(MAX_BLOCK_SIZE)
 #else
 #define ZSTD_MAX_BLCKSZ		0
 #endif
 
-#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(BLCKSZ)
+#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(MAX_BLOCK_SIZE)
 
 /* Buffer size required to store a compressed version of backup block image */
 #define COMPRESS_BUFSIZE	Max(Max(PGLZ_MAX_BLCKSZ, LZ4_MAX_BLCKSZ), ZSTD_MAX_BLCKSZ)
@@ -383,7 +383,7 @@ XLogRegisterData(char *data, uint32 len)
  * block_id, the data is appended.
  *
  * The maximum amount of data that can be registered per block is 65535
- * bytes. That should be plenty; if you need more than BLCKSZ bytes to
+ * bytes. That should be plenty; if you need more than CLUSTER_BLOCK_SIZE bytes to
  * reconstruct the changes to the page, you might as well just log a full
  * copy of it. (the "main data" that's not associated with a block is not
  * limited)
@@ -650,7 +650,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 
 				if (lower >= SizeOfPageHeaderData &&
 					upper > lower &&
-					upper <= BLCKSZ)
+					upper <= CLUSTER_BLOCK_SIZE)
 				{
 					bimg.hole_offset = lower;
 					cbimg.hole_length = upper - lower;
@@ -746,12 +746,12 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			}
 			else
 			{
-				bimg.length = BLCKSZ - cbimg.hole_length;
+				bimg.length = CLUSTER_BLOCK_SIZE - cbimg.hole_length;
 
 				if (cbimg.hole_length == 0)
 				{
 					rdt_datas_last->data = page;
-					rdt_datas_last->len = BLCKSZ;
+					rdt_datas_last->len = CLUSTER_BLOCK_SIZE;
 				}
 				else
 				{
@@ -765,7 +765,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 					rdt_datas_last->data =
 						page + (bimg.hole_offset + cbimg.hole_length);
 					rdt_datas_last->len =
-						BLCKSZ - (bimg.hole_offset + cbimg.hole_length);
+						CLUSTER_BLOCK_SIZE - (bimg.hole_offset + cbimg.hole_length);
 				}
 			}
 
@@ -932,7 +932,7 @@ static bool
 XLogCompressBackupBlock(char *page, uint16 hole_offset, uint16 hole_length,
 						char *dest, uint16 *dlen)
 {
-	int32		orig_len = BLCKSZ - hole_length;
+	int32		orig_len = CLUSTER_BLOCK_SIZE - hole_length;
 	int32		len = -1;
 	int32		extra_bytes = 0;
 	char	   *source;
@@ -945,7 +945,7 @@ XLogCompressBackupBlock(char *page, uint16 hole_offset, uint16 hole_length,
 		memcpy(source, page, hole_offset);
 		memcpy(source + hole_offset,
 			   page + (hole_offset + hole_length),
-			   BLCKSZ - (hole_length + hole_offset));
+			   CLUSTER_BLOCK_SIZE - (hole_length + hole_offset));
 
 		/*
 		 * Extra data needs to be stored in WAL record for the compressed
@@ -1096,10 +1096,10 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 			uint16		upper = ((PageHeader) page)->pd_upper;
 
 			memcpy(copied_buffer.data, origdata, lower);
-			memcpy(copied_buffer.data + upper, origdata + upper, BLCKSZ - upper);
+			memcpy(copied_buffer.data + upper, origdata + upper, CLUSTER_BLOCK_SIZE - upper);
 		}
 		else
-			memcpy(copied_buffer.data, origdata, BLCKSZ);
+			memcpy(copied_buffer.data, origdata, CLUSTER_BLOCK_SIZE);
 
 		XLogBeginInsert();
 
diff --git a/src/backend/access/transam/xlogprefetcher.c b/src/backend/access/transam/xlogprefetcher.c
index 539928cb85..a70baaab12 100644
--- a/src/backend/access/transam/xlogprefetcher.c
+++ b/src/backend/access/transam/xlogprefetcher.c
@@ -51,7 +51,7 @@
  * Every time we process this much WAL, we'll update the values in
  * pg_stat_recovery_prefetch.
  */
-#define XLOGPREFETCHER_STATS_DISTANCE BLCKSZ
+#define XLOGPREFETCHER_STATS_DISTANCE CLUSTER_BLOCK_SIZE
 
 /*
  * To detect repeated access to the same block and skip useless extra system
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 2e7b1ba8e1..70170c2029 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -189,7 +189,7 @@ XLogReaderFree(XLogReaderState *state)
  * readRecordBufSize is set to the new buffer size.
  *
  * To avoid useless small increases, round its size to a multiple of
- * XLOG_BLCKSZ, and make sure it's at least 5*Max(BLCKSZ, XLOG_BLCKSZ) to start
+ * XLOG_BLCKSZ, and make sure it's at least 5*Max(CLUSTER_BLOCK_SIZE, XLOG_BLCKSZ) to start
  * with.  (That is enough for all "normal" records, but very large commit or
  * abort records might need more space.)
  */
@@ -199,7 +199,7 @@ allocate_recordbuf(XLogReaderState *state, uint32 reclength)
 	uint32		newSize = reclength;
 
 	newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
-	newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ));
+	newSize = Max(newSize, 5 * Max(CLUSTER_BLOCK_SIZE, XLOG_BLCKSZ));
 
 #ifndef FRONTEND
 
@@ -1790,17 +1790,17 @@ DecodeXLogRecord(XLogReaderState *state,
 						blk->hole_length = 0;
 				}
 				else
-					blk->hole_length = BLCKSZ - blk->bimg_len;
+					blk->hole_length = CLUSTER_BLOCK_SIZE - blk->bimg_len;
 				datatotal += blk->bimg_len;
 
 				/*
 				 * cross-check that hole_offset > 0, hole_length > 0 and
-				 * bimg_len < BLCKSZ if the HAS_HOLE flag is set.
+				 * bimg_len < CLUSTER_BLOCK_SIZE if the HAS_HOLE flag is set.
 				 */
 				if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
 					(blk->hole_offset == 0 ||
 					 blk->hole_length == 0 ||
-					 blk->bimg_len == BLCKSZ))
+					 blk->bimg_len == CLUSTER_BLOCK_SIZE))
 				{
 					report_invalid_record(state,
 										  "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X",
@@ -1827,10 +1827,10 @@ DecodeXLogRecord(XLogReaderState *state,
 				}
 
 				/*
-				 * Cross-check that bimg_len < BLCKSZ if it is compressed.
+				 * Cross-check that bimg_len < CLUSTER_BLOCK_SIZE if it is compressed.
 				 */
 				if (BKPIMAGE_COMPRESSED(blk->bimg_info) &&
-					blk->bimg_len == BLCKSZ)
+					blk->bimg_len == CLUSTER_BLOCK_SIZE)
 				{
 					report_invalid_record(state,
 										  "BKPIMAGE_COMPRESSED set, but block image length %u at %X/%X",
@@ -1840,12 +1840,12 @@ DecodeXLogRecord(XLogReaderState *state,
 				}
 
 				/*
-				 * cross-check that bimg_len = BLCKSZ if neither HAS_HOLE is
+				 * cross-check that bimg_len = CLUSTER_BLOCK_SIZE if neither HAS_HOLE is
 				 * set nor COMPRESSED().
 				 */
 				if (!(blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
 					!BKPIMAGE_COMPRESSED(blk->bimg_info) &&
-					blk->bimg_len != BLCKSZ)
+					blk->bimg_len != CLUSTER_BLOCK_SIZE)
 				{
 					report_invalid_record(state,
 										  "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_COMPRESSED set, but block image length is %u at %X/%X",
@@ -2077,14 +2077,14 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		if ((bkpb->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
 		{
 			if (pglz_decompress(ptr, bkpb->bimg_len, tmp.data,
-								BLCKSZ - bkpb->hole_length, true) < 0)
+								CLUSTER_BLOCK_SIZE - bkpb->hole_length, true) < 0)
 				decomp_success = false;
 		}
 		else if ((bkpb->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
 		{
 #ifdef USE_LZ4
 			if (LZ4_decompress_safe(ptr, tmp.data,
-									bkpb->bimg_len, BLCKSZ - bkpb->hole_length) <= 0)
+									bkpb->bimg_len, CLUSTER_BLOCK_SIZE - bkpb->hole_length) <= 0)
 				decomp_success = false;
 #else
 			report_invalid_record(record, "could not restore image at %X/%X compressed with %s not supported by build, block %d",
@@ -2098,7 +2098,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		{
 #ifdef USE_ZSTD
 			size_t		decomp_result = ZSTD_decompress(tmp.data,
-														BLCKSZ - bkpb->hole_length,
+														CLUSTER_BLOCK_SIZE - bkpb->hole_length,
 														ptr, bkpb->bimg_len);
 
 			if (ZSTD_isError(decomp_result))
@@ -2133,7 +2133,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 	/* generate page, taking into account hole if necessary */
 	if (bkpb->hole_length == 0)
 	{
-		memcpy(page, ptr, BLCKSZ);
+		memcpy(page, ptr, CLUSTER_BLOCK_SIZE);
 	}
 	else
 	{
@@ -2142,7 +2142,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		MemSet(page + bkpb->hole_offset, 0, bkpb->hole_length);
 		memcpy(page + (bkpb->hole_offset + bkpb->hole_length),
 			   ptr + bkpb->hole_offset,
-			   BLCKSZ - (bkpb->hole_offset + bkpb->hole_length));
+			   CLUSTER_BLOCK_SIZE - (bkpb->hole_offset + bkpb->hole_length));
 	}
 
 	return true;
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index becc2bda62..771a626ffc 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -293,7 +293,7 @@ static bool backupEndRequired = false;
  */
 bool		reachedConsistency = false;
 
-/* Buffers dedicated to consistency checks of size BLCKSZ */
+/* Buffers dedicated to consistency checks of size CLUSTER_BLOCK_SIZE */
 static char *replay_image_masked = NULL;
 static char *primary_image_masked = NULL;
 
@@ -606,8 +606,8 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	 * (2) a static char array isn't guaranteed to have any particular
 	 * alignment, whereas palloc() will provide MAXALIGN'd storage.
 	 */
-	replay_image_masked = (char *) palloc(BLCKSZ);
-	primary_image_masked = (char *) palloc(BLCKSZ);
+	replay_image_masked = (char *) palloc(CLUSTER_BLOCK_SIZE);
+	primary_image_masked = (char *) palloc(CLUSTER_BLOCK_SIZE);
 
 	if (read_backup_label(&CheckPointLoc, &CheckPointTLI, &backupEndRequired,
 						  &backupFromStandby))
@@ -2432,7 +2432,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 		 * Take a copy of the local page where WAL has been applied to have a
 		 * comparison base before masking it...
 		 */
-		memcpy(replay_image_masked, page, BLCKSZ);
+		memcpy(replay_image_masked, page, CLUSTER_BLOCK_SIZE);
 
 		/* No need for this page anymore now that a copy is in. */
 		UnlockReleaseBuffer(buf);
@@ -2467,7 +2467,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 		}
 
 		/* Time to compare the primary and replay images. */
-		if (memcmp(replay_image_masked, primary_image_masked, BLCKSZ) != 0)
+		if (memcmp(replay_image_masked, primary_image_masked, CLUSTER_BLOCK_SIZE) != 0)
 		{
 			elog(FATAL,
 				 "inconsistent page found, rel %u/%u/%u, forknum %u, blkno %u",
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 45be21131c..b37502bf36 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -23,6 +23,7 @@
 #include "backup/basebackup_sink.h"
 #include "backup/basebackup_target.h"
 #include "commands/defrem.h"
+#include "common/blocksize.h"
 #include "common/compression.h"
 #include "common/file_perm.h"
 #include "lib/stringinfo.h"
@@ -55,7 +56,7 @@
  * NB: The buffer size is required to be a multiple of the system block
  * size, so use that value instead if it's bigger than our preference.
  */
-#define SINK_BUFFER_LENGTH			Max(32768, BLCKSZ)
+#define SINK_BUFFER_LENGTH			Max(32768, CLUSTER_BLOCK_SIZE)
 
 typedef struct
 {
@@ -370,8 +371,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 			else
 			{
 				/* Properly terminate the tarfile. */
-				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= BLCKSZ,
-								 "BLCKSZ too small for 2 tar blocks");
+				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= MAX_BLOCK_SIZE,
+								 "CLUSTER_BLOCK_SIZE too small for 2 tar blocks");
 				memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 				bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
 
@@ -623,8 +624,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 		}
 
 		/* Properly terminate the tar file. */
-		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= BLCKSZ,
-						 "BLCKSZ too small for 2 tar blocks");
+		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= MAX_BLOCK_SIZE,
+						 "CLUSTER_BLOCK_SIZE too small for 2 tar blocks");
 		memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 		bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
 
@@ -1562,27 +1563,27 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 
 		/*
 		 * The checksums are verified at block level, so we iterate over the
-		 * buffer in chunks of BLCKSZ, after making sure that
-		 * TAR_SEND_SIZE/buf is divisible by BLCKSZ and we read a multiple of
-		 * BLCKSZ bytes.
+		 * buffer in chunks of CLUSTER_BLOCK_SIZE, after making sure that
+		 * TAR_SEND_SIZE/buf is divisible by CLUSTER_BLOCK_SIZE and we read a multiple of
+		 * CLUSTER_BLOCK_SIZE bytes.
 		 */
-		Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
+		Assert((sink->bbs_buffer_length % CLUSTER_BLOCK_SIZE) == 0);
 
-		if (verify_checksum && (cnt % BLCKSZ != 0))
+		if (verify_checksum && (cnt % CLUSTER_BLOCK_SIZE != 0))
 		{
 			ereport(WARNING,
 					(errmsg("could not verify checksum in file \"%s\", block "
 							"%u: read buffer size %d and page size %d "
 							"differ",
-							readfilename, blkno, (int) cnt, BLCKSZ)));
+							readfilename, blkno, (int) cnt, CLUSTER_BLOCK_SIZE)));
 			verify_checksum = false;
 		}
 
 		if (verify_checksum)
 		{
-			for (i = 0; i < cnt / BLCKSZ; i++)
+			for (i = 0; i < cnt / CLUSTER_BLOCK_SIZE; i++)
 			{
-				page = sink->bbs_buffer + BLCKSZ * i;
+				page = sink->bbs_buffer + CLUSTER_BLOCK_SIZE * i;
 
 				/*
 				 * Only check pages which have not been modified since the
@@ -1594,7 +1595,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 				 */
 				if (!PageIsNew(page) && PageGetLSN(page) < sink->bbs_state->startptr)
 				{
-					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
+					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE, CLUSTER_BLOCK_SIZE);
 					phdr = (PageHeader) page;
 					if (phdr->pd_checksum != checksum)
 					{
@@ -1625,8 +1626,8 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 							/* Reread the failed block */
 							reread_cnt =
 								basebackup_read_file(fd,
-													 sink->bbs_buffer + BLCKSZ * i,
-													 BLCKSZ, len + BLCKSZ * i,
+													 sink->bbs_buffer + CLUSTER_BLOCK_SIZE * i,
+													 CLUSTER_BLOCK_SIZE, len + CLUSTER_BLOCK_SIZE * i,
 													 readfilename,
 													 false);
 							if (reread_cnt == 0)
@@ -1639,7 +1640,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 								 * code that handles that case. (We must fix
 								 * up cnt first, though.)
 								 */
-								cnt = BLCKSZ * i;
+								cnt = CLUSTER_BLOCK_SIZE * i;
 								break;
 							}
 
@@ -1745,12 +1746,12 @@ _tarWriteHeader(bbsink *sink, const char *filename, const char *linktarget,
 		/*
 		 * As of this writing, the smallest supported block size is 1kB, which
 		 * is twice TAR_BLOCK_SIZE. Since the buffer size is required to be a
-		 * multiple of BLCKSZ, it should be safe to assume that the buffer is
+		 * multiple of CLUSTER_BLOCK_SIZE, it should be safe to assume that the buffer is
 		 * large enough to fit an entire tar block. We double-check by means
 		 * of these assertions.
 		 */
-		StaticAssertDecl(TAR_BLOCK_SIZE <= BLCKSZ,
-						 "BLCKSZ too small for tar block");
+		StaticAssertDecl(TAR_BLOCK_SIZE <= DEFAULT_BLOCK_SIZE,
+						 "DEFAULT_BLOCK_SIZE too small for tar block");
 		Assert(sink->bbs_buffer_length >= TAR_BLOCK_SIZE);
 
 		rc = tarCreateHeader(sink->bbs_buffer, filename, linktarget,
diff --git a/src/backend/backup/basebackup_lz4.c b/src/backend/backup/basebackup_lz4.c
index 7acb606564..3634eec9b5 100644
--- a/src/backend/backup/basebackup_lz4.c
+++ b/src/backend/backup/basebackup_lz4.c
@@ -117,10 +117,10 @@ bbsink_lz4_begin_backup(bbsink *sink)
 											 &mysink->prefs);
 
 	/*
-	 * The buffer length is expected to be a multiple of BLCKSZ, so round up.
+	 * The buffer length is expected to be a multiple of CLUSTER_BLOCK_SIZE, so round up.
 	 */
-	output_buffer_bound = output_buffer_bound + BLCKSZ -
-		(output_buffer_bound % BLCKSZ);
+	output_buffer_bound = output_buffer_bound + CLUSTER_BLOCK_SIZE -
+		(output_buffer_bound % CLUSTER_BLOCK_SIZE);
 
 	bbsink_begin_backup(sink->bbs_next, sink->bbs_state, output_buffer_bound);
 }
diff --git a/src/backend/backup/basebackup_zstd.c b/src/backend/backup/basebackup_zstd.c
index 1bb5820c88..567f334e23 100644
--- a/src/backend/backup/basebackup_zstd.c
+++ b/src/backend/backup/basebackup_zstd.c
@@ -143,10 +143,10 @@ bbsink_zstd_begin_backup(bbsink *sink)
 	output_buffer_bound = ZSTD_compressBound(mysink->base.bbs_buffer_length);
 
 	/*
-	 * The buffer length is expected to be a multiple of BLCKSZ, so round up.
+	 * The buffer length is expected to be a multiple of CLUSTER_BLOCK_SIZE, so round up.
 	 */
-	output_buffer_bound = output_buffer_bound + BLCKSZ -
-		(output_buffer_bound % BLCKSZ);
+	output_buffer_bound = output_buffer_bound + CLUSTER_BLOCK_SIZE -
+		(output_buffer_bound % CLUSTER_BLOCK_SIZE);
 
 	bbsink_begin_backup(sink->bbs_next, sink->bbs_state, output_buffer_bound);
 }
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 49e956b2c5..af66b12ac0 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -28,6 +28,7 @@
 #include "catalog/index.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
+#include "common/blocksize.h"
 #include "common/link-canary.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -46,6 +47,7 @@
 #include "utils/relmapper.h"
 
 uint32		bootstrap_data_checksum_version = 0;	/* No checksum */
+uint32		bootstrap_blocksize = DEFAULT_BLOCK_SIZE;
 
 
 static void CheckerModeMain(void);
@@ -221,10 +223,18 @@ 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:B:c:d:D:Fkr:X:-:")) != -1)
 	{
 		switch (flag)
 		{
+			case 'b':
+				bootstrap_blocksize = strtol(optarg, NULL, 0);
+				if (!IsValidBlockSize(bootstrap_blocksize))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid block size: %s; must be power of two between 1k and 32k",
+									optarg)));
+				break;
 			case 'B':
 				SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
 				break;
@@ -309,6 +319,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
 	if (!SelectConfigFiles(userDoption, progname))
 		proc_exit(1);
 
+	BlockSizeInit(bootstrap_blocksize);
+
 	/*
 	 * Validate we have been given a reasonable-looking DataDir and change
 	 * into it
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 2add053489..05877dcdd9 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -800,7 +800,7 @@ smgrDoPendingSyncs(bool isCommit, bool isParallelWorker)
 		 * main fork is longer than ever but FSM fork gets shorter.
 		 */
 		if (pendingsync->is_truncated ||
-			total_blocks * BLCKSZ / 1024 >= wal_skip_threshold)
+			total_blocks * CLUSTER_BLOCK_SIZE / 1024 >= wal_skip_threshold)
 		{
 			/* allocate the initial array, or extend it, if needed */
 			if (maxrels == 0)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index fc9a371f9b..bd6eccfa5e 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -771,9 +771,9 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 
 			if (delay_in_ms > 0)
 			{
-				read_rate = (double) BLCKSZ * AnalyzePageMiss / (1024 * 1024) /
+				read_rate = (double) CLUSTER_BLOCK_SIZE * AnalyzePageMiss / (1024 * 1024) /
 					(delay_in_ms / 1000.0);
-				write_rate = (double) BLCKSZ * AnalyzePageDirty / (1024 * 1024) /
+				write_rate = (double) CLUSTER_BLOCK_SIZE * AnalyzePageDirty / (1024 * 1024) /
 					(delay_in_ms / 1000.0);
 			}
 
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index ef909cf4e0..ed953a5a1f 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -163,7 +163,7 @@
  * than that, so changes in that data structure won't affect user-visible
  * restrictions.
  */
-#define NOTIFY_PAYLOAD_MAX_LENGTH	(BLCKSZ - NAMEDATALEN - 128)
+#define NOTIFY_PAYLOAD_MAX_LENGTH	(DEFAULT_BLOCK_SIZE - NAMEDATALEN - 128)
 
 /*
  * Struct representing an entry in the global notify queue
@@ -311,7 +311,7 @@ static AsyncQueueControl *asyncQueueControl;
 static SlruCtlData NotifyCtlData;
 
 #define NotifyCtl					(&NotifyCtlData)
-#define QUEUE_PAGESIZE				BLCKSZ
+#define QUEUE_PAGESIZE				DEFAULT_BLOCK_SIZE
 #define QUEUE_FULL_WARN_INTERVAL	5000	/* warn at most once every 5s */
 
 /*
@@ -322,7 +322,7 @@ static SlruCtlData NotifyCtlData;
  *
  * The most data we can have in the queue at a time is QUEUE_MAX_PAGE/2
  * pages, because more than that would confuse slru.c into thinking there
- * was a wraparound condition.  With the default BLCKSZ this means there
+ * was a wraparound condition.  With the default CLUSTER_BLOCK_SIZE this means there
  * can be up to 8GB of queued-and-not-read data.
  *
  * Note: it's possible to redefine QUEUE_MAX_PAGE with a smaller multiple of
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index a79067fd46..188cf7875e 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -1029,7 +1029,7 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
 
 	/* Each parallel VACUUM worker gets its own access strategy. */
 	pvs.bstrategy = GetAccessStrategyWithSize(BAS_VACUUM,
-											  shared->ring_nbuffers * (BLCKSZ / 1024));
+											  shared->ring_nbuffers * (CLUSTER_BLOCK_SIZE / 1024));
 
 	/* Setup error traceback support for ereport() */
 	errcallback.callback = parallel_vacuum_error_callback;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 468db94fe5..e8aad64aac 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -216,8 +216,8 @@
  *
  *	  Tapes' buffers can take up substantial memory when many tapes are open at
  *	  once. We only need one tape open at a time in read mode (using a buffer
- *	  that's a multiple of BLCKSZ); but we need one tape open in write mode (each
- *	  requiring a buffer of size BLCKSZ) for each partition.
+ *	  that's a multiple of CLUSTER_BLOCK_SIZE); but we need one tape open in write mode (each
+ *	  requiring a buffer of size CLUSTER_BLOCK_SIZE) for each partition.
  *
  *	  Note that it's possible for transition states to start small but then
  *	  grow very large; for instance in the case of ARRAY_AGG. In such cases,
@@ -299,12 +299,12 @@
 
 /*
  * For reading from tapes, the buffer size must be a multiple of
- * BLCKSZ. Larger values help when reading from multiple tapes concurrently,
- * but that doesn't happen in HashAgg, so we simply use BLCKSZ. Writing to a
- * tape always uses a buffer of size BLCKSZ.
+ * CLUSTER_BLOCK_SIZE. Larger values help when reading from multiple tapes concurrently,
+ * but that doesn't happen in HashAgg, so we simply use CLUSTER_BLOCK_SIZE. Writing to a
+ * tape always uses a buffer of size CLUSTER_BLOCK_SIZE.
  */
-#define HASHAGG_READ_BUFFER_SIZE BLCKSZ
-#define HASHAGG_WRITE_BUFFER_SIZE BLCKSZ
+#define HASHAGG_READ_BUFFER_SIZE CLUSTER_BLOCK_SIZE
+#define HASHAGG_WRITE_BUFFER_SIZE CLUSTER_BLOCK_SIZE
 
 /*
  * HyperLogLog is used for estimating the cardinality of the spilled tuples in
@@ -1945,7 +1945,7 @@ hash_agg_update_metrics(AggState *aggstate, bool from_tape, int npartitions)
 	/* update disk usage */
 	if (aggstate->hash_tapeset != NULL)
 	{
-		uint64		disk_used = LogicalTapeSetBlocks(aggstate->hash_tapeset) * (BLCKSZ / 1024);
+		uint64		disk_used = LogicalTapeSetBlocks(aggstate->hash_tapeset) * (CLUSTER_BLOCK_SIZE / 1024);
 
 		if (aggstate->hash_disk_used < disk_used)
 			aggstate->hash_disk_used = disk_used;
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index 29a1858441..1b34967644 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -13,7 +13,7 @@
  * fact that a particular page needs to be visited.
  *
  * The "lossy" storage uses one bit per disk page, so at the standard 8K
- * BLCKSZ, we can represent all pages in 64Gb of disk space in about 1Mb
+ * CLUSTER_BLOCK_SIZE, we can represent all pages in 64Gb of disk space in about 1Mb
  * of memory.  People pushing around tables of that size should have a
  * couple of Mb to spare, so we don't worry about providing a second level
  * of lossiness.  In theory we could fall back to page ranges at some
@@ -53,7 +53,7 @@
  * the per-page bitmaps variable size.  We just legislate that the size
  * is this:
  */
-#define MAX_TUPLES_PER_PAGE  MaxHeapTuplesPerPage
+#define MAX_TUPLES_PER_PAGE  MaxHeapTuplesPerPageLimit
 
 /*
  * When we have to switch over to lossy storage, we use a data structure
@@ -70,7 +70,7 @@
  * too different.  But we also want PAGES_PER_CHUNK to be a power of 2 to
  * avoid expensive integer remainder operations.  So, define it like this:
  */
-#define PAGES_PER_CHUNK  (BLCKSZ / 32)
+#define PAGES_PER_CHUNK  (MAX_BLOCK_SIZE / 32)
 
 /* We use BITS_PER_BITMAPWORD and typedef bitmapword from nodes/bitmapset.h */
 
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ef475d95a1..61d95d7412 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1890,7 +1890,7 @@ cost_tuplesort(Cost *startup_cost, Cost *run_cost,
 		/*
 		 * We'll have to use a disk-based sort of all the tuples
 		 */
-		double		npages = ceil(input_bytes / BLCKSZ);
+		double		npages = ceil(input_bytes / CLUSTER_BLOCK_SIZE);
 		double		nruns = input_bytes / sort_mem_bytes;
 		double		mergeorder = tuplesort_merge_order(sort_mem_bytes);
 		double		log_runs;
@@ -2455,7 +2455,7 @@ cost_material(Path *path,
 	 */
 	if (nbytes > work_mem_bytes)
 	{
-		double		npages = ceil(nbytes / BLCKSZ);
+		double		npages = ceil(nbytes / CLUSTER_BLOCK_SIZE);
 
 		run_cost += seq_page_cost * npages;
 	}
@@ -2764,7 +2764,7 @@ cost_agg(Path *path, PlannerInfo *root,
 		 * Estimate number of pages read and written. For each level of
 		 * recursion, a tuple must be written and then later read.
 		 */
-		pages = relation_byte_size(input_tuples, input_width) / BLCKSZ;
+		pages = relation_byte_size(input_tuples, input_width) / CLUSTER_BLOCK_SIZE;
 		pages_written = pages_read = pages * depth;
 
 		/*
@@ -4310,7 +4310,7 @@ cost_rescan(PlannerInfo *root, Path *path,
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
-					double		npages = ceil(nbytes / BLCKSZ);
+					double		npages = ceil(nbytes / CLUSTER_BLOCK_SIZE);
 
 					run_cost += seq_page_cost * npages;
 				}
@@ -4337,7 +4337,7 @@ cost_rescan(PlannerInfo *root, Path *path,
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
-					double		npages = ceil(nbytes / BLCKSZ);
+					double		npages = ceil(nbytes / CLUSTER_BLOCK_SIZE);
 
 					run_cost += seq_page_cost * npages;
 				}
@@ -6115,7 +6115,7 @@ relation_byte_size(double tuples, int width)
 static double
 page_size(double tuples, int width)
 {
-	return ceil(relation_byte_size(tuples, width) / BLCKSZ);
+	return ceil(relation_byte_size(tuples, width) / CLUSTER_BLOCK_SIZE);
 }
 
 /*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 39932d3c2d..61228772bc 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1089,7 +1089,7 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
 			tuple_width += MAXALIGN(SizeofHeapTupleHeader);
 			tuple_width += sizeof(ItemIdData);
 			/* note: integer division is intentional here */
-			density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
+			density = (CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData) / tuple_width;
 		}
 		*tuples = rint(density * (double) curpages);
 
diff --git a/src/backend/po/de.po b/src/backend/po/de.po
index e3d9daa041..b538af2011 100644
--- a/src/backend/po/de.po
+++ b/src/backend/po/de.po
@@ -2314,8 +2314,8 @@ msgstr "Der Datenbank-Cluster verwendet anscheinend ein anderes Fließkommazahle
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Der Datenbank-Cluster wurde mit BLCKSZ %d initialisiert, aber der Server wurde mit BLCKSZ %d kompiliert."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "Der Datenbank-Cluster wurde mit CLUSTER_BLOCK_SIZE %d initialisiert, aber der Server wurde mit CLUSTER_BLOCK_SIZE %d kompiliert."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -22111,8 +22111,8 @@ msgstr "debug_io_direct wird für WAL nicht unterstützt, weil XLOG_BLCKSZ zu kl
 
 #: storage/file/fd.c:3951
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
-msgstr "debug_io_direct wird für Daten nicht unterstützt, weil BLCKSZ zu klein ist"
+msgid "debug_io_direct is not supported for data because CLUSTER_BLOCK_SIZE is too small"
+msgstr "debug_io_direct wird für Daten nicht unterstützt, weil CLUSTER_BLOCK_SIZEs zu klein ist"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/po/es.po b/src/backend/po/es.po
index e50a935033..6a44662063 100644
--- a/src/backend/po/es.po
+++ b/src/backend/po/es.po
@@ -2387,8 +2387,8 @@ msgstr "Los archivos de la base de datos parecen usar un formato de número de c
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Los archivos de base de datos fueron inicializados con BLCKSZ %d, pero el servidor fue compilado con BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "Los archivos de base de datos fueron inicializados con CLUSTER_BLOCK_SIZE %d, pero el servidor fue compilado con CLUSTER_BLOCK_SIZE %d."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -21808,7 +21808,7 @@ msgstr ""
 
 #: storage/file/fd.c:3901
 #, c-format
-msgid "io_direct is not supported for data because BLCKSZ is too small"
+msgid "io_direct is not supported for data because CLUSTER_BLOCK_SIZE is too small"
 msgstr ""
 
 #: storage/file/reinit.c:145
diff --git a/src/backend/po/fr.po b/src/backend/po/fr.po
index ff2a32bdb2..f061583bc6 100644
--- a/src/backend/po/fr.po
+++ b/src/backend/po/fr.po
@@ -2380,10 +2380,10 @@ msgstr ""
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
 msgstr ""
-"Le cluster de base de données a été initialisé avec un BLCKSZ à %d alors que\n"
-"le serveur a été compilé avec un BLCKSZ à %d."
+"Le cluster de base de données a été initialisé avec un CLUSTER_BLOCK_SIZE à %d alors que\n"
+"le serveur a été compilé avec un CLUSTER_BLOCK_SIZE à %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
diff --git a/src/backend/po/id.po b/src/backend/po/id.po
index d5d484132b..9817073200 100644
--- a/src/backend/po/id.po
+++ b/src/backend/po/id.po
@@ -1089,8 +1089,8 @@ msgstr "cluster database sepertinya menggunakan format nomor floating-point yang
 
 #: access/transam/xlog.c:3648
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "cluster database telah diinisialkan dengan BLCKSZ %d, tapi server telah dikompilasi dengan BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "cluster database telah diinisialkan dengan CLUSTER_BLOCK_SIZE %d, tapi server telah dikompilasi dengan CLUSTER_BLOCK_SIZE %d."
 
 #: access/transam/xlog.c:3651 access/transam/xlog.c:3658 access/transam/xlog.c:3665 access/transam/xlog.c:3672 access/transam/xlog.c:3679 access/transam/xlog.c:3686 access/transam/xlog.c:3693 access/transam/xlog.c:3701 access/transam/xlog.c:3708 access/transam/xlog.c:3717 access/transam/xlog.c:3724 access/transam/xlog.c:3733 access/transam/xlog.c:3740
 #, c-format
diff --git a/src/backend/po/it.po b/src/backend/po/it.po
index 673e2aaf00..c0e6beee5e 100644
--- a/src/backend/po/it.po
+++ b/src/backend/po/it.po
@@ -2067,8 +2067,8 @@ msgstr "Il cluster di database sta usando un formato per i numeri in virgola mob
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Il cluster di database è stato inizializzato con BLCKSZ %d, ma il server è stato compilato con BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "Il cluster di database è stato inizializzato con CLUSTER_BLOCK_SIZE %d, ma il server è stato compilato con CLUSTER_BLOCK_SIZE %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106 access/transam/xlog.c:4113 access/transam/xlog.c:4120 access/transam/xlog.c:4127 access/transam/xlog.c:4134 access/transam/xlog.c:4141 access/transam/xlog.c:4149 access/transam/xlog.c:4156
 #, c-format
diff --git a/src/backend/po/ja.po b/src/backend/po/ja.po
index 9f6e2c3250..6a9d07a2ad 100644
--- a/src/backend/po/ja.po
+++ b/src/backend/po/ja.po
@@ -2081,8 +2081,8 @@ msgstr "データベースクラスタはサーバー実行ファイルと異な
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "データベースクラスタは BLCKSZ %d で初期化されましたが、サーバーは BLCKSZ %d でコンパイルされています。"
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "データベースクラスタは CLUSTER_BLOCK_SIZE %d で初期化されましたが、サーバーは CLUSTER_BLOCK_SIZE %d でコンパイルされています。"
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080 access/transam/xlog.c:4087 access/transam/xlog.c:4094 access/transam/xlog.c:4101 access/transam/xlog.c:4108 access/transam/xlog.c:4115 access/transam/xlog.c:4123 access/transam/xlog.c:4130
 #, c-format
@@ -21426,8 +21426,8 @@ msgstr "XLOG_BLCKSZが小さすぎるためdebug_io_directはWALに対しては
 
 #: storage/file/fd.c:3901
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
-msgstr "BLCKSZが小さすぎるためdebug_io_directはデータに対してサポートされません"
+msgid "debug_io_direct is not supported for data because CLUSTER_BLOCK_SIZE is too small"
+msgstr "CLUSTER_BLOCK_SIZEが小さすぎるためdebug_io_directはデータに対してサポートされません"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/po/ko.po b/src/backend/po/ko.po
index f330f3da7e..1627570ed3 100644
--- a/src/backend/po/ko.po
+++ b/src/backend/po/ko.po
@@ -2348,10 +2348,10 @@ msgstr ""
 #: access/transam/xlog.c:4802
 #, c-format
 msgid ""
-"The database cluster was initialized with BLCKSZ %d, but the server was "
-"compiled with BLCKSZ %d."
+"The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was "
+"compiled with CLUSTER_BLOCK_SIZE %d."
 msgstr ""
-"이 데이터베이스 클러스터는 BLCKSZ %d (으)로 초기화 되었지만, 서버는 BLCKSZ "
+"이 데이터베이스 클러스터는 CLUSTER_BLOCK_SIZE %d (으)로 초기화 되었지만, 서버는 CLUSTER_BLOCK_SIZE "
 "%d (으)로 컴파일 되어있습니다."
 
 #: access/transam/xlog.c:4805 access/transam/xlog.c:4812
diff --git a/src/backend/po/pl.po b/src/backend/po/pl.po
index 3ac9d0451c..f2c70fc819 100644
--- a/src/backend/po/pl.po
+++ b/src/backend/po/pl.po
@@ -1927,8 +1927,8 @@ msgstr "Klaster bazy danych wydaje się używać innego formatu liczb zmiennoprz
 
 #: access/transam/xlog.c:4550
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Klaster bazy danych został zainicjowany z BLCKSZ %d, ale serwer był skompilowany z BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "Klaster bazy danych został zainicjowany z CLUSTER_BLOCK_SIZE %d, ale serwer był skompilowany z CLUSTER_BLOCK_SIZE %d."
 
 #: access/transam/xlog.c:4553 access/transam/xlog.c:4560
 #: access/transam/xlog.c:4567 access/transam/xlog.c:4574
diff --git a/src/backend/po/pt_BR.po b/src/backend/po/pt_BR.po
index 37e4a28f07..949e441816 100644
--- a/src/backend/po/pt_BR.po
+++ b/src/backend/po/pt_BR.po
@@ -1264,7 +1264,7 @@ msgstr "O agrupamento de banco de dados parece utilizar um formato de número de
 
 #: access/transam/xlog.c:4506
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
 msgstr "O agrupamento de banco de dados foi inicializado com BLCSZ %d, mas o servidor foi compilado com BLCSZ %d."
 
 #: access/transam/xlog.c:4509 access/transam/xlog.c:4516
diff --git a/src/backend/po/ru.po b/src/backend/po/ru.po
index ae9c50eed7..9118189857 100644
--- a/src/backend/po/ru.po
+++ b/src/backend/po/ru.po
@@ -2619,11 +2619,11 @@ msgstr ""
 #: access/transam/xlog.c:4096
 #, c-format
 msgid ""
-"The database cluster was initialized with BLCKSZ %d, but the server was "
-"compiled with BLCKSZ %d."
+"The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was "
+"compiled with CLUSTER_BLOCK_SIZE %d."
 msgstr ""
-"Кластер баз данных был инициализирован с BLCKSZ %d, но сервер скомпилирован "
-"с BLCKSZ %d."
+"Кластер баз данных был инициализирован с CLUSTER_BLOCK_SIZE %d, но сервер скомпилирован "
+"с CLUSTER_BLOCK_SIZE %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
diff --git a/src/backend/po/sv.po b/src/backend/po/sv.po
index a5ac231971..2d5aef8b02 100644
--- a/src/backend/po/sv.po
+++ b/src/backend/po/sv.po
@@ -2306,8 +2306,8 @@ msgstr "Databasklustret verkar använda en annan flyttalsrepresentation än vad
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Databasklustret initierades med BLCKSZ %d, men servern kompilerades med BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "Databasklustret initierades med CLUSTER_BLOCK_SIZE %d, men servern kompilerades med CLUSTER_BLOCK_SIZE %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
diff --git a/src/backend/po/tr.po b/src/backend/po/tr.po
index b791e886b9..87074381a5 100644
--- a/src/backend/po/tr.po
+++ b/src/backend/po/tr.po
@@ -1761,8 +1761,8 @@ msgstr "Veritabanı dosyaları, sunucu programından farklı ondalık sayı biç
 
 #: access/transam/xlog.c:4676
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Veritabanı clusteri BLCKSZ %d ile ilklendirilmiştir, ancak sunucu  BLCKSZ %d ile derlenmiştir."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "Veritabanı clusteri CLUSTER_BLOCK_SIZE %d ile ilklendirilmiştir, ancak sunucu  CLUSTER_BLOCK_SIZE %d ile derlenmiştir."
 
 #: access/transam/xlog.c:4679 access/transam/xlog.c:4686 access/transam/xlog.c:4693 access/transam/xlog.c:4700 access/transam/xlog.c:4707 access/transam/xlog.c:4714 access/transam/xlog.c:4721 access/transam/xlog.c:4729 access/transam/xlog.c:4736 access/transam/xlog.c:4745 access/transam/xlog.c:4752
 #, c-format
diff --git a/src/backend/po/uk.po b/src/backend/po/uk.po
index 1095fd9139..ef22b41d43 100644
--- a/src/backend/po/uk.po
+++ b/src/backend/po/uk.po
@@ -2288,8 +2288,8 @@ msgstr "Здається, в кластері баз даних і в прогр
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Кластер бази даних було ініціалізовано з BLCKSZ %d,  але сервер було скомпільовано з BLCKSZ %d."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "Кластер бази даних було ініціалізовано з CLUSTER_BLOCK_SIZE %d,  але сервер було скомпільовано з CLUSTER_BLOCK_SIZE %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
diff --git a/src/backend/po/zh_CN.po b/src/backend/po/zh_CN.po
index 574684d775..6acbabf785 100644
--- a/src/backend/po/zh_CN.po
+++ b/src/backend/po/zh_CN.po
@@ -1852,8 +1852,8 @@ msgstr "数据库集群在使用与服务器执行部分不同的浮点数格式
 
 #: access/transam/xlog.c:4677
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "数据库簇是以 BLCKSZ %d 初始化的, 但是 服务器是以 BLCKSZ %d 编译的."
+msgid "The database cluster was initialized with CLUSTER_BLOCK_SIZE %d, but the server was compiled with CLUSTER_BLOCK_SIZE %d."
+msgstr "数据库簇是以 CLUSTER_BLOCK_SIZE %d 初始化的, 但是 服务器是以 CLUSTER_BLOCK_SIZE %d 编译的."
 
 #: access/transam/xlog.c:4680 access/transam/xlog.c:4687
 #: access/transam/xlog.c:4694 access/transam/xlog.c:4701
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 0ee764d68f..83d6d735d9 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -2062,7 +2062,7 @@ apply_spooled_messages(FileSet *stream_fileset, TransactionId xid,
 
 	CurrentResourceOwner = oldowner;
 
-	buffer = palloc(BLCKSZ);
+	buffer = palloc(CLUSTER_BLOCK_SIZE);
 	initStringInfo(&s2);
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index 0057443f0c..b7e0d4afa5 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -82,7 +82,7 @@ InitBufferPool(void)
 	BufferBlocks = (char *)
 		TYPEALIGN(PG_IO_ALIGN_SIZE,
 				  ShmemInitStruct("Buffer Blocks",
-								  NBuffers * (Size) BLCKSZ + PG_IO_ALIGN_SIZE,
+								  NBuffers * (Size) CLUSTER_BLOCK_SIZE + PG_IO_ALIGN_SIZE,
 								  &foundBufs));
 
 	/* Align condition variables to cacheline boundary. */
@@ -168,7 +168,7 @@ BufferShmemSize(void)
 
 	/* size of data pages, plus alignment padding */
 	size = add_size(size, PG_IO_ALIGN_SIZE);
-	size = add_size(size, mul_size(NBuffers, BLCKSZ));
+	size = add_size(size, mul_size(NBuffers, CLUSTER_BLOCK_SIZE));
 
 	/* size of stuff controlled by freelist.c */
 	size = add_size(size, StrategyShmemSize());
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 3c59bbd04e..f31f15d7f7 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -60,7 +60,7 @@
 
 
 /* Note: these two macros only work on shared buffers, not local ones! */
-#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * BLCKSZ))
+#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * CLUSTER_BLOCK_SIZE))
 #define BufferGetLSN(bufHdr)	(PageGetLSN(BufHdrGetBlock(bufHdr)))
 
 /* Note: this macro only works on local buffers, not shared ones! */
@@ -1116,7 +1116,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	 * wants us to allocate a buffer.
 	 */
 	if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
-		MemSet((char *) bufBlock, 0, BLCKSZ);
+		MemSet((char *) bufBlock, 0, CLUSTER_BLOCK_SIZE);
 	else
 	{
 		instr_time	io_start = pgstat_prepare_io_time();
@@ -1137,7 +1137,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 						 errmsg("invalid page in block %u of relation %s; zeroing out page",
 								blockNum,
 								relpath(smgr->smgr_rlocator, forkNum))));
-				MemSet((char *) bufBlock, 0, BLCKSZ);
+				MemSet((char *) bufBlock, 0, CLUSTER_BLOCK_SIZE);
 			}
 			else
 				ereport(ERROR,
@@ -1856,7 +1856,7 @@ ExtendBufferedRelShared(ExtendBufferedWhat eb,
 		buf_block = BufHdrGetBlock(GetBufferDescriptor(buffers[i] - 1));
 
 		/* new buffers are zero-filled */
-		MemSet((char *) buf_block, 0, BLCKSZ);
+		MemSet((char *) buf_block, 0, CLUSTER_BLOCK_SIZE);
 	}
 
 	/* in case we need to pin an existing buffer below */
@@ -2285,7 +2285,7 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy)
 				 * not generally guaranteed to be marked undefined or
 				 * non-accessible in any case.
 				 */
-				VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
+				VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), CLUSTER_BLOCK_SIZE);
 				break;
 			}
 		}
@@ -2350,7 +2350,7 @@ PinBuffer_Locked(BufferDesc *buf)
 	 * Valgrind (this is similar to the PinBuffer() case where the backend
 	 * doesn't already have a buffer pin)
 	 */
-	VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
+	VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), CLUSTER_BLOCK_SIZE);
 
 	/*
 	 * Since we hold the buffer spinlock, we can update the buffer state and
@@ -2403,7 +2403,7 @@ UnpinBuffer(BufferDesc *buf)
 		 * within access method code that enforces that buffers are only
 		 * accessed while a buffer lock is held.
 		 */
-		VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), CLUSTER_BLOCK_SIZE);
 
 		/* I'd better not still hold the buffer content lock */
 		Assert(!LWLockHeldByMe(BufferDescriptorGetContentLock(buf)));
@@ -3491,7 +3491,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 	if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
 	{
 		/*
-		 * Not every table AM uses BLCKSZ wide fixed size blocks. Therefore
+		 * Not every table AM uses CLUSTER_BLOCK_SIZE wide fixed size blocks. Therefore
 		 * tableam returns the size in bytes - but for the purpose of this
 		 * routine, we want the number of blocks. Therefore divide, rounding
 		 * up.
@@ -3500,7 +3500,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 
 		szbytes = table_relation_size(relation, forkNum);
 
-		return (szbytes + (BLCKSZ - 1)) / BLCKSZ;
+		return (szbytes + (CLUSTER_BLOCK_SIZE - 1)) / CLUSTER_BLOCK_SIZE;
 	}
 	else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
 	{
@@ -4288,7 +4288,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
 	 * Bulk extend the destination relation of the same size as the source
 	 * relation before starting to copy block by block.
 	 */
-	memset(buf.data, 0, BLCKSZ);
+	memset(buf.data, 0, CLUSTER_BLOCK_SIZE);
 	smgrextend(smgropen(dstlocator, InvalidBackendId), forkNum, nblocks - 1,
 			   buf.data, true);
 
@@ -4316,7 +4316,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
 		START_CRIT_SECTION();
 
 		/* Copy page data from the source to the destination. */
-		memcpy(dstPage, srcPage, BLCKSZ);
+		memcpy(dstPage, srcPage, CLUSTER_BLOCK_SIZE);
 		MarkBufferDirty(dstBuf);
 
 		/* WAL-log the copied page. */
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index 1c804fd2f5..326de13d10 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -589,7 +589,7 @@ GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)
 	Assert(ring_size_kb >= 0);
 
 	/* Figure out how many buffers ring_size_kb is */
-	ring_buffers = ring_size_kb / (BLCKSZ / 1024);
+	ring_buffers = ring_size_kb / (CLUSTER_BLOCK_SIZE / 1024);
 
 	/* 0 means unlimited, so no BufferAccessStrategy required */
 	if (ring_buffers == 0)
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index f684862d98..2939f908d4 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -335,7 +335,7 @@ ExtendBufferedRelLocal(ExtendBufferedWhat eb,
 		buf_block = LocalBufHdrGetBlock(buf_hdr);
 
 		/* new buffers are zero-filled */
-		MemSet((char *) buf_block, 0, BLCKSZ);
+		MemSet((char *) buf_block, 0, CLUSTER_BLOCK_SIZE);
 	}
 
 	first_block = smgrnblocks(eb.smgr, fork);
@@ -745,19 +745,19 @@ GetLocalBufferStorage(void)
 		/* But not more than what we need for all remaining local bufs */
 		num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
 		/* And don't overflow MaxAllocSize, either */
-		num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
+		num_bufs = Min(num_bufs, MaxAllocSize / CLUSTER_BLOCK_SIZE);
 
 		/* Buffers should be I/O aligned. */
 		cur_block = (char *)
 			TYPEALIGN(PG_IO_ALIGN_SIZE,
 					  MemoryContextAlloc(LocalBufferContext,
-										 num_bufs * BLCKSZ + PG_IO_ALIGN_SIZE));
+										 num_bufs * CLUSTER_BLOCK_SIZE + PG_IO_ALIGN_SIZE));
 		next_buf_in_block = 0;
 		num_bufs_in_block = num_bufs;
 	}
 
 	/* Allocate next buffer in current memory block */
-	this_buf = cur_block + next_buf_in_block * BLCKSZ;
+	this_buf = cur_block + next_buf_in_block * CLUSTER_BLOCK_SIZE;
 	next_buf_in_block++;
 	total_bufs_allocated++;
 
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index 41ab64100e..f382d0a2ea 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -60,7 +60,7 @@
  * tablespaces when available.
  */
 #define MAX_PHYSICAL_FILESIZE	0x40000000
-#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / BLCKSZ)
+#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / CLUSTER_BLOCK_SIZE)
 
 /*
  * This data structure represents a buffered file that consists of one or
@@ -681,7 +681,7 @@ BufFileWrite(BufFile *file, const void *ptr, size_t size)
 
 	while (size > 0)
 	{
-		if (file->pos >= BLCKSZ)
+		if (file->pos >= CLUSTER_BLOCK_SIZE)
 		{
 			/* Buffer full, dump it out */
 			if (file->dirty)
@@ -695,7 +695,7 @@ BufFileWrite(BufFile *file, const void *ptr, size_t size)
 			}
 		}
 
-		nthistime = BLCKSZ - file->pos;
+		nthistime = CLUSTER_BLOCK_SIZE - file->pos;
 		if (nthistime > size)
 			nthistime = size;
 		Assert(nthistime > 0);
@@ -839,9 +839,9 @@ BufFileTell(BufFile *file, int *fileno, off_t *offset)
 /*
  * BufFileSeekBlock --- block-oriented seek
  *
- * Performs absolute seek to the start of the n'th BLCKSZ-sized block of
+ * Performs absolute seek to the start of the n'th CLUSTER_BLOCK_SIZE-sized block of
  * the file.  Note that users of this interface will fail if their files
- * exceed BLCKSZ * LONG_MAX bytes, but that is quite a lot; we don't work
+ * exceed CLUSTER_BLOCK_SIZE * LONG_MAX bytes, but that is quite a lot; we don't work
  * with tables bigger than that, either...
  *
  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
@@ -852,7 +852,7 @@ BufFileSeekBlock(BufFile *file, long blknum)
 {
 	return BufFileSeek(file,
 					   (int) (blknum / BUFFILE_SEG_SIZE),
-					   (off_t) (blknum % BUFFILE_SEG_SIZE) * BLCKSZ,
+					   (off_t) (blknum % BUFFILE_SEG_SIZE) * CLUSTER_BLOCK_SIZE,
 					   SEEK_SET);
 }
 
@@ -867,7 +867,7 @@ BufFileTellBlock(BufFile *file)
 {
 	long		blknum;
 
-	blknum = (file->curOffset + file->pos) / BLCKSZ;
+	blknum = (file->curOffset + file->pos) / CLUSTER_BLOCK_SIZE;
 	blknum += file->curFile * BUFFILE_SEG_SIZE;
 	return blknum;
 }
diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c
index e04bc3941a..45a83750b1 100644
--- a/src/backend/storage/file/copydir.c
+++ b/src/backend/storage/file/copydir.c
@@ -124,7 +124,7 @@ copy_file(const char *fromfile, const char *tofile)
 	off_t		flush_offset;
 
 	/* Size of copy buffer (read and write requests) */
-#define COPY_BUF_SIZE (8 * BLCKSZ)
+#define COPY_BUF_SIZE (8 * CLUSTER_BLOCK_SIZE)
 
 	/*
 	 * Size of data flush requests.  It seems beneficial on most platforms to
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 3c2a2fbef7..a7a6cd15a9 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3945,10 +3945,10 @@ check_io_direct(char **newval, void **extra, GucSource source)
 		result = false;
 	}
 #endif
-#if BLCKSZ < PG_IO_ALIGN_SIZE
+#if DEFAULT_BLOCK_SIZE < PG_IO_ALIGN_SIZE
 	if (result && (flags & IO_DIRECT_DATA))
 	{
-		GUC_check_errdetail("debug_io_direct is not supported for data because BLCKSZ is too small");
+		GUC_check_errdetail("debug_io_direct is not supported for data because DEFAULT_BLOCK_SIZE is too small");
 		result = false;
 	}
 #endif
diff --git a/src/backend/storage/freespace/README b/src/backend/storage/freespace/README
index e7ff23b76f..7d03236b95 100644
--- a/src/backend/storage/freespace/README
+++ b/src/backend/storage/freespace/README
@@ -14,8 +14,8 @@ It is important to keep the map small so that it can be searched rapidly.
 Therefore, we don't attempt to record the exact free space on a page.
 We allocate one map byte to each page, allowing us to record free space
 at a granularity of 1/256th of a page.  Another way to say it is that
-the stored value is the free space divided by BLCKSZ/256 (rounding down).
-We assume that the free space must always be less than BLCKSZ, since
+the stored value is the free space divided by CLUSTER_BLOCK_SIZE/256 (rounding down).
+We assume that the free space must always be less than CLUSTER_BLOCK_SIZE, since
 all pages have some overhead; so the maximum map value is 255.
 
 To assist in fast searching, the map isn't simply an array of per-page
@@ -97,7 +97,7 @@ has the same value as the corresponding leaf node on its parent page.
 The root page is always stored at physical block 0.
 
 For example, assuming each FSM page can hold information about 4 pages (in
-reality, it holds (BLCKSZ - headers) / 2, or ~4000 with default BLCKSZ),
+reality, it holds (CLUSTER_BLOCK_SIZE - headers) / 2, or ~4000 with default CLUSTER_BLOCK_SIZE),
 we get a disk layout like this:
 
  0     <-- page 0 at level 2 (root page)
@@ -136,7 +136,7 @@ and so forth.
 
 To keep things simple, the tree is always constant height. To cover the
 maximum relation size of 2^32-1 blocks, three levels is enough with the default
-BLCKSZ (4000^3 > 2^32).
+CLUSTER_BLOCK_SIZE (4000^3 > 2^32).
 
 Addressing
 ----------
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index 2face615d0..5a28252dcd 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -41,8 +41,8 @@
  * represents the range from 254 * FSM_CAT_STEP, inclusive, to
  * MaxFSMRequestSize, exclusive.
  *
- * MaxFSMRequestSize depends on the architecture and BLCKSZ, but assuming
- * default 8k BLCKSZ, and that MaxFSMRequestSize is 8164 bytes, the
+ * MaxFSMRequestSize depends on the architecture and CLUSTER_BLOCK_SIZE, but assuming
+ * default 8k CLUSTER_BLOCK_SIZE, and that MaxFSMRequestSize is 8164 bytes, the
  * categories look like this:
  *
  *
@@ -62,14 +62,14 @@
  * request of exactly MaxFSMRequestSize bytes.
  */
 #define FSM_CATEGORIES	256
-#define FSM_CAT_STEP	(BLCKSZ / FSM_CATEGORIES)
+#define FSM_CAT_STEP	(CLUSTER_BLOCK_SIZE / FSM_CATEGORIES)
 #define MaxFSMRequestSize	MaxHeapTupleSize
 
 /*
  * Depth of the on-disk tree. We need to be able to address 2^32-1 blocks,
  * and 1626 is the smallest number that satisfies X^3 >= 2^32-1. Likewise,
  * 256 is the smallest number that satisfies X^4 >= 2^32-1. In practice,
- * this means that 4096 bytes is the smallest BLCKSZ that we can get away
+ * this means that 4096 bytes is the smallest CLUSTER_BLOCK_SIZE that we can get away
  * with a 3-level tree, and 512 is the smallest we support.
  */
 #define FSM_TREE_DEPTH	((SlotsPerFSMPage >= 1626) ? 3 : 4)
@@ -87,8 +87,8 @@ typedef struct
 	int			logpageno;		/* page number within the level */
 } FSMAddress;
 
-/* Address of the root page. */
-static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0};
+/* Address of the root page. Level is adjusted by FreeSpaceMapInit()  */
+static FSMAddress FSM_ROOT_ADDRESS = {0,0};
 
 /* functions to navigate the tree */
 static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot);
@@ -116,6 +116,17 @@ static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr,
 
 /******** Public API ********/
 
+/*
+ * FreeSpaceMapInit - initialize the FSM system with the cluster block size
+ *
+ */
+
+void
+FreeSpaceMapInit(void)
+{
+	FSM_ROOT_ADDRESS.level = FSM_ROOT_LEVEL;
+}
+
 /*
  * GetPageWithFreeSpace - try to find a page in the given relation with
  *		at least the specified amount of free space.
@@ -217,7 +228,7 @@ XLogRecordPageWithFreeSpace(RelFileLocator rlocator, BlockNumber heapBlk,
 
 	page = BufferGetPage(buf);
 	if (PageIsNew(page))
-		PageInit(page, BLCKSZ, 0);
+		PageInit(page, CLUSTER_BLOCK_SIZE, 0);
 
 	if (fsm_set_avail(page, slot, new_cat))
 		MarkBufferDirtyHint(buf, false);
@@ -370,7 +381,7 @@ fsm_space_avail_to_cat(Size avail)
 {
 	int			cat;
 
-	Assert(avail < BLCKSZ);
+	Assert(avail < CLUSTER_BLOCK_SIZE);
 
 	if (avail >= MaxFSMRequestSize)
 		return 255;
@@ -598,7 +609,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), CLUSTER_BLOCK_SIZE, 0);
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	}
 	return buf;
diff --git a/src/backend/storage/freespace/indexfsm.c b/src/backend/storage/freespace/indexfsm.c
index fff8f4fbfb..ba10e22bc0 100644
--- a/src/backend/storage/freespace/indexfsm.c
+++ b/src/backend/storage/freespace/indexfsm.c
@@ -16,7 +16,7 @@
  *	This is similar to the FSM used for heap, in freespace.c, but instead
  *	of tracking the amount of free space on pages, we only track whether
  *	pages are completely free or in-use. We use the same FSM implementation
- *	as for heaps, using BLCKSZ - 1 to denote used pages, and 0 for unused.
+ *	as for heaps, using CLUSTER_BLOCK_SIZE - 1 to denote used pages, and 0 for unused.
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,7 @@
 BlockNumber
 GetFreeIndexPage(Relation rel)
 {
-	BlockNumber blkno = GetPageWithFreeSpace(rel, BLCKSZ / 2);
+	BlockNumber blkno = GetPageWithFreeSpace(rel, CLUSTER_BLOCK_SIZE / 2);
 
 	if (blkno != InvalidBlockNumber)
 		RecordUsedIndexPage(rel, blkno);
@@ -51,7 +51,7 @@ GetFreeIndexPage(Relation rel)
 void
 RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
 {
-	RecordPageWithFreeSpace(rel, freeBlock, BLCKSZ - 1);
+	RecordPageWithFreeSpace(rel, freeBlock, CLUSTER_BLOCK_SIZE - 1);
 }
 
 
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 84e543e731..a9bc230e4a 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -598,7 +598,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[LOBLKSIZE + VARHDRSZ];
+		char		data[LOBLKSIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
@@ -789,7 +789,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[LOBLKSIZE + VARHDRSZ];
+		char		data[LOBLKSIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 1af41213b4..1eab69fc54 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -321,7 +321,7 @@ static SlruCtlData SerialSlruCtlData;
 
 #define SerialSlruCtl			(&SerialSlruCtlData)
 
-#define SERIAL_PAGESIZE			BLCKSZ
+#define SERIAL_PAGESIZE			CLUSTER_BLOCK_SIZE
 #define SERIAL_ENTRYSIZE			sizeof(SerCommitSeqNo)
 #define SERIAL_ENTRIESPERPAGE	(SERIAL_PAGESIZE / SERIAL_ENTRYSIZE)
 
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 9a302ddc30..a96fa911a5 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -45,7 +45,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
 
 	specialSize = MAXALIGN(specialSize);
 
-	Assert(pageSize == BLCKSZ);
+	Assert(pageSize == CLUSTER_BLOCK_SIZE);
 	Assert(pageSize > specialSize + SizeOfPageHeaderData);
 
 	/* Make sure all fields of page are zero, as well as unused space */
@@ -102,7 +102,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 	{
 		if (DataChecksumsEnabled())
 		{
-			checksum = pg_checksum_page((char *) page, blkno);
+			checksum = pg_checksum_page((char *) page, blkno, CLUSTER_BLOCK_SIZE);
 
 			if (checksum != p->pd_checksum)
 				checksum_failure = true;
@@ -117,7 +117,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 		if ((p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
 			p->pd_lower <= p->pd_upper &&
 			p->pd_upper <= p->pd_special &&
-			p->pd_special <= BLCKSZ &&
+			p->pd_special <= CLUSTER_BLOCK_SIZE &&
 			p->pd_special == MAXALIGN(p->pd_special))
 			header_sane = true;
 
@@ -128,7 +128,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 	/* Check all-zeroes case */
 	all_zeroes = true;
 	pagebytes = (size_t *) page;
-	for (i = 0; i < (BLCKSZ / sizeof(size_t)); i++)
+	for (i = 0; i < (CLUSTER_BLOCK_SIZE / sizeof(size_t)); i++)
 	{
 		if (pagebytes[i] != 0)
 		{
@@ -211,7 +211,7 @@ PageAddItemExtended(Page page,
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ)
+		phdr->pd_special > CLUSTER_BLOCK_SIZE)
 		ereport(PANIC,
 				(errcode(ERRCODE_DATA_CORRUPTED),
 				 errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
@@ -702,7 +702,7 @@ PageRepairFragmentation(Page page)
 	Offset		pd_upper = ((PageHeader) page)->pd_upper;
 	Offset		pd_special = ((PageHeader) page)->pd_special;
 	Offset		last_offset;
-	itemIdCompactData itemidbase[MaxHeapTuplesPerPage];
+	itemIdCompactData itemidbase[MaxHeapTuplesPerPageLimit];
 	itemIdCompact itemidptr;
 	ItemId		lp;
 	int			nline,
@@ -723,7 +723,7 @@ PageRepairFragmentation(Page page)
 	if (pd_lower < SizeOfPageHeaderData ||
 		pd_lower > pd_upper ||
 		pd_upper > pd_special ||
-		pd_special > BLCKSZ ||
+		pd_special > CLUSTER_BLOCK_SIZE ||
 		pd_special != MAXALIGN(pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1066,7 +1066,7 @@ PageIndexTupleDelete(Page page, OffsetNumber offnum)
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > CLUSTER_BLOCK_SIZE ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1165,8 +1165,8 @@ PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
 	Offset		pd_upper = phdr->pd_upper;
 	Offset		pd_special = phdr->pd_special;
 	Offset		last_offset;
-	itemIdCompactData itemidbase[MaxIndexTuplesPerPage];
-	ItemIdData	newitemids[MaxIndexTuplesPerPage];
+	itemIdCompactData itemidbase[MaxIndexTuplesPerPageLimit];
+	ItemIdData	newitemids[MaxIndexTuplesPerPageLimit];
 	itemIdCompact itemidptr;
 	ItemId		lp;
 	int			nline,
@@ -1201,7 +1201,7 @@ PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
 	if (pd_lower < SizeOfPageHeaderData ||
 		pd_lower > pd_upper ||
 		pd_upper > pd_special ||
-		pd_special > BLCKSZ ||
+		pd_special > CLUSTER_BLOCK_SIZE ||
 		pd_special != MAXALIGN(pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1307,7 +1307,7 @@ PageIndexTupleDeleteNoCompact(Page page, OffsetNumber offnum)
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > CLUSTER_BLOCK_SIZE ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1419,7 +1419,7 @@ PageIndexTupleOverwrite(Page page, OffsetNumber offnum,
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > CLUSTER_BLOCK_SIZE ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1523,12 +1523,12 @@ PageSetChecksumCopy(Page page, BlockNumber blkno)
 	 */
 	if (pageCopy == NULL)
 		pageCopy = MemoryContextAllocAligned(TopMemoryContext,
-											 BLCKSZ,
+											 CLUSTER_BLOCK_SIZE,
 											 PG_IO_ALIGN_SIZE,
 											 0);
 
-	memcpy(pageCopy, (char *) page, BLCKSZ);
-	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno);
+	memcpy(pageCopy, (char *) page, CLUSTER_BLOCK_SIZE);
+	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno, CLUSTER_BLOCK_SIZE);
 	return pageCopy;
 }
 
@@ -1545,5 +1545,5 @@ PageSetChecksumInplace(Page page, BlockNumber blkno)
 	if (PageIsNew(page) || !DataChecksumsEnabled())
 		return;
 
-	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno);
+	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno, CLUSTER_BLOCK_SIZE);
 }
diff --git a/src/backend/storage/smgr/README b/src/backend/storage/smgr/README
index cf3aa5632b..9a32663416 100644
--- a/src/backend/storage/smgr/README
+++ b/src/backend/storage/smgr/README
@@ -50,3 +50,55 @@ to relfilelocator and block number, to identify which relation fork you want to
 access. Since most code wants to access the main fork, a shortcut version of
 ReadBuffer that accesses MAIN_FORKNUM is provided in the buffer manager for
 convenience.
+
+
+Adjustments for Variable Block Sizes
+====================================
+
+Prior to 17, the disk block size (aka BLCKSZ) was set at compile-time. While
+each database cluster still has a fixed block size, this is now selected at
+`initdb` time, meaning that a single PostgreSQL binary installation can support
+clusters of varying sizes.
+
+As this is now a per-cluster setting, the block size is set in the pg_control
+file. There are multiple computed settings which rely on the cluster block size,
+so these also need to be initialized at cluster start.  This is managed via
+BlockSizeInit() in src/common/blocksize.c.  This routine computes a table for
+all values which would have been computed at compile time.
+
+The expressions used to calculate the values in this table have been moved from
+their original locations and parameterized based on blocksize; these are named
+CalcXXXX(blocksize), and are stored in src/include/common/blocksize.h.
+
+In order to minimize code changes, we have kept the names of former constants
+(such as MaxHeapTuplesPerPage) and replaced their definitions with an
+appropriate lookup in this table.  Since these lookups are now handled at
+runtime, it is important to initialize these as early as possible so any code
+usage of these constants uses the computed value after it has been initialized
+instead of 0.  This applies to any piece of the code which needs to know about
+the block size or uses any value which was based on it.
+
+To ensure that any code that depended on the old behavior is properly adjusted,
+we have removed BLCKSZ as a constant, which will ensure that external code will
+fail to compile.  When writing new code which cases about the block size, you
+should use one of the following values:
+
+- CLUSTER_BLOCK_SIZE - evaluates to the cluster's block size as initialized by
+  BlockSizeInit().
+
+- DEFAULT_BLOCK_SIZE - the compiled-in default block size; 8k
+
+- MIN_BLOCK_SIZE - the smallest supported block size; 1k
+
+- MAX_BLOCK_SIZE - the largest supported block size; 32k
+
+In general, you should use CLUSTER_BLOCK_SIZE unless you are allocating space on
+the stack, and even then you should be careful to allocate based on
+MAX_BLOCK_SIZE, but still limit accesses to these structs based on the /actual/
+runtime cluster block size.  The core code has bene modified to follow these
+standards.
+
+Occasionally, we had been using BLCKSZ as a "big enough buffer" rather than
+caring about any specific size correlated with the cluster.  In those cases, you
+should just use DEFAULT_BLOCK_SIZE.
+
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 30dbc02f82..24454420c0 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -47,13 +47,13 @@
  * easier to support relations that are larger than the operating
  * system's file size limit (often 2GBytes).  In order to do that,
  * we break relations up into "segment" files that are each shorter than
- * the OS file size limit.  The segment size is set by the RELSEG_SIZE
+ * the OS file size limit.  The segment size is set by the CLUSTER_RELSEG_SIZE
  * configuration constant in pg_config.h.
  *
  * On disk, a relation must consist of consecutively numbered segment
  * files in the pattern
- *	-- Zero or more full segments of exactly RELSEG_SIZE blocks each
- *	-- Exactly one partial segment of size 0 <= size < RELSEG_SIZE blocks
+ *	-- Zero or more full segments of exactly CLUSTER_RELSEG_SIZE blocks each
+ *	-- Exactly one partial segment of size 0 <= size < CLUSTER_RELSEG_SIZE blocks
  *	-- Optionally, any number of inactive segments of size 0 blocks.
  * The full and partial segments are collectively the "active" segments.
  * Inactive segments are those that once contained data but are currently
@@ -110,7 +110,7 @@ static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 #define EXTENSION_CREATE_RECOVERY	(1 << 3)
 /*
  * Allow opening segments which are preceded by segments smaller than
- * RELSEG_SIZE, e.g. inactive segments (see above). Note that this breaks
+ * CLUSTER_RELSEG_SIZE, e.g. inactive segments (see above). Note that this breaks
  * mdnblocks() and related functionality henceforth - which currently is ok,
  * because this is only required in the checkpointer which never uses
  * mdnblocks().
@@ -464,7 +464,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= CLUSTER_BLOCK_SIZE)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	/* This assert is too expensive to have on normally ... */
@@ -487,11 +487,11 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync, EXTENSION_CREATE);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) CLUSTER_BLOCK_SIZE * (blocknum % ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) CLUSTER_BLOCK_SIZE * CLUSTER_RELSEG_SIZE);
 
-	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != BLCKSZ)
+	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, CLUSTER_BLOCK_SIZE, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != CLUSTER_BLOCK_SIZE)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -504,14 +504,14 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 				(errcode(ERRCODE_DISK_FULL),
 				 errmsg("could not extend file \"%s\": wrote only %d of %d bytes at block %u",
 						FilePathName(v->mdfd_vfd),
-						nbytes, BLCKSZ, blocknum),
+						nbytes, CLUSTER_BLOCK_SIZE, blocknum),
 				 errhint("Check free disk space.")));
 	}
 
 	if (!skipFsync && !SmgrIsTemp(reln))
 		register_dirty_segment(reln, forknum, v);
 
-	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) CLUSTER_RELSEG_SIZE));
 }
 
 /*
@@ -549,19 +549,19 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 
 	while (remblocks > 0)
 	{
-		BlockNumber segstartblock = curblocknum % ((BlockNumber) RELSEG_SIZE);
-		off_t		seekpos = (off_t) BLCKSZ * segstartblock;
+		BlockNumber segstartblock = curblocknum % ((BlockNumber) CLUSTER_RELSEG_SIZE);
+		off_t		seekpos = (off_t) CLUSTER_BLOCK_SIZE * segstartblock;
 		int			numblocks;
 
-		if (segstartblock + remblocks > RELSEG_SIZE)
-			numblocks = RELSEG_SIZE - segstartblock;
+		if (segstartblock + remblocks > CLUSTER_RELSEG_SIZE)
+			numblocks = CLUSTER_RELSEG_SIZE - segstartblock;
 		else
 			numblocks = remblocks;
 
 		v = _mdfd_getseg(reln, forknum, curblocknum, skipFsync, EXTENSION_CREATE);
 
-		Assert(segstartblock < RELSEG_SIZE);
-		Assert(segstartblock + numblocks <= RELSEG_SIZE);
+		Assert(segstartblock < CLUSTER_RELSEG_SIZE);
+		Assert(segstartblock + numblocks <= CLUSTER_RELSEG_SIZE);
 
 		/*
 		 * If available and useful, use posix_fallocate() (via
@@ -579,7 +579,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 			int			ret;
 
 			ret = FileFallocate(v->mdfd_vfd,
-								seekpos, (off_t) BLCKSZ * numblocks,
+								seekpos, (off_t) CLUSTER_BLOCK_SIZE * numblocks,
 								WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret != 0)
 			{
@@ -602,7 +602,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 			 * whole length of the extension.
 			 */
 			ret = FileZero(v->mdfd_vfd,
-						   seekpos, (off_t) BLCKSZ * numblocks,
+						   seekpos, (off_t) CLUSTER_BLOCK_SIZE * numblocks,
 						   WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret < 0)
 				ereport(ERROR,
@@ -615,7 +615,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 		if (!skipFsync && !SmgrIsTemp(reln))
 			register_dirty_segment(reln, forknum, v);
 
-		Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+		Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
 		remblocks -= numblocks;
 		curblocknum += numblocks;
@@ -667,7 +667,7 @@ mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 	mdfd->mdfd_vfd = fd;
 	mdfd->mdfd_segno = 0;
 
-	Assert(_mdnblocks(reln, forknum, mdfd) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, mdfd) <= ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
 	return mdfd;
 }
@@ -723,11 +723,11 @@ mdprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 	if (v == NULL)
 		return false;
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) CLUSTER_BLOCK_SIZE * (blocknum % ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) CLUSTER_BLOCK_SIZE * CLUSTER_RELSEG_SIZE);
 
-	(void) FilePrefetch(v->mdfd_vfd, seekpos, BLCKSZ, WAIT_EVENT_DATA_FILE_PREFETCH);
+	(void) FilePrefetch(v->mdfd_vfd, seekpos, CLUSTER_BLOCK_SIZE, WAIT_EVENT_DATA_FILE_PREFETCH);
 #endif							/* USE_PREFETCH */
 
 	return true;
@@ -745,7 +745,7 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= CLUSTER_BLOCK_SIZE)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	TRACE_POSTGRESQL_SMGR_MD_READ_START(forknum, blocknum,
@@ -757,11 +757,11 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, false,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) CLUSTER_BLOCK_SIZE * (blocknum % ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) CLUSTER_BLOCK_SIZE * CLUSTER_RELSEG_SIZE);
 
-	nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_READ);
+	nbytes = FileRead(v->mdfd_vfd, buffer, CLUSTER_BLOCK_SIZE, seekpos, WAIT_EVENT_DATA_FILE_READ);
 
 	TRACE_POSTGRESQL_SMGR_MD_READ_DONE(forknum, blocknum,
 									   reln->smgr_rlocator.locator.spcOid,
@@ -769,9 +769,9 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 									   reln->smgr_rlocator.locator.relNumber,
 									   reln->smgr_rlocator.backend,
 									   nbytes,
-									   BLCKSZ);
+									   CLUSTER_BLOCK_SIZE);
 
-	if (nbytes != BLCKSZ)
+	if (nbytes != CLUSTER_BLOCK_SIZE)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -788,13 +788,13 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 		 * update a block that was later truncated away.
 		 */
 		if (zero_damaged_pages || InRecovery)
-			MemSet(buffer, 0, BLCKSZ);
+			MemSet(buffer, 0, CLUSTER_BLOCK_SIZE);
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
 							blocknum, FilePathName(v->mdfd_vfd),
-							nbytes, BLCKSZ)));
+							nbytes, CLUSTER_BLOCK_SIZE)));
 	}
 }
 
@@ -814,7 +814,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= CLUSTER_BLOCK_SIZE)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	/* This assert is too expensive to have on normally ... */
@@ -831,11 +831,11 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) CLUSTER_BLOCK_SIZE * (blocknum % ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) CLUSTER_BLOCK_SIZE * CLUSTER_RELSEG_SIZE);
 
-	nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
+	nbytes = FileWrite(v->mdfd_vfd, buffer, CLUSTER_BLOCK_SIZE, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
 
 	TRACE_POSTGRESQL_SMGR_MD_WRITE_DONE(forknum, blocknum,
 										reln->smgr_rlocator.locator.spcOid,
@@ -843,9 +843,9 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 										reln->smgr_rlocator.locator.relNumber,
 										reln->smgr_rlocator.backend,
 										nbytes,
-										BLCKSZ);
+										CLUSTER_BLOCK_SIZE);
 
-	if (nbytes != BLCKSZ)
+	if (nbytes != CLUSTER_BLOCK_SIZE)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -858,7 +858,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 				 errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
 						blocknum,
 						FilePathName(v->mdfd_vfd),
-						nbytes, BLCKSZ),
+						nbytes, CLUSTER_BLOCK_SIZE),
 				 errhint("Check free disk space.")));
 	}
 
@@ -904,19 +904,19 @@ mdwriteback(SMgrRelation reln, ForkNumber forknum,
 			return;
 
 		/* compute offset inside the current segment */
-		segnum_start = blocknum / RELSEG_SIZE;
+		segnum_start = blocknum / CLUSTER_RELSEG_SIZE;
 
 		/* compute number of desired writes within the current segment */
-		segnum_end = (blocknum + nblocks - 1) / RELSEG_SIZE;
+		segnum_end = (blocknum + nblocks - 1) / CLUSTER_RELSEG_SIZE;
 		if (segnum_start != segnum_end)
-			nflush = RELSEG_SIZE - (blocknum % ((BlockNumber) RELSEG_SIZE));
+			nflush = CLUSTER_RELSEG_SIZE - (blocknum % ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
 		Assert(nflush >= 1);
 		Assert(nflush <= nblocks);
 
-		seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+		seekpos = (off_t) CLUSTER_BLOCK_SIZE * (blocknum % ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
-		FileWriteback(v->mdfd_vfd, seekpos, (off_t) BLCKSZ * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
+		FileWriteback(v->mdfd_vfd, seekpos, (off_t) CLUSTER_BLOCK_SIZE * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
 
 		nblocks -= nflush;
 		blocknum += nflush;
@@ -945,7 +945,7 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 
 	/*
 	 * Start from the last open segments, to avoid redundant seeks.  We have
-	 * previously verified that these segments are exactly RELSEG_SIZE long,
+	 * previously verified that these segments are exactly CLUSTER_RELSEG_SIZE long,
 	 * and it's useless to recheck that each time.
 	 *
 	 * NOTE: this assumption could only be wrong if another backend has
@@ -962,13 +962,13 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 	for (;;)
 	{
 		nblocks = _mdnblocks(reln, forknum, v);
-		if (nblocks > ((BlockNumber) RELSEG_SIZE))
+		if (nblocks > ((BlockNumber) CLUSTER_RELSEG_SIZE))
 			elog(FATAL, "segment too big");
-		if (nblocks < ((BlockNumber) RELSEG_SIZE))
-			return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks;
+		if (nblocks < ((BlockNumber) CLUSTER_RELSEG_SIZE))
+			return (segno * ((BlockNumber) CLUSTER_RELSEG_SIZE)) + nblocks;
 
 		/*
-		 * If segment is exactly RELSEG_SIZE, advance to next one.
+		 * If segment is exactly CLUSTER_RELSEG_SIZE, advance to next one.
 		 */
 		segno++;
 
@@ -981,7 +981,7 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 		 */
 		v = _mdfd_openseg(reln, forknum, segno, 0);
 		if (v == NULL)
-			return segno * ((BlockNumber) RELSEG_SIZE);
+			return segno * ((BlockNumber) CLUSTER_RELSEG_SIZE);
 	}
 }
 
@@ -1022,7 +1022,7 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 	{
 		MdfdVec    *v;
 
-		priorblocks = (curopensegs - 1) * RELSEG_SIZE;
+		priorblocks = (curopensegs - 1) * CLUSTER_RELSEG_SIZE;
 
 		v = &reln->md_seg_fds[forknum][curopensegs - 1];
 
@@ -1047,18 +1047,18 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 			FileClose(v->mdfd_vfd);
 			_fdvec_resize(reln, forknum, curopensegs - 1);
 		}
-		else if (priorblocks + ((BlockNumber) RELSEG_SIZE) > nblocks)
+		else if (priorblocks + ((BlockNumber) CLUSTER_RELSEG_SIZE) > nblocks)
 		{
 			/*
 			 * This is the last segment we want to keep. Truncate the file to
 			 * the right length. NOTE: if nblocks is exactly a multiple K of
-			 * RELSEG_SIZE, we will truncate the K+1st segment to 0 length but
+			 * CLUSTER_RELSEG_SIZE, we will truncate the K+1st segment to 0 length but
 			 * keep it. This adheres to the invariant given in the header
 			 * comments.
 			 */
 			BlockNumber lastsegblocks = nblocks - priorblocks;
 
-			if (FileTruncate(v->mdfd_vfd, (off_t) lastsegblocks * BLCKSZ, WAIT_EVENT_DATA_FILE_TRUNCATE) < 0)
+			if (FileTruncate(v->mdfd_vfd, (off_t) lastsegblocks * CLUSTER_BLOCK_SIZE, WAIT_EVENT_DATA_FILE_TRUNCATE) < 0)
 				ereport(ERROR,
 						(errcode_for_file_access(),
 						 errmsg("could not truncate file \"%s\" to %u blocks: %m",
@@ -1369,7 +1369,7 @@ _mdfd_openseg(SMgrRelation reln, ForkNumber forknum, BlockNumber segno,
 	v->mdfd_vfd = fd;
 	v->mdfd_segno = segno;
 
-	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) CLUSTER_RELSEG_SIZE));
 
 	/* all done */
 	return v;
@@ -1396,7 +1396,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 		   (EXTENSION_FAIL | EXTENSION_CREATE | EXTENSION_RETURN_NULL |
 			EXTENSION_DONT_OPEN));
 
-	targetseg = blkno / ((BlockNumber) RELSEG_SIZE);
+	targetseg = blkno / ((BlockNumber) CLUSTER_RELSEG_SIZE);
 
 	/* if an existing and opened segment, we're done */
 	if (targetseg < reln->md_num_open_segs[forknum])
@@ -1433,7 +1433,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 
 		Assert(nextsegno == v->mdfd_segno + 1);
 
-		if (nblocks > ((BlockNumber) RELSEG_SIZE))
+		if (nblocks > ((BlockNumber) CLUSTER_RELSEG_SIZE))
 			elog(FATAL, "segment too big");
 
 		if ((behavior & EXTENSION_CREATE) ||
@@ -1448,30 +1448,30 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 			 * ahead and create the segments so we can finish out the replay.
 			 *
 			 * We have to maintain the invariant that segments before the last
-			 * active segment are of size RELSEG_SIZE; therefore, if
+			 * active segment are of size CLUSTER_RELSEG_SIZE; therefore, if
 			 * extending, pad them out with zeroes if needed.  (This only
 			 * matters if in recovery, or if the caller is extending the
 			 * relation discontiguously, but that can happen in hash indexes.)
 			 */
-			if (nblocks < ((BlockNumber) RELSEG_SIZE))
+			if (nblocks < ((BlockNumber) CLUSTER_RELSEG_SIZE))
 			{
-				char	   *zerobuf = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE,
+				char	   *zerobuf = palloc_aligned(CLUSTER_BLOCK_SIZE, PG_IO_ALIGN_SIZE,
 													 MCXT_ALLOC_ZERO);
 
 				mdextend(reln, forknum,
-						 nextsegno * ((BlockNumber) RELSEG_SIZE) - 1,
+						 nextsegno * ((BlockNumber) CLUSTER_RELSEG_SIZE) - 1,
 						 zerobuf, skipFsync);
 				pfree(zerobuf);
 			}
 			flags = O_CREAT;
 		}
 		else if (!(behavior & EXTENSION_DONT_CHECK_SIZE) &&
-				 nblocks < ((BlockNumber) RELSEG_SIZE))
+				 nblocks < ((BlockNumber) CLUSTER_RELSEG_SIZE))
 		{
 			/*
 			 * When not extending (or explicitly including truncated
 			 * segments), only open the next segment if the current one is
-			 * exactly RELSEG_SIZE.  If not (this branch), either return NULL
+			 * exactly CLUSTER_RELSEG_SIZE.  If not (this branch), either return NULL
 			 * or fail.
 			 */
 			if (behavior & EXTENSION_RETURN_NULL)
@@ -1526,7 +1526,7 @@ _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 				 errmsg("could not seek to end of file \"%s\": %m",
 						FilePathName(seg->mdfd_vfd))));
 	/* note that this calculation will ignore any partial block at EOF */
-	return (BlockNumber) (len / BLCKSZ);
+	return (BlockNumber) (len / CLUSTER_BLOCK_SIZE);
 }
 
 /*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 2a4c8ef87f..27254a8b9d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1404,12 +1404,12 @@ pg_stat_get_io(PG_FUNCTION_ARGS)
 				values[IO_COL_RESET_TIME] = TimestampTzGetDatum(reset_time);
 
 				/*
-				 * Hard-code this to the value of BLCKSZ for now. Future
+				 * Hard-code this to the value of CLUSTER_BLOCK_SIZE for now. Future
 				 * values could include XLOG_BLCKSZ, once WAL IO is tracked,
 				 * and constant multipliers, once non-block-oriented IO (e.g.
 				 * temporary file IO) is tracked.
 				 */
-				values[IO_COL_CONVERSION] = Int64GetDatum(BLCKSZ);
+				values[IO_COL_CONVERSION] = Int64GetDatum(CLUSTER_BLOCK_SIZE);
 
 				for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
 				{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c4fcd0076e..9a21c946df 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -7760,7 +7760,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 * around 3 bytes per item is fairly typical.
 	 */
 	dataPagesFetchedBySel = ceil(*indexSelectivity *
-								 (numTuples / (BLCKSZ / 3)));
+								 (numTuples / (CLUSTER_BLOCK_SIZE / 3)));
 	if (dataPagesFetchedBySel > dataPagesFetched)
 		dataPagesFetched = dataPagesFetchedBySel;
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index a604432126..3ef8bc7e66 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1519,8 +1519,8 @@ AddToDataDirLockFile(int target_line, const char *str)
 	int			lineno;
 	char	   *srcptr;
 	char	   *destptr;
-	char		srcbuffer[BLCKSZ];
-	char		destbuffer[BLCKSZ];
+	char		srcbuffer[DEFAULT_BLOCK_SIZE];
+	char		destbuffer[DEFAULT_BLOCK_SIZE];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
@@ -1644,7 +1644,7 @@ RecheckDataDirLockFile(void)
 	int			fd;
 	int			len;
 	long		file_pid;
-	char		buffer[BLCKSZ];
+	char		buffer[DEFAULT_BLOCK_SIZE];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5308896c87..f2d92b9ecd 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -30,6 +30,7 @@
 
 #include "access/xact.h"
 #include "access/xlog.h"
+#include "access/xlog_internal.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_parameter_acl.h"
@@ -105,8 +106,8 @@ typedef struct
 } unit_conversion;
 
 /* Ensure that the constants in the tables don't overflow or underflow */
-#if BLCKSZ < 1024 || BLCKSZ > (1024*1024)
-#error BLCKSZ must be between 1KB and 1MB
+#if DEFAULT_BLOCK_SIZE < 1024 || DEFAULT_BLOCK_SIZE > (1024*1024)
+#error DEFAULT_BLOCK_SIZE must be between 1KB and 1MB
 #endif
 #if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
 #error XLOG_BLCKSZ must be between 1KB and 1MB
@@ -134,11 +135,11 @@ static const unit_conversion memory_unit_conversion_table[] =
 	{"kB", GUC_UNIT_MB, 1.0 / 1024.0},
 	{"B", GUC_UNIT_MB, 1.0 / (1024.0 * 1024.0)},
 
-	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (BLCKSZ / 1024)},
-	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (BLCKSZ / 1024)},
-	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (BLCKSZ / 1024)},
-	{"kB", GUC_UNIT_BLOCKS, 1.0 / (BLCKSZ / 1024)},
-	{"B", GUC_UNIT_BLOCKS, 1.0 / BLCKSZ},
+	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"kB", GUC_UNIT_BLOCKS, 1.0 / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"B", GUC_UNIT_BLOCKS, 1.0 / DEFAULT_BLOCK_SIZE},
 
 	{"TB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
 	{"GB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
@@ -1503,6 +1504,16 @@ InitializeGUCOptions(void)
 	 */
 	pg_timezone_initialize();
 
+	/* Load our block size -- no valid in Bootstrap or init mode, since control file hasn't been written */
+	if (!(IsInitProcessingMode() || IsBootstrapProcessingMode()))
+	{
+		BlockSizeInit(ClusterBlockSize());
+		/*
+		 * Ensure GUCs with dynamic limits have been properly initialized
+		 */
+		update_dynamic_gucs();
+	}
+
 	/*
 	 * Create GUCMemoryContext and build hash table of all GUC variables.
 	 */
@@ -2782,7 +2793,7 @@ get_config_unit_name(int flags)
 
 				/* initialize if first time through */
 				if (bbuf[0] == '\0')
-					snprintf(bbuf, sizeof(bbuf), "%dkB", BLCKSZ / 1024);
+					snprintf(bbuf, sizeof(bbuf), "%dkB", CLUSTER_BLOCK_SIZE / 1024);
 				return bbuf;
 			}
 		case GUC_UNIT_XBLOCKS:
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 71e27f8eb0..0cf735f02e 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -41,6 +41,7 @@
 #include "commands/trigger.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
+#include "common/blocksize.h"
 #include "common/scram-common.h"
 #include "jit/jit.h"
 #include "libpq/auth.h"
@@ -497,8 +498,11 @@ bool		log_btree_build_stats = false;
 char	   *event_source;
 
 bool		row_security;
+
 bool		check_function_bodies = true;
 
+int        	block_size;
+
 /*
  * This GUC exists solely for backward compatibility, check its definition for
  * details.
@@ -580,7 +584,6 @@ static char *session_authorization_string;
 static int	max_function_args;
 static int	max_index_keys;
 static int	max_identifier_length;
-static int	block_size;
 static int	segment_size;
 static int	shared_memory_size_mb;
 static int	shared_memory_size_in_huge_pages;
@@ -3116,7 +3119,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&block_size,
-		BLCKSZ, BLCKSZ, BLCKSZ,
+		DEFAULT_BLOCK_SIZE, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE,
 		NULL, NULL, NULL
 	},
 
@@ -3366,7 +3369,8 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_table_scan_size,
-		(8 * 1024 * 1024) / BLCKSZ, 0, INT_MAX / 3,
+		/* This is set dynamically based on CLUSTER_BLOCK_SIZE, but works for a default */
+		(8 * 1024 * 1024) / DEFAULT_BLOCK_SIZE, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -3377,7 +3381,8 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_index_scan_size,
-		(512 * 1024) / BLCKSZ, 0, INT_MAX / 3,
+		/* This is set dynamically based on CLUSTER_BLOCK_SIZE, but works for a default */
+		(512 * 1024) / DEFAULT_BLOCK_SIZE, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -4973,3 +4978,29 @@ struct config_enum ConfigureNamesEnum[] =
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
 	}
 };
+
+
+
+/*
+ * Update the specific GUCs which have dynamic limits.  Due to having variable
+ * block sizes, what were originally constants now depend on the runtime block
+ * size, so handle the adjustments to those boot values here.
+ */
+void
+update_dynamic_gucs(void)
+{
+	int i;
+
+	for (i = 0; ConfigureNamesInt[i].gen.name; i++)
+	{
+		if (strcmp("min_parallel_table_scan_size", ConfigureNamesInt[i].gen.name) == 0)
+		{
+			ConfigureNamesInt[i].boot_val = (8 * 1024 * 1024) / CLUSTER_BLOCK_SIZE;
+		}
+		else if (strcmp("min_parallel_index_scan_size", ConfigureNamesInt[i].gen.name) == 0)
+		{
+			ConfigureNamesInt[i].boot_val = (512 * 1024) / CLUSTER_BLOCK_SIZE;
+		}
+	}
+
+}
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index 52b8898d5e..a8025d2f7e 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -27,7 +27,7 @@
  * larger size than the underlying OS may support.
  *
  * For simplicity, we allocate and release space in the underlying file
- * in BLCKSZ-size blocks.  Space allocation boils down to keeping track
+ * in CLUSTER_BLOCK_SIZE-size blocks.  Space allocation boils down to keeping track
  * of which blocks in the underlying file belong to which logical tape,
  * plus any blocks that are free (recycled and not yet reused).
  * The blocks in each logical tape form a chain, with a prev- and next-
@@ -86,7 +86,7 @@
 #include "utils/memutils.h"
 
 /*
- * A TapeBlockTrailer is stored at the end of each BLCKSZ block.
+ * A TapeBlockTrailer is stored at the end of each CLUSTER_BLOCK_SIZE block.
  *
  * The first block of a tape has prev == -1.  The last block of a tape
  * stores the number of valid bytes on the block, inverted, in 'next'
@@ -100,7 +100,7 @@ typedef struct TapeBlockTrailer
 								 * bytes on last block (if < 0) */
 } TapeBlockTrailer;
 
-#define TapeBlockPayloadSize  (BLCKSZ - sizeof(TapeBlockTrailer))
+#define TapeBlockPayloadSize  (CLUSTER_BLOCK_SIZE - sizeof(TapeBlockTrailer))
 #define TapeBlockGetTrailer(buf) \
 	((TapeBlockTrailer *) ((char *) buf + TapeBlockPayloadSize))
 
@@ -192,7 +192,7 @@ struct LogicalTapeSet
 
 	/*
 	 * File size tracking.  nBlocksWritten is the size of the underlying file,
-	 * in BLCKSZ blocks.  nBlocksAllocated is the number of blocks allocated
+	 * in CLUSTER_BLOCK_SIZE blocks.  nBlocksAllocated is the number of blocks allocated
 	 * by ltsReleaseBlock(), and it is always greater than or equal to
 	 * nBlocksWritten.  Blocks between nBlocksAllocated and nBlocksWritten are
 	 * blocks that have been allocated for a tape, but have not been written
@@ -265,7 +265,7 @@ ltsWriteBlock(LogicalTapeSet *lts, long blocknum, const void *buffer)
 				(errcode_for_file_access(),
 				 errmsg("could not seek to block %ld of temporary file",
 						blocknum)));
-	BufFileWrite(lts->pfile, buffer, BLCKSZ);
+	BufFileWrite(lts->pfile, buffer, CLUSTER_BLOCK_SIZE);
 
 	/* Update nBlocksWritten, if we extended the file */
 	if (blocknum == lts->nBlocksWritten)
@@ -286,7 +286,7 @@ ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
 				(errcode_for_file_access(),
 				 errmsg("could not seek to block %ld of temporary file",
 						blocknum)));
-	BufFileReadExact(lts->pfile, buffer, BLCKSZ);
+	BufFileReadExact(lts->pfile, buffer, CLUSTER_BLOCK_SIZE);
 }
 
 /*
@@ -328,7 +328,7 @@ ltsReadFillBuffer(LogicalTape *lt)
 			lt->nextBlockNumber = TapeBlockGetTrailer(thisbuf)->next;
 
 		/* Advance to next block, if we have buffer space left */
-	} while (lt->buffer_size - lt->nbytes > BLCKSZ);
+	} while (lt->buffer_size - lt->nbytes > CLUSTER_BLOCK_SIZE);
 
 	return (lt->nbytes > 0);
 }
@@ -640,7 +640,7 @@ LogicalTapeImport(LogicalTapeSet *lts, int worker, TapeShare *shared)
 	}
 	/* Don't allocate more for read buffer than could possibly help */
 	lt->max_size = Min(MaxAllocSize, filesize);
-	tapeblocks = filesize / BLCKSZ;
+	tapeblocks = filesize / CLUSTER_BLOCK_SIZE;
 
 	/*
 	 * Update # of allocated blocks and # blocks written to reflect the
@@ -769,8 +769,8 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
 	/* Allocate data buffer and first block on first write */
 	if (lt->buffer == NULL)
 	{
-		lt->buffer = (char *) palloc(BLCKSZ);
-		lt->buffer_size = BLCKSZ;
+		lt->buffer = (char *) palloc(CLUSTER_BLOCK_SIZE);
+		lt->buffer_size = CLUSTER_BLOCK_SIZE;
 	}
 	if (lt->curBlockNumber == -1)
 	{
@@ -783,7 +783,7 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
 		TapeBlockGetTrailer(lt->buffer)->prev = -1L;
 	}
 
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == CLUSTER_BLOCK_SIZE);
 	while (size > 0)
 	{
 		if (lt->pos >= (int) TapeBlockPayloadSize)
@@ -837,9 +837,9 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
  *
  * 'buffer_size' specifies how much memory to use for the read buffer.
  * Regardless of the argument, the actual amount of memory used is between
- * BLCKSZ and MaxAllocSize, and is a multiple of BLCKSZ.  The given value is
+ * CLUSTER_BLOCK_SIZE and MaxAllocSize, and is a multiple of CLUSTER_BLOCK_SIZE.  The given value is
  * rounded down and truncated to fit those constraints, if necessary.  If the
- * tape is frozen, the 'buffer_size' argument is ignored, and a small BLCKSZ
+ * tape is frozen, the 'buffer_size' argument is ignored, and a small CLUSTER_BLOCK_SIZE
  * byte buffer is used.
  */
 void
@@ -851,19 +851,19 @@ LogicalTapeRewindForRead(LogicalTape *lt, size_t buffer_size)
 	 * Round and cap buffer_size if needed.
 	 */
 	if (lt->frozen)
-		buffer_size = BLCKSZ;
+		buffer_size = CLUSTER_BLOCK_SIZE;
 	else
 	{
 		/* need at least one block */
-		if (buffer_size < BLCKSZ)
-			buffer_size = BLCKSZ;
+		if (buffer_size < CLUSTER_BLOCK_SIZE)
+			buffer_size = CLUSTER_BLOCK_SIZE;
 
 		/* palloc() larger than max_size is unlikely to be helpful */
 		if (buffer_size > lt->max_size)
 			buffer_size = lt->max_size;
 
-		/* round down to BLCKSZ boundary */
-		buffer_size -= buffer_size % BLCKSZ;
+		/* round down to CLUSTER_BLOCK_SIZE boundary */
+		buffer_size -= buffer_size % CLUSTER_BLOCK_SIZE;
 	}
 
 	if (lt->writing)
@@ -1015,12 +1015,12 @@ LogicalTapeFreeze(LogicalTape *lt, TapeShare *share)
 	 * we're reading from multiple tapes.  But at the end of a sort, when a
 	 * tape is frozen, we only read from a single tape anyway.
 	 */
-	if (!lt->buffer || lt->buffer_size != BLCKSZ)
+	if (!lt->buffer || lt->buffer_size != CLUSTER_BLOCK_SIZE)
 	{
 		if (lt->buffer)
 			pfree(lt->buffer);
-		lt->buffer = palloc(BLCKSZ);
-		lt->buffer_size = BLCKSZ;
+		lt->buffer = palloc(CLUSTER_BLOCK_SIZE);
+		lt->buffer_size = CLUSTER_BLOCK_SIZE;
 	}
 
 	/* Read the first block, or reset if tape is empty */
@@ -1064,7 +1064,7 @@ LogicalTapeBackspace(LogicalTape *lt, size_t size)
 	size_t		seekpos = 0;
 
 	Assert(lt->frozen);
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == CLUSTER_BLOCK_SIZE);
 
 	if (lt->buffer == NULL)
 		ltsInitReadBuffer(lt);
@@ -1134,7 +1134,7 @@ LogicalTapeSeek(LogicalTape *lt, long blocknum, int offset)
 {
 	Assert(lt->frozen);
 	Assert(offset >= 0 && offset <= TapeBlockPayloadSize);
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == CLUSTER_BLOCK_SIZE);
 
 	if (lt->buffer == NULL)
 		ltsInitReadBuffer(lt);
@@ -1167,7 +1167,7 @@ LogicalTapeTell(LogicalTape *lt, long *blocknum, int *offset)
 	Assert(lt->offsetBlockNumber == 0L);
 
 	/* With a larger buffer, 'pos' wouldn't be the same as offset within page */
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == CLUSTER_BLOCK_SIZE);
 
 	*blocknum = lt->curBlockNumber;
 	*offset = lt->pos;
diff --git a/src/backend/utils/sort/sharedtuplestore.c b/src/backend/utils/sort/sharedtuplestore.c
index 236be65f22..4f0cebf5be 100644
--- a/src/backend/utils/sort/sharedtuplestore.c
+++ b/src/backend/utils/sort/sharedtuplestore.c
@@ -37,7 +37,7 @@
  */
 #define STS_CHUNK_PAGES 4
 #define STS_CHUNK_HEADER_SIZE offsetof(SharedTuplestoreChunk, data)
-#define STS_CHUNK_DATA_SIZE (STS_CHUNK_PAGES * BLCKSZ - STS_CHUNK_HEADER_SIZE)
+#define STS_CHUNK_DATA_SIZE (STS_CHUNK_PAGES * CLUSTER_BLOCK_SIZE - STS_CHUNK_HEADER_SIZE)
 
 /* Chunk written to disk. */
 typedef struct SharedTuplestoreChunk
@@ -198,7 +198,7 @@ sts_flush_chunk(SharedTuplestoreAccessor *accessor)
 {
 	size_t		size;
 
-	size = STS_CHUNK_PAGES * BLCKSZ;
+	size = STS_CHUNK_PAGES * CLUSTER_BLOCK_SIZE;
 	BufFileWrite(accessor->write_file, accessor->write_chunk, size);
 	memset(accessor->write_chunk, 0, size);
 	accessor->write_pointer = &accessor->write_chunk->data[0];
@@ -332,11 +332,11 @@ sts_puttuple(SharedTuplestoreAccessor *accessor, void *meta_data,
 			/* First time through.  Allocate chunk. */
 			accessor->write_chunk = (SharedTuplestoreChunk *)
 				MemoryContextAllocZero(accessor->context,
-									   STS_CHUNK_PAGES * BLCKSZ);
+									   STS_CHUNK_PAGES * CLUSTER_BLOCK_SIZE);
 			accessor->write_chunk->ntuples = 0;
 			accessor->write_pointer = &accessor->write_chunk->data[0];
 			accessor->write_end = (char *)
-				accessor->write_chunk + STS_CHUNK_PAGES * BLCKSZ;
+				accessor->write_chunk + STS_CHUNK_PAGES * CLUSTER_BLOCK_SIZE;
 		}
 		else
 		{
@@ -445,7 +445,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
 	}
 	remaining_size = size - sizeof(uint32);
 	this_chunk_size = Min(remaining_size,
-						  BLCKSZ * STS_CHUNK_PAGES - accessor->read_bytes);
+						  CLUSTER_BLOCK_SIZE * STS_CHUNK_PAGES - accessor->read_bytes);
 	destination = accessor->read_buffer + sizeof(uint32);
 	BufFileReadExact(accessor->read_file, destination, this_chunk_size);
 	accessor->read_bytes += this_chunk_size;
@@ -468,7 +468,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
 					 errdetail_internal("Expected overflow chunk.")));
 		accessor->read_next_page += STS_CHUNK_PAGES;
 		this_chunk_size = Min(remaining_size,
-							  BLCKSZ * STS_CHUNK_PAGES -
+							  CLUSTER_BLOCK_SIZE * STS_CHUNK_PAGES -
 							  STS_CHUNK_HEADER_SIZE);
 		BufFileReadExact(accessor->read_file, destination, this_chunk_size);
 		accessor->read_bytes += this_chunk_size;
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index e5a4e5b371..09e762a015 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -179,8 +179,8 @@ typedef enum
  */
 #define MINORDER		6		/* minimum merge order */
 #define MAXORDER		500		/* maximum merge order */
-#define TAPE_BUFFER_OVERHEAD		BLCKSZ
-#define MERGE_BUFFER_SIZE			(BLCKSZ * 32)
+#define TAPE_BUFFER_OVERHEAD		CLUSTER_BLOCK_SIZE
+#define MERGE_BUFFER_SIZE			(CLUSTER_BLOCK_SIZE * 32)
 
 
 /*
@@ -1003,7 +1003,7 @@ tuplesort_updatemax(Tuplesortstate *state)
 	if (state->tapeset)
 	{
 		isSpaceDisk = true;
-		spaceUsed = LogicalTapeSetBlocks(state->tapeset) * BLCKSZ;
+		spaceUsed = LogicalTapeSetBlocks(state->tapeset) * CLUSTER_BLOCK_SIZE;
 	}
 	else
 	{
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index fc1fb363e7..2cabd41a03 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -69,6 +69,7 @@
 #include "catalog/pg_class_d.h" /* pgrminclude ignore */
 #include "catalog/pg_collation_d.h"
 #include "catalog/pg_database_d.h"	/* pgrminclude ignore */
+#include "common/blocksize.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
@@ -165,6 +166,8 @@ static bool data_checksums = false;
 static char *xlog_dir = NULL;
 static char *str_wal_segment_size_mb = NULL;
 static int	wal_segment_size_mb;
+static char *str_block_size = NULL;
+int block_size = DEFAULT_BLOCK_SIZE;
 
 
 /* internal vars */
@@ -1108,8 +1111,8 @@ test_config_settings(void)
 
 	for (i = 0; i < bufslen; i++)
 	{
-		/* Use same amount of memory, independent of BLCKSZ */
-		test_buffs = (trial_bufs[i] * 8192) / BLCKSZ;
+		/* Use same amount of memory, independent of block_size */
+		test_buffs = (trial_bufs[i] * 8192) / block_size;
 		if (test_buffs <= ok_buffers)
 		{
 			test_buffs = ok_buffers;
@@ -1121,10 +1124,10 @@ test_config_settings(void)
 	}
 	n_buffers = test_buffs;
 
-	if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
-		printf("%dMB\n", (n_buffers * (BLCKSZ / 1024)) / 1024);
+	if ((n_buffers * (block_size / 1024)) % 1024 == 0)
+		printf("%dMB\n", (n_buffers * (block_size / 1024)) / 1024);
 	else
-		printf("%dkB\n", n_buffers * (BLCKSZ / 1024));
+		printf("%dkB\n", n_buffers * (block_size / 1024));
 
 	printf(_("selecting default time zone ... "));
 	fflush(stdout);
@@ -1145,11 +1148,12 @@ test_specific_config_settings(int test_conns, int test_buffs)
 
 	/* Set up the test postmaster invocation */
 	printfPQExpBuffer(cmd,
-					  "\"%s\" --check %s %s "
+					  "\"%s\" --check %s %s -b %d "
 					  "-c max_connections=%d "
 					  "-c shared_buffers=%d "
 					  "-c dynamic_shared_memory_type=%s",
 					  backend_exec, boot_options, extra_options,
+					  block_size,
 					  test_conns, test_buffs,
 					  dynamic_shared_memory_type);
 
@@ -1214,12 +1218,12 @@ setup_config(void)
 	conflines = replace_guc_value(conflines, "max_connections",
 								  repltok, false);
 
-	if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
+	if ((n_buffers * (block_size / 1024)) % 1024 == 0)
 		snprintf(repltok, sizeof(repltok), "%dMB",
-				 (n_buffers * (BLCKSZ / 1024)) / 1024);
+				 (n_buffers * (block_size / 1024)) / 1024);
 	else
 		snprintf(repltok, sizeof(repltok), "%dkB",
-				 n_buffers * (BLCKSZ / 1024));
+				 n_buffers * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "shared_buffers",
 								  repltok, false);
 
@@ -1294,21 +1298,21 @@ setup_config(void)
 
 #if DEFAULT_BACKEND_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BACKEND_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_BACKEND_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "backend_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_BGWRITER_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BGWRITER_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_BGWRITER_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "bgwriter_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_CHECKPOINT_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "checkpoint_flush_after",
 								  repltok, true);
 #endif
@@ -1531,9 +1535,10 @@ bootstrap_template1(void)
 	unsetenv("PGCLIENTENCODING");
 
 	snprintf(cmd, sizeof(cmd),
-			 "\"%s\" --boot -X %d %s %s %s %s",
+			 "\"%s\" --boot -X %d -b %d %s %s %s %s",
 			 backend_exec,
 			 wal_segment_size_mb * (1024 * 1024),
+			 block_size,
 			 data_checksums ? "-k" : "",
 			 boot_options, extra_options,
 			 debug ? "-d 5" : "");
@@ -2431,6 +2436,7 @@ usage(const char *progname)
 	printf(_("  -A, --auth=METHOD         default authentication method for local connections\n"));
 	printf(_("      --auth-host=METHOD    default authentication method for local TCP/IP connections\n"));
 	printf(_("      --auth-local=METHOD   default authentication method for local-socket connections\n"));
+	printf(_("  -b, --block-size=SIZE     size of database blocks, in kilobytes\n"));
 	printf(_(" [-D, --pgdata=]DATADIR     location for this database cluster\n"));
 	printf(_("  -E, --encoding=ENCODING   set default encoding for new databases\n"));
 	printf(_("  -g, --allow-group-access  allow group read/execute on data directory\n"));
@@ -3094,6 +3100,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"waldir", required_argument, NULL, 'X'},
 		{"wal-segsize", required_argument, NULL, 12},
+		{"block-size", required_argument, NULL, 'b'},
 		{"data-checksums", no_argument, NULL, 'k'},
 		{"allow-group-access", no_argument, NULL, 'g'},
 		{"discard-caches", no_argument, NULL, 14},
@@ -3141,7 +3148,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "A:c:dD:E:gkL:nNsST:U:WX:",
+	while ((c = getopt_long(argc, argv, "A:b:c:dD:E:gkL:nNsST:U:WX:",
 							long_options, &option_index)) != -1)
 	{
 		switch (c)
@@ -3165,6 +3172,9 @@ main(int argc, char *argv[])
 			case 11:
 				authmethodhost = pg_strdup(optarg);
 				break;
+			case 'b':
+				str_block_size = pg_strdup(optarg);
+				break;
 			case 'c':
 				{
 					char	   *buf = pg_strdup(optarg);
@@ -3359,6 +3369,26 @@ main(int argc, char *argv[])
 			pg_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
 	}
 
+	if (str_block_size == NULL)
+		block_size = DEFAULT_BLOCK_SIZE;
+	else
+	{
+		char	   *endptr;
+
+		/* check that the argument is a number */
+		block_size = strtol(str_block_size, &endptr, 10);
+
+		/* verify that the  segment size is valid */
+		if (endptr == str_block_size || *endptr != '\0')
+			pg_fatal("argument of --block-size must be a number");
+		block_size *= 1024;
+		/* check for valid block_size; last is bitwise power of two check */
+		if (!IsValidBlockSize(block_size))
+			pg_fatal("argument of --block-size must be a power of 2 between 1 and 32");
+	}
+
+	BlockSizeInit(block_size);
+
 	get_restricted_token();
 
 	setup_pgdata();
@@ -3392,6 +3422,8 @@ main(int argc, char *argv[])
 	else
 		printf(_("Data page checksums are disabled.\n"));
 
+	printf(_("Selected server block size: %d\n"), block_size);
+
 	if (pwprompt || pwfilename)
 		get_su_pwd();
 
diff --git a/src/bin/initdb/meson.build b/src/bin/initdb/meson.build
index 49743630aa..e2b8d88bff 100644
--- a/src/bin/initdb/meson.build
+++ b/src/bin/initdb/meson.build
@@ -31,6 +31,7 @@ tests += {
     'env': {'with_icu': icu.found() ? 'yes' : 'no'},
     'tests': [
       't/001_initdb.pl',
+      't/002_blocksize.pl',
     ],
   },
 }
diff --git a/src/bin/initdb/t/002_blocksize.pl b/src/bin/initdb/t/002_blocksize.pl
new file mode 100644
index 0000000000..d8d4208fed
--- /dev/null
+++ b/src/bin/initdb/t/002_blocksize.pl
@@ -0,0 +1,24 @@
+
+# Copyright (c) 2023, PostgreSQL Global Development Group
+
+
+use strict;
+use warnings;
+use Fcntl ':mode';
+use File::stat qw{lstat};
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+for my $blocksize (1,2,4,8,16,32) {
+	my $node1 = PostgreSQL::Test::Cluster->new('node' . $blocksize);
+	$node1->init(extra => ['--block-size='.($blocksize)] );
+	$node1->start;
+
+	is($node1->safe_psql('postgres',q{SELECT current_setting('block_size')}),
+	   1024 * $blocksize, "initdb with blocksize " . $blocksize . "k");
+
+	$node1->stop;
+}
+
+done_testing();
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 1dc8efe0cb..b983c2c354 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -28,6 +28,7 @@
 #include "access/xlog_internal.h"
 #include "backup/basebackup.h"
 #include "bbstreamer.h"
+#include "common/blocksize.h"
 #include "common/compression.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -2735,6 +2736,12 @@ main(int argc, char **argv)
 	if (!RetrieveWalSegSize(conn))
 		exit(1);
 
+	/* detect and set remote server's block size */
+	if (!RetrieveBlockSize(conn))
+		exit(1);
+
+	BlockSizeInit(BlockSize);
+
 	/* Create pg_wal symlink, if required */
 	if (xlog_dir)
 	{
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index d0a4079d50..e5f5c804ee 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -897,6 +897,10 @@ main(int argc, char **argv)
 	/* determine remote server's xlog segment size */
 	if (!RetrieveWalSegSize(conn))
 		exit(1);
+	if (!RetrieveBlockSize(conn))
+		exit(1);
+
+	BlockSizeInit(BlockSize);
 
 	/*
 	 * Don't close the connection here so that subsequent StreamLog() can
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 15514599c4..c8aea95c2f 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -32,6 +32,7 @@
 #define ERRCODE_DUPLICATE_OBJECT  "42710"
 
 int			WalSegSz;
+int			BlockSize;
 
 static bool RetrieveDataDirCreatePerm(PGconn *conn);
 
@@ -331,6 +332,64 @@ RetrieveWalSegSize(PGconn *conn)
 	return true;
 }
 
+/*
+ * Use SHOW block_size since ControlFile is not accessible here.
+ */
+bool
+RetrieveBlockSize(PGconn *conn)
+{
+	PGresult   *res;
+
+	/* check connection existence */
+	Assert(conn != NULL);
+
+	/* for previous versions set the default xlog seg size */
+	if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_SHOW_CMD)
+	{
+		BlockSize = DEFAULT_BLOCK_SIZE;
+		return true;
+	}
+
+	res = PQexec(conn, "SHOW block_size");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+	{
+		pg_log_error("could not send replication command \"%s\": %s",
+					 "SHOW block_size", PQerrorMessage(conn));
+
+		PQclear(res);
+		return false;
+	}
+	if (PQntuples(res) != 1 || PQnfields(res) < 1)
+	{
+		pg_log_error("could not fetch block size: got %d rows and %d fields, expected %d rows and %d or more fields",
+					 PQntuples(res), PQnfields(res), 1, 1);
+
+		PQclear(res);
+		return false;
+	}
+
+	/* fetch xlog value and unit from the result */
+	if (sscanf(PQgetvalue(res, 0, 0), "%d", &BlockSize) != 1)
+	{
+		pg_log_error("block size could not be parsed");
+		PQclear(res);
+		return false;
+	}
+
+	PQclear(res);
+
+	if (!IsValidBlockSize(BlockSize))
+	{
+		pg_log_error(ngettext("Block size must be a power of two between 1k and 32k, but the remote server reported a value of %d byte",
+							  "Block size must be a power of two between 1k and 32k, but the remote server reported a value of %d bytes",
+							  BlockSize),
+					 BlockSize);
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * RetrieveDataDirCreatePerm
  *
diff --git a/src/bin/pg_basebackup/streamutil.h b/src/bin/pg_basebackup/streamutil.h
index 268c163213..c97f9fa969 100644
--- a/src/bin/pg_basebackup/streamutil.h
+++ b/src/bin/pg_basebackup/streamutil.h
@@ -13,6 +13,7 @@
 #define STREAMUTIL_H
 
 #include "access/xlogdefs.h"
+#include "common/blocksize.h"
 #include "datatype/timestamp.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
@@ -25,6 +26,7 @@ extern char *dbport;
 extern char *dbname;
 extern int	dbgetpassword;
 extern int	WalSegSz;
+extern int	BlockSize;
 
 /* Connection kept global so we can disconnect easily */
 extern PGconn *conn;
@@ -56,6 +58,7 @@ extern bool GetSlotInformation(PGconn *conn, const char *slot_name,
 							   XLogRecPtr *restart_lsn,
 							   TimeLineID *restart_tli);
 extern bool RetrieveWalSegSize(PGconn *conn);
+extern bool RetrieveBlockSize(PGconn *conn);
 extern TimestampTz feGetCurrentTimestamp(void);
 extern void feTimestampDifference(TimestampTz start_time, TimestampTz stop_time,
 								  long *secs, int *microsecs);
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 19eb67e485..0affb90e66 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/xlog_internal.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -204,18 +205,18 @@ scan_file(const char *fn, int segmentno)
 	for (blockno = 0;; blockno++)
 	{
 		uint16		csum;
-		int			r = read(f, buf.data, BLCKSZ);
+		int			r = read(f, buf.data, CLUSTER_BLOCK_SIZE);
 
 		if (r == 0)
 			break;
-		if (r != BLCKSZ)
+		if (r != CLUSTER_BLOCK_SIZE)
 		{
 			if (r < 0)
 				pg_fatal("could not read block %u in file \"%s\": %m",
 						 blockno, fn);
 			else
 				pg_fatal("could not read block %u in file \"%s\": read %d of %d",
-						 blockno, fn, r, BLCKSZ);
+						 blockno, fn, r, CLUSTER_BLOCK_SIZE);
 		}
 		blocks_scanned++;
 
@@ -231,14 +232,14 @@ scan_file(const char *fn, int segmentno)
 		if (PageIsNew(buf.data))
 			continue;
 
-		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
+		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE, CLUSTER_BLOCK_SIZE);
 		if (mode == PG_MODE_CHECK)
 		{
 			if (csum != header->pd_checksum)
 			{
 				if (ControlFile->data_checksum_version == PG_DATA_CHECKSUM_VERSION)
-					pg_log_error("checksum verification failed in file \"%s\", block %u: calculated checksum %X but block contains %X",
-								 fn, blockno, csum, header->pd_checksum);
+					pg_log_error("checksum verification failed in file \"%s\", block %u, block_size: %u: calculated checksum %X but block contains %X",
+								 fn, blockno, CLUSTER_BLOCK_SIZE, csum, header->pd_checksum);
 				badblocks++;
 			}
 		}
@@ -259,19 +260,19 @@ scan_file(const char *fn, int segmentno)
 			header->pd_checksum = csum;
 
 			/* Seek back to beginning of block */
-			if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
+			if (lseek(f, -(int)CLUSTER_BLOCK_SIZE, SEEK_CUR) < 0)
 				pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
 
 			/* Write block with checksum */
-			w = write(f, buf.data, BLCKSZ);
-			if (w != BLCKSZ)
+			w = write(f, buf.data, CLUSTER_BLOCK_SIZE);
+			if (w != CLUSTER_BLOCK_SIZE)
 			{
 				if (w < 0)
 					pg_fatal("could not write block %u in file \"%s\": %m",
 							 blockno, fn);
 				else
 					pg_fatal("could not write block %u in file \"%s\": wrote %d of %d",
-							 blockno, fn, w, BLCKSZ);
+							 blockno, fn, w, CLUSTER_BLOCK_SIZE);
 			}
 		}
 
@@ -551,14 +552,6 @@ main(int argc, char *argv[])
 	if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
 		pg_fatal("cluster is not compatible with this version of pg_checksums");
 
-	if (ControlFile->blcksz != BLCKSZ)
-	{
-		pg_log_error("database cluster is not compatible");
-		pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
-							ControlFile->blcksz, BLCKSZ);
-		exit(1);
-	}
-
 	/*
 	 * Check if cluster is running.  A clean shutdown is required to avoid
 	 * random checksum failures caused by torn pages.  Note that this doesn't
@@ -580,6 +573,11 @@ main(int argc, char *argv[])
 		mode == PG_MODE_ENABLE)
 		pg_fatal("data checksums are already enabled in cluster");
 
+	if (IsValidBlockSize(ControlFile->blcksz))
+		BlockSizeInit(ControlFile->blcksz);
+	else
+		pg_fatal("invalid cluster block size in control file");
+
 	/* Operate on all files if checking or enabling checksums */
 	if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
 	{
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index e7ef2b8bd0..0c2be791df 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -49,6 +49,7 @@
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
@@ -75,6 +76,7 @@ static TimeLineID minXlogTli = 0;
 static XLogSegNo minXlogSegNo = 0;
 static int	WalSegSz;
 static int	set_wal_segsize;
+static int	set_blocksize = 0;
 
 static void CheckDataVersion(void);
 static bool read_controlfile(void);
@@ -93,6 +95,7 @@ int
 main(int argc, char *argv[])
 {
 	static struct option long_options[] = {
+		{"block-size", required_argument, NULL, 'b'},
 		{"commit-timestamp-ids", required_argument, NULL, 'c'},
 		{"pgdata", required_argument, NULL, 'D'},
 		{"epoch", required_argument, NULL, 'e'},
@@ -137,10 +140,19 @@ main(int argc, char *argv[])
 	}
 
 
-	while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
+	while ((c = getopt_long(argc, argv, "b:c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
 	{
 		switch (c)
 		{
+			case 'b':
+				errno = 0;
+				set_blocksize = strtol(optarg, &endptr, 10);
+				if (endptr == optarg || *endptr != '\0' || errno != 0)
+					pg_fatal("argument of --block-size must be a number");
+				set_blocksize *= 1024;
+				if (!IsValidBlockSize(set_blocksize))
+					pg_fatal("argument of --block-size must be a power of 2 between 1 and 32");
+				break;
 			case 'D':
 				DataDir = optarg;
 				break;
@@ -387,6 +399,33 @@ main(int argc, char *argv[])
 	else
 		WalSegSz = ControlFile.xlog_seg_size;
 
+	/*
+	 * If a blocksize was specified, compare to existing ControlFile; if we
+	 * are wrong, we won't be able to read the data.  We will only want to set
+	 * it if we guessed.
+	 */
+	if (set_blocksize == 0)
+	{
+		if (guessed)
+			pg_fatal("Cannot determine cluster block size; provide explicitly via --block-size");
+	}
+	else
+	{
+		if (!guessed && set_blocksize != ControlFile.blcksz)
+			pg_fatal("Cannot change blocksize in cluster");
+
+		/* hope this is right, but by default we don't know; likely this is
+		 * DEFAULT_BLOCK_SIZE */
+		ControlFile.blcksz = set_blocksize;
+	}
+
+	/*
+	 * Set some dependent calculated fields stored in pg_control
+	 */
+	BlockSizeInit(ControlFile.blcksz);
+	ControlFile.toast_max_chunk_size = TOAST_MAX_CHUNK_SIZE;
+	ControlFile.loblksize = LOBLKSIZE;
+
 	if (log_fname != NULL)
 		XLogFromFileName(log_fname, &minXlogTli, &minXlogSegNo, WalSegSz);
 
@@ -610,6 +649,16 @@ read_controlfile(void)
 			return false;
 		}
 
+		/* return false if block size is not valid */
+		if (!IsValidBlockSize(ControlFile.blcksz))
+		{
+			pg_log_warning(ngettext("pg_control specifies invalid block size (%d byte); proceed with caution",
+									"pg_control specifies invalid block size (%d bytes); proceed with caution",
+									ControlFile.blcksz),
+						   ControlFile.blcksz);
+			return false;
+		}
+
 		return true;
 	}
 
@@ -682,7 +731,7 @@ GuessControlValues(void)
 
 	ControlFile.maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile.floatFormat = FLOATFORMAT_VALUE;
-	ControlFile.blcksz = BLCKSZ;
+	ControlFile.blcksz = DEFAULT_BLOCK_SIZE;
 	ControlFile.relseg_size = RELSEG_SIZE;
 	ControlFile.xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
@@ -1127,6 +1176,7 @@ usage(void)
 	printf(_("%s resets the PostgreSQL write-ahead log.\n\n"), progname);
 	printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
 	printf(_("Options:\n"));
+	printf(_("      --block-size=SIZE            cluster block size, in bytes\n"));
 	printf(_("  -c, --commit-timestamp-ids=XID,XID\n"
 			 "                                   set oldest and newest transactions bearing\n"
 			 "                                   commit timestamp (zero means no change)\n"));
diff --git a/src/bin/pg_resetwal/t/002_corrupted.pl b/src/bin/pg_resetwal/t/002_corrupted.pl
index 6d19a1efd5..84ed4e92b0 100644
--- a/src/bin/pg_resetwal/t/002_corrupted.pl
+++ b/src/bin/pg_resetwal/t/002_corrupted.pl
@@ -31,7 +31,7 @@ print $fh pack("x[$size]");
 close $fh;
 
 command_checks_all(
-	[ 'pg_resetwal', '-n', $node->data_dir ],
+	[ 'pg_resetwal', '-b', '8', '-n', $node->data_dir ],
 	0,
 	[qr/pg_control version number/],
 	[
@@ -47,7 +47,7 @@ print $fh $data, pack("x[" . ($size - 16) . "]");
 close $fh;
 
 command_checks_all(
-	[ 'pg_resetwal', '-n', $node->data_dir ],
+	[ 'pg_resetwal', '-b', '8', '-n', $node->data_dir ],
 	0,
 	[qr/pg_control version number/],
 	[
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index bd5c598e20..e354432aa5 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "catalog/pg_tablespace_d.h"
+#include "common/blocksize.h"
 #include "common/hashfn.h"
 #include "common/string.h"
 #include "datapagemap.h"
@@ -333,7 +334,7 @@ process_target_wal_block_change(ForkNumber forknum, RelFileLocator rlocator,
 			{
 				off_t		end_offset;
 
-				end_offset = (blkno_inseg + 1) * BLCKSZ;
+				end_offset = (blkno_inseg + 1) * CLUSTER_BLOCK_SIZE;
 				if (end_offset <= entry->source_size && end_offset <= entry->target_size)
 					datapagemap_add(&entry->target_pages_to_overwrite, blkno_inseg);
 			}
@@ -468,7 +469,7 @@ calculate_totals(filemap_t *filemap)
 
 			iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
 			while (datapagemap_next(iter, &blk))
-				filemap->fetch_size += BLCKSZ;
+				filemap->fetch_size += CLUSTER_BLOCK_SIZE;
 
 			pg_free(iter);
 		}
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index f7f3b8227f..dda31f6768 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -18,6 +18,7 @@
 #include "access/xlog_internal.h"
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_perm.h"
 #include "common/restricted_token.h"
@@ -340,6 +341,13 @@ main(int argc, char **argv)
 
 	sanityChecks();
 
+	if (IsValidBlockSize(ControlFile_source.blcksz) &&
+		ControlFile_source.blcksz == ControlFile_target.blcksz)
+		BlockSizeInit(ControlFile_source.blcksz);
+	else
+		pg_fatal("cluster block sizes do not match or are invalid: %d",
+				 ControlFile_source.blcksz);
+
 	/*
 	 * Usually, the TLI can be found in the latest checkpoint record. But if
 	 * the source server is just being promoted (or it's a standby that's
@@ -571,8 +579,8 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
 			iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
 			while (datapagemap_next(iter, &blkno))
 			{
-				offset = blkno * BLCKSZ;
-				source->queue_fetch_range(source, entry->path, offset, BLCKSZ);
+				offset = blkno * CLUSTER_BLOCK_SIZE;
+				source->queue_fetch_range(source, entry->path, offset, CLUSTER_BLOCK_SIZE);
 			}
 			pg_free(iter);
 		}
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index d173602882..57c11a43ab 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -19,14 +19,17 @@
 #include <linux/fs.h>
 #endif
 
+
 #include "access/visibilitymapdefs.h"
+#include "common/blocksize.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
+#undef CLUSTER_BLOCK_SIZE
+#define CLUSTER_BLOCK_SIZE BlockSize
 #include "storage/bufpage.h"
 #include "storage/checksum.h"
 #include "storage/checksum_impl.h"
 
-
 /*
  * cloneFile()
  *
@@ -96,7 +99,7 @@ copyFile(const char *src, const char *dst,
 				 schemaName, relName, dst, strerror(errno));
 
 	/* copy in fairly large chunks for best efficiency */
-#define COPY_BUF_SIZE (50 * BLCKSZ)
+#define COPY_BUF_SIZE (50 * BlockSize)
 
 	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
 
@@ -187,7 +190,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 	struct stat statbuf;
 
 	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
+	rewriteVmBytesPerPage = (BlockSize - SizeOfPageHeaderData) / 2;
 
 	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
 		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
@@ -220,7 +223,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 		PageHeaderData pageheader;
 		bool		old_lastblk;
 
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
+		if ((bytesRead = read(src_fd, buffer.data, BlockSize)) != BlockSize)
 		{
 			if (bytesRead < 0)
 				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
@@ -230,7 +233,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 						 schemaName, relName, fromfile);
 		}
 
-		totalBytesRead += BLCKSZ;
+		totalBytesRead += BlockSize;
 		old_lastblk = (totalBytesRead == src_filesize);
 
 		/* Save the page header data */
@@ -293,10 +296,10 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 			/* Set new checksum for visibility map page, if enabled */
 			if (new_cluster.controldata.data_checksum_version != 0)
 				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
+					pg_checksum_page(new_vmbuf.data, new_blkno, BlockSize);
 
 			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
+			if (write(dst_fd, new_vmbuf.data, BlockSize) != BlockSize)
 			{
 				/* if write didn't set errno, assume problem is no disk space */
 				if (errno == 0)
diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index 12a97f84e2..06b26e54a5 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -1,5 +1,7 @@
 # Copyright (c) 2022-2023, PostgreSQL Global Development Group
 
+fs = import('fs')
+
 pg_upgrade_sources = files(
   'check.c',
   'controldata.c',
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 4562dafcff..6a64d8689a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -63,6 +63,7 @@ static void setup(char *argv0, bool *live_check);
 ClusterInfo old_cluster,
 			new_cluster;
 OSInfo		os_info;
+uint32		BlockSize;
 
 char	   *output_files[] = {
 	SERVER_LOG_FILE,
@@ -126,6 +127,8 @@ main(int argc, char **argv)
 
 	check_cluster_compatibility(live_check);
 
+	BlockSize = old_cluster.controldata.blocksz;
+
 	check_and_dump_old_cluster(live_check);
 
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 3eea0139c7..1c98fd51cc 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -336,7 +336,7 @@ extern UserOpts user_opts;
 extern ClusterInfo old_cluster,
 			new_cluster;
 extern OSInfo os_info;
-
+extern uint32 BlockSize;
 
 /* check.c */
 
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index 96845e1a1a..feb7f4af45 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -23,6 +23,8 @@
 #include "access/xlogreader.h"
 #include "access/xlogrecord.h"
 #include "access/xlogstats.h"
+#include "common/blocksize.h"
+#include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -38,8 +40,10 @@
  */
 
 static const char *progname;
+static char *pgdata;
 
 static int	WalSegSz;
+static int	BlockSize;
 static volatile sig_atomic_t time_to_stop = false;
 
 static const RelFileLocator emptyRelFileLocator = {0, 0, 0};
@@ -527,7 +531,7 @@ XLogRecordSaveFPWs(XLogReaderState *record, const char *savepath)
 		if (!file)
 			pg_fatal("could not open file \"%s\": %m", filename);
 
-		if (fwrite(page, BLCKSZ, 1, file) != 1)
+		if (fwrite(page, BlockSize, 1, file) != 1)
 			pg_fatal("could not write file \"%s\": %m", filename);
 
 		if (fclose(file) != 0)
@@ -808,6 +812,7 @@ main(int argc, char **argv)
 		{"help", no_argument, NULL, '?'},
 		{"limit", required_argument, NULL, 'n'},
 		{"path", required_argument, NULL, 'p'},
+		{"pgdata", required_argument, NULL, 'D'},
 		{"quiet", no_argument, NULL, 'q'},
 		{"relation", required_argument, NULL, 'R'},
 		{"rmgr", required_argument, NULL, 'r'},
@@ -881,7 +886,7 @@ main(int argc, char **argv)
 		goto bad_argument;
 	}
 
-	while ((option = getopt_long(argc, argv, "bB:e:fF:n:p:qr:R:s:t:wx:z",
+	while ((option = getopt_long(argc, argv, "bB:D:e:fF:n:p:qr:R:s:t:wx:z",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -899,6 +904,9 @@ main(int argc, char **argv)
 				config.filter_by_relation_block_enabled = true;
 				config.filter_by_extended = true;
 				break;
+			case 'D':
+				pgdata = pg_strdup(optarg);
+				break;
 			case 'e':
 				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
 				{
@@ -1103,7 +1111,31 @@ main(int argc, char **argv)
 	}
 
 	if (config.save_fullpage_path != NULL)
+	{
+		ControlFileData *ControlFile;
+		bool	crc_ok_p;
+
+		/* parse the control file if we're saving the fullpage images */
+
+		if (!pgdata)
+			pgdata = getenv("PGDATA");
+		if (!pgdata || strlen(pgdata) == 0)
+			pg_fatal("Must provide data directory via -D or PGDATA envvar");
+
+		ControlFile = get_controlfile(pgdata, &crc_ok_p);
+
+		if (!ControlFile)
+			pg_fatal("Could not locate control file");
+
+		BlockSize = ControlFile->blcksz;
+
+		if (!IsValidBlockSize(BlockSize))
+			pg_fatal("read invalid block size from control file");
+
+		BlockSizeInit(BlockSize);
+
 		create_fullpage_directory(config.save_fullpage_path);
+	}
 
 	/* parse files as start/end boundaries, extract path if not specified */
 	if (optind < argc)
diff --git a/src/bin/pg_waldump/t/002_save_fullpage.pl b/src/bin/pg_waldump/t/002_save_fullpage.pl
index f0725805f2..5cd8cffd5c 100644
--- a/src/bin/pg_waldump/t/002_save_fullpage.pl
+++ b/src/bin/pg_waldump/t/002_save_fullpage.pl
@@ -73,6 +73,7 @@ $node->command_ok(
 	[
 		'pg_waldump', '--quiet',
 		'--save-fullpage', "$tmp_folder/raw",
+		'-D', $node->data_dir,
 		'--relation', $relation,
 		$walfile
 	],
diff --git a/src/common/Makefile b/src/common/Makefile
index 113029bf7b..da96f2dc8d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -48,6 +48,7 @@ LIBS += $(PTHREAD_LIBS)
 OBJS_COMMON = \
 	archive.o \
 	base64.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..ef2b3ed34c
--- /dev/null
+++ b/src/common/blocksize.c
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.c
+ *	  This file contains methods to calculate various size constants for variable-sized blocks.
+ *
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/common/clustersizes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "access/heaptoast.h"
+#include "access/htup_details.h"
+#include "access/itup.h"
+#include "access/nbtree_int.h"
+#include "common/blocksize.h"
+#ifndef FRONTEND
+#include "storage/freespace.h"
+#endif
+
+PGDLLIMPORT uint32 calculated_block_sizes[BS_NUM_SIZES];
+
+/*
+ * 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).
+ */
+
+void
+BlockSizeInit(Size rawblocksize)
+{
+//	Assert(IsValidBlockSize(rawblocksize));
+
+	calculated_block_sizes[BS_BLOCK_SIZE] = rawblocksize;
+//	Assert(rawblocksize <= MAX_BLOCK_SIZE);
+	calculated_block_sizes[BS_MAX_HEAP_TUPLES] = CalcMaxHeapTupleSize(rawblocksize);
+//    Assert(calculated_block_sizes[BS_MAX_HEAP_TUPLES] <= MaxHeapTupleSizeLimit);
+	calculated_block_sizes[BS_MAX_HEAP_TUPLES_PER_PAGE] = CalcMaxHeapTuplesPerPage(rawblocksize);
+//    Assert(calculated_block_sizes[BS_MAX_HEAP_TUPLES_PER_PAGE] <= MaxHeapTuplesPerPageLimit);
+	calculated_block_sizes[BS_MAX_INDEX_TUPLES_PER_PAGE] = CalcMaxIndexTuplesPerPage(rawblocksize);
+//    Assert(calculated_block_sizes[BS_MAX_INDEX_TUPLES_PER_PAGE] <= MaxIndexTuplesPerPageLimit);
+	calculated_block_sizes[BS_MAX_TIDS_PER_BTREE_PAGE] = CalcMaxTIDsPerBTreePage(rawblocksize);
+//    Assert(calculated_block_sizes[BS_MAX_TIDS_PER_BTREE_PAGE] <= MaxTIDsPerBTreePageLimit);
+	calculated_block_sizes[BS_TOAST_MAX_CHUNK_SIZE] = CalcToastMaxChunkSize(rawblocksize);
+//    Assert(calculated_block_sizes[BS_TOAST_MAX_CHUNK_SIZE] <= TOAST_MAX_CHUNK_SIZE_LIMIT);
+
+	#ifndef FRONTEND
+	/* also setup the FreeSpaceMap internal sizing */
+	FreeSpaceMapInit();
+	#endif
+}
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 74833c4acb..d218510b23 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -540,7 +540,7 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
 ssize_t
 pg_pwrite_zeros(int fd, size_t size, off_t offset)
 {
-	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth BLCKSZ */
+	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth MAX_BLOCK_SIZE */
 	void	   *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
 	struct iovec iov[PG_IOV_MAX];
 	size_t		remaining_size = size;
@@ -558,10 +558,10 @@ pg_pwrite_zeros(int fd, size_t size, off_t offset)
 
 			iov[iovcnt].iov_base = zerobuf_addr;
 
-			if (remaining_size < BLCKSZ)
+			if (remaining_size < sizeof(zbuffer.data))
 				this_iov_size = remaining_size;
 			else
-				this_iov_size = BLCKSZ;
+				this_iov_size = sizeof(zbuffer.data);
 
 			iov[iovcnt].iov_len = this_iov_size;
 			remaining_size -= this_iov_size;
diff --git a/src/common/meson.build b/src/common/meson.build
index 9efc80ac02..140c864c6c 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -3,6 +3,7 @@
 common_sources = files(
   'archive.c',
   'base64.c',
+  'blocksize.c',
   'checksum_helper.c',
   'compression.c',
   'controldata_utils.c',
diff --git a/src/include/access/brin_page.h b/src/include/access/brin_page.h
index 3670ca6010..070bb261e9 100644
--- a/src/include/access/brin_page.h
+++ b/src/include/access/brin_page.h
@@ -86,7 +86,7 @@ typedef struct RevmapContents
 } RevmapContents;
 
 #define REVMAP_CONTENT_SIZE \
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
+	(CLUSTER_BLOCK_SIZE - MAXALIGN(SizeOfPageHeaderData) - \
 	 offsetof(RevmapContents, rm_tids) - \
 	 MAXALIGN(sizeof(BrinSpecialSpace)))
 /* max num of items in the array */
diff --git a/src/include/access/ginblock.h b/src/include/access/ginblock.h
index c59790ec5a..13e5b12cd7 100644
--- a/src/include/access/ginblock.h
+++ b/src/include/access/ginblock.h
@@ -248,7 +248,7 @@ typedef signed char GinNullCategory;
  */
 #define GinMaxItemSize \
 	Min(INDEX_SIZE_MASK, \
-		MAXALIGN_DOWN(((BLCKSZ - \
+		MAXALIGN_DOWN(((CLUSTER_BLOCK_SIZE - \
 						MAXALIGN(SizeOfPageHeaderData + 3 * sizeof(ItemIdData)) - \
 						MAXALIGN(sizeof(GinPageOpaqueData))) / 3)))
 
@@ -318,7 +318,7 @@ typedef signed char GinNullCategory;
 	 GinPageGetOpaque(page)->maxoff * sizeof(PostingItem))
 
 #define GinDataPageMaxDataSize	\
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+	(CLUSTER_BLOCK_SIZE - MAXALIGN(SizeOfPageHeaderData) \
 	 - MAXALIGN(sizeof(ItemPointerData)) \
 	 - MAXALIGN(sizeof(GinPageOpaqueData)))
 
@@ -326,7 +326,7 @@ typedef signed char GinNullCategory;
  * List pages
  */
 #define GinListPageSize  \
-	( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
+	( CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
 
 /*
  * A compressed posting list.
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 0235716c06..2272ac09e7 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -96,7 +96,7 @@ typedef GISTPageOpaqueData *GISTPageOpaque;
  * key size using opclass parameters.
  */
 #define GISTMaxIndexTupleSize	\
-	MAXALIGN_DOWN((BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
+	MAXALIGN_DOWN((CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
 				  4 - sizeof(ItemIdData))
 
 #define GISTMaxIndexKeySize	\
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 3edc740a3f..43d724d160 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -54,7 +54,7 @@ typedef struct
 /* Returns free space in node buffer page */
 #define PAGE_FREE_SPACE(nbp) (nbp->freespace)
 /* Checks if node buffer page is empty */
-#define PAGE_IS_EMPTY(nbp) (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)
+#define PAGE_IS_EMPTY(nbp) (nbp->freespace == CLUSTER_BLOCK_SIZE - BUFFER_PAGE_DATA_OFFSET)
 /* Checks if node buffers page don't contain sufficient space for index tuple */
 #define PAGE_NO_SPACE(nbp, itup) (PAGE_FREE_SPACE(nbp) < \
 										MAXALIGN(IndexTupleSize(itup)))
@@ -171,7 +171,7 @@ typedef struct GISTScanOpaqueData
 	GistNSN		curPageLSN;		/* pos in the WAL stream when page was read */
 
 	/* In a non-ordered search, returnable heap items are stored here: */
-	GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
+	GISTSearchHeapItem pageData[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
 	OffsetNumber nPageData;		/* number of valid items in array */
 	OffsetNumber curPageData;	/* next item to return */
 	MemoryContext pageDataCxt;	/* context holding the fetched tuples, for
@@ -474,7 +474,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
 /* gistutil.c */
 
 #define GiSTPageSize   \
-	( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
+	( CLUSTER_BLOCK_SIZE - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
 
 #define GIST_MIN_FILLFACTOR			10
 #define GIST_DEFAULT_FILLFACTOR		90
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9e035270a1..8eab28c582 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -124,7 +124,7 @@ typedef struct HashScanPosData
 	int			lastItem;		/* last valid index in items[] */
 	int			itemIndex;		/* current index in items[] */
 
-	HashScanPosItem items[MaxIndexTuplesPerPage];	/* MUST BE LAST */
+	HashScanPosItem items[MaxIndexTuplesPerPageLimit];	/* MUST BE LAST */
 } HashScanPosData;
 
 #define HashScanPosIsPinned(scanpos) \
@@ -224,10 +224,10 @@ typedef HashScanOpaqueData *HashScanOpaque;
  * needing to fit into the metapage.  (With 8K block size, 1024 bitmaps
  * limit us to 256 GB of overflow space...).  For smaller block size we
  * can not use 1024 bitmaps as it will lead to the meta page data crossing
- * the block size boundary.  So we use BLCKSZ to determine the maximum number
+ * the block size boundary.  So we use CLUSTER_BLOCK_SIZE to determine the maximum number
  * of bitmaps.
  */
-#define HASH_MAX_BITMAPS			Min(BLCKSZ / 8, 1024)
+#define HASH_MAX_BITMAPS			Min(DEFAULT_BLOCK_SIZE / 8, 1024)
 
 #define HASH_SPLITPOINT_PHASE_BITS	2
 #define HASH_SPLITPOINT_PHASES_PER_GRP	(1 << HASH_SPLITPOINT_PHASE_BITS)
@@ -279,7 +279,7 @@ typedef struct HashOptions
 	 ((HashOptions *) (relation)->rd_options)->fillfactor :	\
 	 HASH_DEFAULT_FILLFACTOR)
 #define HashGetTargetPageUsage(relation) \
-	(BLCKSZ * HashGetFillFactor(relation) / 100)
+	(CLUSTER_BLOCK_SIZE * HashGetFillFactor(relation) / 100)
 
 /*
  * Maximum size of a hash index item (it's okay to have only one per page)
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index faf5026519..644c03145b 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -75,7 +75,7 @@ typedef struct HeapScanDescData
 	/* these fields only used in page-at-a-time mode and for bitmap scans */
 	int			rs_cindex;		/* current tuple's index in vistuples */
 	int			rs_ntuples;		/* number of visible tuples on page */
-	OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
+	OffsetNumber rs_vistuples[MaxHeapTuplesPerPageLimit];	/* their offsets */
 }			HeapScanDescData;
 typedef struct HeapScanDescData *HeapScanDesc;
 
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index 5c0a796f66..f7c87d11c3 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -20,10 +20,7 @@
 /*
  * Find the maximum size of a tuple if there are to be N tuples per page.
  */
-#define MaximumBytesPerTuple(tuplesPerPage) \
-	MAXALIGN_DOWN((BLCKSZ - \
-				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
-				  / (tuplesPerPage))
+#define MaximumBytesPerTuple(tuplesPerPage) CalcMaximumBytesPerTuple(CLUSTER_BLOCK_SIZE,tuplesPerPage)
 
 /*
  * These symbols control toaster activation.  If a tuple is larger than
@@ -45,7 +42,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE	4
 
-#define TOAST_TUPLE_THRESHOLD	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
+#define TOAST_TUPLE_THRESHOLD	CalcMaximumBytesPerTuple(CLUSTER_BLOCK_SIZE,TOAST_TUPLES_PER_PAGE)
 
 #define TOAST_TUPLE_TARGET		TOAST_TUPLE_THRESHOLD
 
@@ -58,7 +55,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE_MAIN	1
 
-#define TOAST_TUPLE_TARGET_MAIN MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
+#define TOAST_TUPLE_TARGET_MAIN CalcMaximumBytesPerTuple(CLUSTER_BLOCK_SIZE,TOAST_TUPLES_PER_PAGE_MAIN)
 
 /*
  * If an index value is larger than TOAST_INDEX_TARGET, we will try to
@@ -81,12 +78,9 @@
 
 #define EXTERN_TUPLE_MAX_SIZE	MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE)
 
-#define TOAST_MAX_CHUNK_SIZE	\
-	(EXTERN_TUPLE_MAX_SIZE -							\
-	 MAXALIGN(SizeofHeapTupleHeader) -					\
-	 sizeof(Oid) -										\
-	 sizeof(int32) -									\
-	 VARHDRSZ)
+#define TOAST_MAX_CHUNK_SIZE_LIMIT CalcToastMaxChunkSize(MAX_BLOCK_SIZE)
+#define TOAST_MAX_CHUNK_SIZE GetBlockSize(BS_TOAST_MAX_CHUNK_SIZE)
+
 
 /* ----------
  * heap_toast_insert_or_update -
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index e01f4f35c8..a76c9a658a 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -18,6 +18,7 @@
 #include "access/transam.h"
 #include "access/tupdesc.h"
 #include "access/tupmacs.h"
+#include "common/blocksize.h"
 #include "storage/bufpage.h"
 #include "varatt.h"
 
@@ -427,8 +428,8 @@ do { \
 	(tup)->t_choice.t_heap.t_field3.t_xvac = (xid); \
 } while (0)
 
-StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
-				 "invalid speculative token constant");
+/* StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber, */
+/* 				 "invalid speculative token constant"); */
 
 #define HeapTupleHeaderIsSpeculative(tup) \
 ( \
@@ -551,7 +552,7 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
 
 /*
  * MaxHeapTupleSize is the maximum allowed size of a heap tuple, including
- * header and MAXALIGN alignment padding.  Basically it's BLCKSZ minus the
+ * header and MAXALIGN alignment padding.  Basically it's CLUSTER_BLOCK_SIZE minus the
  * other stuff that has to be on a disk page.  Since heap pages use no
  * "special space", there's no deduction for that.
  *
@@ -560,7 +561,8 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * ItemIds and tuples have different alignment requirements, don't assume that
  * you can, say, fit 2 tuples of size MaxHeapTupleSize/2 on the same page.
  */
-#define MaxHeapTupleSize  (BLCKSZ - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+#define MaxHeapTupleSize GetBlockSize(BS_MAX_HEAP_TUPLES)
+#define MaxHeapTupleSizeLimit CalcMaxHeapTupleSize(MAX_BLOCK_SIZE)
 #define MinHeapTupleSize  MAXALIGN(SizeofHeapTupleHeader)
 
 /*
@@ -574,9 +576,9 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * pointers to this anyway, to avoid excessive line-pointer bloat and not
  * require increases in the size of work arrays.
  */
-#define MaxHeapTuplesPerPage	\
-	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
-			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
+
+#define MaxHeapTuplesPerPage GetBlockSize(BS_MAX_HEAP_TUPLES_PER_PAGE)
+#define MaxHeapTuplesPerPageLimit CalcMaxHeapTuplesPerPage(MAX_BLOCK_SIZE)
 
 /*
  * MaxAttrSize is a somewhat arbitrary upper limit on the declared size of
diff --git a/src/include/access/itup.h b/src/include/access/itup.h
index 2e2b8c7a47..2d6de24e67 100644
--- a/src/include/access/itup.h
+++ b/src/include/access/itup.h
@@ -163,8 +163,7 @@ index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
  * estimated here, seemingly allowing one more tuple than estimated here.
  * But such a page always has at least MAXALIGN special space, so we're safe.
  */
-#define MaxIndexTuplesPerPage	\
-	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
-			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
+#define MaxIndexTuplesPerPage GetBlockSize(BS_MAX_INDEX_TUPLES_PER_PAGE)
+#define MaxIndexTuplesPerPageLimit CalcMaxIndexTuplesPerPage(MAX_BLOCK_SIZE)
 
 #endif							/* ITUP_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 8891fa7973..bed8595537 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -15,6 +15,7 @@
 #define NBTREE_H
 
 #include "access/amapi.h"
+#include "access/nbtree_int.h"
 #include "access/itup.h"
 #include "access/sdir.h"
 #include "access/tableam.h"
@@ -25,208 +26,6 @@
 #include "storage/bufmgr.h"
 #include "storage/shm_toc.h"
 
-/* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
-typedef uint16 BTCycleId;
-
-/*
- *	BTPageOpaqueData -- At the end of every page, we store a pointer
- *	to both siblings in the tree.  This is used to do forward/backward
- *	index scans.  The next-page link is also critical for recovery when
- *	a search has navigated to the wrong page due to concurrent page splits
- *	or deletions; see src/backend/access/nbtree/README for more info.
- *
- *	In addition, we store the page's btree level (counting upwards from
- *	zero at a leaf page) as well as some flag bits indicating the page type
- *	and status.  If the page is deleted, a BTDeletedPageData struct is stored
- *	in the page's tuple area, while a standard BTPageOpaqueData struct is
- *	stored in the page special area.
- *
- *	We also store a "vacuum cycle ID".  When a page is split while VACUUM is
- *	processing the index, a nonzero value associated with the VACUUM run is
- *	stored into both halves of the split page.  (If VACUUM is not running,
- *	both pages receive zero cycleids.)	This allows VACUUM to detect whether
- *	a page was split since it started, with a small probability of false match
- *	if the page was last split some exact multiple of MAX_BT_CYCLE_ID VACUUMs
- *	ago.  Also, during a split, the BTP_SPLIT_END flag is cleared in the left
- *	(original) page, and set in the right page, but only if the next page
- *	to its right has a different cycleid.
- *
- *	NOTE: the BTP_LEAF flag bit is redundant since level==0 could be tested
- *	instead.
- *
- *	NOTE: the btpo_level field used to be a union type in order to allow
- *	deleted pages to store a 32-bit safexid in the same field.  We now store
- *	64-bit/full safexid values using BTDeletedPageData instead.
- */
-
-typedef struct BTPageOpaqueData
-{
-	BlockNumber btpo_prev;		/* left sibling, or P_NONE if leftmost */
-	BlockNumber btpo_next;		/* right sibling, or P_NONE if rightmost */
-	uint32		btpo_level;		/* tree level --- zero for leaf pages */
-	uint16		btpo_flags;		/* flag bits, see below */
-	BTCycleId	btpo_cycleid;	/* vacuum cycle ID of latest split */
-} BTPageOpaqueData;
-
-typedef BTPageOpaqueData *BTPageOpaque;
-
-#define BTPageGetOpaque(page) ((BTPageOpaque) PageGetSpecialPointer(page))
-
-/* Bits defined in btpo_flags */
-#define BTP_LEAF		(1 << 0)	/* leaf page, i.e. not internal page */
-#define BTP_ROOT		(1 << 1)	/* root page (has no parent) */
-#define BTP_DELETED		(1 << 2)	/* page has been deleted from tree */
-#define BTP_META		(1 << 3)	/* meta-page */
-#define BTP_HALF_DEAD	(1 << 4)	/* empty, but still in tree */
-#define BTP_SPLIT_END	(1 << 5)	/* rightmost page of split group */
-#define BTP_HAS_GARBAGE (1 << 6)	/* page has LP_DEAD tuples (deprecated) */
-#define BTP_INCOMPLETE_SPLIT (1 << 7)	/* right sibling's downlink is missing */
-#define BTP_HAS_FULLXID	(1 << 8)	/* contains BTDeletedPageData */
-
-/*
- * The max allowed value of a cycle ID is a bit less than 64K.  This is
- * for convenience of pg_filedump and similar utilities: we want to use
- * the last 2 bytes of special space as an index type indicator, and
- * restricting cycle ID lets btree use that space for vacuum cycle IDs
- * while still allowing index type to be identified.
- */
-#define MAX_BT_CYCLE_ID		0xFF7F
-
-
-/*
- * The Meta page is always the first page in the btree index.
- * Its primary purpose is to point to the location of the btree root page.
- * We also point to the "fast" root, which is the current effective root;
- * see README for discussion.
- */
-
-typedef struct BTMetaPageData
-{
-	uint32		btm_magic;		/* should contain BTREE_MAGIC */
-	uint32		btm_version;	/* nbtree version (always <= BTREE_VERSION) */
-	BlockNumber btm_root;		/* current root location */
-	uint32		btm_level;		/* tree level of the root page */
-	BlockNumber btm_fastroot;	/* current "fast" root location */
-	uint32		btm_fastlevel;	/* tree level of the "fast" root page */
-	/* remaining fields only valid when btm_version >= BTREE_NOVAC_VERSION */
-
-	/* number of deleted, non-recyclable pages during last cleanup */
-	uint32		btm_last_cleanup_num_delpages;
-	/* number of heap tuples during last cleanup (deprecated) */
-	float8		btm_last_cleanup_num_heap_tuples;
-
-	bool		btm_allequalimage;	/* are all columns "equalimage"? */
-} BTMetaPageData;
-
-#define BTPageGetMeta(p) \
-	((BTMetaPageData *) PageGetContents(p))
-
-/*
- * The current Btree version is 4.  That's what you'll get when you create
- * a new index.
- *
- * Btree version 3 was used in PostgreSQL v11.  It is mostly the same as
- * version 4, but heap TIDs were not part of the keyspace.  Index tuples
- * with duplicate keys could be stored in any order.  We continue to
- * support reading and writing Btree versions 2 and 3, so that they don't
- * need to be immediately re-indexed at pg_upgrade.  In order to get the
- * new heapkeyspace semantics, however, a REINDEX is needed.
- *
- * Deduplication is safe to use when the btm_allequalimage field is set to
- * true.  It's safe to read the btm_allequalimage field on version 3, but
- * only version 4 indexes make use of deduplication.  Even version 4
- * indexes created on PostgreSQL v12 will need a REINDEX to make use of
- * deduplication, though, since there is no other way to set
- * btm_allequalimage to true (pg_upgrade hasn't been taught to set the
- * metapage field).
- *
- * Btree version 2 is mostly the same as version 3.  There are two new
- * fields in the metapage that were introduced in version 3.  A version 2
- * metapage will be automatically upgraded to version 3 on the first
- * insert to it.  INCLUDE indexes cannot use version 2.
- */
-#define BTREE_METAPAGE	0		/* first page is meta */
-#define BTREE_MAGIC		0x053162	/* magic number in metapage */
-#define BTREE_VERSION	4		/* current version number */
-#define BTREE_MIN_VERSION	2	/* minimum supported version */
-#define BTREE_NOVAC_VERSION	3	/* version with all meta fields set */
-
-/*
- * Maximum size of a btree index entry, including its tuple header.
- *
- * We actually need to be able to fit three items on every page,
- * so restrict any one item to 1/3 the per-page available space.
- *
- * There are rare cases where _bt_truncate() will need to enlarge
- * a heap index tuple to make space for a tiebreaker heap TID
- * attribute, which we account for here.
- */
-#define BTMaxItemSize(page) \
-	(MAXALIGN_DOWN((PageGetPageSize(page) - \
-					MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
-					MAXALIGN(sizeof(BTPageOpaqueData))) / 3) - \
-					MAXALIGN(sizeof(ItemPointerData)))
-#define BTMaxItemSizeNoHeapTid(page) \
-	MAXALIGN_DOWN((PageGetPageSize(page) - \
-				   MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
-				   MAXALIGN(sizeof(BTPageOpaqueData))) / 3)
-
-/*
- * MaxTIDsPerBTreePage is an upper bound on the number of heap TIDs tuples
- * that may be stored on a btree leaf page.  It is used to size the
- * per-page temporary buffers.
- *
- * Note: we don't bother considering per-tuple overheads here to keep
- * things simple (value is based on how many elements a single array of
- * heap TIDs must have to fill the space between the page header and
- * special area).  The value is slightly higher (i.e. more conservative)
- * than necessary as a result, which is considered acceptable.
- */
-#define MaxTIDsPerBTreePage \
-	(int) ((BLCKSZ - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
-		   sizeof(ItemPointerData))
-
-/*
- * The leaf-page fillfactor defaults to 90% but is user-adjustable.
- * For pages above the leaf level, we use a fixed 70% fillfactor.
- * The fillfactor is applied during index build and when splitting
- * a rightmost page; when splitting non-rightmost pages we try to
- * divide the data equally.  When splitting a page that's entirely
- * filled with a single value (duplicates), the effective leaf-page
- * fillfactor is 96%, regardless of whether the page is a rightmost
- * page.
- */
-#define BTREE_MIN_FILLFACTOR		10
-#define BTREE_DEFAULT_FILLFACTOR	90
-#define BTREE_NONLEAF_FILLFACTOR	70
-#define BTREE_SINGLEVAL_FILLFACTOR	96
-
-/*
- *	In general, the btree code tries to localize its knowledge about
- *	page layout to a couple of routines.  However, we need a special
- *	value to indicate "no page number" in those places where we expect
- *	page numbers.  We can use zero for this because we never need to
- *	make a pointer to the metadata page.
- */
-
-#define P_NONE			0
-
-/*
- * Macros to test whether a page is leftmost or rightmost on its tree level,
- * as well as other state info kept in the opaque data.
- */
-#define P_LEFTMOST(opaque)		((opaque)->btpo_prev == P_NONE)
-#define P_RIGHTMOST(opaque)		((opaque)->btpo_next == P_NONE)
-#define P_ISLEAF(opaque)		(((opaque)->btpo_flags & BTP_LEAF) != 0)
-#define P_ISROOT(opaque)		(((opaque)->btpo_flags & BTP_ROOT) != 0)
-#define P_ISDELETED(opaque)		(((opaque)->btpo_flags & BTP_DELETED) != 0)
-#define P_ISMETA(opaque)		(((opaque)->btpo_flags & BTP_META) != 0)
-#define P_ISHALFDEAD(opaque)	(((opaque)->btpo_flags & BTP_HALF_DEAD) != 0)
-#define P_IGNORE(opaque)		(((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD)) != 0)
-#define P_HAS_GARBAGE(opaque)	(((opaque)->btpo_flags & BTP_HAS_GARBAGE) != 0)
-#define P_INCOMPLETE_SPLIT(opaque)	(((opaque)->btpo_flags & BTP_INCOMPLETE_SPLIT) != 0)
-#define P_HAS_FULLXID(opaque)	(((opaque)->btpo_flags & BTP_HAS_FULLXID) != 0)
-
 /*
  * BTDeletedPageData is the page contents of a deleted page
  */
@@ -892,7 +691,7 @@ typedef struct BTDedupStateData
 	 * are implicitly unchanged by deduplication pass).
 	 */
 	int			nintervals;		/* current number of intervals in array */
-	BTDedupInterval intervals[MaxIndexTuplesPerPage];
+	BTDedupInterval intervals[MaxIndexTuplesPerPageLimit];
 } BTDedupStateData;
 
 typedef BTDedupStateData *BTDedupState;
@@ -987,7 +786,7 @@ typedef struct BTScanPosData
 	int			lastItem;		/* last valid index in items[] */
 	int			itemIndex;		/* current index in items[] */
 
-	BTScanPosItem items[MaxTIDsPerBTreePage];	/* MUST BE LAST */
+	BTScanPosItem items[MaxTIDsPerBTreePageLimit];	/* MUST BE LAST */
 } BTScanPosData;
 
 typedef BTScanPosData *BTScanPos;
@@ -1057,7 +856,7 @@ typedef struct BTScanOpaqueData
 	/*
 	 * If we are doing an index-only scan, these are the tuple storage
 	 * workspaces for the currPos and markPos respectively.  Each is of size
-	 * BLCKSZ, so it can hold as much as a full page's worth of tuples.
+	 * CLUSTER_BLOCK_SIZE, so it can hold as much as a full page's worth of tuples.
 	 */
 	char	   *currTuples;		/* tuple storage for currPos */
 	char	   *markTuples;		/* tuple storage for markPos */
@@ -1104,7 +903,7 @@ typedef struct BTOptions
 	 ((BTOptions *) (relation)->rd_options)->fillfactor : \
 	 BTREE_DEFAULT_FILLFACTOR)
 #define BTGetTargetPageFreeSpace(relation) \
-	(BLCKSZ * (100 - BTGetFillFactor(relation)) / 100)
+	(CLUSTER_BLOCK_SIZE * (100 - BTGetFillFactor(relation)) / 100)
 #define BTGetDeduplicateItems(relation) \
 	(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
 				 relation->rd_rel->relam == BTREE_AM_OID), \
diff --git a/src/include/access/nbtree_int.h b/src/include/access/nbtree_int.h
new file mode 100644
index 0000000000..b6dce4743f
--- /dev/null
+++ b/src/include/access/nbtree_int.h
@@ -0,0 +1,220 @@
+/*-------------------------------------------------------------------------
+ *
+ * nbtree_int.h
+ *	  internal defs for header file for postgres btree access method implementation.
+ *
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/nbtree_int.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NBTREE_INT_H
+#define NBTREE_INT_H
+
+#include "storage/block.h"
+
+/* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
+typedef uint16 BTCycleId;
+
+/*
+ *	BTPageOpaqueData -- At the end of every page, we store a pointer
+ *	to both siblings in the tree.  This is used to do forward/backward
+ *	index scans.  The next-page link is also critical for recovery when
+ *	a search has navigated to the wrong page due to concurrent page splits
+ *	or deletions; see src/backend/access/nbtree/README for more info.
+ *
+ *	In addition, we store the page's btree level (counting upwards from
+ *	zero at a leaf page) as well as some flag bits indicating the page type
+ *	and status.  If the page is deleted, a BTDeletedPageData struct is stored
+ *	in the page's tuple area, while a standard BTPageOpaqueData struct is
+ *	stored in the page special area.
+ *
+ *	We also store a "vacuum cycle ID".  When a page is split while VACUUM is
+ *	processing the index, a nonzero value associated with the VACUUM run is
+ *	stored into both halves of the split page.  (If VACUUM is not running,
+ *	both pages receive zero cycleids.)	This allows VACUUM to detect whether
+ *	a page was split since it started, with a small probability of false match
+ *	if the page was last split some exact multiple of MAX_BT_CYCLE_ID VACUUMs
+ *	ago.  Also, during a split, the BTP_SPLIT_END flag is cleared in the left
+ *	(original) page, and set in the right page, but only if the next page
+ *	to its right has a different cycleid.
+ *
+ *	NOTE: the BTP_LEAF flag bit is redundant since level==0 could be tested
+ *	instead.
+ *
+ *	NOTE: the btpo_level field used to be a union type in order to allow
+ *	deleted pages to store a 32-bit safexid in the same field.  We now store
+ *	64-bit/full safexid values using BTDeletedPageData instead.
+ */
+
+typedef struct BTPageOpaqueData
+{
+	BlockNumber btpo_prev;		/* left sibling, or P_NONE if leftmost */
+	BlockNumber btpo_next;		/* right sibling, or P_NONE if rightmost */
+	uint32		btpo_level;		/* tree level --- zero for leaf pages */
+	uint16		btpo_flags;		/* flag bits, see below */
+	BTCycleId	btpo_cycleid;	/* vacuum cycle ID of latest split */
+} BTPageOpaqueData;
+
+typedef BTPageOpaqueData *BTPageOpaque;
+
+#define BTPageGetOpaque(page) ((BTPageOpaque) PageGetSpecialPointer(page))
+
+/* Bits defined in btpo_flags */
+#define BTP_LEAF		(1 << 0)	/* leaf page, i.e. not internal page */
+#define BTP_ROOT		(1 << 1)	/* root page (has no parent) */
+#define BTP_DELETED		(1 << 2)	/* page has been deleted from tree */
+#define BTP_META		(1 << 3)	/* meta-page */
+#define BTP_HALF_DEAD	(1 << 4)	/* empty, but still in tree */
+#define BTP_SPLIT_END	(1 << 5)	/* rightmost page of split group */
+#define BTP_HAS_GARBAGE (1 << 6)	/* page has LP_DEAD tuples (deprecated) */
+#define BTP_INCOMPLETE_SPLIT (1 << 7)	/* right sibling's downlink is missing */
+#define BTP_HAS_FULLXID	(1 << 8)	/* contains BTDeletedPageData */
+
+/*
+ * The max allowed value of a cycle ID is a bit less than 64K.  This is
+ * for convenience of pg_filedump and similar utilities: we want to use
+ * the last 2 bytes of special space as an index type indicator, and
+ * restricting cycle ID lets btree use that space for vacuum cycle IDs
+ * while still allowing index type to be identified.
+ */
+#define MAX_BT_CYCLE_ID		0xFF7F
+
+
+/*
+ * The Meta page is always the first page in the btree index.
+ * Its primary purpose is to point to the location of the btree root page.
+ * We also point to the "fast" root, which is the current effective root;
+ * see README for discussion.
+ */
+
+typedef struct BTMetaPageData
+{
+	uint32		btm_magic;		/* should contain BTREE_MAGIC */
+	uint32		btm_version;	/* nbtree version (always <= BTREE_VERSION) */
+	BlockNumber btm_root;		/* current root location */
+	uint32		btm_level;		/* tree level of the root page */
+	BlockNumber btm_fastroot;	/* current "fast" root location */
+	uint32		btm_fastlevel;	/* tree level of the "fast" root page */
+	/* remaining fields only valid when btm_version >= BTREE_NOVAC_VERSION */
+
+	/* number of deleted, non-recyclable pages during last cleanup */
+	uint32		btm_last_cleanup_num_delpages;
+	/* number of heap tuples during last cleanup (deprecated) */
+	float8		btm_last_cleanup_num_heap_tuples;
+
+	bool		btm_allequalimage;	/* are all columns "equalimage"? */
+} BTMetaPageData;
+
+#define BTPageGetMeta(p) \
+	((BTMetaPageData *) PageGetContents(p))
+
+/*
+ * The current Btree version is 4.  That's what you'll get when you create
+ * a new index.
+ *
+ * Btree version 3 was used in PostgreSQL v11.  It is mostly the same as
+ * version 4, but heap TIDs were not part of the keyspace.  Index tuples
+ * with duplicate keys could be stored in any order.  We continue to
+ * support reading and writing Btree versions 2 and 3, so that they don't
+ * need to be immediately re-indexed at pg_upgrade.  In order to get the
+ * new heapkeyspace semantics, however, a REINDEX is needed.
+ *
+ * Deduplication is safe to use when the btm_allequalimage field is set to
+ * true.  It's safe to read the btm_allequalimage field on version 3, but
+ * only version 4 indexes make use of deduplication.  Even version 4
+ * indexes created on PostgreSQL v12 will need a REINDEX to make use of
+ * deduplication, though, since there is no other way to set
+ * btm_allequalimage to true (pg_upgrade hasn't been taught to set the
+ * metapage field).
+ *
+ * Btree version 2 is mostly the same as version 3.  There are two new
+ * fields in the metapage that were introduced in version 3.  A version 2
+ * metapage will be automatically upgraded to version 3 on the first
+ * insert to it.  INCLUDE indexes cannot use version 2.
+ */
+#define BTREE_METAPAGE	0		/* first page is meta */
+#define BTREE_MAGIC		0x053162	/* magic number in metapage */
+#define BTREE_VERSION	4		/* current version number */
+#define BTREE_MIN_VERSION	2	/* minimum supported version */
+#define BTREE_NOVAC_VERSION	3	/* version with all meta fields set */
+
+/*
+ * Maximum size of a btree index entry, including its tuple header.
+ *
+ * We actually need to be able to fit three items on every page,
+ * so restrict any one item to 1/3 the per-page available space.
+ *
+ * There are rare cases where _bt_truncate() will need to enlarge
+ * a heap index tuple to make space for a tiebreaker heap TID
+ * attribute, which we account for here.
+ */
+#define BTMaxItemSize(page) \
+	(MAXALIGN_DOWN((PageGetPageSize(page) - \
+					MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
+					MAXALIGN(sizeof(BTPageOpaqueData))) / 3) - \
+					MAXALIGN(sizeof(ItemPointerData)))
+#define BTMaxItemSizeNoHeapTid(page) \
+	MAXALIGN_DOWN((PageGetPageSize(page) - \
+				   MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
+				   MAXALIGN(sizeof(BTPageOpaqueData))) / 3)
+
+/*
+ * MaxTIDsPerBTreePage is an upper bound on the number of heap TIDs tuples
+ * that may be stored on a btree leaf page.  It is used to size the
+ * per-page temporary buffers.
+ *
+ * Note: we don't bother considering per-tuple overheads here to keep
+ * things simple (value is based on how many elements a single array of
+ * heap TIDs must have to fill the space between the page header and
+ * special area).  The value is slightly higher (i.e. more conservative)
+ * than necessary as a result, which is considered acceptable.
+ */
+#define MaxTIDsPerBTreePage GetBlockSize(BS_MAX_TIDS_PER_BTREE_PAGE)
+#define MaxTIDsPerBTreePageLimit CalcMaxTIDsPerBTreePage(MAX_BLOCK_SIZE)
+
+/*
+ * The leaf-page fillfactor defaults to 90% but is user-adjustable.
+ * For pages above the leaf level, we use a fixed 70% fillfactor.
+ * The fillfactor is applied during index build and when splitting
+ * a rightmost page; when splitting non-rightmost pages we try to
+ * divide the data equally.  When splitting a page that's entirely
+ * filled with a single value (duplicates), the effective leaf-page
+ * fillfactor is 96%, regardless of whether the page is a rightmost
+ * page.
+ */
+#define BTREE_MIN_FILLFACTOR		10
+#define BTREE_DEFAULT_FILLFACTOR	90
+#define BTREE_NONLEAF_FILLFACTOR	70
+#define BTREE_SINGLEVAL_FILLFACTOR	96
+
+/*
+ *	In general, the btree code tries to localize its knowledge about
+ *	page layout to a couple of routines.  However, we need a special
+ *	value to indicate "no page number" in those places where we expect
+ *	page numbers.  We can use zero for this because we never need to
+ *	make a pointer to the metadata page.
+ */
+
+#define P_NONE			0
+
+/*
+ * Macros to test whether a page is leftmost or rightmost on its tree level,
+ * as well as other state info kept in the opaque data.
+ */
+#define P_LEFTMOST(opaque)		((opaque)->btpo_prev == P_NONE)
+#define P_RIGHTMOST(opaque)		((opaque)->btpo_next == P_NONE)
+#define P_ISLEAF(opaque)		(((opaque)->btpo_flags & BTP_LEAF) != 0)
+#define P_ISROOT(opaque)		(((opaque)->btpo_flags & BTP_ROOT) != 0)
+#define P_ISDELETED(opaque)		(((opaque)->btpo_flags & BTP_DELETED) != 0)
+#define P_ISMETA(opaque)		(((opaque)->btpo_flags & BTP_META) != 0)
+#define P_ISHALFDEAD(opaque)	(((opaque)->btpo_flags & BTP_HALF_DEAD) != 0)
+#define P_IGNORE(opaque)		(((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD)) != 0)
+#define P_HAS_GARBAGE(opaque)	(((opaque)->btpo_flags & BTP_HAS_GARBAGE) != 0)
+#define P_INCOMPLETE_SPLIT(opaque)	(((opaque)->btpo_flags & BTP_INCOMPLETE_SPLIT) != 0)
+#define P_HAS_FULLXID(opaque)	(((opaque)->btpo_flags & BTP_HAS_FULLXID) != 0)
+
+#endif
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index a8a424d92d..a477bcdf6b 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -19,7 +19,7 @@
 
 
 /*
- * Define SLRU segment size.  A page is the same BLCKSZ as is used everywhere
+ * Define SLRU segment size.  A page is the same CLUSTER_BLOCK_SIZE as is used everywhere
  * else in Postgres.  The segment size can be chosen somewhat arbitrarily;
  * we make it 32 pages by default, or 256Kb, i.e. 1M transactions for CLOG
  * or 64K transactions for SUBTRANS.
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index c6ef46fc20..cb5a10e6b3 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -36,7 +36,7 @@ typedef struct SpGistOptions
 	 ((SpGistOptions *) (relation)->rd_options)->fillfactor : \
 	 SPGIST_DEFAULT_FILLFACTOR)
 #define SpGistGetTargetPageFreeSpace(relation) \
-	(BLCKSZ * (100 - SpGistGetFillFactor(relation)) / 100)
+	(CLUSTER_BLOCK_SIZE * (100 - SpGistGetFillFactor(relation)) / 100)
 
 
 /* SPGiST leaf tuples have one key column, optionally have included columns */
@@ -226,14 +226,14 @@ typedef struct SpGistScanOpaqueData
 	TupleDesc	reconTupDesc;	/* if so, descriptor for reconstructed tuples */
 	int			nPtrs;			/* number of TIDs found on current page */
 	int			iPtr;			/* index for scanning through same */
-	ItemPointerData heapPtrs[MaxIndexTuplesPerPage];	/* TIDs from cur page */
-	bool		recheck[MaxIndexTuplesPerPage]; /* their recheck flags */
-	bool		recheckDistances[MaxIndexTuplesPerPage];	/* distance recheck
+	ItemPointerData heapPtrs[MaxIndexTuplesPerPageLimit];	/* TIDs from cur page */
+	bool		recheck[MaxIndexTuplesPerPageLimit]; /* their recheck flags */
+	bool		recheckDistances[MaxIndexTuplesPerPageLimit];	/* distance recheck
 															 * flags */
-	HeapTuple	reconTups[MaxIndexTuplesPerPage];	/* reconstructed tuples */
+	HeapTuple	reconTups[MaxIndexTuplesPerPageLimit];	/* reconstructed tuples */
 
 	/* distances (for recheck) */
-	IndexOrderByDistance *distances[MaxIndexTuplesPerPage];
+	IndexOrderByDistance *distances[MaxIndexTuplesPerPageLimit];
 
 	/*
 	 * Note: using MaxIndexTuplesPerPage above is a bit hokey since
@@ -445,7 +445,7 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
 
 /* Page capacity after allowing for fixed header and special space */
 #define SPGIST_PAGE_CAPACITY  \
-	MAXALIGN_DOWN(BLCKSZ - \
+	MAXALIGN_DOWN(CLUSTER_BLOCK_SIZE - \
 				  SizeOfPageHeaderData - \
 				  MAXALIGN(sizeof(SpGistPageOpaqueData)))
 
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index b0fd338a00..3bc419ed78 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -391,6 +391,7 @@ extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli);
 extern void XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 								   bool detailed_format, StringInfo buf,
 								   uint32 *fpi_len);
+extern uint32 ClusterBlockSize(void);
 
 /*
  * Exported for the functions in timeline.c and xlogarchive.c.  Only valid
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index f355e08e1d..ef4cad64ac 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -122,21 +122,21 @@ typedef struct XLogRecordBlockHeader
  * in the middle, which contains only zero bytes.  Since we know that the
  * "hole" is all zeros, we remove it from the stored data (and it's not counted
  * in the XLOG record's CRC, either).  Hence, the amount of block data actually
- * present is (BLCKSZ - <length of "hole" bytes>).
+ * present is (CLUSTER_BLOCK_SIZE - <length of "hole" bytes>).
  *
  * Additionally, when wal_compression is enabled, we will try to compress full
  * page images using one of the supported algorithms, after removing the
  * "hole". This can reduce the WAL volume, but at some extra cost of CPU spent
  * on the compression during WAL logging. In this case, since the "hole"
  * length cannot be calculated by subtracting the number of page image bytes
- * from BLCKSZ, basically it needs to be stored as an extra information.
+ * from CLUSTER_BLOCK_SIZE, basically it needs to be stored as an extra information.
  * But when no "hole" exists, we can assume that the "hole" length is zero
  * and no such an extra information needs to be stored. Note that
  * the original version of page image is stored in WAL instead of the
  * compressed one if the number of bytes saved by compression is less than
  * the length of extra information. Hence, when a page image is successfully
  * compressed, the amount of block data actually present is less than
- * BLCKSZ - the length of "hole" bytes - the length of extra information.
+ * CLUSTER_BLOCK_SIZE - the length of "hole" bytes - the length of extra information.
  */
 typedef struct XLogRecordBlockImageHeader
 {
diff --git a/src/include/backup/basebackup_sink.h b/src/include/backup/basebackup_sink.h
index 224732e333..012bfb7725 100644
--- a/src/include/backup/basebackup_sink.h
+++ b/src/include/backup/basebackup_sink.h
@@ -80,7 +80,7 @@ typedef struct bbsink_state
  * 'bbs_ops' is the relevant callback table.
  *
  * 'bbs_buffer' is the buffer into which data destined for the bbsink
- * should be stored. It must be a multiple of BLCKSZ.
+ * should be stored. It must be a multiple of CLUSTER_BLOCK_SIZE.
  *
  * 'bbs_buffer_length' is the allocated length of the buffer.
  *
@@ -183,7 +183,7 @@ bbsink_begin_backup(bbsink *sink, bbsink_state *state, int buffer_length)
 	sink->bbs_ops->begin_backup(sink);
 
 	Assert(sink->bbs_buffer != NULL);
-	Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
+	Assert((sink->bbs_buffer_length % CLUSTER_BLOCK_SIZE) == 0);
 }
 
 /* Begin an archive. */
diff --git a/src/include/c.h b/src/include/c.h
index f69d739be5..82584b123a 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1118,7 +1118,7 @@ extern void ExceptionalCondition(const char *conditionName,
 	((var) = ((var) < 0) ? 1 : -(var))
 
 /*
- * Use this, not "char buf[BLCKSZ]", to declare a field or local variable
+ * Use this, not "char buf[MAX_BLOCK_SIZE]", to declare a field or local variable
  * holding a page buffer, if that page might be accessed as a page.  Otherwise
  * the variable might be under-aligned, causing problems on alignment-picky
  * hardware.  We include both "double" and "int64" in the union to ensure that
@@ -1127,7 +1127,7 @@ extern void ExceptionalCondition(const char *conditionName,
  */
 typedef union PGAlignedBlock
 {
-	char		data[BLCKSZ];
+	char		data[MAX_BLOCK_SIZE];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGAlignedBlock;
@@ -1145,7 +1145,7 @@ typedef union PGIOAlignedBlock
 #ifdef pg_attribute_aligned
 	pg_attribute_aligned(PG_IO_ALIGN_SIZE)
 #endif
-	char		data[BLCKSZ];
+	char		data[MAX_BLOCK_SIZE];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGIOAlignedBlock;
diff --git a/src/include/common/blocksize.h b/src/include/common/blocksize.h
new file mode 100644
index 0000000000..d8343c4b51
--- /dev/null
+++ b/src/include/common/blocksize.h
@@ -0,0 +1,91 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.h
+ *	  defintions for cluster-specific limits/structure defs
+ *
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION: src/include/clustersizes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef BLOCKSIZE_H
+#define BLOCKSIZE_H
+
+#ifndef DEFAULT_BLOCK_SIZE
+#define DEFAULT_BLOCK_SIZE 8192
+#endif
+
+#ifndef MIN_BLOCK_SIZE
+#define MIN_BLOCK_SIZE 1024
+#endif
+
+#ifndef MAX_BLOCK_SIZE
+#define MAX_BLOCK_SIZE 32*1024
+#endif
+
+#define IsValidBlockSize(size) ((size) >= MIN_BLOCK_SIZE && \
+								(size) <= MAX_BLOCK_SIZE && \
+								((size)&((size)-1)) == 0)
+
+typedef enum ClusterSize {
+    BS_BLOCK_SIZE = 0,
+	BS_MAX_HEAP_TUPLES,
+	BS_MAX_HEAP_TUPLES_PER_PAGE,
+	BS_MAX_INDEX_TUPLES_PER_PAGE,
+	BS_MAX_TIDS_PER_BTREE_PAGE,
+	BS_TOAST_MAX_CHUNK_SIZE,
+	BS_NUM_SIZES
+} ClusterSize;
+
+extern PGDLLIMPORT uint32 calculated_block_sizes[BS_NUM_SIZES];
+
+void BlockSizeInit(Size rawblocksize);
+#define GetBlockSize(theSize) (calculated_block_sizes[theSize])
+#define CLUSTER_BLOCK_SIZE GetBlockSize(BS_BLOCK_SIZE)
+#define CLUSTER_RELSEG_SIZE (RELSEG_SIZE * DEFAULT_BLOCK_SIZE / CLUSTER_BLOCK_SIZE)
+
+/* Specific calculations' now parameterized sources */
+
+/* originally in heaptoast.h */
+
+#define CalcMaximumBytesPerTuple(blocksize,tuplesPerPage)	\
+	MAXALIGN_DOWN((blocksize - \
+				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
+				  / (tuplesPerPage))
+
+#define CalcToastMaxChunkSize(blocksize) \
+	(CalcMaximumBytesPerTuple(blocksize,EXTERN_TUPLES_PER_PAGE) -	\
+	 MAXALIGN(SizeofHeapTupleHeader) -					\
+	 sizeof(Oid) -										\
+	 sizeof(int32) -									\
+	 VARHDRSZ)
+
+/* originally in htup_details.h */
+
+#define CalcMaxHeapTupleSize(size)  (size - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+
+#define CalcMaxHeapTuplesPerPage(size)									\
+	((int) (((size) - SizeOfPageHeaderData) /							\
+			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
+
+/* originally in itup.h */
+
+#define CalcMaxIndexTuplesPerPage(size)		\
+	((int) ((size - SizeOfPageHeaderData) / \
+			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
+
+/* originally in nbtree_int.h */
+
+#define CalcMaxTIDsPerBTreePage(size)									\
+	(int) ((size - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
+		   sizeof(ItemPointerData))
+
+/* originally in bloom.h */
+#define CalcFreeBlockNumberElems(size) (MAXALIGN_DOWN(size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData)) \
+													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) \
+									 ) / sizeof(BlockNumber))
+
+#endif
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 6d572c3820..5c9fd21eca 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -24,11 +24,11 @@
 /* Size of a disk block --- this also limits the size of a tuple. You can set
    it bigger if you need bigger tuples (although TOAST should reduce the need
    to have large tuples, since fields can be spread across multiple tuples).
-   BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
+   CLUSTER_BLOCK_SIZE must be a power of 2. The maximum possible value of CLUSTER_BLOCK_SIZE is
    currently 2^15 (32768). This is determined by the 15-bit widths of the
    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
-   Changing BLCKSZ requires an initdb. */
-#undef BLCKSZ
+   Changing CLUSTER_BLOCK_SIZE requires an initdb. */
+#undef CLUSTER_BLOCK_SIZE
 
 /* Saved arguments from configure */
 #undef CONFIGURE_ARGS
@@ -660,8 +660,8 @@
 #undef PTHREAD_CREATE_JOINABLE
 
 /* RELSEG_SIZE is the maximum number of blocks allowed in one disk file. Thus,
-   the maximum size of a single file is RELSEG_SIZE * BLCKSZ; relations bigger
-   than that are divided into multiple files. RELSEG_SIZE * BLCKSZ must be
+   the maximum size of a single file is RELSEG_SIZE * CLUSTER_BLOCK_SIZE; relations bigger
+   than that are divided into multiple files. RELSEG_SIZE * CLUSTER_BLOCK_SIZE must be
    less than your OS' limit on file size. This is often 2 GB or 4GB in a
    32-bit operating system, unless you have large file support enabled. By
    default, we make the limit 1 GB to avoid any possible integer-overflow
@@ -781,7 +781,7 @@
 # endif
 #endif
 
-/* Size of a WAL file block. This need have no particular relation to BLCKSZ.
+/* Size of a WAL file block. This need have no particular relation to CLUSTER_BLOCK_SIZE.
    XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O,
    XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O
    buffers, else direct I/O may fail. Changing XLOG_BLCKSZ requires an initdb.
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index a1a93ad706..950b651b4a 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -33,7 +33,7 @@
  *
  * The minimum value is 8 (GIN indexes use 8-argument support functions).
  * The maximum possible value is around 600 (limited by index tuple size in
- * pg_proc's index; BLCKSZ larger than 8K would allow more).  Values larger
+ * pg_proc's index; CLUSTER_BLOCK_SIZE larger than 8K would allow more).  Values larger
  * than needed will waste memory and processing time, but do not directly
  * cost disk space.
  *
@@ -233,6 +233,14 @@
  */
 #define PG_IO_ALIGN_SIZE		4096
 
+/*
+ * Blocksize-related constants
+ */
+
+#define MIN_BLOCK_SIZE 1024
+#define MAX_BLOCK_SIZE 32768
+/* #define DEFAULT_BLOCK_SIZE 8192 */
+
 /*
  *------------------------------------------------------------------------
  * The following symbols are for enabling debugging code, not for
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a028ff789..4651389fc2 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -45,6 +45,7 @@
 #include "c.h"
 #include "utils/elog.h"
 #include "utils/palloc.h"
+#include "common/blocksize.h"
 
 /* ----------------------------------------------------------------
  *				Section 1:	Datum type + support functions
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 0f5fb6be00..9255457dbc 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -323,7 +323,7 @@ BufferGetBlock(Buffer buffer)
 	if (BufferIsLocal(buffer))
 		return LocalBufferBlockPointers[-buffer - 1];
 	else
-		return (Block) (BufferBlocks + ((Size) (buffer - 1)) * BLCKSZ);
+		return (Block) (BufferBlocks + ((Size) (buffer - 1)) * CLUSTER_BLOCK_SIZE);
 }
 
 /*
@@ -341,7 +341,7 @@ static inline Size
 BufferGetPageSize(Buffer buffer)
 {
 	AssertMacro(BufferIsValid(buffer));
-	return (Size) BLCKSZ;
+	return (Size) CLUSTER_BLOCK_SIZE;
 }
 
 /*
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 424ecba028..7cd273d5c1 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -325,7 +325,7 @@ static inline void
 PageValidateSpecialPointer(Page page)
 {
 	Assert(page);
-	Assert(((PageHeader) page)->pd_special <= BLCKSZ);
+	Assert(((PageHeader) page)->pd_special <= CLUSTER_BLOCK_SIZE);
 	Assert(((PageHeader) page)->pd_special >= SizeOfPageHeaderData);
 }
 
@@ -475,15 +475,15 @@ do { \
 	PageIsVerifiedExtended(page, blkno, \
 						   PIV_LOG_WARNING | PIV_REPORT_STAT)
 
-/*
- * Check that BLCKSZ is a multiple of sizeof(size_t).  In
- * PageIsVerifiedExtended(), it is much faster to check if a page is
- * full of zeroes using the native word size.  Note that this assertion
- * is kept within a header to make sure that StaticAssertDecl() works
- * across various combinations of platforms and compilers.
- */
-StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),
-				 "BLCKSZ has to be a multiple of sizeof(size_t)");
+/* /\* */
+/*  * Check that CLUSTER_BLOCK_SIZE is a multiple of sizeof(size_t).  In */
+/*  * PageIsVerifiedExtended(), it is much faster to check if a page is */
+/*  * full of zeroes using the native word size.  Note that this assertion */
+/*  * is kept within a header to make sure that StaticAssertDecl() works */
+/*  * across various combinations of platforms and compilers. */
+/*  *\/ */
+/* StaticAssertDecl(CLUSTER_BLOCK_SIZE == ((CLUSTER_BLOCK_SIZE / sizeof(size_t)) * sizeof(size_t)), */
+/* 				 "CLUSTER_BLOCK_SIZE has to be a multiple of sizeof(size_t)"); */
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
 extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
diff --git a/src/include/storage/checksum.h b/src/include/storage/checksum.h
index 4afd25a0af..bea1cbd5b6 100644
--- a/src/include/storage/checksum.h
+++ b/src/include/storage/checksum.h
@@ -19,6 +19,6 @@
  * Compute the checksum for a Postgres page.  The page must be aligned on a
  * 4-byte boundary.
  */
-extern uint16 pg_checksum_page(char *page, BlockNumber blkno);
+extern uint16 pg_checksum_page(char *page, BlockNumber blkno, Size pagesize);
 
 #endif							/* CHECKSUM_H */
diff --git a/src/include/storage/checksum_impl.h b/src/include/storage/checksum_impl.h
index 7b157161a2..683f7c1c76 100644
--- a/src/include/storage/checksum_impl.h
+++ b/src/include/storage/checksum_impl.h
@@ -101,6 +101,7 @@
  */
 
 #include "storage/bufpage.h"
+#include "common/blocksize.h"
 
 /* number of checksums to calculate in parallel */
 #define N_SUMS 32
@@ -111,7 +112,7 @@
 typedef union
 {
 	PageHeaderData phdr;
-	uint32		data[BLCKSZ / (sizeof(uint32) * N_SUMS)][N_SUMS];
+	uint32		data[MAX_BLOCK_SIZE / (sizeof(uint32) * N_SUMS)][N_SUMS];
 } PGChecksummablePage;
 
 /*
@@ -143,7 +144,7 @@ do { \
  * (at least on 4-byte boundary).
  */
 static uint32
-pg_checksum_block(const PGChecksummablePage *page)
+pg_checksum_block(const PGChecksummablePage *page, Size blocksize)
 {
 	uint32		sums[N_SUMS];
 	uint32		result = 0;
@@ -151,13 +152,13 @@ pg_checksum_block(const PGChecksummablePage *page)
 				j;
 
 	/* ensure that the size is compatible with the algorithm */
-	Assert(sizeof(PGChecksummablePage) == BLCKSZ);
+	Assert(CLUSTER_BLOCK_SIZE == blocksize);
 
 	/* initialize partial checksums to their corresponding offsets */
 	memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets));
 
 	/* main checksum calculation */
-	for (i = 0; i < (uint32) (BLCKSZ / (sizeof(uint32) * N_SUMS)); i++)
+	for (i = 0; i < (uint32) (blocksize / (sizeof(uint32) * N_SUMS)); i++)
 		for (j = 0; j < N_SUMS; j++)
 			CHECKSUM_COMP(sums[j], page->data[i][j]);
 
@@ -184,7 +185,7 @@ pg_checksum_block(const PGChecksummablePage *page)
  * checksum itself), and the page data.
  */
 uint16
-pg_checksum_page(char *page, BlockNumber blkno)
+pg_checksum_page(char *page, BlockNumber blkno, Size pagesize)
 {
 	PGChecksummablePage *cpage = (PGChecksummablePage *) page;
 	uint16		save_checksum;
@@ -201,7 +202,7 @@ pg_checksum_page(char *page, BlockNumber blkno)
 	 */
 	save_checksum = cpage->phdr.pd_checksum;
 	cpage->phdr.pd_checksum = 0;
-	checksum = pg_checksum_block(cpage);
+	checksum = pg_checksum_block(cpage, pagesize);
 	cpage->phdr.pd_checksum = save_checksum;
 
 	/* Mix in the block number to detect transposed pages */
diff --git a/src/include/storage/freespace.h b/src/include/storage/freespace.h
index 9e1a85a141..4bbad4aaad 100644
--- a/src/include/storage/freespace.h
+++ b/src/include/storage/freespace.h
@@ -19,6 +19,7 @@
 #include "utils/relcache.h"
 
 /* prototypes for public functions in freespace.c */
+extern void FreeSpaceMapInit(void);
 extern Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk);
 extern BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded);
 extern BlockNumber RecordAndGetPageWithFreeSpace(Relation rel,
diff --git a/src/include/storage/fsm_internals.h b/src/include/storage/fsm_internals.h
index 9e314c83fa..95e4810d3a 100644
--- a/src/include/storage/fsm_internals.h
+++ b/src/include/storage/fsm_internals.h
@@ -48,10 +48,10 @@ typedef FSMPageData *FSMPage;
  * Number of non-leaf and leaf nodes, and nodes in total, on an FSM page.
  * These definitions are internal to fsmpage.c.
  */
-#define NodesPerPage (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
+#define NodesPerPage (CLUSTER_BLOCK_SIZE - MAXALIGN(SizeOfPageHeaderData) - \
 					  offsetof(FSMPageData, fp_nodes))
 
-#define NonLeafNodesPerPage (BLCKSZ / 2 - 1)
+#define NonLeafNodesPerPage (CLUSTER_BLOCK_SIZE / 2 - 1)
 #define LeafNodesPerPage (NodesPerPage - NonLeafNodesPerPage)
 
 /*
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
index db521f23eb..911667b732 100644
--- a/src/include/storage/large_object.h
+++ b/src/include/storage/large_object.h
@@ -54,7 +54,7 @@ typedef struct LargeObjectDesc
 /*
  * Each "page" (tuple) of a large object can hold this much data
  *
- * We could set this as high as BLCKSZ less some overhead, but it seems
+ * We could set this as high as CLUSTER_BLOCK_SIZE less some overhead, but it seems
  * better to make it a smaller value, so that not as much space is used
  * up when a page-tuple is updated.  Note that the value is deliberately
  * chosen large enough to trigger the tuple toaster, so that we will
@@ -67,7 +67,9 @@ typedef struct LargeObjectDesc
  *
  * NB: Changing LOBLKSIZE requires an initdb.
  */
-#define LOBLKSIZE		(BLCKSZ / 4)
+#define CalcLOBLKSIZE(size) (size/4)
+#define LOBLKSIZE		(CLUSTER_BLOCK_SIZE / 4)
+#define LOBLKSIZE_LIMIT		CalcLOBLKSIZE(MAX_BLOCK_SIZE)
 
 /*
  * Maximum length in bytes for a large object.  To make this larger, we'd
diff --git a/src/include/storage/off.h b/src/include/storage/off.h
index 3540308069..a608d21bbf 100644
--- a/src/include/storage/off.h
+++ b/src/include/storage/off.h
@@ -25,7 +25,9 @@ typedef uint16 OffsetNumber;
 
 #define InvalidOffsetNumber		((OffsetNumber) 0)
 #define FirstOffsetNumber		((OffsetNumber) 1)
-#define MaxOffsetNumber			((OffsetNumber) (BLCKSZ / sizeof(ItemIdData)))
+#define CalcMaxOffsetNumber(size)			((OffsetNumber) (size / sizeof(ItemIdData)))
+#define MaxOffsetNumber			((OffsetNumber)CalcMaxOffsetNumber(CLUSTER_BLOCK_SIZE))
+#define MaxOffsetNumberLimit			((OffsetNumber)CalcMaxOffsetNumber(MAX_BLOCK_SIZE))
 
 /* ----------------
  *		support macros
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index d5a0880678..a47b6b6c2d 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -298,6 +298,7 @@ extern bool ConfigOptionIsVisible(struct config_generic *conf);
 /* get the current set of variables */
 extern struct config_generic **get_guc_variables(int *num_vars);
 
+extern void update_dynamic_gucs(void);
 extern void build_guc_variables(void);
 
 /* search in enum options */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1426a353cd..4e1e6e1d97 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -368,14 +368,14 @@ typedef struct StdRdOptions
  *		Returns the relation's desired space usage per page in bytes.
  */
 #define RelationGetTargetPageUsage(relation, defaultff) \
-	(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
+	(CLUSTER_BLOCK_SIZE * RelationGetFillFactor(relation, defaultff) / 100)
 
 /*
  * RelationGetTargetPageFreeSpace
  *		Returns the relation's desired freespace per page in bytes.
  */
 #define RelationGetTargetPageFreeSpace(relation, defaultff) \
-	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+	(CLUSTER_BLOCK_SIZE * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
 
 /*
  * RelationIsUsedAsCatalogTable
diff --git a/src/test/modules/test_slru/test_slru.c b/src/test/modules/test_slru/test_slru.c
index ae21444c47..f3a66aa4a0 100644
--- a/src/test/modules/test_slru/test_slru.c
+++ b/src/test/modules/test_slru/test_slru.c
@@ -77,7 +77,7 @@ test_slru_page_write(PG_FUNCTION_ARGS)
 
 	/* write given data to the page, up to the limit of the page */
 	strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
-			BLCKSZ - 1);
+			CLUSTER_BLOCK_SIZE - 1);
 
 	SimpleLruWritePage(TestSlruCtl, slotno);
 	LWLockRelease(TestSLRULock);
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 93ed5e8cc0..eae79e793e 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -320,7 +320,7 @@ CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicat
 CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off);
 -- Generate enough garbage tuples in index to ensure that even the unique index
 -- with deduplication enabled has to check multiple leaf pages during unique
--- checking (at least with a BLCKSZ of 8192 or less)
+-- checking (at least with a CLUSTER_BLOCK_SIZE of 8192 or less)
 DO $$
 BEGIN
     FOR r IN 1..1350 LOOP
@@ -331,7 +331,7 @@ END$$;
 -- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple.
 -- The implementation prefers deleting existing items to merging any duplicate
 -- tuples into a posting list, so we need an explicit test to make sure we get
--- coverage (note that this test also assumes BLCKSZ is 8192 or less):
+-- coverage (note that this test also assumes CLUSTER_BLOCK_SIZE is 8192 or less):
 DROP INDEX plain_unique;
 DELETE FROM dedup_unique_test_table WHERE a = 1;
 INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i;
diff --git a/src/test/regress/expected/largeobject.out b/src/test/regress/expected/largeobject.out
index bdcede6728..b2e9ab8f84 100644
--- a/src/test/regress/expected/largeobject.out
+++ b/src/test/regress/expected/largeobject.out
@@ -353,7 +353,7 @@ SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
    670800
 (1 row)
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default CLUSTER_BLOCK_SIZE, LOBLKSIZE = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
  lo_lseek 
diff --git a/src/test/regress/expected/largeobject_1.out b/src/test/regress/expected/largeobject_1.out
index d700910c35..f2f4853682 100644
--- a/src/test/regress/expected/largeobject_1.out
+++ b/src/test/regress/expected/largeobject_1.out
@@ -353,7 +353,7 @@ SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
    680800
 (1 row)
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default CLUSTER_BLOCK_SIZE, LOBLKSIZE = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
  lo_lseek 
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 239f4a4755..372e8efbff 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -173,7 +173,7 @@ CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicat
 CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off);
 -- Generate enough garbage tuples in index to ensure that even the unique index
 -- with deduplication enabled has to check multiple leaf pages during unique
--- checking (at least with a BLCKSZ of 8192 or less)
+-- checking (at least with a CLUSTER_BLOCK_SIZE of 8192 or less)
 DO $$
 BEGIN
     FOR r IN 1..1350 LOOP
@@ -185,7 +185,7 @@ END$$;
 -- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple.
 -- The implementation prefers deleting existing items to merging any duplicate
 -- tuples into a posting list, so we need an explicit test to make sure we get
--- coverage (note that this test also assumes BLCKSZ is 8192 or less):
+-- coverage (note that this test also assumes CLUSTER_BLOCK_SIZE is 8192 or less):
 DROP INDEX plain_unique;
 DELETE FROM dedup_unique_test_table WHERE a = 1;
 INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i;
diff --git a/src/test/regress/sql/largeobject.sql b/src/test/regress/sql/largeobject.sql
index 800e4fcc6a..0740c7e796 100644
--- a/src/test/regress/sql/largeobject.sql
+++ b/src/test/regress/sql/largeobject.sql
@@ -191,7 +191,7 @@ UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS inte
 -- verify length of large object
 SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default CLUSTER_BLOCK_SIZE, LOBLKSIZE = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 9e05eb91b1..ead1d4e26f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -135,7 +135,7 @@ sub mkvcbuild
 	}
 
 	our @pgcommonallfiles = qw(
-	  archive.c base64.c checksum_helper.c compression.c
+	  archive.c base64.c blocksize.c checksum_helper.c compression.c
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
 	  keywords.c kwlookup.c link-canary.c md5_common.c percentrepl.c
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index b6d31c3583..280f41a875 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -212,10 +212,10 @@ sub GenerateFiles
 		ALIGNOF_PG_INT128_TYPE => undef,
 		ALIGNOF_SHORT => 2,
 		AC_APPLE_UNIVERSAL_BUILD => undef,
-		BLCKSZ => 1024 * $self->{options}->{blocksize},
 		CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"',
 		DEF_PGPORT => $port,
 		DEF_PGPORT_STR => qq{"$port"},
+		DEFAULT_BLOCK_SIZE => 1024 * $self->{options}->{blocksize},
 		DLSUFFIX => '".dll"',
 		ENABLE_GSS => $self->{options}->{gss} ? 1 : undef,
 		ENABLE_NLS => $self->{options}->{nls} ? 1 : undef,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 260854747b..ada9fc4f70 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -893,7 +893,6 @@ Form_pg_ts_template
 Form_pg_type
 Form_pg_user_mapping
 FormatNode
-FreeBlockNumberArray
 FreeListData
 FreePageBtree
 FreePageBtreeHeader
-- 
2.39.2

#2Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: David Christensen (#1)
Re: Initdb-time block size specification

On 6/30/23 19:35, David Christensen wrote:

Hi,

As discussed somewhat at PgCon, enclosed is v1 of a patch to provide
variable block sizes; basically instead of BLCKSZ being a compile-time
constant, a single set of binaries can support all of the block sizes
Pg can support, using the value stored in pg_control as the basis.
(Possible future plans would be to make this something even more
dynamic, such as configured per tablespace, but this is out of scope;
this just sets up the infrastructure for this.)

Whereas we had traditionally used BLCKSZ to indicate the compile-time selected
block size, this commit adjusted things so the cluster block size can be
selected at initdb time.

In order to code for this, we introduce a few new defines:

- CLUSTER_BLOCK_SIZE is the blocksize for this cluster itself. This is not
valid until BlockSizeInit() has been called in the given backend, which we do as
early as possible by parsing the ControlFile and using the blcksz field.

- MIN_BLOCK_SIZE and MAX_BLOCK_SIZE are the limits for the selectable block
size. It is required that CLUSTER_BLOCK_SIZE is a power of 2 between these two
constants.

- DEFAULT_BLOCK_SIZE is the moral equivalent of BLCKSZ; it is the built-in
default value. This is used in a few places that just needed a buffer of an
arbitrary size, but the dynamic value CLUSTER_BLOCK_SIZE should almost always be
used instead.

- CLUSTER_RELSEG_SIZE is used instead of RELSEG_SIZE, since internally we are
storing the segment size in terms of number of blocks. RELSEG_SIZE is still
kept, but is used in terms of the number of blocks of DEFAULT_BLOCK_SIZE;
CLUSTER_RELSEG_SIZE scales appropriately (and is the only thing used internally)
to keep the same target total segment size regardless of block size.

Do we really want to prefix the option with CLUSTER_? That seems to just
add a lot of noise into the patch, and I don't see much value in this
rename. I'd prefer keeping BLCKSZ and tweak just the couple places that
need "default" to use BLCKSZ_DEFAULT or something like that.

But more importantly, I'd say we use CAPITALIZED_NAMES for compile-time
values, so after making this initdb-time parameter we should abandon
that (just like fc49e24fa69a did for segment sizes). Perhaps something
like cluste_block_size would work ... (yes, that touches all the places
too).

This patch uses a precalculated table to store the block size itself, as well as
additional derived values that have traditionally been compile-time
constants (example: MaxHeapTuplesPerPage). The traditional macro names are kept
so code that doesn't care about it should not need to change, however the
definition of these has changed (see the CalcXXX() routines in blocksize.h for
details).

A new function, BlockSizeInit() populates the appropriate values based on the
target block size. This should be called as early as possible in any code that
utilizes block sizes. This patch adds this in the appropriate place on the
handful of src/bin/ programs that used BLCKSZ, so this caveat mainly impacts new
code.

Code which had previously used BLCKZ should likely be able to get away with
changing these instances to CLUSTER_BLOCK_SIZE, unless you're using a structure
allocated on the stack. In these cases, the compiler will complain about
dynamic structure. The solution is to utilize an expression with MAX_BLOCK_SIZE
instead of BLCKSZ, ensuring enough stack space is allocated for the maximum
size. This also does require using CLUSTER_BLOCK_SIZE or an expression based on
it when actually using this structure, so in practice more stack space may be
allocated then used in principal; as long as there is plenty of stack this
should have no specific impacts on code.

Initial (basic) performance testing shows only minor changes with the pgbench -S
benchmark, though this is obviously an area that will need considerable
testing/verification across multiple workloads.

I wonder how to best evaluate/benchmark this. AFAICS what we want to
measure is the extra cost of making the values dynamic (which means the
compiler can't just optimize them out). I'd say a "pgbench -S" seems
like a good test.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#3David Christensen
david.christensen@crunchydata.com
In reply to: Tomas Vondra (#2)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 1:14 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

Do we really want to prefix the option with CLUSTER_? That seems to just
add a lot of noise into the patch, and I don't see much value in this
rename. I'd prefer keeping BLCKSZ and tweak just the couple places that
need "default" to use BLCKSZ_DEFAULT or something like that.

But more importantly, I'd say we use CAPITALIZED_NAMES for compile-time
values, so after making this initdb-time parameter we should abandon
that (just like fc49e24fa69a did for segment sizes). Perhaps something
like cluste_block_size would work ... (yes, that touches all the places
too).

Yes, I can see that being an equivalent change; thanks for the pointer
there. Definitely the "cluster_block_size" could be an approach,
though since it's just currently a #define for GetBlockSize(), maybe
we just replace with the equivalent instead. I was mainly trying to
make it something that was conceptually similar and easy to reason
about without getting bogged down in the details locally, but can see
that ALL_CAPS does have a specific meaning. Also eliminating the
BLCKSZ symbol meant it was easier to catch anything which depended on
that value. If we wanted to keep BLCKSZ, I'd be okay with that at
this point vs the CLUSTER_BLOCK_SIZE approach, could help to make the
patch smaller at this point.

Initial (basic) performance testing shows only minor changes with the pgbench -S
benchmark, though this is obviously an area that will need considerable
testing/verification across multiple workloads.

I wonder how to best evaluate/benchmark this. AFAICS what we want to
measure is the extra cost of making the values dynamic (which means the
compiler can't just optimize them out). I'd say a "pgbench -S" seems
like a good test.

Yep, I tested 100 runs apiece with pgbench -S at scale factor 100,
default settings for optimized builds of the same base commit with and
without the patch; saw a reduction of TPS around 1% in that case, but
I do think we'd want to look at different workloads; I presume the
largest impact would be seen when it's all in shared_buffers and no IO
is required.

Thanks,

David

#4Andres Freund
andres@anarazel.de
In reply to: David Christensen (#1)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 12:35:09 -0500, David Christensen wrote:

As discussed somewhat at PgCon, enclosed is v1 of a patch to provide
variable block sizes; basically instead of BLCKSZ being a compile-time
constant, a single set of binaries can support all of the block sizes
Pg can support, using the value stored in pg_control as the basis.
(Possible future plans would be to make this something even more
dynamic, such as configured per tablespace, but this is out of scope;
this just sets up the infrastructure for this.)

I am extremely doubtful this is a good idea. For one it causes a lot of churn,
but more importantly it turns currently cheap code paths into more expensive
ones.

Changes like

-#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * BLCKSZ))
+#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * CLUSTER_BLOCK_SIZE))

Note that CLUSTER_BLOCK_SIZE, despite looking like a macro that's constant, is
actually variable.

I am fairly certain this is going to be causing substantial performance
regressions. I think we should reject this even if we don't immediately find
them, because it's almost guaranteed to cause some.

Besides this, I've not really heard any convincing justification for needing
this in the first place.

Greetings,

Andres Freund

#5David Christensen
david.christensen@crunchydata.com
In reply to: Andres Freund (#4)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 2:39 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2023-06-30 12:35:09 -0500, David Christensen wrote:

As discussed somewhat at PgCon, enclosed is v1 of a patch to provide
variable block sizes; basically instead of BLCKSZ being a compile-time
constant, a single set of binaries can support all of the block sizes
Pg can support, using the value stored in pg_control as the basis.
(Possible future plans would be to make this something even more
dynamic, such as configured per tablespace, but this is out of scope;
this just sets up the infrastructure for this.)

I am extremely doubtful this is a good idea. For one it causes a lot of churn,
but more importantly it turns currently cheap code paths into more expensive
ones.

Changes like

-#define BufHdrGetBlock(bufHdr)       ((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * BLCKSZ))
+#define BufHdrGetBlock(bufHdr)       ((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * CLUSTER_BLOCK_SIZE))

Note that CLUSTER_BLOCK_SIZE, despite looking like a macro that's constant, is
actually variable.

Correct; that is mainly a notational device which would be easy enough
to change (and presumably would follow along the lines of the commit
Tomas pointed out above).

I am fairly certain this is going to be causing substantial performance
regressions. I think we should reject this even if we don't immediately find
them, because it's almost guaranteed to cause some.

What would be considered substantial? Some overhead would be expected,
but I think having an actual patch to evaluate lets us see what
potential there is. Seems like this will likely be optimized as an
offset stored in a register, so wouldn't expect huge changes here.
(There may be other approaches I haven't thought of in terms of
getting this.)

Besides this, I've not really heard any convincing justification for needing
this in the first place.

Doing this would open up experiments in larger block sizes, so we
would be able to have larger indexable tuples, say, or be able to
store data types that are larger than currently supported for tuple
row limits without dropping to toast (native vector data types come to
mind as a candidate here). We've had 8k blocks for a long time while
hardware has improved over 20+ years, and it would be interesting to
see how tuning things would open up additional avenues for performance
without requiring packagers to make a single choice on this regardless
of use-case. (The fact that we allow compiling this at a different
value suggests there is thought to be some utility having this be
something other than the default value.)

I just think it's one of those things that is hard to evaluate without
actually having something specific, which is why we have this patch
now.

Thanks,

David

#6Andres Freund
andres@anarazel.de
In reply to: David Christensen (#3)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 14:09:55 -0500, David Christensen wrote:

On Fri, Jun 30, 2023 at 1:14 PM Tomas Vondra

I wonder how to best evaluate/benchmark this. AFAICS what we want to
measure is the extra cost of making the values dynamic (which means the
compiler can't just optimize them out). I'd say a "pgbench -S" seems
like a good test.

Yep, I tested 100 runs apiece with pgbench -S at scale factor 100,
default settings for optimized builds of the same base commit with and
without the patch; saw a reduction of TPS around 1% in that case, but
I do think we'd want to look at different workloads; I presume the
largest impact would be seen when it's all in shared_buffers and no IO
is required.

I think pgbench -S indeed isn't a good workload - the overhead for it is much
more in context switches and instantiating executor state etc than code that
is affected by the change.

And indeed. Comparing e.g. TPC-H, I see *massive* regressions. Some queries
are the same, sobut others regress by up to 70% (although more commonly around
10-20%).

That's larger than I thought, which makes me suspect that there's some bug in
the new code.

Interestingly, repeating the benchmark with a larger work_mem setting, the
regressions are still quite present, but smaller. I suspect the planner
chooses smarter plans which move bottlenecks more towards hashjoin code etc,
which won't be affected by this change.

IOW, you seriously need to evaluate analytics queries before this is worth
looking at further.

Greetings,

Andres Freund

#7Andres Freund
andres@anarazel.de
In reply to: David Christensen (#5)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 15:05:54 -0500, David Christensen wrote:

I am fairly certain this is going to be causing substantial performance
regressions. I think we should reject this even if we don't immediately find
them, because it's almost guaranteed to cause some.

What would be considered substantial? Some overhead would be expected,
but I think having an actual patch to evaluate lets us see what
potential there is.

Anything beyond 1-2%, although even that imo is a hard sell.

Besides this, I've not really heard any convincing justification for needing
this in the first place.

Doing this would open up experiments in larger block sizes, so we
would be able to have larger indexable tuples, say, or be able to
store data types that are larger than currently supported for tuple
row limits without dropping to toast (native vector data types come to
mind as a candidate here).

You can do experiments today with the compile time option. Which does not
require regressing performance for everyone.

We've had 8k blocks for a long time while hardware has improved over 20+
years, and it would be interesting to see how tuning things would open up
additional avenues for performance without requiring packagers to make a
single choice on this regardless of use-case.

I suspect you're going to see more benefits from going to a *lower* setting
than a higher one. Some practical issues aside, plenty of storage hardware
these days would allow to get rid of FPIs if you go to 4k blocks (although it
often requires explicit sysadmin action to reformat the drive into that mode
etc). But obviously that's problematic from the "postgres limits" POV.

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

Greetings,

Andres Freund

#8Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: David Christensen (#5)
Re: Initdb-time block size specification

On 6/30/23 22:05, David Christensen wrote:

On Fri, Jun 30, 2023 at 2:39 PM Andres Freund <andres@anarazel.de> wrote:

...

Besides this, I've not really heard any convincing justification for needing
this in the first place.

Doing this would open up experiments in larger block sizes, so we
would be able to have larger indexable tuples, say, or be able to
store data types that are larger than currently supported for tuple
row limits without dropping to toast (native vector data types come to
mind as a candidate here). We've had 8k blocks for a long time while
hardware has improved over 20+ years, and it would be interesting to
see how tuning things would open up additional avenues for performance
without requiring packagers to make a single choice on this regardless
of use-case. (The fact that we allow compiling this at a different
value suggests there is thought to be some utility having this be
something other than the default value.)

I just think it's one of those things that is hard to evaluate without
actually having something specific, which is why we have this patch
now.

But it's possible to evaluate that - you just need to rebuild with a
different configuration option. Yes, allowing doing that at initdb is
simpler and allows testing this on systems where rebuilding is not
convenient. And having a binary that can deal with any block size would
be nice too.

In fact, I did exactly that a year ago for a conference, and I spoke
about it at the 2022 unconference too. Not sure if there's recording
from pgcon, but there is one from the other conference [1]https://www.youtube.com/watch?v=mVKpoQxtCXk[2]https://blog.pgaddict.com/pdf/block-sizes-postgresvision-2022.pdf.

The short story is that the possible gains are significant (say +50%)
for data sets that don't fit into RAM. But that was with block size set
at compile time, the question is what's the impact of making it a
variable instead of a macro ....

[1]: https://www.youtube.com/watch?v=mVKpoQxtCXk
[2]: https://blog.pgaddict.com/pdf/block-sizes-postgresvision-2022.pdf

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#9Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Andres Freund (#7)
Re: Initdb-time block size specification

On 6/30/23 23:11, Andres Freund wrote:

...

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

I don't quite understand why we shouldn't do this (or at least try to).
IMO the benefits of using smaller blocks were substantial (especially
for 4kB, most likely due matching the internal SSD page size). The other
benefits (reducing WAL volume) seem rather interesting too.

Sure, there are challenges (e.g. the overhead due to making it dynamic).
No doubt about that.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#10David Christensen
david.christensen@crunchydata.com
In reply to: Andres Freund (#6)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 3:29 PM Andres Freund <andres@anarazel.de> wrote:

And indeed. Comparing e.g. TPC-H, I see *massive* regressions. Some queries

are the same, sobut others regress by up to 70% (although more commonly around
10-20%).

Hmm, that is definitely not good.

That's larger than I thought, which makes me suspect that there's some bug in
the new code.

Will do a little profiling here to see if I can figure out the
regression. Which build optimization settings are you seeing this
under?

Interestingly, repeating the benchmark with a larger work_mem setting, the
regressions are still quite present, but smaller. I suspect the planner
chooses smarter plans which move bottlenecks more towards hashjoin code etc,
which won't be affected by this change.

Interesting.

IOW, you seriously need to evaluate analytics queries before this is worth
looking at further.

Makes sense, thanks for reviewing.

David

#11David Christensen
david.christensen@crunchydata.com
In reply to: Tomas Vondra (#8)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 4:12 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

On 6/30/23 22:05, David Christensen wrote:

On Fri, Jun 30, 2023 at 2:39 PM Andres Freund <andres@anarazel.de> wrote:

...

Besides this, I've not really heard any convincing justification for needing
this in the first place.

Doing this would open up experiments in larger block sizes, so we
would be able to have larger indexable tuples, say, or be able to
store data types that are larger than currently supported for tuple
row limits without dropping to toast (native vector data types come to
mind as a candidate here). We've had 8k blocks for a long time while
hardware has improved over 20+ years, and it would be interesting to
see how tuning things would open up additional avenues for performance
without requiring packagers to make a single choice on this regardless
of use-case. (The fact that we allow compiling this at a different
value suggests there is thought to be some utility having this be
something other than the default value.)

I just think it's one of those things that is hard to evaluate without
actually having something specific, which is why we have this patch
now.

But it's possible to evaluate that - you just need to rebuild with a
different configuration option. Yes, allowing doing that at initdb is
simpler and allows testing this on systems where rebuilding is not
convenient. And having a binary that can deal with any block size would
be nice too.

In fact, I did exactly that a year ago for a conference, and I spoke
about it at the 2022 unconference too. Not sure if there's recording
from pgcon, but there is one from the other conference [1][2].

The short story is that the possible gains are significant (say +50%)
for data sets that don't fit into RAM. But that was with block size set
at compile time, the question is what's the impact of making it a
variable instead of a macro ....

[1] https://www.youtube.com/watch?v=mVKpoQxtCXk
[2] https://blog.pgaddict.com/pdf/block-sizes-postgresvision-2022.pdf

Cool, thanks for the video links; will review.

David

#12David Christensen
david.christensen@crunchydata.com
In reply to: Tomas Vondra (#9)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 4:27 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

On 6/30/23 23:11, Andres Freund wrote:

...

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

I don't quite understand why we shouldn't do this (or at least try to).
IMO the benefits of using smaller blocks were substantial (especially
for 4kB, most likely due matching the internal SSD page size). The other
benefits (reducing WAL volume) seem rather interesting too.

If it's dynamic, we could also be able to add detection of the best
block size at initdb time, leading to improvements all around, say.
:-)

Sure, there are challenges (e.g. the overhead due to making it dynamic).
No doubt about that.

I definitely agree that it seems worth looking into. If nothing else,
being able to quantify just what/where the overhead comes from may be
instructive for future efforts.

David

#13David Christensen
david.christensen@crunchydata.com
In reply to: Andres Freund (#7)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 4:17 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2023-06-30 15:05:54 -0500, David Christensen wrote:

I am fairly certain this is going to be causing substantial performance
regressions. I think we should reject this even if we don't immediately find
them, because it's almost guaranteed to cause some.

What would be considered substantial? Some overhead would be expected,
but I think having an actual patch to evaluate lets us see what
potential there is.

Anything beyond 1-2%, although even that imo is a hard sell.

I'd agree that that threshold seems like a reasonable target, and
anything much above that would be regressive.

Besides this, I've not really heard any convincing justification for needing
this in the first place.

Doing this would open up experiments in larger block sizes, so we
would be able to have larger indexable tuples, say, or be able to
store data types that are larger than currently supported for tuple
row limits without dropping to toast (native vector data types come to
mind as a candidate here).

You can do experiments today with the compile time option. Which does not
require regressing performance for everyone.

Sure, not arguing that this is more performant than the current approach.

We've had 8k blocks for a long time while hardware has improved over 20+
years, and it would be interesting to see how tuning things would open up
additional avenues for performance without requiring packagers to make a
single choice on this regardless of use-case.

I suspect you're going to see more benefits from going to a *lower* setting
than a higher one. Some practical issues aside, plenty of storage hardware
these days would allow to get rid of FPIs if you go to 4k blocks (although it
often requires explicit sysadmin action to reformat the drive into that mode
etc). But obviously that's problematic from the "postgres limits" POV.

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

You may well be right, but I think if we haven't tried to do that and
measure it, it's hard to say exactly. There are of course more parts
of the system that are about BLCKSZ than the backend, plus you'd need
to build other extensions to support each option, so there is a lot
more that would need to change. (That's neither here nor there, as my
approach also involves changing all those places, so change isn't
inherently bad; just saying it's not a trivial solution to merely
iterate over the block size for binaries.)

David

#14Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Andres Freund (#7)
Re: Initdb-time block size specification

On 6/30/23 23:11, Andres Freund wrote:

Hi,

...

I suspect you're going to see more benefits from going to a *lower* setting
than a higher one. Some practical issues aside, plenty of storage hardware
these days would allow to get rid of FPIs if you go to 4k blocks (although it
often requires explicit sysadmin action to reformat the drive into that mode
etc). But obviously that's problematic from the "postgres limits" POV.

I wonder what are the conditions/options for disabling FPI. I kinda
assume it'd apply to new drives with 4k sectors, with properly aligned
partitions etc. But I haven't seen any particularly clear confirmation
that's correct.

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

How would that work for extensions which may be built for a particular
BLCKSZ value (like pageinspect)?

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#15Bruce Momjian
bruce@momjian.us
In reply to: Tomas Vondra (#14)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 11:42:30PM +0200, Tomas Vondra wrote:

On 6/30/23 23:11, Andres Freund wrote:

Hi,

...

I suspect you're going to see more benefits from going to a *lower* setting
than a higher one. Some practical issues aside, plenty of storage hardware
these days would allow to get rid of FPIs if you go to 4k blocks (although it
often requires explicit sysadmin action to reformat the drive into that mode
etc). But obviously that's problematic from the "postgres limits" POV.

I wonder what are the conditions/options for disabling FPI. I kinda
assume it'd apply to new drives with 4k sectors, with properly aligned
partitions etc. But I haven't seen any particularly clear confirmation
that's correct.

I don't think we have ever had to study this --- we just request the
write to the operating system, and we either get a successful reply or
we go into WAL recovery to reread the pre-image. We never really care
if the write is atomic, e.g., an 8k write can be done in 2 4kB writes 4
2kB writes --- we don't care --- we only care if they are all done or
not.

For a 4kB write, to say it is not partially written would be to require
the operating system to guarantee that the 4kB write is not split into
smaller writes which might each be atomic because smaller atomic writes
would not help us.

--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EDB https://enterprisedb.com

Only you can decide what is important to you.

#16Andres Freund
andres@anarazel.de
In reply to: Tomas Vondra (#9)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 23:27:45 +0200, Tomas Vondra wrote:

On 6/30/23 23:11, Andres Freund wrote:

...

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

I don't quite understand why we shouldn't do this (or at least try to).

IMO the benefits of using smaller blocks were substantial (especially
for 4kB, most likely due matching the internal SSD page size). The other
benefits (reducing WAL volume) seem rather interesting too.

Mostly because I think there are bigger gains to be had elsewhere.

IME not a whole lot of storage ships by default with externally visible 4k
sectors, but needs to be manually reformated [1]to see the for d in /dev/nvme*n1; do echo "$d:"; sudo nvme id-ns -H $d|grep '^LBA Format';echo ;done, which looses all data, so it
has to be done initially. Then postgres would also need OS specific trickery
to figure out that indeed the IO stack is entirely 4k (checking sector size is
not enough). And you run into the issue that suddenly the #column and
index-tuple-size limits are lower, which won't make it easier.

I think we should change the default of the WAL blocksize to 4k
though. There's practically no downsides, and it drastically reduces
postgres-side write amplification in many transactional workloads, by only
writing out partially filled 4k pages instead of partially filled 8k pages.

Sure, there are challenges (e.g. the overhead due to making it dynamic).
No doubt about that.

I don't think the runtime-dynamic overhead is avoidable with reasonable effort
(leaving aside compiling code multiple times and switching between).

If we were to start building postgres for multiple compile-time settings, I
think there are uses other than switching between BLCKSZ, potentially more
interesting. E.g. you can see substantially improved performance by being able
to use SSE4.2 without CPU dispatch (partially because it allows compiler
autovectorization, partially because it allows to compiler to use newer
non-vectorized math instructions (when targetting AVX IIRC), partially because
the dispatch overhead is not insubstantial). Another example: ARMv8
performance is substantially better if you target ARMv8.1-A instead of
ARMv8.0, due to having atomic instructions instead of LL/SC (it still baffles
me that they didn't do this earlier, ll/sc is just inherently inefficient).

Greetings,

Andres Freund

[1]: to see the for d in /dev/nvme*n1; do echo "$d:"; sudo nvme id-ns -H $d|grep '^LBA Format';echo ;done

#17Andres Freund
andres@anarazel.de
In reply to: Tomas Vondra (#14)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 23:42:30 +0200, Tomas Vondra wrote:

I wonder what are the conditions/options for disabling FPI. I kinda
assume it'd apply to new drives with 4k sectors, with properly aligned
partitions etc. But I haven't seen any particularly clear confirmation
that's correct.

Yea, it's not trivial. And the downsides are also substantial from a
replication / crash recovery performance POV - even with reading blocks ahead
of WAL replay, it's hard to beat just sequentially reading nearly all the data
you're going to need.

On 6/30/23 23:11, Andres Freund wrote:

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

How would that work for extensions which may be built for a particular
BLCKSZ value (like pageinspect)?

I think we'd need to do something similar for extensions, likely loading them
from a path that includes the "subvariant" the server currently is running. Or
alternatively adding a suffix to the filename indicating the
variant. Something like pageinspect.x86-64-v4-4kB.so.

The x86-64-v* stuff is something gcc and clang added a couple years ago, so
that not every project has to define different "baseline" levels. I think it
was done in collaboration with the sytem-v/linux AMD64 ABI specification group
([1]https://gitlab.com/x86-psABIs/x86-64-ABI/-/jobs/artifacts/master/raw/x86-64-ABI/abi.pdf?job=build section 3.1.1.).

Greetings,

Andres

[1]: https://gitlab.com/x86-psABIs/x86-64-ABI/-/jobs/artifacts/master/raw/x86-64-ABI/abi.pdf?job=build section 3.1.1.
section 3.1.1.

#18Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Bruce Momjian (#15)
Re: Initdb-time block size specification

On 6/30/23 23:53, Bruce Momjian wrote:

On Fri, Jun 30, 2023 at 11:42:30PM +0200, Tomas Vondra wrote:

On 6/30/23 23:11, Andres Freund wrote:

Hi,

...

I suspect you're going to see more benefits from going to a *lower* setting
than a higher one. Some practical issues aside, plenty of storage hardware
these days would allow to get rid of FPIs if you go to 4k blocks (although it
often requires explicit sysadmin action to reformat the drive into that mode
etc). But obviously that's problematic from the "postgres limits" POV.

I wonder what are the conditions/options for disabling FPI. I kinda
assume it'd apply to new drives with 4k sectors, with properly aligned
partitions etc. But I haven't seen any particularly clear confirmation
that's correct.

I don't think we have ever had to study this --- we just request the
write to the operating system, and we either get a successful reply or
we go into WAL recovery to reread the pre-image. We never really care
if the write is atomic, e.g., an 8k write can be done in 2 4kB writes 4
2kB writes --- we don't care --- we only care if they are all done or
not.

For a 4kB write, to say it is not partially written would be to require
the operating system to guarantee that the 4kB write is not split into
smaller writes which might each be atomic because smaller atomic writes
would not help us.

Right, that's the dance we do to protect against torn pages. But Andres
suggested that if you have modern storage and configure it correctly,
writing with 4kB pages would be atomic. So we wouldn't need to do this
FPI stuff, eliminating pretty significant source of write amplification.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#19Bruce Momjian
bruce@momjian.us
In reply to: Tomas Vondra (#18)
Re: Initdb-time block size specification

On Sat, Jul 1, 2023 at 12:21:03AM +0200, Tomas Vondra wrote:

On 6/30/23 23:53, Bruce Momjian wrote:

For a 4kB write, to say it is not partially written would be to require
the operating system to guarantee that the 4kB write is not split into
smaller writes which might each be atomic because smaller atomic writes
would not help us.

Right, that's the dance we do to protect against torn pages. But Andres
suggested that if you have modern storage and configure it correctly,
writing with 4kB pages would be atomic. So we wouldn't need to do this
FPI stuff, eliminating pretty significant source of write amplification.

I agree the hardware is atomic for 4k writes, but do we know the OS
always issues 4k writes?

--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EDB https://enterprisedb.com

Only you can decide what is important to you.

#20Andres Freund
andres@anarazel.de
In reply to: Bruce Momjian (#15)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 17:53:34 -0400, Bruce Momjian wrote:

On Fri, Jun 30, 2023 at 11:42:30PM +0200, Tomas Vondra wrote:

On 6/30/23 23:11, Andres Freund wrote:

I suspect you're going to see more benefits from going to a *lower* setting
than a higher one. Some practical issues aside, plenty of storage hardware
these days would allow to get rid of FPIs if you go to 4k blocks (although it
often requires explicit sysadmin action to reformat the drive into that mode
etc). But obviously that's problematic from the "postgres limits" POV.

I wonder what are the conditions/options for disabling FPI. I kinda
assume it'd apply to new drives with 4k sectors, with properly aligned
partitions etc. But I haven't seen any particularly clear confirmation
that's correct.

I don't think we have ever had to study this --- we just request the
write to the operating system, and we either get a successful reply or
we go into WAL recovery to reread the pre-image. We never really care
if the write is atomic, e.g., an 8k write can be done in 2 4kB writes 4
2kB writes --- we don't care --- we only care if they are all done or
not.

Well, that works because we have FPI. This sub-discussion is motivated by
getting rid of FPIs.

For a 4kB write, to say it is not partially written would be to require
the operating system to guarantee that the 4kB write is not split into
smaller writes which might each be atomic because smaller atomic writes
would not help us.

That's why were talking about drives with 4k sector size - you *can't* split
the writes below that.

The problem is that, as far as I know,it's not always obvious what block size
is being used on the actual storage level. It's not even trivial when
operating on a filesystem directly stored on a single block device ([1]On linux I think you need to use stat() to figure out the st_dev for a file, then look in /proc/self/mountinfo for the block device, use the name of the file to look in /sys/block/$d/queue/physical_block_size.). Once
there's things like LVM or disk encryption involved, it gets pretty hairy
([2]The above doesn't work because e.g. a device mapper target might only support 4k sectors, even though the sectors on the underlying storage device are 512b sectors. E.g. my root filesystem is encrypted, and if you follow the above recipe (with the added step of resolving the symlink to know the actual device name), you would see a 4k sector size. Even though the underlying NVMe disk only supports 512b sectors.). Once you know all the block devices, it's not too bad, but ...

Greetings,

Andres Freund

[1]: On linux I think you need to use stat() to figure out the st_dev for a file, then look in /proc/self/mountinfo for the block device, use the name of the file to look in /sys/block/$d/queue/physical_block_size.
file, then look in /proc/self/mountinfo for the block device, use the name
of the file to look in /sys/block/$d/queue/physical_block_size.

[2]: The above doesn't work because e.g. a device mapper target might only support 4k sectors, even though the sectors on the underlying storage device are 512b sectors. E.g. my root filesystem is encrypted, and if you follow the above recipe (with the added step of resolving the symlink to know the actual device name), you would see a 4k sector size. Even though the underlying NVMe disk only supports 512b sectors.
support 4k sectors, even though the sectors on the underlying storage device
are 512b sectors. E.g. my root filesystem is encrypted, and if you follow the
above recipe (with the added step of resolving the symlink to know the actual
device name), you would see a 4k sector size. Even though the underlying NVMe
disk only supports 512b sectors.

#21Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Andres Freund (#16)
Re: Initdb-time block size specification

On 7/1/23 00:05, Andres Freund wrote:

Hi,

On 2023-06-30 23:27:45 +0200, Tomas Vondra wrote:

On 6/30/23 23:11, Andres Freund wrote:

...

If we really wanted to do this - but I don't think we do - I'd argue for
working on the buildsystem support to build the postgres binary multiple
times, for 4, 8, 16 kB BLCKSZ and having a wrapper postgres binary that just
exec's the relevant "real" binary based on the pg_control value. I really
don't see us ever wanting to make BLCKSZ runtime configurable within one
postgres binary. There's just too much intrinsic overhead associated with
that.

I don't quite understand why we shouldn't do this (or at least try to).

IMO the benefits of using smaller blocks were substantial (especially
for 4kB, most likely due matching the internal SSD page size). The other
benefits (reducing WAL volume) seem rather interesting too.

Mostly because I think there are bigger gains to be had elsewhere.

I think that decision is up to whoever chooses to work on it, especially
if performance is not their primary motivation (IIRC this was discussed
as part of the TDE session).

IME not a whole lot of storage ships by default with externally visible 4k
sectors, but needs to be manually reformated [1], which looses all data, so it
has to be done initially.

I don't see why "you have to configure stuff" would be a reason against
improvements in this area. I don't know how prevalent storage with 4k
sectors is now, but AFAIK it's not hard to get and it's likely to get
yet more common in the future.

FWIW I don't think the benefits of different (lower) page sizes hinge on
4k sectors - it's just that not having to do FPIs would make it even
more interesting.

Then postgres would also need OS specific trickery
to figure out that indeed the IO stack is entirely 4k (checking sector size is
not enough).

I haven't suggested we should be doing that automatically (would be
nice, but I'd be happy with knowing when it's safe to disable FPW using
the GUC in config). But knowing when it's safe would make it yet more
interesting be able to use a different block page size at initdb.

And you run into the issue that suddenly the #column and
index-tuple-size limits are lower, which won't make it easier.

True. This limit is annoying, but no one is proposing to change the
default page size. initdb would just provide a more convenient way to do
that, but the user would have to check. (I rather doubt many people
actually index such large values).

I think we should change the default of the WAL blocksize to 4k
though. There's practically no downsides, and it drastically reduces
postgres-side write amplification in many transactional workloads, by only
writing out partially filled 4k pages instead of partially filled 8k pages.

+1 (although in my tests the benefits we much smaller than for BLCKSZ)

Sure, there are challenges (e.g. the overhead due to making it dynamic).
No doubt about that.

I don't think the runtime-dynamic overhead is avoidable with reasonable effort
(leaving aside compiling code multiple times and switching between).

If we were to start building postgres for multiple compile-time settings, I
think there are uses other than switching between BLCKSZ, potentially more
interesting. E.g. you can see substantially improved performance by being able
to use SSE4.2 without CPU dispatch (partially because it allows compiler
autovectorization, partially because it allows to compiler to use newer
non-vectorized math instructions (when targetting AVX IIRC), partially because
the dispatch overhead is not insubstantial). Another example: ARMv8
performance is substantially better if you target ARMv8.1-A instead of
ARMv8.0, due to having atomic instructions instead of LL/SC (it still baffles
me that they didn't do this earlier, ll/sc is just inherently inefficient).

Maybe, although I think it depends on what parts of the code this would
affect. If it's sufficiently small/isolated, it'd be possible to have
multiple paths, specialized to a particular page size (pretty common
technique for GPU/SIMD, I believe).

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#22Bruce Momjian
bruce@momjian.us
In reply to: Andres Freund (#20)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 03:51:18PM -0700, Andres Freund wrote:

For a 4kB write, to say it is not partially written would be to require
the operating system to guarantee that the 4kB write is not split into
smaller writes which might each be atomic because smaller atomic writes
would not help us.

That's why were talking about drives with 4k sector size - you *can't* split
the writes below that.

Okay, good point.

The problem is that, as far as I know,it's not always obvious what block size
is being used on the actual storage level. It's not even trivial when
operating on a filesystem directly stored on a single block device ([1]). Once
there's things like LVM or disk encryption involved, it gets pretty hairy
([2]). Once you know all the block devices, it's not too bad, but ...

Greetings,

Andres Freund

[1] On linux I think you need to use stat() to figure out the st_dev for a
file, then look in /proc/self/mountinfo for the block device, use the name
of the file to look in /sys/block/$d/queue/physical_block_size.

I just got a new server:

https://momjian.us/main/blogs/blog/2023.html#June_28_2023

so tested this on my new M.2 NVME storage device:

$ /sys/block/nvme0n1/queue/physical_block_size
262144

that's 256k, not 4k.

[2] The above doesn't work because e.g. a device mapper target might only
support 4k sectors, even though the sectors on the underlying storage device
are 512b sectors. E.g. my root filesystem is encrypted, and if you follow the
above recipe (with the added step of resolving the symlink to know the actual
device name), you would see a 4k sector size. Even though the underlying NVMe
disk only supports 512b sectors.

Good point.

--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EDB https://enterprisedb.com

Only you can decide what is important to you.

#23Andres Freund
andres@anarazel.de
In reply to: Bruce Momjian (#19)
Re: Initdb-time block size specification

On 2023-06-30 18:37:39 -0400, Bruce Momjian wrote:

On Sat, Jul 1, 2023 at 12:21:03AM +0200, Tomas Vondra wrote:

On 6/30/23 23:53, Bruce Momjian wrote:

For a 4kB write, to say it is not partially written would be to require
the operating system to guarantee that the 4kB write is not split into
smaller writes which might each be atomic because smaller atomic writes
would not help us.

Right, that's the dance we do to protect against torn pages. But Andres
suggested that if you have modern storage and configure it correctly,
writing with 4kB pages would be atomic. So we wouldn't need to do this
FPI stuff, eliminating pretty significant source of write amplification.

I agree the hardware is atomic for 4k writes, but do we know the OS
always issues 4k writes?

When using a sector size of 4K you *can't* make smaller writes via normal
paths. The addressing unit is in sectors. The details obviously differ between
storage protocol, but you pretty much always just specify a start sector and a
number of sectors to be operated on.

Obviously the kernel could read 4k, modify 512 bytes in-memory, and then write
4k back, but that shouldn't be a danger here. There might also be debug
interfaces to allow reading/writing in different increments, but that'd not be
something happening during normal operation.

#24Bruce Momjian
bruce@momjian.us
In reply to: Bruce Momjian (#22)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 06:58:20PM -0400, Bruce Momjian wrote:

I just got a new server:

https://momjian.us/main/blogs/blog/2023.html#June_28_2023

so tested this on my new M.2 NVME storage device:

$ /sys/block/nvme0n1/queue/physical_block_size
262144

that's 256k, not 4k.

I have another approach to this. My storage device has power
protection, so even though it has a 256k physical block size, it should
be fine with 4k write atomicity.

--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EDB https://enterprisedb.com

Only you can decide what is important to you.

#25Andres Freund
andres@anarazel.de
In reply to: Bruce Momjian (#22)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 18:58:20 -0400, Bruce Momjian wrote:

[1] On linux I think you need to use stat() to figure out the st_dev for a
file, then look in /proc/self/mountinfo for the block device, use the name
of the file to look in /sys/block/$d/queue/physical_block_size.

I just got a new server:

https://momjian.us/main/blogs/blog/2023.html#June_28_2023

so tested this on my new M.2 NVME storage device:

$ /sys/block/nvme0n1/queue/physical_block_size
262144

Ah, I got the relevant filename wrong. I think it's logical_block_size, not
physical one (that's the size of addressing). I didn't realize because the
devices I looked at have the same...

Regards,

Andres

#26Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Andres Freund (#23)
Re: Initdb-time block size specification

On 7/1/23 00:59, Andres Freund wrote:

On 2023-06-30 18:37:39 -0400, Bruce Momjian wrote:

On Sat, Jul 1, 2023 at 12:21:03AM +0200, Tomas Vondra wrote:

On 6/30/23 23:53, Bruce Momjian wrote:

For a 4kB write, to say it is not partially written would be to require
the operating system to guarantee that the 4kB write is not split into
smaller writes which might each be atomic because smaller atomic writes
would not help us.

Right, that's the dance we do to protect against torn pages. But Andres
suggested that if you have modern storage and configure it correctly,
writing with 4kB pages would be atomic. So we wouldn't need to do this
FPI stuff, eliminating pretty significant source of write amplification.

I agree the hardware is atomic for 4k writes, but do we know the OS
always issues 4k writes?

When using a sector size of 4K you *can't* make smaller writes via normal
paths. The addressing unit is in sectors. The details obviously differ between
storage protocol, but you pretty much always just specify a start sector and a
number of sectors to be operated on.

Obviously the kernel could read 4k, modify 512 bytes in-memory, and then write
4k back, but that shouldn't be a danger here. There might also be debug
interfaces to allow reading/writing in different increments, but that'd not be
something happening during normal operation.

I think it's important to point out that there's a physical and logical
sector size. The "physical" is what the drive does internally, "logical"
defines what OS does.

Some drives have 4k physical sectors but only 512B logical sectors.
AFAIK most "old" SATA SSDs do it that way, for compatibility reasons.

New drives may have 4k physical sectors but typically support both 512B
and 4k logical sectors - my nvme SSDs do this, for example.

My understanding is that for drives with 4k physical+logical sectors,
the OS would only issue "full" 4k writes.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#27Bruce Momjian
bruce@momjian.us
In reply to: Andres Freund (#25)
Re: Initdb-time block size specification

On Fri, Jun 30, 2023 at 04:04:57PM -0700, Andres Freund wrote:

Hi,

On 2023-06-30 18:58:20 -0400, Bruce Momjian wrote:

[1] On linux I think you need to use stat() to figure out the st_dev for a
file, then look in /proc/self/mountinfo for the block device, use the name
of the file to look in /sys/block/$d/queue/physical_block_size.

I just got a new server:

https://momjian.us/main/blogs/blog/2023.html#June_28_2023

so tested this on my new M.2 NVME storage device:

$ /sys/block/nvme0n1/queue/physical_block_size
262144

Ah, I got the relevant filename wrong. I think it's logical_block_size, not
physical one (that's the size of addressing). I didn't realize because the
devices I looked at have the same...

That one reports 512 _bytes_ for me:

$ cat /sys/block/nvme0n1/queue/logical_block_size
512

--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EDB https://enterprisedb.com

Only you can decide what is important to you.

#28Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Bruce Momjian (#27)
Re: Initdb-time block size specification

On 7/1/23 01:16, Bruce Momjian wrote:

On Fri, Jun 30, 2023 at 04:04:57PM -0700, Andres Freund wrote:

Hi,

On 2023-06-30 18:58:20 -0400, Bruce Momjian wrote:

[1] On linux I think you need to use stat() to figure out the st_dev for a
file, then look in /proc/self/mountinfo for the block device, use the name
of the file to look in /sys/block/$d/queue/physical_block_size.

I just got a new server:

https://momjian.us/main/blogs/blog/2023.html#June_28_2023

so tested this on my new M.2 NVME storage device:

$ /sys/block/nvme0n1/queue/physical_block_size
262144

Ah, I got the relevant filename wrong. I think it's logical_block_size, not
physical one (that's the size of addressing). I didn't realize because the
devices I looked at have the same...

That one reports 512 _bytes_ for me:

$ cat /sys/block/nvme0n1/queue/logical_block_size
512

What does "smartctl -a /dev/nvme0n1" say? There should be something like
this:

Supported LBA Sizes (NSID 0x1)
Id Fmt Data Metadt Rel_Perf
0 - 4096 0 0
1 + 512 0 0

which says the drive supports 4k and 512B sectors, and is currently
configures to use 512B sectors.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#29Bruce Momjian
bruce@momjian.us
In reply to: Tomas Vondra (#28)
Re: Initdb-time block size specification

On Sat, Jul 1, 2023 at 01:18:34AM +0200, Tomas Vondra wrote:

What does "smartctl -a /dev/nvme0n1" say? There should be something like
this:

Supported LBA Sizes (NSID 0x1)
Id Fmt Data Metadt Rel_Perf
0 - 4096 0 0
1 + 512 0 0

which says the drive supports 4k and 512B sectors, and is currently
configures to use 512B sectors.

It says:

Supported LBA Sizes (NSID 0x1)
Id Fmt Data Metadt Rel_Perf
0 + 512 0 2
1 - 4096 0 0

--
Bruce Momjian <bruce@momjian.us> https://momjian.us
EDB https://enterprisedb.com

Only you can decide what is important to you.

#30Andres Freund
andres@anarazel.de
In reply to: David Christensen (#10)
Re: Initdb-time block size specification

Hi,

On 2023-06-30 16:28:59 -0500, David Christensen wrote:

On Fri, Jun 30, 2023 at 3:29 PM Andres Freund <andres@anarazel.de> wrote:

And indeed. Comparing e.g. TPC-H, I see *massive* regressions. Some queries

are the same, sobut others regress by up to 70% (although more commonly around
10-20%).

Hmm, that is definitely not good.

That's larger than I thought, which makes me suspect that there's some bug in
the new code.

Will do a little profiling here to see if I can figure out the
regression. Which build optimization settings are you seeing this
under?

gcc 12 with:

meson setup \
-Doptimization=3 -Ddebug=true \
-Dc_args="-ggdb -g3 -march=native -mtune=native -fno-plt -fno-semantic-interposition -Wno-array-bounds" \
-Dc_link_args="-fuse-ld=mold -Wl,--gdb-index,--Bsymbolic" \
...

Relevant postgres settings:
-c huge_pages=on -c shared_buffers=12GB -c max_connections=120
-c work_mem=32MB
-c autovacuum=0 # I always do that for comparative benchmarks, too much variance
-c track_io_timing=on

The later run where I saw the smaller regression was with work_mem=1GB. I
just had forgotten to adjust that.

I had loaded tpch scale 5 before, which is why I just used that.

FWIW, even just "SELECT count(*) FROM lineitem;" shows a substantial
regression.

I disabled parallelism, prewarmed the data and pinned postgres to a single
core to reduce noise. The result is the best of three (variance was low in all
cases).

HEAD patch
index only scan 1896.364 2242.288
seq scan 1586.990 1628.042

A profile shows that 20% of the runtime in the IOS case is in
visibilitymap_get_status():

+   20.50%  postgres.new  postgres.new          [.] visibilitymap_get_status
+   19.54%  postgres.new  postgres.new          [.] ExecInterpExpr
+   14.47%  postgres.new  postgres.new          [.] IndexOnlyNext
+    6.47%  postgres.new  postgres.new          [.] index_deform_tuple_internal
+    4.67%  postgres.new  postgres.new          [.] ExecScan
+    4.12%  postgres.new  postgres.new          [.] btgettuple
+    3.97%  postgres.new  postgres.new          [.] ExecAgg
+    3.92%  postgres.new  postgres.new          [.] _bt_next
+    3.71%  postgres.new  postgres.new          [.] _bt_readpage
+    3.43%  postgres.new  postgres.new          [.] fetch_input_tuple
+    2.87%  postgres.new  postgres.new          [.] index_getnext_tid
+    2.45%  postgres.new  postgres.new          [.] MemoryContextReset
+    2.35%  postgres.new  postgres.new          [.] tts_virtual_clear
+    1.37%  postgres.new  postgres.new          [.] index_deform_tuple
+    1.14%  postgres.new  postgres.new          [.] ExecStoreVirtualTuple
+    1.13%  postgres.new  postgres.new          [.] PredicateLockPage
+    1.12%  postgres.new  postgres.new          [.] int8inc
+    1.04%  postgres.new  postgres.new          [.] ExecIndexOnlyScan
+    0.57%  postgres.new  postgres.new          [.] BufferGetBlockNumber

mostly due to

│ BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
2.46 │ lea -0x60(,%rdx,4),%rcx
│ xor %edx,%edx
59.79 │ div %rcx

You can't have have divisions for this kind of thing in the vicinity of a
peformance critical path. With compile time constants the compiler can turn
this into shifts, but that's not possible as-is after the change.

While not quite as bad as divisions, the paths with multiplications are also
not going to be ok. E.g.
return (Block) (BufferBlocks + ((Size) (buffer - 1)) * CLUSTER_BLOCK_SIZE);
is going to be noticeable.

You'd have to turn all of this into shifts (and enforce power of 2 sizes, if
you aren't yet).

I don't think pre-computed tables are a viable answer FWIW. Even just going
through a memory indirection is going to be noticable. This stuff is in a
crapton of hot code paths.

Greetings,

Andres Freund

#31Peter Eisentraut
peter@eisentraut.org
In reply to: Tomas Vondra (#18)
Re: Initdb-time block size specification

On 01.07.23 00:21, Tomas Vondra wrote:

Right, that's the dance we do to protect against torn pages. But Andres
suggested that if you have modern storage and configure it correctly,
writing with 4kB pages would be atomic. So we wouldn't need to do this
FPI stuff, eliminating pretty significant source of write amplification.

This work in progress for the Linux kernel was also mentioned at PGCon:
<https://lwn.net/Articles/933015/&gt;. Subject the various conditions, the
kernel would then guarantee atomic writes for blocks larger than the
hardware's native size.

#32David Christensen
david.christensen@crunchydata.com
In reply to: David Christensen (#1)
8 attachment(s)
Re: Initdb-time block size specification

Hi, enclosed is v2 of the variable blocksize patch. This series is
based atop 9b581c5341.

Preparation phase:

0001 - add utility script for retokenizing all necessary scripts.
This is mainly for my own use in generating 0003, which is a simple
rename/preparation patch to change all symbols from their UPPER_CASE
to lower_case form, with several exceptions in renames.
0002 - add script to harness 0001 and apply to the relevant files in the repo
0003 - capture the effects of 0002 on the repo

The other patches in this series are as follows:

0004 - the "main" variable blocksize patch where the bulk of the code
changes take place - see comments here
0005 - utility functions for fast div/mod operations; basically
montgomery multiplication
0006 - use fastdiv code in the visiblity map, the main place where
this change is required
0007 - (optional) add/use libdivide for division which is license
compatible with other headers we bundle
0008 - (optional) tweaks to libdivide to make compiler/CI happy

I have also replaced multiple instances of division or multiplication
of BLOCKSZ with bitshift operations based on the number of bits in the
underlying blocksize.

The current approach for this is to replace any affected constant with
an inline switch statement based on an enum for the blocksize and the
compile-time calculation for that version. In practice with -O2 this
generates a simple lookup table inline in the assembly with the costs
for calculating paid at compile time.

The visibility map was the main hot path which was affected by the
switch from compile-time sizes with the previous version of this
patch. With the switch to a modified approach in 0005/0006 this issue
has been rectified in our testing.

I have tested a few workloads with this modified patch and have seen
positive results compared to v1. I look forward to additional
review/testing/feedback.

Thanks,

David

Attachments:

v2-0001-Add-tool-to-retokenize-for-variable-blocksize.patchapplication/octet-stream; name=v2-0001-Add-tool-to-retokenize-for-variable-blocksize.patchDownload
From a7aa81f2e83bd1730d6ded6d1b321e0d15f81bdd Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Wed, 16 Aug 2023 14:58:11 -0400
Subject: [PATCH v2 1/3] Add tool to retokenize for variable blocksize

---
 src/tools/replace_blcksz | 47 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 src/tools/replace_blcksz

diff --git a/src/tools/replace_blcksz b/src/tools/replace_blcksz
new file mode 100644
index 0000000000..0d9ab75b6d
--- /dev/null
+++ b/src/tools/replace_blcksz
@@ -0,0 +1,47 @@
+#!/usr/bin/env -S perl -i
+# -*-cperl-*-
+
+use strict;
+use warnings;
+
+# Construct our replacement list; if a replacement is specified, then we use
+# that, otherwise we just assume lower-cased version of the given identifier.
+
+my %replacements = map { my ($tok,$repl) = split; $tok, $repl || lc $tok } <DATA>;
+
+# Construct a regex that matches and captures any key
+my $matchre = qr/\b(@{[ join '|' => keys %replacements ]})\b/;
+while (<>) {
+    # Replace all found keys with the corresponding lookup value
+    s/$matchre/$replacements{$1}/eg;
+    print
+}
+
+__DATA__
+BLCKSZ  cluster_block_size
+CLUSTER_BLOCK_BITS
+CLUSTER_BLOCK_SIZE
+CLUSTER_RELSEG_SIZE
+COMMIT_TS_XACTS_PER_PAGE
+FAILSAFE_EVERY_PAGES
+FREE_BLOCK_NUMBER_ELEMS
+FSM_CAT_STEP
+GIN_PAGE_FREESIZE
+HASHAGG_READ_BUFFER_SIZE
+HASHAGG_WRITE_BUFFER_SIZE
+HEAP_USABLE_BYTES_PER_PAGE
+LOBLKSIZE	cluster_loblksize
+MULTIXACT_MEMBERGROUPS_PER_PAGE
+MULTIXACT_MEMBERS_PER_PAGE
+MULTIXACT_OFFSETS_PER_PAGE
+REVMAP_CONTENT_SIZE
+SERIAL_PAGESIZE
+SINK_BUFFER_LENGTH
+SPGIST_PAGE_CAPACITY
+SUBTRANS_XACTS_PER_PAGE
+TOAST_MAX_CHUNK_SIZE	cluster_toast_max_chunk_size
+TOAST_TUPLE_TARGET	cluster_toast_tuple_target
+TOAST_TUPLE_TARGET_MAIN
+TOAST_TUPLE_THRESHOLD
+VACUUM_FSM_EVERY_PAGES
+XLOGPREFETCHER_STATS_DISTANCE
-- 
2.39.2

v2-0002-Add-wrapper-for-tokenizing-the-whole-repo.patchapplication/octet-stream; name=v2-0002-Add-wrapper-for-tokenizing-the-whole-repo.patchDownload
From 42245202d6af6fd9a3a1a018c48d1ea4ffd56dbc Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Wed, 16 Aug 2023 15:04:02 -0400
Subject: [PATCH v2 2/3] Add wrapper for tokenizing the whole repo

---
 src/tools/replace_blcksz_repo | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100755 src/tools/replace_blcksz_repo

diff --git a/src/tools/replace_blcksz_repo b/src/tools/replace_blcksz_repo
new file mode 100755
index 0000000000..7e122eed2c
--- /dev/null
+++ b/src/tools/replace_blcksz_repo
@@ -0,0 +1,13 @@
+#!/bin/bash -e
+
+# Wrapper for refreshing all of the files in the repo This is separate from the
+# replace_blcksz script itself since we may want to individually use that on
+# patch files or other non-repo externalities. This is the interface for batch
+# updating and should wrap the logic for files which should be excluded, etc.
+
+cd $(git rev-parse --show-toplevel)
+git checkout .
+
+perl -i src/tools/replace_blcksz $(git ls-files | egrep -v 'configure|meson|replace_blcksz|pg_config.h.in')
+perl -i -lpe 'print "#define cluster_block_size BLCKSZ" if /endif.*POSTGRES_EXT_H/' src/include/postgres_ext.h
+
-- 
2.39.2

v2-0005-Add-support-for-fast-non-division-based-div-mod-a.patchapplication/octet-stream; name=v2-0005-Add-support-for-fast-non-division-based-div-mod-a.patchDownload
From b291da9fc9889ba3d114658817de30e20d06ba68 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Tue, 18 Jul 2023 17:57:32 -0400
Subject: [PATCH v2 2/5] Add support for fast, non-division-based div/mod
 algorithms

---
 src/include/port/pg_bitutils.h | 41 ++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 4f1a13dba9..84a5fdead9 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -340,4 +340,45 @@ pg_rotate_left32(uint32 word, int n)
 #define pg_prevpower2_size_t pg_prevpower2_64
 #endif
 
+
+/* integer division speedups for constant but runtime divisors */
+
+/*
+ * This value should cached globally and used in the other routines to find
+ * the div/mod quickly relative to `div` operand.  TODO: might have some other
+ * asm-tuned things in port maybe?  general-purpose solution should be ok
+ * though.
+ */
+static inline uint64 pg_fastinverse(uint32 divisor)
+{
+	return UINT64_C(0xFFFFFFFFFFFFFFFF) / divisor + 1;
+}
+
+/*
+ * pg_fastdiv - calculates the quotient of a 32-bit number against a constant
+ * divisor without using the division operator
+ */
+static inline uint32 pg_fastdiv(uint32 n, uint32 divisor, uint64 fastinv)
+{
+#ifdef HAVE_INT128
+	return (((uint128)(fastinv - 1) * n)) >> 64;
+#else
+	return n / divisor;
+#endif
+}
+
+/*
+ * pg_fastmod - calculates the modulus of a 32-bit number against a constant
+ * divisor without using the division operator
+ */
+static inline uint32 pg_fastmod(uint32 n, uint32 divisor, uint64 fastinv)
+{
+#ifdef HAVE_INT128
+	uint64_t lowbits = fastinv * n;
+	return ((uint128)lowbits * divisor) >> 64;
+#else
+	return n % divisor;
+#endif
+}
+
 #endif							/* PG_BITUTILS_H */
-- 
2.39.2

v2-0003-Capture-rename-of-symbols.patchapplication/octet-stream; name=v2-0003-Capture-rename-of-symbols.patchDownload
From ba55020bd3e68e1d7a3c786b3f5aa692750ee38f Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Tue, 29 Aug 2023 16:09:37 -0400
Subject: [PATCH v2 3/3] Capture rename of symbols

---
 contrib/amcheck/expected/check_btree.out    |  2 +-
 contrib/amcheck/sql/check_btree.sql         |  2 +-
 contrib/amcheck/verify_heapam.c             | 12 ++--
 contrib/amcheck/verify_nbtree.c             |  6 +-
 contrib/bloom/blinsert.c                    |  2 +-
 contrib/bloom/bloom.h                       |  4 +-
 contrib/bloom/blutils.c                     |  2 +-
 contrib/file_fdw/file_fdw.c                 |  6 +-
 contrib/pageinspect/btreefuncs.c            |  6 +-
 contrib/pageinspect/expected/checksum.out   |  2 +-
 contrib/pageinspect/expected/checksum_1.out |  2 +-
 contrib/pageinspect/rawpage.c               | 10 ++--
 contrib/pageinspect/sql/checksum.sql        |  2 +-
 contrib/pg_walinspect/pg_walinspect.c       |  6 +-
 contrib/pgstattuple/pgstatapprox.c          |  6 +-
 contrib/pgstattuple/pgstatindex.c           |  4 +-
 contrib/pgstattuple/pgstattuple.c           | 10 ++--
 contrib/postgres_fdw/deparse.c              |  2 +-
 contrib/postgres_fdw/postgres_fdw.c         |  2 +-
 doc/src/sgml/catalogs.sgml                  | 12 ++--
 doc/src/sgml/config.sgml                    | 32 +++++------
 doc/src/sgml/limits.sgml                    |  2 +-
 doc/src/sgml/monitoring.sgml                |  2 +-
 doc/src/sgml/pgfreespacemap.sgml            |  2 +-
 doc/src/sgml/storage.sgml                   |  8 +--
 src/backend/access/brin/brin_bloom.c        |  2 +-
 src/backend/access/brin/brin_pageops.c      |  4 +-
 src/backend/access/common/bufmask.c         |  4 +-
 src/backend/access/common/reloptions.c      |  2 +-
 src/backend/access/common/syncscan.c        |  2 +-
 src/backend/access/common/toast_internals.c |  4 +-
 src/backend/access/gin/ginbtree.c           | 12 ++--
 src/backend/access/gin/gindatapage.c        | 18 +++---
 src/backend/access/gin/ginfast.c            |  8 +--
 src/backend/access/gin/ginget.c             |  6 +-
 src/backend/access/gin/ginvacuum.c          |  2 +-
 src/backend/access/gin/ginxlog.c            |  4 +-
 src/backend/access/gist/gistbuild.c         | 16 +++---
 src/backend/access/gist/gistbuildbuffers.c  | 10 ++--
 src/backend/access/gist/gistutil.c          |  2 +-
 src/backend/access/hash/hash.c              |  2 +-
 src/backend/access/hash/hashpage.c          |  2 +-
 src/backend/access/heap/README.HOT          |  2 +-
 src/backend/access/heap/heapam.c            | 20 +++----
 src/backend/access/heap/heapam_handler.c    | 10 ++--
 src/backend/access/heap/heaptoast.c         | 20 +++----
 src/backend/access/heap/pruneheap.c         |  2 +-
 src/backend/access/heap/rewriteheap.c       |  6 +-
 src/backend/access/heap/vacuumlazy.c        | 18 +++---
 src/backend/access/heap/visibilitymap.c     |  4 +-
 src/backend/access/nbtree/nbtdedup.c        |  8 +--
 src/backend/access/nbtree/nbtpage.c         | 14 ++---
 src/backend/access/nbtree/nbtree.c          |  4 +-
 src/backend/access/nbtree/nbtsort.c         | 12 ++--
 src/backend/access/rmgrdesc/xlogdesc.c      |  2 +-
 src/backend/access/spgist/spgdoinsert.c     | 24 ++++----
 src/backend/access/spgist/spgtextproc.c     | 10 ++--
 src/backend/access/spgist/spgutils.c        | 12 ++--
 src/backend/access/table/tableam.c          |  2 +-
 src/backend/access/transam/README           |  2 +-
 src/backend/access/transam/clog.c           |  6 +-
 src/backend/access/transam/commit_ts.c      | 24 ++++----
 src/backend/access/transam/generic_xlog.c   | 18 +++---
 src/backend/access/transam/multixact.c      | 54 +++++++++---------
 src/backend/access/transam/slru.c           | 22 ++++----
 src/backend/access/transam/subtrans.c       | 20 +++----
 src/backend/access/transam/timeline.c       |  2 +-
 src/backend/access/transam/varsup.c         |  2 +-
 src/backend/access/transam/xlog.c           | 30 +++++-----
 src/backend/access/transam/xloginsert.c     | 24 ++++----
 src/backend/access/transam/xlogprefetcher.c |  4 +-
 src/backend/access/transam/xlogreader.c     | 28 +++++-----
 src/backend/access/transam/xlogrecovery.c   | 10 ++--
 src/backend/backup/basebackup.c             | 40 ++++++-------
 src/backend/backup/basebackup_lz4.c         |  6 +-
 src/backend/backup/basebackup_zstd.c        |  6 +-
 src/backend/catalog/storage.c               |  2 +-
 src/backend/commands/analyze.c              |  4 +-
 src/backend/commands/async.c                |  6 +-
 src/backend/commands/vacuumparallel.c       |  2 +-
 src/backend/executor/nodeAgg.c              | 30 +++++-----
 src/backend/nodes/tidbitmap.c               |  4 +-
 src/backend/optimizer/path/costsize.c       | 12 ++--
 src/backend/optimizer/util/plancat.c        |  2 +-
 src/backend/po/de.po                        | 16 +++---
 src/backend/po/es.po                        | 14 ++---
 src/backend/po/fr.po                        | 18 +++---
 src/backend/po/id.po                        |  8 +--
 src/backend/po/it.po                        | 12 ++--
 src/backend/po/ja.po                        | 16 +++---
 src/backend/po/ko.po                        | 22 ++++----
 src/backend/po/pl.po                        | 12 ++--
 src/backend/po/pt_BR.po                     | 10 ++--
 src/backend/po/ru.po                        | 24 ++++----
 src/backend/po/sv.po                        | 14 ++---
 src/backend/po/tr.po                        | 12 ++--
 src/backend/po/uk.po                        | 12 ++--
 src/backend/po/zh_CN.po                     | 12 ++--
 src/backend/replication/logical/worker.c    |  2 +-
 src/backend/storage/buffer/buf_init.c       |  4 +-
 src/backend/storage/buffer/bufmgr.c         | 22 ++++----
 src/backend/storage/buffer/freelist.c       |  2 +-
 src/backend/storage/buffer/localbuf.c       |  8 +--
 src/backend/storage/file/buffile.c          | 14 ++---
 src/backend/storage/file/copydir.c          |  2 +-
 src/backend/storage/file/fd.c               |  4 +-
 src/backend/storage/freespace/README        |  8 +--
 src/backend/storage/freespace/freespace.c   | 22 ++++----
 src/backend/storage/freespace/indexfsm.c    |  6 +-
 src/backend/storage/large_object/inv_api.c  | 30 +++++-----
 src/backend/storage/lmgr/predicate.c        |  4 +-
 src/backend/storage/page/bufpage.c          | 22 ++++----
 src/backend/storage/smgr/md.c               | 62 ++++++++++-----------
 src/backend/utils/adt/pgstatfuncs.c         |  4 +-
 src/backend/utils/adt/selfuncs.c            |  2 +-
 src/backend/utils/init/miscinit.c           |  6 +-
 src/backend/utils/misc/guc.c                | 16 +++---
 src/backend/utils/misc/guc_tables.c         |  6 +-
 src/backend/utils/sort/logtape.c            | 48 ++++++++--------
 src/backend/utils/sort/sharedtuplestore.c   | 12 ++--
 src/backend/utils/sort/tuplesort.c          |  6 +-
 src/bin/initdb/initdb.c                     | 22 ++++----
 src/bin/pg_checksums/pg_checksums.c         | 18 +++---
 src/bin/pg_resetwal/pg_resetwal.c           |  6 +-
 src/bin/pg_rewind/filemap.c                 |  4 +-
 src/bin/pg_rewind/pg_rewind.c               |  4 +-
 src/bin/pg_upgrade/file.c                   | 10 ++--
 src/bin/pg_waldump/pg_waldump.c             |  2 +-
 src/common/file_utils.c                     |  6 +-
 src/include/access/brin_page.h              |  6 +-
 src/include/access/ginblock.h               |  6 +-
 src/include/access/gist.h                   |  2 +-
 src/include/access/gist_private.h           |  6 +-
 src/include/access/hash.h                   |  6 +-
 src/include/access/heaptoast.h              | 20 +++----
 src/include/access/htup_details.h           |  6 +-
 src/include/access/itup.h                   |  2 +-
 src/include/access/nbtree.h                 |  6 +-
 src/include/access/slru.h                   |  2 +-
 src/include/access/spgist_private.h         |  6 +-
 src/include/access/xlogrecord.h             |  6 +-
 src/include/backup/basebackup_sink.h        |  4 +-
 src/include/c.h                             |  6 +-
 src/include/pg_config_manual.h              |  2 +-
 src/include/postgres_ext.h                  |  1 +
 src/include/storage/bufmgr.h                |  4 +-
 src/include/storage/bufpage.h               |  8 +--
 src/include/storage/checksum_impl.h         |  6 +-
 src/include/storage/fsm_internals.h         |  4 +-
 src/include/storage/large_object.h          |  8 +--
 src/include/storage/off.h                   |  2 +-
 src/include/utils/rel.h                     |  4 +-
 src/test/modules/test_slru/test_slru.c      |  2 +-
 src/test/regress/expected/btree_index.out   |  4 +-
 src/test/regress/expected/largeobject.out   |  2 +-
 src/test/regress/expected/largeobject_1.out |  2 +-
 src/test/regress/sql/btree_index.sql        |  4 +-
 src/test/regress/sql/largeobject.sql        |  2 +-
 src/tools/msvc/Solution.pm                  |  2 +-
 159 files changed, 752 insertions(+), 751 deletions(-)

diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out
index 38791bbc1f..b677475ae9 100644
--- a/contrib/amcheck/expected/check_btree.out
+++ b/contrib/amcheck/expected/check_btree.out
@@ -168,7 +168,7 @@ WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
  x
 (1 row)
 
--- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
+-- Insert compressible heap tuple (comfortably exceeds toast_tuple_threshold):
 INSERT INTO toast_bug SELECT repeat('a', 2200);
 -- Should not get false positive report of corruption:
 SELECT bt_index_check('toasty', true);
diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql
index 033c04b4d0..6bf3f02c22 100644
--- a/contrib/amcheck/sql/check_btree.sql
+++ b/contrib/amcheck/sql/check_btree.sql
@@ -110,7 +110,7 @@ WHERE attrelid = 'toasty'::regclass AND attname = 'buggy';
 SELECT attstorage FROM pg_attribute
 WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
 
--- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
+-- Insert compressible heap tuple (comfortably exceeds toast_tuple_threshold):
 INSERT INTO toast_bug SELECT repeat('a', 2200);
 -- Should not get false positive report of corruption:
 SELECT bt_index_check('toasty', true);
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 97f3253522..004aa26b7c 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -540,13 +540,13 @@ verify_heapam(PG_FUNCTION_ARGS)
 										   (unsigned) MAXALIGN(SizeofHeapTupleHeader)));
 				continue;
 			}
-			if (ctx.lp_off + ctx.lp_len > BLCKSZ)
+			if (ctx.lp_off + ctx.lp_len > cluster_block_size)
 			{
 				report_corruption(&ctx,
 								  psprintf("line pointer to page offset %u with length %u ends beyond maximum page offset %u",
 										   ctx.lp_off,
 										   ctx.lp_len,
-										   (unsigned) BLCKSZ));
+										   (unsigned) cluster_block_size));
 				continue;
 			}
 
@@ -1460,7 +1460,7 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
 				  uint32 extsize)
 {
 	int32		chunk_seq;
-	int32		last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
+	int32		last_chunk_seq = (extsize - 1) / cluster_toast_max_chunk_size;
 	Pointer		chunk;
 	bool		isnull;
 	int32		chunksize;
@@ -1530,8 +1530,8 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
 		return;
 	}
 
-	expected_size = chunk_seq < last_chunk_seq ? TOAST_MAX_CHUNK_SIZE
-		: extsize - (last_chunk_seq * TOAST_MAX_CHUNK_SIZE);
+	expected_size = chunk_seq < last_chunk_seq ? cluster_toast_max_chunk_size
+		: extsize - (last_chunk_seq * cluster_toast_max_chunk_size);
 
 	if (chunksize != expected_size)
 		report_toast_corruption(ctx, ta,
@@ -1773,7 +1773,7 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
 	int32		last_chunk_seq;
 
 	extsize = VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer);
-	last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
+	last_chunk_seq = (extsize - 1) / cluster_toast_max_chunk_size;
 
 	/*
 	 * Setup a scan key to find chunks in toast table with matching va_valueid
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 94a9759322..f1298fd34a 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -2962,7 +2962,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
 	BTPageOpaque opaque;
 	OffsetNumber maxoffset;
 
-	page = palloc(BLCKSZ);
+	page = palloc(cluster_block_size);
 
 	/*
 	 * We copy the page into local storage to avoid holding pin on the buffer
@@ -2979,7 +2979,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
 	_bt_checkpage(state->rel, buffer);
 
 	/* Only use copy of page in palloc()'d memory */
-	memcpy(page, BufferGetPage(buffer), BLCKSZ);
+	memcpy(page, BufferGetPage(buffer), cluster_block_size);
 	UnlockReleaseBuffer(buffer);
 
 	opaque = BTPageGetOpaque(page);
@@ -3163,7 +3163,7 @@ PageGetItemIdCareful(BtreeCheckState *state, BlockNumber block, Page page,
 	ItemId		itemid = PageGetItemId(page, offset);
 
 	if (ItemIdGetOffset(itemid) + ItemIdGetLength(itemid) >
-		BLCKSZ - MAXALIGN(sizeof(BTPageOpaqueData)))
+		cluster_block_size - MAXALIGN(sizeof(BTPageOpaqueData)))
 		ereport(ERROR,
 				(errcode(ERRCODE_INDEX_CORRUPTED),
 				 errmsg("line pointer points past end of tuple space in index \"%s\"",
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index b90145148d..32a339469a 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -52,7 +52,7 @@ flushCachedPage(Relation index, BloomBuildState *buildstate)
 
 	state = GenericXLogStart(index);
 	page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
-	memcpy(page, buildstate->data.data, BLCKSZ);
+	memcpy(page, buildstate->data.data, cluster_block_size);
 	GenericXLogFinish(state);
 	UnlockReleaseBuffer(buffer);
 }
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 330811ec60..d08fe1de26 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -112,7 +112,7 @@ typedef struct BloomOptions
  */
 typedef BlockNumber FreeBlockNumberArray[
 										 MAXALIGN_DOWN(
-													   BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+													   cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
 													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
 													   ) / sizeof(BlockNumber)
 ];
@@ -150,7 +150,7 @@ typedef struct BloomState
 } BloomState;
 
 #define BloomPageGetFreeSpace(state, page) \
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+	(cluster_block_size - MAXALIGN(SizeOfPageHeaderData) \
 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
 
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index f23fbb1d9e..9d79319e90 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -400,7 +400,7 @@ BloomInitPage(Page page, uint16 flags)
 {
 	BloomPageOpaque opaque;
 
-	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+	PageInit(page, cluster_block_size, sizeof(BloomPageOpaqueData));
 
 	opaque = BloomPageGetOpaque(page);
 	opaque->flags = flags;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2189be8a3c..c265b6e309 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -840,7 +840,7 @@ fileAnalyzeForeignTable(Relation relation,
 	 * Convert size to pages.  Must return at least 1 so that we can tell
 	 * later on that pg_class.relpages is not default.
 	 */
-	*totalpages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
+	*totalpages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
 	if (*totalpages < 1)
 		*totalpages = 1;
 
@@ -1010,12 +1010,12 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel,
 	 * back to the default if using a program as the input.
 	 */
 	if (fdw_private->is_program || stat(fdw_private->filename, &stat_buf) < 0)
-		stat_buf.st_size = 10 * BLCKSZ;
+		stat_buf.st_size = 10 * cluster_block_size;
 
 	/*
 	 * Convert size to pages for use in I/O cost estimate later.
 	 */
-	pages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
+	pages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
 	if (pages < 1)
 		pages = 1;
 	fdw_private->pages = pages;
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 9cdc8e182b..77b00ef2b9 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -116,7 +116,7 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
 
 	stat->blkno = blkno;
 
-	stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
+	stat->max_avail = cluster_block_size - (cluster_block_size - phdr->pd_special + SizeOfPageHeaderData);
 
 	stat->dead_items = stat->live_items = 0;
 
@@ -663,8 +663,8 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 
 		uargs = palloc(sizeof(ua_page_items));
 
-		uargs->page = palloc(BLCKSZ);
-		memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
+		uargs->page = palloc(cluster_block_size);
+		memcpy(uargs->page, BufferGetPage(buffer), cluster_block_size);
 
 		UnlockReleaseBuffer(buffer);
 		relation_close(rel, AccessShareLock);
diff --git a/contrib/pageinspect/expected/checksum.out b/contrib/pageinspect/expected/checksum.out
index a85388e158..236df5be64 100644
--- a/contrib/pageinspect/expected/checksum.out
+++ b/contrib/pageinspect/expected/checksum.out
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	cluster_block_size	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pageinspect/expected/checksum_1.out b/contrib/pageinspect/expected/checksum_1.out
index 6fb1b1b04d..ce7a30f3c1 100644
--- a/contrib/pageinspect/expected/checksum_1.out
+++ b/contrib/pageinspect/expected/checksum_1.out
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	cluster_block_size	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index b25a63cbd6..2527501078 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -179,8 +179,8 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 						blkno, RelationGetRelationName(rel))));
 
 	/* Initialize buffer to copy to */
-	raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
-	SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
+	raw_page = (bytea *) palloc(cluster_block_size + VARHDRSZ);
+	SET_VARSIZE(raw_page, cluster_block_size + VARHDRSZ);
 	raw_page_data = VARDATA(raw_page);
 
 	/* Take a verbatim copy of the page */
@@ -188,7 +188,7 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 	buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
 	LockBuffer(buf, BUFFER_LOCK_SHARE);
 
-	memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
+	memcpy(raw_page_data, BufferGetPage(buf), cluster_block_size);
 
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	ReleaseBuffer(buf);
@@ -219,12 +219,12 @@ get_page_from_raw(bytea *raw_page)
 
 	raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
 
-	if (raw_page_size != BLCKSZ)
+	if (raw_page_size != cluster_block_size)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("invalid page size"),
 				 errdetail("Expected %d bytes, got %d.",
-						   BLCKSZ, raw_page_size)));
+						   cluster_block_size, raw_page_size)));
 
 	page = palloc(raw_page_size);
 
diff --git a/contrib/pageinspect/sql/checksum.sql b/contrib/pageinspect/sql/checksum.sql
index b877db0611..2afe04d0d2 100644
--- a/contrib/pageinspect/sql/checksum.sql
+++ b/contrib/pageinspect/sql/checksum.sql
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	cluster_block_size	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pg_walinspect/pg_walinspect.c b/contrib/pg_walinspect/pg_walinspect.c
index 796a74f322..318c55abac 100644
--- a/contrib/pg_walinspect/pg_walinspect.c
+++ b/contrib/pg_walinspect/pg_walinspect.c
@@ -386,9 +386,9 @@ GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record,
 						(errcode(ERRCODE_INTERNAL_ERROR),
 						 errmsg_internal("%s", record->errormsg_buf)));
 
-			block_fpi_data = (bytea *) palloc(BLCKSZ + VARHDRSZ);
-			SET_VARSIZE(block_fpi_data, BLCKSZ + VARHDRSZ);
-			memcpy(VARDATA(block_fpi_data), page, BLCKSZ);
+			block_fpi_data = (bytea *) palloc(cluster_block_size + VARHDRSZ);
+			SET_VARSIZE(block_fpi_data, cluster_block_size + VARHDRSZ);
+			memcpy(VARDATA(block_fpi_data), page, cluster_block_size);
 			values[i++] = PointerGetDatum(block_fpi_data);
 		}
 		else
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index f601dc6121..0ce0b4f9a9 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -94,7 +94,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		if (VM_ALL_VISIBLE(rel, blkno, &vmbuffer))
 		{
 			freespace = GetRecordedFreeSpace(rel, blkno);
-			stat->tuple_len += BLCKSZ - freespace;
+			stat->tuple_len += cluster_block_size - freespace;
 			stat->free_space += freespace;
 			continue;
 		}
@@ -113,7 +113,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		if (!PageIsNew(page))
 			stat->free_space += PageGetHeapFreeSpace(page);
 		else
-			stat->free_space += BLCKSZ - SizeOfPageHeaderData;
+			stat->free_space += cluster_block_size - SizeOfPageHeaderData;
 
 		/* We may count the page as scanned even if it's new/empty */
 		scanned++;
@@ -182,7 +182,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		UnlockReleaseBuffer(buf);
 	}
 
-	stat->table_len = (uint64) nblocks * BLCKSZ;
+	stat->table_len = (uint64) nblocks * cluster_block_size;
 
 	/*
 	 * We don't know how many tuples are in the pages we didn't scan, so
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index d69ac1c93d..9391bd540b 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -297,7 +297,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 		{
 			int			max_avail;
 
-			max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
+			max_avail = cluster_block_size - (cluster_block_size - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
 			indexStat.max_avail += max_avail;
 			indexStat.free_space += PageGetFreeSpace(page);
 
@@ -342,7 +342,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 								indexStat.leaf_pages +
 								indexStat.internal_pages +
 								indexStat.deleted_pages +
-								indexStat.empty_pages) * BLCKSZ);
+								indexStat.empty_pages) * cluster_block_size);
 		values[j++] = psprintf("%u", indexStat.root_blkno);
 		values[j++] = psprintf(INT64_FORMAT, indexStat.internal_pages);
 		values[j++] = psprintf(INT64_FORMAT, indexStat.leaf_pages);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 93b7834b77..6a1c6bc6aa 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -386,7 +386,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
 	table_endscan(scan);
 	relation_close(rel, AccessShareLock);
 
-	stat.table_len = (uint64) nblocks * BLCKSZ;
+	stat.table_len = (uint64) nblocks * cluster_block_size;
 
 	return build_pgstattuple_type(&stat, fcinfo);
 }
@@ -409,7 +409,7 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 	if (PageIsNew(page))
 	{
 		/* fully empty page */
-		stat->free_space += BLCKSZ;
+		stat->free_space += cluster_block_size;
 	}
 	else
 	{
@@ -419,7 +419,7 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 		if (P_IGNORE(opaque))
 		{
 			/* deleted or half-dead page */
-			stat->free_space += BLCKSZ;
+			stat->free_space += cluster_block_size;
 		}
 		else if (P_ISLEAF(opaque))
 		{
@@ -456,7 +456,7 @@ pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 		switch (opaque->hasho_flag & LH_PAGE_TYPE)
 		{
 			case LH_UNUSED_PAGE:
-				stat->free_space += BLCKSZ;
+				stat->free_space += cluster_block_size;
 				break;
 			case LH_BUCKET_PAGE:
 			case LH_OVERFLOW_PAGE:
@@ -531,7 +531,7 @@ pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn,
 		/* Quit if we've scanned the whole relation */
 		if (blkno >= nblocks)
 		{
-			stat.table_len = (uint64) nblocks * BLCKSZ;
+			stat.table_len = (uint64) nblocks * cluster_block_size;
 
 			break;
 		}
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..22cdddba67 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -2364,7 +2364,7 @@ deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
 
 	appendStringInfoString(buf, "SELECT pg_catalog.pg_relation_size(");
 	deparseStringLiteral(buf, relname.data);
-	appendStringInfo(buf, "::pg_catalog.regclass) / %d", BLCKSZ);
+	appendStringInfo(buf, "::pg_catalog.regclass) / %d", cluster_block_size);
 }
 
 /*
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..6c55044f64 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -755,7 +755,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 		{
 			baserel->pages = 10;
 			baserel->tuples =
-				(10 * BLCKSZ) / (baserel->reltarget->width +
+				(10 * cluster_block_size) / (baserel->reltarget->width +
 								 MAXALIGN(SizeofHeapTupleHeader));
 		}
 
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index d17ff51e28..bc678737d6 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2023,7 +2023,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </para>
       <para>
        Size of the on-disk representation of this table in pages (of size
-       <symbol>BLCKSZ</symbol>).  This is only an estimate used by the
+       <symbol>cluster_block_size</symbol>).  This is only an estimate used by the
        planner.  It is updated by <link linkend="sql-vacuum"><command>VACUUM</command></link>,
        <link linkend="sql-analyze"><command>ANALYZE</command></link>, and a few DDL commands such as
        <link linkend="sql-createindex"><command>CREATE INDEX</command></link>.
@@ -4884,8 +4884,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
    assigned when it is created.  Each large object is broken into
    segments or <quote>pages</quote> small enough to be conveniently stored as rows
    in <structname>pg_largeobject</structname>.
-   The amount of data per page is defined to be <symbol>LOBLKSIZE</symbol> (which is currently
-   <literal>BLCKSZ/4</literal>, or typically 2 kB).
+   The amount of data per page is defined to be <symbol>cluster_loblksize</symbol> (which is currently
+   <literal>cluster_block_size/4</literal>, or typically 2 kB).
   </para>
 
   <para>
@@ -4939,7 +4939,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </para>
       <para>
        Actual data stored in the large object.
-       This will never be more than <symbol>LOBLKSIZE</symbol> bytes and might be less.
+       This will never be more than <symbol>cluster_loblksize</symbol> bytes and might be less.
       </para></entry>
      </row>
     </tbody>
@@ -4949,9 +4949,9 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
   <para>
    Each row of <structname>pg_largeobject</structname> holds data
    for one page of a large object, beginning at
-   byte offset (<literal>pageno * LOBLKSIZE</literal>) within the object.  The implementation
+   byte offset (<literal>pageno * cluster_loblksize</literal>) within the object.  The implementation
    allows sparse storage: pages might be missing, and might be shorter than
-   <literal>LOBLKSIZE</literal> bytes even if they are not the last page of the object.
+   <literal>cluster_loblksize</literal> bytes even if they are not the last page of the object.
    Missing regions within a large object read as zeroes.
   </para>
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 694d667bf9..2274bc0ffb 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1631,8 +1631,8 @@ include_dir 'conf.d'
         settings significantly higher than the minimum are usually needed
         for good performance.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
-        (Non-default values of <symbol>BLCKSZ</symbol> change the minimum
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
+        (Non-default values of <symbol>cluster_block_size</symbol> change the minimum
         value.)
         This parameter can only be set at server start.
        </para>
@@ -1762,9 +1762,9 @@ include_dir 'conf.d'
         each database session.  These are session-local buffers used only
         for access to temporary tables.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is eight megabytes (<literal>8MB</literal>).
-        (If <symbol>BLCKSZ</symbol> is not 8kB, the default value scales
+        (If <symbol>cluster_block_size</symbol> is not 8kB, the default value scales
         proportionally to it.)
         This setting can be changed within individual
         sessions, but only before the first use of temporary tables
@@ -1779,7 +1779,7 @@ include_dir 'conf.d'
         buffers is only a buffer descriptor, or about 64 bytes, per
         increment in <varname>temp_buffers</varname>.  However if a buffer is
         actually used an additional 8192 bytes will be consumed for it
-        (or in general, <symbol>BLCKSZ</symbol> bytes).
+        (or in general, <symbol>cluster_block_size</symbol> bytes).
        </para>
       </listitem>
      </varlistentry>
@@ -2435,11 +2435,11 @@ include_dir 'conf.d'
          cache, where performance might degrade.  This setting may have no
          effect on some platforms.
          If this value is specified without units, it is taken as blocks,
-         that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+         that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
          The valid range is between
          <literal>0</literal>, which disables forced writeback, and
          <literal>2MB</literal>.  The default is <literal>512kB</literal> on Linux,
-         <literal>0</literal> elsewhere.  (If <symbol>BLCKSZ</symbol> is not 8kB,
+         <literal>0</literal> elsewhere.  (If <symbol>cluster_block_size</symbol> is not 8kB,
          the default and maximum values scale proportionally to it.)
          This parameter can only be set in the <filename>postgresql.conf</filename>
          file or on the server command line.
@@ -2481,11 +2481,11 @@ include_dir 'conf.d'
          than the OS's page cache, where performance might degrade.  This
          setting may have no effect on some platforms.
          If this value is specified without units, it is taken as blocks,
-         that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+         that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
          The valid range is
          between <literal>0</literal>, which disables forced writeback,
          and <literal>2MB</literal>.  The default is <literal>0</literal>, i.e., no
-         forced writeback.  (If <symbol>BLCKSZ</symbol> is not 8kB,
+         forced writeback.  (If <symbol>cluster_block_size</symbol> is not 8kB,
          the maximum value scales proportionally to it.)
         </para>
        </listitem>
@@ -3488,11 +3488,11 @@ include_dir 'conf.d'
         than the OS's page cache, where performance might degrade.  This
         setting may have no effect on some platforms.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The valid range is
         between <literal>0</literal>, which disables forced writeback,
         and <literal>2MB</literal>.  The default is <literal>256kB</literal> on
-        Linux, <literal>0</literal> elsewhere.  (If <symbol>BLCKSZ</symbol> is not
+        Linux, <literal>0</literal> elsewhere.  (If <symbol>cluster_block_size</symbol> is not
         8kB, the default and maximum values scale proportionally to it.)
         This parameter can only be set in the <filename>postgresql.conf</filename>
         file or on the server command line.
@@ -5614,7 +5614,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         table, but when indexes are used the amount of table data
         scanned will normally be less.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is 8 megabytes (<literal>8MB</literal>).
        </para>
       </listitem>
@@ -5636,7 +5636,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         particular index can participate in a parallel vacuum.  See
         <xref linkend="sql-vacuum"/>.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is 512 kilobytes (<literal>512kB</literal>).
        </para>
       </listitem>
@@ -5668,9 +5668,9 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         purposes.  The system also does not assume data remains in
         the disk cache between queries.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is 4 gigabytes (<literal>4GB</literal>).
-        (If <symbol>BLCKSZ</symbol> is not 8kB, the default value scales
+        (If <symbol>cluster_block_size</symbol> is not 8kB, the default value scales
         proportionally to it.)
        </para>
       </listitem>
@@ -10626,7 +10626,7 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       <listitem>
        <para>
         Reports the size of a disk block.  It is determined by the value
-        of <literal>BLCKSZ</literal> when building the server. The default
+        of <literal>cluster_block_size</literal> when building the server. The default
         value is 8192 bytes.  The meaning of some configuration
         variables (such as <xref linkend="guc-shared-buffers"/>) is
         influenced by <varname>block_size</varname>. See <xref
diff --git a/doc/src/sgml/limits.sgml b/doc/src/sgml/limits.sgml
index d5b2b627dd..bbea9bc733 100644
--- a/doc/src/sgml/limits.sgml
+++ b/doc/src/sgml/limits.sgml
@@ -46,7 +46,7 @@
     <row>
      <entry>relation size</entry>
      <entry>32 TB</entry>
-     <entry>with the default <symbol>BLCKSZ</symbol> of 8192 bytes</entry>
+     <entry>with the default <symbol>cluster_block_size</symbol> of 8192 bytes</entry>
     </row>
 
     <row>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 4ff415d6a0..c655c70e38 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -2693,7 +2693,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        <para>
         Relation data reads, writes, and extends are done in
         <varname>block_size</varname> units, derived from the build-time
-        parameter <symbol>BLCKSZ</symbol>, which is <literal>8192</literal> by
+        parameter <symbol>cluster_block_size</symbol>, which is <literal>8192</literal> by
         default.
        </para>
       </entry>
diff --git a/doc/src/sgml/pgfreespacemap.sgml b/doc/src/sgml/pgfreespacemap.sgml
index 829ad60f32..0da94e5f63 100644
--- a/doc/src/sgml/pgfreespacemap.sgml
+++ b/doc/src/sgml/pgfreespacemap.sgml
@@ -60,7 +60,7 @@
 
   <para>
    The values stored in the free space map are not exact. They're rounded
-   to precision of 1/256th of <symbol>BLCKSZ</symbol> (32 bytes with default <symbol>BLCKSZ</symbol>), and
+   to precision of 1/256th of <symbol>cluster_block_size</symbol> (32 bytes with default <symbol>cluster_block_size</symbol>), and
    they're not kept fully up-to-date as tuples are inserted and updated.
   </para>
 
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 148fb1b49d..4cf7460b0a 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -415,7 +415,7 @@ described in more detail below.
 
 <para>
 Out-of-line values are divided (after compression if used) into chunks of at
-most <symbol>TOAST_MAX_CHUNK_SIZE</symbol> bytes (by default this value is chosen
+most <symbol>cluster_toast_max_chunk_size</symbol> bytes (by default this value is chosen
 so that four chunk rows will fit on a page, making it about 2000 bytes).
 Each chunk is stored as a separate row in the <acronym>TOAST</acronym> table
 belonging to the owning table.  Every
@@ -438,10 +438,10 @@ bytes regardless of the actual size of the represented value.
 <para>
 The <acronym>TOAST</acronym> management code is triggered only
 when a row value to be stored in a table is wider than
-<symbol>TOAST_TUPLE_THRESHOLD</symbol> bytes (normally 2 kB).
+<symbol>toast_tuple_threshold</symbol> bytes (normally 2 kB).
 The <acronym>TOAST</acronym> code will compress and/or move
 field values out-of-line until the row value is shorter than
-<symbol>TOAST_TUPLE_TARGET</symbol> bytes (also normally 2 kB, adjustable)
+<symbol>cluster_toast_tuple_target</symbol> bytes (also normally 2 kB, adjustable)
 or no more gains can be had.  During an UPDATE
 operation, values of unchanged fields are normally preserved as-is; so an
 UPDATE of a row with out-of-line values incurs no <acronym>TOAST</acronym> costs if
@@ -496,7 +496,7 @@ with <link linkend="sql-altertable"><command>ALTER TABLE ... SET STORAGE</comman
 </para>
 
 <para>
-<symbol>TOAST_TUPLE_TARGET</symbol> can be adjusted for each table using
+<symbol>cluster_toast_tuple_target</symbol> can be adjusted for each table using
 <link linkend="sql-altertable"><command>ALTER TABLE ... SET (toast_tuple_target = N)</command></link>
 </para>
 
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index 56534cf29c..b3b0633d73 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -212,7 +212,7 @@ typedef struct BloomOptions
  * be larger because the index has multiple columns.
  */
 #define BloomMaxFilterSize \
-	MAXALIGN_DOWN(BLCKSZ - \
+	MAXALIGN_DOWN(cluster_block_size - \
 				  (MAXALIGN(SizeOfPageHeaderData + \
 							sizeof(ItemIdData)) + \
 				   MAXALIGN(sizeof(BrinSpecialSpace)) + \
diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c
index b578d25954..11103ff25d 100644
--- a/src/backend/access/brin/brin_pageops.c
+++ b/src/backend/access/brin/brin_pageops.c
@@ -27,7 +27,7 @@
  * a single item per page, unlike other index AMs.
  */
 #define BrinMaxItemSize \
-	MAXALIGN_DOWN(BLCKSZ - \
+	MAXALIGN_DOWN(cluster_block_size - \
 				  (MAXALIGN(SizeOfPageHeaderData + \
 							sizeof(ItemIdData)) + \
 				   MAXALIGN(sizeof(BrinSpecialSpace))))
@@ -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, cluster_block_size, sizeof(BrinSpecialSpace));
 
 	BrinPageType(page) = type;
 }
diff --git a/src/backend/access/common/bufmask.c b/src/backend/access/common/bufmask.c
index 5e392dab1e..4e6e9eb7e1 100644
--- a/src/backend/access/common/bufmask.c
+++ b/src/backend/access/common/bufmask.c
@@ -76,7 +76,7 @@ mask_unused_space(Page page)
 
 	/* Sanity check */
 	if (pd_lower > pd_upper || pd_special < pd_upper ||
-		pd_lower < SizeOfPageHeaderData || pd_special > BLCKSZ)
+		pd_lower < SizeOfPageHeaderData || pd_special > cluster_block_size)
 	{
 		elog(ERROR, "invalid page pd_lower %u pd_upper %u pd_special %u",
 			 pd_lower, pd_upper, pd_special);
@@ -120,7 +120,7 @@ mask_page_content(Page page)
 {
 	/* Mask Page Content */
 	memset(page + SizeOfPageHeaderData, MASK_MARKER,
-		   BLCKSZ - SizeOfPageHeaderData);
+		   cluster_block_size - SizeOfPageHeaderData);
 
 	/* Mask pd_lower and pd_upper */
 	memset(&((PageHeader) page)->pd_lower, MASK_MARKER,
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 469de9bb49..52d4ee6834 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -328,7 +328,7 @@ static relopt_int intRelOpts[] =
 			RELOPT_KIND_HEAP,
 			ShareUpdateExclusiveLock
 		},
-		TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
+		cluster_toast_tuple_target, 128, toast_tuple_target_main
 	},
 	{
 		{
diff --git a/src/backend/access/common/syncscan.c b/src/backend/access/common/syncscan.c
index 2bc6828883..3aa54f28d4 100644
--- a/src/backend/access/common/syncscan.c
+++ b/src/backend/access/common/syncscan.c
@@ -80,7 +80,7 @@ bool		trace_syncscan = false;
  * the buffer cache anyway, and on the other hand the page is most likely
  * still in the OS cache.
  */
-#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / BLCKSZ)
+#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / cluster_block_size)
 
 
 /*
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 588825ed85..8745f87be6 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -133,7 +133,7 @@ toast_save_datum(Relation rel, Datum value,
 	{
 		struct varlena hdr;
 		/* this is to make the union big enough for a chunk: */
-		char		data[TOAST_MAX_CHUNK_SIZE + VARHDRSZ];
+		char		data[cluster_toast_max_chunk_size + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			chunk_data;
@@ -311,7 +311,7 @@ toast_save_datum(Relation rel, Datum value,
 		/*
 		 * Calculate the size of this chunk
 		 */
-		chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);
+		chunk_size = Min(cluster_toast_max_chunk_size, data_todo);
 
 		/*
 		 * Build a tuple and store it
diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c
index 35490c7283..6ec59ae354 100644
--- a/src/backend/access/gin/ginbtree.c
+++ b/src/backend/access/gin/ginbtree.c
@@ -510,7 +510,7 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
 			 * critical section yet.)
 			 */
 			newrootpg = PageGetTempPage(newrpage);
-			GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), BLCKSZ);
+			GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), cluster_block_size);
 
 			btree->fillRoot(btree, newrootpg,
 							BufferGetBlockNumber(lbuffer), newlpage,
@@ -567,15 +567,15 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
 		{
 			/* Splitting the root, three pages to update */
 			MarkBufferDirty(lbuffer);
-			memcpy(page, newrootpg, BLCKSZ);
-			memcpy(BufferGetPage(lbuffer), newlpage, BLCKSZ);
-			memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
+			memcpy(page, newrootpg, cluster_block_size);
+			memcpy(BufferGetPage(lbuffer), newlpage, cluster_block_size);
+			memcpy(BufferGetPage(rbuffer), newrpage, cluster_block_size);
 		}
 		else
 		{
 			/* Normal split, only two pages to update */
-			memcpy(page, newlpage, BLCKSZ);
-			memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
+			memcpy(page, newlpage, cluster_block_size);
+			memcpy(BufferGetPage(rbuffer), newrpage, cluster_block_size);
 		}
 
 		/* We also clear childbuf's INCOMPLETE_SPLIT flag, if passed */
diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
index 9caeac164a..9bef09651e 100644
--- a/src/backend/access/gin/gindatapage.c
+++ b/src/backend/access/gin/gindatapage.c
@@ -655,7 +655,7 @@ dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
 						break;
 					if (append)
 					{
-						if ((leaf->lsize - segsize) < (BLCKSZ * 3) / 4)
+						if ((leaf->lsize - segsize) < (cluster_block_size * 3) / 4)
 							break;
 					}
 
@@ -681,8 +681,8 @@ dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
 		/*
 		 * Now allocate a couple of temporary page images, and fill them.
 		 */
-		*newlpage = palloc(BLCKSZ);
-		*newrpage = palloc(BLCKSZ);
+		*newlpage = palloc(cluster_block_size);
+		*newrpage = palloc(cluster_block_size);
 
 		dataPlaceToPageLeafSplit(leaf, lbound, rbound,
 								 *newlpage, *newrpage);
@@ -887,7 +887,7 @@ computeLeafRecompressWALData(disassembledLeaf *leaf)
 
 	walbufbegin =
 		palloc(sizeof(ginxlogRecompressDataLeaf) +
-			   BLCKSZ +			/* max size needed to hold the segment data */
+			   cluster_block_size +			/* max size needed to hold the segment data */
 			   nmodified * 2	/* (segno + action) per action */
 		);
 	walbufend = walbufbegin;
@@ -1041,8 +1041,8 @@ dataPlaceToPageLeafSplit(disassembledLeaf *leaf,
 	leafSegmentInfo *seginfo;
 
 	/* Initialize temporary pages to hold the new left and right pages */
-	GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
-	GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
+	GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, cluster_block_size);
+	GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, cluster_block_size);
 
 	/*
 	 * Copy the segments that go to the left page.
@@ -1259,7 +1259,7 @@ dataSplitPageInternal(GinBtree btree, Buffer origbuf,
 	Page		lpage;
 	Page		rpage;
 	OffsetNumber separator;
-	PostingItem allitems[(BLCKSZ / sizeof(PostingItem)) + 1];
+	PostingItem allitems[(cluster_block_size / sizeof(PostingItem)) + 1];
 
 	lpage = PageGetTempPage(oldpage);
 	rpage = PageGetTempPage(oldpage);
@@ -1779,8 +1779,8 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
 	bool		is_build = (buildStats != NULL);
 
 	/* Construct the new root page in memory first. */
-	tmppage = (Page) palloc(BLCKSZ);
-	GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
+	tmppage = (Page) palloc(cluster_block_size);
+	GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, cluster_block_size);
 	GinPageGetOpaque(tmppage)->rightlink = InvalidBlockNumber;
 
 	/*
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index eb6c554831..9a816e5bfe 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -38,8 +38,8 @@
 /* GUC parameter */
 int			gin_pending_list_limit = 0;
 
-#define GIN_PAGE_FREESIZE \
-	( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
+#define gin_page_freesize \
+	( cluster_block_size - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
 
 typedef struct KeyArray
 {
@@ -92,7 +92,7 @@ writeListPage(Relation index, Buffer buffer,
 		off++;
 	}
 
-	Assert(size <= BLCKSZ);		/* else we overran workspace */
+	Assert(size <= cluster_block_size);		/* else we overran workspace */
 
 	GinPageGetOpaque(page)->rightlink = rightlink;
 
@@ -455,7 +455,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 	 * ginInsertCleanup() should not be called inside our CRIT_SECTION.
 	 */
 	cleanupSize = GinGetPendingListCleanupSize(index);
-	if (metadata->nPendingPages * GIN_PAGE_FREESIZE > cleanupSize * 1024L)
+	if (metadata->nPendingPages * gin_page_freesize > cleanupSize * 1024L)
 		needCleanup = true;
 
 	UnlockReleaseBuffer(metabuffer);
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 1f0214498c..022f71e25c 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1632,9 +1632,9 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
 	 */
 	for (;;)
 	{
-		Datum		datum[BLCKSZ / sizeof(IndexTupleData)];
-		GinNullCategory category[BLCKSZ / sizeof(IndexTupleData)];
-		bool		datumExtracted[BLCKSZ / sizeof(IndexTupleData)];
+		Datum		datum[cluster_block_size / sizeof(IndexTupleData)];
+		GinNullCategory category[cluster_block_size / sizeof(IndexTupleData)];
+		bool		datumExtracted[cluster_block_size / sizeof(IndexTupleData)];
 
 		Assert(pos->lastOffset > pos->firstOffset);
 		memset(datumExtracted + pos->firstOffset - 1, 0,
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index e5d310d836..628d0f3a40 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -569,7 +569,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	BlockNumber blkno = GIN_ROOT_BLKNO;
 	GinVacuumState gvs;
 	Buffer		buffer;
-	BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
+	BlockNumber rootOfPostingTree[cluster_block_size / (sizeof(IndexTupleData) + sizeof(ItemId))];
 	uint32		nRoot;
 
 	gvs.tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index f7c84beef8..9adaf5e80c 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -146,7 +146,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
 			GinPostingList *plist;
 
 			plist = ginCompressPostingList(uncompressed, nuncompressed,
-										   BLCKSZ, &npacked);
+										   cluster_block_size, &npacked);
 			totalsize = SizeOfGinPostingList(plist);
 
 			Assert(npacked == nuncompressed);
@@ -236,7 +236,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
 			Assert(nnewitems == nolditems + nitems);
 
 			newseg = ginCompressPostingList(newitems, nnewitems,
-											BLCKSZ, &npacked);
+											cluster_block_size, &npacked);
 			Assert(npacked == nnewitems);
 
 			newsegsize = SizeOfGinPostingList(newseg);
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index 5e0c1447f9..8f4bf77616 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -255,7 +255,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * Calculate target amount of free space to leave on pages.
 	 */
 	fillfactor = options ? options->fillfactor : GIST_DEFAULT_FILLFACTOR;
-	buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
+	buildstate.freespace = cluster_block_size * (100 - fillfactor) / 100;
 
 	/*
 	 * Build the index using the chosen strategy.
@@ -415,7 +415,7 @@ gist_indexsortbuild(GISTBuildState *state)
 	 * Write an empty page as a placeholder for the root page. It will be
 	 * replaced with the real root page at the end.
 	 */
-	page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
+	page = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
 	smgrextend(RelationGetSmgr(state->indexrel), MAIN_FORKNUM, GIST_ROOT_BLKNO,
 			   page, true);
 	state->pages_allocated++;
@@ -510,7 +510,7 @@ gist_indexsortbuild_levelstate_add(GISTBuildState *state,
 
 		if (levelstate->pages[levelstate->current_page] == NULL)
 			levelstate->pages[levelstate->current_page] =
-				palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+				palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 
 		newPage = levelstate->pages[levelstate->current_page];
 		gistinitpage(newPage, old_page_flags);
@@ -580,7 +580,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
 
 		/* Create page and copy data */
 		data = (char *) (dist->list);
-		target = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
+		target = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
 		gistinitpage(target, isleaf ? F_LEAF : 0);
 		for (int i = 0; i < dist->block.num; i++)
 		{
@@ -631,7 +631,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
 		if (parent == NULL)
 		{
 			parent = palloc0(sizeof(GistSortedBuildLevelState));
-			parent->pages[0] = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+			parent->pages[0] = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 			parent->parent = NULL;
 			gistinitpage(parent->pages[0], 0);
 
@@ -702,7 +702,7 @@ gistInitBuffering(GISTBuildState *buildstate)
 	int			levelStep;
 
 	/* Calc space of index page which is available for index tuples */
-	pageFreeSpace = BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
+	pageFreeSpace = cluster_block_size - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
 		- sizeof(ItemIdData)
 		- buildstate->freespace;
 
@@ -799,7 +799,7 @@ gistInitBuffering(GISTBuildState *buildstate)
 			break;
 
 		/* each node in the lowest level of a subtree has one page in memory */
-		if (maxlowestlevelpages > ((double) maintenance_work_mem * 1024) / BLCKSZ)
+		if (maxlowestlevelpages > ((double) maintenance_work_mem * 1024) / cluster_block_size)
 			break;
 
 		/* Good, we can handle this levelStep. See if we can go one higher. */
@@ -858,7 +858,7 @@ calculatePagesPerBuffer(GISTBuildState *buildstate, int levelStep)
 	Size		pageFreeSpace;
 
 	/* Calc space of index page which is available for index tuples */
-	pageFreeSpace = BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
+	pageFreeSpace = cluster_block_size - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
 		- sizeof(ItemIdData)
 		- buildstate->freespace;
 
diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c
index 1423b4b047..bdedd12ce2 100644
--- a/src/backend/access/gist/gistbuildbuffers.c
+++ b/src/backend/access/gist/gistbuildbuffers.c
@@ -187,11 +187,11 @@ gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb)
 	GISTNodeBufferPage *pageBuffer;
 
 	pageBuffer = (GISTNodeBufferPage *) MemoryContextAllocZero(gfbb->context,
-															   BLCKSZ);
+															   cluster_block_size);
 	pageBuffer->prev = InvalidBlockNumber;
 
 	/* Set page free space */
-	PAGE_FREE_SPACE(pageBuffer) = BLCKSZ - BUFFER_PAGE_DATA_OFFSET;
+	PAGE_FREE_SPACE(pageBuffer) = cluster_block_size - BUFFER_PAGE_DATA_OFFSET;
 	return pageBuffer;
 }
 
@@ -379,7 +379,7 @@ gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer,
 		 * the new page by storing its block number in the prev-link.
 		 */
 		PAGE_FREE_SPACE(nodeBuffer->pageBuffer) =
-			BLCKSZ - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
+			cluster_block_size - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
 		nodeBuffer->pageBuffer->prev = blkno;
 
 		/* We've just added one more page */
@@ -755,7 +755,7 @@ ReadTempFileBlock(BufFile *file, long blknum, void *ptr)
 {
 	if (BufFileSeekBlock(file, blknum) != 0)
 		elog(ERROR, "could not seek to block %ld in temporary file", blknum);
-	BufFileReadExact(file, ptr, BLCKSZ);
+	BufFileReadExact(file, ptr, cluster_block_size);
 }
 
 static void
@@ -763,5 +763,5 @@ WriteTempFileBlock(BufFile *file, long blknum, const void *ptr)
 {
 	if (BufFileSeekBlock(file, blknum) != 0)
 		elog(ERROR, "could not seek to block %ld in temporary file", blknum);
-	BufFileWrite(file, ptr, BLCKSZ);
+	BufFileWrite(file, ptr, cluster_block_size);
 }
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index b6bc8c2c56..ae9ab2931c 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, cluster_block_size, sizeof(GISTPageOpaqueData));
 
 	opaque = GistPageGetOpaque(page);
 	opaque->rightlink = InvalidBlockNumber;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index fc5d97f606..ea78dc81c2 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -152,7 +152,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * one page.  Also, "initial index size" accounting does not include the
 	 * metapage, nor the first bitmap page.
 	 */
-	sort_threshold = (maintenance_work_mem * 1024L) / BLCKSZ;
+	sort_threshold = (maintenance_work_mem * 1024L) / cluster_block_size;
 	if (index->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
 		sort_threshold = Min(sort_threshold, NBuffers);
 	else
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 0c6e79f1bd..2a77d13feb 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -1012,7 +1012,7 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
 	 * _hash_freeovflpage for similar usage.  We take care to make the special
 	 * space valid for the benefit of tools such as pageinspect.
 	 */
-	_hash_pageinit(page, BLCKSZ);
+	_hash_pageinit(page, cluster_block_size);
 
 	ovflopaque = HashPageGetOpaque(page);
 
diff --git a/src/backend/access/heap/README.HOT b/src/backend/access/heap/README.HOT
index 74e407f375..25995e7b3b 100644
--- a/src/backend/access/heap/README.HOT
+++ b/src/backend/access/heap/README.HOT
@@ -254,7 +254,7 @@ large enough to accept any extra maintenance burden for.
 The currently planned heuristic is to prune and defrag when first accessing
 a page that potentially has prunable tuples (as flagged by the pd_prune_xid
 page hint field) and that either has free space less than MAX(fillfactor
-target free space, BLCKSZ/10) *or* has recently had an UPDATE fail to
+target free space, cluster_block_size/10) *or* has recently had an UPDATE fail to
 find enough free space to store an updated tuple version.  (These rules
 are subject to change.)
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6a66214a58..ef3f3e0bdf 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2048,7 +2048,7 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
 		Assert(!HeapTupleHasExternal(tup));
 		return tup;
 	}
-	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
+	else if (HeapTupleHasExternal(tup) || tup->t_len > toast_tuple_threshold)
 		return heap_toast_insert_or_update(relation, tup, NULL, options);
 	else
 		return tup;
@@ -2062,7 +2062,7 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
 static int
 heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveFreeSpace)
 {
-	size_t		page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+	size_t		page_avail = cluster_block_size - SizeOfPageHeaderData - saveFreeSpace;
 	int			npages = 1;
 
 	for (int i = done; i < ntuples; i++)
@@ -2072,7 +2072,7 @@ heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveF
 		if (page_avail < tup_sz)
 		{
 			npages++;
-			page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+			page_avail = cluster_block_size - SizeOfPageHeaderData - saveFreeSpace;
 		}
 		page_avail -= tup_sz;
 	}
@@ -2333,7 +2333,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
 				scratchptr += datalen;
 			}
 			totaldatalen = scratchptr - tupledata;
-			Assert((scratchptr - scratch.data) < BLCKSZ);
+			Assert((scratchptr - scratch.data) < cluster_block_size);
 
 			if (need_tuple_data)
 				xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
@@ -3490,7 +3490,7 @@ l2:
 	else
 		need_toast = (HeapTupleHasExternal(&oldtup) ||
 					  HeapTupleHasExternal(newtup) ||
-					  newtup->t_len > TOAST_TUPLE_THRESHOLD);
+					  newtup->t_len > toast_tuple_threshold);
 
 	pagefree = PageGetHeapFreeSpace(page);
 
@@ -7990,7 +7990,7 @@ index_delete_sort(TM_IndexDeleteOp *delstate)
 	 * Shellsort gap sequence (taken from Sedgewick-Incerpi paper).
 	 *
 	 * This implementation is fast with array sizes up to ~4500.  This covers
-	 * all supported BLCKSZ values.
+	 * all supported cluster_block_size values.
 	 */
 	const int	gaps[9] = {1968, 861, 336, 112, 48, 21, 7, 3, 1};
 
@@ -9020,7 +9020,7 @@ heap_xlog_visible(XLogReaderState *record)
 
 		/* initialize the page if it was read as zeros */
 		if (PageIsNew(vmpage))
-			PageInit(vmpage, BLCKSZ, 0);
+			PageInit(vmpage, cluster_block_size, 0);
 
 		/* remove VISIBILITYMAP_XLOG_* */
 		vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS;
@@ -9325,7 +9325,7 @@ heap_xlog_insert(XLogReaderState *record)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
+	if (action == BLK_NEEDS_REDO && freespace < cluster_block_size / 5)
 		XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
 }
 
@@ -9472,7 +9472,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
+	if (action == BLK_NEEDS_REDO && freespace < cluster_block_size / 5)
 		XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
 }
 
@@ -9747,7 +9747,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
+	if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < cluster_block_size / 5)
 		XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
 }
 
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 5a17112c91..6857b46203 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -2030,7 +2030,7 @@ heapam_scan_get_blocks_done(HeapScanDesc hscan)
 /*
  * Check to see whether the table needs a TOAST table.  It does only if
  * (1) there are any toastable attributes, and (2) the maximum length
- * of a tuple could exceed TOAST_TUPLE_THRESHOLD.  (We don't want to
+ * of a tuple could exceed toast_tuple_threshold.  (We don't want to
  * create a toast table for something like "f1 varchar(20)".)
  */
 static bool
@@ -2075,7 +2075,7 @@ heapam_relation_needs_toast_table(Relation rel)
 	tuple_length = MAXALIGN(SizeofHeapTupleHeader +
 							BITMAPLEN(tupdesc->natts)) +
 		MAXALIGN(data_length);
-	return (tuple_length > TOAST_TUPLE_THRESHOLD);
+	return (tuple_length > toast_tuple_threshold);
 }
 
 /*
@@ -2095,8 +2095,8 @@ heapam_relation_toast_am(Relation rel)
 
 #define HEAP_OVERHEAD_BYTES_PER_TUPLE \
 	(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
-#define HEAP_USABLE_BYTES_PER_PAGE \
-	(BLCKSZ - SizeOfPageHeaderData)
+#define heap_usable_bytes_per_page \
+	(cluster_block_size - SizeOfPageHeaderData)
 
 static void
 heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
@@ -2106,7 +2106,7 @@ heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
 	table_block_relation_estimate_size(rel, attr_widths, pages,
 									   tuples, allvisfrac,
 									   HEAP_OVERHEAD_BYTES_PER_TUPLE,
-									   HEAP_USABLE_BYTES_PER_PAGE);
+									   heap_usable_bytes_per_page);
 }
 
 
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 52ecd45654..98b0a701e5 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -174,7 +174,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		hoff += BITMAPLEN(numAttrs);
 	hoff = MAXALIGN(hoff);
 	/* now convert to a limit on the tuple data size */
-	maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
+	maxDataLen = RelationGetToastTupleTarget(rel, cluster_toast_tuple_target) - hoff;
 
 	/*
 	 * Look for attributes with attstorage EXTENDED to compress.  Also find
@@ -255,7 +255,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 	 * increase the target tuple size, so that MAIN attributes aren't stored
 	 * externally unless really necessary.
 	 */
-	maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
+	maxDataLen = toast_tuple_target_main - hoff;
 
 	while (heap_compute_data_size(tupleDesc,
 								  toast_values, toast_isnull) > maxDataLen &&
@@ -634,7 +634,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 	SysScanDesc toastscan;
 	HeapTuple	ttup;
 	int32		expectedchunk;
-	int32		totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
+	int32		totalchunks = ((attrsize - 1) / cluster_toast_max_chunk_size) + 1;
 	int			startchunk;
 	int			endchunk;
 	int			num_indexes;
@@ -647,8 +647,8 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 									&toastidxs,
 									&num_indexes);
 
-	startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
-	endchunk = (sliceoffset + slicelength - 1) / TOAST_MAX_CHUNK_SIZE;
+	startchunk = sliceoffset / cluster_toast_max_chunk_size;
+	endchunk = (sliceoffset + slicelength - 1) / cluster_toast_max_chunk_size;
 	Assert(endchunk <= totalchunks);
 
 	/* Set up a scan key to fetch from the index. */
@@ -749,8 +749,8 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 									 curchunk,
 									 startchunk, endchunk, valueid,
 									 RelationGetRelationName(toastrel))));
-		expected_size = curchunk < totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
-			: attrsize - ((totalchunks - 1) * TOAST_MAX_CHUNK_SIZE);
+		expected_size = curchunk < totalchunks - 1 ? cluster_toast_max_chunk_size
+			: attrsize - ((totalchunks - 1) * cluster_toast_max_chunk_size);
 		if (chunksize != expected_size)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
@@ -765,12 +765,12 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 		chcpystrt = 0;
 		chcpyend = chunksize - 1;
 		if (curchunk == startchunk)
-			chcpystrt = sliceoffset % TOAST_MAX_CHUNK_SIZE;
+			chcpystrt = sliceoffset % cluster_toast_max_chunk_size;
 		if (curchunk == endchunk)
-			chcpyend = (sliceoffset + slicelength - 1) % TOAST_MAX_CHUNK_SIZE;
+			chcpyend = (sliceoffset + slicelength - 1) % cluster_toast_max_chunk_size;
 
 		memcpy(VARDATA(result) +
-			   (curchunk * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
+			   (curchunk * cluster_toast_max_chunk_size - sliceoffset) + chcpystrt,
 			   chunkdata + chcpystrt,
 			   (chcpyend - chcpystrt) + 1);
 
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 47b9e20915..8b8b9c4d79 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -187,7 +187,7 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
 	 */
 	minfree = RelationGetTargetPageFreeSpace(relation,
 											 HEAP_DEFAULT_FILLFACTOR);
-	minfree = Max(minfree, BLCKSZ / 10);
+	minfree = Max(minfree, cluster_block_size / 10);
 
 	if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
 	{
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 424958912c..3402625d6d 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -255,7 +255,7 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm
 
 	state->rs_old_rel = old_heap;
 	state->rs_new_rel = new_heap;
-	state->rs_buffer = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	state->rs_buffer = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 	/* new_heap needn't be empty, just locked */
 	state->rs_blockno = RelationGetNumberOfBlocks(new_heap);
 	state->rs_buffer_valid = false;
@@ -631,7 +631,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 		Assert(!HeapTupleHasExternal(tup));
 		heaptup = tup;
 	}
-	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
+	else if (HeapTupleHasExternal(tup) || tup->t_len > toast_tuple_threshold)
 	{
 		int			options = HEAP_INSERT_SKIP_FSM;
 
@@ -702,7 +702,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 	if (!state->rs_buffer_valid)
 	{
 		/* Initialize a new empty page */
-		PageInit(page, BLCKSZ, 0);
+		PageInit(page, cluster_block_size, 0);
 		state->rs_buffer_valid = true;
 	}
 
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 6a41ee635d..d6f46ad60e 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -97,8 +97,8 @@
  * Perform a failsafe check each time we scan another 4GB of pages.
  * (Note that this is deliberately kept to a power-of-two, usually 2^19.)
  */
-#define FAILSAFE_EVERY_PAGES \
-	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / BLCKSZ))
+#define failsafe_every_pages \
+	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / cluster_block_size))
 
 /*
  * When a table has no indexes, vacuum the FSM after every 8GB, approximately
@@ -106,8 +106,8 @@
  * that has some removable tuples).  When there are indexes, this is ignored,
  * and we vacuum FSM after each index/heap cleaning pass.
  */
-#define VACUUM_FSM_EVERY_PAGES \
-	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / BLCKSZ))
+#define vacuum_fsm_every_pages \
+	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / cluster_block_size))
 
 /*
  * Before we consider skipping a page that's marked as clean in
@@ -749,9 +749,9 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
 			}
 			if (secs_dur > 0 || usecs_dur > 0)
 			{
-				read_rate = (double) BLCKSZ * PageMissOp / (1024 * 1024) /
+				read_rate = (double) cluster_block_size * PageMissOp / (1024 * 1024) /
 					(secs_dur + usecs_dur / 1000000.0);
-				write_rate = (double) BLCKSZ * PageDirtyOp / (1024 * 1024) /
+				write_rate = (double) cluster_block_size * PageDirtyOp / (1024 * 1024) /
 					(secs_dur + usecs_dur / 1000000.0);
 			}
 			appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
@@ -900,7 +900,7 @@ lazy_scan_heap(LVRelState *vacrel)
 		 * one-pass strategy, and the two-pass strategy with the index_cleanup
 		 * param set to 'off'.
 		 */
-		if (vacrel->scanned_pages % FAILSAFE_EVERY_PAGES == 0)
+		if (vacrel->scanned_pages % failsafe_every_pages == 0)
 			lazy_check_wraparound_failsafe(vacrel);
 
 		/*
@@ -1051,7 +1051,7 @@ lazy_scan_heap(LVRelState *vacrel)
 				 * space visible on upper FSM pages.  Note we have not yet
 				 * performed FSM processing for blkno.
 				 */
-				if (blkno - next_fsm_block_to_vacuum >= VACUUM_FSM_EVERY_PAGES)
+				if (blkno - next_fsm_block_to_vacuum >= vacuum_fsm_every_pages)
 				{
 					FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum,
 											blkno);
@@ -1439,7 +1439,7 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno,
 
 		if (GetRecordedFreeSpace(vacrel->rel, blkno) == 0)
 		{
-			freespace = BLCKSZ - SizeOfPageHeaderData;
+			freespace = cluster_block_size - SizeOfPageHeaderData;
 
 			RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
 		}
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 2e18cd88bc..833774c164 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -105,7 +105,7 @@
  * extra headers, so the whole page minus the standard page header is
  * used for the bitmap.
  */
-#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData))
+#define MAPSIZE (cluster_block_size - MAXALIGN(SizeOfPageHeaderData))
 
 /* Number of heap blocks we can represent in one byte */
 #define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
@@ -613,7 +613,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), cluster_block_size, 0);
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	}
 	return buf;
diff --git a/src/backend/access/nbtree/nbtdedup.c b/src/backend/access/nbtree/nbtdedup.c
index d4db0b28f2..6d1b02b17c 100644
--- a/src/backend/access/nbtree/nbtdedup.c
+++ b/src/backend/access/nbtree/nbtdedup.c
@@ -324,7 +324,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 	state = (BTDedupState) palloc(sizeof(BTDedupStateData));
 	state->deduplicate = true;
 	state->nmaxitems = 0;
-	state->maxpostingsize = BLCKSZ; /* We're not really deduplicating */
+	state->maxpostingsize = cluster_block_size; /* We're not really deduplicating */
 	state->base = NULL;
 	state->baseoff = InvalidOffsetNumber;
 	state->basetupsize = 0;
@@ -353,7 +353,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 	delstate.irel = rel;
 	delstate.iblknum = BufferGetBlockNumber(buf);
 	delstate.bottomup = true;
-	delstate.bottomupfreespace = Max(BLCKSZ / 16, newitemsz);
+	delstate.bottomupfreespace = Max(cluster_block_size / 16, newitemsz);
 	delstate.ndeltids = 0;
 	delstate.deltids = palloc(MaxTIDsPerBTreePage * sizeof(TM_IndexDelete));
 	delstate.status = palloc(MaxTIDsPerBTreePage * sizeof(TM_IndexStatus));
@@ -417,7 +417,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 		return true;
 
 	/* Don't dedup when we won't end up back here any time soon anyway */
-	return PageGetExactFreeSpace(page) >= Max(BLCKSZ / 24, newitemsz);
+	return PageGetExactFreeSpace(page) >= Max(cluster_block_size / 24, newitemsz);
 }
 
 /*
@@ -597,7 +597,7 @@ _bt_dedup_finish_pending(Page newpage, BTDedupState state)
 		spacesaving = state->phystupsize - (tuplesz + sizeof(ItemIdData));
 		/* Increment nintervals, since we wrote a new posting list tuple */
 		state->nintervals++;
-		Assert(spacesaving > 0 && spacesaving < BLCKSZ);
+		Assert(spacesaving > 0 && spacesaving < cluster_block_size);
 	}
 
 	/* Reset state for next pending posting list */
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 6558aea42b..58dcdf6de4 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -70,7 +70,7 @@ _bt_initmetapage(Page page, BlockNumber rootbknum, uint32 level,
 	BTMetaPageData *metad;
 	BTPageOpaque metaopaque;
 
-	_bt_pageinit(page, BLCKSZ);
+	_bt_pageinit(page, cluster_block_size);
 
 	metad = BTPageGetMeta(page);
 	metad->btm_magic = BTREE_MAGIC;
@@ -977,7 +977,7 @@ _bt_allocbuf(Relation rel, Relation heaprel)
 	 */
 	buf = ExtendBufferedRel(BMR_REL(rel), MAIN_FORKNUM, NULL, EB_LOCK_FIRST);
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	/* Initialize the new page before returning it */
 	page = BufferGetPage(buf);
@@ -1060,7 +1060,7 @@ _bt_lockbuf(Relation rel, Buffer buf, int access)
 	 * lock/pin held, though.
 	 */
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), cluster_block_size);
 }
 
 /*
@@ -1073,13 +1073,13 @@ _bt_unlockbuf(Relation rel, Buffer buf)
 	 * Buffer is pinned and locked, which means that it is expected to be
 	 * defined and addressable.  Check that proactively.
 	 */
-	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), BLCKSZ);
+	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	/* LockBuffer() asserts that pin is held by this backend */
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_NOACCESS(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_NOACCESS(BufferGetPage(buf), cluster_block_size);
 }
 
 /*
@@ -1097,7 +1097,7 @@ _bt_conditionallockbuf(Relation rel, Buffer buf)
 		return false;
 
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	return true;
 }
@@ -1112,7 +1112,7 @@ _bt_upgradelockbufcleanup(Relation rel, Buffer buf)
 	 * Buffer is pinned and locked, which means that it is expected to be
 	 * defined and addressable.  Check that proactively.
 	 */
-	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), BLCKSZ);
+	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	/* LockBuffer() asserts that pin is held by this backend */
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 62bc9917f1..cb2757f68d 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -427,8 +427,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
 	 */
 	if (scan->xs_want_itup && so->currTuples == NULL)
 	{
-		so->currTuples = (char *) palloc(BLCKSZ * 2);
-		so->markTuples = so->currTuples + BLCKSZ;
+		so->currTuples = (char *) palloc(cluster_block_size * 2);
+		so->markTuples = so->currTuples + cluster_block_size;
 	}
 
 	/*
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index c2665fce41..b701e492fe 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -619,10 +619,10 @@ _bt_blnewpage(uint32 level)
 	Page		page;
 	BTPageOpaque opaque;
 
-	page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	page = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 
 	/* Zero the page and set up standard page header info */
-	_bt_pageinit(page, BLCKSZ);
+	_bt_pageinit(page, cluster_block_size);
 
 	/* Initialize BT opaque state */
 	opaque = BTPageGetOpaque(page);
@@ -660,7 +660,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
 	while (blkno > wstate->btws_pages_written)
 	{
 		if (!wstate->btws_zeropage)
-			wstate->btws_zeropage = (Page) palloc_aligned(BLCKSZ,
+			wstate->btws_zeropage = (Page) palloc_aligned(cluster_block_size,
 														  PG_IO_ALIGN_SIZE,
 														  MCXT_ALLOC_ZERO);
 		/* don't set checksum for all-zero page */
@@ -715,7 +715,7 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
 	state->btps_level = level;
 	/* set "full" threshold based on level.  See notes at head of file. */
 	if (level > 0)
-		state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
+		state->btps_full = (cluster_block_size * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
 	else
 		state->btps_full = BTGetTargetPageFreeSpace(wstate->index);
 
@@ -1172,7 +1172,7 @@ _bt_uppershutdown(BTWriteState *wstate, BTPageState *state)
 	 * set to point to "P_NONE").  This changes the index to the "valid" state
 	 * by filling in a valid magic number in the metapage.
 	 */
-	metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	metapage = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 	_bt_initmetapage(metapage, rootblkno, rootlevel,
 					 wstate->inskey->allequalimage);
 	_bt_blwritepage(wstate, metapage, BTREE_METAPAGE);
@@ -1350,7 +1350,7 @@ _bt_load(BTWriteState *wstate, BTSpool *btspool, BTSpool *btspool2)
 				 * leaf pages full with few very large tuples doesn't seem
 				 * like a useful goal.)
 				 */
-				dstate->maxpostingsize = MAXALIGN_DOWN((BLCKSZ * 10 / 100)) -
+				dstate->maxpostingsize = MAXALIGN_DOWN((cluster_block_size * 10 / 100)) -
 					sizeof(ItemIdData);
 				Assert(dstate->maxpostingsize <= BTMaxItemSize(state->btps_page) &&
 					   dstate->maxpostingsize <= INDEX_SIZE_MASK);
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index f390c177e4..b9dd2b7e5d 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -271,7 +271,7 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 									 "" : " for WAL verification",
 									 XLogRecGetBlock(record, block_id)->hole_offset,
 									 XLogRecGetBlock(record, block_id)->hole_length,
-									 BLCKSZ -
+									 cluster_block_size -
 									 XLogRecGetBlock(record, block_id)->hole_length -
 									 XLogRecGetBlock(record, block_id)->bimg_len,
 									 method);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 3554edcc9a..d8c2752e85 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -341,8 +341,8 @@ checkSplitConditions(Relation index, SpGistState *state,
 	if (SpGistBlockIsRoot(current->blkno))
 	{
 		/* return impossible values to force split */
-		*nToSplit = BLCKSZ;
-		return BLCKSZ;
+		*nToSplit = cluster_block_size;
+		return cluster_block_size;
 	}
 
 	i = current->offnum;
@@ -899,7 +899,7 @@ doPickSplit(Relation index, SpGistState *state,
 	 * fit on one page.
 	 */
 	allTheSame = checkAllTheSame(&in, &out,
-								 totalLeafSizes > SPGIST_PAGE_CAPACITY,
+								 totalLeafSizes > spgist_page_capacity,
 								 &includeNew);
 
 	/*
@@ -1030,7 +1030,7 @@ doPickSplit(Relation index, SpGistState *state,
 		for (i = 0; i < nToInsert; i++)
 			leafPageSelect[i] = 0;	/* signifies current page */
 	}
-	else if (in.nTuples == 1 && totalLeafSizes > SPGIST_PAGE_CAPACITY)
+	else if (in.nTuples == 1 && totalLeafSizes > spgist_page_capacity)
 	{
 		/*
 		 * We're trying to split up a long value by repeated suffixing, but
@@ -1051,7 +1051,7 @@ doPickSplit(Relation index, SpGistState *state,
 		newLeafBuffer = SpGistGetBuffer(index,
 										GBUF_LEAF | (isNulls ? GBUF_NULLS : 0),
 										Min(totalLeafSizes,
-											SPGIST_PAGE_CAPACITY),
+											spgist_page_capacity),
 										&xlrec.initDest);
 
 		/*
@@ -1995,13 +1995,13 @@ spgdoinsert(Relation index, SpGistState *state,
 	 * If it isn't gonna fit, and the opclass can't reduce the datum size by
 	 * suffixing, bail out now rather than doing a lot of useless work.
 	 */
-	if (leafSize > SPGIST_PAGE_CAPACITY &&
+	if (leafSize > spgist_page_capacity &&
 		(isnull || !state->config.longValuesOK))
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
 						leafSize - sizeof(ItemIdData),
-						SPGIST_PAGE_CAPACITY - sizeof(ItemIdData),
+						spgist_page_capacity - sizeof(ItemIdData),
 						RelationGetRelationName(index)),
 				 errhint("Values larger than a buffer page cannot be indexed.")));
 	bestLeafSize = leafSize;
@@ -2059,7 +2059,7 @@ spgdoinsert(Relation index, SpGistState *state,
 			current.buffer =
 				SpGistGetBuffer(index,
 								GBUF_LEAF | (isnull ? GBUF_NULLS : 0),
-								Min(leafSize, SPGIST_PAGE_CAPACITY),
+								Min(leafSize, spgist_page_capacity),
 								&isNew);
 			current.blkno = BufferGetBlockNumber(current.buffer);
 		}
@@ -2122,9 +2122,9 @@ spgdoinsert(Relation index, SpGistState *state,
 			}
 			else if ((sizeToSplit =
 					  checkSplitConditions(index, state, &current,
-										   &nToSplit)) < SPGIST_PAGE_CAPACITY / 2 &&
+										   &nToSplit)) < spgist_page_capacity / 2 &&
 					 nToSplit < 64 &&
-					 leafTuple->size + sizeof(ItemIdData) + sizeToSplit <= SPGIST_PAGE_CAPACITY)
+					 leafTuple->size + sizeof(ItemIdData) + sizeToSplit <= spgist_page_capacity)
 			{
 				/*
 				 * the amount of data is pretty small, so just move the whole
@@ -2258,7 +2258,7 @@ spgdoinsert(Relation index, SpGistState *state,
 					 * than MAXALIGN, to accommodate opclasses that trim one
 					 * byte from the leaf datum per pass.)
 					 */
-					if (leafSize > SPGIST_PAGE_CAPACITY)
+					if (leafSize > spgist_page_capacity)
 					{
 						bool		ok = false;
 
@@ -2278,7 +2278,7 @@ spgdoinsert(Relation index, SpGistState *state,
 									(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 									 errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
 											leafSize - sizeof(ItemIdData),
-											SPGIST_PAGE_CAPACITY - sizeof(ItemIdData),
+											spgist_page_capacity - sizeof(ItemIdData),
 											RelationGetRelationName(index)),
 									 errhint("Values larger than a buffer page cannot be indexed.")));
 					}
diff --git a/src/backend/access/spgist/spgtextproc.c b/src/backend/access/spgist/spgtextproc.c
index 03a7afdbab..0b247d4789 100644
--- a/src/backend/access/spgist/spgtextproc.c
+++ b/src/backend/access/spgist/spgtextproc.c
@@ -53,20 +53,20 @@
  * In the worst case, an inner tuple in a text radix tree could have as many
  * as 258 nodes (one for each possible byte value, plus the two special
  * cases).  Each node can take 16 bytes on MAXALIGN=8 machines.  The inner
- * tuple must fit on an index page of size BLCKSZ.  Rather than assuming we
+ * tuple must fit on an index page of size cluster_block_size.  Rather than assuming we
  * know the exact amount of overhead imposed by page headers, tuple headers,
  * etc, we leave 100 bytes for that (the actual overhead should be no more
  * than 56 bytes at this writing, so there is slop in this number).
- * So we can safely create prefixes up to BLCKSZ - 258 * 16 - 100 bytes long.
+ * So we can safely create prefixes up to cluster_block_size - 258 * 16 - 100 bytes long.
  * Unfortunately, because 258 * 16 is over 4K, there is no safe prefix length
- * when BLCKSZ is less than 8K; it is always possible to get "SPGiST inner
+ * when cluster_block_size is less than 8K; it is always possible to get "SPGiST inner
  * tuple size exceeds maximum" if there are too many distinct next-byte values
  * at a given place in the tree.  Since use of nonstandard block sizes appears
  * to be negligible in the field, we just live with that fact for now,
- * choosing a max prefix size of 32 bytes when BLCKSZ is configured smaller
+ * choosing a max prefix size of 32 bytes when cluster_block_size is configured smaller
  * than default.
  */
-#define SPGIST_MAX_PREFIX_LENGTH	Max((int) (BLCKSZ - 258 * 16 - 100), 32)
+#define SPGIST_MAX_PREFIX_LENGTH	Max((int) (cluster_block_size - 258 * 16 - 100), 32)
 
 /*
  * Strategy for collation aware operator on text is equal to btree strategy
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 8f32e46fb8..6dfd875bc9 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -544,7 +544,7 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
 	SpGistLastUsedPage *lup;
 
 	/* Bail out if even an empty page wouldn't meet the demand */
-	if (needSpace > SPGIST_PAGE_CAPACITY)
+	if (needSpace > spgist_page_capacity)
 		elog(ERROR, "desired SPGiST tuple size is too big");
 
 	/*
@@ -555,7 +555,7 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
 	 * error for requests that would otherwise be legal.
 	 */
 	needSpace += SpGistGetTargetPageFreeSpace(index);
-	needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
+	needSpace = Min(needSpace, spgist_page_capacity);
 
 	/* Get the cache entry for this flags setting */
 	lup = GET_LUP(cache, flags);
@@ -681,7 +681,7 @@ SpGistInitPage(Page page, uint16 f)
 {
 	SpGistPageOpaque opaque;
 
-	PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
+	PageInit(page, cluster_block_size, sizeof(SpGistPageOpaqueData));
 	opaque = SpGistPageGetOpaque(page);
 	opaque->flags = f;
 	opaque->spgist_page_id = SPGIST_PAGE_ID;
@@ -693,7 +693,7 @@ SpGistInitPage(Page page, uint16 f)
 void
 SpGistInitBuffer(Buffer b, uint16 f)
 {
-	Assert(BufferGetPageSize(b) == BLCKSZ);
+	Assert(BufferGetPageSize(b) == cluster_block_size);
 	SpGistInitPage(BufferGetPage(b), f);
 }
 
@@ -1002,12 +1002,12 @@ spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix,
 	/*
 	 * Inner tuple should be small enough to fit on a page
 	 */
-	if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
+	if (size > spgist_page_capacity - sizeof(ItemIdData))
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
 						(Size) size,
-						SPGIST_PAGE_CAPACITY - sizeof(ItemIdData)),
+						spgist_page_capacity - sizeof(ItemIdData)),
 				 errhint("Values larger than a buffer page cannot be indexed.")));
 
 	/*
diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
index c6bdb7e1c6..6ca676143c 100644
--- a/src/backend/access/table/tableam.c
+++ b/src/backend/access/table/tableam.c
@@ -636,7 +636,7 @@ table_block_relation_size(Relation rel, ForkNumber forkNumber)
 	else
 		nblocks = smgrnblocks(RelationGetSmgr(rel), forkNumber);
 
-	return nblocks * BLCKSZ;
+	return nblocks * cluster_block_size;
 }
 
 /*
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index 22c8ae9755..1773ed1da5 100644
--- a/src/backend/access/transam/README
+++ b/src/backend/access/transam/README
@@ -851,7 +851,7 @@ we won't be able to hint its outputs until the second xact is sync'd, up to
 three walwriter cycles later.  This argues for keeping N (the group size)
 as small as possible.  For the moment we are setting the group size to 32,
 which makes the LSN cache space the same size as the actual clog buffer
-space (independently of BLCKSZ).
+space (independently of cluster_block_size).
 
 It is useful that we can run both synchronous and asynchronous commit
 transactions concurrently, but the safety of this is perhaps not
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 4a431d5876..fc9118487d 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -45,7 +45,7 @@
 #include "storage/sync.h"
 
 /*
- * Defines for CLOG page sizes.  A page is the same BLCKSZ as is used
+ * Defines for CLOG page sizes.  A page is the same cluster_block_size as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
@@ -59,7 +59,7 @@
 /* We need two bits per xact, so four xacts fit in a byte */
 #define CLOG_BITS_PER_XACT	2
 #define CLOG_XACTS_PER_BYTE 4
-#define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
+#define CLOG_XACTS_PER_PAGE (cluster_block_size * CLOG_XACTS_PER_BYTE)
 #define CLOG_XACT_BITMASK	((1 << CLOG_BITS_PER_XACT) - 1)
 
 #define TransactionIdToPage(xid)	((xid) / (TransactionId) CLOG_XACTS_PER_PAGE)
@@ -802,7 +802,7 @@ TrimCLOG(void)
 		/* Zero so-far-unused positions in the current byte */
 		*byteptr &= (1 << bshift) - 1;
 		/* Zero the rest of the page */
-		MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
+		MemSet(byteptr + 1, 0, cluster_block_size - byteno - 1);
 
 		XactCtl->shared->page_dirty[slotno] = true;
 	}
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index b897fabc70..e9fb797c82 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -37,13 +37,13 @@
 #include "utils/timestamp.h"
 
 /*
- * Defines for CommitTs page sizes.  A page is the same BLCKSZ as is used
+ * Defines for CommitTs page sizes.  A page is the same cluster_block_size as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
  * CommitTs page numbering also wraps around at
- * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE, and CommitTs segment numbering at
- * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need take no
+ * 0xFFFFFFFF/commit_ts_xacts_per_page, and CommitTs segment numbering at
+ * 0xFFFFFFFF/commit_ts_xacts_per_page/SLRU_PAGES_PER_SEGMENT.  We need take no
  * explicit notice of that fact in this module, except when comparing segment
  * and page numbers in TruncateCommitTs (see CommitTsPagePrecedes).
  */
@@ -62,13 +62,13 @@ typedef struct CommitTimestampEntry
 #define SizeOfCommitTimestampEntry (offsetof(CommitTimestampEntry, nodeid) + \
 									sizeof(RepOriginId))
 
-#define COMMIT_TS_XACTS_PER_PAGE \
-	(BLCKSZ / SizeOfCommitTimestampEntry)
+#define commit_ts_xacts_per_page \
+	(cluster_block_size / SizeOfCommitTimestampEntry)
 
 #define TransactionIdToCTsPage(xid) \
-	((xid) / (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
+	((xid) / (TransactionId) commit_ts_xacts_per_page)
 #define TransactionIdToCTsEntry(xid)	\
-	((xid) % (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
+	((xid) % (TransactionId) commit_ts_xacts_per_page)
 
 /*
  * Link to shared-memory data structures for CommitTs control
@@ -524,7 +524,7 @@ CommitTsShmemInit(void)
 				  CommitTsSLRULock, "pg_commit_ts",
 				  LWTRANCHE_COMMITTS_BUFFER,
 				  SYNC_HANDLER_COMMIT_TS);
-	SlruPagePrecedesUnitTests(CommitTsCtl, COMMIT_TS_XACTS_PER_PAGE);
+	SlruPagePrecedesUnitTests(CommitTsCtl, commit_ts_xacts_per_page);
 
 	commitTsShared = ShmemInitStruct("CommitTs shared",
 									 sizeof(CommitTimestampShared),
@@ -898,7 +898,7 @@ AdvanceOldestCommitTsXid(TransactionId oldestXact)
  * Decide whether a commitTS page number is "older" for truncation purposes.
  * Analogous to CLOGPagePrecedes().
  *
- * At default BLCKSZ, (1 << 31) % COMMIT_TS_XACTS_PER_PAGE == 128.  This
+ * At default cluster_block_size, (1 << 31) % commit_ts_xacts_per_page == 128.  This
  * introduces differences compared to CLOG and the other SLRUs having (1 <<
  * 31) % per_page == 0.  This function never tests exactly
  * TransactionIdPrecedes(x-2^31, x).  When the system reaches xidStopLimit,
@@ -923,13 +923,13 @@ CommitTsPagePrecedes(int page1, int page2)
 	TransactionId xid1;
 	TransactionId xid2;
 
-	xid1 = ((TransactionId) page1) * COMMIT_TS_XACTS_PER_PAGE;
+	xid1 = ((TransactionId) page1) * commit_ts_xacts_per_page;
 	xid1 += FirstNormalTransactionId + 1;
-	xid2 = ((TransactionId) page2) * COMMIT_TS_XACTS_PER_PAGE;
+	xid2 = ((TransactionId) page2) * commit_ts_xacts_per_page;
 	xid2 += FirstNormalTransactionId + 1;
 
 	return (TransactionIdPrecedes(xid1, xid2) &&
-			TransactionIdPrecedes(xid1, xid2 + COMMIT_TS_XACTS_PER_PAGE - 1));
+			TransactionIdPrecedes(xid1, xid2 + commit_ts_xacts_per_page - 1));
 }
 
 
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 6c68191ca6..771dde3419 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -45,7 +45,7 @@
  */
 #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
 #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
-#define MAX_DELTA_SIZE			(BLCKSZ + 2 * FRAGMENT_HEADER_SIZE)
+#define MAX_DELTA_SIZE			(cluster_block_size + 2 * FRAGMENT_HEADER_SIZE)
 
 /* Struct of generic xlog data for single page */
 typedef struct
@@ -241,8 +241,8 @@ computeDelta(PageData *pageData, Page curpage, Page targetpage)
 					   0, curLower);
 	/* ... and for upper part, ignoring what's between */
 	computeRegionDelta(pageData, curpage, targetpage,
-					   targetUpper, BLCKSZ,
-					   curUpper, BLCKSZ);
+					   targetUpper, cluster_block_size,
+					   curUpper, cluster_block_size);
 
 	/*
 	 * If xlog debug is enabled, then check produced delta.  Result of delta
@@ -253,11 +253,11 @@ computeDelta(PageData *pageData, Page curpage, Page targetpage)
 	{
 		PGAlignedBlock tmp;
 
-		memcpy(tmp.data, curpage, BLCKSZ);
+		memcpy(tmp.data, curpage, cluster_block_size);
 		applyPageRedo(tmp.data, pageData->delta, pageData->deltaLen);
 		if (memcmp(tmp.data, targetpage, targetLower) != 0 ||
 			memcmp(tmp.data + targetUpper, targetpage + targetUpper,
-				   BLCKSZ - targetUpper) != 0)
+				   cluster_block_size - targetUpper) != 0)
 			elog(ERROR, "result of generic xlog apply does not match");
 	}
 #endif
@@ -311,7 +311,7 @@ GenericXLogRegisterBuffer(GenericXLogState *state, Buffer buffer, int flags)
 			/* Empty slot, so use it (there cannot be a match later) */
 			page->buffer = buffer;
 			page->flags = flags;
-			memcpy(page->image, BufferGetPage(buffer), BLCKSZ);
+			memcpy(page->image, BufferGetPage(buffer), cluster_block_size);
 			return (Page) page->image;
 		}
 		else if (page->buffer == buffer)
@@ -373,7 +373,7 @@ GenericXLogFinish(GenericXLogState *state)
 					   pageHeader->pd_upper - pageHeader->pd_lower);
 				memcpy(page + pageHeader->pd_upper,
 					   pageData->image + pageHeader->pd_upper,
-					   BLCKSZ - pageHeader->pd_upper);
+					   cluster_block_size - pageHeader->pd_upper);
 
 				XLogRegisterBuffer(i, pageData->buffer,
 								   REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
@@ -392,7 +392,7 @@ GenericXLogFinish(GenericXLogState *state)
 					   pageHeader->pd_upper - pageHeader->pd_lower);
 				memcpy(page + pageHeader->pd_upper,
 					   pageData->image + pageHeader->pd_upper,
-					   BLCKSZ - pageHeader->pd_upper);
+					   cluster_block_size - pageHeader->pd_upper);
 
 				XLogRegisterBuffer(i, pageData->buffer, REGBUF_STANDARD);
 				XLogRegisterBufData(i, pageData->delta, pageData->deltaLen);
@@ -426,7 +426,7 @@ GenericXLogFinish(GenericXLogState *state)
 				continue;
 			memcpy(BufferGetPage(pageData->buffer),
 				   pageData->image,
-				   BLCKSZ);
+				   cluster_block_size);
 			/* We don't worry about zeroing the "hole" in this case */
 			MarkBufferDirty(pageData->buffer);
 		}
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index abb022e067..ef135140e3 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -93,25 +93,25 @@
 
 
 /*
- * Defines for MultiXactOffset page sizes.  A page is the same BLCKSZ as is
+ * Defines for MultiXactOffset page sizes.  A page is the same cluster_block_size as is
  * used everywhere else in Postgres.
  *
  * Note: because MultiXactOffsets are 32 bits and wrap around at 0xFFFFFFFF,
  * MultiXact page numbering also wraps around at
- * 0xFFFFFFFF/MULTIXACT_OFFSETS_PER_PAGE, and segment numbering at
- * 0xFFFFFFFF/MULTIXACT_OFFSETS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need
+ * 0xFFFFFFFF/multixact_offsets_per_page, and segment numbering at
+ * 0xFFFFFFFF/multixact_offsets_per_page/SLRU_PAGES_PER_SEGMENT.  We need
  * take no explicit notice of that fact in this module, except when comparing
  * segment and page numbers in TruncateMultiXact (see
  * MultiXactOffsetPagePrecedes).
  */
 
 /* We need four bytes per offset */
-#define MULTIXACT_OFFSETS_PER_PAGE (BLCKSZ / sizeof(MultiXactOffset))
+#define multixact_offsets_per_page (cluster_block_size / sizeof(MultiXactOffset))
 
 #define MultiXactIdToOffsetPage(xid) \
-	((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
+	((xid) / (MultiXactOffset) multixact_offsets_per_page)
 #define MultiXactIdToOffsetEntry(xid) \
-	((xid) % (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
+	((xid) % (MultiXactOffset) multixact_offsets_per_page)
 #define MultiXactIdToOffsetSegment(xid) (MultiXactIdToOffsetPage(xid) / SLRU_PAGES_PER_SEGMENT)
 
 /*
@@ -119,7 +119,7 @@
  * additional flag bits for each TransactionId.  To do this without getting
  * into alignment issues, we store four bytes of flags, and then the
  * corresponding 4 Xids.  Each such 5-word (20-byte) set we call a "group", and
- * are stored as a whole in pages.  Thus, with 8kB BLCKSZ, we keep 409 groups
+ * are stored as a whole in pages.  Thus, with 8kB cluster_block_size, we keep 409 groups
  * per page.  This wastes 12 bytes per page, but that's OK -- simplicity (and
  * performance) trumps space efficiency here.
  *
@@ -138,9 +138,9 @@
 /* size in bytes of a complete group */
 #define MULTIXACT_MEMBERGROUP_SIZE \
 	(sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)
-#define MULTIXACT_MEMBERGROUPS_PER_PAGE (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)
-#define MULTIXACT_MEMBERS_PER_PAGE	\
-	(MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
+#define multixact_membergroups_per_page (cluster_block_size / MULTIXACT_MEMBERGROUP_SIZE)
+#define multixact_members_per_page	\
+	(multixact_membergroups_per_page * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
 
 /*
  * Because the number of items per page is not a divisor of the last item
@@ -153,16 +153,16 @@
  * This constant is the number of members in the last page of the last segment.
  */
 #define MAX_MEMBERS_IN_LAST_MEMBERS_PAGE \
-		((uint32) ((0xFFFFFFFF % MULTIXACT_MEMBERS_PER_PAGE) + 1))
+		((uint32) ((0xFFFFFFFF % multixact_members_per_page) + 1))
 
 /* page in which a member is to be found */
-#define MXOffsetToMemberPage(xid) ((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
+#define MXOffsetToMemberPage(xid) ((xid) / (TransactionId) multixact_members_per_page)
 #define MXOffsetToMemberSegment(xid) (MXOffsetToMemberPage(xid) / SLRU_PAGES_PER_SEGMENT)
 
 /* Location (byte offset within page) of flag word for a given member */
 #define MXOffsetToFlagsOffset(xid) \
 	((((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) % \
-	  (TransactionId) MULTIXACT_MEMBERGROUPS_PER_PAGE) * \
+	  (TransactionId) multixact_membergroups_per_page) * \
 	 (TransactionId) MULTIXACT_MEMBERGROUP_SIZE)
 #define MXOffsetToFlagsBitShift(xid) \
 	(((xid) % (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) * \
@@ -1152,7 +1152,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
 	if (MultiXactState->oldestOffsetKnown &&
 		MultiXactOffsetWouldWrap(MultiXactState->offsetStopLimit,
 								 nextOffset,
-								 nmembers + MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT * OFFSET_WARN_SEGMENTS))
+								 nmembers + multixact_members_per_page * SLRU_PAGES_PER_SEGMENT * OFFSET_WARN_SEGMENTS))
 		ereport(WARNING,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg_plural("database with OID %u must be vacuumed before %d more multixact member is used",
@@ -1855,7 +1855,7 @@ MultiXactShmemInit(void)
 				  MultiXactOffsetSLRULock, "pg_multixact/offsets",
 				  LWTRANCHE_MULTIXACTOFFSET_BUFFER,
 				  SYNC_HANDLER_MULTIXACT_OFFSET);
-	SlruPagePrecedesUnitTests(MultiXactOffsetCtl, MULTIXACT_OFFSETS_PER_PAGE);
+	SlruPagePrecedesUnitTests(MultiXactOffsetCtl, multixact_offsets_per_page);
 	SimpleLruInit(MultiXactMemberCtl,
 				  "MultiXactMember", NUM_MULTIXACTMEMBER_BUFFERS, 0,
 				  MultiXactMemberSLRULock, "pg_multixact/members",
@@ -2072,7 +2072,7 @@ TrimMultiXact(void)
 		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 		offptr += entryno;
 
-		MemSet(offptr, 0, BLCKSZ - (entryno * sizeof(MultiXactOffset)));
+		MemSet(offptr, 0, cluster_block_size - (entryno * sizeof(MultiXactOffset)));
 
 		MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
 	}
@@ -2104,7 +2104,7 @@ TrimMultiXact(void)
 		xidptr = (TransactionId *)
 			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
-		MemSet(xidptr, 0, BLCKSZ - memberoff);
+		MemSet(xidptr, 0, cluster_block_size - memberoff);
 
 		/*
 		 * Note: we don't need to zero out the flag bits in the remaining
@@ -2480,7 +2480,7 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
 			difference = MaxMultiXactOffset - offset + 1;
 		}
 		else
-			difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE;
+			difference = multixact_members_per_page - offset % multixact_members_per_page;
 
 		/*
 		 * Advance to next page, taking care to properly handle the wraparound
@@ -2634,10 +2634,10 @@ SetOffsetVacuumLimit(bool is_startup)
 	{
 		/* move back to start of the corresponding segment */
 		offsetStopLimit = oldestOffset - (oldestOffset %
-										  (MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT));
+										  (multixact_members_per_page * SLRU_PAGES_PER_SEGMENT));
 
 		/* always leave one segment before the wraparound point */
-		offsetStopLimit -= (MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT);
+		offsetStopLimit -= (multixact_members_per_page * SLRU_PAGES_PER_SEGMENT);
 
 		if (!prevOldestOffsetKnown && !is_startup)
 			ereport(LOG,
@@ -2997,7 +2997,7 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
 	 */
 	trunc.earliestExistingPage = -1;
 	SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
-	earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;
+	earliest = trunc.earliestExistingPage * multixact_offsets_per_page;
 	if (earliest < FirstMultiXactId)
 		earliest = FirstMultiXactId;
 
@@ -3118,14 +3118,14 @@ MultiXactOffsetPagePrecedes(int page1, int page2)
 	MultiXactId multi1;
 	MultiXactId multi2;
 
-	multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE;
+	multi1 = ((MultiXactId) page1) * multixact_offsets_per_page;
 	multi1 += FirstMultiXactId + 1;
-	multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE;
+	multi2 = ((MultiXactId) page2) * multixact_offsets_per_page;
 	multi2 += FirstMultiXactId + 1;
 
 	return (MultiXactIdPrecedes(multi1, multi2) &&
 			MultiXactIdPrecedes(multi1,
-								multi2 + MULTIXACT_OFFSETS_PER_PAGE - 1));
+								multi2 + multixact_offsets_per_page - 1));
 }
 
 /*
@@ -3138,12 +3138,12 @@ MultiXactMemberPagePrecedes(int page1, int page2)
 	MultiXactOffset offset1;
 	MultiXactOffset offset2;
 
-	offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE;
-	offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE;
+	offset1 = ((MultiXactOffset) page1) * multixact_members_per_page;
+	offset2 = ((MultiXactOffset) page2) * multixact_members_per_page;
 
 	return (MultiXactOffsetPrecedes(offset1, offset2) &&
 			MultiXactOffsetPrecedes(offset1,
-									offset2 + MULTIXACT_MEMBERS_PER_PAGE - 1));
+									offset2 + multixact_members_per_page - 1));
 }
 
 /*
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 71ac70fb40..675eae6361 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -169,7 +169,7 @@ SimpleLruShmemSize(int nslots, int nlsns)
 	if (nlsns > 0)
 		sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));	/* group_lsn[] */
 
-	return BUFFERALIGN(sz) + BLCKSZ * nslots;
+	return BUFFERALIGN(sz) + cluster_block_size * nslots;
 }
 
 /*
@@ -251,7 +251,7 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
 			shared->page_dirty[slotno] = false;
 			shared->page_lru_count[slotno] = 0;
-			ptr += BLCKSZ;
+			ptr += cluster_block_size;
 		}
 
 		/* Should fit to estimated shmem size */
@@ -297,7 +297,7 @@ SimpleLruZeroPage(SlruCtl ctl, int pageno)
 	SlruRecentlyUsed(shared, slotno);
 
 	/* Set the buffer to zeroes */
-	MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+	MemSet(shared->page_buffer[slotno], 0, cluster_block_size);
 
 	/* Set the LSNs for this new page to zero */
 	SimpleLruZeroLSNs(ctl, slotno);
@@ -628,7 +628,7 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
 {
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	int			offset = rpageno * BLCKSZ;
+	int			offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 	int			fd;
 	bool		result;
@@ -659,7 +659,7 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
 		SlruReportIOError(ctl, pageno, 0);
 	}
 
-	result = endpos >= (off_t) (offset + BLCKSZ);
+	result = endpos >= (off_t) (offset + cluster_block_size);
 
 	if (CloseTransientFile(fd) != 0)
 	{
@@ -687,7 +687,7 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
 	SlruShared	shared = ctl->shared;
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	off_t		offset = rpageno * BLCKSZ;
+	off_t		offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 	int			fd;
 
@@ -713,13 +713,13 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
 		ereport(LOG,
 				(errmsg("file \"%s\" doesn't exist, reading as zeroes",
 						path)));
-		MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+		MemSet(shared->page_buffer[slotno], 0, cluster_block_size);
 		return true;
 	}
 
 	errno = 0;
 	pgstat_report_wait_start(WAIT_EVENT_SLRU_READ);
-	if (pg_pread(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	if (pg_pread(fd, shared->page_buffer[slotno], cluster_block_size, offset) != cluster_block_size)
 	{
 		pgstat_report_wait_end();
 		slru_errcause = SLRU_READ_FAILED;
@@ -759,7 +759,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
 	SlruShared	shared = ctl->shared;
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	off_t		offset = rpageno * BLCKSZ;
+	off_t		offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 	int			fd = -1;
 
@@ -874,7 +874,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
 
 	errno = 0;
 	pgstat_report_wait_start(WAIT_EVENT_SLRU_WRITE);
-	if (pg_pwrite(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	if (pg_pwrite(fd, shared->page_buffer[slotno], cluster_block_size, offset) != cluster_block_size)
 	{
 		pgstat_report_wait_end();
 		/* if write didn't set errno, assume problem is no disk space */
@@ -933,7 +933,7 @@ SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
 {
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	int			offset = rpageno * BLCKSZ;
+	int			offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 
 	SlruFileName(ctl, path, segno);
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index 62bb610167..c7f3246164 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -36,23 +36,23 @@
 
 
 /*
- * Defines for SubTrans page sizes.  A page is the same BLCKSZ as is used
+ * Defines for SubTrans page sizes.  A page is the same cluster_block_size as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
  * SubTrans page numbering also wraps around at
- * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE, and segment numbering at
- * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need take no
+ * 0xFFFFFFFF/subtrans_xacts_per_page, and segment numbering at
+ * 0xFFFFFFFF/subtrans_xacts_per_page/SLRU_PAGES_PER_SEGMENT.  We need take no
  * explicit notice of that fact in this module, except when comparing segment
  * and page numbers in TruncateSUBTRANS (see SubTransPagePrecedes) and zeroing
  * them in StartupSUBTRANS.
  */
 
 /* We need four bytes per xact */
-#define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
+#define subtrans_xacts_per_page (cluster_block_size / sizeof(TransactionId))
 
-#define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
-#define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
+#define TransactionIdToPage(xid) ((xid) / (TransactionId) subtrans_xacts_per_page)
+#define TransactionIdToEntry(xid) ((xid) % (TransactionId) subtrans_xacts_per_page)
 
 
 /*
@@ -194,7 +194,7 @@ SUBTRANSShmemInit(void)
 	SimpleLruInit(SubTransCtl, "Subtrans", NUM_SUBTRANS_BUFFERS, 0,
 				  SubtransSLRULock, "pg_subtrans",
 				  LWTRANCHE_SUBTRANS_BUFFER, SYNC_HANDLER_NONE);
-	SlruPagePrecedesUnitTests(SubTransCtl, SUBTRANS_XACTS_PER_PAGE);
+	SlruPagePrecedesUnitTests(SubTransCtl, subtrans_xacts_per_page);
 }
 
 /*
@@ -364,11 +364,11 @@ SubTransPagePrecedes(int page1, int page2)
 	TransactionId xid1;
 	TransactionId xid2;
 
-	xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
+	xid1 = ((TransactionId) page1) * subtrans_xacts_per_page;
 	xid1 += FirstNormalTransactionId + 1;
-	xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
+	xid2 = ((TransactionId) page2) * subtrans_xacts_per_page;
 	xid2 += FirstNormalTransactionId + 1;
 
 	return (TransactionIdPrecedes(xid1, xid2) &&
-			TransactionIdPrecedes(xid1, xid2 + SUBTRANS_XACTS_PER_PAGE - 1));
+			TransactionIdPrecedes(xid1, xid2 + subtrans_xacts_per_page - 1));
 }
diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c
index 94e152694e..8b4ed3d053 100644
--- a/src/backend/access/transam/timeline.c
+++ b/src/backend/access/transam/timeline.c
@@ -307,7 +307,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
 	char		path[MAXPGPATH];
 	char		tmppath[MAXPGPATH];
 	char		histfname[MAXFNAMELEN];
-	char		buffer[BLCKSZ];
+	char		buffer[cluster_block_size];
 	int			srcfd;
 	int			fd;
 	int			nbytes;
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 334adac09e..ff375874b1 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -370,7 +370,7 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	 * being significant compared to total XID space. (VACUUM requires an XID
 	 * if it truncates at wal_level!=minimal.  "VACUUM (ANALYZE)", which a DBA
 	 * might do by reflex, assigns an XID.  Hence, we had better be sure
-	 * there's lots of XIDs left...)  Also, at default BLCKSZ, this leaves two
+	 * there's lots of XIDs left...)  Also, at default cluster_block_size, this leaves two
 	 * completely-idle segments.  In the event of edge-case bugs involving
 	 * page or segment arithmetic, idle segments render the bugs unreachable
 	 * outside of single-user mode.
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f6f8adc72a..c7f38f7bc6 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -3923,7 +3923,7 @@ WriteControlFile(void)
 	ControlFile->maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile->floatFormat = FLOATFORMAT_VALUE;
 
-	ControlFile->blcksz = BLCKSZ;
+	ControlFile->blcksz = cluster_block_size;
 	ControlFile->relseg_size = RELSEG_SIZE;
 	ControlFile->xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile->xlog_seg_size = wal_segment_size;
@@ -3931,8 +3931,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 = cluster_toast_max_chunk_size;
+	ControlFile->loblksize = cluster_loblksize;
 
 	ControlFile->float8ByVal = FLOAT8PASSBYVAL;
 
@@ -4087,12 +4087,12 @@ ReadControlFile(void)
 				(errmsg("database files are incompatible with server"),
 				 errdetail("The database cluster appears to use a different floating-point number format than the server executable."),
 				 errhint("It looks like you need to initdb.")));
-	if (ControlFile->blcksz != BLCKSZ)
+	if (ControlFile->blcksz != cluster_block_size)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with BLCKSZ %d,"
-						   " but the server was compiled with BLCKSZ %d.",
-						   ControlFile->blcksz, BLCKSZ),
+				 errdetail("The database cluster was initialized with cluster_block_size %d,"
+						   " but the server was compiled with cluster_block_size %d.",
+						   ControlFile->blcksz, cluster_block_size),
 				 errhint("It looks like you need to recompile or initdb.")));
 	if (ControlFile->relseg_size != RELSEG_SIZE)
 		ereport(FATAL,
@@ -4122,19 +4122,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 != cluster_toast_max_chunk_size)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d,"
-						   " but the server was compiled with TOAST_MAX_CHUNK_SIZE %d.",
-						   ControlFile->toast_max_chunk_size, (int) TOAST_MAX_CHUNK_SIZE),
+				 errdetail("The database cluster was initialized with cluster_toast_max_chunk_size %d,"
+						   " but the server was compiled with cluster_toast_max_chunk_size %d.",
+						   ControlFile->toast_max_chunk_size, (int) cluster_toast_max_chunk_size),
 				 errhint("It looks like you need to recompile or initdb.")));
-	if (ControlFile->loblksize != LOBLKSIZE)
+	if (ControlFile->loblksize != cluster_loblksize)
 		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),
+				 errdetail("The database cluster was initialized with cluster_loblksize %d,"
+						   " but the server was compiled with cluster_loblksize %d.",
+						   ControlFile->loblksize, (int) cluster_loblksize),
 				 errhint("It looks like you need to recompile or initdb.")));
 
 #ifdef USE_FLOAT8_BYVAL
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 258cbd7035..87e04d6e03 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -46,18 +46,18 @@
  * backup block image.
  */
 #ifdef USE_LZ4
-#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(BLCKSZ)
+#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(cluster_block_size)
 #else
 #define LZ4_MAX_BLCKSZ		0
 #endif
 
 #ifdef USE_ZSTD
-#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(BLCKSZ)
+#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(cluster_block_size)
 #else
 #define ZSTD_MAX_BLCKSZ		0
 #endif
 
-#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(BLCKSZ)
+#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(cluster_block_size)
 
 /* Buffer size required to store a compressed version of backup block image */
 #define COMPRESS_BUFSIZE	Max(Max(PGLZ_MAX_BLCKSZ, LZ4_MAX_BLCKSZ), ZSTD_MAX_BLCKSZ)
@@ -383,7 +383,7 @@ XLogRegisterData(char *data, uint32 len)
  * block_id, the data is appended.
  *
  * The maximum amount of data that can be registered per block is 65535
- * bytes. That should be plenty; if you need more than BLCKSZ bytes to
+ * bytes. That should be plenty; if you need more than cluster_block_size bytes to
  * reconstruct the changes to the page, you might as well just log a full
  * copy of it. (the "main data" that's not associated with a block is not
  * limited)
@@ -650,7 +650,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 
 				if (lower >= SizeOfPageHeaderData &&
 					upper > lower &&
-					upper <= BLCKSZ)
+					upper <= cluster_block_size)
 				{
 					bimg.hole_offset = lower;
 					cbimg.hole_length = upper - lower;
@@ -746,12 +746,12 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			}
 			else
 			{
-				bimg.length = BLCKSZ - cbimg.hole_length;
+				bimg.length = cluster_block_size - cbimg.hole_length;
 
 				if (cbimg.hole_length == 0)
 				{
 					rdt_datas_last->data = page;
-					rdt_datas_last->len = BLCKSZ;
+					rdt_datas_last->len = cluster_block_size;
 				}
 				else
 				{
@@ -765,7 +765,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 					rdt_datas_last->data =
 						page + (bimg.hole_offset + cbimg.hole_length);
 					rdt_datas_last->len =
-						BLCKSZ - (bimg.hole_offset + cbimg.hole_length);
+						cluster_block_size - (bimg.hole_offset + cbimg.hole_length);
 				}
 			}
 
@@ -932,7 +932,7 @@ static bool
 XLogCompressBackupBlock(char *page, uint16 hole_offset, uint16 hole_length,
 						char *dest, uint16 *dlen)
 {
-	int32		orig_len = BLCKSZ - hole_length;
+	int32		orig_len = cluster_block_size - hole_length;
 	int32		len = -1;
 	int32		extra_bytes = 0;
 	char	   *source;
@@ -945,7 +945,7 @@ XLogCompressBackupBlock(char *page, uint16 hole_offset, uint16 hole_length,
 		memcpy(source, page, hole_offset);
 		memcpy(source + hole_offset,
 			   page + (hole_offset + hole_length),
-			   BLCKSZ - (hole_length + hole_offset));
+			   cluster_block_size - (hole_length + hole_offset));
 
 		/*
 		 * Extra data needs to be stored in WAL record for the compressed
@@ -1096,10 +1096,10 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 			uint16		upper = ((PageHeader) page)->pd_upper;
 
 			memcpy(copied_buffer.data, origdata, lower);
-			memcpy(copied_buffer.data + upper, origdata + upper, BLCKSZ - upper);
+			memcpy(copied_buffer.data + upper, origdata + upper, cluster_block_size - upper);
 		}
 		else
-			memcpy(copied_buffer.data, origdata, BLCKSZ);
+			memcpy(copied_buffer.data, origdata, cluster_block_size);
 
 		XLogBeginInsert();
 
diff --git a/src/backend/access/transam/xlogprefetcher.c b/src/backend/access/transam/xlogprefetcher.c
index 539928cb85..93026d5847 100644
--- a/src/backend/access/transam/xlogprefetcher.c
+++ b/src/backend/access/transam/xlogprefetcher.c
@@ -51,7 +51,7 @@
  * Every time we process this much WAL, we'll update the values in
  * pg_stat_recovery_prefetch.
  */
-#define XLOGPREFETCHER_STATS_DISTANCE BLCKSZ
+#define xlogprefetcher_stats_distance cluster_block_size
 
 /*
  * To detect repeated access to the same block and skip useless extra system
@@ -442,7 +442,7 @@ XLogPrefetcherComputeStats(XLogPrefetcher *prefetcher)
 	SharedStats->wal_distance = wal_distance;
 
 	prefetcher->next_stats_shm_lsn =
-		prefetcher->reader->ReadRecPtr + XLOGPREFETCHER_STATS_DISTANCE;
+		prefetcher->reader->ReadRecPtr + xlogprefetcher_stats_distance;
 }
 
 /*
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index c9f9f6e98f..cdce1f21ad 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -189,7 +189,7 @@ XLogReaderFree(XLogReaderState *state)
  * readRecordBufSize is set to the new buffer size.
  *
  * To avoid useless small increases, round its size to a multiple of
- * XLOG_BLCKSZ, and make sure it's at least 5*Max(BLCKSZ, XLOG_BLCKSZ) to start
+ * XLOG_BLCKSZ, and make sure it's at least 5*Max(cluster_block_size, XLOG_BLCKSZ) to start
  * with.  (That is enough for all "normal" records, but very large commit or
  * abort records might need more space.)
  */
@@ -199,7 +199,7 @@ allocate_recordbuf(XLogReaderState *state, uint32 reclength)
 	uint32		newSize = reclength;
 
 	newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
-	newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ));
+	newSize = Max(newSize, 5 * Max(cluster_block_size, XLOG_BLCKSZ));
 
 #ifndef FRONTEND
 
@@ -1786,17 +1786,17 @@ DecodeXLogRecord(XLogReaderState *state,
 						blk->hole_length = 0;
 				}
 				else
-					blk->hole_length = BLCKSZ - blk->bimg_len;
+					blk->hole_length = cluster_block_size - blk->bimg_len;
 				datatotal += blk->bimg_len;
 
 				/*
 				 * cross-check that hole_offset > 0, hole_length > 0 and
-				 * bimg_len < BLCKSZ if the HAS_HOLE flag is set.
+				 * bimg_len < cluster_block_size if the HAS_HOLE flag is set.
 				 */
 				if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
 					(blk->hole_offset == 0 ||
 					 blk->hole_length == 0 ||
-					 blk->bimg_len == BLCKSZ))
+					 blk->bimg_len == cluster_block_size))
 				{
 					report_invalid_record(state,
 										  "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X",
@@ -1823,10 +1823,10 @@ DecodeXLogRecord(XLogReaderState *state,
 				}
 
 				/*
-				 * Cross-check that bimg_len < BLCKSZ if it is compressed.
+				 * Cross-check that bimg_len < cluster_block_size if it is compressed.
 				 */
 				if (BKPIMAGE_COMPRESSED(blk->bimg_info) &&
-					blk->bimg_len == BLCKSZ)
+					blk->bimg_len == cluster_block_size)
 				{
 					report_invalid_record(state,
 										  "BKPIMAGE_COMPRESSED set, but block image length %u at %X/%X",
@@ -1836,12 +1836,12 @@ DecodeXLogRecord(XLogReaderState *state,
 				}
 
 				/*
-				 * cross-check that bimg_len = BLCKSZ if neither HAS_HOLE is
+				 * cross-check that bimg_len = cluster_block_size if neither HAS_HOLE is
 				 * set nor COMPRESSED().
 				 */
 				if (!(blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
 					!BKPIMAGE_COMPRESSED(blk->bimg_info) &&
-					blk->bimg_len != BLCKSZ)
+					blk->bimg_len != cluster_block_size)
 				{
 					report_invalid_record(state,
 										  "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_COMPRESSED set, but block image length is %u at %X/%X",
@@ -2073,14 +2073,14 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		if ((bkpb->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
 		{
 			if (pglz_decompress(ptr, bkpb->bimg_len, tmp.data,
-								BLCKSZ - bkpb->hole_length, true) < 0)
+								cluster_block_size - bkpb->hole_length, true) < 0)
 				decomp_success = false;
 		}
 		else if ((bkpb->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
 		{
 #ifdef USE_LZ4
 			if (LZ4_decompress_safe(ptr, tmp.data,
-									bkpb->bimg_len, BLCKSZ - bkpb->hole_length) <= 0)
+									bkpb->bimg_len, cluster_block_size - bkpb->hole_length) <= 0)
 				decomp_success = false;
 #else
 			report_invalid_record(record, "could not restore image at %X/%X compressed with %s not supported by build, block %d",
@@ -2094,7 +2094,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		{
 #ifdef USE_ZSTD
 			size_t		decomp_result = ZSTD_decompress(tmp.data,
-														BLCKSZ - bkpb->hole_length,
+														cluster_block_size - bkpb->hole_length,
 														ptr, bkpb->bimg_len);
 
 			if (ZSTD_isError(decomp_result))
@@ -2129,7 +2129,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 	/* generate page, taking into account hole if necessary */
 	if (bkpb->hole_length == 0)
 	{
-		memcpy(page, ptr, BLCKSZ);
+		memcpy(page, ptr, cluster_block_size);
 	}
 	else
 	{
@@ -2138,7 +2138,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		MemSet(page + bkpb->hole_offset, 0, bkpb->hole_length);
 		memcpy(page + (bkpb->hole_offset + bkpb->hole_length),
 			   ptr + bkpb->hole_offset,
-			   BLCKSZ - (bkpb->hole_offset + bkpb->hole_length));
+			   cluster_block_size - (bkpb->hole_offset + bkpb->hole_length));
 	}
 
 	return true;
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index becc2bda62..0ac856c945 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -293,7 +293,7 @@ static bool backupEndRequired = false;
  */
 bool		reachedConsistency = false;
 
-/* Buffers dedicated to consistency checks of size BLCKSZ */
+/* Buffers dedicated to consistency checks of size cluster_block_size */
 static char *replay_image_masked = NULL;
 static char *primary_image_masked = NULL;
 
@@ -606,8 +606,8 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	 * (2) a static char array isn't guaranteed to have any particular
 	 * alignment, whereas palloc() will provide MAXALIGN'd storage.
 	 */
-	replay_image_masked = (char *) palloc(BLCKSZ);
-	primary_image_masked = (char *) palloc(BLCKSZ);
+	replay_image_masked = (char *) palloc(cluster_block_size);
+	primary_image_masked = (char *) palloc(cluster_block_size);
 
 	if (read_backup_label(&CheckPointLoc, &CheckPointTLI, &backupEndRequired,
 						  &backupFromStandby))
@@ -2432,7 +2432,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 		 * Take a copy of the local page where WAL has been applied to have a
 		 * comparison base before masking it...
 		 */
-		memcpy(replay_image_masked, page, BLCKSZ);
+		memcpy(replay_image_masked, page, cluster_block_size);
 
 		/* No need for this page anymore now that a copy is in. */
 		UnlockReleaseBuffer(buf);
@@ -2467,7 +2467,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 		}
 
 		/* Time to compare the primary and replay images. */
-		if (memcmp(replay_image_masked, primary_image_masked, BLCKSZ) != 0)
+		if (memcmp(replay_image_masked, primary_image_masked, cluster_block_size) != 0)
 		{
 			elog(FATAL,
 				 "inconsistent page found, rel %u/%u/%u, forknum %u, blkno %u",
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 45be21131c..8fa1ae018a 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -55,7 +55,7 @@
  * NB: The buffer size is required to be a multiple of the system block
  * size, so use that value instead if it's bigger than our preference.
  */
-#define SINK_BUFFER_LENGTH			Max(32768, BLCKSZ)
+#define sink_buffer_length			Max(32768, cluster_block_size)
 
 typedef struct
 {
@@ -306,7 +306,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 		}
 
 		/* notify basebackup sink about start of backup */
-		bbsink_begin_backup(sink, &state, SINK_BUFFER_LENGTH);
+		bbsink_begin_backup(sink, &state, sink_buffer_length);
 
 		/* Send off our tablespaces one by one */
 		foreach(lc, state.tablespaces)
@@ -370,8 +370,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 			else
 			{
 				/* Properly terminate the tarfile. */
-				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= BLCKSZ,
-								 "BLCKSZ too small for 2 tar blocks");
+				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+								 "cluster_block_size too small for 2 tar blocks");
 				memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 				bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
 
@@ -623,8 +623,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 		}
 
 		/* Properly terminate the tar file. */
-		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= BLCKSZ,
-						 "BLCKSZ too small for 2 tar blocks");
+		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+						 "cluster_block_size too small for 2 tar blocks");
 		memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 		bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
 
@@ -1562,27 +1562,27 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 
 		/*
 		 * The checksums are verified at block level, so we iterate over the
-		 * buffer in chunks of BLCKSZ, after making sure that
-		 * TAR_SEND_SIZE/buf is divisible by BLCKSZ and we read a multiple of
-		 * BLCKSZ bytes.
+		 * buffer in chunks of cluster_block_size, after making sure that
+		 * TAR_SEND_SIZE/buf is divisible by cluster_block_size and we read a multiple of
+		 * cluster_block_size bytes.
 		 */
-		Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
+		Assert((sink->bbs_buffer_length % cluster_block_size) == 0);
 
-		if (verify_checksum && (cnt % BLCKSZ != 0))
+		if (verify_checksum && (cnt % cluster_block_size != 0))
 		{
 			ereport(WARNING,
 					(errmsg("could not verify checksum in file \"%s\", block "
 							"%u: read buffer size %d and page size %d "
 							"differ",
-							readfilename, blkno, (int) cnt, BLCKSZ)));
+							readfilename, blkno, (int) cnt, cluster_block_size)));
 			verify_checksum = false;
 		}
 
 		if (verify_checksum)
 		{
-			for (i = 0; i < cnt / BLCKSZ; i++)
+			for (i = 0; i < cnt / cluster_block_size; i++)
 			{
-				page = sink->bbs_buffer + BLCKSZ * i;
+				page = sink->bbs_buffer + cluster_block_size * i;
 
 				/*
 				 * Only check pages which have not been modified since the
@@ -1625,8 +1625,8 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 							/* Reread the failed block */
 							reread_cnt =
 								basebackup_read_file(fd,
-													 sink->bbs_buffer + BLCKSZ * i,
-													 BLCKSZ, len + BLCKSZ * i,
+													 sink->bbs_buffer + cluster_block_size * i,
+													 cluster_block_size, len + cluster_block_size * i,
 													 readfilename,
 													 false);
 							if (reread_cnt == 0)
@@ -1639,7 +1639,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 								 * code that handles that case. (We must fix
 								 * up cnt first, though.)
 								 */
-								cnt = BLCKSZ * i;
+								cnt = cluster_block_size * i;
 								break;
 							}
 
@@ -1745,12 +1745,12 @@ _tarWriteHeader(bbsink *sink, const char *filename, const char *linktarget,
 		/*
 		 * As of this writing, the smallest supported block size is 1kB, which
 		 * is twice TAR_BLOCK_SIZE. Since the buffer size is required to be a
-		 * multiple of BLCKSZ, it should be safe to assume that the buffer is
+		 * multiple of cluster_block_size, it should be safe to assume that the buffer is
 		 * large enough to fit an entire tar block. We double-check by means
 		 * of these assertions.
 		 */
-		StaticAssertDecl(TAR_BLOCK_SIZE <= BLCKSZ,
-						 "BLCKSZ too small for tar block");
+		StaticAssertDecl(TAR_BLOCK_SIZE <= cluster_block_size,
+						 "cluster_block_size too small for tar block");
 		Assert(sink->bbs_buffer_length >= TAR_BLOCK_SIZE);
 
 		rc = tarCreateHeader(sink->bbs_buffer, filename, linktarget,
diff --git a/src/backend/backup/basebackup_lz4.c b/src/backend/backup/basebackup_lz4.c
index 7acb606564..25f23bd8d3 100644
--- a/src/backend/backup/basebackup_lz4.c
+++ b/src/backend/backup/basebackup_lz4.c
@@ -117,10 +117,10 @@ bbsink_lz4_begin_backup(bbsink *sink)
 											 &mysink->prefs);
 
 	/*
-	 * The buffer length is expected to be a multiple of BLCKSZ, so round up.
+	 * The buffer length is expected to be a multiple of cluster_block_size, so round up.
 	 */
-	output_buffer_bound = output_buffer_bound + BLCKSZ -
-		(output_buffer_bound % BLCKSZ);
+	output_buffer_bound = output_buffer_bound + cluster_block_size -
+		(output_buffer_bound % cluster_block_size);
 
 	bbsink_begin_backup(sink->bbs_next, sink->bbs_state, output_buffer_bound);
 }
diff --git a/src/backend/backup/basebackup_zstd.c b/src/backend/backup/basebackup_zstd.c
index 6a078cdf48..62d73e4b22 100644
--- a/src/backend/backup/basebackup_zstd.c
+++ b/src/backend/backup/basebackup_zstd.c
@@ -143,10 +143,10 @@ bbsink_zstd_begin_backup(bbsink *sink)
 	output_buffer_bound = ZSTD_compressBound(mysink->base.bbs_buffer_length);
 
 	/*
-	 * The buffer length is expected to be a multiple of BLCKSZ, so round up.
+	 * The buffer length is expected to be a multiple of cluster_block_size, so round up.
 	 */
-	output_buffer_bound = output_buffer_bound + BLCKSZ -
-		(output_buffer_bound % BLCKSZ);
+	output_buffer_bound = output_buffer_bound + cluster_block_size -
+		(output_buffer_bound % cluster_block_size);
 
 	bbsink_begin_backup(sink->bbs_next, sink->bbs_state, output_buffer_bound);
 }
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 2add053489..32797d718c 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -800,7 +800,7 @@ smgrDoPendingSyncs(bool isCommit, bool isParallelWorker)
 		 * main fork is longer than ever but FSM fork gets shorter.
 		 */
 		if (pendingsync->is_truncated ||
-			total_blocks * BLCKSZ / 1024 >= wal_skip_threshold)
+			total_blocks * cluster_block_size / 1024 >= wal_skip_threshold)
 		{
 			/* allocate the initial array, or extend it, if needed */
 			if (maxrels == 0)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index bfd981aa3f..871101e550 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -772,9 +772,9 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 
 			if (delay_in_ms > 0)
 			{
-				read_rate = (double) BLCKSZ * AnalyzePageMiss / (1024 * 1024) /
+				read_rate = (double) cluster_block_size * AnalyzePageMiss / (1024 * 1024) /
 					(delay_in_ms / 1000.0);
-				write_rate = (double) BLCKSZ * AnalyzePageDirty / (1024 * 1024) /
+				write_rate = (double) cluster_block_size * AnalyzePageDirty / (1024 * 1024) /
 					(delay_in_ms / 1000.0);
 			}
 
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index d148d10850..c7d19f4ee0 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -163,7 +163,7 @@
  * than that, so changes in that data structure won't affect user-visible
  * restrictions.
  */
-#define NOTIFY_PAYLOAD_MAX_LENGTH	(BLCKSZ - NAMEDATALEN - 128)
+#define NOTIFY_PAYLOAD_MAX_LENGTH	(cluster_block_size - NAMEDATALEN - 128)
 
 /*
  * Struct representing an entry in the global notify queue
@@ -311,7 +311,7 @@ static AsyncQueueControl *asyncQueueControl;
 static SlruCtlData NotifyCtlData;
 
 #define NotifyCtl					(&NotifyCtlData)
-#define QUEUE_PAGESIZE				BLCKSZ
+#define QUEUE_PAGESIZE				cluster_block_size
 #define QUEUE_FULL_WARN_INTERVAL	5000	/* warn at most once every 5s */
 
 /*
@@ -322,7 +322,7 @@ static SlruCtlData NotifyCtlData;
  *
  * The most data we can have in the queue at a time is QUEUE_MAX_PAGE/2
  * pages, because more than that would confuse slru.c into thinking there
- * was a wraparound condition.  With the default BLCKSZ this means there
+ * was a wraparound condition.  With the default cluster_block_size this means there
  * can be up to 8GB of queued-and-not-read data.
  *
  * Note: it's possible to redefine QUEUE_MAX_PAGE with a smaller multiple of
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index 351ab4957a..1263603df9 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -1036,7 +1036,7 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
 
 	/* Each parallel VACUUM worker gets its own access strategy. */
 	pvs.bstrategy = GetAccessStrategyWithSize(BAS_VACUUM,
-											  shared->ring_nbuffers * (BLCKSZ / 1024));
+											  shared->ring_nbuffers * (cluster_block_size / 1024));
 
 	/* Setup error traceback support for ereport() */
 	errcallback.callback = parallel_vacuum_error_callback;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 468db94fe5..02358ba372 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -216,8 +216,8 @@
  *
  *	  Tapes' buffers can take up substantial memory when many tapes are open at
  *	  once. We only need one tape open at a time in read mode (using a buffer
- *	  that's a multiple of BLCKSZ); but we need one tape open in write mode (each
- *	  requiring a buffer of size BLCKSZ) for each partition.
+ *	  that's a multiple of cluster_block_size); but we need one tape open in write mode (each
+ *	  requiring a buffer of size cluster_block_size) for each partition.
  *
  *	  Note that it's possible for transition states to start small but then
  *	  grow very large; for instance in the case of ARRAY_AGG. In such cases,
@@ -299,12 +299,12 @@
 
 /*
  * For reading from tapes, the buffer size must be a multiple of
- * BLCKSZ. Larger values help when reading from multiple tapes concurrently,
- * but that doesn't happen in HashAgg, so we simply use BLCKSZ. Writing to a
- * tape always uses a buffer of size BLCKSZ.
+ * cluster_block_size. Larger values help when reading from multiple tapes concurrently,
+ * but that doesn't happen in HashAgg, so we simply use cluster_block_size. Writing to a
+ * tape always uses a buffer of size cluster_block_size.
  */
-#define HASHAGG_READ_BUFFER_SIZE BLCKSZ
-#define HASHAGG_WRITE_BUFFER_SIZE BLCKSZ
+#define hashagg_read_buffer_size cluster_block_size
+#define hashagg_write_buffer_size cluster_block_size
 
 /*
  * HyperLogLog is used for estimating the cardinality of the spilled tuples in
@@ -1827,8 +1827,8 @@ hash_agg_set_limits(double hashentrysize, double input_groups, int used_bits,
 		*num_partitions = npartitions;
 
 	partition_mem =
-		HASHAGG_READ_BUFFER_SIZE +
-		HASHAGG_WRITE_BUFFER_SIZE * npartitions;
+		hashagg_read_buffer_size +
+		hashagg_write_buffer_size * npartitions;
 
 	/*
 	 * Don't set the limit below 3/4 of hash_mem. In that case, we are at the
@@ -1933,9 +1933,9 @@ hash_agg_update_metrics(AggState *aggstate, bool from_tape, int npartitions)
 	hashkey_mem = MemoryContextMemAllocated(aggstate->hashcontext->ecxt_per_tuple_memory, true);
 
 	/* memory for read/write tape buffers, if spilled */
-	buffer_mem = npartitions * HASHAGG_WRITE_BUFFER_SIZE;
+	buffer_mem = npartitions * hashagg_write_buffer_size;
 	if (from_tape)
-		buffer_mem += HASHAGG_READ_BUFFER_SIZE;
+		buffer_mem += hashagg_read_buffer_size;
 
 	/* update peak mem */
 	total_mem = meta_mem + hashkey_mem + buffer_mem;
@@ -1945,7 +1945,7 @@ hash_agg_update_metrics(AggState *aggstate, bool from_tape, int npartitions)
 	/* update disk usage */
 	if (aggstate->hash_tapeset != NULL)
 	{
-		uint64		disk_used = LogicalTapeSetBlocks(aggstate->hash_tapeset) * (BLCKSZ / 1024);
+		uint64		disk_used = LogicalTapeSetBlocks(aggstate->hash_tapeset) * (cluster_block_size / 1024);
 
 		if (aggstate->hash_disk_used < disk_used)
 			aggstate->hash_disk_used = disk_used;
@@ -2004,8 +2004,8 @@ hash_choose_num_partitions(double input_groups, double hashentrysize,
 	 * open partition files are greater than 1/4 of hash_mem.
 	 */
 	partition_limit =
-		(hash_mem_limit * 0.25 - HASHAGG_READ_BUFFER_SIZE) /
-		HASHAGG_WRITE_BUFFER_SIZE;
+		(hash_mem_limit * 0.25 - hashagg_read_buffer_size) /
+		hashagg_write_buffer_size;
 
 	mem_wanted = HASHAGG_PARTITION_FACTOR * input_groups * hashentrysize;
 
@@ -3113,7 +3113,7 @@ hashagg_spill_finish(AggState *aggstate, HashAggSpill *spill, int setno)
 		freeHyperLogLog(&spill->hll_card[i]);
 
 		/* rewinding frees the buffer while not in use */
-		LogicalTapeRewindForRead(tape, HASHAGG_READ_BUFFER_SIZE);
+		LogicalTapeRewindForRead(tape, hashagg_read_buffer_size);
 
 		new_batch = hashagg_batch_new(tape, setno,
 									  spill->ntuples[i], cardinality,
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index 29a1858441..971c29c428 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -13,7 +13,7 @@
  * fact that a particular page needs to be visited.
  *
  * The "lossy" storage uses one bit per disk page, so at the standard 8K
- * BLCKSZ, we can represent all pages in 64Gb of disk space in about 1Mb
+ * cluster_block_size, we can represent all pages in 64Gb of disk space in about 1Mb
  * of memory.  People pushing around tables of that size should have a
  * couple of Mb to spare, so we don't worry about providing a second level
  * of lossiness.  In theory we could fall back to page ranges at some
@@ -70,7 +70,7 @@
  * too different.  But we also want PAGES_PER_CHUNK to be a power of 2 to
  * avoid expensive integer remainder operations.  So, define it like this:
  */
-#define PAGES_PER_CHUNK  (BLCKSZ / 32)
+#define PAGES_PER_CHUNK  (cluster_block_size / 32)
 
 /* We use BITS_PER_BITMAPWORD and typedef bitmapword from nodes/bitmapset.h */
 
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index d6ceafd51c..2a66cdd355 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1890,7 +1890,7 @@ cost_tuplesort(Cost *startup_cost, Cost *run_cost,
 		/*
 		 * We'll have to use a disk-based sort of all the tuples
 		 */
-		double		npages = ceil(input_bytes / BLCKSZ);
+		double		npages = ceil(input_bytes / cluster_block_size);
 		double		nruns = input_bytes / sort_mem_bytes;
 		double		mergeorder = tuplesort_merge_order(sort_mem_bytes);
 		double		log_runs;
@@ -2455,7 +2455,7 @@ cost_material(Path *path,
 	 */
 	if (nbytes > work_mem_bytes)
 	{
-		double		npages = ceil(nbytes / BLCKSZ);
+		double		npages = ceil(nbytes / cluster_block_size);
 
 		run_cost += seq_page_cost * npages;
 	}
@@ -2764,7 +2764,7 @@ cost_agg(Path *path, PlannerInfo *root,
 		 * Estimate number of pages read and written. For each level of
 		 * recursion, a tuple must be written and then later read.
 		 */
-		pages = relation_byte_size(input_tuples, input_width) / BLCKSZ;
+		pages = relation_byte_size(input_tuples, input_width) / cluster_block_size;
 		pages_written = pages_read = pages * depth;
 
 		/*
@@ -4551,7 +4551,7 @@ cost_rescan(PlannerInfo *root, Path *path,
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
-					double		npages = ceil(nbytes / BLCKSZ);
+					double		npages = ceil(nbytes / cluster_block_size);
 
 					run_cost += seq_page_cost * npages;
 				}
@@ -4578,7 +4578,7 @@ cost_rescan(PlannerInfo *root, Path *path,
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
-					double		npages = ceil(nbytes / BLCKSZ);
+					double		npages = ceil(nbytes / cluster_block_size);
 
 					run_cost += seq_page_cost * npages;
 				}
@@ -6356,7 +6356,7 @@ relation_byte_size(double tuples, int width)
 static double
 page_size(double tuples, int width)
 {
-	return ceil(relation_byte_size(tuples, width) / BLCKSZ);
+	return ceil(relation_byte_size(tuples, width) / cluster_block_size);
 }
 
 /*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 243c8fb1e4..cfe03dedff 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1089,7 +1089,7 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
 			tuple_width += MAXALIGN(SizeofHeapTupleHeader);
 			tuple_width += sizeof(ItemIdData);
 			/* note: integer division is intentional here */
-			density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
+			density = (cluster_block_size - SizeOfPageHeaderData) / tuple_width;
 		}
 		*tuples = rint(density * (double) curpages);
 
diff --git a/src/backend/po/de.po b/src/backend/po/de.po
index 0a9e668c38..ac2cb6e4cf 100644
--- a/src/backend/po/de.po
+++ b/src/backend/po/de.po
@@ -2329,8 +2329,8 @@ msgstr "Der Datenbank-Cluster verwendet anscheinend ein anderes Fließkommazahle
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Der Datenbank-Cluster wurde mit BLCKSZ %d initialisiert, aber der Server wurde mit BLCKSZ %d kompiliert."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Der Datenbank-Cluster wurde mit cluster_block_size %d initialisiert, aber der Server wurde mit cluster_block_size %d kompiliert."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -2363,13 +2363,13 @@ msgstr "Der Datenbank-Cluster wurde mit INDEX_MAX_KEYS %d initialisiert, aber de
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Der Datenbank-Cluster wurde mit TOAST_MAX_CHUNK_SIZE %d initialisiert, aber der Server wurde mit TOAST_MAX_CHUNK_SIZE %d kompiliert."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Der Datenbank-Cluster wurde mit cluster_toast_max_chunk_size %d initialisiert, aber der Server wurde mit cluster_toast_max_chunk_size %d kompiliert."
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Der Datenbank-Cluster wurde mit LOBLKSIZE %d initialisiert, aber der Server wurde mit LOBLKSIZE %d kompiliert."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Der Datenbank-Cluster wurde mit cluster_loblksize %d initialisiert, aber der Server wurde mit cluster_loblksize %d kompiliert."
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -22029,8 +22029,8 @@ msgstr "debug_io_direct wird für WAL nicht unterstützt, weil XLOG_BLCKSZ zu kl
 
 #: storage/file/fd.c:3951
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
-msgstr "debug_io_direct wird für Daten nicht unterstützt, weil BLCKSZ zu klein ist"
+msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
+msgstr "debug_io_direct wird für Daten nicht unterstützt, weil cluster_block_size zu klein ist"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/po/es.po b/src/backend/po/es.po
index e50a935033..b420226eb9 100644
--- a/src/backend/po/es.po
+++ b/src/backend/po/es.po
@@ -2387,8 +2387,8 @@ msgstr "Los archivos de la base de datos parecen usar un formato de número de c
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Los archivos de base de datos fueron inicializados con BLCKSZ %d, pero el servidor fue compilado con BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Los archivos de base de datos fueron inicializados con cluster_block_size %d, pero el servidor fue compilado con cluster_block_size %d."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -2421,13 +2421,13 @@ msgstr "Los archivos de la base de datos fueron inicializados con INDEX_MAX_KEYS
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Los archivos de la base de datos fueron inicializados con TOAST_MAX_CHUNK_SIZE %d, pero el servidor fue compilado con TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Los archivos de la base de datos fueron inicializados con cluster_toast_max_chunk_size %d, pero el servidor fue compilado con cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Los archivos de base de datos fueron inicializados con LOBLKSIZE %d, pero el servidor fue compilado con LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Los archivos de base de datos fueron inicializados con cluster_loblksize %d, pero el servidor fue compilado con cluster_loblksize %d."
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -21808,7 +21808,7 @@ msgstr ""
 
 #: storage/file/fd.c:3901
 #, c-format
-msgid "io_direct is not supported for data because BLCKSZ is too small"
+msgid "io_direct is not supported for data because cluster_block_size is too small"
 msgstr ""
 
 #: storage/file/reinit.c:145
diff --git a/src/backend/po/fr.po b/src/backend/po/fr.po
index fd51500b93..2b73cd3fdc 100644
--- a/src/backend/po/fr.po
+++ b/src/backend/po/fr.po
@@ -2380,10 +2380,10 @@ msgstr ""
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
 msgstr ""
-"Le cluster de base de données a été initialisé avec un BLCKSZ à %d alors que\n"
-"le serveur a été compilé avec un BLCKSZ à %d."
+"Le cluster de base de données a été initialisé avec un cluster_block_size à %d alors que\n"
+"le serveur a été compilé avec un cluster_block_size à %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
@@ -2424,17 +2424,17 @@ msgstr ""
 
 #: access/transam/xlog.c:4131
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
 msgstr ""
-"Le cluster de bases de données a été initialisé avec un TOAST_MAX_CHUNK_SIZE\n"
-"à %d alors que le serveur a été compilé avec un TOAST_MAX_CHUNK_SIZE à %d."
+"Le cluster de bases de données a été initialisé avec un cluster_toast_max_chunk_size\n"
+"à %d alors que le serveur a été compilé avec un cluster_toast_max_chunk_size à %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
 msgstr ""
-"Le cluster de base de données a été initialisé avec un LOBLKSIZE à %d alors que\n"
-"le serveur a été compilé avec un LOBLKSIZE à %d."
+"Le cluster de base de données a été initialisé avec un cluster_loblksize à %d alors que\n"
+"le serveur a été compilé avec un cluster_loblksize à %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/id.po b/src/backend/po/id.po
index d5d484132b..e503bc4117 100644
--- a/src/backend/po/id.po
+++ b/src/backend/po/id.po
@@ -1089,8 +1089,8 @@ msgstr "cluster database sepertinya menggunakan format nomor floating-point yang
 
 #: access/transam/xlog.c:3648
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "cluster database telah diinisialkan dengan BLCKSZ %d, tapi server telah dikompilasi dengan BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "cluster database telah diinisialkan dengan cluster_block_size %d, tapi server telah dikompilasi dengan cluster_block_size %d."
 
 #: access/transam/xlog.c:3651 access/transam/xlog.c:3658 access/transam/xlog.c:3665 access/transam/xlog.c:3672 access/transam/xlog.c:3679 access/transam/xlog.c:3686 access/transam/xlog.c:3693 access/transam/xlog.c:3701 access/transam/xlog.c:3708 access/transam/xlog.c:3717 access/transam/xlog.c:3724 access/transam/xlog.c:3733 access/transam/xlog.c:3740
 #, c-format
@@ -1124,8 +1124,8 @@ msgstr "cluster database telah diinisialkan dengan INDEX_MAX_KEYS %d, tapi serve
 
 #: access/transam/xlog.c:3690
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "cluster database telah diinisialkan dengan TOAST_MAX_CHUNK_SIZE %d, tapi server telah dikompilasi dengan TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "cluster database telah diinisialkan dengan cluster_toast_max_chunk_size %d, tapi server telah dikompilasi dengan cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:3699
 #, c-format
diff --git a/src/backend/po/it.po b/src/backend/po/it.po
index 673e2aaf00..5cd524e120 100644
--- a/src/backend/po/it.po
+++ b/src/backend/po/it.po
@@ -2067,8 +2067,8 @@ msgstr "Il cluster di database sta usando un formato per i numeri in virgola mob
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Il cluster di database è stato inizializzato con BLCKSZ %d, ma il server è stato compilato con BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Il cluster di database è stato inizializzato con cluster_block_size %d, ma il server è stato compilato con cluster_block_size %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106 access/transam/xlog.c:4113 access/transam/xlog.c:4120 access/transam/xlog.c:4127 access/transam/xlog.c:4134 access/transam/xlog.c:4141 access/transam/xlog.c:4149 access/transam/xlog.c:4156
 #, c-format
@@ -2097,13 +2097,13 @@ msgstr "Il cluster di database è stato inizializzato con INDEX_MAX_KEYS %d, ma
 
 #: access/transam/xlog.c:4131
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Il cluster di database è stato inizializzato con TOAST_MAX_CHUNK_SIZE %d, ma il server è stato compilato con TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Il cluster di database è stato inizializzato con cluster_toast_max_chunk_size %d, ma il server è stato compilato con cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Il cluster di database è stato inizializzato con LOBLKSIZE %d, ma il server è stato compilato con LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Il cluster di database è stato inizializzato con cluster_loblksize %d, ma il server è stato compilato con cluster_loblksize %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/ja.po b/src/backend/po/ja.po
index 1ab9f7f68f..c7a8044f23 100644
--- a/src/backend/po/ja.po
+++ b/src/backend/po/ja.po
@@ -2096,8 +2096,8 @@ msgstr "データベースクラスタはサーバー実行ファイルと異な
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "データベースクラスタは BLCKSZ %d で初期化されましたが、サーバーは BLCKSZ %d でコンパイルされています。"
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "データベースクラスタは cluster_block_size %d で初期化されましたが、サーバーは cluster_block_size %d でコンパイルされています。"
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080 access/transam/xlog.c:4087 access/transam/xlog.c:4094 access/transam/xlog.c:4101 access/transam/xlog.c:4108 access/transam/xlog.c:4115 access/transam/xlog.c:4123 access/transam/xlog.c:4130
 #, c-format
@@ -2126,13 +2126,13 @@ msgstr "データベースクラスタは INDEX_MAX_KEYS %d で初期化され
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "データベースクラスタは TOAST_MAX_CHUNK_SIZE %d で初期化されましたが、サーバーは TOAST_MAX_CHUNK_SIZE %d でコンパイルされています。"
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "データベースクラスタは cluster_toast_max_chunk_size %d で初期化されましたが、サーバーは cluster_toast_max_chunk_size %d でコンパイルされています。"
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "データベースクラスタは LOBLKSIZE %d で初期化されましたが、サーバーは LOBLKSIZE %d でコンパイルされています。"
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "データベースクラスタは cluster_loblksize %d で初期化されましたが、サーバーは cluster_loblksize %d でコンパイルされています。"
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -21403,8 +21403,8 @@ msgstr "XLOG_BLCKSZが小さすぎるためdebug_io_directはWALに対しては
 
 #: storage/file/fd.c:3951
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
-msgstr "BLCKSZが小さすぎるためdebug_io_directはデータに対してサポートされません"
+msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
+msgstr "cluster_block_sizeが小さすぎるためdebug_io_directはデータに対してサポートされません"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/po/ko.po b/src/backend/po/ko.po
index f330f3da7e..825dae75cf 100644
--- a/src/backend/po/ko.po
+++ b/src/backend/po/ko.po
@@ -2348,10 +2348,10 @@ msgstr ""
 #: access/transam/xlog.c:4802
 #, c-format
 msgid ""
-"The database cluster was initialized with BLCKSZ %d, but the server was "
-"compiled with BLCKSZ %d."
+"The database cluster was initialized with cluster_block_size %d, but the server was "
+"compiled with cluster_block_size %d."
 msgstr ""
-"이 데이터베이스 클러스터는 BLCKSZ %d (으)로 초기화 되었지만, 서버는 BLCKSZ "
+"이 데이터베이스 클러스터는 cluster_block_size %d (으)로 초기화 되었지만, 서버는 cluster_block_size "
 "%d (으)로 컴파일 되어있습니다."
 
 #: access/transam/xlog.c:4805 access/transam/xlog.c:4812
@@ -2404,20 +2404,20 @@ msgstr ""
 #: access/transam/xlog.c:4837
 #, c-format
 msgid ""
-"The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the "
-"server was compiled with TOAST_MAX_CHUNK_SIZE %d."
+"The database cluster was initialized with cluster_toast_max_chunk_size %d, but the "
+"server was compiled with cluster_toast_max_chunk_size %d."
 msgstr ""
-"데이터베이스 클러스터는 TOAST_MAX_CHUNK_SIZE %d(으)로 초기화되었지만 서버는 "
-"TOAST_MAX_CHUNK_SIZE %d(으)로 컴파일 되었습니다."
+"데이터베이스 클러스터는 cluster_toast_max_chunk_size %d(으)로 초기화되었지만 서버는 "
+"cluster_toast_max_chunk_size %d(으)로 컴파일 되었습니다."
 
 #: access/transam/xlog.c:4844
 #, c-format
 msgid ""
-"The database cluster was initialized with LOBLKSIZE %d, but the server was "
-"compiled with LOBLKSIZE %d."
+"The database cluster was initialized with cluster_loblksize %d, but the server was "
+"compiled with cluster_loblksize %d."
 msgstr ""
-"이 데이터베이스 클러스터는 LOBLKSIZE %d(으)로 초기화 되었지만, 서버는 "
-"LOBLKSIZE %d (으)로 컴파일 되어있습니다."
+"이 데이터베이스 클러스터는 cluster_loblksize %d(으)로 초기화 되었지만, 서버는 "
+"cluster_loblksize %d (으)로 컴파일 되어있습니다."
 
 #: access/transam/xlog.c:4853
 #, c-format
diff --git a/src/backend/po/pl.po b/src/backend/po/pl.po
index 3ac9d0451c..08fdc7a3d5 100644
--- a/src/backend/po/pl.po
+++ b/src/backend/po/pl.po
@@ -1927,8 +1927,8 @@ msgstr "Klaster bazy danych wydaje się używać innego formatu liczb zmiennoprz
 
 #: access/transam/xlog.c:4550
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Klaster bazy danych został zainicjowany z BLCKSZ %d, ale serwer był skompilowany z BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Klaster bazy danych został zainicjowany z cluster_block_size %d, ale serwer był skompilowany z cluster_block_size %d."
 
 #: access/transam/xlog.c:4553 access/transam/xlog.c:4560
 #: access/transam/xlog.c:4567 access/transam/xlog.c:4574
@@ -1967,13 +1967,13 @@ msgstr "Klaster bazy danych został zainicjowany z INDEX_MAX_KEYS %d, ale serwer
 
 #: access/transam/xlog.c:4592
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Klaster bazy danych został zainicjowany z TOAST_MAX_CHUNK_SIZE %d, ale serwer był skompilowany z TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Klaster bazy danych został zainicjowany z cluster_toast_max_chunk_size %d, ale serwer był skompilowany z cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4599
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Klaster bazy danych został zainicjowany z LOBLKSIZE %d, ale serwer był skompilowany z LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Klaster bazy danych został zainicjowany z cluster_loblksize %d, ale serwer był skompilowany z cluster_loblksize %d."
 
 #: access/transam/xlog.c:4608
 #, c-format
diff --git a/src/backend/po/pt_BR.po b/src/backend/po/pt_BR.po
index 37e4a28f07..7e30e2119d 100644
--- a/src/backend/po/pt_BR.po
+++ b/src/backend/po/pt_BR.po
@@ -1264,7 +1264,7 @@ msgstr "O agrupamento de banco de dados parece utilizar um formato de número de
 
 #: access/transam/xlog.c:4506
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
 msgstr "O agrupamento de banco de dados foi inicializado com BLCSZ %d, mas o servidor foi compilado com BLCSZ %d."
 
 #: access/transam/xlog.c:4509 access/transam/xlog.c:4516
@@ -1305,13 +1305,13 @@ msgstr "O agrupamento de banco de dados foi inicializado com INDEX_MAX_KEYS %d,
 
 #: access/transam/xlog.c:4548
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "O agrupamento de banco de dados foi inicializado com TOAST_MAX_CHUNK_SIZE %d, mas o servidor foi compilado com TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "O agrupamento de banco de dados foi inicializado com cluster_toast_max_chunk_size %d, mas o servidor foi compilado com cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4555
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "O agrupamento de banco de dados foi inicializado com LOBLKSIZE %d, mas o servidor foi compilado com LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "O agrupamento de banco de dados foi inicializado com cluster_loblksize %d, mas o servidor foi compilado com cluster_loblksize %d."
 
 #: access/transam/xlog.c:4564
 #, c-format
diff --git a/src/backend/po/ru.po b/src/backend/po/ru.po
index ae9c50eed7..97ee086a7b 100644
--- a/src/backend/po/ru.po
+++ b/src/backend/po/ru.po
@@ -2619,11 +2619,11 @@ msgstr ""
 #: access/transam/xlog.c:4096
 #, c-format
 msgid ""
-"The database cluster was initialized with BLCKSZ %d, but the server was "
-"compiled with BLCKSZ %d."
+"The database cluster was initialized with cluster_block_size %d, but the server was "
+"compiled with cluster_block_size %d."
 msgstr ""
-"Кластер баз данных был инициализирован с BLCKSZ %d, но сервер скомпилирован "
-"с BLCKSZ %d."
+"Кластер баз данных был инициализирован с cluster_block_size %d, но сервер скомпилирован "
+"с cluster_block_size %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
@@ -2673,20 +2673,20 @@ msgstr ""
 #: access/transam/xlog.c:4131
 #, c-format
 msgid ""
-"The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the "
-"server was compiled with TOAST_MAX_CHUNK_SIZE %d."
+"The database cluster was initialized with cluster_toast_max_chunk_size %d, but the "
+"server was compiled with cluster_toast_max_chunk_size %d."
 msgstr ""
-"Кластер баз данных был инициализирован с TOAST_MAX_CHUNK_SIZE %d, но сервер "
-"скомпилирован с TOAST_MAX_CHUNK_SIZE %d."
+"Кластер баз данных был инициализирован с cluster_toast_max_chunk_size %d, но сервер "
+"скомпилирован с cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
 msgid ""
-"The database cluster was initialized with LOBLKSIZE %d, but the server was "
-"compiled with LOBLKSIZE %d."
+"The database cluster was initialized with cluster_loblksize %d, but the server was "
+"compiled with cluster_loblksize %d."
 msgstr ""
-"Кластер баз данных был инициализирован с LOBLKSIZE %d, но сервер "
-"скомпилирован с LOBLKSIZE %d."
+"Кластер баз данных был инициализирован с cluster_loblksize %d, но сервер "
+"скомпилирован с cluster_loblksize %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/sv.po b/src/backend/po/sv.po
index 0da20b6d43..39060c459e 100644
--- a/src/backend/po/sv.po
+++ b/src/backend/po/sv.po
@@ -2345,8 +2345,8 @@ msgstr "Databasklustret verkar använda en annan flyttalsrepresentation än vad
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Databasklustret initierades med BLCKSZ %d, men servern kompilerades med BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Databasklustret initierades med cluster_block_size %d, men servern kompilerades med cluster_block_size %d."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -2379,13 +2379,13 @@ msgstr "Databasklustret initierades med INDEX_MAX_KEYS %d, men servern kompilera
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Databasklustret initierades med TOAST_MAX_CHUNK_SIZE %d, men servern kompilerades med TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Databasklustret initierades med cluster_toast_max_chunk_size %d, men servern kompilerades med cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Databasklustret initierades med LOBLKSIZE %d, men servern kompilerades med LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Databasklustret initierades med cluster_loblksize %d, men servern kompilerades med cluster_loblksize %d."
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -22134,7 +22134,7 @@ msgstr ""
 
 #: storage/file/fd.c:3951
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
+msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
 msgstr ""
 
 #: storage/file/reinit.c:145
diff --git a/src/backend/po/tr.po b/src/backend/po/tr.po
index b791e886b9..695ee51478 100644
--- a/src/backend/po/tr.po
+++ b/src/backend/po/tr.po
@@ -1761,8 +1761,8 @@ msgstr "Veritabanı dosyaları, sunucu programından farklı ondalık sayı biç
 
 #: access/transam/xlog.c:4676
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Veritabanı clusteri BLCKSZ %d ile ilklendirilmiştir, ancak sunucu  BLCKSZ %d ile derlenmiştir."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Veritabanı clusteri cluster_block_size %d ile ilklendirilmiştir, ancak sunucu  cluster_block_size %d ile derlenmiştir."
 
 #: access/transam/xlog.c:4679 access/transam/xlog.c:4686 access/transam/xlog.c:4693 access/transam/xlog.c:4700 access/transam/xlog.c:4707 access/transam/xlog.c:4714 access/transam/xlog.c:4721 access/transam/xlog.c:4729 access/transam/xlog.c:4736 access/transam/xlog.c:4745 access/transam/xlog.c:4752
 #, c-format
@@ -1791,13 +1791,13 @@ msgstr "Veritabanı clusteri INDEX_MAX_KEYS %d ile ilklendirilmiştir, ancak sun
 
 #: access/transam/xlog.c:4711
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Veritabanı clusteri TOAST_MAX_CHUNK_SIZE %d ile ilklendirilmiştir, ancak sunucu  TOAST_MAX_CHUNK_SIZE %d ile derlenmiştir."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Veritabanı clusteri cluster_toast_max_chunk_size %d ile ilklendirilmiştir, ancak sunucu  cluster_toast_max_chunk_size %d ile derlenmiştir."
 
 #: access/transam/xlog.c:4718
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Veritabanı clusteri LOBLKSIZE %d ile ilklendirilmiştir, ancak sunucu  LOBLKSIZE %d ile derlenmiştir."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Veritabanı clusteri cluster_loblksize %d ile ilklendirilmiştir, ancak sunucu  cluster_loblksize %d ile derlenmiştir."
 
 #: access/transam/xlog.c:4727
 #, c-format
diff --git a/src/backend/po/uk.po b/src/backend/po/uk.po
index 1095fd9139..abb8139f4d 100644
--- a/src/backend/po/uk.po
+++ b/src/backend/po/uk.po
@@ -2288,8 +2288,8 @@ msgstr "Здається, в кластері баз даних і в прогр
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Кластер бази даних було ініціалізовано з BLCKSZ %d,  але сервер було скомпільовано з BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Кластер бази даних було ініціалізовано з cluster_block_size %d,  але сервер було скомпільовано з cluster_block_size %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
@@ -2322,13 +2322,13 @@ msgstr "Кластер бази даних було ініціалізовано
 
 #: access/transam/xlog.c:4131
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Кластер бази даних було ініціалізовано з TOAST_MAX_CHUNK_SIZE %d, але сервер було скомпільовано з TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Кластер бази даних було ініціалізовано з cluster_toast_max_chunk_size %d, але сервер було скомпільовано з cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Кластер бази даних було ініціалізовано з LOBLKSIZE %d, але сервер було скомпільовано з LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Кластер бази даних було ініціалізовано з cluster_loblksize %d, але сервер було скомпільовано з cluster_loblksize %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/zh_CN.po b/src/backend/po/zh_CN.po
index 574684d775..b576a6e015 100644
--- a/src/backend/po/zh_CN.po
+++ b/src/backend/po/zh_CN.po
@@ -1852,8 +1852,8 @@ msgstr "数据库集群在使用与服务器执行部分不同的浮点数格式
 
 #: access/transam/xlog.c:4677
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "数据库簇是以 BLCKSZ %d 初始化的, 但是 服务器是以 BLCKSZ %d 编译的."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "数据库簇是以 cluster_block_size %d 初始化的, 但是 服务器是以 cluster_block_size %d 编译的."
 
 #: access/transam/xlog.c:4680 access/transam/xlog.c:4687
 #: access/transam/xlog.c:4694 access/transam/xlog.c:4701
@@ -1887,13 +1887,13 @@ msgstr "数据库集群是以 INDEX_MAX_KEYS  %d 初始化的, 但是 服务器
 
 #: access/transam/xlog.c:4712
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "数据库集群是以 TOAST_MAX_CHUNK_SIZE %d 初始化的, 但是 服务器是以 TOAST_MAX_CHUNK_SIZE %d 编译的."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "数据库集群是以 cluster_toast_max_chunk_size %d 初始化的, 但是 服务器是以 cluster_toast_max_chunk_size %d 编译的."
 
 #: access/transam/xlog.c:4719
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "数据库簇是以 LOBLKSIZE %d 初始化的, 但是 服务器是以 LOBLKSIZE %d 编译的."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "数据库簇是以 cluster_loblksize %d 初始化的, 但是 服务器是以 cluster_loblksize %d 编译的."
 
 #: access/transam/xlog.c:4728
 #, c-format
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 597947410f..2653f68363 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -2056,7 +2056,7 @@ apply_spooled_messages(FileSet *stream_fileset, TransactionId xid,
 
 	CurrentResourceOwner = oldowner;
 
-	buffer = palloc(BLCKSZ);
+	buffer = palloc(cluster_block_size);
 	initStringInfo(&s2);
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index 0057443f0c..69f21908e3 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -82,7 +82,7 @@ InitBufferPool(void)
 	BufferBlocks = (char *)
 		TYPEALIGN(PG_IO_ALIGN_SIZE,
 				  ShmemInitStruct("Buffer Blocks",
-								  NBuffers * (Size) BLCKSZ + PG_IO_ALIGN_SIZE,
+								  NBuffers * (Size) cluster_block_size + PG_IO_ALIGN_SIZE,
 								  &foundBufs));
 
 	/* Align condition variables to cacheline boundary. */
@@ -168,7 +168,7 @@ BufferShmemSize(void)
 
 	/* size of data pages, plus alignment padding */
 	size = add_size(size, PG_IO_ALIGN_SIZE);
-	size = add_size(size, mul_size(NBuffers, BLCKSZ));
+	size = add_size(size, mul_size(NBuffers, cluster_block_size));
 
 	/* size of stuff controlled by freelist.c */
 	size = add_size(size, StrategyShmemSize());
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 3bd82dbfca..d11f73c13b 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -60,7 +60,7 @@
 
 
 /* Note: these two macros only work on shared buffers, not local ones! */
-#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * BLCKSZ))
+#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * cluster_block_size))
 #define BufferGetLSN(bufHdr)	(PageGetLSN(BufHdrGetBlock(bufHdr)))
 
 /* Note: this macro only works on local buffers, not shared ones! */
@@ -1116,7 +1116,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	 * wants us to allocate a buffer.
 	 */
 	if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
-		MemSet((char *) bufBlock, 0, BLCKSZ);
+		MemSet((char *) bufBlock, 0, cluster_block_size);
 	else
 	{
 		instr_time	io_start = pgstat_prepare_io_time();
@@ -1137,7 +1137,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 						 errmsg("invalid page in block %u of relation %s; zeroing out page",
 								blockNum,
 								relpath(smgr->smgr_rlocator, forkNum))));
-				MemSet((char *) bufBlock, 0, BLCKSZ);
+				MemSet((char *) bufBlock, 0, cluster_block_size);
 			}
 			else
 				ereport(ERROR,
@@ -1856,7 +1856,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
 		buf_block = BufHdrGetBlock(GetBufferDescriptor(buffers[i] - 1));
 
 		/* new buffers are zero-filled */
-		MemSet((char *) buf_block, 0, BLCKSZ);
+		MemSet((char *) buf_block, 0, cluster_block_size);
 	}
 
 	/* in case we need to pin an existing buffer below */
@@ -2285,7 +2285,7 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy)
 				 * not generally guaranteed to be marked undefined or
 				 * non-accessible in any case.
 				 */
-				VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
+				VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), cluster_block_size);
 				break;
 			}
 		}
@@ -2350,7 +2350,7 @@ PinBuffer_Locked(BufferDesc *buf)
 	 * Valgrind (this is similar to the PinBuffer() case where the backend
 	 * doesn't already have a buffer pin)
 	 */
-	VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
+	VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), cluster_block_size);
 
 	/*
 	 * Since we hold the buffer spinlock, we can update the buffer state and
@@ -2403,7 +2403,7 @@ UnpinBuffer(BufferDesc *buf)
 		 * within access method code that enforces that buffers are only
 		 * accessed while a buffer lock is held.
 		 */
-		VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), cluster_block_size);
 
 		/* I'd better not still hold the buffer content lock */
 		Assert(!LWLockHeldByMe(BufferDescriptorGetContentLock(buf)));
@@ -3491,7 +3491,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 	if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
 	{
 		/*
-		 * Not every table AM uses BLCKSZ wide fixed size blocks. Therefore
+		 * Not every table AM uses cluster_block_size wide fixed size blocks. Therefore
 		 * tableam returns the size in bytes - but for the purpose of this
 		 * routine, we want the number of blocks. Therefore divide, rounding
 		 * up.
@@ -3500,7 +3500,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 
 		szbytes = table_relation_size(relation, forkNum);
 
-		return (szbytes + (BLCKSZ - 1)) / BLCKSZ;
+		return (szbytes + (cluster_block_size - 1)) / cluster_block_size;
 	}
 	else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
 	{
@@ -4288,7 +4288,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
 	 * Bulk extend the destination relation of the same size as the source
 	 * relation before starting to copy block by block.
 	 */
-	memset(buf.data, 0, BLCKSZ);
+	memset(buf.data, 0, cluster_block_size);
 	smgrextend(smgropen(dstlocator, InvalidBackendId), forkNum, nblocks - 1,
 			   buf.data, true);
 
@@ -4316,7 +4316,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
 		START_CRIT_SECTION();
 
 		/* Copy page data from the source to the destination. */
-		memcpy(dstPage, srcPage, BLCKSZ);
+		memcpy(dstPage, srcPage, cluster_block_size);
 		MarkBufferDirty(dstBuf);
 
 		/* WAL-log the copied page. */
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index 1c804fd2f5..e7365030e9 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -589,7 +589,7 @@ GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)
 	Assert(ring_size_kb >= 0);
 
 	/* Figure out how many buffers ring_size_kb is */
-	ring_buffers = ring_size_kb / (BLCKSZ / 1024);
+	ring_buffers = ring_size_kb / (cluster_block_size / 1024);
 
 	/* 0 means unlimited, so no BufferAccessStrategy required */
 	if (ring_buffers == 0)
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 1735ec7141..935b870933 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -335,7 +335,7 @@ ExtendBufferedRelLocal(BufferManagerRelation bmr,
 		buf_block = LocalBufHdrGetBlock(buf_hdr);
 
 		/* new buffers are zero-filled */
-		MemSet((char *) buf_block, 0, BLCKSZ);
+		MemSet((char *) buf_block, 0, cluster_block_size);
 	}
 
 	first_block = smgrnblocks(bmr.smgr, fork);
@@ -745,19 +745,19 @@ GetLocalBufferStorage(void)
 		/* But not more than what we need for all remaining local bufs */
 		num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
 		/* And don't overflow MaxAllocSize, either */
-		num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
+		num_bufs = Min(num_bufs, MaxAllocSize / cluster_block_size);
 
 		/* Buffers should be I/O aligned. */
 		cur_block = (char *)
 			TYPEALIGN(PG_IO_ALIGN_SIZE,
 					  MemoryContextAlloc(LocalBufferContext,
-										 num_bufs * BLCKSZ + PG_IO_ALIGN_SIZE));
+										 num_bufs * cluster_block_size + PG_IO_ALIGN_SIZE));
 		next_buf_in_block = 0;
 		num_bufs_in_block = num_bufs;
 	}
 
 	/* Allocate next buffer in current memory block */
-	this_buf = cur_block + next_buf_in_block * BLCKSZ;
+	this_buf = cur_block + next_buf_in_block * cluster_block_size;
 	next_buf_in_block++;
 	total_bufs_allocated++;
 
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index 41ab64100e..cf5d980026 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -60,7 +60,7 @@
  * tablespaces when available.
  */
 #define MAX_PHYSICAL_FILESIZE	0x40000000
-#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / BLCKSZ)
+#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / cluster_block_size)
 
 /*
  * This data structure represents a buffered file that consists of one or
@@ -681,7 +681,7 @@ BufFileWrite(BufFile *file, const void *ptr, size_t size)
 
 	while (size > 0)
 	{
-		if (file->pos >= BLCKSZ)
+		if (file->pos >= cluster_block_size)
 		{
 			/* Buffer full, dump it out */
 			if (file->dirty)
@@ -695,7 +695,7 @@ BufFileWrite(BufFile *file, const void *ptr, size_t size)
 			}
 		}
 
-		nthistime = BLCKSZ - file->pos;
+		nthistime = cluster_block_size - file->pos;
 		if (nthistime > size)
 			nthistime = size;
 		Assert(nthistime > 0);
@@ -839,9 +839,9 @@ BufFileTell(BufFile *file, int *fileno, off_t *offset)
 /*
  * BufFileSeekBlock --- block-oriented seek
  *
- * Performs absolute seek to the start of the n'th BLCKSZ-sized block of
+ * Performs absolute seek to the start of the n'th cluster_block_size-sized block of
  * the file.  Note that users of this interface will fail if their files
- * exceed BLCKSZ * LONG_MAX bytes, but that is quite a lot; we don't work
+ * exceed cluster_block_size * LONG_MAX bytes, but that is quite a lot; we don't work
  * with tables bigger than that, either...
  *
  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
@@ -852,7 +852,7 @@ BufFileSeekBlock(BufFile *file, long blknum)
 {
 	return BufFileSeek(file,
 					   (int) (blknum / BUFFILE_SEG_SIZE),
-					   (off_t) (blknum % BUFFILE_SEG_SIZE) * BLCKSZ,
+					   (off_t) (blknum % BUFFILE_SEG_SIZE) * cluster_block_size,
 					   SEEK_SET);
 }
 
@@ -867,7 +867,7 @@ BufFileTellBlock(BufFile *file)
 {
 	long		blknum;
 
-	blknum = (file->curOffset + file->pos) / BLCKSZ;
+	blknum = (file->curOffset + file->pos) / cluster_block_size;
 	blknum += file->curFile * BUFFILE_SEG_SIZE;
 	return blknum;
 }
diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c
index e04bc3941a..327bd43d36 100644
--- a/src/backend/storage/file/copydir.c
+++ b/src/backend/storage/file/copydir.c
@@ -124,7 +124,7 @@ copy_file(const char *fromfile, const char *tofile)
 	off_t		flush_offset;
 
 	/* Size of copy buffer (read and write requests) */
-#define COPY_BUF_SIZE (8 * BLCKSZ)
+#define COPY_BUF_SIZE (8 * cluster_block_size)
 
 	/*
 	 * Size of data flush requests.  It seems beneficial on most platforms to
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index b490a76ba7..7207405e06 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3943,10 +3943,10 @@ check_debug_io_direct(char **newval, void **extra, GucSource source)
 		result = false;
 	}
 #endif
-#if BLCKSZ < PG_IO_ALIGN_SIZE
+#if cluster_block_size < PG_IO_ALIGN_SIZE
 	if (result && (flags & IO_DIRECT_DATA))
 	{
-		GUC_check_errdetail("debug_io_direct is not supported for data because BLCKSZ is too small");
+		GUC_check_errdetail("debug_io_direct is not supported for data because cluster_block_size is too small");
 		result = false;
 	}
 #endif
diff --git a/src/backend/storage/freespace/README b/src/backend/storage/freespace/README
index e7ff23b76f..4fa6bacc83 100644
--- a/src/backend/storage/freespace/README
+++ b/src/backend/storage/freespace/README
@@ -14,8 +14,8 @@ It is important to keep the map small so that it can be searched rapidly.
 Therefore, we don't attempt to record the exact free space on a page.
 We allocate one map byte to each page, allowing us to record free space
 at a granularity of 1/256th of a page.  Another way to say it is that
-the stored value is the free space divided by BLCKSZ/256 (rounding down).
-We assume that the free space must always be less than BLCKSZ, since
+the stored value is the free space divided by cluster_block_size/256 (rounding down).
+We assume that the free space must always be less than cluster_block_size, since
 all pages have some overhead; so the maximum map value is 255.
 
 To assist in fast searching, the map isn't simply an array of per-page
@@ -97,7 +97,7 @@ has the same value as the corresponding leaf node on its parent page.
 The root page is always stored at physical block 0.
 
 For example, assuming each FSM page can hold information about 4 pages (in
-reality, it holds (BLCKSZ - headers) / 2, or ~4000 with default BLCKSZ),
+reality, it holds (cluster_block_size - headers) / 2, or ~4000 with default cluster_block_size),
 we get a disk layout like this:
 
  0     <-- page 0 at level 2 (root page)
@@ -136,7 +136,7 @@ and so forth.
 
 To keep things simple, the tree is always constant height. To cover the
 maximum relation size of 2^32-1 blocks, three levels is enough with the default
-BLCKSZ (4000^3 > 2^32).
+cluster_block_size (4000^3 > 2^32).
 
 Addressing
 ----------
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index fb9440ff72..f17fe758d6 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -38,11 +38,11 @@
  * divide the amount of free space a page can have into 256 different
  * categories. The highest category, 255, represents a page with at least
  * MaxFSMRequestSize bytes of free space, and the second highest category
- * represents the range from 254 * FSM_CAT_STEP, inclusive, to
+ * represents the range from 254 * fsm_cat_step, inclusive, to
  * MaxFSMRequestSize, exclusive.
  *
- * MaxFSMRequestSize depends on the architecture and BLCKSZ, but assuming
- * default 8k BLCKSZ, and that MaxFSMRequestSize is 8164 bytes, the
+ * MaxFSMRequestSize depends on the architecture and cluster_block_size, but assuming
+ * default 8k cluster_block_size, and that MaxFSMRequestSize is 8164 bytes, the
  * categories look like this:
  *
  *
@@ -62,14 +62,14 @@
  * request of exactly MaxFSMRequestSize bytes.
  */
 #define FSM_CATEGORIES	256
-#define FSM_CAT_STEP	(BLCKSZ / FSM_CATEGORIES)
+#define fsm_cat_step	(cluster_block_size / FSM_CATEGORIES)
 #define MaxFSMRequestSize	MaxHeapTupleSize
 
 /*
  * Depth of the on-disk tree. We need to be able to address 2^32-1 blocks,
  * and 1626 is the smallest number that satisfies X^3 >= 2^32-1. Likewise,
  * 256 is the smallest number that satisfies X^4 >= 2^32-1. In practice,
- * this means that 4096 bytes is the smallest BLCKSZ that we can get away
+ * this means that 4096 bytes is the smallest cluster_block_size that we can get away
  * with a 3-level tree, and 512 is the smallest we support.
  */
 #define FSM_TREE_DEPTH	((SlotsPerFSMPage >= 1626) ? 3 : 4)
@@ -217,7 +217,7 @@ XLogRecordPageWithFreeSpace(RelFileLocator rlocator, BlockNumber heapBlk,
 
 	page = BufferGetPage(buf);
 	if (PageIsNew(page))
-		PageInit(page, BLCKSZ, 0);
+		PageInit(page, cluster_block_size, 0);
 
 	if (fsm_set_avail(page, slot, new_cat))
 		MarkBufferDirtyHint(buf, false);
@@ -370,12 +370,12 @@ fsm_space_avail_to_cat(Size avail)
 {
 	int			cat;
 
-	Assert(avail < BLCKSZ);
+	Assert(avail < cluster_block_size);
 
 	if (avail >= MaxFSMRequestSize)
 		return 255;
 
-	cat = avail / FSM_CAT_STEP;
+	cat = avail / fsm_cat_step;
 
 	/*
 	 * The highest category, 255, is reserved for MaxFSMRequestSize bytes or
@@ -398,7 +398,7 @@ fsm_space_cat_to_avail(uint8 cat)
 	if (cat == 255)
 		return MaxFSMRequestSize;
 	else
-		return cat * FSM_CAT_STEP;
+		return cat * fsm_cat_step;
 }
 
 /*
@@ -417,7 +417,7 @@ fsm_space_needed_to_cat(Size needed)
 	if (needed == 0)
 		return 1;
 
-	cat = (needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP;
+	cat = (needed + fsm_cat_step - 1) / fsm_cat_step;
 
 	if (cat > 255)
 		cat = 255;
@@ -598,7 +598,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), cluster_block_size, 0);
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	}
 	return buf;
diff --git a/src/backend/storage/freespace/indexfsm.c b/src/backend/storage/freespace/indexfsm.c
index fff8f4fbfb..0047793d7c 100644
--- a/src/backend/storage/freespace/indexfsm.c
+++ b/src/backend/storage/freespace/indexfsm.c
@@ -16,7 +16,7 @@
  *	This is similar to the FSM used for heap, in freespace.c, but instead
  *	of tracking the amount of free space on pages, we only track whether
  *	pages are completely free or in-use. We use the same FSM implementation
- *	as for heaps, using BLCKSZ - 1 to denote used pages, and 0 for unused.
+ *	as for heaps, using cluster_block_size - 1 to denote used pages, and 0 for unused.
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,7 @@
 BlockNumber
 GetFreeIndexPage(Relation rel)
 {
-	BlockNumber blkno = GetPageWithFreeSpace(rel, BLCKSZ / 2);
+	BlockNumber blkno = GetPageWithFreeSpace(rel, cluster_block_size / 2);
 
 	if (blkno != InvalidBlockNumber)
 		RecordUsedIndexPage(rel, blkno);
@@ -51,7 +51,7 @@ GetFreeIndexPage(Relation rel)
 void
 RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
 {
-	RecordPageWithFreeSpace(rel, freeBlock, BLCKSZ - 1);
+	RecordPageWithFreeSpace(rel, freeBlock, cluster_block_size - 1);
 }
 
 
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 84e543e731..17b834d39b 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -185,7 +185,7 @@ getdatafield(Form_pg_largeobject tuple,
 		freeit = true;
 	}
 	len = VARSIZE(datafield) - VARHDRSZ;
-	if (len < 0 || len > LOBLKSIZE)
+	if (len < 0 || len > cluster_loblksize)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
 				 errmsg("pg_largeobject entry for OID %u, page %d has invalid data field size %d",
@@ -414,7 +414,7 @@ inv_getsize(LargeObjectDesc *obj_desc)
 			elog(ERROR, "null field found in pg_largeobject");
 		data = (Form_pg_largeobject) GETSTRUCT(tuple);
 		getdatafield(data, &datafield, &len, &pfreeit);
-		lastbyte = (uint64) data->pageno * LOBLKSIZE + len;
+		lastbyte = (uint64) data->pageno * cluster_loblksize + len;
 		if (pfreeit)
 			pfree(datafield);
 	}
@@ -493,7 +493,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 	int64		n;
 	int64		off;
 	int			len;
-	int32		pageno = (int32) (obj_desc->offset / LOBLKSIZE);
+	int32		pageno = (int32) (obj_desc->offset / cluster_loblksize);
 	uint64		pageoff;
 	ScanKeyData skey[2];
 	SysScanDesc sd;
@@ -541,7 +541,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 		 * there may be missing pages if the LO contains unwritten "holes". We
 		 * want missing sections to read out as zeroes.
 		 */
-		pageoff = ((uint64) data->pageno) * LOBLKSIZE;
+		pageoff = ((uint64) data->pageno) * cluster_loblksize;
 		if (pageoff > obj_desc->offset)
 		{
 			n = pageoff - obj_desc->offset;
@@ -555,7 +555,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 		{
 			Assert(obj_desc->offset >= pageoff);
 			off = (int) (obj_desc->offset - pageoff);
-			Assert(off >= 0 && off < LOBLKSIZE);
+			Assert(off >= 0 && off < cluster_loblksize);
 
 			getdatafield(data, &datafield, &len, &pfreeit);
 			if (len > off)
@@ -586,7 +586,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	int			n;
 	int			off;
 	int			len;
-	int32		pageno = (int32) (obj_desc->offset / LOBLKSIZE);
+	int32		pageno = (int32) (obj_desc->offset / cluster_loblksize);
 	ScanKeyData skey[2];
 	SysScanDesc sd;
 	HeapTuple	oldtuple;
@@ -598,7 +598,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[LOBLKSIZE + VARHDRSZ];
+		char		data[cluster_loblksize + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
@@ -687,14 +687,14 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 			/*
 			 * Fill any hole
 			 */
-			off = (int) (obj_desc->offset % LOBLKSIZE);
+			off = (int) (obj_desc->offset % cluster_loblksize);
 			if (off > len)
 				MemSet(workb + len, 0, off - len);
 
 			/*
 			 * Insert appropriate portion of new data
 			 */
-			n = LOBLKSIZE - off;
+			n = cluster_loblksize - off;
 			n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
 			memcpy(workb + off, buf + nwritten, n);
 			nwritten += n;
@@ -732,14 +732,14 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 			 *
 			 * First, fill any hole
 			 */
-			off = (int) (obj_desc->offset % LOBLKSIZE);
+			off = (int) (obj_desc->offset % cluster_loblksize);
 			if (off > 0)
 				MemSet(workb, 0, off);
 
 			/*
 			 * Insert appropriate portion of new data
 			 */
-			n = LOBLKSIZE - off;
+			n = cluster_loblksize - off;
 			n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
 			memcpy(workb + off, buf + nwritten, n);
 			nwritten += n;
@@ -779,7 +779,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 void
 inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 {
-	int32		pageno = (int32) (len / LOBLKSIZE);
+	int32		pageno = (int32) (len / cluster_loblksize);
 	int32		off;
 	ScanKeyData skey[2];
 	SysScanDesc sd;
@@ -789,7 +789,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[LOBLKSIZE + VARHDRSZ];
+		char		data[cluster_loblksize + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
@@ -872,7 +872,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		/*
 		 * Fill any hole
 		 */
-		off = len % LOBLKSIZE;
+		off = len % cluster_loblksize;
 		if (off > pagelen)
 			MemSet(workb + pagelen, 0, off - pagelen);
 
@@ -911,7 +911,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		 *
 		 * Fill the hole up to the truncation point
 		 */
-		off = len % LOBLKSIZE;
+		off = len % cluster_loblksize;
 		if (off > 0)
 			MemSet(workb, 0, off);
 
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 1af41213b4..a16509089a 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -321,9 +321,9 @@ static SlruCtlData SerialSlruCtlData;
 
 #define SerialSlruCtl			(&SerialSlruCtlData)
 
-#define SERIAL_PAGESIZE			BLCKSZ
+#define serial_pagesize			cluster_block_size
 #define SERIAL_ENTRYSIZE			sizeof(SerCommitSeqNo)
-#define SERIAL_ENTRIESPERPAGE	(SERIAL_PAGESIZE / SERIAL_ENTRYSIZE)
+#define SERIAL_ENTRIESPERPAGE	(serial_pagesize / SERIAL_ENTRYSIZE)
 
 /*
  * Set maximum pages based on the number needed to track all transactions.
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 9a302ddc30..07a664355f 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -45,7 +45,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
 
 	specialSize = MAXALIGN(specialSize);
 
-	Assert(pageSize == BLCKSZ);
+	Assert(pageSize == cluster_block_size);
 	Assert(pageSize > specialSize + SizeOfPageHeaderData);
 
 	/* Make sure all fields of page are zero, as well as unused space */
@@ -117,7 +117,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 		if ((p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
 			p->pd_lower <= p->pd_upper &&
 			p->pd_upper <= p->pd_special &&
-			p->pd_special <= BLCKSZ &&
+			p->pd_special <= cluster_block_size &&
 			p->pd_special == MAXALIGN(p->pd_special))
 			header_sane = true;
 
@@ -128,7 +128,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 	/* Check all-zeroes case */
 	all_zeroes = true;
 	pagebytes = (size_t *) page;
-	for (i = 0; i < (BLCKSZ / sizeof(size_t)); i++)
+	for (i = 0; i < (cluster_block_size / sizeof(size_t)); i++)
 	{
 		if (pagebytes[i] != 0)
 		{
@@ -211,7 +211,7 @@ PageAddItemExtended(Page page,
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ)
+		phdr->pd_special > cluster_block_size)
 		ereport(PANIC,
 				(errcode(ERRCODE_DATA_CORRUPTED),
 				 errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
@@ -723,7 +723,7 @@ PageRepairFragmentation(Page page)
 	if (pd_lower < SizeOfPageHeaderData ||
 		pd_lower > pd_upper ||
 		pd_upper > pd_special ||
-		pd_special > BLCKSZ ||
+		pd_special > cluster_block_size ||
 		pd_special != MAXALIGN(pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1066,7 +1066,7 @@ PageIndexTupleDelete(Page page, OffsetNumber offnum)
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > cluster_block_size ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1201,7 +1201,7 @@ PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
 	if (pd_lower < SizeOfPageHeaderData ||
 		pd_lower > pd_upper ||
 		pd_upper > pd_special ||
-		pd_special > BLCKSZ ||
+		pd_special > cluster_block_size ||
 		pd_special != MAXALIGN(pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1307,7 +1307,7 @@ PageIndexTupleDeleteNoCompact(Page page, OffsetNumber offnum)
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > cluster_block_size ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1419,7 +1419,7 @@ PageIndexTupleOverwrite(Page page, OffsetNumber offnum,
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > cluster_block_size ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1523,11 +1523,11 @@ PageSetChecksumCopy(Page page, BlockNumber blkno)
 	 */
 	if (pageCopy == NULL)
 		pageCopy = MemoryContextAllocAligned(TopMemoryContext,
-											 BLCKSZ,
+											 cluster_block_size,
 											 PG_IO_ALIGN_SIZE,
 											 0);
 
-	memcpy(pageCopy, (char *) page, BLCKSZ);
+	memcpy(pageCopy, (char *) page, cluster_block_size);
 	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno);
 	return pageCopy;
 }
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index fdecbad170..319e8d8655 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -467,7 +467,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= cluster_block_size)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	/* This assert is too expensive to have on normally ... */
@@ -490,11 +490,11 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync, EXTENSION_CREATE);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != BLCKSZ)
+	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != cluster_block_size)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -507,7 +507,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 				(errcode(ERRCODE_DISK_FULL),
 				 errmsg("could not extend file \"%s\": wrote only %d of %d bytes at block %u",
 						FilePathName(v->mdfd_vfd),
-						nbytes, BLCKSZ, blocknum),
+						nbytes, cluster_block_size, blocknum),
 				 errhint("Check free disk space.")));
 	}
 
@@ -553,7 +553,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 	while (remblocks > 0)
 	{
 		BlockNumber segstartblock = curblocknum % ((BlockNumber) RELSEG_SIZE);
-		off_t		seekpos = (off_t) BLCKSZ * segstartblock;
+		off_t		seekpos = (off_t) cluster_block_size * segstartblock;
 		int			numblocks;
 
 		if (segstartblock + remblocks > RELSEG_SIZE)
@@ -582,7 +582,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 			int			ret;
 
 			ret = FileFallocate(v->mdfd_vfd,
-								seekpos, (off_t) BLCKSZ * numblocks,
+								seekpos, (off_t) cluster_block_size * numblocks,
 								WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret != 0)
 			{
@@ -605,7 +605,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 			 * whole length of the extension.
 			 */
 			ret = FileZero(v->mdfd_vfd,
-						   seekpos, (off_t) BLCKSZ * numblocks,
+						   seekpos, (off_t) cluster_block_size * numblocks,
 						   WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret < 0)
 				ereport(ERROR,
@@ -726,11 +726,11 @@ mdprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 	if (v == NULL)
 		return false;
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	(void) FilePrefetch(v->mdfd_vfd, seekpos, BLCKSZ, WAIT_EVENT_DATA_FILE_PREFETCH);
+	(void) FilePrefetch(v->mdfd_vfd, seekpos, cluster_block_size, WAIT_EVENT_DATA_FILE_PREFETCH);
 #endif							/* USE_PREFETCH */
 
 	return true;
@@ -748,7 +748,7 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= cluster_block_size)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	TRACE_POSTGRESQL_SMGR_MD_READ_START(forknum, blocknum,
@@ -760,11 +760,11 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, false,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_READ);
+	nbytes = FileRead(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_READ);
 
 	TRACE_POSTGRESQL_SMGR_MD_READ_DONE(forknum, blocknum,
 									   reln->smgr_rlocator.locator.spcOid,
@@ -772,9 +772,9 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 									   reln->smgr_rlocator.locator.relNumber,
 									   reln->smgr_rlocator.backend,
 									   nbytes,
-									   BLCKSZ);
+									   cluster_block_size);
 
-	if (nbytes != BLCKSZ)
+	if (nbytes != cluster_block_size)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -791,13 +791,13 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 		 * update a block that was later truncated away.
 		 */
 		if (zero_damaged_pages || InRecovery)
-			MemSet(buffer, 0, BLCKSZ);
+			MemSet(buffer, 0, cluster_block_size);
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
 							blocknum, FilePathName(v->mdfd_vfd),
-							nbytes, BLCKSZ)));
+							nbytes, cluster_block_size)));
 	}
 }
 
@@ -817,7 +817,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= cluster_block_size)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	/* This assert is too expensive to have on normally ... */
@@ -834,11 +834,11 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
+	nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
 
 	TRACE_POSTGRESQL_SMGR_MD_WRITE_DONE(forknum, blocknum,
 										reln->smgr_rlocator.locator.spcOid,
@@ -846,9 +846,9 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 										reln->smgr_rlocator.locator.relNumber,
 										reln->smgr_rlocator.backend,
 										nbytes,
-										BLCKSZ);
+										cluster_block_size);
 
-	if (nbytes != BLCKSZ)
+	if (nbytes != cluster_block_size)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -861,7 +861,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 				 errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
 						blocknum,
 						FilePathName(v->mdfd_vfd),
-						nbytes, BLCKSZ),
+						nbytes, cluster_block_size),
 				 errhint("Check free disk space.")));
 	}
 
@@ -917,9 +917,9 @@ mdwriteback(SMgrRelation reln, ForkNumber forknum,
 		Assert(nflush >= 1);
 		Assert(nflush <= nblocks);
 
-		seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+		seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-		FileWriteback(v->mdfd_vfd, seekpos, (off_t) BLCKSZ * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
+		FileWriteback(v->mdfd_vfd, seekpos, (off_t) cluster_block_size * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
 
 		nblocks -= nflush;
 		blocknum += nflush;
@@ -1061,7 +1061,7 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 			 */
 			BlockNumber lastsegblocks = nblocks - priorblocks;
 
-			if (FileTruncate(v->mdfd_vfd, (off_t) lastsegblocks * BLCKSZ, WAIT_EVENT_DATA_FILE_TRUNCATE) < 0)
+			if (FileTruncate(v->mdfd_vfd, (off_t) lastsegblocks * cluster_block_size, WAIT_EVENT_DATA_FILE_TRUNCATE) < 0)
 				ereport(ERROR,
 						(errcode_for_file_access(),
 						 errmsg("could not truncate file \"%s\" to %u blocks: %m",
@@ -1458,7 +1458,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 			 */
 			if (nblocks < ((BlockNumber) RELSEG_SIZE))
 			{
-				char	   *zerobuf = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE,
+				char	   *zerobuf = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE,
 													 MCXT_ALLOC_ZERO);
 
 				mdextend(reln, forknum,
@@ -1529,7 +1529,7 @@ _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 				 errmsg("could not seek to end of file \"%s\": %m",
 						FilePathName(seg->mdfd_vfd))));
 	/* note that this calculation will ignore any partial block at EOF */
-	return (BlockNumber) (len / BLCKSZ);
+	return (BlockNumber) (len / cluster_block_size);
 }
 
 /*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 2b9742ad21..1bb1ae53b1 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1405,12 +1405,12 @@ pg_stat_get_io(PG_FUNCTION_ARGS)
 				values[IO_COL_RESET_TIME] = TimestampTzGetDatum(reset_time);
 
 				/*
-				 * Hard-code this to the value of BLCKSZ for now. Future
+				 * Hard-code this to the value of cluster_block_size for now. Future
 				 * values could include XLOG_BLCKSZ, once WAL IO is tracked,
 				 * and constant multipliers, once non-block-oriented IO (e.g.
 				 * temporary file IO) is tracked.
 				 */
-				values[IO_COL_CONVERSION] = Int64GetDatum(BLCKSZ);
+				values[IO_COL_CONVERSION] = Int64GetDatum(cluster_block_size);
 
 				for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
 				{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c4fcd0076e..83577060f8 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -7760,7 +7760,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 * around 3 bytes per item is fairly typical.
 	 */
 	dataPagesFetchedBySel = ceil(*indexSelectivity *
-								 (numTuples / (BLCKSZ / 3)));
+								 (numTuples / (cluster_block_size / 3)));
 	if (dataPagesFetchedBySel > dataPagesFetched)
 		dataPagesFetched = dataPagesFetchedBySel;
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 1e671c560c..37d91cb902 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1501,8 +1501,8 @@ AddToDataDirLockFile(int target_line, const char *str)
 	int			lineno;
 	char	   *srcptr;
 	char	   *destptr;
-	char		srcbuffer[BLCKSZ];
-	char		destbuffer[BLCKSZ];
+	char		srcbuffer[cluster_block_size];
+	char		destbuffer[cluster_block_size];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
@@ -1626,7 +1626,7 @@ RecheckDataDirLockFile(void)
 	int			fd;
 	int			len;
 	long		file_pid;
-	char		buffer[BLCKSZ];
+	char		buffer[cluster_block_size];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 84e7ad4d90..1ec0edd6ac 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -105,8 +105,8 @@ typedef struct
 } unit_conversion;
 
 /* Ensure that the constants in the tables don't overflow or underflow */
-#if BLCKSZ < 1024 || BLCKSZ > (1024*1024)
-#error BLCKSZ must be between 1KB and 1MB
+#if cluster_block_size < 1024 || cluster_block_size > (1024*1024)
+#error cluster_block_size must be between 1KB and 1MB
 #endif
 #if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
 #error XLOG_BLCKSZ must be between 1KB and 1MB
@@ -134,11 +134,11 @@ static const unit_conversion memory_unit_conversion_table[] =
 	{"kB", GUC_UNIT_MB, 1.0 / 1024.0},
 	{"B", GUC_UNIT_MB, 1.0 / (1024.0 * 1024.0)},
 
-	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (BLCKSZ / 1024)},
-	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (BLCKSZ / 1024)},
-	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (BLCKSZ / 1024)},
-	{"kB", GUC_UNIT_BLOCKS, 1.0 / (BLCKSZ / 1024)},
-	{"B", GUC_UNIT_BLOCKS, 1.0 / BLCKSZ},
+	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (cluster_block_size / 1024)},
+	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (cluster_block_size / 1024)},
+	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (cluster_block_size / 1024)},
+	{"kB", GUC_UNIT_BLOCKS, 1.0 / (cluster_block_size / 1024)},
+	{"B", GUC_UNIT_BLOCKS, 1.0 / cluster_block_size},
 
 	{"TB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
 	{"GB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
@@ -2782,7 +2782,7 @@ get_config_unit_name(int flags)
 
 				/* initialize if first time through */
 				if (bbuf[0] == '\0')
-					snprintf(bbuf, sizeof(bbuf), "%dkB", BLCKSZ / 1024);
+					snprintf(bbuf, sizeof(bbuf), "%dkB", cluster_block_size / 1024);
 				return bbuf;
 			}
 		case GUC_UNIT_XBLOCKS:
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index e565a3092f..a465f7f9d1 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -3118,7 +3118,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&block_size,
-		BLCKSZ, BLCKSZ, BLCKSZ,
+		cluster_block_size, cluster_block_size, cluster_block_size,
 		NULL, NULL, NULL
 	},
 
@@ -3368,7 +3368,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_table_scan_size,
-		(8 * 1024 * 1024) / BLCKSZ, 0, INT_MAX / 3,
+		(8 * 1024 * 1024) / cluster_block_size, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -3379,7 +3379,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_index_scan_size,
-		(512 * 1024) / BLCKSZ, 0, INT_MAX / 3,
+		(512 * 1024) / cluster_block_size, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index 52b8898d5e..f46d2bac60 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -27,7 +27,7 @@
  * larger size than the underlying OS may support.
  *
  * For simplicity, we allocate and release space in the underlying file
- * in BLCKSZ-size blocks.  Space allocation boils down to keeping track
+ * in cluster_block_size-size blocks.  Space allocation boils down to keeping track
  * of which blocks in the underlying file belong to which logical tape,
  * plus any blocks that are free (recycled and not yet reused).
  * The blocks in each logical tape form a chain, with a prev- and next-
@@ -86,7 +86,7 @@
 #include "utils/memutils.h"
 
 /*
- * A TapeBlockTrailer is stored at the end of each BLCKSZ block.
+ * A TapeBlockTrailer is stored at the end of each cluster_block_size block.
  *
  * The first block of a tape has prev == -1.  The last block of a tape
  * stores the number of valid bytes on the block, inverted, in 'next'
@@ -100,7 +100,7 @@ typedef struct TapeBlockTrailer
 								 * bytes on last block (if < 0) */
 } TapeBlockTrailer;
 
-#define TapeBlockPayloadSize  (BLCKSZ - sizeof(TapeBlockTrailer))
+#define TapeBlockPayloadSize  (cluster_block_size - sizeof(TapeBlockTrailer))
 #define TapeBlockGetTrailer(buf) \
 	((TapeBlockTrailer *) ((char *) buf + TapeBlockPayloadSize))
 
@@ -192,7 +192,7 @@ struct LogicalTapeSet
 
 	/*
 	 * File size tracking.  nBlocksWritten is the size of the underlying file,
-	 * in BLCKSZ blocks.  nBlocksAllocated is the number of blocks allocated
+	 * in cluster_block_size blocks.  nBlocksAllocated is the number of blocks allocated
 	 * by ltsReleaseBlock(), and it is always greater than or equal to
 	 * nBlocksWritten.  Blocks between nBlocksAllocated and nBlocksWritten are
 	 * blocks that have been allocated for a tape, but have not been written
@@ -265,7 +265,7 @@ ltsWriteBlock(LogicalTapeSet *lts, long blocknum, const void *buffer)
 				(errcode_for_file_access(),
 				 errmsg("could not seek to block %ld of temporary file",
 						blocknum)));
-	BufFileWrite(lts->pfile, buffer, BLCKSZ);
+	BufFileWrite(lts->pfile, buffer, cluster_block_size);
 
 	/* Update nBlocksWritten, if we extended the file */
 	if (blocknum == lts->nBlocksWritten)
@@ -286,7 +286,7 @@ ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
 				(errcode_for_file_access(),
 				 errmsg("could not seek to block %ld of temporary file",
 						blocknum)));
-	BufFileReadExact(lts->pfile, buffer, BLCKSZ);
+	BufFileReadExact(lts->pfile, buffer, cluster_block_size);
 }
 
 /*
@@ -328,7 +328,7 @@ ltsReadFillBuffer(LogicalTape *lt)
 			lt->nextBlockNumber = TapeBlockGetTrailer(thisbuf)->next;
 
 		/* Advance to next block, if we have buffer space left */
-	} while (lt->buffer_size - lt->nbytes > BLCKSZ);
+	} while (lt->buffer_size - lt->nbytes > cluster_block_size);
 
 	return (lt->nbytes > 0);
 }
@@ -640,7 +640,7 @@ LogicalTapeImport(LogicalTapeSet *lts, int worker, TapeShare *shared)
 	}
 	/* Don't allocate more for read buffer than could possibly help */
 	lt->max_size = Min(MaxAllocSize, filesize);
-	tapeblocks = filesize / BLCKSZ;
+	tapeblocks = filesize / cluster_block_size;
 
 	/*
 	 * Update # of allocated blocks and # blocks written to reflect the
@@ -769,8 +769,8 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
 	/* Allocate data buffer and first block on first write */
 	if (lt->buffer == NULL)
 	{
-		lt->buffer = (char *) palloc(BLCKSZ);
-		lt->buffer_size = BLCKSZ;
+		lt->buffer = (char *) palloc(cluster_block_size);
+		lt->buffer_size = cluster_block_size;
 	}
 	if (lt->curBlockNumber == -1)
 	{
@@ -783,7 +783,7 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
 		TapeBlockGetTrailer(lt->buffer)->prev = -1L;
 	}
 
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 	while (size > 0)
 	{
 		if (lt->pos >= (int) TapeBlockPayloadSize)
@@ -837,9 +837,9 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
  *
  * 'buffer_size' specifies how much memory to use for the read buffer.
  * Regardless of the argument, the actual amount of memory used is between
- * BLCKSZ and MaxAllocSize, and is a multiple of BLCKSZ.  The given value is
+ * cluster_block_size and MaxAllocSize, and is a multiple of cluster_block_size.  The given value is
  * rounded down and truncated to fit those constraints, if necessary.  If the
- * tape is frozen, the 'buffer_size' argument is ignored, and a small BLCKSZ
+ * tape is frozen, the 'buffer_size' argument is ignored, and a small cluster_block_size
  * byte buffer is used.
  */
 void
@@ -851,19 +851,19 @@ LogicalTapeRewindForRead(LogicalTape *lt, size_t buffer_size)
 	 * Round and cap buffer_size if needed.
 	 */
 	if (lt->frozen)
-		buffer_size = BLCKSZ;
+		buffer_size = cluster_block_size;
 	else
 	{
 		/* need at least one block */
-		if (buffer_size < BLCKSZ)
-			buffer_size = BLCKSZ;
+		if (buffer_size < cluster_block_size)
+			buffer_size = cluster_block_size;
 
 		/* palloc() larger than max_size is unlikely to be helpful */
 		if (buffer_size > lt->max_size)
 			buffer_size = lt->max_size;
 
-		/* round down to BLCKSZ boundary */
-		buffer_size -= buffer_size % BLCKSZ;
+		/* round down to cluster_block_size boundary */
+		buffer_size -= buffer_size % cluster_block_size;
 	}
 
 	if (lt->writing)
@@ -1015,12 +1015,12 @@ LogicalTapeFreeze(LogicalTape *lt, TapeShare *share)
 	 * we're reading from multiple tapes.  But at the end of a sort, when a
 	 * tape is frozen, we only read from a single tape anyway.
 	 */
-	if (!lt->buffer || lt->buffer_size != BLCKSZ)
+	if (!lt->buffer || lt->buffer_size != cluster_block_size)
 	{
 		if (lt->buffer)
 			pfree(lt->buffer);
-		lt->buffer = palloc(BLCKSZ);
-		lt->buffer_size = BLCKSZ;
+		lt->buffer = palloc(cluster_block_size);
+		lt->buffer_size = cluster_block_size;
 	}
 
 	/* Read the first block, or reset if tape is empty */
@@ -1064,7 +1064,7 @@ LogicalTapeBackspace(LogicalTape *lt, size_t size)
 	size_t		seekpos = 0;
 
 	Assert(lt->frozen);
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 
 	if (lt->buffer == NULL)
 		ltsInitReadBuffer(lt);
@@ -1134,7 +1134,7 @@ LogicalTapeSeek(LogicalTape *lt, long blocknum, int offset)
 {
 	Assert(lt->frozen);
 	Assert(offset >= 0 && offset <= TapeBlockPayloadSize);
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 
 	if (lt->buffer == NULL)
 		ltsInitReadBuffer(lt);
@@ -1167,7 +1167,7 @@ LogicalTapeTell(LogicalTape *lt, long *blocknum, int *offset)
 	Assert(lt->offsetBlockNumber == 0L);
 
 	/* With a larger buffer, 'pos' wouldn't be the same as offset within page */
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 
 	*blocknum = lt->curBlockNumber;
 	*offset = lt->pos;
diff --git a/src/backend/utils/sort/sharedtuplestore.c b/src/backend/utils/sort/sharedtuplestore.c
index 236be65f22..b34bed7466 100644
--- a/src/backend/utils/sort/sharedtuplestore.c
+++ b/src/backend/utils/sort/sharedtuplestore.c
@@ -37,7 +37,7 @@
  */
 #define STS_CHUNK_PAGES 4
 #define STS_CHUNK_HEADER_SIZE offsetof(SharedTuplestoreChunk, data)
-#define STS_CHUNK_DATA_SIZE (STS_CHUNK_PAGES * BLCKSZ - STS_CHUNK_HEADER_SIZE)
+#define STS_CHUNK_DATA_SIZE (STS_CHUNK_PAGES * cluster_block_size - STS_CHUNK_HEADER_SIZE)
 
 /* Chunk written to disk. */
 typedef struct SharedTuplestoreChunk
@@ -198,7 +198,7 @@ sts_flush_chunk(SharedTuplestoreAccessor *accessor)
 {
 	size_t		size;
 
-	size = STS_CHUNK_PAGES * BLCKSZ;
+	size = STS_CHUNK_PAGES * cluster_block_size;
 	BufFileWrite(accessor->write_file, accessor->write_chunk, size);
 	memset(accessor->write_chunk, 0, size);
 	accessor->write_pointer = &accessor->write_chunk->data[0];
@@ -332,11 +332,11 @@ sts_puttuple(SharedTuplestoreAccessor *accessor, void *meta_data,
 			/* First time through.  Allocate chunk. */
 			accessor->write_chunk = (SharedTuplestoreChunk *)
 				MemoryContextAllocZero(accessor->context,
-									   STS_CHUNK_PAGES * BLCKSZ);
+									   STS_CHUNK_PAGES * cluster_block_size);
 			accessor->write_chunk->ntuples = 0;
 			accessor->write_pointer = &accessor->write_chunk->data[0];
 			accessor->write_end = (char *)
-				accessor->write_chunk + STS_CHUNK_PAGES * BLCKSZ;
+				accessor->write_chunk + STS_CHUNK_PAGES * cluster_block_size;
 		}
 		else
 		{
@@ -445,7 +445,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
 	}
 	remaining_size = size - sizeof(uint32);
 	this_chunk_size = Min(remaining_size,
-						  BLCKSZ * STS_CHUNK_PAGES - accessor->read_bytes);
+						  cluster_block_size * STS_CHUNK_PAGES - accessor->read_bytes);
 	destination = accessor->read_buffer + sizeof(uint32);
 	BufFileReadExact(accessor->read_file, destination, this_chunk_size);
 	accessor->read_bytes += this_chunk_size;
@@ -468,7 +468,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
 					 errdetail_internal("Expected overflow chunk.")));
 		accessor->read_next_page += STS_CHUNK_PAGES;
 		this_chunk_size = Min(remaining_size,
-							  BLCKSZ * STS_CHUNK_PAGES -
+							  cluster_block_size * STS_CHUNK_PAGES -
 							  STS_CHUNK_HEADER_SIZE);
 		BufFileReadExact(accessor->read_file, destination, this_chunk_size);
 		accessor->read_bytes += this_chunk_size;
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index c7a6c03f97..f6f98bb273 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -179,8 +179,8 @@ typedef enum
  */
 #define MINORDER		6		/* minimum merge order */
 #define MAXORDER		500		/* maximum merge order */
-#define TAPE_BUFFER_OVERHEAD		BLCKSZ
-#define MERGE_BUFFER_SIZE			(BLCKSZ * 32)
+#define TAPE_BUFFER_OVERHEAD		cluster_block_size
+#define MERGE_BUFFER_SIZE			(cluster_block_size * 32)
 
 
 /*
@@ -1000,7 +1000,7 @@ tuplesort_updatemax(Tuplesortstate *state)
 	if (state->tapeset)
 	{
 		isSpaceDisk = true;
-		spaceUsed = LogicalTapeSetBlocks(state->tapeset) * BLCKSZ;
+		spaceUsed = LogicalTapeSetBlocks(state->tapeset) * cluster_block_size;
 	}
 	else
 	{
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 905b979947..84b0cc7954 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1108,8 +1108,8 @@ test_config_settings(void)
 
 	for (i = 0; i < bufslen; i++)
 	{
-		/* Use same amount of memory, independent of BLCKSZ */
-		test_buffs = (trial_bufs[i] * 8192) / BLCKSZ;
+		/* Use same amount of memory, independent of cluster_block_size */
+		test_buffs = (trial_bufs[i] * 8192) / cluster_block_size;
 		if (test_buffs <= ok_buffers)
 		{
 			test_buffs = ok_buffers;
@@ -1121,10 +1121,10 @@ test_config_settings(void)
 	}
 	n_buffers = test_buffs;
 
-	if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
-		printf("%dMB\n", (n_buffers * (BLCKSZ / 1024)) / 1024);
+	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
+		printf("%dMB\n", (n_buffers * (cluster_block_size / 1024)) / 1024);
 	else
-		printf("%dkB\n", n_buffers * (BLCKSZ / 1024));
+		printf("%dkB\n", n_buffers * (cluster_block_size / 1024));
 
 	printf(_("selecting default time zone ... "));
 	fflush(stdout);
@@ -1216,12 +1216,12 @@ setup_config(void)
 	conflines = replace_guc_value(conflines, "max_connections",
 								  repltok, false);
 
-	if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
+	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
 		snprintf(repltok, sizeof(repltok), "%dMB",
-				 (n_buffers * (BLCKSZ / 1024)) / 1024);
+				 (n_buffers * (cluster_block_size / 1024)) / 1024);
 	else
 		snprintf(repltok, sizeof(repltok), "%dkB",
-				 n_buffers * (BLCKSZ / 1024));
+				 n_buffers * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "shared_buffers",
 								  repltok, false);
 
@@ -1296,21 +1296,21 @@ setup_config(void)
 
 #if DEFAULT_BACKEND_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BACKEND_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_BACKEND_FLUSH_AFTER * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "backend_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_BGWRITER_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BGWRITER_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_BGWRITER_FLUSH_AFTER * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "bgwriter_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_CHECKPOINT_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "checkpoint_flush_after",
 								  repltok, true);
 #endif
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 19eb67e485..0323bc4937 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -204,18 +204,18 @@ scan_file(const char *fn, int segmentno)
 	for (blockno = 0;; blockno++)
 	{
 		uint16		csum;
-		int			r = read(f, buf.data, BLCKSZ);
+		int			r = read(f, buf.data, cluster_block_size);
 
 		if (r == 0)
 			break;
-		if (r != BLCKSZ)
+		if (r != cluster_block_size)
 		{
 			if (r < 0)
 				pg_fatal("could not read block %u in file \"%s\": %m",
 						 blockno, fn);
 			else
 				pg_fatal("could not read block %u in file \"%s\": read %d of %d",
-						 blockno, fn, r, BLCKSZ);
+						 blockno, fn, r, cluster_block_size);
 		}
 		blocks_scanned++;
 
@@ -259,19 +259,19 @@ scan_file(const char *fn, int segmentno)
 			header->pd_checksum = csum;
 
 			/* Seek back to beginning of block */
-			if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
+			if (lseek(f, -cluster_block_size, SEEK_CUR) < 0)
 				pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
 
 			/* Write block with checksum */
-			w = write(f, buf.data, BLCKSZ);
-			if (w != BLCKSZ)
+			w = write(f, buf.data, cluster_block_size);
+			if (w != cluster_block_size)
 			{
 				if (w < 0)
 					pg_fatal("could not write block %u in file \"%s\": %m",
 							 blockno, fn);
 				else
 					pg_fatal("could not write block %u in file \"%s\": wrote %d of %d",
-							 blockno, fn, w, BLCKSZ);
+							 blockno, fn, w, cluster_block_size);
 			}
 		}
 
@@ -551,11 +551,11 @@ main(int argc, char *argv[])
 	if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
 		pg_fatal("cluster is not compatible with this version of pg_checksums");
 
-	if (ControlFile->blcksz != BLCKSZ)
+	if (ControlFile->blcksz != cluster_block_size)
 	{
 		pg_log_error("database cluster is not compatible");
 		pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
-							ControlFile->blcksz, BLCKSZ);
+							ControlFile->blcksz, cluster_block_size);
 		exit(1);
 	}
 
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 25ecdaaa15..84c65069aa 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -686,14 +686,14 @@ GuessControlValues(void)
 
 	ControlFile.maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile.floatFormat = FLOATFORMAT_VALUE;
-	ControlFile.blcksz = BLCKSZ;
+	ControlFile.blcksz = cluster_block_size;
 	ControlFile.relseg_size = RELSEG_SIZE;
 	ControlFile.xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
 	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 = cluster_toast_max_chunk_size;
+	ControlFile.loblksize = cluster_loblksize;
 	ControlFile.float8ByVal = FLOAT8PASSBYVAL;
 
 	/*
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index bd5c598e20..a28de44170 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -333,7 +333,7 @@ process_target_wal_block_change(ForkNumber forknum, RelFileLocator rlocator,
 			{
 				off_t		end_offset;
 
-				end_offset = (blkno_inseg + 1) * BLCKSZ;
+				end_offset = (blkno_inseg + 1) * cluster_block_size;
 				if (end_offset <= entry->source_size && end_offset <= entry->target_size)
 					datapagemap_add(&entry->target_pages_to_overwrite, blkno_inseg);
 			}
@@ -468,7 +468,7 @@ calculate_totals(filemap_t *filemap)
 
 			iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
 			while (datapagemap_next(iter, &blk))
-				filemap->fetch_size += BLCKSZ;
+				filemap->fetch_size += cluster_block_size;
 
 			pg_free(iter);
 		}
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index 7f69f02441..d77dc890c9 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -571,8 +571,8 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
 			iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
 			while (datapagemap_next(iter, &blkno))
 			{
-				offset = blkno * BLCKSZ;
-				source->queue_fetch_range(source, entry->path, offset, BLCKSZ);
+				offset = blkno * cluster_block_size;
+				source->queue_fetch_range(source, entry->path, offset, cluster_block_size);
 			}
 			pg_free(iter);
 		}
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index d173602882..3143cf28aa 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -96,7 +96,7 @@ copyFile(const char *src, const char *dst,
 				 schemaName, relName, dst, strerror(errno));
 
 	/* copy in fairly large chunks for best efficiency */
-#define COPY_BUF_SIZE (50 * BLCKSZ)
+#define COPY_BUF_SIZE (50 * cluster_block_size)
 
 	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
 
@@ -187,7 +187,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 	struct stat statbuf;
 
 	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
+	rewriteVmBytesPerPage = (cluster_block_size - SizeOfPageHeaderData) / 2;
 
 	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
 		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
@@ -220,7 +220,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 		PageHeaderData pageheader;
 		bool		old_lastblk;
 
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
+		if ((bytesRead = read(src_fd, buffer.data, cluster_block_size)) != cluster_block_size)
 		{
 			if (bytesRead < 0)
 				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
@@ -230,7 +230,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 						 schemaName, relName, fromfile);
 		}
 
-		totalBytesRead += BLCKSZ;
+		totalBytesRead += cluster_block_size;
 		old_lastblk = (totalBytesRead == src_filesize);
 
 		/* Save the page header data */
@@ -296,7 +296,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 					pg_checksum_page(new_vmbuf.data, new_blkno);
 
 			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
+			if (write(dst_fd, new_vmbuf.data, cluster_block_size) != cluster_block_size)
 			{
 				/* if write didn't set errno, assume problem is no disk space */
 				if (errno == 0)
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index b9acfed3b7..b3ad58ec66 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -531,7 +531,7 @@ XLogRecordSaveFPWs(XLogReaderState *record, const char *savepath)
 		if (!file)
 			pg_fatal("could not open file \"%s\": %m", filename);
 
-		if (fwrite(page, BLCKSZ, 1, file) != 1)
+		if (fwrite(page, cluster_block_size, 1, file) != 1)
 			pg_fatal("could not write file \"%s\": %m", filename);
 
 		if (fclose(file) != 0)
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 74833c4acb..ea74aa1eb5 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -540,7 +540,7 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
 ssize_t
 pg_pwrite_zeros(int fd, size_t size, off_t offset)
 {
-	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth BLCKSZ */
+	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth cluster_block_size */
 	void	   *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
 	struct iovec iov[PG_IOV_MAX];
 	size_t		remaining_size = size;
@@ -558,10 +558,10 @@ pg_pwrite_zeros(int fd, size_t size, off_t offset)
 
 			iov[iovcnt].iov_base = zerobuf_addr;
 
-			if (remaining_size < BLCKSZ)
+			if (remaining_size < cluster_block_size)
 				this_iov_size = remaining_size;
 			else
-				this_iov_size = BLCKSZ;
+				this_iov_size = cluster_block_size;
 
 			iov[iovcnt].iov_len = this_iov_size;
 			remaining_size -= this_iov_size;
diff --git a/src/include/access/brin_page.h b/src/include/access/brin_page.h
index 3670ca6010..a4881cb971 100644
--- a/src/include/access/brin_page.h
+++ b/src/include/access/brin_page.h
@@ -85,12 +85,12 @@ typedef struct RevmapContents
 	ItemPointerData rm_tids[1];
 } RevmapContents;
 
-#define REVMAP_CONTENT_SIZE \
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
+#define revmap_content_size \
+	(cluster_block_size - MAXALIGN(SizeOfPageHeaderData) - \
 	 offsetof(RevmapContents, rm_tids) - \
 	 MAXALIGN(sizeof(BrinSpecialSpace)))
 /* max num of items in the array */
 #define REVMAP_PAGE_MAXITEMS \
-	(REVMAP_CONTENT_SIZE / sizeof(ItemPointerData))
+	(revmap_content_size / sizeof(ItemPointerData))
 
 #endif							/* BRIN_PAGE_H */
diff --git a/src/include/access/ginblock.h b/src/include/access/ginblock.h
index c59790ec5a..191471cb1c 100644
--- a/src/include/access/ginblock.h
+++ b/src/include/access/ginblock.h
@@ -248,7 +248,7 @@ typedef signed char GinNullCategory;
  */
 #define GinMaxItemSize \
 	Min(INDEX_SIZE_MASK, \
-		MAXALIGN_DOWN(((BLCKSZ - \
+		MAXALIGN_DOWN(((cluster_block_size - \
 						MAXALIGN(SizeOfPageHeaderData + 3 * sizeof(ItemIdData)) - \
 						MAXALIGN(sizeof(GinPageOpaqueData))) / 3)))
 
@@ -318,7 +318,7 @@ typedef signed char GinNullCategory;
 	 GinPageGetOpaque(page)->maxoff * sizeof(PostingItem))
 
 #define GinDataPageMaxDataSize	\
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+	(cluster_block_size - MAXALIGN(SizeOfPageHeaderData) \
 	 - MAXALIGN(sizeof(ItemPointerData)) \
 	 - MAXALIGN(sizeof(GinPageOpaqueData)))
 
@@ -326,7 +326,7 @@ typedef signed char GinNullCategory;
  * List pages
  */
 #define GinListPageSize  \
-	( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
+	( cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
 
 /*
  * A compressed posting list.
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 0235716c06..5fff12eb65 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -96,7 +96,7 @@ typedef GISTPageOpaqueData *GISTPageOpaque;
  * key size using opclass parameters.
  */
 #define GISTMaxIndexTupleSize	\
-	MAXALIGN_DOWN((BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
+	MAXALIGN_DOWN((cluster_block_size - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
 				  4 - sizeof(ItemIdData))
 
 #define GISTMaxIndexKeySize	\
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 3edc740a3f..4c1dd74db7 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -54,7 +54,7 @@ typedef struct
 /* Returns free space in node buffer page */
 #define PAGE_FREE_SPACE(nbp) (nbp->freespace)
 /* Checks if node buffer page is empty */
-#define PAGE_IS_EMPTY(nbp) (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)
+#define PAGE_IS_EMPTY(nbp) (nbp->freespace == cluster_block_size - BUFFER_PAGE_DATA_OFFSET)
 /* Checks if node buffers page don't contain sufficient space for index tuple */
 #define PAGE_NO_SPACE(nbp, itup) (PAGE_FREE_SPACE(nbp) < \
 										MAXALIGN(IndexTupleSize(itup)))
@@ -171,7 +171,7 @@ typedef struct GISTScanOpaqueData
 	GistNSN		curPageLSN;		/* pos in the WAL stream when page was read */
 
 	/* In a non-ordered search, returnable heap items are stored here: */
-	GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
+	GISTSearchHeapItem pageData[cluster_block_size / sizeof(IndexTupleData)];
 	OffsetNumber nPageData;		/* number of valid items in array */
 	OffsetNumber curPageData;	/* next item to return */
 	MemoryContext pageDataCxt;	/* context holding the fetched tuples, for
@@ -474,7 +474,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
 /* gistutil.c */
 
 #define GiSTPageSize   \
-	( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
+	( cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
 
 #define GIST_MIN_FILLFACTOR			10
 #define GIST_DEFAULT_FILLFACTOR		90
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9e035270a1..4f6df08020 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -224,10 +224,10 @@ typedef HashScanOpaqueData *HashScanOpaque;
  * needing to fit into the metapage.  (With 8K block size, 1024 bitmaps
  * limit us to 256 GB of overflow space...).  For smaller block size we
  * can not use 1024 bitmaps as it will lead to the meta page data crossing
- * the block size boundary.  So we use BLCKSZ to determine the maximum number
+ * the block size boundary.  So we use cluster_block_size to determine the maximum number
  * of bitmaps.
  */
-#define HASH_MAX_BITMAPS			Min(BLCKSZ / 8, 1024)
+#define HASH_MAX_BITMAPS			Min(cluster_block_size / 8, 1024)
 
 #define HASH_SPLITPOINT_PHASE_BITS	2
 #define HASH_SPLITPOINT_PHASES_PER_GRP	(1 << HASH_SPLITPOINT_PHASE_BITS)
@@ -279,7 +279,7 @@ typedef struct HashOptions
 	 ((HashOptions *) (relation)->rd_options)->fillfactor :	\
 	 HASH_DEFAULT_FILLFACTOR)
 #define HashGetTargetPageUsage(relation) \
-	(BLCKSZ * HashGetFillFactor(relation) / 100)
+	(cluster_block_size * HashGetFillFactor(relation) / 100)
 
 /*
  * Maximum size of a hash index item (it's okay to have only one per page)
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index 5c0a796f66..3658856a60 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -21,14 +21,14 @@
  * Find the maximum size of a tuple if there are to be N tuples per page.
  */
 #define MaximumBytesPerTuple(tuplesPerPage) \
-	MAXALIGN_DOWN((BLCKSZ - \
+	MAXALIGN_DOWN((cluster_block_size - \
 				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
 				  / (tuplesPerPage))
 
 /*
  * These symbols control toaster activation.  If a tuple is larger than
- * TOAST_TUPLE_THRESHOLD, we will try to toast it down to no more than
- * TOAST_TUPLE_TARGET bytes through compressing compressible fields and
+ * toast_tuple_threshold, we will try to toast it down to no more than
+ * cluster_toast_tuple_target bytes through compressing compressible fields and
  * moving EXTENDED and EXTERNAL data out-of-line.
  *
  * The numbers need not be the same, though they currently are.  It doesn't
@@ -40,14 +40,14 @@
  *
  * XXX while these can be modified without initdb, some thought needs to be
  * given to needs_toast_table() in toasting.c before unleashing random
- * changes.  Also see LOBLKSIZE in large_object.h, which can *not* be
+ * changes.  Also see cluster_loblksize in large_object.h, which can *not* be
  * changed without initdb.
  */
 #define TOAST_TUPLES_PER_PAGE	4
 
-#define TOAST_TUPLE_THRESHOLD	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
+#define toast_tuple_threshold	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
 
-#define TOAST_TUPLE_TARGET		TOAST_TUPLE_THRESHOLD
+#define cluster_toast_tuple_target		toast_tuple_threshold
 
 /*
  * The code will also consider moving MAIN data out-of-line, but only as a
@@ -58,7 +58,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE_MAIN	1
 
-#define TOAST_TUPLE_TARGET_MAIN MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
+#define toast_tuple_target_main MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
 
 /*
  * If an index value is larger than TOAST_INDEX_TARGET, we will try to
@@ -69,19 +69,19 @@
 
 /*
  * When we store an oversize datum externally, we divide it into chunks
- * containing at most TOAST_MAX_CHUNK_SIZE data bytes.  This number *must*
+ * containing at most cluster_toast_max_chunk_size data bytes.  This number *must*
  * be small enough that the completed toast-table tuple (including the
  * ID and sequence fields and all overhead) will fit on a page.
  * The coding here sets the size on the theory that we want to fit
  * EXTERN_TUPLES_PER_PAGE tuples of maximum size onto a page.
  *
- * NB: Changing TOAST_MAX_CHUNK_SIZE requires an initdb.
+ * NB: Changing cluster_toast_max_chunk_size requires an initdb.
  */
 #define EXTERN_TUPLES_PER_PAGE	4	/* tweak only this */
 
 #define EXTERN_TUPLE_MAX_SIZE	MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE)
 
-#define TOAST_MAX_CHUNK_SIZE	\
+#define cluster_toast_max_chunk_size	\
 	(EXTERN_TUPLE_MAX_SIZE -							\
 	 MAXALIGN(SizeofHeapTupleHeader) -					\
 	 sizeof(Oid) -										\
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index e01f4f35c8..005a530e0a 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -551,7 +551,7 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
 
 /*
  * MaxHeapTupleSize is the maximum allowed size of a heap tuple, including
- * header and MAXALIGN alignment padding.  Basically it's BLCKSZ minus the
+ * header and MAXALIGN alignment padding.  Basically it's cluster_block_size minus the
  * other stuff that has to be on a disk page.  Since heap pages use no
  * "special space", there's no deduction for that.
  *
@@ -560,7 +560,7 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * ItemIds and tuples have different alignment requirements, don't assume that
  * you can, say, fit 2 tuples of size MaxHeapTupleSize/2 on the same page.
  */
-#define MaxHeapTupleSize  (BLCKSZ - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+#define MaxHeapTupleSize  (cluster_block_size - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
 #define MinHeapTupleSize  MAXALIGN(SizeofHeapTupleHeader)
 
 /*
@@ -575,7 +575,7 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * require increases in the size of work arrays.
  */
 #define MaxHeapTuplesPerPage	\
-	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
+	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
 			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
 
 /*
diff --git a/src/include/access/itup.h b/src/include/access/itup.h
index 2e2b8c7a47..8a68b9405b 100644
--- a/src/include/access/itup.h
+++ b/src/include/access/itup.h
@@ -164,7 +164,7 @@ index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
  * But such a page always has at least MAXALIGN special space, so we're safe.
  */
 #define MaxIndexTuplesPerPage	\
-	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
+	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
 			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
 
 #endif							/* ITUP_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 8891fa7973..033624e9c3 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -183,7 +183,7 @@ typedef struct BTMetaPageData
  * than necessary as a result, which is considered acceptable.
  */
 #define MaxTIDsPerBTreePage \
-	(int) ((BLCKSZ - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
+	(int) ((cluster_block_size - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
 		   sizeof(ItemPointerData))
 
 /*
@@ -1057,7 +1057,7 @@ typedef struct BTScanOpaqueData
 	/*
 	 * If we are doing an index-only scan, these are the tuple storage
 	 * workspaces for the currPos and markPos respectively.  Each is of size
-	 * BLCKSZ, so it can hold as much as a full page's worth of tuples.
+	 * cluster_block_size, so it can hold as much as a full page's worth of tuples.
 	 */
 	char	   *currTuples;		/* tuple storage for currPos */
 	char	   *markTuples;		/* tuple storage for markPos */
@@ -1104,7 +1104,7 @@ typedef struct BTOptions
 	 ((BTOptions *) (relation)->rd_options)->fillfactor : \
 	 BTREE_DEFAULT_FILLFACTOR)
 #define BTGetTargetPageFreeSpace(relation) \
-	(BLCKSZ * (100 - BTGetFillFactor(relation)) / 100)
+	(cluster_block_size * (100 - BTGetFillFactor(relation)) / 100)
 #define BTGetDeduplicateItems(relation) \
 	(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
 				 relation->rd_rel->relam == BTREE_AM_OID), \
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index a8a424d92d..f008a01acb 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -19,7 +19,7 @@
 
 
 /*
- * Define SLRU segment size.  A page is the same BLCKSZ as is used everywhere
+ * Define SLRU segment size.  A page is the same cluster_block_size as is used everywhere
  * else in Postgres.  The segment size can be chosen somewhat arbitrarily;
  * we make it 32 pages by default, or 256Kb, i.e. 1M transactions for CLOG
  * or 64K transactions for SUBTRANS.
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index c6ef46fc20..ea803eccd8 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -36,7 +36,7 @@ typedef struct SpGistOptions
 	 ((SpGistOptions *) (relation)->rd_options)->fillfactor : \
 	 SPGIST_DEFAULT_FILLFACTOR)
 #define SpGistGetTargetPageFreeSpace(relation) \
-	(BLCKSZ * (100 - SpGistGetFillFactor(relation)) / 100)
+	(cluster_block_size * (100 - SpGistGetFillFactor(relation)) / 100)
 
 
 /* SPGiST leaf tuples have one key column, optionally have included columns */
@@ -444,8 +444,8 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
  */
 
 /* Page capacity after allowing for fixed header and special space */
-#define SPGIST_PAGE_CAPACITY  \
-	MAXALIGN_DOWN(BLCKSZ - \
+#define spgist_page_capacity  \
+	MAXALIGN_DOWN(cluster_block_size - \
 				  SizeOfPageHeaderData - \
 				  MAXALIGN(sizeof(SpGistPageOpaqueData)))
 
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index f355e08e1d..5395678786 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -122,21 +122,21 @@ typedef struct XLogRecordBlockHeader
  * in the middle, which contains only zero bytes.  Since we know that the
  * "hole" is all zeros, we remove it from the stored data (and it's not counted
  * in the XLOG record's CRC, either).  Hence, the amount of block data actually
- * present is (BLCKSZ - <length of "hole" bytes>).
+ * present is (cluster_block_size - <length of "hole" bytes>).
  *
  * Additionally, when wal_compression is enabled, we will try to compress full
  * page images using one of the supported algorithms, after removing the
  * "hole". This can reduce the WAL volume, but at some extra cost of CPU spent
  * on the compression during WAL logging. In this case, since the "hole"
  * length cannot be calculated by subtracting the number of page image bytes
- * from BLCKSZ, basically it needs to be stored as an extra information.
+ * from cluster_block_size, basically it needs to be stored as an extra information.
  * But when no "hole" exists, we can assume that the "hole" length is zero
  * and no such an extra information needs to be stored. Note that
  * the original version of page image is stored in WAL instead of the
  * compressed one if the number of bytes saved by compression is less than
  * the length of extra information. Hence, when a page image is successfully
  * compressed, the amount of block data actually present is less than
- * BLCKSZ - the length of "hole" bytes - the length of extra information.
+ * cluster_block_size - the length of "hole" bytes - the length of extra information.
  */
 typedef struct XLogRecordBlockImageHeader
 {
diff --git a/src/include/backup/basebackup_sink.h b/src/include/backup/basebackup_sink.h
index 224732e333..53b0e6e485 100644
--- a/src/include/backup/basebackup_sink.h
+++ b/src/include/backup/basebackup_sink.h
@@ -80,7 +80,7 @@ typedef struct bbsink_state
  * 'bbs_ops' is the relevant callback table.
  *
  * 'bbs_buffer' is the buffer into which data destined for the bbsink
- * should be stored. It must be a multiple of BLCKSZ.
+ * should be stored. It must be a multiple of cluster_block_size.
  *
  * 'bbs_buffer_length' is the allocated length of the buffer.
  *
@@ -183,7 +183,7 @@ bbsink_begin_backup(bbsink *sink, bbsink_state *state, int buffer_length)
 	sink->bbs_ops->begin_backup(sink);
 
 	Assert(sink->bbs_buffer != NULL);
-	Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
+	Assert((sink->bbs_buffer_length % cluster_block_size) == 0);
 }
 
 /* Begin an archive. */
diff --git a/src/include/c.h b/src/include/c.h
index 82f8e9d4c7..f156886798 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1123,7 +1123,7 @@ extern void ExceptionalCondition(const char *conditionName,
 	((var) = ((var) < 0) ? 1 : -(var))
 
 /*
- * Use this, not "char buf[BLCKSZ]", to declare a field or local variable
+ * Use this, not "char buf[cluster_block_size]", to declare a field or local variable
  * holding a page buffer, if that page might be accessed as a page.  Otherwise
  * the variable might be under-aligned, causing problems on alignment-picky
  * hardware.  We include both "double" and "int64" in the union to ensure that
@@ -1132,7 +1132,7 @@ extern void ExceptionalCondition(const char *conditionName,
  */
 typedef union PGAlignedBlock
 {
-	char		data[BLCKSZ];
+	char		data[cluster_block_size];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGAlignedBlock;
@@ -1150,7 +1150,7 @@ typedef union PGIOAlignedBlock
 #ifdef pg_attribute_aligned
 	pg_attribute_aligned(PG_IO_ALIGN_SIZE)
 #endif
-	char		data[BLCKSZ];
+	char		data[cluster_block_size];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGIOAlignedBlock;
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 8a6e67a445..0446bfb407 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -33,7 +33,7 @@
  *
  * The minimum value is 8 (GIN indexes use 8-argument support functions).
  * The maximum possible value is around 600 (limited by index tuple size in
- * pg_proc's index; BLCKSZ larger than 8K would allow more).  Values larger
+ * pg_proc's index; cluster_block_size larger than 8K would allow more).  Values larger
  * than needed will waste memory and processing time, but do not directly
  * cost disk space.
  *
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index 240ad4e93b..1ed3d694ee 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -70,4 +70,5 @@ typedef PG_INT64_TYPE pg_int64;
 #define PG_DIAG_SOURCE_LINE		'L'
 #define PG_DIAG_SOURCE_FUNCTION 'R'
 
+#define cluster_block_size BLCKSZ
 #endif							/* POSTGRES_EXT_H */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index b379c76e27..b33ea1a261 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -323,7 +323,7 @@ BufferGetBlock(Buffer buffer)
 	if (BufferIsLocal(buffer))
 		return LocalBufferBlockPointers[-buffer - 1];
 	else
-		return (Block) (BufferBlocks + ((Size) (buffer - 1)) * BLCKSZ);
+		return (Block) (BufferBlocks + ((Size) (buffer - 1)) * cluster_block_size);
 }
 
 /*
@@ -341,7 +341,7 @@ static inline Size
 BufferGetPageSize(Buffer buffer)
 {
 	AssertMacro(BufferIsValid(buffer));
-	return (Size) BLCKSZ;
+	return (Size) cluster_block_size;
 }
 
 /*
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 424ecba028..20d8d7277a 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -325,7 +325,7 @@ static inline void
 PageValidateSpecialPointer(Page page)
 {
 	Assert(page);
-	Assert(((PageHeader) page)->pd_special <= BLCKSZ);
+	Assert(((PageHeader) page)->pd_special <= cluster_block_size);
 	Assert(((PageHeader) page)->pd_special >= SizeOfPageHeaderData);
 }
 
@@ -476,14 +476,14 @@ do { \
 						   PIV_LOG_WARNING | PIV_REPORT_STAT)
 
 /*
- * Check that BLCKSZ is a multiple of sizeof(size_t).  In
+ * Check that cluster_block_size is a multiple of sizeof(size_t).  In
  * PageIsVerifiedExtended(), it is much faster to check if a page is
  * full of zeroes using the native word size.  Note that this assertion
  * is kept within a header to make sure that StaticAssertDecl() works
  * across various combinations of platforms and compilers.
  */
-StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),
-				 "BLCKSZ has to be a multiple of sizeof(size_t)");
+StaticAssertDecl(cluster_block_size == ((cluster_block_size / sizeof(size_t)) * sizeof(size_t)),
+				 "cluster_block_size has to be a multiple of sizeof(size_t)");
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
 extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
diff --git a/src/include/storage/checksum_impl.h b/src/include/storage/checksum_impl.h
index 7b157161a2..18951c36c7 100644
--- a/src/include/storage/checksum_impl.h
+++ b/src/include/storage/checksum_impl.h
@@ -111,7 +111,7 @@
 typedef union
 {
 	PageHeaderData phdr;
-	uint32		data[BLCKSZ / (sizeof(uint32) * N_SUMS)][N_SUMS];
+	uint32		data[cluster_block_size / (sizeof(uint32) * N_SUMS)][N_SUMS];
 } PGChecksummablePage;
 
 /*
@@ -151,13 +151,13 @@ pg_checksum_block(const PGChecksummablePage *page)
 				j;
 
 	/* ensure that the size is compatible with the algorithm */
-	Assert(sizeof(PGChecksummablePage) == BLCKSZ);
+	Assert(sizeof(PGChecksummablePage) == cluster_block_size);
 
 	/* initialize partial checksums to their corresponding offsets */
 	memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets));
 
 	/* main checksum calculation */
-	for (i = 0; i < (uint32) (BLCKSZ / (sizeof(uint32) * N_SUMS)); i++)
+	for (i = 0; i < (uint32) (cluster_block_size / (sizeof(uint32) * N_SUMS)); i++)
 		for (j = 0; j < N_SUMS; j++)
 			CHECKSUM_COMP(sums[j], page->data[i][j]);
 
diff --git a/src/include/storage/fsm_internals.h b/src/include/storage/fsm_internals.h
index 9e314c83fa..3e96d2335a 100644
--- a/src/include/storage/fsm_internals.h
+++ b/src/include/storage/fsm_internals.h
@@ -48,10 +48,10 @@ typedef FSMPageData *FSMPage;
  * Number of non-leaf and leaf nodes, and nodes in total, on an FSM page.
  * These definitions are internal to fsmpage.c.
  */
-#define NodesPerPage (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
+#define NodesPerPage (cluster_block_size - MAXALIGN(SizeOfPageHeaderData) - \
 					  offsetof(FSMPageData, fp_nodes))
 
-#define NonLeafNodesPerPage (BLCKSZ / 2 - 1)
+#define NonLeafNodesPerPage (cluster_block_size / 2 - 1)
 #define LeafNodesPerPage (NodesPerPage - NonLeafNodesPerPage)
 
 /*
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
index db521f23eb..9fab334c7b 100644
--- a/src/include/storage/large_object.h
+++ b/src/include/storage/large_object.h
@@ -54,7 +54,7 @@ typedef struct LargeObjectDesc
 /*
  * Each "page" (tuple) of a large object can hold this much data
  *
- * We could set this as high as BLCKSZ less some overhead, but it seems
+ * We could set this as high as cluster_block_size less some overhead, but it seems
  * better to make it a smaller value, so that not as much space is used
  * up when a page-tuple is updated.  Note that the value is deliberately
  * chosen large enough to trigger the tuple toaster, so that we will
@@ -65,15 +65,15 @@ typedef struct LargeObjectDesc
  * since clients will often be written to send data in power-of-2 blocks.
  * This avoids unnecessary tuple updates caused by partial-page writes.
  *
- * NB: Changing LOBLKSIZE requires an initdb.
+ * NB: Changing cluster_loblksize requires an initdb.
  */
-#define LOBLKSIZE		(BLCKSZ / 4)
+#define cluster_loblksize		(cluster_block_size / 4)
 
 /*
  * Maximum length in bytes for a large object.  To make this larger, we'd
  * have to widen pg_largeobject.pageno as well as various internal variables.
  */
-#define MAX_LARGE_OBJECT_SIZE	((int64) INT_MAX * LOBLKSIZE)
+#define MAX_LARGE_OBJECT_SIZE	((int64) INT_MAX * cluster_loblksize)
 
 
 /*
diff --git a/src/include/storage/off.h b/src/include/storage/off.h
index 3540308069..7fbd45a084 100644
--- a/src/include/storage/off.h
+++ b/src/include/storage/off.h
@@ -25,7 +25,7 @@ typedef uint16 OffsetNumber;
 
 #define InvalidOffsetNumber		((OffsetNumber) 0)
 #define FirstOffsetNumber		((OffsetNumber) 1)
-#define MaxOffsetNumber			((OffsetNumber) (BLCKSZ / sizeof(ItemIdData)))
+#define MaxOffsetNumber			((OffsetNumber) (cluster_block_size / sizeof(ItemIdData)))
 
 /* ----------------
  *		support macros
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1426a353cd..08256699b4 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -368,14 +368,14 @@ typedef struct StdRdOptions
  *		Returns the relation's desired space usage per page in bytes.
  */
 #define RelationGetTargetPageUsage(relation, defaultff) \
-	(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
+	(cluster_block_size * RelationGetFillFactor(relation, defaultff) / 100)
 
 /*
  * RelationGetTargetPageFreeSpace
  *		Returns the relation's desired freespace per page in bytes.
  */
 #define RelationGetTargetPageFreeSpace(relation, defaultff) \
-	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+	(cluster_block_size * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
 
 /*
  * RelationIsUsedAsCatalogTable
diff --git a/src/test/modules/test_slru/test_slru.c b/src/test/modules/test_slru/test_slru.c
index ae21444c47..1d64fe0e69 100644
--- a/src/test/modules/test_slru/test_slru.c
+++ b/src/test/modules/test_slru/test_slru.c
@@ -77,7 +77,7 @@ test_slru_page_write(PG_FUNCTION_ARGS)
 
 	/* write given data to the page, up to the limit of the page */
 	strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
-			BLCKSZ - 1);
+			cluster_block_size - 1);
 
 	SimpleLruWritePage(TestSlruCtl, slotno);
 	LWLockRelease(TestSLRULock);
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 93ed5e8cc0..12dc4aae8a 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -320,7 +320,7 @@ CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicat
 CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off);
 -- Generate enough garbage tuples in index to ensure that even the unique index
 -- with deduplication enabled has to check multiple leaf pages during unique
--- checking (at least with a BLCKSZ of 8192 or less)
+-- checking (at least with a cluster_block_size of 8192 or less)
 DO $$
 BEGIN
     FOR r IN 1..1350 LOOP
@@ -331,7 +331,7 @@ END$$;
 -- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple.
 -- The implementation prefers deleting existing items to merging any duplicate
 -- tuples into a posting list, so we need an explicit test to make sure we get
--- coverage (note that this test also assumes BLCKSZ is 8192 or less):
+-- coverage (note that this test also assumes cluster_block_size is 8192 or less):
 DROP INDEX plain_unique;
 DELETE FROM dedup_unique_test_table WHERE a = 1;
 INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i;
diff --git a/src/test/regress/expected/largeobject.out b/src/test/regress/expected/largeobject.out
index bdcede6728..81bfe9887a 100644
--- a/src/test/regress/expected/largeobject.out
+++ b/src/test/regress/expected/largeobject.out
@@ -353,7 +353,7 @@ SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
    670800
 (1 row)
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default cluster_block_size, cluster_loblksize = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
  lo_lseek 
diff --git a/src/test/regress/expected/largeobject_1.out b/src/test/regress/expected/largeobject_1.out
index d700910c35..b78dc77f7a 100644
--- a/src/test/regress/expected/largeobject_1.out
+++ b/src/test/regress/expected/largeobject_1.out
@@ -353,7 +353,7 @@ SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
    680800
 (1 row)
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default cluster_block_size, cluster_loblksize = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
  lo_lseek 
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 239f4a4755..f4e2ddf4f8 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -173,7 +173,7 @@ CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicat
 CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off);
 -- Generate enough garbage tuples in index to ensure that even the unique index
 -- with deduplication enabled has to check multiple leaf pages during unique
--- checking (at least with a BLCKSZ of 8192 or less)
+-- checking (at least with a cluster_block_size of 8192 or less)
 DO $$
 BEGIN
     FOR r IN 1..1350 LOOP
@@ -185,7 +185,7 @@ END$$;
 -- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple.
 -- The implementation prefers deleting existing items to merging any duplicate
 -- tuples into a posting list, so we need an explicit test to make sure we get
--- coverage (note that this test also assumes BLCKSZ is 8192 or less):
+-- coverage (note that this test also assumes cluster_block_size is 8192 or less):
 DROP INDEX plain_unique;
 DELETE FROM dedup_unique_test_table WHERE a = 1;
 INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i;
diff --git a/src/test/regress/sql/largeobject.sql b/src/test/regress/sql/largeobject.sql
index 800e4fcc6a..9dd258210b 100644
--- a/src/test/regress/sql/largeobject.sql
+++ b/src/test/regress/sql/largeobject.sql
@@ -191,7 +191,7 @@ UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS inte
 -- verify length of large object
 SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default cluster_block_size, cluster_loblksize = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
 
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index a50f730260..f6a40ae730 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -212,7 +212,7 @@ sub GenerateFiles
 		ALIGNOF_PG_INT128_TYPE => undef,
 		ALIGNOF_SHORT => 2,
 		AC_APPLE_UNIVERSAL_BUILD => undef,
-		BLCKSZ => 1024 * $self->{options}->{blocksize},
+		cluster_block_size => 1024 * $self->{options}->{blocksize},
 		CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"',
 		DEF_PGPORT => $port,
 		DEF_PGPORT_STR => qq{"$port"},
-- 
2.39.2

v2-0004-Introduce-initdb-selectable-block-sizes.patchapplication/octet-stream; name=v2-0004-Introduce-initdb-selectable-block-sizes.patchDownload
From 63a1b25eb6d3c93c453b4e6dc5c4ef5c67e3de8a Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Thu, 1 Jun 2023 11:34:14 -0400
Subject: [PATCH v2 1/5] Introduce initdb-selectable block sizes

Whereas we had traditionally used BLCKSZ to indicate the compile-time selected
block size, this commit adjusted things so the cluster block size can be
selected at initdb time.

In order to code for this, we introduce a few new defines:

- cluster_block_size is the blocksize for this cluster itself.  This is not
valid until BlockSizeInit() has been called in the given backend, which we do as
early as possible by parsing the ControlFile and using the blcksz field.

- MIN_BLOCK_SIZE and MAX_BLOCK_SIZE are the limits for the selectable block
size. It is required that cluster_block_size is a power of 2 between these two
constants.

- DEFAULT_BLOCK_SIZE is the moral equivalent of BLCKSZ; it is the built-in
default value.  This is used in a few places that just needed a buffer of an
arbitrary size, but the dynamic value cluster_block_size should almost always be
used instead.

- cluster_relseg_size is used instead of RELSEG_SIZE, since internally we are
storing the segment size in terms of number of blocks.  RELSEG_SIZE is still
kept, but is used in terms of the number of blocks of DEFAULT_BLOCK_SIZE;
cluster_relseg_size scales appropriately (and is the only thing used internally)
to keep the same target total segment size regardless of block size.

This patch uses inline functions to switch between compile-time constants for
all blocksize-derived fields; this makes what could be a heavy computation or
memory load/store in hot code paths a relatively fast (~ 4 instructions)
computation for any access to such a dynamic value. Where possible, the
traditional macro names are kept so code that doesn't care about it should not
need to change, however the definition of these has changed (see the CalcXXX()
routines in blocksize.h for details).

A new function, BlockSizeInit() populates the appropriate values based on the
target block size.  This should be called as early as possible in any code that
utilizes block sizes.  This patch adds this in the appropriate place on the
handful of src/bin/ programs that used cluster_block_size, so this caveat mainly impacts new
code.

Headers which define now dynamic values should declare the symbol name and the
calculation macro in order to properly register it; BlockSizeDecl() ends up
creating the necessary static inline function, but only in files which include
the given headers.

Code which had previously used BLCKZ should likely be able to get away with
changing these instances to cluster_block_size, unless you're using a structure
allocated on the stack.  In these cases, the compiler will complain about
dynamic structure.  The solution is to utilize an expression with MAX_BLOCK_SIZE
instead of cluster_block_size, ensuring enough stack space is allocated for the maximum
size. This also does require using cluster_block_size or an expression based on
it when actually using this structure, so in practice more stack space may be
allocated then used in principal; as long as there is plenty of stack this
should have no specific impacts on code.

From what I can tell, TPC-H results are in the neighborhood of the equivalent
unpatched version, which is promising compared to the previous version.
---
 configure                                   |  14 +-
 configure.ac                                |  25 +--
 contrib/amcheck/verify_heapam.c             |  10 +-
 contrib/bloom/bloom.h                       |  22 +-
 contrib/bloom/blutils.c                     |   4 +-
 contrib/bloom/blvacuum.c                    |   2 +-
 contrib/file_fdw/file_fdw.c                 |   4 +-
 contrib/pageinspect/rawpage.c               |   2 +-
 contrib/pg_surgery/heap_surgery.c           |   2 +-
 doc/src/sgml/config.sgml                    |   9 +-
 doc/src/sgml/monitoring.sgml                |   2 +-
 doc/src/sgml/pgfreespacemap.sgml            |   3 +-
 doc/src/sgml/ref/initdb.sgml                |  16 ++
 doc/src/sgml/ref/pg_resetwal.sgml           |  19 ++
 meson.build                                 |  11 +-
 src/backend/access/common/reloptions.c      |  33 ++-
 src/backend/access/common/syncscan.c        |   2 +-
 src/backend/access/common/toast_internals.c |   2 +-
 src/backend/access/gin/gindatapage.c        |   2 +-
 src/backend/access/gin/ginget.c             |   6 +-
 src/backend/access/gin/ginvacuum.c          |   2 +-
 src/backend/access/gist/gist.c              |   2 +-
 src/backend/access/gist/gistvacuum.c        |   6 +-
 src/backend/access/hash/hash.c              |   4 +-
 src/backend/access/hash/hashinsert.c        |   2 +-
 src/backend/access/hash/hashovfl.c          |   8 +-
 src/backend/access/hash/hashpage.c          |   4 +-
 src/backend/access/heap/heapam.c            |  10 +-
 src/backend/access/heap/heapam_handler.c    |   6 +-
 src/backend/access/heap/heaptoast.c         |   2 +-
 src/backend/access/heap/hio.c               |   2 +-
 src/backend/access/heap/pruneheap.c         |  12 +-
 src/backend/access/heap/rewriteheap.c       |   2 +-
 src/backend/access/heap/vacuumlazy.c        |  12 +-
 src/backend/access/heap/visibilitymap.c     |   6 +-
 src/backend/access/nbtree/nbtinsert.c       |   2 +-
 src/backend/access/nbtree/nbtpage.c         |   8 +-
 src/backend/access/nbtree/nbtree.c          |   4 +-
 src/backend/access/nbtree/nbtxlog.c         |   4 +-
 src/backend/access/spgist/spgdoinsert.c     |   2 +-
 src/backend/access/spgist/spgscan.c         |   4 +-
 src/backend/access/spgist/spgvacuum.c       |  22 +-
 src/backend/access/transam/generic_xlog.c   |   2 +-
 src/backend/access/transam/timeline.c       |   2 +-
 src/backend/access/transam/xlog.c           | 130 ++++++++++--
 src/backend/access/transam/xloginsert.c     |   6 +-
 src/backend/backup/basebackup.c             |  13 +-
 src/backend/bootstrap/bootstrap.c           |  14 +-
 src/backend/commands/async.c                |   4 +-
 src/backend/nodes/tidbitmap.c               |   4 +-
 src/backend/po/de.po                        |   2 +-
 src/backend/storage/buffer/bufmgr.c         |   2 +-
 src/backend/storage/buffer/localbuf.c       |   2 +-
 src/backend/storage/file/buffile.c          |   4 +-
 src/backend/storage/file/fd.c               |   4 +-
 src/backend/storage/freespace/freespace.c   |  15 +-
 src/backend/storage/large_object/inv_api.c  |   4 +-
 src/backend/storage/page/bufpage.c          |  12 +-
 src/backend/storage/smgr/README             |  52 +++++
 src/backend/storage/smgr/md.c               |  84 ++++----
 src/backend/utils/init/miscinit.c           |   6 +-
 src/backend/utils/misc/guc.c                |  25 ++-
 src/backend/utils/misc/guc_tables.c         |  39 +++-
 src/backend/utils/sort/logtape.c            |   2 +-
 src/bin/initdb/initdb.c                     |  59 ++++--
 src/bin/initdb/meson.build                  |   1 +
 src/bin/initdb/t/002_blocksize.pl           |  24 +++
 src/bin/pg_basebackup/pg_basebackup.c       |   7 +
 src/bin/pg_basebackup/pg_receivewal.c       |   4 +
 src/bin/pg_basebackup/streamutil.c          |  59 ++++++
 src/bin/pg_basebackup/streamutil.h          |   3 +
 src/bin/pg_checksums/pg_checksums.c         |  22 +-
 src/bin/pg_resetwal/pg_resetwal.c           |  54 ++++-
 src/bin/pg_resetwal/t/002_corrupted.pl      |   4 +-
 src/bin/pg_rewind/filemap.c                 |   1 +
 src/bin/pg_rewind/pg_rewind.c               |   8 +
 src/bin/pg_upgrade/file.c                   |  17 +-
 src/bin/pg_upgrade/meson.build              |   2 +
 src/bin/pg_upgrade/pg_upgrade.c             |   3 +
 src/bin/pg_upgrade/pg_upgrade.h             |   2 +-
 src/bin/pg_verifybackup/pg_verifybackup.c   |   4 +-
 src/bin/pg_waldump/pg_waldump.c             |  31 ++-
 src/bin/pg_waldump/t/001_basic.pl           |   2 +
 src/bin/pg_waldump/t/002_save_fullpage.pl   |   3 +
 src/common/Makefile                         |   1 +
 src/common/blocksize.c                      |  53 +++++
 src/common/file_utils.c                     |   6 +-
 src/common/meson.build                      |   1 +
 src/include/access/gist_private.h           |   2 +-
 src/include/access/hash.h                   |   4 +-
 src/include/access/heapam.h                 |   2 +-
 src/include/access/heaptoast.h              |  20 +-
 src/include/access/htup_details.h           |  16 +-
 src/include/access/itup.h                   |   6 +-
 src/include/access/nbtree.h                 | 207 +-----------------
 src/include/access/nbtree_int.h             | 223 ++++++++++++++++++++
 src/include/access/spgist_private.h         |  10 +-
 src/include/access/xlog_internal.h          |   1 +
 src/include/c.h                             |   6 +-
 src/include/common/blocksize.h              | 119 +++++++++++
 src/include/pg_config.h.in                  |  39 ++--
 src/include/pg_config_manual.h              |   8 +
 src/include/postgres.h                      |   1 +
 src/include/postgres_ext.h                  |   1 -
 src/include/storage/bufpage.h               |  18 +-
 src/include/storage/checksum.h              |   2 +-
 src/include/storage/checksum_impl.h         |  13 +-
 src/include/storage/freespace.h             |   1 +
 src/include/storage/large_object.h          |   2 +
 src/include/storage/off.h                   |   4 +-
 src/include/utils/guc_tables.h              |   1 +
 src/tools/msvc/Mkvcbuild.pm                 |   2 +-
 src/tools/msvc/Solution.pm                  |   2 +-
 src/tools/pgindent/typedefs.list            |   1 -
 114 files changed, 1277 insertions(+), 549 deletions(-)
 create mode 100644 src/bin/initdb/t/002_blocksize.pl
 create mode 100644 src/common/blocksize.c
 create mode 100644 src/include/access/nbtree_int.h
 create mode 100644 src/include/common/blocksize.h

diff --git a/configure b/configure
index d47e0f8b26..d8512697d9 100755
--- a/configure
+++ b/configure
@@ -3711,12 +3711,12 @@ fi
 
 
 case ${blocksize} in
-  1) BLCKSZ=1024;;
-  2) BLCKSZ=2048;;
-  4) BLCKSZ=4096;;
-  8) BLCKSZ=8192;;
- 16) BLCKSZ=16384;;
- 32) BLCKSZ=32768;;
+  1) DEFAULT_BLOCK_SIZE=1024;;
+  2) DEFAULT_BLOCK_SIZE=2048;;
+  4) DEFAULT_BLOCK_SIZE=4096;;
+  8) DEFAULT_BLOCK_SIZE=8192;;
+ 16) DEFAULT_BLOCK_SIZE=16384;;
+ 32) DEFAULT_BLOCK_SIZE=32768;;
   *) as_fn_error $? "Invalid block size. Allowed values are 1,2,4,8,16,32." "$LINENO" 5
 esac
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${blocksize}kB" >&5
@@ -3724,7 +3724,7 @@ $as_echo "${blocksize}kB" >&6; }
 
 
 cat >>confdefs.h <<_ACEOF
-#define BLCKSZ ${BLCKSZ}
+#define DEFAULT_BLOCK_SIZE ${DEFAULT_BLOCK_SIZE}
 _ACEOF
 
 
diff --git a/configure.ac b/configure.ac
index 440b08d113..0d6b8b3b07 100644
--- a/configure.ac
+++ b/configure.ac
@@ -258,28 +258,29 @@ PGAC_ARG_REQ(with, blocksize, [BLOCKSIZE], [set table block size in kB [8]],
              [blocksize=$withval],
              [blocksize=8])
 case ${blocksize} in
-  1) BLCKSZ=1024;;
-  2) BLCKSZ=2048;;
-  4) BLCKSZ=4096;;
-  8) BLCKSZ=8192;;
- 16) BLCKSZ=16384;;
- 32) BLCKSZ=32768;;
+  1) DEFAULT_BLOCK_SIZE=1024;;
+  2) DEFAULT_BLOCK_SIZE=2048;;
+  4) DEFAULT_BLOCK_SIZE=4096;;
+  8) DEFAULT_BLOCK_SIZE=8192;;
+ 16) DEFAULT_BLOCK_SIZE=16384;;
+ 32) DEFAULT_BLOCK_SIZE=32768;;
   *) AC_MSG_ERROR([Invalid block size. Allowed values are 1,2,4,8,16,32.])
 esac
 AC_MSG_RESULT([${blocksize}kB])
 
-AC_DEFINE_UNQUOTED([BLCKSZ], ${BLCKSZ}, [
+AC_DEFINE_UNQUOTED([DEFAULT_BLOCK_SIZE], ${DEFAULT_BLOCK_SIZE}, [
  Size of a disk block --- this also limits the size of a tuple.  You
  can set it bigger if you need bigger tuples (although TOAST should
  reduce the need to have large tuples, since fields can be spread
  across multiple tuples).
 
- BLCKSZ must be a power of 2.  The maximum possible value of BLCKSZ
+ DEFAULT_BLOCK_SIZE must be a power of 2.  The maximum possible value of DEFAULT_BLOCK_SIZE
  is currently 2^15 (32768).  This is determined by the 15-bit widths
  of the lp_off and lp_len fields in ItemIdData (see
  include/storage/itemid.h).
 
- Changing BLCKSZ requires an initdb.
+ Changing DEFAULT_BLOCK_SIZE requires an initdb, however an individual cluster
+ can have its block size set.
 ])
 
 #
@@ -313,10 +314,10 @@ fi
 
 AC_DEFINE_UNQUOTED([RELSEG_SIZE], ${RELSEG_SIZE}, [
  RELSEG_SIZE is the maximum number of blocks allowed in one disk file.
- Thus, the maximum size of a single file is RELSEG_SIZE * BLCKSZ;
+ Thus, the maximum size of a single file is RELSEG_SIZE * cluster_block_size;
  relations bigger than that are divided into multiple files.
 
- RELSEG_SIZE * BLCKSZ must be less than your OS' limit on file size.
+ RELSEG_SIZE * cluster_block_size must be less than your OS' limit on file size.
  This is often 2 GB or 4GB in a 32-bit operating system, unless you
  have large file support enabled.  By default, we make the limit 1 GB
  to avoid any possible integer-overflow problems within the OS.
@@ -350,7 +351,7 @@ esac
 AC_MSG_RESULT([${wal_blocksize}kB])
 
 AC_DEFINE_UNQUOTED([XLOG_BLCKSZ], ${XLOG_BLCKSZ}, [
- Size of a WAL file block.  This need have no particular relation to BLCKSZ.
+ Size of a WAL file block.  This need have no particular relation to cluster_block_size.
  XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O,
  XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O
  buffers, else direct I/O may fail.
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 004aa26b7c..450496dd8a 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -403,11 +403,11 @@ verify_heapam(PG_FUNCTION_ARGS)
 	for (ctx.blkno = first_block; ctx.blkno <= last_block; ctx.blkno++)
 	{
 		OffsetNumber maxoff;
-		OffsetNumber predecessor[MaxOffsetNumber];
-		OffsetNumber successor[MaxOffsetNumber];
-		bool		lp_valid[MaxOffsetNumber];
-		bool		xmin_commit_status_ok[MaxOffsetNumber];
-		XidCommitStatus xmin_commit_status[MaxOffsetNumber];
+		OffsetNumber predecessor[MaxOffsetNumberLimit];
+		OffsetNumber successor[MaxOffsetNumberLimit];
+		bool		lp_valid[MaxOffsetNumberLimit];
+		bool		xmin_commit_status_ok[MaxOffsetNumberLimit];
+		XidCommitStatus xmin_commit_status[MaxOffsetNumberLimit];
 
 		CHECK_FOR_INTERRUPTS();
 
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index d08fe1de26..e3f54ac02b 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -19,6 +19,7 @@
 #include "access/xlog.h"
 #include "fmgr.h"
 #include "nodes/pathnodes.h"
+#include "storage/bufpage.h"
 
 /* Support procedures numbers */
 #define BLOOM_HASH_PROC			1
@@ -106,16 +107,9 @@ typedef struct BloomOptions
 											 * index key */
 } BloomOptions;
 
-/*
- * FreeBlockNumberArray - array of block numbers sized so that metadata fill
- * all space in metapage.
- */
-typedef BlockNumber FreeBlockNumberArray[
-										 MAXALIGN_DOWN(
-													   cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
-													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
-													   ) / sizeof(BlockNumber)
-];
+#define free_block_number_elems CalcFreeBlockNumberElems(cluster_block_size)
+BlockSizeDecl(CalcFreeBlockNumberElems);
+
 
 /* Metadata of bloom index */
 typedef struct BloomMetaPageData
@@ -124,14 +118,18 @@ typedef struct BloomMetaPageData
 	uint16		nStart;
 	uint16		nEnd;
 	BloomOptions opts;
-	FreeBlockNumberArray notFullPage;
+	BlockNumber notFullPage[FLEXIBLE_ARRAY_MEMBER];
 } BloomMetaPageData;
 
+#define SizeOfBloomMetaPageData (offsetof(BloomMetaPageData,notFullPage) + sizeof(BlockNumber) * BloomMetaBlockN)
+
 /* Magic number to distinguish bloom pages among anothers */
 #define BLOOM_MAGICK_NUMBER (0xDBAC0DED)
 
 /* Number of blocks numbers fit in BloomMetaPageData */
-#define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+#define BloomMetaBlockN		(MAXALIGN_DOWN(cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData)) \
+													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) \
+								  ) / sizeof(BlockNumber))
 
 #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
 
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 9d79319e90..89aa31ec73 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -430,10 +430,10 @@ BloomFillMetapage(Relation index, Page metaPage)
 	 */
 	BloomInitPage(metaPage, BLOOM_META);
 	metadata = BloomPageGetMeta(metaPage);
-	memset(metadata, 0, sizeof(BloomMetaPageData));
+	memset(metadata, 0, SizeOfBloomMetaPageData);
 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
 	metadata->opts = *opts;
-	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+	((PageHeader) metaPage)->pd_lower += SizeOfBloomMetaPageData;
 
 	/* If this fails, probably FreeBlockNumberArray size calc is wrong: */
 	Assert(((PageHeader) metaPage)->pd_lower <= ((PageHeader) metaPage)->pd_upper);
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
index 2340d49e00..373227af98 100644
--- a/contrib/bloom/blvacuum.c
+++ b/contrib/bloom/blvacuum.c
@@ -37,7 +37,7 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	Relation	index = info->index;
 	BlockNumber blkno,
 				npages;
-	FreeBlockNumberArray notFullPage;
+	BlockNumber notFullPage[CalcFreeBlockNumberElems(MAX_BLOCK_SIZE)];
 	int			countPage = 0;
 	BloomState	state;
 	Buffer		buffer;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index c265b6e309..13a3a73ebd 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -840,7 +840,7 @@ fileAnalyzeForeignTable(Relation relation,
 	 * Convert size to pages.  Must return at least 1 so that we can tell
 	 * later on that pg_class.relpages is not default.
 	 */
-	*totalpages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
+	*totalpages = (stat_buf.st_size + (cluster_block_size - 1)) >> cluster_block_bits;
 	if (*totalpages < 1)
 		*totalpages = 1;
 
@@ -1015,7 +1015,7 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel,
 	/*
 	 * Convert size to pages for use in I/O cost estimate later.
 	 */
-	pages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
+	pages = (stat_buf.st_size + (cluster_block_size - 1)) >> cluster_block_bits;
 	if (pages < 1)
 		pages = 1;
 	fdw_private->pages = pages;
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index 2527501078..bdeeb027d3 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -357,7 +357,7 @@ page_checksum_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 	if (PageIsNew(page))
 		PG_RETURN_NULL();
 
-	PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
+	PG_RETURN_INT16(pg_checksum_page((char *) page, blkno, cluster_block_size));
 }
 
 Datum
diff --git a/contrib/pg_surgery/heap_surgery.c b/contrib/pg_surgery/heap_surgery.c
index 88a40ab7d3..58c4819675 100644
--- a/contrib/pg_surgery/heap_surgery.c
+++ b/contrib/pg_surgery/heap_surgery.c
@@ -89,7 +89,7 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
 	Relation	rel;
 	OffsetNumber curr_start_ptr,
 				next_start_ptr;
-	bool		include_this_tid[MaxHeapTuplesPerPage];
+	bool		include_this_tid[MaxHeapTuplesPerPageLimit];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 2274bc0ffb..19510505b5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -10625,12 +10625,13 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </term>
       <listitem>
        <para>
-        Reports the size of a disk block.  It is determined by the value
-        of <literal>cluster_block_size</literal> when building the server. The default
-        value is 8192 bytes.  The meaning of some configuration
+        Reports the size of a disk block for this cluster.
+        The default value is 8192 bytes.  The meaning of some configuration
         variables (such as <xref linkend="guc-shared-buffers"/>) is
         influenced by <varname>block_size</varname>. See <xref
-        linkend="runtime-config-resource"/> for information.
+        linkend="runtime-config-resource"/> for information on GUC configuration
+        or <xref linkend="app-initdb-blocksize"/> for more information on
+        selecting a cluster's block size.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index c655c70e38..b80b43780e 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -2692,7 +2692,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        </para>
        <para>
         Relation data reads, writes, and extends are done in
-        <varname>block_size</varname> units, derived from the build-time
+        <varname>block_size</varname> units, derived from the cluster init-time
         parameter <symbol>cluster_block_size</symbol>, which is <literal>8192</literal> by
         default.
        </para>
diff --git a/doc/src/sgml/pgfreespacemap.sgml b/doc/src/sgml/pgfreespacemap.sgml
index 0da94e5f63..1777c044ab 100644
--- a/doc/src/sgml/pgfreespacemap.sgml
+++ b/doc/src/sgml/pgfreespacemap.sgml
@@ -60,7 +60,8 @@
 
   <para>
    The values stored in the free space map are not exact. They're rounded
-   to precision of 1/256th of <symbol>cluster_block_size</symbol> (32 bytes with default <symbol>cluster_block_size</symbol>), and
+   to precision of 1/256th of <symbol>cluster_block_size</symbol> (32 bytes
+   with default <symbol>cluster_block_size</symbol>), and
    they're not kept fully up-to-date as tuples are inserted and updated.
   </para>
 
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 22f1011781..ed8b20a0aa 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -277,6 +277,22 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-blocksize" xreflabel="blocksize">
+      <term><option>-b <replaceable>blocksize</replaceable></option></term>
+      <term><option>--block-size=<replaceable>blocksize</replaceable></option></term>
+      <listitem>
+       <para>
+        Sets the cluster's database block size
+        to <replaceable>blocksize</replaceable> kilobytes.  This should be one
+        of 1, 2, 4, 8, 16, or 32.  Many database limits are defined relative
+        to this parameter.  If unspecified, defaults to 8.
+       </para>
+       <para>
+        This used to be the compile-time parameter <literal>cluster_block_size</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-option-locale">
       <term><option>--locale=<replaceable>locale</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml
index fd539f5604..54dd73bb12 100644
--- a/doc/src/sgml/ref/pg_resetwal.sgml
+++ b/doc/src/sgml/ref/pg_resetwal.sgml
@@ -136,6 +136,25 @@ PostgreSQL documentation
   </para>
 
   <variablelist>
+   <varlistentry>
+    <term><option>-b <replaceable class="parameter">blocksize</replaceable></option></term>
+    <term><option>--block-size=<replaceable class="parameter">blocksize</replaceable></option></term>
+    <listitem>
+     <para>
+      Sets the cluster's database block size
+      to <replaceable>blocksize</replaceable> kilobytes.  This should be one
+      of 1, 2, 4, 8, 16, or 32.  Many database limits are defined relative
+      to this parameter.
+     </para>
+     <para>
+      This <emphasis>must</emphasis> be the same value selected
+      at <command>initdb</command> time, or this will result in database
+      corruption.  This option should <emphasis>only</emphasis> be provided
+      when <filename>pg_control</filename> is corrupted or missing.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><option>-c <replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
     <term><option>--commit-timestamp-ids=<replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
diff --git a/meson.build b/meson.build
index 5422885b0a..9794be4793 100644
--- a/meson.build
+++ b/meson.build
@@ -444,14 +444,15 @@ else
   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
 endif
 
-cdata.set('BLCKSZ', blocksize, description:
-'''Size of a disk block --- this also limits the size of a tuple. You can set
+cdata.set('DEFAULT_BLOCK_SIZE', blocksize, description:
+'''Default size of a disk block --- this also limits the size of a tuple. You can set
    it bigger if you need bigger tuples (although TOAST should reduce the need
    to have large tuples, since fields can be spread across multiple tuples).
-   BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
+   DEFAULT_BLOCK_SIZE must be a power of 2. The maximum possible value of DEFAULT_BLOCK_SIZE is
    currently 2^15 (32768). This is determined by the 15-bit widths of the
    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
-   Changing BLCKSZ requires an initdb.''')
+   Changing DEFAULT_BLOCK_SIZE requires an initdb, however individual clusters
+   can have different values for block size (cluster_block_size)''')
 
 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
 cdata.set('RELSEG_SIZE', segsize)
@@ -3336,7 +3337,7 @@ if meson.version().version_compare('>=0.57')
 
   summary(
     {
-      'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
+      'data block size': '@0@ kB'.format(cdata.get('DEFAULT_BLOCK_SIZE') / 1024),
       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
       'segment size': get_option('segsize_blocks') != 0 ?
         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 52d4ee6834..9d8a16f6ad 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -28,6 +28,7 @@
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
 #include "commands/view.h"
+#include "common/blocksize.h"
 #include "nodes/makefuncs.h"
 #include "postmaster/postmaster.h"
 #include "utils/array.h"
@@ -328,7 +329,8 @@ static relopt_int intRelOpts[] =
 			RELOPT_KIND_HEAP,
 			ShareUpdateExclusiveLock
 		},
-		cluster_toast_tuple_target, 128, toast_tuple_target_main
+		/* NOTE: these limits are dynamically updated */
+		0, 0, 0
 	},
 	{
 		{
@@ -563,6 +565,8 @@ static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 								int text_len, bool validate);
 
+static void update_dynamic_reloptions(void);
+
 /*
  * Get the length of a string reloption (either default or the user-defined
  * value).  This is used for allocation purposes when building a set of
@@ -572,6 +576,26 @@ static void parse_one_reloption(relopt_value *option, char *text_str,
 	((option).isset ? strlen((option).values.string_val) : \
 	 ((relopt_string *) (option).gen)->default_len)
 
+/*
+ * handle adjustments to the config table based on dynamic parameters' limits
+ */
+
+static void
+update_dynamic_reloptions(void)
+{
+	int i;
+
+	for (i = 0; intRelOpts[i].gen.name; i++)
+	{
+		if (strcmp("toast_tuple_target", intRelOpts[i].gen.name) == 0)
+		{
+			intRelOpts[i].min = 128;
+			intRelOpts[i].default_val = cluster_toast_tuple_target;
+			intRelOpts[i].max = toast_tuple_target_main;
+		}
+	}
+}
+
 /*
  * initialize_reloptions
  *		initialization routine, must be called before parsing
@@ -584,6 +608,13 @@ initialize_reloptions(void)
 	int			i;
 	int			j;
 
+	/*
+	 * Set the dynamic limits based on block size; if we get multiple can make
+	 * more sophisticated.
+	 */
+
+	update_dynamic_reloptions();
+
 	j = 0;
 	for (i = 0; boolRelOpts[i].gen.name; i++)
 	{
diff --git a/src/backend/access/common/syncscan.c b/src/backend/access/common/syncscan.c
index 3aa54f28d4..1d8e4b35a9 100644
--- a/src/backend/access/common/syncscan.c
+++ b/src/backend/access/common/syncscan.c
@@ -80,7 +80,7 @@ bool		trace_syncscan = false;
  * the buffer cache anyway, and on the other hand the page is most likely
  * still in the OS cache.
  */
-#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / cluster_block_size)
+#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 >> cluster_block_bits)
 
 
 /*
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 8745f87be6..7c87b938fc 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -133,7 +133,7 @@ toast_save_datum(Relation rel, Datum value,
 	{
 		struct varlena hdr;
 		/* this is to make the union big enough for a chunk: */
-		char		data[cluster_toast_max_chunk_size + VARHDRSZ];
+		char		data[TOAST_MAX_CHUNK_SIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			chunk_data;
diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
index 9bef09651e..9c42501a8d 100644
--- a/src/backend/access/gin/gindatapage.c
+++ b/src/backend/access/gin/gindatapage.c
@@ -1259,7 +1259,7 @@ dataSplitPageInternal(GinBtree btree, Buffer origbuf,
 	Page		lpage;
 	Page		rpage;
 	OffsetNumber separator;
-	PostingItem allitems[(cluster_block_size / sizeof(PostingItem)) + 1];
+	PostingItem allitems[(MAX_BLOCK_SIZE / sizeof(PostingItem)) + 1];
 
 	lpage = PageGetTempPage(oldpage);
 	rpage = PageGetTempPage(oldpage);
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 022f71e25c..59aa2a73e4 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1632,9 +1632,9 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
 	 */
 	for (;;)
 	{
-		Datum		datum[cluster_block_size / sizeof(IndexTupleData)];
-		GinNullCategory category[cluster_block_size / sizeof(IndexTupleData)];
-		bool		datumExtracted[cluster_block_size / sizeof(IndexTupleData)];
+		Datum		datum[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
+		GinNullCategory category[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
+		bool		datumExtracted[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
 
 		Assert(pos->lastOffset > pos->firstOffset);
 		memset(datumExtracted + pos->firstOffset - 1, 0,
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index 628d0f3a40..c7198d85c9 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -569,7 +569,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	BlockNumber blkno = GIN_ROOT_BLKNO;
 	GinVacuumState gvs;
 	Buffer		buffer;
-	BlockNumber rootOfPostingTree[cluster_block_size / (sizeof(IndexTupleData) + sizeof(ItemId))];
+	BlockNumber rootOfPostingTree[MAX_BLOCK_SIZE / (sizeof(IndexTupleData) + sizeof(ItemId))];
 	uint32		nRoot;
 
 	gvs.tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b1f19f6a8e..4fbfd986a1 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -1642,7 +1642,7 @@ freeGISTstate(GISTSTATE *giststate)
 static void
 gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
 {
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				maxoff;
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index 3f60d3274d..53a29e55b3 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -309,7 +309,7 @@ restart:
 	}
 	else if (GistPageIsLeaf(page))
 	{
-		OffsetNumber todelete[MaxOffsetNumber];
+		OffsetNumber todelete[MaxOffsetNumberLimit];
 		int			ntodelete = 0;
 		int			nremain;
 		GISTPageOpaque opaque = GistPageGetOpaque(page);
@@ -476,8 +476,8 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistVacState *vstate)
 		Page		page;
 		OffsetNumber off,
 					maxoff;
-		OffsetNumber todelete[MaxOffsetNumber];
-		BlockNumber leafs_to_delete[MaxOffsetNumber];
+		OffsetNumber todelete[MaxOffsetNumberLimit];
+		BlockNumber leafs_to_delete[MaxOffsetNumberLimit];
 		int			ntodelete;
 		int			deleted;
 
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index ea78dc81c2..452119220d 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -152,7 +152,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * one page.  Also, "initial index size" accounting does not include the
 	 * metapage, nor the first bitmap page.
 	 */
-	sort_threshold = (maintenance_work_mem * 1024L) / cluster_block_size;
+	sort_threshold = (maintenance_work_mem * 1024L) >> cluster_block_bits;
 	if (index->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
 		sort_threshold = Min(sort_threshold, NBuffers);
 	else
@@ -709,7 +709,7 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf,
 		OffsetNumber maxoffno;
 		Buffer		next_buf;
 		Page		page;
-		OffsetNumber deletable[MaxOffsetNumber];
+		OffsetNumber deletable[MaxOffsetNumberLimit];
 		int			ndeletable = 0;
 		bool		retain_pin = false;
 		bool		clear_dead_marking = false;
diff --git a/src/backend/access/hash/hashinsert.c b/src/backend/access/hash/hashinsert.c
index 22656b24e2..48d986272a 100644
--- a/src/backend/access/hash/hashinsert.c
+++ b/src/backend/access/hash/hashinsert.c
@@ -371,7 +371,7 @@ _hash_pgaddmultitup(Relation rel, Buffer buf, IndexTuple *itups,
 static void
 _hash_vacuum_one_page(Relation rel, Relation hrel, Buffer metabuf, Buffer buf)
 {
-	OffsetNumber deletable[MaxOffsetNumber];
+	OffsetNumber deletable[MaxOffsetNumberLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				maxoff;
diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.c
index 39bb2cb9f6..dd414c557e 100644
--- a/src/backend/access/hash/hashovfl.c
+++ b/src/backend/access/hash/hashovfl.c
@@ -866,10 +866,10 @@ _hash_squeezebucket(Relation rel,
 	{
 		OffsetNumber roffnum;
 		OffsetNumber maxroffnum;
-		OffsetNumber deletable[MaxOffsetNumber];
-		IndexTuple	itups[MaxIndexTuplesPerPage];
-		Size		tups_size[MaxIndexTuplesPerPage];
-		OffsetNumber itup_offsets[MaxIndexTuplesPerPage];
+		OffsetNumber deletable[MaxOffsetNumberLimit];
+		IndexTuple	itups[MaxIndexTuplesPerPageLimit];
+		Size		tups_size[MaxIndexTuplesPerPageLimit];
+		OffsetNumber itup_offsets[MaxIndexTuplesPerPageLimit];
 		uint16		ndeletable = 0;
 		uint16		nitups = 0;
 		Size		all_tups_size = 0;
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 2a77d13feb..1cc99ae27a 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -1087,8 +1087,8 @@ _hash_splitbucket(Relation rel,
 	Page		npage;
 	HashPageOpaque oopaque;
 	HashPageOpaque nopaque;
-	OffsetNumber itup_offsets[MaxIndexTuplesPerPage];
-	IndexTuple	itups[MaxIndexTuplesPerPage];
+	OffsetNumber itup_offsets[MaxIndexTuplesPerPageLimit];
+	IndexTuple	itups[MaxIndexTuplesPerPageLimit];
 	Size		all_tups_size = 0;
 	int			i;
 	uint16		nitups = 0;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index ef3f3e0bdf..8c16ae0ea2 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -6741,8 +6741,8 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
 	/* Now WAL-log freezing if necessary */
 	if (RelationNeedsWAL(rel))
 	{
-		xl_heap_freeze_plan plans[MaxHeapTuplesPerPage];
-		OffsetNumber offsets[MaxHeapTuplesPerPage];
+		xl_heap_freeze_plan plans[MaxHeapTuplesPerPageLimit];
+		OffsetNumber offsets[MaxHeapTuplesPerPageLimit];
 		int			nplans;
 		xl_heap_freeze_page xlrec;
 		XLogRecPtr	recptr;
@@ -9222,7 +9222,7 @@ heap_xlog_insert(XLogReaderState *record)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	HeapTupleHeader htup;
 	xl_heap_header xlhdr;
@@ -9344,7 +9344,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	HeapTupleHeader htup;
 	uint32		newlen;
@@ -9501,7 +9501,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	xl_heap_header xlhdr;
 	uint32		newlen;
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 6857b46203..4cbfca1996 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -1195,7 +1195,7 @@ heapam_index_build_range_scan(Relation heapRelation,
 	TransactionId OldestXmin;
 	BlockNumber previous_blkno = InvalidBlockNumber;
 	BlockNumber root_blkno = InvalidBlockNumber;
-	OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+	OffsetNumber root_offsets[MaxHeapTuplesPerPageLimit];
 
 	/*
 	 * sanity checks
@@ -1758,8 +1758,8 @@ heapam_index_validate_scan(Relation heapRelation,
 	EState	   *estate;
 	ExprContext *econtext;
 	BlockNumber root_blkno = InvalidBlockNumber;
-	OffsetNumber root_offsets[MaxHeapTuplesPerPage];
-	bool		in_index[MaxHeapTuplesPerPage];
+	OffsetNumber root_offsets[MaxHeapTuplesPerPageLimit];
+	bool		in_index[MaxHeapTuplesPerPageLimit];
 	BlockNumber previous_blkno = InvalidBlockNumber;
 
 	/* state variables for the merge */
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 98b0a701e5..6c2d1c9dc1 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -770,7 +770,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 			chcpyend = (sliceoffset + slicelength - 1) % cluster_toast_max_chunk_size;
 
 		memcpy(VARDATA(result) +
-			   (curchunk * cluster_toast_max_chunk_size - sliceoffset) + chcpystrt,
+			   ((int32)(curchunk * cluster_toast_max_chunk_size) - sliceoffset) + chcpystrt,
 			   chunkdata + chcpystrt,
 			   (chcpyend - chcpystrt) + 1);
 
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index caa62708aa..4f2018410a 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -534,7 +534,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("row is too big: size %zu, maximum size %zu",
-						len, MaxHeapTupleSize)));
+						len, (Size)MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
 	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 8b8b9c4d79..deba4d8ffd 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -54,9 +54,9 @@ typedef struct
 	int			ndead;
 	int			nunused;
 	/* arrays that accumulate indexes of items to be changed */
-	OffsetNumber redirected[MaxHeapTuplesPerPage * 2];
-	OffsetNumber nowdead[MaxHeapTuplesPerPage];
-	OffsetNumber nowunused[MaxHeapTuplesPerPage];
+	OffsetNumber redirected[MaxHeapTuplesPerPageLimit * 2];
+	OffsetNumber nowdead[MaxHeapTuplesPerPageLimit];
+	OffsetNumber nowunused[MaxHeapTuplesPerPageLimit];
 
 	/*
 	 * marked[i] is true if item i is entered in one of the above arrays.
@@ -64,7 +64,7 @@ typedef struct
 	 * This needs to be MaxHeapTuplesPerPage + 1 long as FirstOffsetNumber is
 	 * 1. Otherwise every access would need to subtract 1.
 	 */
-	bool		marked[MaxHeapTuplesPerPage + 1];
+	bool		marked[MaxHeapTuplesPerPageLimit + 1];
 
 	/*
 	 * Tuple visibility is only computed once for each tuple, for correctness
@@ -74,7 +74,7 @@ typedef struct
 	 *
 	 * Same indexing as ->marked.
 	 */
-	int8		htsv[MaxHeapTuplesPerPage + 1];
+	int8		htsv[MaxHeapTuplesPerPageLimit + 1];
 } PruneState;
 
 /* Local functions */
@@ -598,7 +598,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
 	OffsetNumber latestdead = InvalidOffsetNumber,
 				maxoff = PageGetMaxOffsetNumber(dp),
 				offnum;
-	OffsetNumber chainitems[MaxHeapTuplesPerPage];
+	OffsetNumber chainitems[MaxHeapTuplesPerPageLimit];
 	int			nchain = 0,
 				i;
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 3402625d6d..9f9c90d6ea 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -657,7 +657,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("row is too big: size %zu, maximum size %zu",
-						len, MaxHeapTupleSize)));
+						len, (Size)MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
 	saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index d6f46ad60e..0bf1d13896 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -98,7 +98,7 @@
  * (Note that this is deliberately kept to a power-of-two, usually 2^19.)
  */
 #define failsafe_every_pages \
-	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / cluster_block_size))
+	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) >> cluster_block_bits))
 
 /*
  * When a table has no indexes, vacuum the FSM after every 8GB, approximately
@@ -107,7 +107,7 @@
  * and we vacuum FSM after each index/heap cleaning pass.
  */
 #define vacuum_fsm_every_pages \
-	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / cluster_block_size))
+	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) >> cluster_block_bits))
 
 /*
  * Before we consider skipping a page that's marked as clean in
@@ -1552,8 +1552,8 @@ lazy_scan_prune(LVRelState *vacrel,
 	int			nnewlpdead;
 	HeapPageFreeze pagefrz;
 	int64		fpi_before = pgWalUsage.wal_fpi;
-	OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
-	HeapTupleFreeze frozen[MaxHeapTuplesPerPage];
+	OffsetNumber deadoffsets[MaxHeapTuplesPerPageLimit];
+	HeapTupleFreeze frozen[MaxHeapTuplesPerPageLimit];
 
 	Assert(BufferGetBlockNumber(buf) == blkno);
 
@@ -1972,7 +1972,7 @@ lazy_scan_noprune(LVRelState *vacrel,
 	HeapTupleHeader tupleheader;
 	TransactionId NoFreezePageRelfrozenXid = vacrel->NewRelfrozenXid;
 	MultiXactId NoFreezePageRelminMxid = vacrel->NewRelminMxid;
-	OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
+	OffsetNumber deadoffsets[MaxHeapTuplesPerPageLimit];
 
 	Assert(BufferGetBlockNumber(buf) == blkno);
 
@@ -2526,7 +2526,7 @@ lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer,
 {
 	VacDeadItems *dead_items = vacrel->dead_items;
 	Page		page = BufferGetPage(buffer);
-	OffsetNumber unused[MaxHeapTuplesPerPage];
+	OffsetNumber unused[MaxHeapTuplesPerPageLimit];
 	int			nunused = 0;
 	TransactionId visibility_cutoff_xid;
 	bool		all_frozen;
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 833774c164..ab4d38fbe6 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -111,7 +111,7 @@
 #define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
 
 /* Number of heap blocks we can represent in one visibility map page. */
-#define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE)
+#define HEAPBLOCKS_PER_PAGE (MAPSIZE << BITS_PER_HEAPBLOCK)
 
 /* Mapping from heap block number to the right bit in the visibility map */
 #define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
@@ -414,8 +414,8 @@ visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_fro
 		 */
 		map = (uint64 *) PageGetContents(BufferGetPage(mapBuffer));
 
-		StaticAssertStmt(MAPSIZE % sizeof(uint64) == 0,
-						 "unsupported MAPSIZE");
+		Assert(MAPSIZE % sizeof(uint64) == 0);
+
 		if (all_frozen == NULL)
 		{
 			for (i = 0; i < MAPSIZE / sizeof(uint64); i++)
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index d33f814a93..eed23d35fd 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -2685,7 +2685,7 @@ _bt_delete_or_dedup_one_page(Relation rel, Relation heapRel,
 							 bool simpleonly, bool checkingunique,
 							 bool uniquedup, bool indexUnchanged)
 {
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				minoff,
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 58dcdf6de4..fb6985b4cf 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -1160,7 +1160,7 @@ _bt_delitems_vacuum(Relation rel, Buffer buf,
 	bool		needswal = RelationNeedsWAL(rel);
 	char	   *updatedbuf = NULL;
 	Size		updatedbuflen = 0;
-	OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
+	OffsetNumber updatedoffsets[MaxIndexTuplesPerPageLimit];
 
 	/* Shouldn't be called unless there's something to do */
 	Assert(ndeletable > 0 || nupdatable > 0);
@@ -1291,7 +1291,7 @@ _bt_delitems_delete(Relation rel, Buffer buf,
 	bool		needswal = RelationNeedsWAL(rel);
 	char	   *updatedbuf = NULL;
 	Size		updatedbuflen = 0;
-	OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
+	OffsetNumber updatedoffsets[MaxIndexTuplesPerPageLimit];
 
 	/* Shouldn't be called unless there's something to do */
 	Assert(ndeletable > 0 || nupdatable > 0);
@@ -1524,8 +1524,8 @@ _bt_delitems_delete_check(Relation rel, Buffer buf, Relation heapRel,
 	OffsetNumber postingidxoffnum = InvalidOffsetNumber;
 	int			ndeletable = 0,
 				nupdatable = 0;
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
-	BTVacuumPosting updatable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
+	BTVacuumPosting updatable[MaxIndexTuplesPerPageLimit];
 
 	/* Use tableam interface to determine which tuples to delete first */
 	snapshotConflictHorizon = table_index_delete_tuples(heapRel, delstate);
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index cb2757f68d..749a6bed9b 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -1156,9 +1156,9 @@ backtrack:
 	}
 	else if (P_ISLEAF(opaque))
 	{
-		OffsetNumber deletable[MaxIndexTuplesPerPage];
+		OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 		int			ndeletable;
-		BTVacuumPosting updatable[MaxIndexTuplesPerPage];
+		BTVacuumPosting updatable[MaxIndexTuplesPerPageLimit];
 		int			nupdatable;
 		OffsetNumber offnum,
 					minoff,
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index c87e46ed66..7035665eaf 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -40,8 +40,8 @@ _bt_restore_page(Page page, char *from, int len)
 	IndexTupleData itupdata;
 	Size		itemsz;
 	char	   *end = from + len;
-	Item		items[MaxIndexTuplesPerPage];
-	uint16		itemsizes[MaxIndexTuplesPerPage];
+	Item		items[MaxIndexTuplesPerPageLimit];
+	uint16		itemsizes[MaxIndexTuplesPerPageLimit];
 	int			i;
 	int			nitems;
 
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index d8c2752e85..02c5b31907 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -135,7 +135,7 @@ spgPageIndexMultiDelete(SpGistState *state, Page page,
 						BlockNumber blkno, OffsetNumber offnum)
 {
 	OffsetNumber firstItem;
-	OffsetNumber sortednos[MaxIndexTuplesPerPage];
+	OffsetNumber sortednos[MaxIndexTuplesPerPageLimit];
 	SpGistDeadTuple tuple = NULL;
 	int			i;
 
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index cbfaf0c00a..8686916846 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -755,8 +755,8 @@ spgGetNextQueueItem(SpGistScanOpaque so)
 enum SpGistSpecialOffsetNumbers
 {
 	SpGistBreakOffsetNumber = InvalidOffsetNumber,
-	SpGistRedirectOffsetNumber = MaxOffsetNumber + 1,
-	SpGistErrorOffsetNumber = MaxOffsetNumber + 2
+	SpGistRedirectOffsetNumber = MaxOffsetNumberLimit + 1,
+	SpGistErrorOffsetNumber = MaxOffsetNumberLimit + 2
 };
 
 static OffsetNumber
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index 8a5b540c80..f561ff6e2b 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -128,14 +128,14 @@ vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer,
 {
 	Page		page = BufferGetPage(buffer);
 	spgxlogVacuumLeaf xlrec;
-	OffsetNumber toDead[MaxIndexTuplesPerPage];
-	OffsetNumber toPlaceholder[MaxIndexTuplesPerPage];
-	OffsetNumber moveSrc[MaxIndexTuplesPerPage];
-	OffsetNumber moveDest[MaxIndexTuplesPerPage];
-	OffsetNumber chainSrc[MaxIndexTuplesPerPage];
-	OffsetNumber chainDest[MaxIndexTuplesPerPage];
-	OffsetNumber predecessor[MaxIndexTuplesPerPage + 1];
-	bool		deletable[MaxIndexTuplesPerPage + 1];
+	OffsetNumber toDead[MaxIndexTuplesPerPageLimit];
+	OffsetNumber toPlaceholder[MaxIndexTuplesPerPageLimit];
+	OffsetNumber moveSrc[MaxIndexTuplesPerPageLimit];
+	OffsetNumber moveDest[MaxIndexTuplesPerPageLimit];
+	OffsetNumber chainSrc[MaxIndexTuplesPerPageLimit];
+	OffsetNumber chainDest[MaxIndexTuplesPerPageLimit];
+	OffsetNumber predecessor[MaxIndexTuplesPerPageLimit + 1];
+	bool		deletable[MaxIndexTuplesPerPageLimit + 1];
 	int			nDeletable;
 	OffsetNumber i,
 				max = PageGetMaxOffsetNumber(page);
@@ -408,7 +408,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
 {
 	Page		page = BufferGetPage(buffer);
 	spgxlogVacuumRoot xlrec;
-	OffsetNumber toDelete[MaxIndexTuplesPerPage];
+	OffsetNumber toDelete[MaxIndexTuplesPerPageLimit];
 	OffsetNumber i,
 				max = PageGetMaxOffsetNumber(page);
 
@@ -498,8 +498,8 @@ vacuumRedirectAndPlaceholder(Relation index, Relation heaprel, Buffer buffer)
 				firstPlaceholder = InvalidOffsetNumber;
 	bool		hasNonPlaceholder = false;
 	bool		hasUpdate = false;
-	OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPage];
-	OffsetNumber itemnos[MaxIndexTuplesPerPage];
+	OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPageLimit];
+	OffsetNumber itemnos[MaxIndexTuplesPerPageLimit];
 	spgxlogVacuumRedirect xlrec;
 	GlobalVisState *vistest;
 
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 771dde3419..ba98cad70b 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -45,7 +45,7 @@
  */
 #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
 #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
-#define MAX_DELTA_SIZE			(cluster_block_size + 2 * FRAGMENT_HEADER_SIZE)
+#define MAX_DELTA_SIZE			(MAX_BLOCK_SIZE + 2 * FRAGMENT_HEADER_SIZE)
 
 /* Struct of generic xlog data for single page */
 typedef struct
diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c
index 8b4ed3d053..24319d1aeb 100644
--- a/src/backend/access/transam/timeline.c
+++ b/src/backend/access/transam/timeline.c
@@ -307,7 +307,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
 	char		path[MAXPGPATH];
 	char		tmppath[MAXPGPATH];
 	char		histfname[MAXFNAMELEN];
-	char		buffer[cluster_block_size];
+	char		buffer[DEFAULT_BLOCK_SIZE];
 	int			srcfd;
 	int			fd;
 	int			nbytes;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c7f38f7bc6..032ea78f3d 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -67,6 +67,7 @@
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_database.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_utils.h"
 #include "executor/instrument.h"
@@ -3924,7 +3925,7 @@ WriteControlFile(void)
 	ControlFile->floatFormat = FLOATFORMAT_VALUE;
 
 	ControlFile->blcksz = cluster_block_size;
-	ControlFile->relseg_size = RELSEG_SIZE;
+	ControlFile->relseg_size = cluster_relseg_size;
 	ControlFile->xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile->xlog_seg_size = wal_segment_size;
 
@@ -3990,14 +3991,104 @@ WriteControlFile(void)
 						XLOG_CONTROL_FILE)));
 }
 
+extern int block_size;
+
+/*
+ * This routine reads and returns the block size from the control file as
+ * needed; since this is the only field we care about and it needs to be
+ * handled early in the process, we don't worry about locks and the like; this
+ * is static for the life of the cluster.
+ */
+uint32
+ClusterBlockSize(void)
+{
+	pg_crc32c	crc;
+	int fd, r;
+	ControlFileData cluster_control;
+
+	if (block_size)
+		return block_size;
+
+	if (ControlFile)
+		return ControlFile->blcksz;
+
+	// TODO: check for bootstrap mode, since that's passed in the param?
+
+	/* neither shortcut worked, so go ahead and open the control file and
+	 * parse out only basic field */
+	fd = BasicOpenFile(XLOG_CONTROL_FILE,
+					   O_RDONLY | PG_BINARY);
+	if (fd < 0)
+		ereport(PANIC,
+				(errcode_for_file_access(),
+				 errmsg("could not open file \"%s\": %m",
+						XLOG_CONTROL_FILE)));
+
+	r = read(fd, &cluster_control, sizeof(ControlFileData));
+	if (r != sizeof(ControlFileData))
+	{
+		if (r < 0)
+			ereport(PANIC,
+					(errcode_for_file_access(),
+					 errmsg("could not read file \"%s\": %m",
+							XLOG_CONTROL_FILE)));
+		else
+			ereport(PANIC,
+					(errcode(ERRCODE_DATA_CORRUPTED),
+					 errmsg("could not read file \"%s\": read %d of %zu",
+							XLOG_CONTROL_FILE, r, sizeof(ControlFileData))));
+	}
+	close(fd);
+
+	/*
+	 * Check for expected pg_control format version.  If this is wrong, the
+	 * CRC check will likely fail because we'll be checking the wrong number
+	 * of bytes.  Complaining about wrong version will probably be more
+	 * enlightening than complaining about wrong CRC.
+	 */
+
+	if (cluster_control.pg_control_version != PG_CONTROL_VERSION && cluster_control.pg_control_version % 65536 == 0 && cluster_control.pg_control_version / 65536 != 0)
+		ereport(FATAL,
+				(errmsg("database files are incompatible with server"),
+				 errdetail("The database cluster was initialized with PG_CONTROL_VERSION %d (0x%08x),"
+						   " but the server was compiled with PG_CONTROL_VERSION %d (0x%08x).",
+						   cluster_control.pg_control_version, cluster_control.pg_control_version,
+						   PG_CONTROL_VERSION, PG_CONTROL_VERSION),
+				 errhint("This could be a problem of mismatched byte ordering.  It looks like you need to initdb.")));
+
+	if (cluster_control.pg_control_version != PG_CONTROL_VERSION)
+		ereport(FATAL,
+				(errmsg("database files are incompatible with server"),
+				 errdetail("The database cluster was initialized with PG_CONTROL_VERSION %d,"
+						   " but the server was compiled with PG_CONTROL_VERSION %d.",
+						   cluster_control.pg_control_version, PG_CONTROL_VERSION),
+				 errhint("It looks like you need to initdb.")));
+
+	/* Now check the CRC. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc,
+				(char *) &cluster_control,
+				offsetof(ControlFileData, crc));
+	FIN_CRC32C(crc);
+
+	if (!EQ_CRC32C(crc, cluster_control.crc))
+		ereport(FATAL,
+				(errmsg("incorrect checksum in control file")));
+
+	block_size = cluster_control.blcksz;
+
+	return block_size;
+}
+
 static void
 ReadControlFile(void)
 {
 	pg_crc32c	crc;
 	int			fd;
 	static char wal_segsz_str[20];
+	static char block_size_str[20];
 	int			r;
-
+	int block_size;
 	/*
 	 * Read data...
 	 */
@@ -4063,6 +4154,26 @@ ReadControlFile(void)
 		ereport(FATAL,
 				(errmsg("incorrect checksum in control file")));
 
+	/*
+	 * Block size computations affect a number of things that are later
+	 * checked, so ensure that we calculate as soon as CRC has been validated
+	 * before checking other things that may depend on it.
+	 */
+
+	block_size = ControlFile->blcksz;
+
+	if (!IsValidBlockSize(block_size))
+		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg_plural("Block size must be a power of two between 1k and 32k, but the control file specifies %d byte",
+									  "Block size must be a power of two between 1k and 32k, but the control file specifies %d bytes",
+									  block_size,
+									  block_size)));
+
+	BlockSizeInit(block_size);
+	snprintf(block_size_str, sizeof(block_size_str), "%d", block_size);
+	SetConfigOption("block_size", block_size_str, PGC_INTERNAL,
+					PGC_S_DYNAMIC_DEFAULT);
+
 	/*
 	 * Do compatibility checking immediately.  If the database isn't
 	 * compatible with the backend executable, we want to abort before we can
@@ -4087,19 +4198,12 @@ ReadControlFile(void)
 				(errmsg("database files are incompatible with server"),
 				 errdetail("The database cluster appears to use a different floating-point number format than the server executable."),
 				 errhint("It looks like you need to initdb.")));
-	if (ControlFile->blcksz != cluster_block_size)
-		ereport(FATAL,
-				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with cluster_block_size %d,"
-						   " but the server was compiled with cluster_block_size %d.",
-						   ControlFile->blcksz, cluster_block_size),
-				 errhint("It looks like you need to recompile or initdb.")));
-	if (ControlFile->relseg_size != RELSEG_SIZE)
+	if (ControlFile->relseg_size != cluster_relseg_size)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with RELSEG_SIZE %d,"
-						   " but the server was compiled with RELSEG_SIZE %d.",
-						   ControlFile->relseg_size, RELSEG_SIZE),
+				 errdetail("The database cluster was initialized with cluster_relseg_size %d,"
+						   " but the server was compiled with cluster_relseg_size %d.",
+						   ControlFile->relseg_size, cluster_relseg_size),
 				 errhint("It looks like you need to recompile or initdb.")));
 	if (ControlFile->xlog_blcksz != XLOG_BLCKSZ)
 		ereport(FATAL,
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 87e04d6e03..d833a5734a 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -46,18 +46,18 @@
  * backup block image.
  */
 #ifdef USE_LZ4
-#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(cluster_block_size)
+#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(MAX_BLOCK_SIZE)
 #else
 #define LZ4_MAX_BLCKSZ		0
 #endif
 
 #ifdef USE_ZSTD
-#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(cluster_block_size)
+#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(MAX_BLOCK_SIZE)
 #else
 #define ZSTD_MAX_BLCKSZ		0
 #endif
 
-#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(cluster_block_size)
+#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(MAX_BLOCK_SIZE)
 
 /* Buffer size required to store a compressed version of backup block image */
 #define COMPRESS_BUFSIZE	Max(Max(PGLZ_MAX_BLCKSZ, LZ4_MAX_BLCKSZ), ZSTD_MAX_BLCKSZ)
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 8fa1ae018a..a275b186f6 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -23,6 +23,7 @@
 #include "backup/basebackup_sink.h"
 #include "backup/basebackup_target.h"
 #include "commands/defrem.h"
+#include "common/blocksize.h"
 #include "common/compression.h"
 #include "common/file_perm.h"
 #include "lib/stringinfo.h"
@@ -370,7 +371,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 			else
 			{
 				/* Properly terminate the tarfile. */
-				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= MAX_BLOCK_SIZE,
 								 "cluster_block_size too small for 2 tar blocks");
 				memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 				bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
@@ -623,7 +624,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 		}
 
 		/* Properly terminate the tar file. */
-		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= MAX_BLOCK_SIZE,
 						 "cluster_block_size too small for 2 tar blocks");
 		memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 		bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
@@ -1580,7 +1581,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 
 		if (verify_checksum)
 		{
-			for (i = 0; i < cnt / cluster_block_size; i++)
+			for (i = 0; i < cnt >> cluster_block_bits; i++)
 			{
 				page = sink->bbs_buffer + cluster_block_size * i;
 
@@ -1594,7 +1595,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 				 */
 				if (!PageIsNew(page) && PageGetLSN(page) < sink->bbs_state->startptr)
 				{
-					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
+					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE, cluster_block_size);
 					phdr = (PageHeader) page;
 					if (phdr->pd_checksum != checksum)
 					{
@@ -1749,8 +1750,8 @@ _tarWriteHeader(bbsink *sink, const char *filename, const char *linktarget,
 		 * large enough to fit an entire tar block. We double-check by means
 		 * of these assertions.
 		 */
-		StaticAssertDecl(TAR_BLOCK_SIZE <= cluster_block_size,
-						 "cluster_block_size too small for tar block");
+		StaticAssertDecl(TAR_BLOCK_SIZE <= DEFAULT_BLOCK_SIZE,
+						 "DEFAULT_BLOCK_SIZE too small for tar block");
 		Assert(sink->bbs_buffer_length >= TAR_BLOCK_SIZE);
 
 		rc = tarCreateHeader(sink->bbs_buffer, filename, linktarget,
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 5810f8825e..91ace7613d 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -28,6 +28,7 @@
 #include "catalog/index.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
+#include "common/blocksize.h"
 #include "common/link-canary.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -46,6 +47,7 @@
 #include "utils/relmapper.h"
 
 uint32		bootstrap_data_checksum_version = 0;	/* No checksum */
+uint32		bootstrap_blocksize = DEFAULT_BLOCK_SIZE;
 
 
 static void CheckerModeMain(void);
@@ -221,10 +223,18 @@ 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:B:c:d:D:Fkr:X:-:")) != -1)
 	{
 		switch (flag)
 		{
+			case 'b':
+				bootstrap_blocksize = strtol(optarg, NULL, 0);
+				if (!IsValidBlockSize(bootstrap_blocksize))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid block size: %s; must be power of two between 1k and 32k",
+									optarg)));
+				break;
 			case 'B':
 				SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
 				break;
@@ -300,6 +310,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
 	if (!SelectConfigFiles(userDoption, progname))
 		proc_exit(1);
 
+	BlockSizeInit(bootstrap_blocksize);
+
 	/*
 	 * Validate we have been given a reasonable-looking DataDir and change
 	 * into it
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index c7d19f4ee0..6a1ae24955 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -163,7 +163,7 @@
  * than that, so changes in that data structure won't affect user-visible
  * restrictions.
  */
-#define NOTIFY_PAYLOAD_MAX_LENGTH	(cluster_block_size - NAMEDATALEN - 128)
+#define NOTIFY_PAYLOAD_MAX_LENGTH	(DEFAULT_BLOCK_SIZE - NAMEDATALEN - 128)
 
 /*
  * Struct representing an entry in the global notify queue
@@ -311,7 +311,7 @@ static AsyncQueueControl *asyncQueueControl;
 static SlruCtlData NotifyCtlData;
 
 #define NotifyCtl					(&NotifyCtlData)
-#define QUEUE_PAGESIZE				cluster_block_size
+#define QUEUE_PAGESIZE				DEFAULT_BLOCK_SIZE
 #define QUEUE_FULL_WARN_INTERVAL	5000	/* warn at most once every 5s */
 
 /*
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index 971c29c428..5c7593617a 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -53,7 +53,7 @@
  * the per-page bitmaps variable size.  We just legislate that the size
  * is this:
  */
-#define MAX_TUPLES_PER_PAGE  MaxHeapTuplesPerPage
+#define MAX_TUPLES_PER_PAGE  MaxHeapTuplesPerPageLimit
 
 /*
  * When we have to switch over to lossy storage, we use a data structure
@@ -70,7 +70,7 @@
  * too different.  But we also want PAGES_PER_CHUNK to be a power of 2 to
  * avoid expensive integer remainder operations.  So, define it like this:
  */
-#define PAGES_PER_CHUNK  (cluster_block_size / 32)
+#define PAGES_PER_CHUNK  (MAX_BLOCK_SIZE / 32)
 
 /* We use BITS_PER_BITMAPWORD and typedef bitmapword from nodes/bitmapset.h */
 
diff --git a/src/backend/po/de.po b/src/backend/po/de.po
index ac2cb6e4cf..be75aea0a4 100644
--- a/src/backend/po/de.po
+++ b/src/backend/po/de.po
@@ -22030,7 +22030,7 @@ msgstr "debug_io_direct wird für WAL nicht unterstützt, weil XLOG_BLCKSZ zu kl
 #: storage/file/fd.c:3951
 #, c-format
 msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
-msgstr "debug_io_direct wird für Daten nicht unterstützt, weil cluster_block_size zu klein ist"
+msgstr "debug_io_direct wird für Daten nicht unterstützt, weil cluster_block_sizes zu klein ist"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index d11f73c13b..cdac86cd20 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3500,7 +3500,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 
 		szbytes = table_relation_size(relation, forkNum);
 
-		return (szbytes + (cluster_block_size - 1)) / cluster_block_size;
+		return (szbytes + (cluster_block_size - 1)) >> cluster_block_bits;
 	}
 	else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
 	{
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 935b870933..efb9cccc44 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -745,7 +745,7 @@ GetLocalBufferStorage(void)
 		/* But not more than what we need for all remaining local bufs */
 		num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
 		/* And don't overflow MaxAllocSize, either */
-		num_bufs = Min(num_bufs, MaxAllocSize / cluster_block_size);
+		num_bufs = Min(num_bufs, MaxAllocSize >> cluster_block_bits);
 
 		/* Buffers should be I/O aligned. */
 		cur_block = (char *)
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index cf5d980026..d817a6ec5a 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -60,7 +60,7 @@
  * tablespaces when available.
  */
 #define MAX_PHYSICAL_FILESIZE	0x40000000
-#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / cluster_block_size)
+#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE >> cluster_block_bits)
 
 /*
  * This data structure represents a buffered file that consists of one or
@@ -867,7 +867,7 @@ BufFileTellBlock(BufFile *file)
 {
 	long		blknum;
 
-	blknum = (file->curOffset + file->pos) / cluster_block_size;
+	blknum = (file->curOffset + file->pos) >> cluster_block_bits;
 	blknum += file->curFile * BUFFILE_SEG_SIZE;
 	return blknum;
 }
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 7207405e06..c04166d19d 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3943,10 +3943,10 @@ check_debug_io_direct(char **newval, void **extra, GucSource source)
 		result = false;
 	}
 #endif
-#if cluster_block_size < PG_IO_ALIGN_SIZE
+#if DEFAULT_BLOCK_SIZE < PG_IO_ALIGN_SIZE
 	if (result && (flags & IO_DIRECT_DATA))
 	{
-		GUC_check_errdetail("debug_io_direct is not supported for data because cluster_block_size is too small");
+		GUC_check_errdetail("debug_io_direct is not supported for data because DEFAULT_BLOCK_SIZE is too small");
 		result = false;
 	}
 #endif
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index f17fe758d6..c89ed0c23f 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -87,8 +87,8 @@ typedef struct
 	int			logpageno;		/* page number within the level */
 } FSMAddress;
 
-/* Address of the root page. */
-static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0};
+/* Address of the root page. Level is adjusted by FreeSpaceMapInit()  */
+static FSMAddress FSM_ROOT_ADDRESS = {0,0};
 
 /* functions to navigate the tree */
 static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot);
@@ -116,6 +116,17 @@ static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr,
 
 /******** Public API ********/
 
+/*
+ * FreeSpaceMapInit - initialize the FSM system with the cluster block size
+ *
+ */
+
+void
+FreeSpaceMapInit(void)
+{
+	FSM_ROOT_ADDRESS.level = FSM_ROOT_LEVEL;
+}
+
 /*
  * GetPageWithFreeSpace - try to find a page in the given relation with
  *		at least the specified amount of free space.
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 17b834d39b..b178213391 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -598,7 +598,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[cluster_loblksize + VARHDRSZ];
+		char		data[LOBLKSIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
@@ -789,7 +789,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[cluster_loblksize + VARHDRSZ];
+		char		data[LOBLKSIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 07a664355f..6c6217bb00 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -102,7 +102,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 	{
 		if (DataChecksumsEnabled())
 		{
-			checksum = pg_checksum_page((char *) page, blkno);
+			checksum = pg_checksum_page((char *) page, blkno, cluster_block_size);
 
 			if (checksum != p->pd_checksum)
 				checksum_failure = true;
@@ -702,7 +702,7 @@ PageRepairFragmentation(Page page)
 	Offset		pd_upper = ((PageHeader) page)->pd_upper;
 	Offset		pd_special = ((PageHeader) page)->pd_special;
 	Offset		last_offset;
-	itemIdCompactData itemidbase[MaxHeapTuplesPerPage];
+	itemIdCompactData itemidbase[MaxHeapTuplesPerPageLimit];
 	itemIdCompact itemidptr;
 	ItemId		lp;
 	int			nline,
@@ -1165,8 +1165,8 @@ PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
 	Offset		pd_upper = phdr->pd_upper;
 	Offset		pd_special = phdr->pd_special;
 	Offset		last_offset;
-	itemIdCompactData itemidbase[MaxIndexTuplesPerPage];
-	ItemIdData	newitemids[MaxIndexTuplesPerPage];
+	itemIdCompactData itemidbase[MaxIndexTuplesPerPageLimit];
+	ItemIdData	newitemids[MaxIndexTuplesPerPageLimit];
 	itemIdCompact itemidptr;
 	ItemId		lp;
 	int			nline,
@@ -1528,7 +1528,7 @@ PageSetChecksumCopy(Page page, BlockNumber blkno)
 											 0);
 
 	memcpy(pageCopy, (char *) page, cluster_block_size);
-	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno);
+	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno, cluster_block_size);
 	return pageCopy;
 }
 
@@ -1545,5 +1545,5 @@ PageSetChecksumInplace(Page page, BlockNumber blkno)
 	if (PageIsNew(page) || !DataChecksumsEnabled())
 		return;
 
-	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno);
+	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno, cluster_block_size);
 }
diff --git a/src/backend/storage/smgr/README b/src/backend/storage/smgr/README
index cf3aa5632b..22515ab823 100644
--- a/src/backend/storage/smgr/README
+++ b/src/backend/storage/smgr/README
@@ -50,3 +50,55 @@ to relfilelocator and block number, to identify which relation fork you want to
 access. Since most code wants to access the main fork, a shortcut version of
 ReadBuffer that accesses MAIN_FORKNUM is provided in the buffer manager for
 convenience.
+
+
+Adjustments for Variable Block Sizes
+====================================
+
+Prior to 17, the disk block size (aka cluster_block_size) was set at compile-time. While
+each database cluster still has a fixed block size, this is now selected at
+`initdb` time, meaning that a single PostgreSQL binary installation can support
+clusters of varying sizes.
+
+As this is now a per-cluster setting, the block size is set in the pg_control
+file. There are multiple computed settings which rely on the cluster block size,
+so these also need to be initialized at cluster start.  This is managed via
+BlockSizeInit() in src/common/blocksize.c.  This routine computes a table for
+all values which would have been computed at compile time.
+
+The expressions used to calculate the values in this table have been moved from
+their original locations and parameterized based on blocksize; these are named
+CalcXXXX(blocksize), and are stored in src/include/common/blocksize.h.
+
+In order to minimize code changes, we have kept the names of former constants
+(such as MaxHeapTuplesPerPage) and replaced their definitions with an
+appropriate lookup in this table.  Since these lookups are now handled at
+runtime, it is important to initialize these as early as possible so any code
+usage of these constants uses the computed value after it has been initialized
+instead of 0.  This applies to any piece of the code which needs to know about
+the block size or uses any value which was based on it.
+
+To ensure that any code that depended on the old behavior is properly adjusted,
+we have removed cluster_block_size as a constant, which will ensure that external code will
+fail to compile.  When writing new code which cases about the block size, you
+should use one of the following values:
+
+- cluster_block_size - evaluates to the cluster's block size as initialized by
+  BlockSizeInit().
+
+- DEFAULT_BLOCK_SIZE - the compiled-in default block size; 8k
+
+- MIN_BLOCK_SIZE - the smallest supported block size; 1k
+
+- MAX_BLOCK_SIZE - the largest supported block size; 32k
+
+In general, you should use cluster_block_size unless you are allocating space on
+the stack, and even then you should be careful to allocate based on
+MAX_BLOCK_SIZE, but still limit accesses to these structs based on the /actual/
+runtime cluster block size.  The core code has bene modified to follow these
+standards.
+
+Occasionally, we had been using cluster_block_size as a "big enough buffer" rather than
+caring about any specific size correlated with the cluster.  In those cases, you
+should just use DEFAULT_BLOCK_SIZE.
+
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 319e8d8655..15f1d9849e 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -47,13 +47,13 @@
  * easier to support relations that are larger than the operating
  * system's file size limit (often 2GBytes).  In order to do that,
  * we break relations up into "segment" files that are each shorter than
- * the OS file size limit.  The segment size is set by the RELSEG_SIZE
+ * the OS file size limit.  The segment size is set by the cluster_relseg_size
  * configuration constant in pg_config.h.
  *
  * On disk, a relation must consist of consecutively numbered segment
  * files in the pattern
- *	-- Zero or more full segments of exactly RELSEG_SIZE blocks each
- *	-- Exactly one partial segment of size 0 <= size < RELSEG_SIZE blocks
+ *	-- Zero or more full segments of exactly cluster_relseg_size blocks each
+ *	-- Exactly one partial segment of size 0 <= size < cluster_relseg_size blocks
  *	-- Optionally, any number of inactive segments of size 0 blocks.
  * The full and partial segments are collectively the "active" segments.
  * Inactive segments are those that once contained data but are currently
@@ -110,7 +110,7 @@ static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 #define EXTENSION_CREATE_RECOVERY	(1 << 3)
 /*
  * Allow opening segments which are preceded by segments smaller than
- * RELSEG_SIZE, e.g. inactive segments (see above). Note that this breaks
+ * cluster_relseg_size, e.g. inactive segments (see above). Note that this breaks
  * mdnblocks() and related functionality henceforth - which currently is ok,
  * because this is only required in the checkpointer which never uses
  * mdnblocks().
@@ -490,9 +490,9 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync, EXTENSION_CREATE);
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != cluster_block_size)
 	{
@@ -514,7 +514,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	if (!skipFsync && !SmgrIsTemp(reln))
 		register_dirty_segment(reln, forknum, v);
 
-	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) cluster_relseg_size));
 }
 
 /*
@@ -552,19 +552,19 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 
 	while (remblocks > 0)
 	{
-		BlockNumber segstartblock = curblocknum % ((BlockNumber) RELSEG_SIZE);
+		BlockNumber segstartblock = curblocknum % ((BlockNumber) cluster_relseg_size);
 		off_t		seekpos = (off_t) cluster_block_size * segstartblock;
 		int			numblocks;
 
-		if (segstartblock + remblocks > RELSEG_SIZE)
-			numblocks = RELSEG_SIZE - segstartblock;
+		if (segstartblock + remblocks > cluster_relseg_size)
+			numblocks = cluster_relseg_size - segstartblock;
 		else
 			numblocks = remblocks;
 
 		v = _mdfd_getseg(reln, forknum, curblocknum, skipFsync, EXTENSION_CREATE);
 
-		Assert(segstartblock < RELSEG_SIZE);
-		Assert(segstartblock + numblocks <= RELSEG_SIZE);
+		Assert(segstartblock < cluster_relseg_size);
+		Assert(segstartblock + numblocks <= cluster_relseg_size);
 
 		/*
 		 * If available and useful, use posix_fallocate() (via
@@ -618,7 +618,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 		if (!skipFsync && !SmgrIsTemp(reln))
 			register_dirty_segment(reln, forknum, v);
 
-		Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+		Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) cluster_relseg_size));
 
 		remblocks -= numblocks;
 		curblocknum += numblocks;
@@ -670,7 +670,7 @@ mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 	mdfd->mdfd_vfd = fd;
 	mdfd->mdfd_segno = 0;
 
-	Assert(_mdnblocks(reln, forknum, mdfd) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, mdfd) <= ((BlockNumber) cluster_relseg_size));
 
 	return mdfd;
 }
@@ -726,9 +726,9 @@ mdprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 	if (v == NULL)
 		return false;
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	(void) FilePrefetch(v->mdfd_vfd, seekpos, cluster_block_size, WAIT_EVENT_DATA_FILE_PREFETCH);
 #endif							/* USE_PREFETCH */
@@ -760,9 +760,9 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, false,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	nbytes = FileRead(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_READ);
 
@@ -834,9 +834,9 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
 
@@ -907,17 +907,17 @@ mdwriteback(SMgrRelation reln, ForkNumber forknum,
 			return;
 
 		/* compute offset inside the current segment */
-		segnum_start = blocknum / RELSEG_SIZE;
+		segnum_start = blocknum / cluster_relseg_size;
 
 		/* compute number of desired writes within the current segment */
-		segnum_end = (blocknum + nblocks - 1) / RELSEG_SIZE;
+		segnum_end = (blocknum + nblocks - 1) / cluster_relseg_size;
 		if (segnum_start != segnum_end)
-			nflush = RELSEG_SIZE - (blocknum % ((BlockNumber) RELSEG_SIZE));
+			nflush = cluster_relseg_size - (blocknum % ((BlockNumber) cluster_relseg_size));
 
 		Assert(nflush >= 1);
 		Assert(nflush <= nblocks);
 
-		seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+		seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
 		FileWriteback(v->mdfd_vfd, seekpos, (off_t) cluster_block_size * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
 
@@ -948,7 +948,7 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 
 	/*
 	 * Start from the last open segments, to avoid redundant seeks.  We have
-	 * previously verified that these segments are exactly RELSEG_SIZE long,
+	 * previously verified that these segments are exactly cluster_relseg_size long,
 	 * and it's useless to recheck that each time.
 	 *
 	 * NOTE: this assumption could only be wrong if another backend has
@@ -965,13 +965,13 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 	for (;;)
 	{
 		nblocks = _mdnblocks(reln, forknum, v);
-		if (nblocks > ((BlockNumber) RELSEG_SIZE))
+		if (nblocks > ((BlockNumber) cluster_relseg_size))
 			elog(FATAL, "segment too big");
-		if (nblocks < ((BlockNumber) RELSEG_SIZE))
-			return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks;
+		if (nblocks < ((BlockNumber) cluster_relseg_size))
+			return (segno * ((BlockNumber) cluster_relseg_size)) + nblocks;
 
 		/*
-		 * If segment is exactly RELSEG_SIZE, advance to next one.
+		 * If segment is exactly cluster_relseg_size, advance to next one.
 		 */
 		segno++;
 
@@ -984,7 +984,7 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 		 */
 		v = _mdfd_openseg(reln, forknum, segno, 0);
 		if (v == NULL)
-			return segno * ((BlockNumber) RELSEG_SIZE);
+			return segno * ((BlockNumber) cluster_relseg_size);
 	}
 }
 
@@ -1025,7 +1025,7 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 	{
 		MdfdVec    *v;
 
-		priorblocks = (curopensegs - 1) * RELSEG_SIZE;
+		priorblocks = (curopensegs - 1) * cluster_relseg_size;
 
 		v = &reln->md_seg_fds[forknum][curopensegs - 1];
 
@@ -1050,12 +1050,12 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 			FileClose(v->mdfd_vfd);
 			_fdvec_resize(reln, forknum, curopensegs - 1);
 		}
-		else if (priorblocks + ((BlockNumber) RELSEG_SIZE) > nblocks)
+		else if (priorblocks + ((BlockNumber) cluster_relseg_size) > nblocks)
 		{
 			/*
 			 * This is the last segment we want to keep. Truncate the file to
 			 * the right length. NOTE: if nblocks is exactly a multiple K of
-			 * RELSEG_SIZE, we will truncate the K+1st segment to 0 length but
+			 * cluster_relseg_size, we will truncate the K+1st segment to 0 length but
 			 * keep it. This adheres to the invariant given in the header
 			 * comments.
 			 */
@@ -1372,7 +1372,7 @@ _mdfd_openseg(SMgrRelation reln, ForkNumber forknum, BlockNumber segno,
 	v->mdfd_vfd = fd;
 	v->mdfd_segno = segno;
 
-	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) cluster_relseg_size));
 
 	/* all done */
 	return v;
@@ -1399,7 +1399,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 		   (EXTENSION_FAIL | EXTENSION_CREATE | EXTENSION_RETURN_NULL |
 			EXTENSION_DONT_OPEN));
 
-	targetseg = blkno / ((BlockNumber) RELSEG_SIZE);
+	targetseg = blkno / ((BlockNumber) cluster_relseg_size);
 
 	/* if an existing and opened segment, we're done */
 	if (targetseg < reln->md_num_open_segs[forknum])
@@ -1436,7 +1436,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 
 		Assert(nextsegno == v->mdfd_segno + 1);
 
-		if (nblocks > ((BlockNumber) RELSEG_SIZE))
+		if (nblocks > ((BlockNumber) cluster_relseg_size))
 			elog(FATAL, "segment too big");
 
 		if ((behavior & EXTENSION_CREATE) ||
@@ -1451,30 +1451,30 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 			 * ahead and create the segments so we can finish out the replay.
 			 *
 			 * We have to maintain the invariant that segments before the last
-			 * active segment are of size RELSEG_SIZE; therefore, if
+			 * active segment are of size cluster_relseg_size; therefore, if
 			 * extending, pad them out with zeroes if needed.  (This only
 			 * matters if in recovery, or if the caller is extending the
 			 * relation discontiguously, but that can happen in hash indexes.)
 			 */
-			if (nblocks < ((BlockNumber) RELSEG_SIZE))
+			if (nblocks < ((BlockNumber) cluster_relseg_size))
 			{
 				char	   *zerobuf = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE,
 													 MCXT_ALLOC_ZERO);
 
 				mdextend(reln, forknum,
-						 nextsegno * ((BlockNumber) RELSEG_SIZE) - 1,
+						 nextsegno * ((BlockNumber) cluster_relseg_size) - 1,
 						 zerobuf, skipFsync);
 				pfree(zerobuf);
 			}
 			flags = O_CREAT;
 		}
 		else if (!(behavior & EXTENSION_DONT_CHECK_SIZE) &&
-				 nblocks < ((BlockNumber) RELSEG_SIZE))
+				 nblocks < ((BlockNumber) cluster_relseg_size))
 		{
 			/*
 			 * When not extending (or explicitly including truncated
 			 * segments), only open the next segment if the current one is
-			 * exactly RELSEG_SIZE.  If not (this branch), either return NULL
+			 * exactly cluster_relseg_size.  If not (this branch), either return NULL
 			 * or fail.
 			 */
 			if (behavior & EXTENSION_RETURN_NULL)
@@ -1529,7 +1529,7 @@ _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 				 errmsg("could not seek to end of file \"%s\": %m",
 						FilePathName(seg->mdfd_vfd))));
 	/* note that this calculation will ignore any partial block at EOF */
-	return (BlockNumber) (len / cluster_block_size);
+	return (BlockNumber) (len >> cluster_block_bits);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 37d91cb902..da82e96496 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1501,8 +1501,8 @@ AddToDataDirLockFile(int target_line, const char *str)
 	int			lineno;
 	char	   *srcptr;
 	char	   *destptr;
-	char		srcbuffer[cluster_block_size];
-	char		destbuffer[cluster_block_size];
+	char		srcbuffer[DEFAULT_BLOCK_SIZE];
+	char		destbuffer[DEFAULT_BLOCK_SIZE];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
@@ -1626,7 +1626,7 @@ RecheckDataDirLockFile(void)
 	int			fd;
 	int			len;
 	long		file_pid;
-	char		buffer[cluster_block_size];
+	char		buffer[DEFAULT_BLOCK_SIZE];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1ec0edd6ac..98d78c8792 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -30,6 +30,7 @@
 
 #include "access/xact.h"
 #include "access/xlog.h"
+#include "access/xlog_internal.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_parameter_acl.h"
@@ -105,8 +106,8 @@ typedef struct
 } unit_conversion;
 
 /* Ensure that the constants in the tables don't overflow or underflow */
-#if cluster_block_size < 1024 || cluster_block_size > (1024*1024)
-#error cluster_block_size must be between 1KB and 1MB
+#if DEFAULT_BLOCK_SIZE < 1024 || DEFAULT_BLOCK_SIZE > (1024*1024)
+#error DEFAULT_BLOCK_SIZE must be between 1KB and 1MB
 #endif
 #if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
 #error XLOG_BLCKSZ must be between 1KB and 1MB
@@ -134,11 +135,11 @@ static const unit_conversion memory_unit_conversion_table[] =
 	{"kB", GUC_UNIT_MB, 1.0 / 1024.0},
 	{"B", GUC_UNIT_MB, 1.0 / (1024.0 * 1024.0)},
 
-	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (cluster_block_size / 1024)},
-	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (cluster_block_size / 1024)},
-	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (cluster_block_size / 1024)},
-	{"kB", GUC_UNIT_BLOCKS, 1.0 / (cluster_block_size / 1024)},
-	{"B", GUC_UNIT_BLOCKS, 1.0 / cluster_block_size},
+	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"kB", GUC_UNIT_BLOCKS, 1.0 / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"B", GUC_UNIT_BLOCKS, 1.0 / DEFAULT_BLOCK_SIZE},
 
 	{"TB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
 	{"GB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
@@ -1503,6 +1504,16 @@ InitializeGUCOptions(void)
 	 */
 	pg_timezone_initialize();
 
+	/* Load our block size -- no valid in Bootstrap or init mode, since control file hasn't been written */
+	if (!(IsInitProcessingMode() || IsBootstrapProcessingMode()))
+	{
+		BlockSizeInit(ClusterBlockSize());
+		/*
+		 * Ensure GUCs with dynamic limits have been properly initialized
+		 */
+		update_dynamic_gucs();
+	}
+
 	/*
 	 * Create GUCMemoryContext and build hash table of all GUC variables.
 	 */
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index a465f7f9d1..fb8c47c406 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -41,6 +41,7 @@
 #include "commands/trigger.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
+#include "common/blocksize.h"
 #include "common/scram-common.h"
 #include "jit/jit.h"
 #include "libpq/auth.h"
@@ -504,8 +505,11 @@ bool		log_btree_build_stats = false;
 char	   *event_source;
 
 bool		row_security;
+
 bool		check_function_bodies = true;
 
+int        	block_size;
+
 /*
  * This GUC exists solely for backward compatibility, check its definition for
  * details.
@@ -588,7 +592,6 @@ static char *session_authorization_string;
 static int	max_function_args;
 static int	max_index_keys;
 static int	max_identifier_length;
-static int	block_size;
 static int	segment_size;
 static int	shared_memory_size_mb;
 static int	shared_memory_size_in_huge_pages;
@@ -3118,7 +3121,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&block_size,
-		cluster_block_size, cluster_block_size, cluster_block_size,
+		DEFAULT_BLOCK_SIZE, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE,
 		NULL, NULL, NULL
 	},
 
@@ -3368,7 +3371,8 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_table_scan_size,
-		(8 * 1024 * 1024) / cluster_block_size, 0, INT_MAX / 3,
+		/* This is set dynamically based on cluster_block_size, but works for a default */
+		(8 * 1024 * 1024) / DEFAULT_BLOCK_SIZE, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -3379,7 +3383,8 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_index_scan_size,
-		(512 * 1024) / cluster_block_size, 0, INT_MAX / 3,
+		/* This is set dynamically based on cluster_block_size, but works for a default */
+		(512 * 1024) / DEFAULT_BLOCK_SIZE, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -4986,3 +4991,29 @@ struct config_enum ConfigureNamesEnum[] =
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
 	}
 };
+
+
+
+/*
+ * Update the specific GUCs which have dynamic limits.  Due to having variable
+ * block sizes, what were originally constants now depend on the runtime block
+ * size, so handle the adjustments to those boot values here.
+ */
+void
+update_dynamic_gucs(void)
+{
+	int i;
+
+	for (i = 0; ConfigureNamesInt[i].gen.name; i++)
+	{
+		if (strcmp("min_parallel_table_scan_size", ConfigureNamesInt[i].gen.name) == 0)
+		{
+			ConfigureNamesInt[i].boot_val = (8 * 1024 * 1024) >> cluster_block_bits;
+		}
+		else if (strcmp("min_parallel_index_scan_size", ConfigureNamesInt[i].gen.name) == 0)
+		{
+			ConfigureNamesInt[i].boot_val = (512 * 1024) >> cluster_block_bits;
+		}
+	}
+
+}
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index f46d2bac60..9a53205a58 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -640,7 +640,7 @@ LogicalTapeImport(LogicalTapeSet *lts, int worker, TapeShare *shared)
 	}
 	/* Don't allocate more for read buffer than could possibly help */
 	lt->max_size = Min(MaxAllocSize, filesize);
-	tapeblocks = filesize / cluster_block_size;
+	tapeblocks = filesize >> cluster_block_bits;
 
 	/*
 	 * Update # of allocated blocks and # blocks written to reflect the
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 84b0cc7954..ef810b0f59 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -69,6 +69,7 @@
 #include "catalog/pg_class_d.h" /* pgrminclude ignore */
 #include "catalog/pg_collation_d.h"
 #include "catalog/pg_database_d.h"	/* pgrminclude ignore */
+#include "common/blocksize.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
@@ -165,6 +166,8 @@ static bool show_setting = false;
 static bool data_checksums = false;
 static char *xlog_dir = NULL;
 static int	wal_segment_size_mb = (DEFAULT_XLOG_SEG_SIZE) / (1024 * 1024);
+static char *str_block_size = NULL;
+int block_size = DEFAULT_BLOCK_SIZE;
 
 
 /* internal vars */
@@ -1108,8 +1111,8 @@ test_config_settings(void)
 
 	for (i = 0; i < bufslen; i++)
 	{
-		/* Use same amount of memory, independent of cluster_block_size */
-		test_buffs = (trial_bufs[i] * 8192) / cluster_block_size;
+		/* Use same amount of memory, independent of block_size */
+		test_buffs = (trial_bufs[i] * 8192) / block_size;
 		if (test_buffs <= ok_buffers)
 		{
 			test_buffs = ok_buffers;
@@ -1121,10 +1124,10 @@ test_config_settings(void)
 	}
 	n_buffers = test_buffs;
 
-	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
-		printf("%dMB\n", (n_buffers * (cluster_block_size / 1024)) / 1024);
+	if ((n_buffers * (block_size / 1024)) % 1024 == 0)
+		printf("%dMB\n", (n_buffers * (block_size / 1024)) / 1024);
 	else
-		printf("%dkB\n", n_buffers * (cluster_block_size / 1024));
+		printf("%dkB\n", n_buffers * (block_size / 1024));
 
 	printf(_("selecting default time zone ... "));
 	fflush(stdout);
@@ -1147,11 +1150,12 @@ test_specific_config_settings(int test_conns, int test_buffs)
 
 	/* Set up the test postmaster invocation */
 	printfPQExpBuffer(&cmd,
-					  "\"%s\" --check %s %s "
+					  "\"%s\" --check %s %s -b %d "
 					  "-c max_connections=%d "
 					  "-c shared_buffers=%d "
 					  "-c dynamic_shared_memory_type=%s",
 					  backend_exec, boot_options, extra_options,
+					  block_size,
 					  test_conns, test_buffs,
 					  dynamic_shared_memory_type);
 
@@ -1216,12 +1220,12 @@ setup_config(void)
 	conflines = replace_guc_value(conflines, "max_connections",
 								  repltok, false);
 
-	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
+	if ((n_buffers * (block_size / 1024)) % 1024 == 0)
 		snprintf(repltok, sizeof(repltok), "%dMB",
-				 (n_buffers * (cluster_block_size / 1024)) / 1024);
+				 (n_buffers * (block_size / 1024)) / 1024);
 	else
 		snprintf(repltok, sizeof(repltok), "%dkB",
-				 n_buffers * (cluster_block_size / 1024));
+				 n_buffers * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "shared_buffers",
 								  repltok, false);
 
@@ -1296,21 +1300,21 @@ setup_config(void)
 
 #if DEFAULT_BACKEND_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BACKEND_FLUSH_AFTER * (cluster_block_size / 1024));
+			 DEFAULT_BACKEND_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "backend_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_BGWRITER_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BGWRITER_FLUSH_AFTER * (cluster_block_size / 1024));
+			 DEFAULT_BGWRITER_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "bgwriter_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_CHECKPOINT_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (cluster_block_size / 1024));
+			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "checkpoint_flush_after",
 								  repltok, true);
 #endif
@@ -1537,12 +1541,12 @@ bootstrap_template1(void)
 
 	printfPQExpBuffer(&cmd, "\"%s\" --boot %s %s", backend_exec, boot_options, extra_options);
 	appendPQExpBuffer(&cmd, " -X %d", wal_segment_size_mb * (1024 * 1024));
+	appendPQExpBuffer(&cmd, " -b %d", block_size);
 	if (data_checksums)
 		appendPQExpBuffer(&cmd, " -k");
 	if (debug)
 		appendPQExpBuffer(&cmd, " -d 5");
 
-
 	PG_CMD_OPEN(cmd.data);
 
 	for (line = bki_lines; *line != NULL; line++)
@@ -2436,6 +2440,7 @@ usage(const char *progname)
 	printf(_("  -A, --auth=METHOD         default authentication method for local connections\n"));
 	printf(_("      --auth-host=METHOD    default authentication method for local TCP/IP connections\n"));
 	printf(_("      --auth-local=METHOD   default authentication method for local-socket connections\n"));
+	printf(_("  -b, --block-size=SIZE     size of database blocks, in kilobytes\n"));
 	printf(_(" [-D, --pgdata=]DATADIR     location for this database cluster\n"));
 	printf(_("  -E, --encoding=ENCODING   set default encoding for new databases\n"));
 	printf(_("  -g, --allow-group-access  allow group read/execute on data directory\n"));
@@ -3100,6 +3105,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"waldir", required_argument, NULL, 'X'},
 		{"wal-segsize", required_argument, NULL, 12},
+		{"block-size", required_argument, NULL, 'b'},
 		{"data-checksums", no_argument, NULL, 'k'},
 		{"allow-group-access", no_argument, NULL, 'g'},
 		{"discard-caches", no_argument, NULL, 14},
@@ -3147,7 +3153,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "A:c:dD:E:gkL:nNsST:U:WX:",
+	while ((c = getopt_long(argc, argv, "A:b:c:dD:E:gkL:nNsST:U:WX:",
 							long_options, &option_index)) != -1)
 	{
 		switch (c)
@@ -3171,6 +3177,9 @@ main(int argc, char *argv[])
 			case 11:
 				authmethodhost = pg_strdup(optarg);
 				break;
+			case 'b':
+				str_block_size = pg_strdup(optarg);
+				break;
 			case 'c':
 				{
 					char	   *buf = pg_strdup(optarg);
@@ -3352,6 +3361,26 @@ main(int argc, char *argv[])
 	if (!IsValidWalSegSize(wal_segment_size_mb * 1024 * 1024))
 		pg_fatal("argument of %s must be a power of two between 1 and 1024", "--wal-segsize");
 
+	if (str_block_size == NULL)
+		block_size = DEFAULT_BLOCK_SIZE;
+	else
+	{
+		char	   *endptr;
+
+		/* check that the argument is a number */
+		block_size = strtol(str_block_size, &endptr, 10);
+
+		/* verify that the  segment size is valid */
+		if (endptr == str_block_size || *endptr != '\0')
+			pg_fatal("argument of --block-size must be a number");
+		block_size *= 1024;
+		/* check for valid block_size; last is bitwise power of two check */
+		if (!IsValidBlockSize(block_size))
+			pg_fatal("argument of --block-size must be a power of 2 between 1 and 32");
+	}
+
+	BlockSizeInit(block_size);
+
 	get_restricted_token();
 
 	setup_pgdata();
@@ -3385,6 +3414,8 @@ main(int argc, char *argv[])
 	else
 		printf(_("Data page checksums are disabled.\n"));
 
+	printf(_("Selected server block size: %d\n"), block_size);
+
 	if (pwprompt || pwfilename)
 		get_su_pwd();
 
diff --git a/src/bin/initdb/meson.build b/src/bin/initdb/meson.build
index 49743630aa..e2b8d88bff 100644
--- a/src/bin/initdb/meson.build
+++ b/src/bin/initdb/meson.build
@@ -31,6 +31,7 @@ tests += {
     'env': {'with_icu': icu.found() ? 'yes' : 'no'},
     'tests': [
       't/001_initdb.pl',
+      't/002_blocksize.pl',
     ],
   },
 }
diff --git a/src/bin/initdb/t/002_blocksize.pl b/src/bin/initdb/t/002_blocksize.pl
new file mode 100644
index 0000000000..d8d4208fed
--- /dev/null
+++ b/src/bin/initdb/t/002_blocksize.pl
@@ -0,0 +1,24 @@
+
+# Copyright (c) 2023, PostgreSQL Global Development Group
+
+
+use strict;
+use warnings;
+use Fcntl ':mode';
+use File::stat qw{lstat};
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+for my $blocksize (1,2,4,8,16,32) {
+	my $node1 = PostgreSQL::Test::Cluster->new('node' . $blocksize);
+	$node1->init(extra => ['--block-size='.($blocksize)] );
+	$node1->start;
+
+	is($node1->safe_psql('postgres',q{SELECT current_setting('block_size')}),
+	   1024 * $blocksize, "initdb with blocksize " . $blocksize . "k");
+
+	$node1->stop;
+}
+
+done_testing();
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 1dc8efe0cb..b983c2c354 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -28,6 +28,7 @@
 #include "access/xlog_internal.h"
 #include "backup/basebackup.h"
 #include "bbstreamer.h"
+#include "common/blocksize.h"
 #include "common/compression.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -2735,6 +2736,12 @@ main(int argc, char **argv)
 	if (!RetrieveWalSegSize(conn))
 		exit(1);
 
+	/* detect and set remote server's block size */
+	if (!RetrieveBlockSize(conn))
+		exit(1);
+
+	BlockSizeInit(BlockSize);
+
 	/* Create pg_wal symlink, if required */
 	if (xlog_dir)
 	{
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index d0a4079d50..e5f5c804ee 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -897,6 +897,10 @@ main(int argc, char **argv)
 	/* determine remote server's xlog segment size */
 	if (!RetrieveWalSegSize(conn))
 		exit(1);
+	if (!RetrieveBlockSize(conn))
+		exit(1);
+
+	BlockSizeInit(BlockSize);
 
 	/*
 	 * Don't close the connection here so that subsequent StreamLog() can
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 75ab9e56f3..9ff0fd8e34 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -32,6 +32,7 @@
 #define ERRCODE_DUPLICATE_OBJECT  "42710"
 
 int			WalSegSz;
+int			BlockSize;
 
 static bool RetrieveDataDirCreatePerm(PGconn *conn);
 
@@ -332,6 +333,64 @@ RetrieveWalSegSize(PGconn *conn)
 	return true;
 }
 
+/*
+ * Use SHOW block_size since ControlFile is not accessible here.
+ */
+bool
+RetrieveBlockSize(PGconn *conn)
+{
+	PGresult   *res;
+
+	/* check connection existence */
+	Assert(conn != NULL);
+
+	/* for previous versions set the default xlog seg size */
+	if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_SHOW_CMD)
+	{
+		BlockSize = DEFAULT_BLOCK_SIZE;
+		return true;
+	}
+
+	res = PQexec(conn, "SHOW block_size");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+	{
+		pg_log_error("could not send replication command \"%s\": %s",
+					 "SHOW block_size", PQerrorMessage(conn));
+
+		PQclear(res);
+		return false;
+	}
+	if (PQntuples(res) != 1 || PQnfields(res) < 1)
+	{
+		pg_log_error("could not fetch block size: got %d rows and %d fields, expected %d rows and %d or more fields",
+					 PQntuples(res), PQnfields(res), 1, 1);
+
+		PQclear(res);
+		return false;
+	}
+
+	/* fetch xlog value and unit from the result */
+	if (sscanf(PQgetvalue(res, 0, 0), "%d", &BlockSize) != 1)
+	{
+		pg_log_error("block size could not be parsed");
+		PQclear(res);
+		return false;
+	}
+
+	PQclear(res);
+
+	if (!IsValidBlockSize(BlockSize))
+	{
+		pg_log_error(ngettext("Block size must be a power of two between 1k and 32k, but the remote server reported a value of %d byte",
+							  "Block size must be a power of two between 1k and 32k, but the remote server reported a value of %d bytes",
+							  BlockSize),
+					 BlockSize);
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * RetrieveDataDirCreatePerm
  *
diff --git a/src/bin/pg_basebackup/streamutil.h b/src/bin/pg_basebackup/streamutil.h
index 268c163213..c97f9fa969 100644
--- a/src/bin/pg_basebackup/streamutil.h
+++ b/src/bin/pg_basebackup/streamutil.h
@@ -13,6 +13,7 @@
 #define STREAMUTIL_H
 
 #include "access/xlogdefs.h"
+#include "common/blocksize.h"
 #include "datatype/timestamp.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
@@ -25,6 +26,7 @@ extern char *dbport;
 extern char *dbname;
 extern int	dbgetpassword;
 extern int	WalSegSz;
+extern int	BlockSize;
 
 /* Connection kept global so we can disconnect easily */
 extern PGconn *conn;
@@ -56,6 +58,7 @@ extern bool GetSlotInformation(PGconn *conn, const char *slot_name,
 							   XLogRecPtr *restart_lsn,
 							   TimeLineID *restart_tli);
 extern bool RetrieveWalSegSize(PGconn *conn);
+extern bool RetrieveBlockSize(PGconn *conn);
 extern TimestampTz feGetCurrentTimestamp(void);
 extern void feTimestampDifference(TimestampTz start_time, TimestampTz stop_time,
 								  long *secs, int *microsecs);
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 0323bc4937..107ed1fd64 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/xlog_internal.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -231,14 +232,14 @@ scan_file(const char *fn, int segmentno)
 		if (PageIsNew(buf.data))
 			continue;
 
-		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
+		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE, cluster_block_size);
 		if (mode == PG_MODE_CHECK)
 		{
 			if (csum != header->pd_checksum)
 			{
 				if (ControlFile->data_checksum_version == PG_DATA_CHECKSUM_VERSION)
-					pg_log_error("checksum verification failed in file \"%s\", block %u: calculated checksum %X but block contains %X",
-								 fn, blockno, csum, header->pd_checksum);
+					pg_log_error("checksum verification failed in file \"%s\", block %u, block_size: %u: calculated checksum %X but block contains %X",
+								 fn, blockno, cluster_block_size, csum, header->pd_checksum);
 				badblocks++;
 			}
 		}
@@ -259,7 +260,7 @@ scan_file(const char *fn, int segmentno)
 			header->pd_checksum = csum;
 
 			/* Seek back to beginning of block */
-			if (lseek(f, -cluster_block_size, SEEK_CUR) < 0)
+			if (lseek(f, -(int)cluster_block_size, SEEK_CUR) < 0)
 				pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
 
 			/* Write block with checksum */
@@ -551,14 +552,6 @@ main(int argc, char *argv[])
 	if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
 		pg_fatal("cluster is not compatible with this version of pg_checksums");
 
-	if (ControlFile->blcksz != cluster_block_size)
-	{
-		pg_log_error("database cluster is not compatible");
-		pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
-							ControlFile->blcksz, cluster_block_size);
-		exit(1);
-	}
-
 	/*
 	 * Check if cluster is running.  A clean shutdown is required to avoid
 	 * random checksum failures caused by torn pages.  Note that this doesn't
@@ -580,6 +573,11 @@ main(int argc, char *argv[])
 		mode == PG_MODE_ENABLE)
 		pg_fatal("data checksums are already enabled in cluster");
 
+	if (IsValidBlockSize(ControlFile->blcksz))
+		BlockSizeInit(ControlFile->blcksz);
+	else
+		pg_fatal("invalid cluster block size in control file");
+
 	/* Operate on all files if checking or enabling checksums */
 	if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
 	{
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 84c65069aa..da9b21776f 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -49,6 +49,7 @@
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
@@ -76,6 +77,7 @@ static TimeLineID minXlogTli = 0;
 static XLogSegNo minXlogSegNo = 0;
 static int	WalSegSz;
 static int	set_wal_segsize;
+static int	set_blocksize = 0;
 
 static void CheckDataVersion(void);
 static bool read_controlfile(void);
@@ -94,6 +96,7 @@ int
 main(int argc, char *argv[])
 {
 	static struct option long_options[] = {
+		{"block-size", required_argument, NULL, 'b'},
 		{"commit-timestamp-ids", required_argument, NULL, 'c'},
 		{"pgdata", required_argument, NULL, 'D'},
 		{"epoch", required_argument, NULL, 'e'},
@@ -138,10 +141,19 @@ main(int argc, char *argv[])
 	}
 
 
-	while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
+	while ((c = getopt_long(argc, argv, "b:c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
 	{
 		switch (c)
 		{
+			case 'b':
+				errno = 0;
+				set_blocksize = strtol(optarg, &endptr, 10);
+				if (endptr == optarg || *endptr != '\0' || errno != 0)
+					pg_fatal("argument of --block-size must be a number");
+				set_blocksize *= 1024;
+				if (!IsValidBlockSize(set_blocksize))
+					pg_fatal("argument of --block-size must be a power of 2 between 1 and 32");
+				break;
 			case 'D':
 				DataDir = optarg;
 				break;
@@ -391,6 +403,33 @@ main(int argc, char *argv[])
 	else
 		WalSegSz = ControlFile.xlog_seg_size;
 
+	/*
+	 * If a blocksize was specified, compare to existing ControlFile; if we
+	 * are wrong, we won't be able to read the data.  We will only want to set
+	 * it if we guessed.
+	 */
+	if (set_blocksize == 0)
+	{
+		if (guessed)
+			pg_fatal("Cannot determine cluster block size; provide explicitly via --block-size");
+	}
+	else
+	{
+		if (!guessed && set_blocksize != ControlFile.blcksz)
+			pg_fatal("Cannot change blocksize in cluster");
+
+		/* hope this is right, but by default we don't know; likely this is
+		 * DEFAULT_BLOCK_SIZE */
+		ControlFile.blcksz = set_blocksize;
+	}
+
+	/*
+	 * Set some dependent calculated fields stored in pg_control
+	 */
+	BlockSizeInit(ControlFile.blcksz);
+	ControlFile.toast_max_chunk_size = cluster_toast_max_chunk_size;
+	ControlFile.loblksize = cluster_loblksize;
+
 	if (log_fname != NULL)
 		XLogFromFileName(log_fname, &minXlogTli, &minXlogSegNo, WalSegSz);
 
@@ -614,6 +653,16 @@ read_controlfile(void)
 			return false;
 		}
 
+		/* return false if block size is not valid */
+		if (!IsValidBlockSize(ControlFile.blcksz))
+		{
+			pg_log_warning(ngettext("pg_control specifies invalid block size (%d byte); proceed with caution",
+									"pg_control specifies invalid block size (%d bytes); proceed with caution",
+									ControlFile.blcksz),
+						   ControlFile.blcksz);
+			return false;
+		}
+
 		return true;
 	}
 
@@ -686,7 +735,7 @@ GuessControlValues(void)
 
 	ControlFile.maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile.floatFormat = FLOATFORMAT_VALUE;
-	ControlFile.blcksz = cluster_block_size;
+	ControlFile.blcksz = DEFAULT_BLOCK_SIZE;
 	ControlFile.relseg_size = RELSEG_SIZE;
 	ControlFile.xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
@@ -1131,6 +1180,7 @@ usage(void)
 	printf(_("%s resets the PostgreSQL write-ahead log.\n\n"), progname);
 	printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
 	printf(_("Options:\n"));
+	printf(_("      --block-size=SIZE            cluster block size, in bytes\n"));
 	printf(_("  -c, --commit-timestamp-ids=XID,XID\n"
 			 "                                   set oldest and newest transactions bearing\n"
 			 "                                   commit timestamp (zero means no change)\n"));
diff --git a/src/bin/pg_resetwal/t/002_corrupted.pl b/src/bin/pg_resetwal/t/002_corrupted.pl
index 6d19a1efd5..84ed4e92b0 100644
--- a/src/bin/pg_resetwal/t/002_corrupted.pl
+++ b/src/bin/pg_resetwal/t/002_corrupted.pl
@@ -31,7 +31,7 @@ print $fh pack("x[$size]");
 close $fh;
 
 command_checks_all(
-	[ 'pg_resetwal', '-n', $node->data_dir ],
+	[ 'pg_resetwal', '-b', '8', '-n', $node->data_dir ],
 	0,
 	[qr/pg_control version number/],
 	[
@@ -47,7 +47,7 @@ print $fh $data, pack("x[" . ($size - 16) . "]");
 close $fh;
 
 command_checks_all(
-	[ 'pg_resetwal', '-n', $node->data_dir ],
+	[ 'pg_resetwal', '-b', '8', '-n', $node->data_dir ],
 	0,
 	[qr/pg_control version number/],
 	[
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index a28de44170..1dea3efb5a 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "catalog/pg_tablespace_d.h"
+#include "common/blocksize.h"
 #include "common/hashfn.h"
 #include "common/string.h"
 #include "datapagemap.h"
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index d77dc890c9..dd1606bb55 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -18,6 +18,7 @@
 #include "access/xlog_internal.h"
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_perm.h"
 #include "common/restricted_token.h"
@@ -340,6 +341,13 @@ main(int argc, char **argv)
 
 	sanityChecks();
 
+	if (IsValidBlockSize(ControlFile_source.blcksz) &&
+		ControlFile_source.blcksz == ControlFile_target.blcksz)
+		BlockSizeInit(ControlFile_source.blcksz);
+	else
+		pg_fatal("cluster block sizes do not match or are invalid: %d",
+				 ControlFile_source.blcksz);
+
 	/*
 	 * Usually, the TLI can be found in the latest checkpoint record. But if
 	 * the source server is just being promoted (or it's a standby that's
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 3143cf28aa..db16a420ba 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -19,14 +19,17 @@
 #include <linux/fs.h>
 #endif
 
+
 #include "access/visibilitymapdefs.h"
+#include "common/blocksize.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
+#undef cluster_block_size
+#define cluster_block_size BlockSize
 #include "storage/bufpage.h"
 #include "storage/checksum.h"
 #include "storage/checksum_impl.h"
 
-
 /*
  * cloneFile()
  *
@@ -96,7 +99,7 @@ copyFile(const char *src, const char *dst,
 				 schemaName, relName, dst, strerror(errno));
 
 	/* copy in fairly large chunks for best efficiency */
-#define COPY_BUF_SIZE (50 * cluster_block_size)
+#define COPY_BUF_SIZE (50 * BlockSize)
 
 	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
 
@@ -187,7 +190,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 	struct stat statbuf;
 
 	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (cluster_block_size - SizeOfPageHeaderData) / 2;
+	rewriteVmBytesPerPage = (BlockSize - SizeOfPageHeaderData) / 2;
 
 	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
 		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
@@ -220,7 +223,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 		PageHeaderData pageheader;
 		bool		old_lastblk;
 
-		if ((bytesRead = read(src_fd, buffer.data, cluster_block_size)) != cluster_block_size)
+		if ((bytesRead = read(src_fd, buffer.data, BlockSize)) != BlockSize)
 		{
 			if (bytesRead < 0)
 				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
@@ -230,7 +233,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 						 schemaName, relName, fromfile);
 		}
 
-		totalBytesRead += cluster_block_size;
+		totalBytesRead += BlockSize;
 		old_lastblk = (totalBytesRead == src_filesize);
 
 		/* Save the page header data */
@@ -293,10 +296,10 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 			/* Set new checksum for visibility map page, if enabled */
 			if (new_cluster.controldata.data_checksum_version != 0)
 				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
+					pg_checksum_page(new_vmbuf.data, new_blkno, BlockSize);
 
 			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, cluster_block_size) != cluster_block_size)
+			if (write(dst_fd, new_vmbuf.data, BlockSize) != BlockSize)
 			{
 				/* if write didn't set errno, assume problem is no disk space */
 				if (errno == 0)
diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index 12a97f84e2..06b26e54a5 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -1,5 +1,7 @@
 # Copyright (c) 2022-2023, PostgreSQL Global Development Group
 
+fs = import('fs')
+
 pg_upgrade_sources = files(
   'check.c',
   'controldata.c',
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 4562dafcff..6a64d8689a 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -63,6 +63,7 @@ static void setup(char *argv0, bool *live_check);
 ClusterInfo old_cluster,
 			new_cluster;
 OSInfo		os_info;
+uint32		BlockSize;
 
 char	   *output_files[] = {
 	SERVER_LOG_FILE,
@@ -126,6 +127,8 @@ main(int argc, char **argv)
 
 	check_cluster_compatibility(live_check);
 
+	BlockSize = old_cluster.controldata.blocksz;
+
 	check_and_dump_old_cluster(live_check);
 
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 7afa96716e..fa36020ea3 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -336,7 +336,7 @@ extern UserOpts user_opts;
 extern ClusterInfo old_cluster,
 			new_cluster;
 extern OSInfo os_info;
-
+extern uint32 BlockSize;
 
 /* check.c */
 
diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index 059836f0e6..dc187a9f4d 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -841,8 +841,8 @@ parse_required_wal(verifier_context *context, char *pg_waldump_path,
 	{
 		char	   *pg_waldump_cmd;
 
-		pg_waldump_cmd = psprintf("\"%s\" --quiet --path=\"%s\" --timeline=%u --start=%X/%X --end=%X/%X\n",
-								  pg_waldump_path, wal_directory, this_wal_range->tli,
+		pg_waldump_cmd = psprintf("\"%s\" --quiet --pgdata=\"%s\" --path=\"%s\" --timeline=%u --start=%X/%X --end=%X/%X\n",
+								  pg_waldump_path, context->backup_directory, wal_directory, this_wal_range->tli,
 								  LSN_FORMAT_ARGS(this_wal_range->start_lsn),
 								  LSN_FORMAT_ARGS(this_wal_range->end_lsn));
 		fflush(NULL);
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index b3ad58ec66..9365a4c0f1 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -23,6 +23,8 @@
 #include "access/xlogreader.h"
 #include "access/xlogrecord.h"
 #include "access/xlogstats.h"
+#include "common/blocksize.h"
+#include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -38,8 +40,10 @@
  */
 
 static const char *progname;
+static char *pgdata;
 
 static int	WalSegSz;
+static int	BlockSize;
 static volatile sig_atomic_t time_to_stop = false;
 
 static const RelFileLocator emptyRelFileLocator = {0, 0, 0};
@@ -531,7 +535,7 @@ XLogRecordSaveFPWs(XLogReaderState *record, const char *savepath)
 		if (!file)
 			pg_fatal("could not open file \"%s\": %m", filename);
 
-		if (fwrite(page, cluster_block_size, 1, file) != 1)
+		if (fwrite(page, BlockSize, 1, file) != 1)
 			pg_fatal("could not write file \"%s\": %m", filename);
 
 		if (fclose(file) != 0)
@@ -801,6 +805,8 @@ main(int argc, char **argv)
 	XLogRecPtr	first_record;
 	char	   *waldir = NULL;
 	char	   *errormsg;
+	ControlFileData *ControlFile;
+	bool	crc_ok_p;
 
 	static struct option long_options[] = {
 		{"bkp-details", no_argument, NULL, 'b'},
@@ -812,6 +818,7 @@ main(int argc, char **argv)
 		{"help", no_argument, NULL, '?'},
 		{"limit", required_argument, NULL, 'n'},
 		{"path", required_argument, NULL, 'p'},
+		{"pgdata", required_argument, NULL, 'D'},
 		{"quiet", no_argument, NULL, 'q'},
 		{"relation", required_argument, NULL, 'R'},
 		{"rmgr", required_argument, NULL, 'r'},
@@ -885,7 +892,7 @@ main(int argc, char **argv)
 		goto bad_argument;
 	}
 
-	while ((option = getopt_long(argc, argv, "bB:e:fF:n:p:qr:R:s:t:wx:z",
+	while ((option = getopt_long(argc, argv, "bB:D:e:fF:n:p:qr:R:s:t:wx:z",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -903,6 +910,9 @@ main(int argc, char **argv)
 				config.filter_by_relation_block_enabled = true;
 				config.filter_by_extended = true;
 				break;
+			case 'D':
+				pgdata = pg_strdup(optarg);
+				break;
 			case 'e':
 				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
 				{
@@ -1106,6 +1116,23 @@ main(int argc, char **argv)
 		}
 	}
 
+	if (!pgdata)
+		pgdata = getenv("PGDATA");
+	if (!pgdata || strlen(pgdata) == 0)
+		pg_fatal("Must provide data directory via -D or PGDATA envvar");
+
+	ControlFile = get_controlfile(pgdata, &crc_ok_p);
+
+	if (!ControlFile)
+		pg_fatal("Could not locate control file");
+
+	BlockSize = ControlFile->blcksz;
+
+	if (!IsValidBlockSize(BlockSize))
+		pg_fatal("read invalid block size from control file");
+
+	BlockSizeInit(BlockSize);
+
 	if (config.save_fullpage_path != NULL)
 		create_fullpage_directory(config.save_fullpage_path);
 
diff --git a/src/bin/pg_waldump/t/001_basic.pl b/src/bin/pg_waldump/t/001_basic.pl
index 029a0d0521..4b33fe45a6 100644
--- a/src/bin/pg_waldump/t/001_basic.pl
+++ b/src/bin/pg_waldump/t/001_basic.pl
@@ -66,6 +66,8 @@ wal_level=logical
 });
 $node->start;
 
+$ENV{PGDATA} = $node->data_dir;
+
 my ($start_lsn, $start_walfile) = split /\|/, $node->safe_psql('postgres', q{SELECT pg_current_wal_insert_lsn(), pg_walfile_name(pg_current_wal_insert_lsn())});
 
 $node->safe_psql('postgres', q{
diff --git a/src/bin/pg_waldump/t/002_save_fullpage.pl b/src/bin/pg_waldump/t/002_save_fullpage.pl
index f0725805f2..d136324968 100644
--- a/src/bin/pg_waldump/t/002_save_fullpage.pl
+++ b/src/bin/pg_waldump/t/002_save_fullpage.pl
@@ -38,6 +38,8 @@ max_wal_senders = 4
 });
 $node->start;
 
+$ENV{PGDATA} = $node->data_dir;
+
 # Generate data/WAL to examine that will have full pages in them.
 $node->safe_psql(
 	'postgres',
@@ -73,6 +75,7 @@ $node->command_ok(
 	[
 		'pg_waldump', '--quiet',
 		'--save-fullpage', "$tmp_folder/raw",
+		'-D', $node->data_dir,
 		'--relation', $relation,
 		$walfile
 	],
diff --git a/src/common/Makefile b/src/common/Makefile
index 113029bf7b..da96f2dc8d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -48,6 +48,7 @@ LIBS += $(PTHREAD_LIBS)
 OBJS_COMMON = \
 	archive.o \
 	base64.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..640b1516d2
--- /dev/null
+++ b/src/common/blocksize.c
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.c
+ *	  This file contains methods to calculate various size constants for variable-sized blocks.
+ *
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/common/clustersizes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "access/heaptoast.h"
+#include "access/htup_details.h"
+#include "access/itup.h"
+#include "access/nbtree_int.h"
+#include "common/blocksize.h"
+#ifndef FRONTEND
+#include "storage/freespace.h"
+#endif
+
+PGDLLIMPORT BlockSizeIdent cluster_block_setting = BLOCK_SIZE_UNSET;
+
+/*
+ * 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).
+ */
+
+void
+BlockSizeInit(Size rawblocksize)
+{
+	uint32 bits = 0;
+	Size blocksize = rawblocksize;
+
+	Assert(IsValidBlockSize(rawblocksize));
+
+	// calculate max number of bits in the passed-in size
+	while (blocksize >>= 1)
+		bits++;
+
+	// our smallest block size, 1k, is 2^10, and we want this to be 1 if initialized
+	cluster_block_setting = (BlockSizeIdent)(bits - 10) + 1;
+
+	#ifndef FRONTEND
+	/* also setup the FreeSpaceMap internal sizing */
+	FreeSpaceMapInit();
+	#endif
+}
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index ea74aa1eb5..d218510b23 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -540,7 +540,7 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
 ssize_t
 pg_pwrite_zeros(int fd, size_t size, off_t offset)
 {
-	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth cluster_block_size */
+	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth MAX_BLOCK_SIZE */
 	void	   *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
 	struct iovec iov[PG_IOV_MAX];
 	size_t		remaining_size = size;
@@ -558,10 +558,10 @@ pg_pwrite_zeros(int fd, size_t size, off_t offset)
 
 			iov[iovcnt].iov_base = zerobuf_addr;
 
-			if (remaining_size < cluster_block_size)
+			if (remaining_size < sizeof(zbuffer.data))
 				this_iov_size = remaining_size;
 			else
-				this_iov_size = cluster_block_size;
+				this_iov_size = sizeof(zbuffer.data);
 
 			iov[iovcnt].iov_len = this_iov_size;
 			remaining_size -= this_iov_size;
diff --git a/src/common/meson.build b/src/common/meson.build
index 53942a9a61..6d39671157 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -3,6 +3,7 @@
 common_sources = files(
   'archive.c',
   'base64.c',
+  'blocksize.c',
   'checksum_helper.c',
   'compression.c',
   'controldata_utils.c',
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 4c1dd74db7..939b8ef3f3 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -171,7 +171,7 @@ typedef struct GISTScanOpaqueData
 	GistNSN		curPageLSN;		/* pos in the WAL stream when page was read */
 
 	/* In a non-ordered search, returnable heap items are stored here: */
-	GISTSearchHeapItem pageData[cluster_block_size / sizeof(IndexTupleData)];
+	GISTSearchHeapItem pageData[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
 	OffsetNumber nPageData;		/* number of valid items in array */
 	OffsetNumber curPageData;	/* next item to return */
 	MemoryContext pageDataCxt;	/* context holding the fetched tuples, for
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 4f6df08020..7bab4c0105 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -124,7 +124,7 @@ typedef struct HashScanPosData
 	int			lastItem;		/* last valid index in items[] */
 	int			itemIndex;		/* current index in items[] */
 
-	HashScanPosItem items[MaxIndexTuplesPerPage];	/* MUST BE LAST */
+	HashScanPosItem items[MaxIndexTuplesPerPageLimit];	/* MUST BE LAST */
 } HashScanPosData;
 
 #define HashScanPosIsPinned(scanpos) \
@@ -227,7 +227,7 @@ typedef HashScanOpaqueData *HashScanOpaque;
  * the block size boundary.  So we use cluster_block_size to determine the maximum number
  * of bitmaps.
  */
-#define HASH_MAX_BITMAPS			Min(cluster_block_size / 8, 1024)
+#define HASH_MAX_BITMAPS			Min(DEFAULT_BLOCK_SIZE / 8, 1024)
 
 #define HASH_SPLITPOINT_PHASE_BITS	2
 #define HASH_SPLITPOINT_PHASES_PER_GRP	(1 << HASH_SPLITPOINT_PHASE_BITS)
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index faf5026519..644c03145b 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -75,7 +75,7 @@ typedef struct HeapScanDescData
 	/* these fields only used in page-at-a-time mode and for bitmap scans */
 	int			rs_cindex;		/* current tuple's index in vistuples */
 	int			rs_ntuples;		/* number of visible tuples on page */
-	OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
+	OffsetNumber rs_vistuples[MaxHeapTuplesPerPageLimit];	/* their offsets */
 }			HeapScanDescData;
 typedef struct HeapScanDescData *HeapScanDesc;
 
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index 3658856a60..1aaf08a166 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -20,10 +20,8 @@
 /*
  * Find the maximum size of a tuple if there are to be N tuples per page.
  */
-#define MaximumBytesPerTuple(tuplesPerPage) \
-	MAXALIGN_DOWN((cluster_block_size - \
-				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
-				  / (tuplesPerPage))
+#define MaximumBytesPerTuple(tuplesPerPage) CalcMaximumBytesPerTuple(cluster_block_size,tuplesPerPage)
+BlockSizeDecl2(CalcMaximumBytesPerTuple);
 
 /*
  * These symbols control toaster activation.  If a tuple is larger than
@@ -45,7 +43,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE	4
 
-#define toast_tuple_threshold	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
+#define toast_tuple_threshold	CalcMaximumBytesPerTuple(cluster_block_size,TOAST_TUPLES_PER_PAGE)
 
 #define cluster_toast_tuple_target		toast_tuple_threshold
 
@@ -58,7 +56,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE_MAIN	1
 
-#define toast_tuple_target_main MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
+#define toast_tuple_target_main CalcMaximumBytesPerTuple(cluster_block_size,TOAST_TUPLES_PER_PAGE_MAIN)
 
 /*
  * If an index value is larger than TOAST_INDEX_TARGET, we will try to
@@ -81,12 +79,10 @@
 
 #define EXTERN_TUPLE_MAX_SIZE	MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE)
 
-#define cluster_toast_max_chunk_size	\
-	(EXTERN_TUPLE_MAX_SIZE -							\
-	 MAXALIGN(SizeofHeapTupleHeader) -					\
-	 sizeof(Oid) -										\
-	 sizeof(int32) -									\
-	 VARHDRSZ)
+#define TOAST_MAX_CHUNK_SIZE_LIMIT CalcToastMaxChunkSize(MAX_BLOCK_SIZE)
+#define cluster_toast_max_chunk_size BlockSizeCalc(cluster_block_setting,CalcToastMaxChunkSize)
+BlockSizeDecl(CalcToastMaxChunkSize);
+
 
 /* ----------
  * heap_toast_insert_or_update -
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 005a530e0a..7acb40386b 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -18,6 +18,7 @@
 #include "access/transam.h"
 #include "access/tupdesc.h"
 #include "access/tupmacs.h"
+#include "common/blocksize.h"
 #include "storage/bufpage.h"
 #include "varatt.h"
 
@@ -427,8 +428,8 @@ do { \
 	(tup)->t_choice.t_heap.t_field3.t_xvac = (xid); \
 } while (0)
 
-StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
-				 "invalid speculative token constant");
+/* StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber, */
+/* 				 "invalid speculative token constant"); */
 
 #define HeapTupleHeaderIsSpeculative(tup) \
 ( \
@@ -560,8 +561,10 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * ItemIds and tuples have different alignment requirements, don't assume that
  * you can, say, fit 2 tuples of size MaxHeapTupleSize/2 on the same page.
  */
-#define MaxHeapTupleSize  (cluster_block_size - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+#define MaxHeapTupleSize BlockSizeCalc(cluster_block_setting,CalcMaxHeapTupleSize)
+#define MaxHeapTupleSizeLimit CalcMaxHeapTupleSize(MAX_BLOCK_SIZE)
 #define MinHeapTupleSize  MAXALIGN(SizeofHeapTupleHeader)
+BlockSizeDecl(CalcMaxHeapTupleSize);
 
 /*
  * MaxHeapTuplesPerPage is an upper bound on the number of tuples that can
@@ -574,9 +577,10 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * pointers to this anyway, to avoid excessive line-pointer bloat and not
  * require increases in the size of work arrays.
  */
-#define MaxHeapTuplesPerPage	\
-	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
-			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
+
+#define MaxHeapTuplesPerPage BlockSizeCalc(cluster_block_setting,CalcMaxHeapTuplesPerPage)
+#define MaxHeapTuplesPerPageLimit CalcMaxHeapTuplesPerPage(MAX_BLOCK_SIZE)
+BlockSizeDecl(CalcMaxHeapTuplesPerPage);
 
 /*
  * MaxAttrSize is a somewhat arbitrary upper limit on the declared size of
diff --git a/src/include/access/itup.h b/src/include/access/itup.h
index 8a68b9405b..90fce7ea15 100644
--- a/src/include/access/itup.h
+++ b/src/include/access/itup.h
@@ -163,8 +163,8 @@ index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
  * estimated here, seemingly allowing one more tuple than estimated here.
  * But such a page always has at least MAXALIGN special space, so we're safe.
  */
-#define MaxIndexTuplesPerPage	\
-	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
-			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
+#define MaxIndexTuplesPerPage BlockSizeCalc(cluster_block_setting,CalcMaxIndexTuplesPerPage)
+#define MaxIndexTuplesPerPageLimit CalcMaxIndexTuplesPerPage(MAX_BLOCK_SIZE)
+BlockSizeDecl(CalcMaxIndexTuplesPerPage);
 
 #endif							/* ITUP_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 033624e9c3..1071b0569f 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -15,6 +15,7 @@
 #define NBTREE_H
 
 #include "access/amapi.h"
+#include "access/nbtree_int.h"
 #include "access/itup.h"
 #include "access/sdir.h"
 #include "access/tableam.h"
@@ -25,208 +26,6 @@
 #include "storage/bufmgr.h"
 #include "storage/shm_toc.h"
 
-/* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
-typedef uint16 BTCycleId;
-
-/*
- *	BTPageOpaqueData -- At the end of every page, we store a pointer
- *	to both siblings in the tree.  This is used to do forward/backward
- *	index scans.  The next-page link is also critical for recovery when
- *	a search has navigated to the wrong page due to concurrent page splits
- *	or deletions; see src/backend/access/nbtree/README for more info.
- *
- *	In addition, we store the page's btree level (counting upwards from
- *	zero at a leaf page) as well as some flag bits indicating the page type
- *	and status.  If the page is deleted, a BTDeletedPageData struct is stored
- *	in the page's tuple area, while a standard BTPageOpaqueData struct is
- *	stored in the page special area.
- *
- *	We also store a "vacuum cycle ID".  When a page is split while VACUUM is
- *	processing the index, a nonzero value associated with the VACUUM run is
- *	stored into both halves of the split page.  (If VACUUM is not running,
- *	both pages receive zero cycleids.)	This allows VACUUM to detect whether
- *	a page was split since it started, with a small probability of false match
- *	if the page was last split some exact multiple of MAX_BT_CYCLE_ID VACUUMs
- *	ago.  Also, during a split, the BTP_SPLIT_END flag is cleared in the left
- *	(original) page, and set in the right page, but only if the next page
- *	to its right has a different cycleid.
- *
- *	NOTE: the BTP_LEAF flag bit is redundant since level==0 could be tested
- *	instead.
- *
- *	NOTE: the btpo_level field used to be a union type in order to allow
- *	deleted pages to store a 32-bit safexid in the same field.  We now store
- *	64-bit/full safexid values using BTDeletedPageData instead.
- */
-
-typedef struct BTPageOpaqueData
-{
-	BlockNumber btpo_prev;		/* left sibling, or P_NONE if leftmost */
-	BlockNumber btpo_next;		/* right sibling, or P_NONE if rightmost */
-	uint32		btpo_level;		/* tree level --- zero for leaf pages */
-	uint16		btpo_flags;		/* flag bits, see below */
-	BTCycleId	btpo_cycleid;	/* vacuum cycle ID of latest split */
-} BTPageOpaqueData;
-
-typedef BTPageOpaqueData *BTPageOpaque;
-
-#define BTPageGetOpaque(page) ((BTPageOpaque) PageGetSpecialPointer(page))
-
-/* Bits defined in btpo_flags */
-#define BTP_LEAF		(1 << 0)	/* leaf page, i.e. not internal page */
-#define BTP_ROOT		(1 << 1)	/* root page (has no parent) */
-#define BTP_DELETED		(1 << 2)	/* page has been deleted from tree */
-#define BTP_META		(1 << 3)	/* meta-page */
-#define BTP_HALF_DEAD	(1 << 4)	/* empty, but still in tree */
-#define BTP_SPLIT_END	(1 << 5)	/* rightmost page of split group */
-#define BTP_HAS_GARBAGE (1 << 6)	/* page has LP_DEAD tuples (deprecated) */
-#define BTP_INCOMPLETE_SPLIT (1 << 7)	/* right sibling's downlink is missing */
-#define BTP_HAS_FULLXID	(1 << 8)	/* contains BTDeletedPageData */
-
-/*
- * The max allowed value of a cycle ID is a bit less than 64K.  This is
- * for convenience of pg_filedump and similar utilities: we want to use
- * the last 2 bytes of special space as an index type indicator, and
- * restricting cycle ID lets btree use that space for vacuum cycle IDs
- * while still allowing index type to be identified.
- */
-#define MAX_BT_CYCLE_ID		0xFF7F
-
-
-/*
- * The Meta page is always the first page in the btree index.
- * Its primary purpose is to point to the location of the btree root page.
- * We also point to the "fast" root, which is the current effective root;
- * see README for discussion.
- */
-
-typedef struct BTMetaPageData
-{
-	uint32		btm_magic;		/* should contain BTREE_MAGIC */
-	uint32		btm_version;	/* nbtree version (always <= BTREE_VERSION) */
-	BlockNumber btm_root;		/* current root location */
-	uint32		btm_level;		/* tree level of the root page */
-	BlockNumber btm_fastroot;	/* current "fast" root location */
-	uint32		btm_fastlevel;	/* tree level of the "fast" root page */
-	/* remaining fields only valid when btm_version >= BTREE_NOVAC_VERSION */
-
-	/* number of deleted, non-recyclable pages during last cleanup */
-	uint32		btm_last_cleanup_num_delpages;
-	/* number of heap tuples during last cleanup (deprecated) */
-	float8		btm_last_cleanup_num_heap_tuples;
-
-	bool		btm_allequalimage;	/* are all columns "equalimage"? */
-} BTMetaPageData;
-
-#define BTPageGetMeta(p) \
-	((BTMetaPageData *) PageGetContents(p))
-
-/*
- * The current Btree version is 4.  That's what you'll get when you create
- * a new index.
- *
- * Btree version 3 was used in PostgreSQL v11.  It is mostly the same as
- * version 4, but heap TIDs were not part of the keyspace.  Index tuples
- * with duplicate keys could be stored in any order.  We continue to
- * support reading and writing Btree versions 2 and 3, so that they don't
- * need to be immediately re-indexed at pg_upgrade.  In order to get the
- * new heapkeyspace semantics, however, a REINDEX is needed.
- *
- * Deduplication is safe to use when the btm_allequalimage field is set to
- * true.  It's safe to read the btm_allequalimage field on version 3, but
- * only version 4 indexes make use of deduplication.  Even version 4
- * indexes created on PostgreSQL v12 will need a REINDEX to make use of
- * deduplication, though, since there is no other way to set
- * btm_allequalimage to true (pg_upgrade hasn't been taught to set the
- * metapage field).
- *
- * Btree version 2 is mostly the same as version 3.  There are two new
- * fields in the metapage that were introduced in version 3.  A version 2
- * metapage will be automatically upgraded to version 3 on the first
- * insert to it.  INCLUDE indexes cannot use version 2.
- */
-#define BTREE_METAPAGE	0		/* first page is meta */
-#define BTREE_MAGIC		0x053162	/* magic number in metapage */
-#define BTREE_VERSION	4		/* current version number */
-#define BTREE_MIN_VERSION	2	/* minimum supported version */
-#define BTREE_NOVAC_VERSION	3	/* version with all meta fields set */
-
-/*
- * Maximum size of a btree index entry, including its tuple header.
- *
- * We actually need to be able to fit three items on every page,
- * so restrict any one item to 1/3 the per-page available space.
- *
- * There are rare cases where _bt_truncate() will need to enlarge
- * a heap index tuple to make space for a tiebreaker heap TID
- * attribute, which we account for here.
- */
-#define BTMaxItemSize(page) \
-	(MAXALIGN_DOWN((PageGetPageSize(page) - \
-					MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
-					MAXALIGN(sizeof(BTPageOpaqueData))) / 3) - \
-					MAXALIGN(sizeof(ItemPointerData)))
-#define BTMaxItemSizeNoHeapTid(page) \
-	MAXALIGN_DOWN((PageGetPageSize(page) - \
-				   MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
-				   MAXALIGN(sizeof(BTPageOpaqueData))) / 3)
-
-/*
- * MaxTIDsPerBTreePage is an upper bound on the number of heap TIDs tuples
- * that may be stored on a btree leaf page.  It is used to size the
- * per-page temporary buffers.
- *
- * Note: we don't bother considering per-tuple overheads here to keep
- * things simple (value is based on how many elements a single array of
- * heap TIDs must have to fill the space between the page header and
- * special area).  The value is slightly higher (i.e. more conservative)
- * than necessary as a result, which is considered acceptable.
- */
-#define MaxTIDsPerBTreePage \
-	(int) ((cluster_block_size - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
-		   sizeof(ItemPointerData))
-
-/*
- * The leaf-page fillfactor defaults to 90% but is user-adjustable.
- * For pages above the leaf level, we use a fixed 70% fillfactor.
- * The fillfactor is applied during index build and when splitting
- * a rightmost page; when splitting non-rightmost pages we try to
- * divide the data equally.  When splitting a page that's entirely
- * filled with a single value (duplicates), the effective leaf-page
- * fillfactor is 96%, regardless of whether the page is a rightmost
- * page.
- */
-#define BTREE_MIN_FILLFACTOR		10
-#define BTREE_DEFAULT_FILLFACTOR	90
-#define BTREE_NONLEAF_FILLFACTOR	70
-#define BTREE_SINGLEVAL_FILLFACTOR	96
-
-/*
- *	In general, the btree code tries to localize its knowledge about
- *	page layout to a couple of routines.  However, we need a special
- *	value to indicate "no page number" in those places where we expect
- *	page numbers.  We can use zero for this because we never need to
- *	make a pointer to the metadata page.
- */
-
-#define P_NONE			0
-
-/*
- * Macros to test whether a page is leftmost or rightmost on its tree level,
- * as well as other state info kept in the opaque data.
- */
-#define P_LEFTMOST(opaque)		((opaque)->btpo_prev == P_NONE)
-#define P_RIGHTMOST(opaque)		((opaque)->btpo_next == P_NONE)
-#define P_ISLEAF(opaque)		(((opaque)->btpo_flags & BTP_LEAF) != 0)
-#define P_ISROOT(opaque)		(((opaque)->btpo_flags & BTP_ROOT) != 0)
-#define P_ISDELETED(opaque)		(((opaque)->btpo_flags & BTP_DELETED) != 0)
-#define P_ISMETA(opaque)		(((opaque)->btpo_flags & BTP_META) != 0)
-#define P_ISHALFDEAD(opaque)	(((opaque)->btpo_flags & BTP_HALF_DEAD) != 0)
-#define P_IGNORE(opaque)		(((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD)) != 0)
-#define P_HAS_GARBAGE(opaque)	(((opaque)->btpo_flags & BTP_HAS_GARBAGE) != 0)
-#define P_INCOMPLETE_SPLIT(opaque)	(((opaque)->btpo_flags & BTP_INCOMPLETE_SPLIT) != 0)
-#define P_HAS_FULLXID(opaque)	(((opaque)->btpo_flags & BTP_HAS_FULLXID) != 0)
-
 /*
  * BTDeletedPageData is the page contents of a deleted page
  */
@@ -892,7 +691,7 @@ typedef struct BTDedupStateData
 	 * are implicitly unchanged by deduplication pass).
 	 */
 	int			nintervals;		/* current number of intervals in array */
-	BTDedupInterval intervals[MaxIndexTuplesPerPage];
+	BTDedupInterval intervals[MaxIndexTuplesPerPageLimit];
 } BTDedupStateData;
 
 typedef BTDedupStateData *BTDedupState;
@@ -987,7 +786,7 @@ typedef struct BTScanPosData
 	int			lastItem;		/* last valid index in items[] */
 	int			itemIndex;		/* current index in items[] */
 
-	BTScanPosItem items[MaxTIDsPerBTreePage];	/* MUST BE LAST */
+	BTScanPosItem items[MaxTIDsPerBTreePageLimit];	/* MUST BE LAST */
 } BTScanPosData;
 
 typedef BTScanPosData *BTScanPos;
diff --git a/src/include/access/nbtree_int.h b/src/include/access/nbtree_int.h
new file mode 100644
index 0000000000..923cc5cb29
--- /dev/null
+++ b/src/include/access/nbtree_int.h
@@ -0,0 +1,223 @@
+/*-------------------------------------------------------------------------
+ *
+ * nbtree_int.h
+ *	  internal defs for header file for postgres btree access method implementation.
+ *
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/nbtree_int.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NBTREE_INT_H
+#define NBTREE_INT_H
+
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/itemptr.h"
+
+/* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
+typedef uint16 BTCycleId;
+
+/*
+ *	BTPageOpaqueData -- At the end of every page, we store a pointer
+ *	to both siblings in the tree.  This is used to do forward/backward
+ *	index scans.  The next-page link is also critical for recovery when
+ *	a search has navigated to the wrong page due to concurrent page splits
+ *	or deletions; see src/backend/access/nbtree/README for more info.
+ *
+ *	In addition, we store the page's btree level (counting upwards from
+ *	zero at a leaf page) as well as some flag bits indicating the page type
+ *	and status.  If the page is deleted, a BTDeletedPageData struct is stored
+ *	in the page's tuple area, while a standard BTPageOpaqueData struct is
+ *	stored in the page special area.
+ *
+ *	We also store a "vacuum cycle ID".  When a page is split while VACUUM is
+ *	processing the index, a nonzero value associated with the VACUUM run is
+ *	stored into both halves of the split page.  (If VACUUM is not running,
+ *	both pages receive zero cycleids.)	This allows VACUUM to detect whether
+ *	a page was split since it started, with a small probability of false match
+ *	if the page was last split some exact multiple of MAX_BT_CYCLE_ID VACUUMs
+ *	ago.  Also, during a split, the BTP_SPLIT_END flag is cleared in the left
+ *	(original) page, and set in the right page, but only if the next page
+ *	to its right has a different cycleid.
+ *
+ *	NOTE: the BTP_LEAF flag bit is redundant since level==0 could be tested
+ *	instead.
+ *
+ *	NOTE: the btpo_level field used to be a union type in order to allow
+ *	deleted pages to store a 32-bit safexid in the same field.  We now store
+ *	64-bit/full safexid values using BTDeletedPageData instead.
+ */
+
+typedef struct BTPageOpaqueData
+{
+	BlockNumber btpo_prev;		/* left sibling, or P_NONE if leftmost */
+	BlockNumber btpo_next;		/* right sibling, or P_NONE if rightmost */
+	uint32		btpo_level;		/* tree level --- zero for leaf pages */
+	uint16		btpo_flags;		/* flag bits, see below */
+	BTCycleId	btpo_cycleid;	/* vacuum cycle ID of latest split */
+} BTPageOpaqueData;
+
+typedef BTPageOpaqueData *BTPageOpaque;
+
+#define BTPageGetOpaque(page) ((BTPageOpaque) PageGetSpecialPointer(page))
+
+/* Bits defined in btpo_flags */
+#define BTP_LEAF		(1 << 0)	/* leaf page, i.e. not internal page */
+#define BTP_ROOT		(1 << 1)	/* root page (has no parent) */
+#define BTP_DELETED		(1 << 2)	/* page has been deleted from tree */
+#define BTP_META		(1 << 3)	/* meta-page */
+#define BTP_HALF_DEAD	(1 << 4)	/* empty, but still in tree */
+#define BTP_SPLIT_END	(1 << 5)	/* rightmost page of split group */
+#define BTP_HAS_GARBAGE (1 << 6)	/* page has LP_DEAD tuples (deprecated) */
+#define BTP_INCOMPLETE_SPLIT (1 << 7)	/* right sibling's downlink is missing */
+#define BTP_HAS_FULLXID	(1 << 8)	/* contains BTDeletedPageData */
+
+/*
+ * The max allowed value of a cycle ID is a bit less than 64K.  This is
+ * for convenience of pg_filedump and similar utilities: we want to use
+ * the last 2 bytes of special space as an index type indicator, and
+ * restricting cycle ID lets btree use that space for vacuum cycle IDs
+ * while still allowing index type to be identified.
+ */
+#define MAX_BT_CYCLE_ID		0xFF7F
+
+
+/*
+ * The Meta page is always the first page in the btree index.
+ * Its primary purpose is to point to the location of the btree root page.
+ * We also point to the "fast" root, which is the current effective root;
+ * see README for discussion.
+ */
+
+typedef struct BTMetaPageData
+{
+	uint32		btm_magic;		/* should contain BTREE_MAGIC */
+	uint32		btm_version;	/* nbtree version (always <= BTREE_VERSION) */
+	BlockNumber btm_root;		/* current root location */
+	uint32		btm_level;		/* tree level of the root page */
+	BlockNumber btm_fastroot;	/* current "fast" root location */
+	uint32		btm_fastlevel;	/* tree level of the "fast" root page */
+	/* remaining fields only valid when btm_version >= BTREE_NOVAC_VERSION */
+
+	/* number of deleted, non-recyclable pages during last cleanup */
+	uint32		btm_last_cleanup_num_delpages;
+	/* number of heap tuples during last cleanup (deprecated) */
+	float8		btm_last_cleanup_num_heap_tuples;
+
+	bool		btm_allequalimage;	/* are all columns "equalimage"? */
+} BTMetaPageData;
+
+#define BTPageGetMeta(p) \
+	((BTMetaPageData *) PageGetContents(p))
+
+/*
+ * The current Btree version is 4.  That's what you'll get when you create
+ * a new index.
+ *
+ * Btree version 3 was used in PostgreSQL v11.  It is mostly the same as
+ * version 4, but heap TIDs were not part of the keyspace.  Index tuples
+ * with duplicate keys could be stored in any order.  We continue to
+ * support reading and writing Btree versions 2 and 3, so that they don't
+ * need to be immediately re-indexed at pg_upgrade.  In order to get the
+ * new heapkeyspace semantics, however, a REINDEX is needed.
+ *
+ * Deduplication is safe to use when the btm_allequalimage field is set to
+ * true.  It's safe to read the btm_allequalimage field on version 3, but
+ * only version 4 indexes make use of deduplication.  Even version 4
+ * indexes created on PostgreSQL v12 will need a REINDEX to make use of
+ * deduplication, though, since there is no other way to set
+ * btm_allequalimage to true (pg_upgrade hasn't been taught to set the
+ * metapage field).
+ *
+ * Btree version 2 is mostly the same as version 3.  There are two new
+ * fields in the metapage that were introduced in version 3.  A version 2
+ * metapage will be automatically upgraded to version 3 on the first
+ * insert to it.  INCLUDE indexes cannot use version 2.
+ */
+#define BTREE_METAPAGE	0		/* first page is meta */
+#define BTREE_MAGIC		0x053162	/* magic number in metapage */
+#define BTREE_VERSION	4		/* current version number */
+#define BTREE_MIN_VERSION	2	/* minimum supported version */
+#define BTREE_NOVAC_VERSION	3	/* version with all meta fields set */
+
+/*
+ * Maximum size of a btree index entry, including its tuple header.
+ *
+ * We actually need to be able to fit three items on every page,
+ * so restrict any one item to 1/3 the per-page available space.
+ *
+ * There are rare cases where _bt_truncate() will need to enlarge
+ * a heap index tuple to make space for a tiebreaker heap TID
+ * attribute, which we account for here.
+ */
+#define BTMaxItemSize(page) \
+	(MAXALIGN_DOWN((PageGetPageSize(page) - \
+					MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
+					MAXALIGN(sizeof(BTPageOpaqueData))) / 3) - \
+					MAXALIGN(sizeof(ItemPointerData)))
+#define BTMaxItemSizeNoHeapTid(page) \
+	MAXALIGN_DOWN((PageGetPageSize(page) - \
+				   MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
+				   MAXALIGN(sizeof(BTPageOpaqueData))) / 3)
+
+/*
+ * MaxTIDsPerBTreePage is an upper bound on the number of heap TIDs tuples
+ * that may be stored on a btree leaf page.  It is used to size the
+ * per-page temporary buffers.
+ *
+ * Note: we don't bother considering per-tuple overheads here to keep
+ * things simple (value is based on how many elements a single array of
+ * heap TIDs must have to fill the space between the page header and
+ * special area).  The value is slightly higher (i.e. more conservative)
+ * than necessary as a result, which is considered acceptable.
+ */
+#define MaxTIDsPerBTreePage BlockSizeCalc(cluster_block_setting,CalcMaxTIDsPerBTreePage)
+#define MaxTIDsPerBTreePageLimit CalcMaxTIDsPerBTreePage(MAX_BLOCK_SIZE)
+BlockSizeDecl(CalcMaxTIDsPerBTreePage);
+
+/*
+ * The leaf-page fillfactor defaults to 90% but is user-adjustable.
+ * For pages above the leaf level, we use a fixed 70% fillfactor.
+ * The fillfactor is applied during index build and when splitting
+ * a rightmost page; when splitting non-rightmost pages we try to
+ * divide the data equally.  When splitting a page that's entirely
+ * filled with a single value (duplicates), the effective leaf-page
+ * fillfactor is 96%, regardless of whether the page is a rightmost
+ * page.
+ */
+#define BTREE_MIN_FILLFACTOR		10
+#define BTREE_DEFAULT_FILLFACTOR	90
+#define BTREE_NONLEAF_FILLFACTOR	70
+#define BTREE_SINGLEVAL_FILLFACTOR	96
+
+/*
+ *	In general, the btree code tries to localize its knowledge about
+ *	page layout to a couple of routines.  However, we need a special
+ *	value to indicate "no page number" in those places where we expect
+ *	page numbers.  We can use zero for this because we never need to
+ *	make a pointer to the metadata page.
+ */
+
+#define P_NONE			0
+
+/*
+ * Macros to test whether a page is leftmost or rightmost on its tree level,
+ * as well as other state info kept in the opaque data.
+ */
+#define P_LEFTMOST(opaque)		((opaque)->btpo_prev == P_NONE)
+#define P_RIGHTMOST(opaque)		((opaque)->btpo_next == P_NONE)
+#define P_ISLEAF(opaque)		(((opaque)->btpo_flags & BTP_LEAF) != 0)
+#define P_ISROOT(opaque)		(((opaque)->btpo_flags & BTP_ROOT) != 0)
+#define P_ISDELETED(opaque)		(((opaque)->btpo_flags & BTP_DELETED) != 0)
+#define P_ISMETA(opaque)		(((opaque)->btpo_flags & BTP_META) != 0)
+#define P_ISHALFDEAD(opaque)	(((opaque)->btpo_flags & BTP_HALF_DEAD) != 0)
+#define P_IGNORE(opaque)		(((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD)) != 0)
+#define P_HAS_GARBAGE(opaque)	(((opaque)->btpo_flags & BTP_HAS_GARBAGE) != 0)
+#define P_INCOMPLETE_SPLIT(opaque)	(((opaque)->btpo_flags & BTP_INCOMPLETE_SPLIT) != 0)
+#define P_HAS_FULLXID(opaque)	(((opaque)->btpo_flags & BTP_HAS_FULLXID) != 0)
+
+#endif
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index ea803eccd8..89102369c0 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -226,14 +226,14 @@ typedef struct SpGistScanOpaqueData
 	TupleDesc	reconTupDesc;	/* if so, descriptor for reconstructed tuples */
 	int			nPtrs;			/* number of TIDs found on current page */
 	int			iPtr;			/* index for scanning through same */
-	ItemPointerData heapPtrs[MaxIndexTuplesPerPage];	/* TIDs from cur page */
-	bool		recheck[MaxIndexTuplesPerPage]; /* their recheck flags */
-	bool		recheckDistances[MaxIndexTuplesPerPage];	/* distance recheck
+	ItemPointerData heapPtrs[MaxIndexTuplesPerPageLimit];	/* TIDs from cur page */
+	bool		recheck[MaxIndexTuplesPerPageLimit]; /* their recheck flags */
+	bool		recheckDistances[MaxIndexTuplesPerPageLimit];	/* distance recheck
 															 * flags */
-	HeapTuple	reconTups[MaxIndexTuplesPerPage];	/* reconstructed tuples */
+	HeapTuple	reconTups[MaxIndexTuplesPerPageLimit];	/* reconstructed tuples */
 
 	/* distances (for recheck) */
-	IndexOrderByDistance *distances[MaxIndexTuplesPerPage];
+	IndexOrderByDistance *distances[MaxIndexTuplesPerPageLimit];
 
 	/*
 	 * Note: using MaxIndexTuplesPerPage above is a bit hokey since
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index b0fd338a00..3bc419ed78 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -391,6 +391,7 @@ extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli);
 extern void XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 								   bool detailed_format, StringInfo buf,
 								   uint32 *fpi_len);
+extern uint32 ClusterBlockSize(void);
 
 /*
  * Exported for the functions in timeline.c and xlogarchive.c.  Only valid
diff --git a/src/include/c.h b/src/include/c.h
index f156886798..2b0835b988 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1123,7 +1123,7 @@ extern void ExceptionalCondition(const char *conditionName,
 	((var) = ((var) < 0) ? 1 : -(var))
 
 /*
- * Use this, not "char buf[cluster_block_size]", to declare a field or local variable
+ * Use this, not "char buf[MAX_BLOCK_SIZE]", to declare a field or local variable
  * holding a page buffer, if that page might be accessed as a page.  Otherwise
  * the variable might be under-aligned, causing problems on alignment-picky
  * hardware.  We include both "double" and "int64" in the union to ensure that
@@ -1132,7 +1132,7 @@ extern void ExceptionalCondition(const char *conditionName,
  */
 typedef union PGAlignedBlock
 {
-	char		data[cluster_block_size];
+	char		data[MAX_BLOCK_SIZE];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGAlignedBlock;
@@ -1150,7 +1150,7 @@ typedef union PGIOAlignedBlock
 #ifdef pg_attribute_aligned
 	pg_attribute_aligned(PG_IO_ALIGN_SIZE)
 #endif
-	char		data[cluster_block_size];
+	char		data[MAX_BLOCK_SIZE];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGIOAlignedBlock;
diff --git a/src/include/common/blocksize.h b/src/include/common/blocksize.h
new file mode 100644
index 0000000000..b5fe75ea7b
--- /dev/null
+++ b/src/include/common/blocksize.h
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.h
+ *	  defintions for cluster-specific limits/structure defs
+ *
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION: src/include/clustersizes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef BLOCKSIZE_H
+#define BLOCKSIZE_H
+
+#ifndef DEFAULT_BLOCK_SIZE
+#define DEFAULT_BLOCK_SIZE 8192
+#endif
+
+#ifndef MIN_BLOCK_SIZE
+#define MIN_BLOCK_SIZE 1024
+#endif
+
+#ifndef MAX_BLOCK_SIZE
+#define MAX_BLOCK_SIZE 32*1024
+#endif
+
+#define IsValidBlockSize(size) ((size) >= MIN_BLOCK_SIZE && \
+								(size) <= MAX_BLOCK_SIZE && \
+								((size)&((size)-1)) == 0)
+
+/* identifiers only */
+typedef enum {
+	BLOCK_SIZE_UNSET = 0,
+	BLOCK_SIZE_1K,
+	BLOCK_SIZE_2K,
+	BLOCK_SIZE_4K,
+	BLOCK_SIZE_8K,
+	BLOCK_SIZE_16K,
+	BLOCK_SIZE_32K,
+} BlockSizeIdent;
+
+extern PGDLLIMPORT BlockSizeIdent cluster_block_setting;
+
+void BlockSizeInit(Size rawblocksize);
+#define cluster_block_bits (cluster_block_setting+9)
+#define cluster_block_size (1<<cluster_block_bits)
+// TODO: make this calculate using use DEFAULT_BLOCK_SIZE instead?
+#define DEFAULT_BLOCK_SIZE_BITS 13 
+#define cluster_relseg_size (RELSEG_SIZE << DEFAULT_BLOCK_SIZE_BITS >> cluster_block_bits)
+
+/* Specific calculations' now parameterized sources */
+
+/* originally in heaptoast.h */
+
+#define CalcMaximumBytesPerTuple(blocksize,tuplesPerPage)	\
+	MAXALIGN_DOWN((blocksize - \
+				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
+				  / (tuplesPerPage))
+
+#define CalcToastMaxChunkSize(blocksize) \
+	(CalcMaximumBytesPerTuple(blocksize,EXTERN_TUPLES_PER_PAGE) -	\
+	 MAXALIGN(SizeofHeapTupleHeader) -					\
+	 sizeof(Oid) -										\
+	 sizeof(int32) -									\
+	 VARHDRSZ)
+
+/* originally in htup_details.h */
+
+#define CalcMaxHeapTupleSize(size)  (size - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+
+#define CalcMaxHeapTuplesPerPage(size)									\
+	((int) (((size) - SizeOfPageHeaderData) /							\
+			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
+
+/* originally in itup.h */
+
+#define CalcMaxIndexTuplesPerPage(size)		\
+	((int) ((size - SizeOfPageHeaderData) / \
+			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
+
+/* originally in nbtree_int.h */
+
+#define CalcMaxTIDsPerBTreePage(size)									\
+	(int) ((size - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
+		   sizeof(ItemPointerData))
+
+/* originally in bloom.h */
+#define CalcFreeBlockNumberElems(size) (MAXALIGN_DOWN(size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData)) \
+													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) \
+									 ) / sizeof(BlockNumber))
+
+#define BlockSizeDecl(calc) \
+	static inline unsigned int _block_size_##calc(BlockSizeIdent bsi) {	\
+	switch(bsi){														\
+	case BLOCK_SIZE_1K: return calc(1024); break;						\
+	case BLOCK_SIZE_2K: return calc(2048); break;						\
+	case BLOCK_SIZE_4K: return calc(4096); break;						\
+	case BLOCK_SIZE_8K: return calc(8192); break;						\
+	case BLOCK_SIZE_16K: return calc(16384); break;						\
+	case BLOCK_SIZE_32K: return calc(32768); break;						\
+	default: return 0;}}
+
+#define BlockSizeDecl2(calc)											\
+	static inline unsigned int _block_size_##calc(BlockSizeIdent bsi, unsigned int arg) { \
+	switch(bsi){														\
+	case BLOCK_SIZE_1K: return calc(1024,arg); break;						\
+	case BLOCK_SIZE_2K: return calc(2048,arg); break;						\
+	case BLOCK_SIZE_4K: return calc(4096,arg); break;						\
+	case BLOCK_SIZE_8K: return calc(8192,arg); break;						\
+	case BLOCK_SIZE_16K: return calc(16384,arg); break;					\
+	case BLOCK_SIZE_32K: return calc(32768,arg); break;					\
+	default: return 0;}}
+
+#define BlockSizeCalc(bsi,calc) _block_size_##calc(bsi)
+
+
+#endif
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index d8a2985567..acac7282b1 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -21,14 +21,15 @@
 /* The normal alignment of `short', in bytes. */
 #undef ALIGNOF_SHORT
 
-/* Size of a disk block --- this also limits the size of a tuple. You can set
-   it bigger if you need bigger tuples (although TOAST should reduce the need
-   to have large tuples, since fields can be spread across multiple tuples).
-   BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
-   currently 2^15 (32768). This is determined by the 15-bit widths of the
-   lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
-   Changing BLCKSZ requires an initdb. */
-#undef BLCKSZ
+/* Default size of a disk block --- this also limits the size of a tuple. You
+   can set it bigger if you need bigger tuples (although TOAST should reduce
+   the need to have large tuples, since fields can be spread across multiple
+   tuples).  DEFAULT_BLOCK_SIZE must be a power of 2. The maximum possible
+   value of DEFAULT_BLOCK_SIZE is currently 2^15 (32768). This is determined
+   by the 15-bit widths of the lp_off and lp_len fields in ItemIdData (see
+   include/storage/itemid.h).  Changing DEFAULT_BLOCK_SIZE requires an
+   initdb. */
+#undef DEFAULT_BLOCK_SIZE
 
 /* Saved arguments from configure */
 #undef CONFIGURE_ARGS
@@ -650,16 +651,16 @@
 #undef PTHREAD_CREATE_JOINABLE
 
 /* RELSEG_SIZE is the maximum number of blocks allowed in one disk file. Thus,
-   the maximum size of a single file is RELSEG_SIZE * BLCKSZ; relations bigger
-   than that are divided into multiple files. RELSEG_SIZE * BLCKSZ must be
-   less than your OS' limit on file size. This is often 2 GB or 4GB in a
-   32-bit operating system, unless you have large file support enabled. By
-   default, we make the limit 1 GB to avoid any possible integer-overflow
-   problems within the OS. A limit smaller than necessary only means we divide
-   a large relation into more chunks than necessary, so it seems best to err
-   in the direction of a small limit. A power-of-2 value is recommended to
-   save a few cycles in md.c, but is not absolutely required. Changing
-   RELSEG_SIZE requires an initdb. */
+   the maximum size of a single file is RELSEG_SIZE * cluster_block_size;
+   relations bigger than that are divided into multiple files. RELSEG_SIZE *
+   cluster_block_size must be less than your OS' limit on file size. This is
+   often 2 GB or 4GB in a 32-bit operating system, unless you have large file
+   support enabled. By default, we make the limit 1 GB to avoid any possible
+   integer-overflow problems within the OS. A limit smaller than necessary
+   only means we divide a large relation into more chunks than necessary, so
+   it seems best to err in the direction of a small limit. A power-of-2 value
+   is recommended to save a few cycles in md.c, but is not absolutely
+   required. Changing RELSEG_SIZE requires an initdb. */
 #undef RELSEG_SIZE
 
 /* The size of `bool', as computed by sizeof. */
@@ -774,7 +775,7 @@
 # endif
 #endif
 
-/* Size of a WAL file block. This need have no particular relation to BLCKSZ.
+/* Size of a WAL file block. This need have no particular relation to cluster_block_size.
    XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O,
    XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O
    buffers, else direct I/O may fail. Changing XLOG_BLCKSZ requires an initdb.
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 0446bfb407..70bddf2c7b 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -233,6 +233,14 @@
  */
 #define PG_IO_ALIGN_SIZE		4096
 
+/*
+ * Blocksize-related constants
+ */
+
+#define MIN_BLOCK_SIZE 1024
+#define MAX_BLOCK_SIZE 32768
+/* #define DEFAULT_BLOCK_SIZE 8192 */
+
 /*
  *------------------------------------------------------------------------
  * The following symbols are for enabling debugging code, not for
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a028ff789..4651389fc2 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -45,6 +45,7 @@
 #include "c.h"
 #include "utils/elog.h"
 #include "utils/palloc.h"
+#include "common/blocksize.h"
 
 /* ----------------------------------------------------------------
  *				Section 1:	Datum type + support functions
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index 1ed3d694ee..240ad4e93b 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -70,5 +70,4 @@ typedef PG_INT64_TYPE pg_int64;
 #define PG_DIAG_SOURCE_LINE		'L'
 #define PG_DIAG_SOURCE_FUNCTION 'R'
 
-#define cluster_block_size BLCKSZ
 #endif							/* POSTGRES_EXT_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 20d8d7277a..a997380707 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -475,15 +475,15 @@ do { \
 	PageIsVerifiedExtended(page, blkno, \
 						   PIV_LOG_WARNING | PIV_REPORT_STAT)
 
-/*
- * Check that cluster_block_size is a multiple of sizeof(size_t).  In
- * PageIsVerifiedExtended(), it is much faster to check if a page is
- * full of zeroes using the native word size.  Note that this assertion
- * is kept within a header to make sure that StaticAssertDecl() works
- * across various combinations of platforms and compilers.
- */
-StaticAssertDecl(cluster_block_size == ((cluster_block_size / sizeof(size_t)) * sizeof(size_t)),
-				 "cluster_block_size has to be a multiple of sizeof(size_t)");
+/* /\* */
+/*  * Check that cluster_block_size is a multiple of sizeof(size_t).  In */
+/*  * PageIsVerifiedExtended(), it is much faster to check if a page is */
+/*  * full of zeroes using the native word size.  Note that this assertion */
+/*  * is kept within a header to make sure that StaticAssertDecl() works */
+/*  * across various combinations of platforms and compilers. */
+/*  *\/ */
+/* StaticAssertDecl(cluster_block_size == ((cluster_block_size / sizeof(size_t)) * sizeof(size_t)), */
+/* 				 "cluster_block_size has to be a multiple of sizeof(size_t)"); */
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
 extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
diff --git a/src/include/storage/checksum.h b/src/include/storage/checksum.h
index 4afd25a0af..bea1cbd5b6 100644
--- a/src/include/storage/checksum.h
+++ b/src/include/storage/checksum.h
@@ -19,6 +19,6 @@
  * Compute the checksum for a Postgres page.  The page must be aligned on a
  * 4-byte boundary.
  */
-extern uint16 pg_checksum_page(char *page, BlockNumber blkno);
+extern uint16 pg_checksum_page(char *page, BlockNumber blkno, Size pagesize);
 
 #endif							/* CHECKSUM_H */
diff --git a/src/include/storage/checksum_impl.h b/src/include/storage/checksum_impl.h
index 18951c36c7..e51e04ec4a 100644
--- a/src/include/storage/checksum_impl.h
+++ b/src/include/storage/checksum_impl.h
@@ -101,6 +101,7 @@
  */
 
 #include "storage/bufpage.h"
+#include "common/blocksize.h"
 
 /* number of checksums to calculate in parallel */
 #define N_SUMS 32
@@ -111,7 +112,7 @@
 typedef union
 {
 	PageHeaderData phdr;
-	uint32		data[cluster_block_size / (sizeof(uint32) * N_SUMS)][N_SUMS];
+	uint32		data[MAX_BLOCK_SIZE / (sizeof(uint32) * N_SUMS)][N_SUMS];
 } PGChecksummablePage;
 
 /*
@@ -143,7 +144,7 @@ do { \
  * (at least on 4-byte boundary).
  */
 static uint32
-pg_checksum_block(const PGChecksummablePage *page)
+pg_checksum_block(const PGChecksummablePage *page, Size blocksize)
 {
 	uint32		sums[N_SUMS];
 	uint32		result = 0;
@@ -151,13 +152,13 @@ pg_checksum_block(const PGChecksummablePage *page)
 				j;
 
 	/* ensure that the size is compatible with the algorithm */
-	Assert(sizeof(PGChecksummablePage) == cluster_block_size);
+	Assert(cluster_block_size == blocksize);
 
 	/* initialize partial checksums to their corresponding offsets */
 	memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets));
 
 	/* main checksum calculation */
-	for (i = 0; i < (uint32) (cluster_block_size / (sizeof(uint32) * N_SUMS)); i++)
+	for (i = 0; i < (uint32) (blocksize / (sizeof(uint32) * N_SUMS)); i++)
 		for (j = 0; j < N_SUMS; j++)
 			CHECKSUM_COMP(sums[j], page->data[i][j]);
 
@@ -184,7 +185,7 @@ pg_checksum_block(const PGChecksummablePage *page)
  * checksum itself), and the page data.
  */
 uint16
-pg_checksum_page(char *page, BlockNumber blkno)
+pg_checksum_page(char *page, BlockNumber blkno, Size pagesize)
 {
 	PGChecksummablePage *cpage = (PGChecksummablePage *) page;
 	uint16		save_checksum;
@@ -201,7 +202,7 @@ pg_checksum_page(char *page, BlockNumber blkno)
 	 */
 	save_checksum = cpage->phdr.pd_checksum;
 	cpage->phdr.pd_checksum = 0;
-	checksum = pg_checksum_block(cpage);
+	checksum = pg_checksum_block(cpage, pagesize);
 	cpage->phdr.pd_checksum = save_checksum;
 
 	/* Mix in the block number to detect transposed pages */
diff --git a/src/include/storage/freespace.h b/src/include/storage/freespace.h
index 9e1a85a141..4bbad4aaad 100644
--- a/src/include/storage/freespace.h
+++ b/src/include/storage/freespace.h
@@ -19,6 +19,7 @@
 #include "utils/relcache.h"
 
 /* prototypes for public functions in freespace.c */
+extern void FreeSpaceMapInit(void);
 extern Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk);
 extern BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded);
 extern BlockNumber RecordAndGetPageWithFreeSpace(Relation rel,
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
index 9fab334c7b..9515df5f05 100644
--- a/src/include/storage/large_object.h
+++ b/src/include/storage/large_object.h
@@ -67,7 +67,9 @@ typedef struct LargeObjectDesc
  *
  * NB: Changing cluster_loblksize requires an initdb.
  */
+#define CalcLOBLKSIZE(size) (size/4)
 #define cluster_loblksize		(cluster_block_size / 4)
+#define LOBLKSIZE_LIMIT		CalcLOBLKSIZE(MAX_BLOCK_SIZE)
 
 /*
  * Maximum length in bytes for a large object.  To make this larger, we'd
diff --git a/src/include/storage/off.h b/src/include/storage/off.h
index 7fbd45a084..bfcd0a431f 100644
--- a/src/include/storage/off.h
+++ b/src/include/storage/off.h
@@ -25,7 +25,9 @@ typedef uint16 OffsetNumber;
 
 #define InvalidOffsetNumber		((OffsetNumber) 0)
 #define FirstOffsetNumber		((OffsetNumber) 1)
-#define MaxOffsetNumber			((OffsetNumber) (cluster_block_size / sizeof(ItemIdData)))
+#define CalcMaxOffsetNumber(size)			((OffsetNumber) (size / sizeof(ItemIdData)))
+#define MaxOffsetNumber			((OffsetNumber)CalcMaxOffsetNumber(cluster_block_size))
+#define MaxOffsetNumberLimit			((OffsetNumber)CalcMaxOffsetNumber(MAX_BLOCK_SIZE))
 
 /* ----------------
  *		support macros
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index d5a0880678..a47b6b6c2d 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -298,6 +298,7 @@ extern bool ConfigOptionIsVisible(struct config_generic *conf);
 /* get the current set of variables */
 extern struct config_generic **get_guc_variables(int *num_vars);
 
+extern void update_dynamic_gucs(void);
 extern void build_guc_variables(void);
 
 /* search in enum options */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 9e05eb91b1..ead1d4e26f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -135,7 +135,7 @@ sub mkvcbuild
 	}
 
 	our @pgcommonallfiles = qw(
-	  archive.c base64.c checksum_helper.c compression.c
+	  archive.c base64.c blocksize.c checksum_helper.c compression.c
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
 	  keywords.c kwlookup.c link-canary.c md5_common.c percentrepl.c
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index f6a40ae730..d7717d2bbc 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -212,10 +212,10 @@ sub GenerateFiles
 		ALIGNOF_PG_INT128_TYPE => undef,
 		ALIGNOF_SHORT => 2,
 		AC_APPLE_UNIVERSAL_BUILD => undef,
-		cluster_block_size => 1024 * $self->{options}->{blocksize},
 		CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"',
 		DEF_PGPORT => $port,
 		DEF_PGPORT_STR => qq{"$port"},
+		DEFAULT_BLOCK_SIZE => 1024 * $self->{options}->{blocksize},
 		DLSUFFIX => '".dll"',
 		ENABLE_GSS => $self->{options}->{gss} ? 1 : undef,
 		ENABLE_NLS => $self->{options}->{nls} ? 1 : undef,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 49a33c0387..42375cca63 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -893,7 +893,6 @@ Form_pg_ts_template
 Form_pg_type
 Form_pg_user_mapping
 FormatNode
-FreeBlockNumberArray
 FreeListData
 FreePageBtree
 FreePageBtreeHeader
-- 
2.39.2

v2-0008-Add-pragmas-to-libdivide-to-make-header-check-hap.patchapplication/octet-stream; name=v2-0008-Add-pragmas-to-libdivide-to-make-header-check-hap.patchDownload
From 139d9b3d68cb1b3bae6018a01e77ff572826d49c Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Wed, 19 Jul 2023 12:50:34 -0400
Subject: [PATCH v2 5/5] Add pragmas to libdivide to make header check happy

---
 src/include/common/libdivide.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/include/common/libdivide.h b/src/include/common/libdivide.h
index 96dd272119..340f9a168f 100644
--- a/src/include/common/libdivide.h
+++ b/src/include/common/libdivide.h
@@ -1,3 +1,7 @@
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
+#undef __cplusplus
+
 // libdivide.h - Optimized integer division
 // https://libdivide.com
 //
-- 
2.39.2

v2-0006-Use-fastdiv-code-in-visibility-map.patchapplication/octet-stream; name=v2-0006-Use-fastdiv-code-in-visibility-map.patchDownload
From e4a4ca3f9abe620c283504e0b9d3597d3c04281c Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Tue, 18 Jul 2023 17:58:20 -0400
Subject: [PATCH v2 3/5] Use fastdiv code in visibility map

---
 src/backend/access/heap/visibilitymap.c | 93 ++++++++++++++++++-------
 1 file changed, 68 insertions(+), 25 deletions(-)

diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index ab4d38fbe6..7c80df680c 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -105,18 +105,25 @@
  * extra headers, so the whole page minus the standard page header is
  * used for the bitmap.
  */
-#define MAPSIZE (cluster_block_size - MAXALIGN(SizeOfPageHeaderData))
+#define MAPSIZE(blocksize) (blocksize - MAXALIGN(SizeOfPageHeaderData))
 
 /* Number of heap blocks we can represent in one byte */
-#define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
+#define HEAPBLOCKS_PER_BYTE(blocksize) (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
+
+/* Init routine for our fastmath */
+#define MAPBLOCK_INIT if (unlikely(!mapblock_size))		\
+	{													\
+		mapblock_size = MAPSIZE(cluster_block_size);	\
+		mapblock_inv = pg_fastinverse(mapblock_size);	\
+	}
 
 /* Number of heap blocks we can represent in one visibility map page. */
-#define HEAPBLOCKS_PER_PAGE (MAPSIZE << BITS_PER_HEAPBLOCK)
+#define HEAPBLOCKS_PER_PAGE(blocksize) (MAPSIZE(blocksize) << BITS_PER_HEAPBLOCK)
 
 /* Mapping from heap block number to the right bit in the visibility map */
-#define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
-#define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE)
-#define HEAPBLK_TO_OFFSET(x) (((x) % HEAPBLOCKS_PER_BYTE) * BITS_PER_HEAPBLOCK)
+#define HEAPBLK_TO_MAPBLOCK(x) (pg_fastdiv((x),mapblock_size,mapblock_inv))
+#define HEAPBLK_TO_MAPBYTE(x) (pg_fastmod((x),mapblock_size,mapblock_inv) >> 2) /* always 4 blocks per byte */
+#define HEAPBLK_TO_OFFSET(x) (((x) & 0x3) << 1) /* always 2 bits per entry */
 
 /* Masks for counting subsets of bits in the visibility map. */
 #define VISIBLE_MASK64	UINT64CONST(0x5555555555555555) /* The lower bit of each
@@ -128,6 +135,9 @@
 static Buffer vm_readbuf(Relation rel, BlockNumber blkno, bool extend);
 static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks);
 
+/* storage for the fast div/mod inverse */
+static uint64 mapblock_inv = 0;
+static uint32 mapblock_size = 0;
 
 /*
  *	visibilitymap_clear - clear specified bits for one page in visibility map
@@ -139,13 +149,20 @@ static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks);
 bool
 visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
-	int			mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
-	int			mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
-	uint8		mask = flags << mapOffset;
+	BlockNumber mapBlock;
+	int			mapByte;
+	int			mapOffset;
+	uint8		mask;
 	char	   *map;
 	bool		cleared = false;
 
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
+	mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+	mask = flags << mapOffset;
+
 	/* Must never clear all_visible bit while leaving all_frozen bit set */
 	Assert(flags & VISIBILITYMAP_VALID_BITS);
 	Assert(flags != VISIBILITYMAP_ALL_VISIBLE);
@@ -192,7 +209,11 @@ visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags
 void
 visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	BlockNumber mapBlock;
+
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
 
 	/* Reuse the old pinned buffer if possible */
 	if (BufferIsValid(*vmbuf))
@@ -216,7 +237,11 @@ visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
 bool
 visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	BlockNumber mapBlock;
+
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
 
 	return BufferIsValid(vmbuf) && BufferGetBlockNumber(vmbuf) == mapBlock;
 }
@@ -247,12 +272,18 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
 				  XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid,
 				  uint8 flags)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
-	uint32		mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
-	uint8		mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+	BlockNumber mapBlock;
+	uint32		mapByte;
+	uint8		mapOffset;
 	Page		page;
 	uint8	   *map;
 
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
+	mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+
 #ifdef TRACE_VISIBILITYMAP
 	elog(DEBUG1, "vm_set %s %d", RelationGetRelationName(rel), heapBlk);
 #endif
@@ -337,12 +368,18 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
 uint8
 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
-	uint32		mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
-	uint8		mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+	BlockNumber mapBlock;
+	uint32		mapByte;
+	uint8		mapOffset;
 	char	   *map;
 	uint8		result;
 
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
+	mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+
 #ifdef TRACE_VISIBILITYMAP
 	elog(DEBUG1, "vm_get_status %s %d", RelationGetRelationName(rel), heapBlk);
 #endif
@@ -414,16 +451,16 @@ visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_fro
 		 */
 		map = (uint64 *) PageGetContents(BufferGetPage(mapBuffer));
 
-		Assert(MAPSIZE % sizeof(uint64) == 0);
+		Assert(mapblock_size % sizeof(uint64) == 0);
 
 		if (all_frozen == NULL)
 		{
-			for (i = 0; i < MAPSIZE / sizeof(uint64); i++)
+			for (i = 0; i < mapblock_size / sizeof(uint64); i++)
 				nvisible += pg_popcount64(map[i] & VISIBLE_MASK64);
 		}
 		else
 		{
-			for (i = 0; i < MAPSIZE / sizeof(uint64); i++)
+			for (i = 0; i < mapblock_size / sizeof(uint64); i++)
 			{
 				nvisible += pg_popcount64(map[i] & VISIBLE_MASK64);
 				nfrozen += pg_popcount64(map[i] & FROZEN_MASK64);
@@ -455,9 +492,15 @@ visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks)
 	BlockNumber newnblocks;
 
 	/* last remaining block, byte, and bit */
-	BlockNumber truncBlock = HEAPBLK_TO_MAPBLOCK(nheapblocks);
-	uint32		truncByte = HEAPBLK_TO_MAPBYTE(nheapblocks);
-	uint8		truncOffset = HEAPBLK_TO_OFFSET(nheapblocks);
+	BlockNumber truncBlock;
+	uint32		truncByte;
+	uint8		truncOffset;
+
+	MAPBLOCK_INIT;
+
+	truncBlock = HEAPBLK_TO_MAPBLOCK(nheapblocks);
+	truncByte = HEAPBLK_TO_MAPBYTE(nheapblocks);
+	truncOffset = HEAPBLK_TO_OFFSET(nheapblocks);
 
 #ifdef TRACE_VISIBILITYMAP
 	elog(DEBUG1, "vm_truncate %s %d", RelationGetRelationName(rel), nheapblocks);
@@ -501,7 +544,7 @@ visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks)
 		START_CRIT_SECTION();
 
 		/* Clear out the unwanted bytes. */
-		MemSet(&map[truncByte + 1], 0, MAPSIZE - (truncByte + 1));
+		MemSet(&map[truncByte + 1], 0, mapblock_size - (truncByte + 1));
 
 		/*----
 		 * Mask out the unwanted bits of the last remaining byte.
-- 
2.39.2

v2-0007-Use-libdivide-for-fast-division.patchapplication/octet-stream; name=v2-0007-Use-libdivide-for-fast-division.patchDownload
From 3c849bfbdb86607e1e31ac36b64b839b4bb9b692 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Wed, 19 Jul 2023 12:00:45 -0400
Subject: [PATCH v2 4/5] Use libdivide for fast division

---
 src/backend/access/heap/visibilitymap.c |   15 +-
 src/include/common/libdivide.h          | 3269 +++++++++++++++++++++++
 2 files changed, 3278 insertions(+), 6 deletions(-)
 create mode 100644 src/include/common/libdivide.h

diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 7c80df680c..c5aafdbfca 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -90,6 +90,7 @@
 #include "access/visibilitymap.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
+#include "common/libdivide.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
 #include "storage/bufmgr.h"
@@ -111,17 +112,18 @@
 #define HEAPBLOCKS_PER_BYTE(blocksize) (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
 
 /* Init routine for our fastmath */
-#define MAPBLOCK_INIT if (unlikely(!mapblock_size))		\
-	{													\
-		mapblock_size = MAPSIZE(cluster_block_size);	\
-		mapblock_inv = pg_fastinverse(mapblock_size);	\
+#define MAPBLOCK_INIT if (unlikely(!mapblock_size))			\
+	{														\
+		mapblock_size = MAPSIZE(cluster_block_size);		\
+		mapblock_div = libdivide_u32_gen(mapblock_size);	\
+		mapblock_inv = pg_fastinverse(mapblock_size);		\
 	}
 
 /* Number of heap blocks we can represent in one visibility map page. */
 #define HEAPBLOCKS_PER_PAGE(blocksize) (MAPSIZE(blocksize) << BITS_PER_HEAPBLOCK)
 
 /* Mapping from heap block number to the right bit in the visibility map */
-#define HEAPBLK_TO_MAPBLOCK(x) (pg_fastdiv((x),mapblock_size,mapblock_inv))
+#define HEAPBLK_TO_MAPBLOCK(x) (libdivide_u32_do((x),&mapblock_div))
 #define HEAPBLK_TO_MAPBYTE(x) (pg_fastmod((x),mapblock_size,mapblock_inv) >> 2) /* always 4 blocks per byte */
 #define HEAPBLK_TO_OFFSET(x) (((x) & 0x3) << 1) /* always 2 bits per entry */
 
@@ -136,8 +138,9 @@ static Buffer vm_readbuf(Relation rel, BlockNumber blkno, bool extend);
 static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks);
 
 /* storage for the fast div/mod inverse */
-static uint64 mapblock_inv = 0;
 static uint32 mapblock_size = 0;
+static uint64 mapblock_inv;
+static struct libdivide_u32_t mapblock_div;
 
 /*
  *	visibilitymap_clear - clear specified bits for one page in visibility map
diff --git a/src/include/common/libdivide.h b/src/include/common/libdivide.h
new file mode 100644
index 0000000000..96dd272119
--- /dev/null
+++ b/src/include/common/libdivide.h
@@ -0,0 +1,3269 @@
+// libdivide.h - Optimized integer division
+// https://libdivide.com
+//
+// Copyright (C) 2010 - 2022 ridiculous_fish, <libdivide@ridiculousfish.com>
+// Copyright (C) 2016 - 2022 Kim Walisch, <kim.walisch@gmail.com>
+//
+// libdivide is dual-licensed under the Boost or zlib licenses.
+// You may use libdivide under the terms of either of these.
+// See LICENSE.txt for more details.
+
+#ifndef LIBDIVIDE_H
+#define LIBDIVIDE_H
+
+#define LIBDIVIDE_VERSION "5.0"
+#define LIBDIVIDE_VERSION_MAJOR 5
+#define LIBDIVIDE_VERSION_MINOR 0
+
+#include <stdint.h>
+#if !defined(__AVR__)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#if defined(LIBDIVIDE_SSE2)
+#include <emmintrin.h>
+#endif
+#if defined(LIBDIVIDE_AVX2) || defined(LIBDIVIDE_AVX512)
+#include <immintrin.h>
+#endif
+#if defined(LIBDIVIDE_NEON)
+#include <arm_neon.h>
+#endif
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#pragma warning(push)
+// disable warning C4146: unary minus operator applied
+// to unsigned type, result still unsigned
+#pragma warning(disable : 4146)
+// disable warning C4204: nonstandard extension used : non-constant aggregate
+// initializer
+//
+// It's valid C99
+#pragma warning(disable : 4204)
+#define LIBDIVIDE_VC
+#endif
+
+#if !defined(__has_builtin)
+#define __has_builtin(x) 0
+#endif
+
+#if defined(__SIZEOF_INT128__)
+#define HAS_INT128_T
+// clang-cl on Windows does not yet support 128-bit division
+#if !(defined(__clang__) && defined(LIBDIVIDE_VC))
+#define HAS_INT128_DIV
+#endif
+#endif
+
+#if defined(__x86_64__) || defined(_M_X64)
+#define LIBDIVIDE_X86_64
+#endif
+
+#if defined(__i386__)
+#define LIBDIVIDE_i386
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define LIBDIVIDE_GCC_STYLE_ASM
+#endif
+
+#if defined(__cplusplus) || defined(LIBDIVIDE_VC)
+#define LIBDIVIDE_FUNCTION __FUNCTION__
+#else
+#define LIBDIVIDE_FUNCTION __func__
+#endif
+
+// Set up forced inlining if possible.
+// We need both the attribute and keyword to avoid "might not be inlineable" warnings.
+#ifdef __has_attribute
+#if __has_attribute(always_inline)
+#define LIBDIVIDE_INLINE __attribute__((always_inline)) inline
+#endif
+#endif
+#ifndef LIBDIVIDE_INLINE
+#define LIBDIVIDE_INLINE inline
+#endif
+
+#if defined(__AVR__)
+#define LIBDIVIDE_ERROR(msg)
+#else
+#define LIBDIVIDE_ERROR(msg)                                                                     \
+    do {                                                                                         \
+        fprintf(stderr, "libdivide.h:%d: %s(): Error: %s\n", __LINE__, LIBDIVIDE_FUNCTION, msg); \
+        abort();                                                                                 \
+    } while (0)
+#endif
+
+#if defined(LIBDIVIDE_ASSERTIONS_ON) && !defined(__AVR__)
+#define LIBDIVIDE_ASSERT(x)                                                           \
+    do {                                                                              \
+        if (!(x)) {                                                                   \
+            fprintf(stderr, "libdivide.h:%d: %s(): Assertion failed: %s\n", __LINE__, \
+                LIBDIVIDE_FUNCTION, #x);                                              \
+            abort();                                                                  \
+        }                                                                             \
+    } while (0)
+#else
+#define LIBDIVIDE_ASSERT(x)
+#endif
+
+#ifdef __cplusplus
+namespace libdivide {
+#endif
+
+// pack divider structs to prevent compilers from padding.
+// This reduces memory usage by up to 43% when using a large
+// array of libdivide dividers and improves performance
+// by up to 10% because of reduced memory bandwidth.
+#pragma pack(push, 1)
+
+struct libdivide_u16_t {
+    uint16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s16_t {
+    int16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u32_t {
+    uint32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s32_t {
+    int32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u64_t {
+    uint64_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s64_t {
+    int64_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u16_branchfree_t {
+    uint16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s16_branchfree_t {
+    int16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u32_branchfree_t {
+    uint32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s32_branchfree_t {
+    int32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u64_branchfree_t {
+    uint64_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s64_branchfree_t {
+    int64_t magic;
+    uint8_t more;
+};
+
+#pragma pack(pop)
+
+// Explanation of the "more" field:
+//
+// * Bits 0-5 is the shift value (for shift path or mult path).
+// * Bit 6 is the add indicator for mult path.
+// * Bit 7 is set if the divisor is negative. We use bit 7 as the negative
+//   divisor indicator so that we can efficiently use sign extension to
+//   create a bitmask with all bits set to 1 (if the divisor is negative)
+//   or 0 (if the divisor is positive).
+//
+// u32: [0-4] shift value
+//      [5] ignored
+//      [6] add indicator
+//      magic number of 0 indicates shift path
+//
+// s32: [0-4] shift value
+//      [5] ignored
+//      [6] add indicator
+//      [7] indicates negative divisor
+//      magic number of 0 indicates shift path
+//
+// u64: [0-5] shift value
+//      [6] add indicator
+//      magic number of 0 indicates shift path
+//
+// s64: [0-5] shift value
+//      [6] add indicator
+//      [7] indicates negative divisor
+//      magic number of 0 indicates shift path
+//
+// In s32 and s64 branchfree modes, the magic number is negated according to
+// whether the divisor is negated. In branchfree strategy, it is not negated.
+
+enum {
+    LIBDIVIDE_16_SHIFT_MASK = 0x1F,
+    LIBDIVIDE_32_SHIFT_MASK = 0x1F,
+    LIBDIVIDE_64_SHIFT_MASK = 0x3F,
+    LIBDIVIDE_ADD_MARKER = 0x40,
+    LIBDIVIDE_NEGATIVE_DIVISOR = 0x80
+};
+
+static LIBDIVIDE_INLINE struct libdivide_s16_t libdivide_s16_gen(int16_t d);
+static LIBDIVIDE_INLINE struct libdivide_u16_t libdivide_u16_gen(uint16_t d);
+static LIBDIVIDE_INLINE struct libdivide_s32_t libdivide_s32_gen(int32_t d);
+static LIBDIVIDE_INLINE struct libdivide_u32_t libdivide_u32_gen(uint32_t d);
+static LIBDIVIDE_INLINE struct libdivide_s64_t libdivide_s64_gen(int64_t d);
+static LIBDIVIDE_INLINE struct libdivide_u64_t libdivide_u64_gen(uint64_t d);
+
+static LIBDIVIDE_INLINE struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen(int16_t d);
+static LIBDIVIDE_INLINE struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen(uint16_t d);
+static LIBDIVIDE_INLINE struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d);
+static LIBDIVIDE_INLINE struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d);
+static LIBDIVIDE_INLINE struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d);
+static LIBDIVIDE_INLINE struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_do_raw(int16_t numer, int16_t magic, uint8_t more);
+static LIBDIVIDE_INLINE int16_t libdivide_s16_do(
+    int16_t numer, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_do_raw(uint16_t numer, uint16_t magic, uint8_t more);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_do(
+    uint16_t numer, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_do(
+    int32_t numer, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_do(
+    uint32_t numer, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_do(
+    int64_t numer, const struct libdivide_s64_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_do(
+    uint64_t numer, const struct libdivide_u64_t *denom);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_branchfree_do(
+    int16_t numer, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_branchfree_do(
+    uint16_t numer, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_branchfree_do(
+    int32_t numer, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_branchfree_do(
+    uint32_t numer, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_branchfree_do(
+    int64_t numer, const struct libdivide_s64_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_branchfree_do(
+    uint64_t numer, const struct libdivide_u64_branchfree_t *denom);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_recover(const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_recover(const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_branchfree_recover(
+    const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_branchfree_recover(
+    const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_branchfree_recover(
+    const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_branchfree_recover(
+    const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_branchfree_recover(
+    const struct libdivide_s64_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_branchfree_recover(
+    const struct libdivide_u64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+static LIBDIVIDE_INLINE uint16_t libdivide_mullhi_u16(uint16_t x, uint16_t y) {
+    uint32_t xl = x, yl = y;
+    uint32_t rl = xl * yl;
+    return (uint16_t)(rl >> 16);
+}
+
+static LIBDIVIDE_INLINE int16_t libdivide_mullhi_s16(int16_t x, int16_t y) {
+    int32_t xl = x, yl = y;
+    int32_t rl = xl * yl;
+    // needs to be arithmetic shift
+    return (int16_t)(rl >> 16);
+}
+
+static LIBDIVIDE_INLINE uint32_t libdivide_mullhi_u32(uint32_t x, uint32_t y) {
+    uint64_t xl = x, yl = y;
+    uint64_t rl = xl * yl;
+    return (uint32_t)(rl >> 32);
+}
+
+static LIBDIVIDE_INLINE int32_t libdivide_mullhi_s32(int32_t x, int32_t y) {
+    int64_t xl = x, yl = y;
+    int64_t rl = xl * yl;
+    // needs to be arithmetic shift
+    return (int32_t)(rl >> 32);
+}
+
+static LIBDIVIDE_INLINE uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) {
+#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
+    return __umulh(x, y);
+#elif defined(HAS_INT128_T)
+    __uint128_t xl = x, yl = y;
+    __uint128_t rl = xl * yl;
+    return (uint64_t)(rl >> 64);
+#else
+    // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64)
+    uint32_t mask = 0xFFFFFFFF;
+    uint32_t x0 = (uint32_t)(x & mask);
+    uint32_t x1 = (uint32_t)(x >> 32);
+    uint32_t y0 = (uint32_t)(y & mask);
+    uint32_t y1 = (uint32_t)(y >> 32);
+    uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0);
+    uint64_t x0y1 = x0 * (uint64_t)y1;
+    uint64_t x1y0 = x1 * (uint64_t)y0;
+    uint64_t x1y1 = x1 * (uint64_t)y1;
+    uint64_t temp = x1y0 + x0y0_hi;
+    uint64_t temp_lo = temp & mask;
+    uint64_t temp_hi = temp >> 32;
+
+    return x1y1 + temp_hi + ((temp_lo + x0y1) >> 32);
+#endif
+}
+
+static LIBDIVIDE_INLINE int64_t libdivide_mullhi_s64(int64_t x, int64_t y) {
+#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
+    return __mulh(x, y);
+#elif defined(HAS_INT128_T)
+    __int128_t xl = x, yl = y;
+    __int128_t rl = xl * yl;
+    return (int64_t)(rl >> 64);
+#else
+    // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64)
+    uint32_t mask = 0xFFFFFFFF;
+    uint32_t x0 = (uint32_t)(x & mask);
+    uint32_t y0 = (uint32_t)(y & mask);
+    int32_t x1 = (int32_t)(x >> 32);
+    int32_t y1 = (int32_t)(y >> 32);
+    uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0);
+    int64_t t = x1 * (int64_t)y0 + x0y0_hi;
+    int64_t w1 = x0 * (int64_t)y1 + (t & mask);
+
+    return x1 * (int64_t)y1 + (t >> 32) + (w1 >> 32);
+#endif
+}
+
+static LIBDIVIDE_INLINE int16_t libdivide_count_leading_zeros16(uint16_t val) {
+#if defined(__AVR__)
+    // Fast way to count leading zeros
+    // On the AVR 8-bit architecture __builtin_clz() works on a int16_t.
+    return __builtin_clz(val);
+#elif defined(__GNUC__) || __has_builtin(__builtin_clz)
+    // Fast way to count leading zeros
+    return __builtin_clz(val) - 16;
+#elif defined(LIBDIVIDE_VC)
+    unsigned long result;
+    if (_BitScanReverse(&result, (unsigned long)val)) {
+        return (int16_t)(15 - result);
+    }
+    return 0;
+#else
+    if (val == 0) return 16;
+    int16_t result = 4;
+    uint16_t hi = 0xFU << 12;
+    while ((val & hi) == 0) {
+        hi >>= 4;
+        result += 4;
+    }
+    while (val & hi) {
+        result -= 1;
+        hi <<= 1;
+    }
+    return result;
+#endif
+}
+
+static LIBDIVIDE_INLINE int32_t libdivide_count_leading_zeros32(uint32_t val) {
+#if defined(__AVR__)
+    // Fast way to count leading zeros
+    return __builtin_clzl(val);
+#elif defined(__GNUC__) || __has_builtin(__builtin_clz)
+    // Fast way to count leading zeros
+    return __builtin_clz(val);
+#elif defined(LIBDIVIDE_VC)
+    unsigned long result;
+    if (_BitScanReverse(&result, val)) {
+        return 31 - result;
+    }
+    return 0;
+#else
+    if (val == 0) return 32;
+    int32_t result = 8;
+    uint32_t hi = 0xFFU << 24;
+    while ((val & hi) == 0) {
+        hi >>= 8;
+        result += 8;
+    }
+    while (val & hi) {
+        result -= 1;
+        hi <<= 1;
+    }
+    return result;
+#endif
+}
+
+static LIBDIVIDE_INLINE int32_t libdivide_count_leading_zeros64(uint64_t val) {
+#if defined(__GNUC__) || __has_builtin(__builtin_clzll)
+    // Fast way to count leading zeros
+    return __builtin_clzll(val);
+#elif defined(LIBDIVIDE_VC) && defined(_WIN64)
+    unsigned long result;
+    if (_BitScanReverse64(&result, val)) {
+        return 63 - result;
+    }
+    return 0;
+#else
+    uint32_t hi = val >> 32;
+    uint32_t lo = val & 0xFFFFFFFF;
+    if (hi != 0) return libdivide_count_leading_zeros32(hi);
+    return 32 + libdivide_count_leading_zeros32(lo);
+#endif
+}
+
+// libdivide_32_div_16_to_16: divides a 32-bit uint {u1, u0} by a 16-bit
+// uint {v}. The result must fit in 16 bits.
+// Returns the quotient directly and the remainder in *r
+static LIBDIVIDE_INLINE uint16_t libdivide_32_div_16_to_16(
+    uint16_t u1, uint16_t u0, uint16_t v, uint16_t *r) {
+    uint32_t n = ((uint32_t)u1 << 16) | u0;
+    uint16_t result = (uint16_t)(n / v);
+    *r = (uint16_t)(n - result * (uint32_t)v);
+    return result;
+}
+
+// libdivide_64_div_32_to_32: divides a 64-bit uint {u1, u0} by a 32-bit
+// uint {v}. The result must fit in 32 bits.
+// Returns the quotient directly and the remainder in *r
+static LIBDIVIDE_INLINE uint32_t libdivide_64_div_32_to_32(
+    uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) {
+#if (defined(LIBDIVIDE_i386) || defined(LIBDIVIDE_X86_64)) && defined(LIBDIVIDE_GCC_STYLE_ASM)
+    uint32_t result;
+    __asm__("divl %[v]" : "=a"(result), "=d"(*r) : [v] "r"(v), "a"(u0), "d"(u1));
+    return result;
+#else
+    uint64_t n = ((uint64_t)u1 << 32) | u0;
+    uint32_t result = (uint32_t)(n / v);
+    *r = (uint32_t)(n - result * (uint64_t)v);
+    return result;
+#endif
+}
+
+// libdivide_128_div_64_to_64: divides a 128-bit uint {numhi, numlo} by a 64-bit uint {den}. The
+// result must fit in 64 bits. Returns the quotient directly and the remainder in *r
+static LIBDIVIDE_INLINE uint64_t libdivide_128_div_64_to_64(
+    uint64_t numhi, uint64_t numlo, uint64_t den, uint64_t *r) {
+    // N.B. resist the temptation to use __uint128_t here.
+    // In LLVM compiler-rt, it performs a 128/128 -> 128 division which is many times slower than
+    // necessary. In gcc it's better but still slower than the divlu implementation, perhaps because
+    // it's not LIBDIVIDE_INLINEd.
+#if defined(LIBDIVIDE_X86_64) && defined(LIBDIVIDE_GCC_STYLE_ASM)
+    uint64_t result;
+    __asm__("divq %[v]" : "=a"(result), "=d"(*r) : [v] "r"(den), "a"(numlo), "d"(numhi));
+    return result;
+#else
+    // We work in base 2**32.
+    // A uint32 holds a single digit. A uint64 holds two digits.
+    // Our numerator is conceptually [num3, num2, num1, num0].
+    // Our denominator is [den1, den0].
+    const uint64_t b = ((uint64_t)1 << 32);
+
+    // The high and low digits of our computed quotient.
+    uint32_t q1;
+    uint32_t q0;
+
+    // The normalization shift factor.
+    int shift;
+
+    // The high and low digits of our denominator (after normalizing).
+    // Also the low 2 digits of our numerator (after normalizing).
+    uint32_t den1;
+    uint32_t den0;
+    uint32_t num1;
+    uint32_t num0;
+
+    // A partial remainder.
+    uint64_t rem;
+
+    // The estimated quotient, and its corresponding remainder (unrelated to true remainder).
+    uint64_t qhat;
+    uint64_t rhat;
+
+    // Variables used to correct the estimated quotient.
+    uint64_t c1;
+    uint64_t c2;
+
+    // Check for overflow and divide by 0.
+    if (numhi >= den) {
+        if (r != NULL) *r = ~0ull;
+        return ~0ull;
+    }
+
+    // Determine the normalization factor. We multiply den by this, so that its leading digit is at
+    // least half b. In binary this means just shifting left by the number of leading zeros, so that
+    // there's a 1 in the MSB.
+    // We also shift numer by the same amount. This cannot overflow because numhi < den.
+    // The expression (-shift & 63) is the same as (64 - shift), except it avoids the UB of shifting
+    // by 64. The funny bitwise 'and' ensures that numlo does not get shifted into numhi if shift is
+    // 0. clang 11 has an x86 codegen bug here: see LLVM bug 50118. The sequence below avoids it.
+    shift = libdivide_count_leading_zeros64(den);
+    den <<= shift;
+    numhi <<= shift;
+    numhi |= (numlo >> (-shift & 63)) & (-(int64_t)shift >> 63);
+    numlo <<= shift;
+
+    // Extract the low digits of the numerator and both digits of the denominator.
+    num1 = (uint32_t)(numlo >> 32);
+    num0 = (uint32_t)(numlo & 0xFFFFFFFFu);
+    den1 = (uint32_t)(den >> 32);
+    den0 = (uint32_t)(den & 0xFFFFFFFFu);
+
+    // We wish to compute q1 = [n3 n2 n1] / [d1 d0].
+    // Estimate q1 as [n3 n2] / [d1], and then correct it.
+    // Note while qhat may be 2 digits, q1 is always 1 digit.
+    qhat = numhi / den1;
+    rhat = numhi % den1;
+    c1 = qhat * den0;
+    c2 = rhat * b + num1;
+    if (c1 > c2) qhat -= (c1 - c2 > den) ? 2 : 1;
+    q1 = (uint32_t)qhat;
+
+    // Compute the true (partial) remainder.
+    rem = numhi * b + num1 - q1 * den;
+
+    // We wish to compute q0 = [rem1 rem0 n0] / [d1 d0].
+    // Estimate q0 as [rem1 rem0] / [d1] and correct it.
+    qhat = rem / den1;
+    rhat = rem % den1;
+    c1 = qhat * den0;
+    c2 = rhat * b + num0;
+    if (c1 > c2) qhat -= (c1 - c2 > den) ? 2 : 1;
+    q0 = (uint32_t)qhat;
+
+    // Return remainder if requested.
+    if (r != NULL) *r = (rem * b + num0 - q0 * den) >> shift;
+    return ((uint64_t)q1 << 32) | q0;
+#endif
+}
+
+#if !(defined(HAS_INT128_T) && \
+      defined(HAS_INT128_DIV))
+
+// Bitshift a u128 in place, left (signed_shift > 0) or right (signed_shift < 0)
+static LIBDIVIDE_INLINE void libdivide_u128_shift(
+    uint64_t *u1, uint64_t *u0, int32_t signed_shift) {
+    if (signed_shift > 0) {
+        uint32_t shift = signed_shift;
+        *u1 <<= shift;
+        *u1 |= *u0 >> (64 - shift);
+        *u0 <<= shift;
+    } else if (signed_shift < 0) {
+        uint32_t shift = -signed_shift;
+        *u0 >>= shift;
+        *u0 |= *u1 << (64 - shift);
+        *u1 >>= shift;
+    }
+}
+
+#endif
+
+// Computes a 128 / 128 -> 64 bit division, with a 128 bit remainder.
+static LIBDIVIDE_INLINE uint64_t libdivide_128_div_128_to_64(
+    uint64_t u_hi, uint64_t u_lo, uint64_t v_hi, uint64_t v_lo, uint64_t *r_hi, uint64_t *r_lo) {
+#if defined(HAS_INT128_T) && defined(HAS_INT128_DIV)
+    __uint128_t ufull = u_hi;
+    __uint128_t vfull = v_hi;
+    ufull = (ufull << 64) | u_lo;
+    vfull = (vfull << 64) | v_lo;
+    uint64_t res = (uint64_t)(ufull / vfull);
+    __uint128_t remainder = ufull - (vfull * res);
+    *r_lo = (uint64_t)remainder;
+    *r_hi = (uint64_t)(remainder >> 64);
+    return res;
+#else
+    // Adapted from "Unsigned Doubleword Division" in Hacker's Delight
+    // We want to compute u / v
+    typedef struct {
+        uint64_t hi;
+        uint64_t lo;
+    } u128_t;
+    u128_t u = {u_hi, u_lo};
+    u128_t v = {v_hi, v_lo};
+
+    if (v.hi == 0) {
+        // divisor v is a 64 bit value, so we just need one 128/64 division
+        // Note that we are simpler than Hacker's Delight here, because we know
+        // the quotient fits in 64 bits whereas Hacker's Delight demands a full
+        // 128 bit quotient
+        *r_hi = 0;
+        return libdivide_128_div_64_to_64(u.hi, u.lo, v.lo, r_lo);
+    }
+    // Here v >= 2**64
+    // We know that v.hi != 0, so count leading zeros is OK
+    // We have 0 <= n <= 63
+    uint32_t n = libdivide_count_leading_zeros64(v.hi);
+
+    // Normalize the divisor so its MSB is 1
+    u128_t v1t = v;
+    libdivide_u128_shift(&v1t.hi, &v1t.lo, n);
+    uint64_t v1 = v1t.hi;  // i.e. v1 = v1t >> 64
+
+    // To ensure no overflow
+    u128_t u1 = u;
+    libdivide_u128_shift(&u1.hi, &u1.lo, -1);
+
+    // Get quotient from divide unsigned insn.
+    uint64_t rem_ignored;
+    uint64_t q1 = libdivide_128_div_64_to_64(u1.hi, u1.lo, v1, &rem_ignored);
+
+    // Undo normalization and division of u by 2.
+    u128_t q0 = {0, q1};
+    libdivide_u128_shift(&q0.hi, &q0.lo, n);
+    libdivide_u128_shift(&q0.hi, &q0.lo, -63);
+
+    // Make q0 correct or too small by 1
+    // Equivalent to `if (q0 != 0) q0 = q0 - 1;`
+    if (q0.hi != 0 || q0.lo != 0) {
+        q0.hi -= (q0.lo == 0);  // borrow
+        q0.lo -= 1;
+    }
+
+    // Now q0 is correct.
+    // Compute q0 * v as q0v
+    // = (q0.hi << 64 + q0.lo) * (v.hi << 64 + v.lo)
+    // = (q0.hi * v.hi << 128) + (q0.hi * v.lo << 64) +
+    //   (q0.lo * v.hi <<  64) + q0.lo * v.lo)
+    // Each term is 128 bit
+    // High half of full product (upper 128 bits!) are dropped
+    u128_t q0v = {0, 0};
+    q0v.hi = q0.hi * v.lo + q0.lo * v.hi + libdivide_mullhi_u64(q0.lo, v.lo);
+    q0v.lo = q0.lo * v.lo;
+
+    // Compute u - q0v as u_q0v
+    // This is the remainder
+    u128_t u_q0v = u;
+    u_q0v.hi -= q0v.hi + (u.lo < q0v.lo);  // second term is borrow
+    u_q0v.lo -= q0v.lo;
+
+    // Check if u_q0v >= v
+    // This checks if our remainder is larger than the divisor
+    if ((u_q0v.hi > v.hi) || (u_q0v.hi == v.hi && u_q0v.lo >= v.lo)) {
+        // Increment q0
+        q0.lo += 1;
+        q0.hi += (q0.lo == 0);  // carry
+
+        // Subtract v from remainder
+        u_q0v.hi -= v.hi + (u_q0v.lo < v.lo);
+        u_q0v.lo -= v.lo;
+    }
+
+    *r_hi = u_q0v.hi;
+    *r_lo = u_q0v.lo;
+
+    LIBDIVIDE_ASSERT(q0.hi == 0);
+    return q0.lo;
+#endif
+}
+
+////////// UINT16
+
+static LIBDIVIDE_INLINE struct libdivide_u16_t libdivide_internal_u16_gen(
+    uint16_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_u16_t result;
+    uint8_t floor_log_2_d = (uint8_t)(15 - libdivide_count_leading_zeros16(d));
+
+    // Power of 2
+    if ((d & (d - 1)) == 0) {
+        // We need to subtract 1 from the shift value in case of an unsigned
+        // branchfree divider because there is a hardcoded right shift by 1
+        // in its division algorithm. Because of this we also need to add back
+        // 1 in its recovery algorithm.
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d - (branchfree != 0));
+    } else {
+        uint8_t more;
+        uint16_t rem, proposed_m;
+        proposed_m = libdivide_32_div_16_to_16((uint16_t)1 << floor_log_2_d, 0, d, &rem);
+
+        LIBDIVIDE_ASSERT(rem > 0 && rem < d);
+        const uint16_t e = d - rem;
+
+        // This power works if e < 2**floor_log_2_d.
+        if (!branchfree && (e < ((uint16_t)1 << floor_log_2_d))) {
+            // This power works
+            more = floor_log_2_d;
+        } else {
+            // We have to use the general 17-bit algorithm.  We need to compute
+            // (2**power) / d. However, we already have (2**(power-1))/d and
+            // its remainder.  By doubling both, and then correcting the
+            // remainder, we can compute the larger division.
+            // don't care about overflow here - in fact, we expect it
+            proposed_m += proposed_m;
+            const uint16_t twice_rem = rem + rem;
+            if (twice_rem >= d || twice_rem < rem) proposed_m += 1;
+            more = floor_log_2_d | LIBDIVIDE_ADD_MARKER;
+        }
+        result.magic = 1 + proposed_m;
+        result.more = more;
+        // result.more's shift should in general be ceil_log_2_d. But if we
+        // used the smaller power, we subtract one from the shift because we're
+        // using the smaller power. If we're using the larger power, we
+        // subtract one from the shift because it's taken care of by the add
+        // indicator. So floor_log_2_d happens to be correct in both cases.
+    }
+    return result;
+}
+
+struct libdivide_u16_t libdivide_u16_gen(uint16_t d) {
+    return libdivide_internal_u16_gen(d, 0);
+}
+
+struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen(uint16_t d) {
+    if (d == 1) {
+        LIBDIVIDE_ERROR("branchfree divider must be != 1");
+    }
+    struct libdivide_u16_t tmp = libdivide_internal_u16_gen(d, 1);
+    struct libdivide_u16_branchfree_t ret = {
+        tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_16_SHIFT_MASK)};
+    return ret;
+}
+
+// The original libdivide_u16_do takes a const pointer. However, this cannot be used
+// with a compile time constant libdivide_u16_t: it will generate a warning about
+// taking the address of a temporary. Hence this overload.
+uint16_t libdivide_u16_do_raw(uint16_t numer, uint16_t magic, uint8_t more) {
+    if (!magic) {
+        return numer >> more;
+    } else {
+        uint16_t q = libdivide_mullhi_u16(magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            uint16_t t = ((numer - q) >> 1) + q;
+            return t >> (more & LIBDIVIDE_16_SHIFT_MASK);
+        } else {
+            // All upper bits are 0,
+            // don't need to mask them off.
+            return q >> more;
+        }
+    }
+}
+
+uint16_t libdivide_u16_do(uint16_t numer, const struct libdivide_u16_t *denom) {
+    return libdivide_u16_do_raw(numer, denom->magic, denom->more);
+}
+
+uint16_t libdivide_u16_branchfree_do(
+    uint16_t numer, const struct libdivide_u16_branchfree_t *denom) {
+    uint16_t q = libdivide_mullhi_u16(denom->magic, numer);
+    uint16_t t = ((numer - q) >> 1) + q;
+    return t >> denom->more;
+}
+
+uint16_t libdivide_u16_recover(const struct libdivide_u16_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint16_t)1 << shift;
+    } else if (!(more & LIBDIVIDE_ADD_MARKER)) {
+        // We compute q = n/d = n*m / 2^(16 + shift)
+        // Therefore we have d = 2^(16 + shift) / m
+        // We need to ceil it.
+        // We know d is not a power of 2, so m is not a power of 2,
+        // so we can just add 1 to the floor
+        uint16_t hi_dividend = (uint16_t)1 << shift;
+        uint16_t rem_ignored;
+        return 1 + libdivide_32_div_16_to_16(hi_dividend, 0, denom->magic, &rem_ignored);
+    } else {
+        // Here we wish to compute d = 2^(16+shift+1)/(m+2^16).
+        // Notice (m + 2^16) is a 17 bit number. Use 32 bit division for now
+        // Also note that shift may be as high as 15, so shift + 1 will
+        // overflow. So we have to compute it as 2^(16+shift)/(m+2^16), and
+        // then double the quotient and remainder.
+        uint32_t half_n = (uint32_t)1 << (16 + shift);
+        uint32_t d = ((uint32_t)1 << 16) | denom->magic;
+        // Note that the quotient is guaranteed <= 16 bits, but the remainder
+        // may need 17!
+        uint16_t half_q = (uint16_t)(half_n / d);
+        uint32_t rem = half_n % d;
+        // We computed 2^(16+shift)/(m+2^16)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 17 bits
+        uint16_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+uint16_t libdivide_u16_branchfree_recover(const struct libdivide_u16_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint16_t)1 << (shift + 1);
+    } else {
+        // Here we wish to compute d = 2^(16+shift+1)/(m+2^16).
+        // Notice (m + 2^16) is a 17 bit number. Use 32 bit division for now
+        // Also note that shift may be as high as 15, so shift + 1 will
+        // overflow. So we have to compute it as 2^(16+shift)/(m+2^16), and
+        // then double the quotient and remainder.
+        uint32_t half_n = (uint32_t)1 << (16 + shift);
+        uint32_t d = ((uint32_t)1 << 16) | denom->magic;
+        // Note that the quotient is guaranteed <= 16 bits, but the remainder
+        // may need 17!
+        uint16_t half_q = (uint16_t)(half_n / d);
+        uint32_t rem = half_n % d;
+        // We computed 2^(16+shift)/(m+2^16)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
+        uint16_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+////////// UINT32
+
+static LIBDIVIDE_INLINE struct libdivide_u32_t libdivide_internal_u32_gen(
+    uint32_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_u32_t result;
+    uint32_t floor_log_2_d = 31 - libdivide_count_leading_zeros32(d);
+
+    // Power of 2
+    if ((d & (d - 1)) == 0) {
+        // We need to subtract 1 from the shift value in case of an unsigned
+        // branchfree divider because there is a hardcoded right shift by 1
+        // in its division algorithm. Because of this we also need to add back
+        // 1 in its recovery algorithm.
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d - (branchfree != 0));
+    } else {
+        uint8_t more;
+        uint32_t rem, proposed_m;
+        proposed_m = libdivide_64_div_32_to_32((uint32_t)1 << floor_log_2_d, 0, d, &rem);
+
+        LIBDIVIDE_ASSERT(rem > 0 && rem < d);
+        const uint32_t e = d - rem;
+
+        // This power works if e < 2**floor_log_2_d.
+        if (!branchfree && (e < ((uint32_t)1 << floor_log_2_d))) {
+            // This power works
+            more = (uint8_t)floor_log_2_d;
+        } else {
+            // We have to use the general 33-bit algorithm.  We need to compute
+            // (2**power) / d. However, we already have (2**(power-1))/d and
+            // its remainder.  By doubling both, and then correcting the
+            // remainder, we can compute the larger division.
+            // don't care about overflow here - in fact, we expect it
+            proposed_m += proposed_m;
+            const uint32_t twice_rem = rem + rem;
+            if (twice_rem >= d || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+        result.magic = 1 + proposed_m;
+        result.more = more;
+        // result.more's shift should in general be ceil_log_2_d. But if we
+        // used the smaller power, we subtract one from the shift because we're
+        // using the smaller power. If we're using the larger power, we
+        // subtract one from the shift because it's taken care of by the add
+        // indicator. So floor_log_2_d happens to be correct in both cases.
+    }
+    return result;
+}
+
+struct libdivide_u32_t libdivide_u32_gen(uint32_t d) {
+    return libdivide_internal_u32_gen(d, 0);
+}
+
+struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) {
+    if (d == 1) {
+        LIBDIVIDE_ERROR("branchfree divider must be != 1");
+    }
+    struct libdivide_u32_t tmp = libdivide_internal_u32_gen(d, 1);
+    struct libdivide_u32_branchfree_t ret = {
+        tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_32_SHIFT_MASK)};
+    return ret;
+}
+
+uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return numer >> more;
+    } else {
+        uint32_t q = libdivide_mullhi_u32(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            uint32_t t = ((numer - q) >> 1) + q;
+            return t >> (more & LIBDIVIDE_32_SHIFT_MASK);
+        } else {
+            // All upper bits are 0,
+            // don't need to mask them off.
+            return q >> more;
+        }
+    }
+}
+
+uint32_t libdivide_u32_branchfree_do(
+    uint32_t numer, const struct libdivide_u32_branchfree_t *denom) {
+    uint32_t q = libdivide_mullhi_u32(denom->magic, numer);
+    uint32_t t = ((numer - q) >> 1) + q;
+    return t >> denom->more;
+}
+
+uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint32_t)1 << shift;
+    } else if (!(more & LIBDIVIDE_ADD_MARKER)) {
+        // We compute q = n/d = n*m / 2^(32 + shift)
+        // Therefore we have d = 2^(32 + shift) / m
+        // We need to ceil it.
+        // We know d is not a power of 2, so m is not a power of 2,
+        // so we can just add 1 to the floor
+        uint32_t hi_dividend = (uint32_t)1 << shift;
+        uint32_t rem_ignored;
+        return 1 + libdivide_64_div_32_to_32(hi_dividend, 0, denom->magic, &rem_ignored);
+    } else {
+        // Here we wish to compute d = 2^(32+shift+1)/(m+2^32).
+        // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now
+        // Also note that shift may be as high as 31, so shift + 1 will
+        // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and
+        // then double the quotient and remainder.
+        uint64_t half_n = (uint64_t)1 << (32 + shift);
+        uint64_t d = ((uint64_t)1 << 32) | denom->magic;
+        // Note that the quotient is guaranteed <= 32 bits, but the remainder
+        // may need 33!
+        uint32_t half_q = (uint32_t)(half_n / d);
+        uint64_t rem = half_n % d;
+        // We computed 2^(32+shift)/(m+2^32)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
+        uint32_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint32_t)1 << (shift + 1);
+    } else {
+        // Here we wish to compute d = 2^(32+shift+1)/(m+2^32).
+        // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now
+        // Also note that shift may be as high as 31, so shift + 1 will
+        // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and
+        // then double the quotient and remainder.
+        uint64_t half_n = (uint64_t)1 << (32 + shift);
+        uint64_t d = ((uint64_t)1 << 32) | denom->magic;
+        // Note that the quotient is guaranteed <= 32 bits, but the remainder
+        // may need 33!
+        uint32_t half_q = (uint32_t)(half_n / d);
+        uint64_t rem = half_n % d;
+        // We computed 2^(32+shift)/(m+2^32)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
+        uint32_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+/////////// UINT64
+
+static LIBDIVIDE_INLINE struct libdivide_u64_t libdivide_internal_u64_gen(
+    uint64_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_u64_t result;
+    uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(d);
+
+    // Power of 2
+    if ((d & (d - 1)) == 0) {
+        // We need to subtract 1 from the shift value in case of an unsigned
+        // branchfree divider because there is a hardcoded right shift by 1
+        // in its division algorithm. Because of this we also need to add back
+        // 1 in its recovery algorithm.
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d - (branchfree != 0));
+    } else {
+        uint64_t proposed_m, rem;
+        uint8_t more;
+        // (1 << (64 + floor_log_2_d)) / d
+        proposed_m = libdivide_128_div_64_to_64((uint64_t)1 << floor_log_2_d, 0, d, &rem);
+
+        LIBDIVIDE_ASSERT(rem > 0 && rem < d);
+        const uint64_t e = d - rem;
+
+        // This power works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint64_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)floor_log_2_d;
+        } else {
+            // We have to use the general 65-bit algorithm.  We need to compute
+            // (2**power) / d. However, we already have (2**(power-1))/d and
+            // its remainder. By doubling both, and then correcting the
+            // remainder, we can compute the larger division.
+            // don't care about overflow here - in fact, we expect it
+            proposed_m += proposed_m;
+            const uint64_t twice_rem = rem + rem;
+            if (twice_rem >= d || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+        result.magic = 1 + proposed_m;
+        result.more = more;
+        // result.more's shift should in general be ceil_log_2_d. But if we
+        // used the smaller power, we subtract one from the shift because we're
+        // using the smaller power. If we're using the larger power, we
+        // subtract one from the shift because it's taken care of by the add
+        // indicator. So floor_log_2_d happens to be correct in both cases,
+        // which is why we do it outside of the if statement.
+    }
+    return result;
+}
+
+struct libdivide_u64_t libdivide_u64_gen(uint64_t d) {
+    return libdivide_internal_u64_gen(d, 0);
+}
+
+struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) {
+    if (d == 1) {
+        LIBDIVIDE_ERROR("branchfree divider must be != 1");
+    }
+    struct libdivide_u64_t tmp = libdivide_internal_u64_gen(d, 1);
+    struct libdivide_u64_branchfree_t ret = {
+        tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_64_SHIFT_MASK)};
+    return ret;
+}
+
+uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return numer >> more;
+    } else {
+        uint64_t q = libdivide_mullhi_u64(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            uint64_t t = ((numer - q) >> 1) + q;
+            return t >> (more & LIBDIVIDE_64_SHIFT_MASK);
+        } else {
+            // All upper bits are 0,
+            // don't need to mask them off.
+            return q >> more;
+        }
+    }
+}
+
+uint64_t libdivide_u64_branchfree_do(
+    uint64_t numer, const struct libdivide_u64_branchfree_t *denom) {
+    uint64_t q = libdivide_mullhi_u64(denom->magic, numer);
+    uint64_t t = ((numer - q) >> 1) + q;
+    return t >> denom->more;
+}
+
+uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint64_t)1 << shift;
+    } else if (!(more & LIBDIVIDE_ADD_MARKER)) {
+        // We compute q = n/d = n*m / 2^(64 + shift)
+        // Therefore we have d = 2^(64 + shift) / m
+        // We need to ceil it.
+        // We know d is not a power of 2, so m is not a power of 2,
+        // so we can just add 1 to the floor
+        uint64_t hi_dividend = (uint64_t)1 << shift;
+        uint64_t rem_ignored;
+        return 1 + libdivide_128_div_64_to_64(hi_dividend, 0, denom->magic, &rem_ignored);
+    } else {
+        // Here we wish to compute d = 2^(64+shift+1)/(m+2^64).
+        // Notice (m + 2^64) is a 65 bit number. This gets hairy. See
+        // libdivide_u32_recover for more on what we do here.
+        // TODO: do something better than 128 bit math
+
+        // Full n is a (potentially) 129 bit value
+        // half_n is a 128 bit value
+        // Compute the hi half of half_n. Low half is 0.
+        uint64_t half_n_hi = (uint64_t)1 << shift, half_n_lo = 0;
+        // d is a 65 bit value. The high bit is always set to 1.
+        const uint64_t d_hi = 1, d_lo = denom->magic;
+        // Note that the quotient is guaranteed <= 64 bits,
+        // but the remainder may need 65!
+        uint64_t r_hi, r_lo;
+        uint64_t half_q =
+            libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
+        // We computed 2^(64+shift)/(m+2^64)
+        // Double the remainder ('dr') and check if that is larger than d
+        // Note that d is a 65 bit value, so r1 is small and so r1 + r1
+        // cannot overflow
+        uint64_t dr_lo = r_lo + r_lo;
+        uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo);  // last term is carry
+        int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo);
+        uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0);
+        return full_q + 1;
+    }
+}
+
+uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint64_t)1 << (shift + 1);
+    } else {
+        // Here we wish to compute d = 2^(64+shift+1)/(m+2^64).
+        // Notice (m + 2^64) is a 65 bit number. This gets hairy. See
+        // libdivide_u32_recover for more on what we do here.
+        // TODO: do something better than 128 bit math
+
+        // Full n is a (potentially) 129 bit value
+        // half_n is a 128 bit value
+        // Compute the hi half of half_n. Low half is 0.
+        uint64_t half_n_hi = (uint64_t)1 << shift, half_n_lo = 0;
+        // d is a 65 bit value. The high bit is always set to 1.
+        const uint64_t d_hi = 1, d_lo = denom->magic;
+        // Note that the quotient is guaranteed <= 64 bits,
+        // but the remainder may need 65!
+        uint64_t r_hi, r_lo;
+        uint64_t half_q =
+            libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
+        // We computed 2^(64+shift)/(m+2^64)
+        // Double the remainder ('dr') and check if that is larger than d
+        // Note that d is a 65 bit value, so r1 is small and so r1 + r1
+        // cannot overflow
+        uint64_t dr_lo = r_lo + r_lo;
+        uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo);  // last term is carry
+        int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo);
+        uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0);
+        return full_q + 1;
+    }
+}
+
+/////////// SINT16
+
+static LIBDIVIDE_INLINE struct libdivide_s16_t libdivide_internal_s16_gen(
+    int16_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_s16_t result;
+
+    // If d is a power of 2, or negative a power of 2, we have to use a shift.
+    // This is especially important because the magic algorithm fails for -1.
+    // To check if d is a power of 2 or its inverse, it suffices to check
+    // whether its absolute value has exactly one bit set. This works even for
+    // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set
+    // and is a power of 2.
+    uint16_t ud = (uint16_t)d;
+    uint16_t absD = (d < 0) ? -ud : ud;
+    uint16_t floor_log_2_d = 15 - libdivide_count_leading_zeros16(absD);
+    // check if exactly one bit is set,
+    // don't care if absD is 0 since that's divide by zero
+    if ((absD & (absD - 1)) == 0) {
+        // Branchfree and normal paths are exactly the same
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0));
+    } else {
+        LIBDIVIDE_ASSERT(floor_log_2_d >= 1);
+
+        uint8_t more;
+        // the dividend here is 2**(floor_log_2_d + 31), so the low 16 bit word
+        // is 0 and the high word is floor_log_2_d - 1
+        uint16_t rem, proposed_m;
+        proposed_m = libdivide_32_div_16_to_16((uint16_t)1 << (floor_log_2_d - 1), 0, absD, &rem);
+        const uint16_t e = absD - rem;
+
+        // We are going to start with a power of floor_log_2_d - 1.
+        // This works if works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint16_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)(floor_log_2_d - 1);
+        } else {
+            // We need to go one higher. This should not make proposed_m
+            // overflow, but it will make it negative when interpreted as an
+            // int16_t.
+            proposed_m += proposed_m;
+            const uint16_t twice_rem = rem + rem;
+            if (twice_rem >= absD || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+
+        proposed_m += 1;
+        int16_t magic = (int16_t)proposed_m;
+
+        // Mark if we are negative. Note we only negate the magic number in the
+        // branchfull case.
+        if (d < 0) {
+            more |= LIBDIVIDE_NEGATIVE_DIVISOR;
+            if (!branchfree) {
+                magic = -magic;
+            }
+        }
+
+        result.more = more;
+        result.magic = magic;
+    }
+    return result;
+}
+
+struct libdivide_s16_t libdivide_s16_gen(int16_t d) {
+    return libdivide_internal_s16_gen(d, 0);
+}
+
+struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen(int16_t d) {
+    struct libdivide_s16_t tmp = libdivide_internal_s16_gen(d, 1);
+    struct libdivide_s16_branchfree_t result = {tmp.magic, tmp.more};
+    return result;
+}
+
+// The original libdivide_s16_do takes a const pointer. However, this cannot be used
+// with a compile time constant libdivide_s16_t: it will generate a warning about
+// taking the address of a temporary. Hence this overload.
+int16_t libdivide_s16_do_raw(int16_t numer, int16_t magic, uint8_t more) {
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+
+    if (!magic) {
+        uint16_t sign = (int8_t)more >> 7;
+        uint16_t mask = ((uint16_t)1 << shift) - 1;
+        uint16_t uq = numer + ((numer >> 15) & mask);
+        int16_t q = (int16_t)uq;
+        q >>= shift;
+        q = (q ^ sign) - sign;
+        return q;
+    } else {
+        uint16_t uq = (uint16_t)libdivide_mullhi_s16(magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift and then sign extend
+            int16_t sign = (int8_t)more >> 7;
+            // q += (more < 0 ? -numer : numer)
+            // cast required to avoid UB
+            uq += ((uint16_t)numer ^ sign) - sign;
+        }
+        int16_t q = (int16_t)uq;
+        q >>= shift;
+        q += (q < 0);
+        return q;
+    }
+}
+
+int16_t libdivide_s16_do(int16_t numer, const struct libdivide_s16_t *denom) {
+    return libdivide_s16_do_raw(numer, denom->magic, denom->more);
+}
+
+int16_t libdivide_s16_branchfree_do(int16_t numer, const struct libdivide_s16_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    // must be arithmetic shift and then sign extend
+    int16_t sign = (int8_t)more >> 7;
+    int16_t magic = denom->magic;
+    int16_t q = libdivide_mullhi_s16(magic, numer);
+    q += numer;
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is a power of
+    // 2, or (2**shift) if it is not a power of 2
+    uint16_t is_power_of_2 = (magic == 0);
+    uint16_t q_sign = (uint16_t)(q >> 15);
+    q += q_sign & (((uint16_t)1 << shift) - is_power_of_2);
+
+    // Now arithmetic right shift
+    q >>= shift;
+    // Negate if needed
+    q = (q ^ sign) - sign;
+
+    return q;
+}
+
+int16_t libdivide_s16_recover(const struct libdivide_s16_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    if (!denom->magic) {
+        uint16_t absD = (uint16_t)1 << shift;
+        if (more & LIBDIVIDE_NEGATIVE_DIVISOR) {
+            absD = -absD;
+        }
+        return (int16_t)absD;
+    } else {
+        // Unsigned math is much easier
+        // We negate the magic number only in the branchfull case, and we don't
+        // know which case we're in. However we have enough information to
+        // determine the correct sign of the magic number. The divisor was
+        // negative if LIBDIVIDE_NEGATIVE_DIVISOR is set. If ADD_MARKER is set,
+        // the magic number's sign is opposite that of the divisor.
+        // We want to compute the positive magic number.
+        int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
+        int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
+
+        // Handle the power of 2 case (including branchfree)
+        if (denom->magic == 0) {
+            int16_t result = (uint16_t)1 << shift;
+            return negative_divisor ? -result : result;
+        }
+
+        uint16_t d = (uint16_t)(magic_was_negated ? -denom->magic : denom->magic);
+        uint32_t n = (uint32_t)1 << (16 + shift);  // this shift cannot exceed 30
+        uint16_t q = (uint16_t)(n / d);
+        int16_t result = (int16_t)q;
+        result += 1;
+        return negative_divisor ? -result : result;
+    }
+}
+
+int16_t libdivide_s16_branchfree_recover(const struct libdivide_s16_branchfree_t *denom) {
+    return libdivide_s16_recover((const struct libdivide_s16_t *)denom);
+}
+
+/////////// SINT32
+
+static LIBDIVIDE_INLINE struct libdivide_s32_t libdivide_internal_s32_gen(
+    int32_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_s32_t result;
+
+    // If d is a power of 2, or negative a power of 2, we have to use a shift.
+    // This is especially important because the magic algorithm fails for -1.
+    // To check if d is a power of 2 or its inverse, it suffices to check
+    // whether its absolute value has exactly one bit set. This works even for
+    // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set
+    // and is a power of 2.
+    uint32_t ud = (uint32_t)d;
+    uint32_t absD = (d < 0) ? -ud : ud;
+    uint32_t floor_log_2_d = 31 - libdivide_count_leading_zeros32(absD);
+    // check if exactly one bit is set,
+    // don't care if absD is 0 since that's divide by zero
+    if ((absD & (absD - 1)) == 0) {
+        // Branchfree and normal paths are exactly the same
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0));
+    } else {
+        LIBDIVIDE_ASSERT(floor_log_2_d >= 1);
+
+        uint8_t more;
+        // the dividend here is 2**(floor_log_2_d + 31), so the low 32 bit word
+        // is 0 and the high word is floor_log_2_d - 1
+        uint32_t rem, proposed_m;
+        proposed_m = libdivide_64_div_32_to_32((uint32_t)1 << (floor_log_2_d - 1), 0, absD, &rem);
+        const uint32_t e = absD - rem;
+
+        // We are going to start with a power of floor_log_2_d - 1.
+        // This works if works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint32_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)(floor_log_2_d - 1);
+        } else {
+            // We need to go one higher. This should not make proposed_m
+            // overflow, but it will make it negative when interpreted as an
+            // int32_t.
+            proposed_m += proposed_m;
+            const uint32_t twice_rem = rem + rem;
+            if (twice_rem >= absD || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+
+        proposed_m += 1;
+        int32_t magic = (int32_t)proposed_m;
+
+        // Mark if we are negative. Note we only negate the magic number in the
+        // branchfull case.
+        if (d < 0) {
+            more |= LIBDIVIDE_NEGATIVE_DIVISOR;
+            if (!branchfree) {
+                magic = -magic;
+            }
+        }
+
+        result.more = more;
+        result.magic = magic;
+    }
+    return result;
+}
+
+struct libdivide_s32_t libdivide_s32_gen(int32_t d) {
+    return libdivide_internal_s32_gen(d, 0);
+}
+
+struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) {
+    struct libdivide_s32_t tmp = libdivide_internal_s32_gen(d, 1);
+    struct libdivide_s32_branchfree_t result = {tmp.magic, tmp.more};
+    return result;
+}
+
+int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+
+    if (!denom->magic) {
+        uint32_t sign = (int8_t)more >> 7;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        uint32_t uq = numer + ((numer >> 31) & mask);
+        int32_t q = (int32_t)uq;
+        q >>= shift;
+        q = (q ^ sign) - sign;
+        return q;
+    } else {
+        uint32_t uq = (uint32_t)libdivide_mullhi_s32(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift and then sign extend
+            int32_t sign = (int8_t)more >> 7;
+            // q += (more < 0 ? -numer : numer)
+            // cast required to avoid UB
+            uq += ((uint32_t)numer ^ sign) - sign;
+        }
+        int32_t q = (int32_t)uq;
+        q >>= shift;
+        q += (q < 0);
+        return q;
+    }
+}
+
+int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift and then sign extend
+    int32_t sign = (int8_t)more >> 7;
+    int32_t magic = denom->magic;
+    int32_t q = libdivide_mullhi_s32(magic, numer);
+    q += numer;
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is a power of
+    // 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    uint32_t q_sign = (uint32_t)(q >> 31);
+    q += q_sign & (((uint32_t)1 << shift) - is_power_of_2);
+
+    // Now arithmetic right shift
+    q >>= shift;
+    // Negate if needed
+    q = (q ^ sign) - sign;
+
+    return q;
+}
+
+int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    if (!denom->magic) {
+        uint32_t absD = (uint32_t)1 << shift;
+        if (more & LIBDIVIDE_NEGATIVE_DIVISOR) {
+            absD = -absD;
+        }
+        return (int32_t)absD;
+    } else {
+        // Unsigned math is much easier
+        // We negate the magic number only in the branchfull case, and we don't
+        // know which case we're in. However we have enough information to
+        // determine the correct sign of the magic number. The divisor was
+        // negative if LIBDIVIDE_NEGATIVE_DIVISOR is set. If ADD_MARKER is set,
+        // the magic number's sign is opposite that of the divisor.
+        // We want to compute the positive magic number.
+        int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
+        int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
+
+        // Handle the power of 2 case (including branchfree)
+        if (denom->magic == 0) {
+            int32_t result = (uint32_t)1 << shift;
+            return negative_divisor ? -result : result;
+        }
+
+        uint32_t d = (uint32_t)(magic_was_negated ? -denom->magic : denom->magic);
+        uint64_t n = (uint64_t)1 << (32 + shift);  // this shift cannot exceed 30
+        uint32_t q = (uint32_t)(n / d);
+        int32_t result = (int32_t)q;
+        result += 1;
+        return negative_divisor ? -result : result;
+    }
+}
+
+int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom) {
+    return libdivide_s32_recover((const struct libdivide_s32_t *)denom);
+}
+
+///////////// SINT64
+
+static LIBDIVIDE_INLINE struct libdivide_s64_t libdivide_internal_s64_gen(
+    int64_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_s64_t result;
+
+    // If d is a power of 2, or negative a power of 2, we have to use a shift.
+    // This is especially important because the magic algorithm fails for -1.
+    // To check if d is a power of 2 or its inverse, it suffices to check
+    // whether its absolute value has exactly one bit set.  This works even for
+    // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set
+    // and is a power of 2.
+    uint64_t ud = (uint64_t)d;
+    uint64_t absD = (d < 0) ? -ud : ud;
+    uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(absD);
+    // check if exactly one bit is set,
+    // don't care if absD is 0 since that's divide by zero
+    if ((absD & (absD - 1)) == 0) {
+        // Branchfree and non-branchfree cases are the same
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0));
+    } else {
+        // the dividend here is 2**(floor_log_2_d + 63), so the low 64 bit word
+        // is 0 and the high word is floor_log_2_d - 1
+        uint8_t more;
+        uint64_t rem, proposed_m;
+        proposed_m = libdivide_128_div_64_to_64((uint64_t)1 << (floor_log_2_d - 1), 0, absD, &rem);
+        const uint64_t e = absD - rem;
+
+        // We are going to start with a power of floor_log_2_d - 1.
+        // This works if works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint64_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)(floor_log_2_d - 1);
+        } else {
+            // We need to go one higher. This should not make proposed_m
+            // overflow, but it will make it negative when interpreted as an
+            // int32_t.
+            proposed_m += proposed_m;
+            const uint64_t twice_rem = rem + rem;
+            if (twice_rem >= absD || twice_rem < rem) proposed_m += 1;
+            // note that we only set the LIBDIVIDE_NEGATIVE_DIVISOR bit if we
+            // also set ADD_MARKER this is an annoying optimization that
+            // enables algorithm #4 to avoid the mask. However we always set it
+            // in the branchfree case
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+        proposed_m += 1;
+        int64_t magic = (int64_t)proposed_m;
+
+        // Mark if we are negative
+        if (d < 0) {
+            more |= LIBDIVIDE_NEGATIVE_DIVISOR;
+            if (!branchfree) {
+                magic = -magic;
+            }
+        }
+
+        result.more = more;
+        result.magic = magic;
+    }
+    return result;
+}
+
+struct libdivide_s64_t libdivide_s64_gen(int64_t d) {
+    return libdivide_internal_s64_gen(d, 0);
+}
+
+struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d) {
+    struct libdivide_s64_t tmp = libdivide_internal_s64_gen(d, 1);
+    struct libdivide_s64_branchfree_t ret = {tmp.magic, tmp.more};
+    return ret;
+}
+
+int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+
+    if (!denom->magic) {  // shift path
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        uint64_t uq = numer + ((numer >> 63) & mask);
+        int64_t q = (int64_t)uq;
+        q >>= shift;
+        // must be arithmetic shift and then sign-extend
+        int64_t sign = (int8_t)more >> 7;
+        q = (q ^ sign) - sign;
+        return q;
+    } else {
+        uint64_t uq = (uint64_t)libdivide_mullhi_s64(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift and then sign extend
+            int64_t sign = (int8_t)more >> 7;
+            // q += (more < 0 ? -numer : numer)
+            // cast required to avoid UB
+            uq += ((uint64_t)numer ^ sign) - sign;
+        }
+        int64_t q = (int64_t)uq;
+        q >>= shift;
+        q += (q < 0);
+        return q;
+    }
+}
+
+int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift and then sign extend
+    int64_t sign = (int8_t)more >> 7;
+    int64_t magic = denom->magic;
+    int64_t q = libdivide_mullhi_s64(magic, numer);
+    q += numer;
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is a power of
+    // 2, or (2**shift) if it is not a power of 2.
+    uint64_t is_power_of_2 = (magic == 0);
+    uint64_t q_sign = (uint64_t)(q >> 63);
+    q += q_sign & (((uint64_t)1 << shift) - is_power_of_2);
+
+    // Arithmetic right shift
+    q >>= shift;
+    // Negate if needed
+    q = (q ^ sign) - sign;
+
+    return q;
+}
+
+int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    if (denom->magic == 0) {  // shift path
+        uint64_t absD = (uint64_t)1 << shift;
+        if (more & LIBDIVIDE_NEGATIVE_DIVISOR) {
+            absD = -absD;
+        }
+        return (int64_t)absD;
+    } else {
+        // Unsigned math is much easier
+        int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
+        int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
+
+        uint64_t d = (uint64_t)(magic_was_negated ? -denom->magic : denom->magic);
+        uint64_t n_hi = (uint64_t)1 << shift, n_lo = 0;
+        uint64_t rem_ignored;
+        uint64_t q = libdivide_128_div_64_to_64(n_hi, n_lo, d, &rem_ignored);
+        int64_t result = (int64_t)(q + 1);
+        if (negative_divisor) {
+            result = -result;
+        }
+        return result;
+    }
+}
+
+int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom) {
+    return libdivide_s64_recover((const struct libdivide_s64_t *)denom);
+}
+
+// Simplest possible vector type division: treat the vector type as an array
+// of underlying native type.
+//
+// Use a union to read a vector via pointer-to-integer, without violating strict
+// aliasing.
+#define SIMPLE_VECTOR_DIVISION(IntT, VecT, Algo)                          \
+    const size_t count = sizeof(VecT) / sizeof(IntT);                     \
+    union type_pun_vec {                                                  \
+        VecT vec;                                                         \
+        IntT arr[sizeof(VecT) / sizeof(IntT)];                            \
+    };                                                                    \
+    union type_pun_vec result;                                            \
+    union type_pun_vec input;                                             \
+    input.vec = numers;                                                   \
+    for (size_t loop = 0; loop < count; ++loop) {                         \
+        result.arr[loop] = libdivide_##Algo##_do(input.arr[loop], denom); \
+    }                                                                     \
+    return result.vec;
+
+#if defined(LIBDIVIDE_NEON)
+
+static LIBDIVIDE_INLINE uint16x8_t libdivide_u16_do_vec128(
+    uint16x8_t numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE int16x8_t libdivide_s16_do_vec128(
+    int16x8_t numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE uint32x4_t libdivide_u32_do_vec128(
+    uint32x4_t numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE int32x4_t libdivide_s32_do_vec128(
+    int32x4_t numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE uint64x2_t libdivide_u64_do_vec128(
+    uint64x2_t numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_do_vec128(
+    int64x2_t numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE uint16x8_t libdivide_u16_branchfree_do_vec128(
+    uint16x8_t numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE int16x8_t libdivide_s16_branchfree_do_vec128(
+    int16x8_t numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint32x4_t libdivide_u32_branchfree_do_vec128(
+    uint32x4_t numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE int32x4_t libdivide_s32_branchfree_do_vec128(
+    int32x4_t numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint64x2_t libdivide_u64_branchfree_do_vec128(
+    uint64x2_t numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_branchfree_do_vec128(
+    int64x2_t numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+// Logical right shift by runtime value.
+// NEON implements right shift as left shits by negative values.
+static LIBDIVIDE_INLINE uint32x4_t libdivide_u32_neon_srl(uint32x4_t v, uint8_t amt) {
+    int32_t wamt = (int32_t)(amt);
+    return vshlq_u32(v, vdupq_n_s32(-wamt));
+}
+
+static LIBDIVIDE_INLINE uint64x2_t libdivide_u64_neon_srl(uint64x2_t v, uint8_t amt) {
+    int64_t wamt = (int64_t)(amt);
+    return vshlq_u64(v, vdupq_n_s64(-wamt));
+}
+
+// Arithmetic right shift by runtime value.
+static LIBDIVIDE_INLINE int32x4_t libdivide_s32_neon_sra(int32x4_t v, uint8_t amt) {
+    int32_t wamt = (int32_t)(amt);
+    return vshlq_s32(v, vdupq_n_s32(-wamt));
+}
+
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_neon_sra(int64x2_t v, uint8_t amt) {
+    int64_t wamt = (int64_t)(amt);
+    return vshlq_s64(v, vdupq_n_s64(-wamt));
+}
+
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_signbits(int64x2_t v) { return vshrq_n_s64(v, 63); }
+
+static LIBDIVIDE_INLINE uint32x4_t libdivide_mullhi_u32_vec128(uint32x4_t a, uint32_t b) {
+    // Desire is [x0, x1, x2, x3]
+    uint32x4_t w1 = vreinterpretq_u32_u64(vmull_n_u32(vget_low_u32(a), b));  // [_, x0, _, x1]
+    uint32x4_t w2 = vreinterpretq_u32_u64(vmull_high_n_u32(a, b));           //[_, x2, _, x3]
+    return vuzp2q_u32(w1, w2);                                               // [x0, x1, x2, x3]
+}
+
+static LIBDIVIDE_INLINE int32x4_t libdivide_mullhi_s32_vec128(int32x4_t a, int32_t b) {
+    int32x4_t w1 = vreinterpretq_s32_s64(vmull_n_s32(vget_low_s32(a), b));  // [_, x0, _, x1]
+    int32x4_t w2 = vreinterpretq_s32_s64(vmull_high_n_s32(a, b));           //[_, x2, _, x3]
+    return vuzp2q_s32(w1, w2);                                              // [x0, x1, x2, x3]
+}
+
+static LIBDIVIDE_INLINE uint64x2_t libdivide_mullhi_u64_vec128(uint64x2_t x, uint64_t sy) {
+    // full 128 bits product is:
+    // x0*y0 + (x0*y1 << 32) + (x1*y0 << 32) + (x1*y1 << 64)
+    // Note x0,y0,x1,y1 are all conceptually uint32, products are 32x32->64.
+
+    // Get low and high words. x0 contains low 32 bits, x1 is high 32 bits.
+    uint64x2_t y = vdupq_n_u64(sy);
+    uint32x2_t x0 = vmovn_u64(x);
+    uint32x2_t y0 = vmovn_u64(y);
+    uint32x2_t x1 = vshrn_n_u64(x, 32);
+    uint32x2_t y1 = vshrn_n_u64(y, 32);
+
+    // Compute x0*y0.
+    uint64x2_t x0y0 = vmull_u32(x0, y0);
+    uint64x2_t x0y0_hi = vshrq_n_u64(x0y0, 32);
+
+    // Compute other intermediate products.
+    uint64x2_t temp = vmlal_u32(x0y0_hi, x1, y0);  // temp = x0y0_hi + x1*y0;
+    // We want to split temp into its low 32 bits and high 32 bits, both
+    // in the low half of 64 bit registers.
+    // Use shifts to avoid needing a reg for the mask.
+    uint64x2_t temp_lo = vshrq_n_u64(vshlq_n_u64(temp, 32), 32);  // temp_lo = temp & 0xFFFFFFFF;
+    uint64x2_t temp_hi = vshrq_n_u64(temp, 32);                   // temp_hi = temp >> 32;
+
+    temp_lo = vmlal_u32(temp_lo, x0, y1);  // temp_lo += x0*y0
+    temp_lo = vshrq_n_u64(temp_lo, 32);    // temp_lo >>= 32
+    temp_hi = vmlal_u32(temp_hi, x1, y1);  // temp_hi += x1*y1
+    uint64x2_t result = vaddq_u64(temp_hi, temp_lo);
+    return result;
+}
+
+static LIBDIVIDE_INLINE int64x2_t libdivide_mullhi_s64_vec128(int64x2_t x, int64_t sy) {
+    int64x2_t p = vreinterpretq_s64_u64(
+        libdivide_mullhi_u64_vec128(vreinterpretq_u64_s64(x), (uint64_t)(sy)));
+    int64x2_t y = vdupq_n_s64(sy);
+    int64x2_t t1 = vandq_s64(libdivide_s64_signbits(x), y);
+    int64x2_t t2 = vandq_s64(libdivide_s64_signbits(y), x);
+    p = vsubq_s64(p, t1);
+    p = vsubq_s64(p, t2);
+    return p;
+}
+
+////////// UINT16
+
+uint16x8_t libdivide_u16_do_vec128(uint16x8_t numers, const struct libdivide_u16_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, uint16x8_t, u16)}
+
+uint16x8_t libdivide_u16_branchfree_do_vec128(
+    uint16x8_t numers, const struct libdivide_u16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, uint16x8_t, u16_branchfree)}
+
+////////// UINT32
+
+uint32x4_t libdivide_u32_do_vec128(uint32x4_t numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return libdivide_u32_neon_srl(numers, more);
+    } else {
+        uint32x4_t q = libdivide_mullhi_u32_vec128(numers, denom->magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            // Note we can use halving-subtract to avoid the shift.
+            uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            uint32x4_t t = vaddq_u32(vhsubq_u32(numers, q), q);
+            return libdivide_u32_neon_srl(t, shift);
+        } else {
+            return libdivide_u32_neon_srl(q, more);
+        }
+    }
+}
+
+uint32x4_t libdivide_u32_branchfree_do_vec128(
+    uint32x4_t numers, const struct libdivide_u32_branchfree_t *denom) {
+    uint32x4_t q = libdivide_mullhi_u32_vec128(numers, denom->magic);
+    uint32x4_t t = vaddq_u32(vhsubq_u32(numers, q), q);
+    return libdivide_u32_neon_srl(t, denom->more);
+}
+
+////////// UINT64
+
+uint64x2_t libdivide_u64_do_vec128(uint64x2_t numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return libdivide_u64_neon_srl(numers, more);
+    } else {
+        uint64x2_t q = libdivide_mullhi_u64_vec128(numers, denom->magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            // No 64-bit halving subtracts in NEON :(
+            uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            uint64x2_t t = vaddq_u64(vshrq_n_u64(vsubq_u64(numers, q), 1), q);
+            return libdivide_u64_neon_srl(t, shift);
+        } else {
+            return libdivide_u64_neon_srl(q, more);
+        }
+    }
+}
+
+uint64x2_t libdivide_u64_branchfree_do_vec128(
+    uint64x2_t numers, const struct libdivide_u64_branchfree_t *denom) {
+    uint64x2_t q = libdivide_mullhi_u64_vec128(numers, denom->magic);
+    uint64x2_t t = vaddq_u64(vshrq_n_u64(vsubq_u64(numers, q), 1), q);
+    return libdivide_u64_neon_srl(t, denom->more);
+}
+
+////////// SINT16
+
+int16x8_t libdivide_s16_do_vec128(int16x8_t numers, const struct libdivide_s16_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, int16x8_t, s16)}
+
+int16x8_t libdivide_s16_branchfree_do_vec128(
+    int16x8_t numers, const struct libdivide_s16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, int16x8_t, s16_branchfree)}
+
+////////// SINT32
+
+int32x4_t libdivide_s32_do_vec128(int32x4_t numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        int32x4_t roundToZeroTweak = vdupq_n_s32((int)mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        int32x4_t q = vaddq_s32(numers, vandq_s32(vshrq_n_s32(numers, 31), roundToZeroTweak));
+        q = libdivide_s32_neon_sra(q, shift);
+        int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = vsubq_s32(veorq_s32(q, sign), sign);
+        return q;
+    } else {
+        int32x4_t q = libdivide_mullhi_s32_vec128(numers, denom->magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = vaddq_s32(q, vsubq_s32(veorq_s32(numers, sign), sign));
+        }
+        // q >>= shift
+        q = libdivide_s32_neon_sra(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = vaddq_s32(
+            q, vreinterpretq_s32_u32(vshrq_n_u32(vreinterpretq_u32_s32(q), 31)));  // q += (q < 0)
+        return q;
+    }
+}
+
+int32x4_t libdivide_s32_branchfree_do_vec128(
+    int32x4_t numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+    int32x4_t q = libdivide_mullhi_s32_vec128(numers, magic);
+    q = vaddq_s32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    int32x4_t q_sign = vshrq_n_s32(q, 31);  // q_sign = q >> 31
+    int32x4_t mask = vdupq_n_s32(((uint32_t)1 << shift) - is_power_of_2);
+    q = vaddq_s32(q, vandq_s32(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s32_neon_sra(q, shift);       // q >>= shift
+    q = vsubq_s32(veorq_s32(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+int64x2_t libdivide_s64_do_vec128(int64x2_t numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        int64x2_t roundToZeroTweak = vdupq_n_s64(mask);  // TODO: no need to sign extend
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        int64x2_t q =
+            vaddq_s64(numers, vandq_s64(libdivide_s64_signbits(numers), roundToZeroTweak));
+        q = libdivide_s64_neon_sra(q, shift);
+        // q = (q ^ sign) - sign;
+        int64x2_t sign = vreinterpretq_s64_s8(vdupq_n_s8((int8_t)more >> 7));
+        q = vsubq_s64(veorq_s64(q, sign), sign);
+        return q;
+    } else {
+        int64x2_t q = libdivide_mullhi_s64_vec128(numers, magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            int64x2_t sign = vdupq_n_s64((int8_t)more >> 7);  // TODO: no need to widen
+            // q += ((numer ^ sign) - sign);
+            q = vaddq_s64(q, vsubq_s64(veorq_s64(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_neon_sra(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = vaddq_s64(
+            q, vreinterpretq_s64_u64(vshrq_n_u64(vreinterpretq_u64_s64(q), 63)));  // q += (q < 0)
+        return q;
+    }
+}
+
+int64x2_t libdivide_s64_branchfree_do_vec128(
+    int64x2_t numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    int64x2_t sign = vdupq_n_s64((int8_t)more >> 7);  // TODO: avoid sign extend
+
+    // libdivide_mullhi_s64(numers, magic);
+    int64x2_t q = libdivide_mullhi_s64_vec128(numers, magic);
+    q = vaddq_s64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    int64x2_t q_sign = libdivide_s64_signbits(q);  // q_sign = q >> 63
+    int64x2_t mask = vdupq_n_s64(((uint64_t)1 << shift) - is_power_of_2);
+    q = vaddq_s64(q, vandq_s64(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_neon_sra(q, shift);       // q >>= shift
+    q = vsubq_s64(veorq_s64(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+#if defined(LIBDIVIDE_AVX512)
+
+static LIBDIVIDE_INLINE __m512i libdivide_u16_do_vec512(
+    __m512i numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s16_do_vec512(
+    __m512i numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u32_do_vec512(
+    __m512i numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s32_do_vec512(
+    __m512i numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u64_do_vec512(
+    __m512i numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s64_do_vec512(
+    __m512i numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE __m512i libdivide_u16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+static LIBDIVIDE_INLINE __m512i libdivide_s64_signbits_vec512(__m512i v) {
+    ;
+    return _mm512_srai_epi64(v, 63);
+}
+
+static LIBDIVIDE_INLINE __m512i libdivide_s64_shift_right_vec512(__m512i v, int amt) {
+    return _mm512_srai_epi64(v, amt);
+}
+
+// Here, b is assumed to contain one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_u32_vec512(__m512i a, __m512i b) {
+    __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epu32(a, b), 32);
+    __m512i a1X3X = _mm512_srli_epi64(a, 32);
+    __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0);
+    __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epu32(a1X3X, b), mask);
+    return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// b is one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_s32_vec512(__m512i a, __m512i b) {
+    __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epi32(a, b), 32);
+    __m512i a1X3X = _mm512_srli_epi64(a, 32);
+    __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0);
+    __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epi32(a1X3X, b), mask);
+    return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// Here, y is assumed to contain one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_u64_vec512(__m512i x, __m512i y) {
+    // see m128i variant for comments.
+    __m512i x0y0 = _mm512_mul_epu32(x, y);
+    __m512i x0y0_hi = _mm512_srli_epi64(x0y0, 32);
+
+    __m512i x1 = _mm512_shuffle_epi32(x, (_MM_PERM_ENUM)_MM_SHUFFLE(3, 3, 1, 1));
+    __m512i y1 = _mm512_shuffle_epi32(y, (_MM_PERM_ENUM)_MM_SHUFFLE(3, 3, 1, 1));
+
+    __m512i x0y1 = _mm512_mul_epu32(x, y1);
+    __m512i x1y0 = _mm512_mul_epu32(x1, y);
+    __m512i x1y1 = _mm512_mul_epu32(x1, y1);
+
+    __m512i mask = _mm512_set1_epi64(0xFFFFFFFF);
+    __m512i temp = _mm512_add_epi64(x1y0, x0y0_hi);
+    __m512i temp_lo = _mm512_and_si512(temp, mask);
+    __m512i temp_hi = _mm512_srli_epi64(temp, 32);
+
+    temp_lo = _mm512_srli_epi64(_mm512_add_epi64(temp_lo, x0y1), 32);
+    temp_hi = _mm512_add_epi64(x1y1, temp_hi);
+    return _mm512_add_epi64(temp_lo, temp_hi);
+}
+
+// y is one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_s64_vec512(__m512i x, __m512i y) {
+    __m512i p = libdivide_mullhi_u64_vec512(x, y);
+    __m512i t1 = _mm512_and_si512(libdivide_s64_signbits_vec512(x), y);
+    __m512i t2 = _mm512_and_si512(libdivide_s64_signbits_vec512(y), x);
+    p = _mm512_sub_epi64(p, t1);
+    p = _mm512_sub_epi64(p, t2);
+    return p;
+}
+
+////////// UINT16
+
+__m512i libdivide_u16_do_vec512(__m512i numers, const struct libdivide_u16_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, __m512i, u16)}
+
+__m512i libdivide_u16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, __m512i, u16_branchfree)}
+
+////////// UINT32
+
+__m512i libdivide_u32_do_vec512(__m512i numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm512_srli_epi32(numers, more);
+    } else {
+        __m512i q = libdivide_mullhi_u32_vec512(numers, _mm512_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q);
+            return _mm512_srli_epi32(t, shift);
+        } else {
+            return _mm512_srli_epi32(q, more);
+        }
+    }
+}
+
+__m512i libdivide_u32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u32_branchfree_t *denom) {
+    __m512i q = libdivide_mullhi_u32_vec512(numers, _mm512_set1_epi32(denom->magic));
+    __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q);
+    return _mm512_srli_epi32(t, denom->more);
+}
+
+////////// UINT64
+
+__m512i libdivide_u64_do_vec512(__m512i numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm512_srli_epi64(numers, more);
+    } else {
+        __m512i q = libdivide_mullhi_u64_vec512(numers, _mm512_set1_epi64(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q);
+            return _mm512_srli_epi64(t, shift);
+        } else {
+            return _mm512_srli_epi64(q, more);
+        }
+    }
+}
+
+__m512i libdivide_u64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u64_branchfree_t *denom) {
+    __m512i q = libdivide_mullhi_u64_vec512(numers, _mm512_set1_epi64(denom->magic));
+    __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q);
+    return _mm512_srli_epi64(t, denom->more);
+}
+
+////////// SINT16
+
+__m512i libdivide_s16_do_vec512(__m512i numers, const struct libdivide_s16_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, __m512i, s16)}
+
+__m512i libdivide_s16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, __m512i, s16_branchfree)}
+
+////////// SINT32
+
+__m512i libdivide_s32_do_vec512(__m512i numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        __m512i roundToZeroTweak = _mm512_set1_epi32(mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        __m512i q = _mm512_add_epi32(
+            numers, _mm512_and_si512(_mm512_srai_epi32(numers, 31), roundToZeroTweak));
+        q = _mm512_srai_epi32(q, shift);
+        __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign);
+        return q;
+    } else {
+        __m512i q = libdivide_mullhi_s32_vec512(numers, _mm512_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm512_add_epi32(q, _mm512_sub_epi32(_mm512_xor_si512(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm512_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = _mm512_add_epi32(q, _mm512_srli_epi32(q, 31));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m512i libdivide_s32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+    __m512i q = libdivide_mullhi_s32_vec512(numers, _mm512_set1_epi32(magic));
+    q = _mm512_add_epi32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    __m512i q_sign = _mm512_srai_epi32(q, 31);  // q_sign = q >> 31
+    __m512i mask = _mm512_set1_epi32(((uint32_t)1 << shift) - is_power_of_2);
+    q = _mm512_add_epi32(q, _mm512_and_si512(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm512_srai_epi32(q, shift);                          // q >>= shift
+    q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+__m512i libdivide_s64_do_vec512(__m512i numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        __m512i roundToZeroTweak = _mm512_set1_epi64(mask);
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        __m512i q = _mm512_add_epi64(
+            numers, _mm512_and_si512(libdivide_s64_signbits_vec512(numers), roundToZeroTweak));
+        q = libdivide_s64_shift_right_vec512(q, shift);
+        __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign);
+        return q;
+    } else {
+        __m512i q = libdivide_mullhi_s64_vec512(numers, _mm512_set1_epi64(magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm512_add_epi64(q, _mm512_sub_epi64(_mm512_xor_si512(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_shift_right_vec512(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = _mm512_add_epi64(q, _mm512_srli_epi64(q, 63));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m512i libdivide_s64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+
+    // libdivide_mullhi_s64(numers, magic);
+    __m512i q = libdivide_mullhi_s64_vec512(numers, _mm512_set1_epi64(magic));
+    q = _mm512_add_epi64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    __m512i q_sign = libdivide_s64_signbits_vec512(q);  // q_sign = q >> 63
+    __m512i mask = _mm512_set1_epi64(((uint64_t)1 << shift) - is_power_of_2);
+    q = _mm512_add_epi64(q, _mm512_and_si512(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_shift_right_vec512(q, shift);           // q >>= shift
+    q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+#if defined(LIBDIVIDE_AVX2)
+
+static LIBDIVIDE_INLINE __m256i libdivide_u16_do_vec256(
+    __m256i numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s16_do_vec256(
+    __m256i numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u32_do_vec256(
+    __m256i numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s32_do_vec256(
+    __m256i numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u64_do_vec256(
+    __m256i numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s64_do_vec256(
+    __m256i numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE __m256i libdivide_u16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+// Implementation of _mm256_srai_epi64(v, 63) (from AVX512).
+static LIBDIVIDE_INLINE __m256i libdivide_s64_signbits_vec256(__m256i v) {
+    __m256i hiBitsDuped = _mm256_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1));
+    __m256i signBits = _mm256_srai_epi32(hiBitsDuped, 31);
+    return signBits;
+}
+
+// Implementation of _mm256_srai_epi64 (from AVX512).
+static LIBDIVIDE_INLINE __m256i libdivide_s64_shift_right_vec256(__m256i v, int amt) {
+    const int b = 64 - amt;
+    __m256i m = _mm256_set1_epi64x((uint64_t)1 << (b - 1));
+    __m256i x = _mm256_srli_epi64(v, amt);
+    __m256i result = _mm256_sub_epi64(_mm256_xor_si256(x, m), m);
+    return result;
+}
+
+// Here, b is assumed to contain one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_u32_vec256(__m256i a, __m256i b) {
+    __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epu32(a, b), 32);
+    __m256i a1X3X = _mm256_srli_epi64(a, 32);
+    __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0);
+    __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epu32(a1X3X, b), mask);
+    return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// b is one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_s32_vec256(__m256i a, __m256i b) {
+    __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epi32(a, b), 32);
+    __m256i a1X3X = _mm256_srli_epi64(a, 32);
+    __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0);
+    __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epi32(a1X3X, b), mask);
+    return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// Here, y is assumed to contain one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_u64_vec256(__m256i x, __m256i y) {
+    // see m128i variant for comments.
+    __m256i x0y0 = _mm256_mul_epu32(x, y);
+    __m256i x0y0_hi = _mm256_srli_epi64(x0y0, 32);
+
+    __m256i x1 = _mm256_shuffle_epi32(x, _MM_SHUFFLE(3, 3, 1, 1));
+    __m256i y1 = _mm256_shuffle_epi32(y, _MM_SHUFFLE(3, 3, 1, 1));
+
+    __m256i x0y1 = _mm256_mul_epu32(x, y1);
+    __m256i x1y0 = _mm256_mul_epu32(x1, y);
+    __m256i x1y1 = _mm256_mul_epu32(x1, y1);
+
+    __m256i mask = _mm256_set1_epi64x(0xFFFFFFFF);
+    __m256i temp = _mm256_add_epi64(x1y0, x0y0_hi);
+    __m256i temp_lo = _mm256_and_si256(temp, mask);
+    __m256i temp_hi = _mm256_srli_epi64(temp, 32);
+
+    temp_lo = _mm256_srli_epi64(_mm256_add_epi64(temp_lo, x0y1), 32);
+    temp_hi = _mm256_add_epi64(x1y1, temp_hi);
+    return _mm256_add_epi64(temp_lo, temp_hi);
+}
+
+// y is one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_s64_vec256(__m256i x, __m256i y) {
+    __m256i p = libdivide_mullhi_u64_vec256(x, y);
+    __m256i t1 = _mm256_and_si256(libdivide_s64_signbits_vec256(x), y);
+    __m256i t2 = _mm256_and_si256(libdivide_s64_signbits_vec256(y), x);
+    p = _mm256_sub_epi64(p, t1);
+    p = _mm256_sub_epi64(p, t2);
+    return p;
+}
+
+////////// UINT16
+
+__m256i libdivide_u16_do_vec256(__m256i numers, const struct libdivide_u16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm256_srli_epi16(numers, more);
+    } else {
+        __m256i q = _mm256_mulhi_epu16(numers, _mm256_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            __m256i t = _mm256_adds_epu16(_mm256_srli_epi16(_mm256_subs_epu16(numers, q), 1), q);
+            return _mm256_srli_epi16(t, (more & LIBDIVIDE_16_SHIFT_MASK));
+        } else {
+            return _mm256_srli_epi16(q, more);
+        }
+    }
+}
+
+__m256i libdivide_u16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u16_branchfree_t *denom) {
+    __m256i q = _mm256_mulhi_epu16(numers, _mm256_set1_epi16(denom->magic));
+    __m256i t = _mm256_adds_epu16(_mm256_srli_epi16(_mm256_subs_epu16(numers, q), 1), q);
+    return _mm256_srli_epi16(t, denom->more);
+}
+
+////////// UINT32
+
+__m256i libdivide_u32_do_vec256(__m256i numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm256_srli_epi32(numers, more);
+    } else {
+        __m256i q = libdivide_mullhi_u32_vec256(numers, _mm256_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q);
+            return _mm256_srli_epi32(t, shift);
+        } else {
+            return _mm256_srli_epi32(q, more);
+        }
+    }
+}
+
+__m256i libdivide_u32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u32_branchfree_t *denom) {
+    __m256i q = libdivide_mullhi_u32_vec256(numers, _mm256_set1_epi32(denom->magic));
+    __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q);
+    return _mm256_srli_epi32(t, denom->more);
+}
+
+////////// UINT64
+
+__m256i libdivide_u64_do_vec256(__m256i numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm256_srli_epi64(numers, more);
+    } else {
+        __m256i q = libdivide_mullhi_u64_vec256(numers, _mm256_set1_epi64x(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q);
+            return _mm256_srli_epi64(t, shift);
+        } else {
+            return _mm256_srli_epi64(q, more);
+        }
+    }
+}
+
+__m256i libdivide_u64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u64_branchfree_t *denom) {
+    __m256i q = libdivide_mullhi_u64_vec256(numers, _mm256_set1_epi64x(denom->magic));
+    __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q);
+    return _mm256_srli_epi64(t, denom->more);
+}
+
+////////// SINT16
+
+__m256i libdivide_s16_do_vec256(__m256i numers, const struct libdivide_s16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint16_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+        uint16_t mask = ((uint16_t)1 << shift) - 1;
+        __m256i roundToZeroTweak = _mm256_set1_epi16(mask);
+        // q = numer + ((numer >> 15) & roundToZeroTweak);
+        __m256i q = _mm256_add_epi16(
+            numers, _mm256_and_si256(_mm256_srai_epi16(numers, 15), roundToZeroTweak));
+        q = _mm256_srai_epi16(q, shift);
+        __m256i sign = _mm256_set1_epi16((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm256_sub_epi16(_mm256_xor_si256(q, sign), sign);
+        return q;
+    } else {
+        __m256i q = _mm256_mulhi_epi16(numers, _mm256_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m256i sign = _mm256_set1_epi16((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm256_add_epi16(q, _mm256_sub_epi16(_mm256_xor_si256(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm256_srai_epi16(q, more & LIBDIVIDE_16_SHIFT_MASK);
+        q = _mm256_add_epi16(q, _mm256_srli_epi16(q, 15));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m256i libdivide_s16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s16_branchfree_t *denom) {
+    int16_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    // must be arithmetic shift
+    __m256i sign = _mm256_set1_epi16((int8_t)more >> 7);
+    __m256i q = _mm256_mulhi_epi16(numers, _mm256_set1_epi16(magic));
+    q = _mm256_add_epi16(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint16_t is_power_of_2 = (magic == 0);
+    __m256i q_sign = _mm256_srai_epi16(q, 15);  // q_sign = q >> 15
+    __m256i mask = _mm256_set1_epi16(((uint16_t)1 << shift) - is_power_of_2);
+    q = _mm256_add_epi16(q, _mm256_and_si256(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm256_srai_epi16(q, shift);                          // q >>= shift
+    q = _mm256_sub_epi16(_mm256_xor_si256(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT32
+
+__m256i libdivide_s32_do_vec256(__m256i numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        __m256i roundToZeroTweak = _mm256_set1_epi32(mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        __m256i q = _mm256_add_epi32(
+            numers, _mm256_and_si256(_mm256_srai_epi32(numers, 31), roundToZeroTweak));
+        q = _mm256_srai_epi32(q, shift);
+        __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign);
+        return q;
+    } else {
+        __m256i q = libdivide_mullhi_s32_vec256(numers, _mm256_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm256_add_epi32(q, _mm256_sub_epi32(_mm256_xor_si256(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm256_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = _mm256_add_epi32(q, _mm256_srli_epi32(q, 31));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m256i libdivide_s32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+    __m256i q = libdivide_mullhi_s32_vec256(numers, _mm256_set1_epi32(magic));
+    q = _mm256_add_epi32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    __m256i q_sign = _mm256_srai_epi32(q, 31);  // q_sign = q >> 31
+    __m256i mask = _mm256_set1_epi32(((uint32_t)1 << shift) - is_power_of_2);
+    q = _mm256_add_epi32(q, _mm256_and_si256(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm256_srai_epi32(q, shift);                          // q >>= shift
+    q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+__m256i libdivide_s64_do_vec256(__m256i numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        __m256i roundToZeroTweak = _mm256_set1_epi64x(mask);
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        __m256i q = _mm256_add_epi64(
+            numers, _mm256_and_si256(libdivide_s64_signbits_vec256(numers), roundToZeroTweak));
+        q = libdivide_s64_shift_right_vec256(q, shift);
+        __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign);
+        return q;
+    } else {
+        __m256i q = libdivide_mullhi_s64_vec256(numers, _mm256_set1_epi64x(magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm256_add_epi64(q, _mm256_sub_epi64(_mm256_xor_si256(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_shift_right_vec256(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = _mm256_add_epi64(q, _mm256_srli_epi64(q, 63));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m256i libdivide_s64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+
+    // libdivide_mullhi_s64(numers, magic);
+    __m256i q = libdivide_mullhi_s64_vec256(numers, _mm256_set1_epi64x(magic));
+    q = _mm256_add_epi64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    __m256i q_sign = libdivide_s64_signbits_vec256(q);  // q_sign = q >> 63
+    __m256i mask = _mm256_set1_epi64x(((uint64_t)1 << shift) - is_power_of_2);
+    q = _mm256_add_epi64(q, _mm256_and_si256(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_shift_right_vec256(q, shift);           // q >>= shift
+    q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+#if defined(LIBDIVIDE_SSE2)
+
+static LIBDIVIDE_INLINE __m128i libdivide_u16_do_vec128(
+    __m128i numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s16_do_vec128(
+    __m128i numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u32_do_vec128(
+    __m128i numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s32_do_vec128(
+    __m128i numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u64_do_vec128(
+    __m128i numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s64_do_vec128(
+    __m128i numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE __m128i libdivide_u16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+// Implementation of _mm_srai_epi64(v, 63) (from AVX512).
+static LIBDIVIDE_INLINE __m128i libdivide_s64_signbits_vec128(__m128i v) {
+    __m128i hiBitsDuped = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1));
+    __m128i signBits = _mm_srai_epi32(hiBitsDuped, 31);
+    return signBits;
+}
+
+// Implementation of _mm_srai_epi64 (from AVX512).
+static LIBDIVIDE_INLINE __m128i libdivide_s64_shift_right_vec128(__m128i v, int amt) {
+    const int b = 64 - amt;
+    __m128i m = _mm_set1_epi64x((uint64_t)1 << (b - 1));
+    __m128i x = _mm_srli_epi64(v, amt);
+    __m128i result = _mm_sub_epi64(_mm_xor_si128(x, m), m);
+    return result;
+}
+
+// Here, b is assumed to contain one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_u32_vec128(__m128i a, __m128i b) {
+    __m128i hi_product_0Z2Z = _mm_srli_epi64(_mm_mul_epu32(a, b), 32);
+    __m128i a1X3X = _mm_srli_epi64(a, 32);
+    __m128i mask = _mm_set_epi32(-1, 0, -1, 0);
+    __m128i hi_product_Z1Z3 = _mm_and_si128(_mm_mul_epu32(a1X3X, b), mask);
+    return _mm_or_si128(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// SSE2 does not have a signed multiplication instruction, but we can convert
+// unsigned to signed pretty efficiently. Again, b is just a 32 bit value
+// repeated four times.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_s32_vec128(__m128i a, __m128i b) {
+    __m128i p = libdivide_mullhi_u32_vec128(a, b);
+    // t1 = (a >> 31) & y, arithmetic shift
+    __m128i t1 = _mm_and_si128(_mm_srai_epi32(a, 31), b);
+    __m128i t2 = _mm_and_si128(_mm_srai_epi32(b, 31), a);
+    p = _mm_sub_epi32(p, t1);
+    p = _mm_sub_epi32(p, t2);
+    return p;
+}
+
+// Here, y is assumed to contain one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_u64_vec128(__m128i x, __m128i y) {
+    // full 128 bits product is:
+    // x0*y0 + (x0*y1 << 32) + (x1*y0 << 32) + (x1*y1 << 64)
+    // Note x0,y0,x1,y1 are all conceptually uint32, products are 32x32->64.
+
+    // Compute x0*y0.
+    // Note x1, y1 are ignored by mul_epu32.
+    __m128i x0y0 = _mm_mul_epu32(x, y);
+    __m128i x0y0_hi = _mm_srli_epi64(x0y0, 32);
+
+    // Get x1, y1 in the low bits.
+    // We could shuffle or right shift. Shuffles are preferred as they preserve
+    // the source register for the next computation.
+    __m128i x1 = _mm_shuffle_epi32(x, _MM_SHUFFLE(3, 3, 1, 1));
+    __m128i y1 = _mm_shuffle_epi32(y, _MM_SHUFFLE(3, 3, 1, 1));
+
+    // No need to mask off top 32 bits for mul_epu32.
+    __m128i x0y1 = _mm_mul_epu32(x, y1);
+    __m128i x1y0 = _mm_mul_epu32(x1, y);
+    __m128i x1y1 = _mm_mul_epu32(x1, y1);
+
+    // Mask here selects low bits only.
+    __m128i mask = _mm_set1_epi64x(0xFFFFFFFF);
+    __m128i temp = _mm_add_epi64(x1y0, x0y0_hi);
+    __m128i temp_lo = _mm_and_si128(temp, mask);
+    __m128i temp_hi = _mm_srli_epi64(temp, 32);
+
+    temp_lo = _mm_srli_epi64(_mm_add_epi64(temp_lo, x0y1), 32);
+    temp_hi = _mm_add_epi64(x1y1, temp_hi);
+    return _mm_add_epi64(temp_lo, temp_hi);
+}
+
+// y is one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_s64_vec128(__m128i x, __m128i y) {
+    __m128i p = libdivide_mullhi_u64_vec128(x, y);
+    __m128i t1 = _mm_and_si128(libdivide_s64_signbits_vec128(x), y);
+    __m128i t2 = _mm_and_si128(libdivide_s64_signbits_vec128(y), x);
+    p = _mm_sub_epi64(p, t1);
+    p = _mm_sub_epi64(p, t2);
+    return p;
+}
+
+////////// UINT26
+
+__m128i libdivide_u16_do_vec128(__m128i numers, const struct libdivide_u16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm_srli_epi16(numers, more);
+    } else {
+        __m128i q = _mm_mulhi_epu16(numers, _mm_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            __m128i t = _mm_adds_epu16(_mm_srli_epi16(_mm_subs_epu16(numers, q), 1), q);
+            return _mm_srli_epi16(t, (more & LIBDIVIDE_16_SHIFT_MASK));
+        } else {
+            return _mm_srli_epi16(q, more);
+        }
+    }
+}
+
+__m128i libdivide_u16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u16_branchfree_t *denom) {
+    __m128i q = _mm_mulhi_epu16(numers, _mm_set1_epi16(denom->magic));
+    __m128i t = _mm_adds_epu16(_mm_srli_epi16(_mm_subs_epu16(numers, q), 1), q);
+    return _mm_srli_epi16(t, denom->more);
+}
+
+////////// UINT32
+
+__m128i libdivide_u32_do_vec128(__m128i numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm_srli_epi32(numers, more);
+    } else {
+        __m128i q = libdivide_mullhi_u32_vec128(numers, _mm_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q);
+            return _mm_srli_epi32(t, shift);
+        } else {
+            return _mm_srli_epi32(q, more);
+        }
+    }
+}
+
+__m128i libdivide_u32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u32_branchfree_t *denom) {
+    __m128i q = libdivide_mullhi_u32_vec128(numers, _mm_set1_epi32(denom->magic));
+    __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q);
+    return _mm_srli_epi32(t, denom->more);
+}
+
+////////// UINT64
+
+__m128i libdivide_u64_do_vec128(__m128i numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm_srli_epi64(numers, more);
+    } else {
+        __m128i q = libdivide_mullhi_u64_vec128(numers, _mm_set1_epi64x(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q);
+            return _mm_srli_epi64(t, shift);
+        } else {
+            return _mm_srli_epi64(q, more);
+        }
+    }
+}
+
+__m128i libdivide_u64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u64_branchfree_t *denom) {
+    __m128i q = libdivide_mullhi_u64_vec128(numers, _mm_set1_epi64x(denom->magic));
+    __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q);
+    return _mm_srli_epi64(t, denom->more);
+}
+
+////////// SINT16
+
+__m128i libdivide_s16_do_vec128(__m128i numers, const struct libdivide_s16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint16_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+        uint16_t mask = ((uint16_t)1 << shift) - 1;
+        __m128i roundToZeroTweak = _mm_set1_epi16(mask);
+        // q = numer + ((numer >> 15) & roundToZeroTweak);
+        __m128i q =
+            _mm_add_epi16(numers, _mm_and_si128(_mm_srai_epi16(numers, 15), roundToZeroTweak));
+        q = _mm_srai_epi16(q, shift);
+        __m128i sign = _mm_set1_epi16((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm_sub_epi16(_mm_xor_si128(q, sign), sign);
+        return q;
+    } else {
+        __m128i q = _mm_mulhi_epi16(numers, _mm_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m128i sign = _mm_set1_epi16((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm_add_epi16(q, _mm_sub_epi16(_mm_xor_si128(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm_srai_epi16(q, more & LIBDIVIDE_16_SHIFT_MASK);
+        q = _mm_add_epi16(q, _mm_srli_epi16(q, 15));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m128i libdivide_s16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s16_branchfree_t *denom) {
+    int16_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    // must be arithmetic shift
+    __m128i sign = _mm_set1_epi16((int8_t)more >> 7);
+    __m128i q = _mm_mulhi_epi16(numers, _mm_set1_epi16(magic));
+    q = _mm_add_epi16(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint16_t is_power_of_2 = (magic == 0);
+    __m128i q_sign = _mm_srai_epi16(q, 15);  // q_sign = q >> 15
+    __m128i mask = _mm_set1_epi16(((uint16_t)1 << shift) - is_power_of_2);
+    q = _mm_add_epi16(q, _mm_and_si128(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm_srai_epi16(q, shift);                       // q >>= shift
+    q = _mm_sub_epi16(_mm_xor_si128(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT32
+
+__m128i libdivide_s32_do_vec128(__m128i numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        __m128i roundToZeroTweak = _mm_set1_epi32(mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        __m128i q =
+            _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak));
+        q = _mm_srai_epi32(q, shift);
+        __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign);
+        return q;
+    } else {
+        __m128i q = libdivide_mullhi_s32_vec128(numers, _mm_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm_add_epi32(q, _mm_sub_epi32(_mm_xor_si128(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = _mm_add_epi32(q, _mm_srli_epi32(q, 31));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m128i libdivide_s32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+    __m128i q = libdivide_mullhi_s32_vec128(numers, _mm_set1_epi32(magic));
+    q = _mm_add_epi32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    __m128i q_sign = _mm_srai_epi32(q, 31);  // q_sign = q >> 31
+    __m128i mask = _mm_set1_epi32(((uint32_t)1 << shift) - is_power_of_2);
+    q = _mm_add_epi32(q, _mm_and_si128(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm_srai_epi32(q, shift);                       // q >>= shift
+    q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+__m128i libdivide_s64_do_vec128(__m128i numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        __m128i roundToZeroTweak = _mm_set1_epi64x(mask);
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        __m128i q = _mm_add_epi64(
+            numers, _mm_and_si128(libdivide_s64_signbits_vec128(numers), roundToZeroTweak));
+        q = libdivide_s64_shift_right_vec128(q, shift);
+        __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign);
+        return q;
+    } else {
+        __m128i q = libdivide_mullhi_s64_vec128(numers, _mm_set1_epi64x(magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm_add_epi64(q, _mm_sub_epi64(_mm_xor_si128(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_shift_right_vec128(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = _mm_add_epi64(q, _mm_srli_epi64(q, 63));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m128i libdivide_s64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+
+    // libdivide_mullhi_s64(numers, magic);
+    __m128i q = libdivide_mullhi_s64_vec128(numers, _mm_set1_epi64x(magic));
+    q = _mm_add_epi64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    __m128i q_sign = libdivide_s64_signbits_vec128(q);  // q_sign = q >> 63
+    __m128i mask = _mm_set1_epi64x(((uint64_t)1 << shift) - is_power_of_2);
+    q = _mm_add_epi64(q, _mm_and_si128(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_shift_right_vec128(q, shift);     // q >>= shift
+    q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+/////////// C++ stuff
+
+#ifdef __cplusplus
+
+enum Branching {
+    BRANCHFULL,  // use branching algorithms
+    BRANCHFREE   // use branchfree algorithms
+};
+
+namespace detail {
+enum Signedness {
+    SIGNED,
+    UNSIGNED,
+};
+
+#if defined(LIBDIVIDE_NEON)
+// Helper to deduce NEON vector type for integral type.
+template <int _WIDTH, Signedness _SIGN>
+struct NeonVec {};
+
+template <>
+struct NeonVec<16, UNSIGNED> {
+    typedef uint16x8_t type;
+};
+
+template <>
+struct NeonVec<16, SIGNED> {
+    typedef int16x8_t type;
+};
+
+template <>
+struct NeonVec<32, UNSIGNED> {
+    typedef uint32x4_t type;
+};
+
+template <>
+struct NeonVec<32, SIGNED> {
+    typedef int32x4_t type;
+};
+
+template <>
+struct NeonVec<64, UNSIGNED> {
+    typedef uint64x2_t type;
+};
+
+template <>
+struct NeonVec<64, SIGNED> {
+    typedef int64x2_t type;
+};
+
+template <typename T>
+struct NeonVecFor {
+    // See 'class divider' for an explanation of these template parameters.
+    typedef typename NeonVec<sizeof(T) * 8, (((T)0 >> 0) > (T)(-1) ? SIGNED : UNSIGNED)>::type type;
+};
+
+#define LIBDIVIDE_DIVIDE_NEON(ALGO, INT_TYPE)                    \
+    LIBDIVIDE_INLINE typename NeonVecFor<INT_TYPE>::type divide( \
+        typename NeonVecFor<INT_TYPE>::type n) const {           \
+        return libdivide_##ALGO##_do_vec128(n, &denom);          \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_NEON(ALGO, INT_TYPE)
+#endif
+
+#if defined(LIBDIVIDE_SSE2)
+#define LIBDIVIDE_DIVIDE_SSE2(ALGO)                     \
+    LIBDIVIDE_INLINE __m128i divide(__m128i n) const {  \
+        return libdivide_##ALGO##_do_vec128(n, &denom); \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_SSE2(ALGO)
+#endif
+
+#if defined(LIBDIVIDE_AVX2)
+#define LIBDIVIDE_DIVIDE_AVX2(ALGO)                     \
+    LIBDIVIDE_INLINE __m256i divide(__m256i n) const {  \
+        return libdivide_##ALGO##_do_vec256(n, &denom); \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_AVX2(ALGO)
+#endif
+
+#if defined(LIBDIVIDE_AVX512)
+#define LIBDIVIDE_DIVIDE_AVX512(ALGO)                   \
+    LIBDIVIDE_INLINE __m512i divide(__m512i n) const {  \
+        return libdivide_##ALGO##_do_vec512(n, &denom); \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_AVX512(ALGO)
+#endif
+
+// The DISPATCHER_GEN() macro generates C++ methods (for the given integer
+// and algorithm types) that redirect to libdivide's C API.
+#define DISPATCHER_GEN(T, ALGO)                                                       \
+    libdivide_##ALGO##_t denom;                                                       \
+    LIBDIVIDE_INLINE dispatcher() {}                                                  \
+    LIBDIVIDE_INLINE dispatcher(T d) : denom(libdivide_##ALGO##_gen(d)) {}            \
+    LIBDIVIDE_INLINE T divide(T n) const { return libdivide_##ALGO##_do(n, &denom); } \
+    LIBDIVIDE_INLINE T recover() const { return libdivide_##ALGO##_recover(&denom); } \
+    LIBDIVIDE_DIVIDE_NEON(ALGO, T)                                                    \
+    LIBDIVIDE_DIVIDE_SSE2(ALGO)                                                       \
+    LIBDIVIDE_DIVIDE_AVX2(ALGO)                                                       \
+    LIBDIVIDE_DIVIDE_AVX512(ALGO)
+
+// The dispatcher selects a specific division algorithm for a given
+// width, signedness, and ALGO using partial template specialization.
+template <int _WIDTH, Signedness _SIGN, Branching _ALGO>
+struct dispatcher {};
+
+template <>
+struct dispatcher<16, SIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(int16_t, s16)
+};
+template <>
+struct dispatcher<16, SIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(int16_t, s16_branchfree)
+};
+template <>
+struct dispatcher<16, UNSIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(uint16_t, u16)
+};
+template <>
+struct dispatcher<16, UNSIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(uint16_t, u16_branchfree)
+};
+template <>
+struct dispatcher<32, SIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(int32_t, s32)
+};
+template <>
+struct dispatcher<32, SIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(int32_t, s32_branchfree)
+};
+template <>
+struct dispatcher<32, UNSIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(uint32_t, u32)
+};
+template <>
+struct dispatcher<32, UNSIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(uint32_t, u32_branchfree)
+};
+template <>
+struct dispatcher<64, SIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(int64_t, s64)
+};
+template <>
+struct dispatcher<64, SIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(int64_t, s64_branchfree)
+};
+template <>
+struct dispatcher<64, UNSIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(uint64_t, u64)
+};
+template <>
+struct dispatcher<64, UNSIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(uint64_t, u64_branchfree)
+};
+}  // namespace detail
+
+#if defined(LIBDIVIDE_NEON)
+// Allow NeonVecFor outside of detail namespace.
+template <typename T>
+struct NeonVecFor {
+    typedef typename detail::NeonVecFor<T>::type type;
+};
+#endif
+
+// This is the main divider class for use by the user (C++ API).
+// The actual division algorithm is selected using the dispatcher struct
+// based on the integer width and algorithm template parameters.
+template <typename T, Branching ALGO = BRANCHFULL>
+class divider {
+   private:
+    // Dispatch based on the size and signedness.
+    // We avoid using type_traits as it's not available in AVR.
+    // Detect signedness by checking if T(-1) is less than T(0).
+    // Also throw in a shift by 0, which prevents floating point types from being passed.
+    typedef detail::dispatcher<sizeof(T) * 8,
+        (((T)0 >> 0) > (T)(-1) ? detail::SIGNED : detail::UNSIGNED), ALGO>
+        dispatcher_t;
+
+   public:
+    // We leave the default constructor empty so that creating
+    // an array of dividers and then initializing them
+    // later doesn't slow us down.
+    divider() {}
+
+    // Constructor that takes the divisor as a parameter
+    LIBDIVIDE_INLINE divider(T d) : div(d) {}
+
+    // Divides n by the divisor
+    LIBDIVIDE_INLINE T divide(T n) const { return div.divide(n); }
+
+    // Recovers the divisor, returns the value that was
+    // used to initialize this divider object.
+    T recover() const { return div.recover(); }
+
+    bool operator==(const divider<T, ALGO> &other) const {
+        return div.denom.magic == other.denom.magic && div.denom.more == other.denom.more;
+    }
+
+    bool operator!=(const divider<T, ALGO> &other) const { return !(*this == other); }
+
+    // Vector variants treat the input as packed integer values with the same type as the divider
+    // (e.g. s32, u32, s64, u64) and divides each of them by the divider, returning the packed
+    // quotients.
+#if defined(LIBDIVIDE_SSE2)
+    LIBDIVIDE_INLINE __m128i divide(__m128i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_AVX2)
+    LIBDIVIDE_INLINE __m256i divide(__m256i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_AVX512)
+    LIBDIVIDE_INLINE __m512i divide(__m512i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_NEON)
+    LIBDIVIDE_INLINE typename NeonVecFor<T>::type divide(typename NeonVecFor<T>::type n) const {
+        return div.divide(n);
+    }
+#endif
+
+   private:
+    // Storage for the actual divisor
+    dispatcher_t div;
+};
+
+// Overload of operator / for scalar division
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE T operator/(T n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+// Overload of operator /= for scalar division
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE T &operator/=(T &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+
+// Overloads for vector types.
+#if defined(LIBDIVIDE_SSE2)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m128i operator/(__m128i n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m128i operator/=(__m128i &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+#if defined(LIBDIVIDE_AVX2)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m256i operator/(__m256i n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m256i operator/=(__m256i &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+#if defined(LIBDIVIDE_AVX512)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m512i operator/(__m512i n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m512i operator/=(__m512i &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+
+#if defined(LIBDIVIDE_NEON)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE typename NeonVecFor<T>::type operator/(
+    typename NeonVecFor<T>::type n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE typename NeonVecFor<T>::type operator/=(
+    typename NeonVecFor<T>::type &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
+// libdivide::branchfree_divider<T>
+template <typename T>
+using branchfree_divider = divider<T, BRANCHFREE>;
+#endif
+
+}  // namespace libdivide
+
+#endif  // __cplusplus
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#endif  // LIBDIVIDE_H
-- 
2.39.2

#33David Christensen
david.christensen@crunchydata.com
In reply to: David Christensen (#32)
2 attachment(s)
Re: Initdb-time block size specification

Enclosed are TPC-H results for 1GB shared_buffers, 64MB work_mem on a
64GB laptop with SSD storage; everything else is default settings.

TL;DR: unpatched version: 17.30 seconds, patched version: 17.15; there
are some slight variations in runtime, but seems to be within the
noise level at this point.

Attachments:

tpch.unpatched.rstapplication/octet-stream; name=tpch.unpatched.rstDownload
tpch.patched.rstapplication/octet-stream; name=tpch.patched.rstDownload
#34John Naylor
john.naylor@enterprisedb.com
In reply to: David Christensen (#32)
Re: Initdb-time block size specification

On Thu, Aug 31, 2023 at 8:51 AM David Christensen <
david.christensen@crunchydata.com> wrote:

0005 - utility functions for fast div/mod operations; basically
montgomery multiplication

+/*
+ * pg_fastmod - calculates the modulus of a 32-bit number against a
constant
+ * divisor without using the division operator
+ */
+static inline uint32 pg_fastmod(uint32 n, uint32 divisor, uint64 fastinv)
+{
+#ifdef HAVE_INT128
+ uint64_t lowbits = fastinv * n;
+ return ((uint128)lowbits * divisor) >> 64;
+#else
+ return n % divisor;
+#endif
+}

Requiring 128-bit arithmetic to avoid serious regression is a non-starter
as written. Software that relies on fast 128-bit multiplication has to do
backflips to get that working on multiple platforms. But I'm not sure it's
necessary -- if the max block number is UINT32_MAX and max block size is
UINT16_MAX, can't we just use 64-bit multiplication?

--
John Naylor
EDB: http://www.enterprisedb.com

#35David Christensen
david.christensen@crunchydata.com
In reply to: John Naylor (#34)
Re: Initdb-time block size specification
+ * pg_fastmod - calculates the modulus of a 32-bit number against a constant
+ * divisor without using the division operator
+ */
+static inline uint32 pg_fastmod(uint32 n, uint32 divisor, uint64 fastinv)
+{
+#ifdef HAVE_INT128
+ uint64_t lowbits = fastinv * n;
+ return ((uint128)lowbits * divisor) >> 64;
+#else
+ return n % divisor;
+#endif
+}

Requiring 128-bit arithmetic to avoid serious regression is a non-starter as written. Software that relies on fast 128-bit multiplication has to do backflips to get that working on multiple platforms. But I'm not sure it's necessary -- if the max block number is UINT32_MAX and max block size is UINT16_MAX, can't we just use 64-bit multiplication?

I was definitely hand-waving additional implementation here for
non-native 128 bit support; the modulus algorithm as presented
requires 4 times the space as the divisor, so a uint16 implementation
should work for all 64-bit machines. Certainly open to other ideas or
implementations, this was the one I was able to find initially. If
the 16bit approach is all that is needed in practice we can also see
about narrowing the domain and not worry about making this a
general-purpose function.

Best,

David

#36David Christensen
david.christensen@crunchydata.com
In reply to: David Christensen (#35)
1 attachment(s)
Re: Initdb-time block size specification

I was definitely hand-waving additional implementation here for
non-native 128 bit support; the modulus algorithm as presented
requires 4 times the space as the divisor, so a uint16 implementation
should work for all 64-bit machines. Certainly open to other ideas or
implementations, this was the one I was able to find initially. If
the 16bit approach is all that is needed in practice we can also see
about narrowing the domain and not worry about making this a
general-purpose function.

Here's a patch atop the series which converts to 16-bit uints and
passes regressions, but I don't consider well-vetted at this point.

David

Attachments:

0001-Switch-to-using-only-16-bit-to-avoid-128-bit-require.patchapplication/octet-stream; name=0001-Switch-to-using-only-16-bit-to-avoid-128-bit-require.patchDownload
From dc4d4c52e19171cf11268e6327aeb44b1e88b172 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Thu, 31 Aug 2023 12:09:55 -0400
Subject: [PATCH] Switch to using only 16-bit to avoid 128-bit requirement

---
 src/include/port/pg_bitutils.h | 26 +++++++++-----------------
 1 file changed, 9 insertions(+), 17 deletions(-)

diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 84a5fdead9..2d7311ebb5 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -349,36 +349,28 @@ pg_rotate_left32(uint32 word, int n)
  * asm-tuned things in port maybe?  general-purpose solution should be ok
  * though.
  */
-static inline uint64 pg_fastinverse(uint32 divisor)
+static inline uint32 pg_fastinverse(uint16 divisor)
 {
-	return UINT64_C(0xFFFFFFFFFFFFFFFF) / divisor + 1;
+	return UINT32_C(0xFFFFFFFF) / divisor + 1;
 }
 
 /*
- * pg_fastdiv - calculates the quotient of a 32-bit number against a constant
+ * pg_fastdiv - calculates the quotient of a 16-bit number against a constant
  * divisor without using the division operator
  */
-static inline uint32 pg_fastdiv(uint32 n, uint32 divisor, uint64 fastinv)
+static inline uint16 pg_fastdiv(uint16 n, uint16 divisor, uint32 fastinv)
 {
-#ifdef HAVE_INT128
-	return (((uint128)(fastinv - 1) * n)) >> 64;
-#else
-	return n / divisor;
-#endif
+	return (((uint64)(fastinv - 1) * n)) >> 32;
 }
 
 /*
- * pg_fastmod - calculates the modulus of a 32-bit number against a constant
+ * pg_fastmod - calculates the modulus of a 16-bit number against a constant
  * divisor without using the division operator
  */
-static inline uint32 pg_fastmod(uint32 n, uint32 divisor, uint64 fastinv)
+static inline uint16 pg_fastmod(uint16 n, uint16 divisor, uint32 fastinv)
 {
-#ifdef HAVE_INT128
-	uint64_t lowbits = fastinv * n;
-	return ((uint128)lowbits * divisor) >> 64;
-#else
-	return n % divisor;
-#endif
+	uint32 lowbits = fastinv * n;
+	return ((uint64)lowbits * divisor) >> 32;
 }
 
 #endif							/* PG_BITUTILS_H */
-- 
2.39.2

#37Robert Haas
robertmhaas@gmail.com
In reply to: David Christensen (#36)
Re: Initdb-time block size specification

On Thu, Aug 31, 2023 at 2:32 PM David Christensen
<david.christensen@crunchydata.com> wrote:

Here's a patch atop the series which converts to 16-bit uints and
passes regressions, but I don't consider well-vetted at this point.

For what it's worth, my gut reaction to this patch series is similar
to that of Andres: I think it will be a disaster. If the disaster is
not evident to us, that's far more likely to mean that we've failed to
test the right things than it is to mean that there is no disaster.

I don't see that there is a lot of upside, either. I don't think we
have a lot of evidence that changing the block size is really going to
help performance. In fact, my guess is that there are large amounts of
code that are heavily optimized, without the authors even realizing
it, for 8kB blocks, because that's what we've always had. If we had
much larger or smaller blocks, the structure of heap pages or of the
various index AMs used for blocks might no longer be optimal, or might
be less optimal than they are for an 8kB block size. If you use really
large blocks, your blocks may need more internal structure than we
have today in order to avoid CPU inefficiencies. I suspect there's
been so little testing of non-default block sizes that I wouldn't even
count on the code to not be outright buggy.

If we could find a safe way to get rid of full page writes, I would
certainly agree that that was worth considering. I'm not sure that
anything in this thread adds up to that being a reasonable way to go,
but the savings would be massive.

I feel like the proposal here is a bit like deciding to change the
speed limit on all American highways from 65 mph or whatever it is to
130 mph or 32.5 mph and see which way works out best. The whole
infrastructure has basically been designed around the current rules.
The rate of curvature of the roads is appropriate for the speed that
you're currently allowed to drive on them. The vehicles are optimized
for long-term operation at about that speed. The people who drive the
vehicles are accustomed to driving at that speed, and the people who
maintain them are accustomed to the problems that happen when you
drive them at that speed. Just changing the speed limit doesn't change
all that other stuff, and changing all that other stuff is a truly
massive undertaking. Maybe this example somewhat overstates the
difficulties here, but I do think the difficulties are considerable.
The fact that we have 8kB block sizes has affected the thinking of
hundreds of developers over decades in making thousands or tens of
thousands or hundreds of thousands of decisions about algorithm
selection and page format and all kinds of stuff. Even if some other
page size seems to work better in a certain context, it's pretty hard
to believe that it has much chance of being better overall, even
without the added overhead of run-time configuration.

--
Robert Haas
EDB: http://www.enterprisedb.com

#38Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Robert Haas (#37)
Re: Initdb-time block size specification

On 9/1/23 16:57, Robert Haas wrote:

On Thu, Aug 31, 2023 at 2:32 PM David Christensen
<david.christensen@crunchydata.com> wrote:

Here's a patch atop the series which converts to 16-bit uints and
passes regressions, but I don't consider well-vetted at this point.

For what it's worth, my gut reaction to this patch series is similar
to that of Andres: I think it will be a disaster. If the disaster is
not evident to us, that's far more likely to mean that we've failed to
test the right things than it is to mean that there is no disaster.

Perhaps. The block size certainly affects a lot of places - both in
terms of the actual value, and being known (constant) at compile time.

I don't see that there is a lot of upside, either. I don't think we
have a lot of evidence that changing the block size is really going to
help performance.

I don't think that's quite true. We have plenty of empirical evidence
that smaller block sizes bring significant improvements for certain
workloads. And we also have theoretical explanations for why that is.

In fact, my guess is that there are large amounts of
code that are heavily optimized, without the authors even realizing
it, for 8kB blocks, because that's what we've always had. If we had
much larger or smaller blocks, the structure of heap pages or of the
various index AMs used for blocks might no longer be optimal, or might
be less optimal than they are for an 8kB block size. If you use really
large blocks, your blocks may need more internal structure than we
have today in order to avoid CPU inefficiencies. I suspect there's
been so little testing of non-default block sizes that I wouldn't even
count on the code to not be outright buggy.

Sure, and there are even various places where the page size implies hard
limits (e.g. index key size for btree indexes).

But so what? If that matters for your workload, keep using 8kB ...

If we could find a safe way to get rid of full page writes, I would
certainly agree that that was worth considering. I'm not sure that
anything in this thread adds up to that being a reasonable way to go,
but the savings would be massive.

That's true, that'd be great. But that's clearly just a next level of
the optimization. It doesn't mean that if you can't eliminate FPW for
whatever reason it's worthless.

I feel like the proposal here is a bit like deciding to change the
speed limit on all American highways from 65 mph or whatever it is to
130 mph or 32.5 mph and see which way works out best. The whole
infrastructure has basically been designed around the current rules.
The rate of curvature of the roads is appropriate for the speed that
you're currently allowed to drive on them. The vehicles are optimized
for long-term operation at about that speed. The people who drive the
vehicles are accustomed to driving at that speed, and the people who
maintain them are accustomed to the problems that happen when you
drive them at that speed. Just changing the speed limit doesn't change
all that other stuff, and changing all that other stuff is a truly
massive undertaking. Maybe this example somewhat overstates the
difficulties here, but I do think the difficulties are considerable.
The fact that we have 8kB block sizes has affected the thinking of
hundreds of developers over decades in making thousands or tens of
thousands or hundreds of thousands of decisions about algorithm
selection and page format and all kinds of stuff. Even if some other
page size seems to work better in a certain context, it's pretty hard
to believe that it has much chance of being better overall, even
without the added overhead of run-time configuration.

Except that no one is forcing you to actually go 130mph or 32mph, right?
You make it seem like this patch forces people to use some other page
size, but that's clearly not what it's doing - it gives you the option
to use smaller or larger block, if you chose to. Just like increasing
the speed limit to 130mph doesn't mean you can't keep going 65mph.

The thing is - we *already* allow using different block size, except
that you have to do custom build. This just makes it easier.

I don't have strong opinions on how the patch actually does that, and
there certainly can be negative effects of making it dynamic. And yes,
we will have to do more testing with non-default block sizes. But
frankly, that's a gap we probably need to address anyway, considering we
allow changing the block size.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#39Robert Haas
robertmhaas@gmail.com
In reply to: Tomas Vondra (#38)
Re: Initdb-time block size specification

On Sat, Sep 2, 2023 at 3:09 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

Except that no one is forcing you to actually go 130mph or 32mph, right?
You make it seem like this patch forces people to use some other page
size, but that's clearly not what it's doing - it gives you the option
to use smaller or larger block, if you chose to. Just like increasing
the speed limit to 130mph doesn't mean you can't keep going 65mph.

The thing is - we *already* allow using different block size, except
that you have to do custom build. This just makes it easier.

Right. Which is worth doing if it doesn't hurt performance and is
likely to be useful to a lot of people, and is not worth doing if it
will hurt performance and be useful to relatively few people. My bet
is on the latter.

--
Robert Haas
EDB: http://www.enterprisedb.com

#40David Christensen
david.christensen@crunchydata.com
In reply to: Robert Haas (#39)
Re: Initdb-time block size specification

On Tue, Sep 5, 2023 at 9:04 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Sep 2, 2023 at 3:09 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

Except that no one is forcing you to actually go 130mph or 32mph, right?
You make it seem like this patch forces people to use some other page
size, but that's clearly not what it's doing - it gives you the option
to use smaller or larger block, if you chose to. Just like increasing
the speed limit to 130mph doesn't mean you can't keep going 65mph.

The thing is - we *already* allow using different block size, except
that you have to do custom build. This just makes it easier.

Right. Which is worth doing if it doesn't hurt performance and is
likely to be useful to a lot of people, and is not worth doing if it
will hurt performance and be useful to relatively few people. My bet
is on the latter.

Agreed that this doesn't make sense if there are major performance
regressions, however the goal here is patch evaluation to measure this
against other workloads and see if this is the case; from my localized
testing things were within acceptable noise levels with the latest
version.

I agree with Tomas' earlier thoughts: we already allow different block
sizes, and if there are baked-in algorithmic assumptions about block
size (which there probably are), then identifying those or places in
the code where we need additional work or test coverage will only
improve things overall for those non-standard block sizes.

Best,

David

#41Hannu Krosing
hannuk@google.com
In reply to: David Christensen (#40)
Re: Initdb-time block size specification

Something I also asked at this years Unconference - Do we currently
have Build Farm animals testing with different page sizes ?

I'd say that testing all sizes from 4KB up (so 4, 8, 16, 32) should be
done at least before each release if not continuously.

-- Cheers

Hannu

On Tue, Sep 5, 2023 at 4:31 PM David Christensen
<david.christensen@crunchydata.com> wrote:

Show quoted text

On Tue, Sep 5, 2023 at 9:04 AM Robert Haas <robertmhaas@gmail.com> wrote:

On Sat, Sep 2, 2023 at 3:09 PM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:

Except that no one is forcing you to actually go 130mph or 32mph, right?
You make it seem like this patch forces people to use some other page
size, but that's clearly not what it's doing - it gives you the option
to use smaller or larger block, if you chose to. Just like increasing
the speed limit to 130mph doesn't mean you can't keep going 65mph.

The thing is - we *already* allow using different block size, except
that you have to do custom build. This just makes it easier.

Right. Which is worth doing if it doesn't hurt performance and is
likely to be useful to a lot of people, and is not worth doing if it
will hurt performance and be useful to relatively few people. My bet
is on the latter.

Agreed that this doesn't make sense if there are major performance
regressions, however the goal here is patch evaluation to measure this
against other workloads and see if this is the case; from my localized
testing things were within acceptable noise levels with the latest
version.

I agree with Tomas' earlier thoughts: we already allow different block
sizes, and if there are baked-in algorithmic assumptions about block
size (which there probably are), then identifying those or places in
the code where we need additional work or test coverage will only
improve things overall for those non-standard block sizes.

Best,

David

#42Andres Freund
andres@anarazel.de
In reply to: Hannu Krosing (#41)
Re: Initdb-time block size specification

Hi,

On 2023-09-05 21:52:18 +0200, Hannu Krosing wrote:

Something I also asked at this years Unconference - Do we currently
have Build Farm animals testing with different page sizes ?

You can check that yourself as easily as anybody else.

Greetings,

Andres Freund

#43David Christensen
david.christensen@crunchydata.com
In reply to: Hannu Krosing (#41)
Re: Initdb-time block size specification

On Tue, Sep 5, 2023 at 2:52 PM Hannu Krosing <hannuk@google.com> wrote:

Something I also asked at this years Unconference - Do we currently
have Build Farm animals testing with different page sizes ?

I'd say that testing all sizes from 4KB up (so 4, 8, 16, 32) should be
done at least before each release if not continuously.

-- Cheers

Hannu

The regression tests currently have a lot of breakage when running
against non-standard block sizes, so I would assume the answer here is
no. I would expect that we will want to add regression test variants
or otherwise normalize results so they will work with differing block
sizes, but have not done that yet.

#44Hannu Krosing
hannuk@google.com
In reply to: Andres Freund (#42)
Re: Initdb-time block size specification

Sure, I was just hoping that somebody already knew without needing to
specifically check :)

And as I see in David's response, the tests are actually broken for other sizes.

I'll see if I can (convince somebody to) set this up .

Cheers
Hannu

Show quoted text

On Tue, Sep 5, 2023 at 10:23 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2023-09-05 21:52:18 +0200, Hannu Krosing wrote:

Something I also asked at this years Unconference - Do we currently
have Build Farm animals testing with different page sizes ?

You can check that yourself as easily as anybody else.

Greetings,

Andres Freund

#45David Christensen
david.christensen@crunchydata.com
In reply to: David Christensen (#32)
8 attachment(s)
Re: Initdb-time block size specification

Hi,

Here is version 3 of the patch series, rebased on 6d0c39a293. The main
tweak was to tweak per John N's suggestion and utilize only the 16-bit
unsigned mod/div for the utility routines. The breakdown of the patch
series is the same as the last one, but re-including the descriptions here:

Preparation phase:

0001 - add utility script for retokenizing all necessary scripts.
This is mainly for my own use in generating 0003, which is a simple
rename/preparation patch to change all symbols from their UPPER_CASE
to lower_case form, with several exceptions in renames.
0002 - add script to harness 0001 and apply to the relevant files in the
repo
0003 - capture the effects of 0002 on the repo

The other patches in this series are as follows:

0004 - the "main" variable blocksize patch where the bulk of the code
changes take place - see comments here
0005 - utility functions for fast div/mod operations; basically
montgomery multiplication
0006 - use fastdiv code in the visiblity map, the main place where
this change is required
0007 - (optional) add/use libdivide for division which is license
compatible with other headers we bundle
0008 - (optional) tweaks to libdivide to make compiler/CI happy

Best,

David

Attachments:

v3-0002-Add-wrapper-for-tokenizing-the-whole-repo.patchapplication/octet-stream; name=v3-0002-Add-wrapper-for-tokenizing-the-whole-repo.patchDownload
From 73f26893dc4a81c3e727434182ea37281869a558 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Wed, 16 Aug 2023 15:04:02 -0400
Subject: [PATCH v3 2/8] Add wrapper for tokenizing the whole repo

---
 src/tools/replace_blcksz_repo | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100755 src/tools/replace_blcksz_repo

diff --git a/src/tools/replace_blcksz_repo b/src/tools/replace_blcksz_repo
new file mode 100755
index 0000000000..7e122eed2c
--- /dev/null
+++ b/src/tools/replace_blcksz_repo
@@ -0,0 +1,13 @@
+#!/bin/bash -e
+
+# Wrapper for refreshing all of the files in the repo This is separate from the
+# replace_blcksz script itself since we may want to individually use that on
+# patch files or other non-repo externalities. This is the interface for batch
+# updating and should wrap the logic for files which should be excluded, etc.
+
+cd $(git rev-parse --show-toplevel)
+git checkout .
+
+perl -i src/tools/replace_blcksz $(git ls-files | egrep -v 'configure|meson|replace_blcksz|pg_config.h.in')
+perl -i -lpe 'print "#define cluster_block_size BLCKSZ" if /endif.*POSTGRES_EXT_H/' src/include/postgres_ext.h
+
-- 
2.40.1

v3-0005-Add-support-for-fast-non-division-based-div-mod-a.patchapplication/octet-stream; name=v3-0005-Add-support-for-fast-non-division-based-div-mod-a.patchDownload
From a3a005650a7dd3fa8522c6f8c7d3fb3e08215f65 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Fri, 29 Sep 2023 15:02:00 -0400
Subject: [PATCH v3 5/8] Add support for fast, non-division-based div/mod
 algorithms

---
 src/include/port/pg_bitutils.h | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 4f1a13dba9..2d7311ebb5 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -340,4 +340,37 @@ pg_rotate_left32(uint32 word, int n)
 #define pg_prevpower2_size_t pg_prevpower2_64
 #endif
 
+
+/* integer division speedups for constant but runtime divisors */
+
+/*
+ * This value should cached globally and used in the other routines to find
+ * the div/mod quickly relative to `div` operand.  TODO: might have some other
+ * asm-tuned things in port maybe?  general-purpose solution should be ok
+ * though.
+ */
+static inline uint32 pg_fastinverse(uint16 divisor)
+{
+	return UINT32_C(0xFFFFFFFF) / divisor + 1;
+}
+
+/*
+ * pg_fastdiv - calculates the quotient of a 16-bit number against a constant
+ * divisor without using the division operator
+ */
+static inline uint16 pg_fastdiv(uint16 n, uint16 divisor, uint32 fastinv)
+{
+	return (((uint64)(fastinv - 1) * n)) >> 32;
+}
+
+/*
+ * pg_fastmod - calculates the modulus of a 16-bit number against a constant
+ * divisor without using the division operator
+ */
+static inline uint16 pg_fastmod(uint16 n, uint16 divisor, uint32 fastinv)
+{
+	uint32 lowbits = fastinv * n;
+	return ((uint64)lowbits * divisor) >> 32;
+}
+
 #endif							/* PG_BITUTILS_H */
-- 
2.40.1

v3-0001-Add-tool-to-retokenize-for-variable-blocksize.patchapplication/octet-stream; name=v3-0001-Add-tool-to-retokenize-for-variable-blocksize.patchDownload
From 7cbae5d9825eea83146476451601eb1455772f9a Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Wed, 16 Aug 2023 14:58:11 -0400
Subject: [PATCH v3 1/8] Add tool to retokenize for variable blocksize

---
 src/tools/replace_blcksz | 47 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 src/tools/replace_blcksz

diff --git a/src/tools/replace_blcksz b/src/tools/replace_blcksz
new file mode 100644
index 0000000000..0d9ab75b6d
--- /dev/null
+++ b/src/tools/replace_blcksz
@@ -0,0 +1,47 @@
+#!/usr/bin/env -S perl -i
+# -*-cperl-*-
+
+use strict;
+use warnings;
+
+# Construct our replacement list; if a replacement is specified, then we use
+# that, otherwise we just assume lower-cased version of the given identifier.
+
+my %replacements = map { my ($tok,$repl) = split; $tok, $repl || lc $tok } <DATA>;
+
+# Construct a regex that matches and captures any key
+my $matchre = qr/\b(@{[ join '|' => keys %replacements ]})\b/;
+while (<>) {
+    # Replace all found keys with the corresponding lookup value
+    s/$matchre/$replacements{$1}/eg;
+    print
+}
+
+__DATA__
+BLCKSZ  cluster_block_size
+CLUSTER_BLOCK_BITS
+CLUSTER_BLOCK_SIZE
+CLUSTER_RELSEG_SIZE
+COMMIT_TS_XACTS_PER_PAGE
+FAILSAFE_EVERY_PAGES
+FREE_BLOCK_NUMBER_ELEMS
+FSM_CAT_STEP
+GIN_PAGE_FREESIZE
+HASHAGG_READ_BUFFER_SIZE
+HASHAGG_WRITE_BUFFER_SIZE
+HEAP_USABLE_BYTES_PER_PAGE
+LOBLKSIZE	cluster_loblksize
+MULTIXACT_MEMBERGROUPS_PER_PAGE
+MULTIXACT_MEMBERS_PER_PAGE
+MULTIXACT_OFFSETS_PER_PAGE
+REVMAP_CONTENT_SIZE
+SERIAL_PAGESIZE
+SINK_BUFFER_LENGTH
+SPGIST_PAGE_CAPACITY
+SUBTRANS_XACTS_PER_PAGE
+TOAST_MAX_CHUNK_SIZE	cluster_toast_max_chunk_size
+TOAST_TUPLE_TARGET	cluster_toast_tuple_target
+TOAST_TUPLE_TARGET_MAIN
+TOAST_TUPLE_THRESHOLD
+VACUUM_FSM_EVERY_PAGES
+XLOGPREFETCHER_STATS_DISTANCE
-- 
2.40.1

v3-0004-Introduce-initdb-selectable-block-sizes.patchapplication/octet-stream; name=v3-0004-Introduce-initdb-selectable-block-sizes.patchDownload
From ef316c0e74fd02116d06d53cbef9d1e0d5061510 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Fri, 29 Sep 2023 15:01:00 -0400
Subject: [PATCH v3 4/8] Introduce initdb-selectable block sizes

Whereas we had traditionally used BLCKSZ to indicate the compile-time selected
block size, this commit adjusted things so the cluster block size can be
selected at initdb time.

In order to code for this, we introduce a few new defines:

- cluster_block_size is the blocksize for this cluster itself.  This is not
valid until BlockSizeInit() has been called in the given backend, which we do as
early as possible by parsing the ControlFile and using the blcksz field.

- MIN_BLOCK_SIZE and MAX_BLOCK_SIZE are the limits for the selectable block
size. It is required that cluster_block_size is a power of 2 between these two
constants.

- DEFAULT_BLOCK_SIZE is the moral equivalent of BLCKSZ; it is the built-in
default value.  This is used in a few places that just needed a buffer of an
arbitrary size, but the dynamic value cluster_block_size should almost always be
used instead.

- cluster_relseg_size is used instead of RELSEG_SIZE, since internally we are
storing the segment size in terms of number of blocks.  RELSEG_SIZE is still
kept, but is used in terms of the number of blocks of DEFAULT_BLOCK_SIZE;
cluster_relseg_size scales appropriately (and is the only thing used internally)
to keep the same target total segment size regardless of block size.

This patch uses inline functions to switch between compile-time constants for
all blocksize-derived fields; this makes what could be a heavy computation or
memory load/store in hot code paths a relatively fast (~ 4 instructions)
computation for any access to such a dynamic value. Where possible, the
traditional macro names are kept so code that doesn't care about it should not
need to change, however the definition of these has changed (see the CalcXXX()
routines in blocksize.h for details).

A new function, BlockSizeInit() populates the appropriate values based on the
target block size.  This should be called as early as possible in any code that
utilizes block sizes.  This patch adds this in the appropriate place on the
handful of src/bin/ programs that used cluster_block_size, so this caveat mainly impacts new
code.

Headers which define now dynamic values should declare the symbol name and the
calculation macro in order to properly register it; BlockSizeDecl() ends up
creating the necessary static inline function, but only in files which include
the given headers.

Code which had previously used BLCKZ should likely be able to get away with
changing these instances to cluster_block_size, unless you're using a structure
allocated on the stack.  In these cases, the compiler will complain about
dynamic structure.  The solution is to utilize an expression with MAX_BLOCK_SIZE
instead of cluster_block_size, ensuring enough stack space is allocated for the maximum
size. This also does require using cluster_block_size or an expression based on
it when actually using this structure, so in practice more stack space may be
allocated then used in principal; as long as there is plenty of stack this
should have no specific impacts on code.

From what I can tell, TPC-H results are in the neighborhood of the equivalent
unpatched version, which is promising compared to the previous version.
---
 configure                                   |  14 +-
 configure.ac                                |  25 +--
 contrib/amcheck/verify_heapam.c             |  10 +-
 contrib/bloom/bloom.h                       |  22 +-
 contrib/bloom/blutils.c                     |   4 +-
 contrib/bloom/blvacuum.c                    |   2 +-
 contrib/file_fdw/file_fdw.c                 |   4 +-
 contrib/pageinspect/rawpage.c               |   2 +-
 contrib/pg_surgery/heap_surgery.c           |   2 +-
 doc/src/sgml/config.sgml                    |   9 +-
 doc/src/sgml/monitoring.sgml                |   2 +-
 doc/src/sgml/pgfreespacemap.sgml            |   3 +-
 doc/src/sgml/ref/initdb.sgml                |  16 ++
 doc/src/sgml/ref/pg_resetwal.sgml           |  19 ++
 meson.build                                 |  11 +-
 src/backend/access/common/reloptions.c      |  33 ++-
 src/backend/access/common/syncscan.c        |   2 +-
 src/backend/access/common/toast_internals.c |   2 +-
 src/backend/access/gin/gindatapage.c        |   2 +-
 src/backend/access/gin/ginget.c             |   6 +-
 src/backend/access/gin/ginvacuum.c          |   2 +-
 src/backend/access/gist/gist.c              |   2 +-
 src/backend/access/gist/gistvacuum.c        |   6 +-
 src/backend/access/hash/hash.c              |   4 +-
 src/backend/access/hash/hashinsert.c        |   2 +-
 src/backend/access/hash/hashovfl.c          |   8 +-
 src/backend/access/hash/hashpage.c          |   4 +-
 src/backend/access/heap/heapam.c            |  10 +-
 src/backend/access/heap/heapam_handler.c    |   6 +-
 src/backend/access/heap/heaptoast.c         |   2 +-
 src/backend/access/heap/hio.c               |   2 +-
 src/backend/access/heap/pruneheap.c         |  12 +-
 src/backend/access/heap/rewriteheap.c       |   2 +-
 src/backend/access/heap/vacuumlazy.c        |  12 +-
 src/backend/access/heap/visibilitymap.c     |   6 +-
 src/backend/access/nbtree/nbtinsert.c       |   2 +-
 src/backend/access/nbtree/nbtpage.c         |   8 +-
 src/backend/access/nbtree/nbtree.c          |   4 +-
 src/backend/access/nbtree/nbtxlog.c         |   4 +-
 src/backend/access/spgist/spgdoinsert.c     |   2 +-
 src/backend/access/spgist/spgscan.c         |   4 +-
 src/backend/access/spgist/spgvacuum.c       |  22 +-
 src/backend/access/transam/generic_xlog.c   |   2 +-
 src/backend/access/transam/timeline.c       |   2 +-
 src/backend/access/transam/xlog.c           | 130 ++++++++++--
 src/backend/access/transam/xloginsert.c     |   6 +-
 src/backend/backup/basebackup.c             |  13 +-
 src/backend/bootstrap/bootstrap.c           |  14 +-
 src/backend/commands/async.c                |   4 +-
 src/backend/nodes/tidbitmap.c               |   4 +-
 src/backend/po/de.po                        |   2 +-
 src/backend/storage/buffer/bufmgr.c         |   2 +-
 src/backend/storage/buffer/localbuf.c       |   2 +-
 src/backend/storage/file/buffile.c          |   4 +-
 src/backend/storage/file/fd.c               |   4 +-
 src/backend/storage/freespace/freespace.c   |  15 +-
 src/backend/storage/large_object/inv_api.c  |   4 +-
 src/backend/storage/page/bufpage.c          |  12 +-
 src/backend/storage/smgr/README             |  52 +++++
 src/backend/storage/smgr/md.c               |  84 ++++----
 src/backend/utils/init/miscinit.c           |   6 +-
 src/backend/utils/misc/guc.c                |  25 ++-
 src/backend/utils/misc/guc_tables.c         |  39 +++-
 src/backend/utils/sort/logtape.c            |   2 +-
 src/bin/initdb/initdb.c                     |  59 ++++--
 src/bin/initdb/meson.build                  |   1 +
 src/bin/initdb/t/002_blocksize.pl           |  24 +++
 src/bin/pg_basebackup/pg_basebackup.c       |   7 +
 src/bin/pg_basebackup/pg_receivewal.c       |   4 +
 src/bin/pg_basebackup/streamutil.c          |  59 ++++++
 src/bin/pg_basebackup/streamutil.h          |   3 +
 src/bin/pg_checksums/pg_checksums.c         |  22 +-
 src/bin/pg_resetwal/pg_resetwal.c           |  54 ++++-
 src/bin/pg_resetwal/t/002_corrupted.pl      |   4 +-
 src/bin/pg_rewind/filemap.c                 |   1 +
 src/bin/pg_rewind/pg_rewind.c               |   8 +
 src/bin/pg_upgrade/file.c                   |  17 +-
 src/bin/pg_upgrade/meson.build              |   2 +
 src/bin/pg_upgrade/pg_upgrade.c             |   3 +
 src/bin/pg_upgrade/pg_upgrade.h             |   2 +-
 src/bin/pg_verifybackup/pg_verifybackup.c   |   4 +-
 src/bin/pg_waldump/pg_waldump.c             |  31 ++-
 src/bin/pg_waldump/t/001_basic.pl           |   2 +
 src/bin/pg_waldump/t/002_save_fullpage.pl   |   3 +
 src/common/Makefile                         |   1 +
 src/common/blocksize.c                      |  53 +++++
 src/common/file_utils.c                     |   6 +-
 src/common/meson.build                      |   1 +
 src/include/access/gist_private.h           |   2 +-
 src/include/access/hash.h                   |   4 +-
 src/include/access/heapam.h                 |   2 +-
 src/include/access/heaptoast.h              |  20 +-
 src/include/access/htup_details.h           |  16 +-
 src/include/access/itup.h                   |   6 +-
 src/include/access/nbtree.h                 | 207 +-----------------
 src/include/access/nbtree_int.h             | 223 ++++++++++++++++++++
 src/include/access/spgist_private.h         |  10 +-
 src/include/access/xlog_internal.h          |   1 +
 src/include/c.h                             |   6 +-
 src/include/common/blocksize.h              | 119 +++++++++++
 src/include/pg_config.h.in                  |  39 ++--
 src/include/pg_config_manual.h              |   8 +
 src/include/postgres.h                      |   1 +
 src/include/postgres_ext.h                  |   1 -
 src/include/storage/bufpage.h               |  18 +-
 src/include/storage/checksum.h              |   2 +-
 src/include/storage/checksum_impl.h         |  13 +-
 src/include/storage/freespace.h             |   1 +
 src/include/storage/large_object.h          |   2 +
 src/include/storage/off.h                   |   4 +-
 src/include/utils/guc_tables.h              |   1 +
 src/tools/msvc/Mkvcbuild.pm                 |   2 +-
 src/tools/msvc/Solution.pm                  |   2 +-
 src/tools/pgindent/typedefs.list            |   1 -
 114 files changed, 1277 insertions(+), 549 deletions(-)
 create mode 100644 src/bin/initdb/t/002_blocksize.pl
 create mode 100644 src/common/blocksize.c
 create mode 100644 src/include/access/nbtree_int.h
 create mode 100644 src/include/common/blocksize.h

diff --git a/configure b/configure
index d47e0f8b26..d8512697d9 100755
--- a/configure
+++ b/configure
@@ -3711,12 +3711,12 @@ fi
 
 
 case ${blocksize} in
-  1) BLCKSZ=1024;;
-  2) BLCKSZ=2048;;
-  4) BLCKSZ=4096;;
-  8) BLCKSZ=8192;;
- 16) BLCKSZ=16384;;
- 32) BLCKSZ=32768;;
+  1) DEFAULT_BLOCK_SIZE=1024;;
+  2) DEFAULT_BLOCK_SIZE=2048;;
+  4) DEFAULT_BLOCK_SIZE=4096;;
+  8) DEFAULT_BLOCK_SIZE=8192;;
+ 16) DEFAULT_BLOCK_SIZE=16384;;
+ 32) DEFAULT_BLOCK_SIZE=32768;;
   *) as_fn_error $? "Invalid block size. Allowed values are 1,2,4,8,16,32." "$LINENO" 5
 esac
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${blocksize}kB" >&5
@@ -3724,7 +3724,7 @@ $as_echo "${blocksize}kB" >&6; }
 
 
 cat >>confdefs.h <<_ACEOF
-#define BLCKSZ ${BLCKSZ}
+#define DEFAULT_BLOCK_SIZE ${DEFAULT_BLOCK_SIZE}
 _ACEOF
 
 
diff --git a/configure.ac b/configure.ac
index 440b08d113..0d6b8b3b07 100644
--- a/configure.ac
+++ b/configure.ac
@@ -258,28 +258,29 @@ PGAC_ARG_REQ(with, blocksize, [BLOCKSIZE], [set table block size in kB [8]],
              [blocksize=$withval],
              [blocksize=8])
 case ${blocksize} in
-  1) BLCKSZ=1024;;
-  2) BLCKSZ=2048;;
-  4) BLCKSZ=4096;;
-  8) BLCKSZ=8192;;
- 16) BLCKSZ=16384;;
- 32) BLCKSZ=32768;;
+  1) DEFAULT_BLOCK_SIZE=1024;;
+  2) DEFAULT_BLOCK_SIZE=2048;;
+  4) DEFAULT_BLOCK_SIZE=4096;;
+  8) DEFAULT_BLOCK_SIZE=8192;;
+ 16) DEFAULT_BLOCK_SIZE=16384;;
+ 32) DEFAULT_BLOCK_SIZE=32768;;
   *) AC_MSG_ERROR([Invalid block size. Allowed values are 1,2,4,8,16,32.])
 esac
 AC_MSG_RESULT([${blocksize}kB])
 
-AC_DEFINE_UNQUOTED([BLCKSZ], ${BLCKSZ}, [
+AC_DEFINE_UNQUOTED([DEFAULT_BLOCK_SIZE], ${DEFAULT_BLOCK_SIZE}, [
  Size of a disk block --- this also limits the size of a tuple.  You
  can set it bigger if you need bigger tuples (although TOAST should
  reduce the need to have large tuples, since fields can be spread
  across multiple tuples).
 
- BLCKSZ must be a power of 2.  The maximum possible value of BLCKSZ
+ DEFAULT_BLOCK_SIZE must be a power of 2.  The maximum possible value of DEFAULT_BLOCK_SIZE
  is currently 2^15 (32768).  This is determined by the 15-bit widths
  of the lp_off and lp_len fields in ItemIdData (see
  include/storage/itemid.h).
 
- Changing BLCKSZ requires an initdb.
+ Changing DEFAULT_BLOCK_SIZE requires an initdb, however an individual cluster
+ can have its block size set.
 ])
 
 #
@@ -313,10 +314,10 @@ fi
 
 AC_DEFINE_UNQUOTED([RELSEG_SIZE], ${RELSEG_SIZE}, [
  RELSEG_SIZE is the maximum number of blocks allowed in one disk file.
- Thus, the maximum size of a single file is RELSEG_SIZE * BLCKSZ;
+ Thus, the maximum size of a single file is RELSEG_SIZE * cluster_block_size;
  relations bigger than that are divided into multiple files.
 
- RELSEG_SIZE * BLCKSZ must be less than your OS' limit on file size.
+ RELSEG_SIZE * cluster_block_size must be less than your OS' limit on file size.
  This is often 2 GB or 4GB in a 32-bit operating system, unless you
  have large file support enabled.  By default, we make the limit 1 GB
  to avoid any possible integer-overflow problems within the OS.
@@ -350,7 +351,7 @@ esac
 AC_MSG_RESULT([${wal_blocksize}kB])
 
 AC_DEFINE_UNQUOTED([XLOG_BLCKSZ], ${XLOG_BLCKSZ}, [
- Size of a WAL file block.  This need have no particular relation to BLCKSZ.
+ Size of a WAL file block.  This need have no particular relation to cluster_block_size.
  XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O,
  XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O
  buffers, else direct I/O may fail.
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 004aa26b7c..450496dd8a 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -403,11 +403,11 @@ verify_heapam(PG_FUNCTION_ARGS)
 	for (ctx.blkno = first_block; ctx.blkno <= last_block; ctx.blkno++)
 	{
 		OffsetNumber maxoff;
-		OffsetNumber predecessor[MaxOffsetNumber];
-		OffsetNumber successor[MaxOffsetNumber];
-		bool		lp_valid[MaxOffsetNumber];
-		bool		xmin_commit_status_ok[MaxOffsetNumber];
-		XidCommitStatus xmin_commit_status[MaxOffsetNumber];
+		OffsetNumber predecessor[MaxOffsetNumberLimit];
+		OffsetNumber successor[MaxOffsetNumberLimit];
+		bool		lp_valid[MaxOffsetNumberLimit];
+		bool		xmin_commit_status_ok[MaxOffsetNumberLimit];
+		XidCommitStatus xmin_commit_status[MaxOffsetNumberLimit];
 
 		CHECK_FOR_INTERRUPTS();
 
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index d08fe1de26..e3f54ac02b 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -19,6 +19,7 @@
 #include "access/xlog.h"
 #include "fmgr.h"
 #include "nodes/pathnodes.h"
+#include "storage/bufpage.h"
 
 /* Support procedures numbers */
 #define BLOOM_HASH_PROC			1
@@ -106,16 +107,9 @@ typedef struct BloomOptions
 											 * index key */
 } BloomOptions;
 
-/*
- * FreeBlockNumberArray - array of block numbers sized so that metadata fill
- * all space in metapage.
- */
-typedef BlockNumber FreeBlockNumberArray[
-										 MAXALIGN_DOWN(
-													   cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
-													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
-													   ) / sizeof(BlockNumber)
-];
+#define free_block_number_elems CalcFreeBlockNumberElems(cluster_block_size)
+BlockSizeDecl(CalcFreeBlockNumberElems);
+
 
 /* Metadata of bloom index */
 typedef struct BloomMetaPageData
@@ -124,14 +118,18 @@ typedef struct BloomMetaPageData
 	uint16		nStart;
 	uint16		nEnd;
 	BloomOptions opts;
-	FreeBlockNumberArray notFullPage;
+	BlockNumber notFullPage[FLEXIBLE_ARRAY_MEMBER];
 } BloomMetaPageData;
 
+#define SizeOfBloomMetaPageData (offsetof(BloomMetaPageData,notFullPage) + sizeof(BlockNumber) * BloomMetaBlockN)
+
 /* Magic number to distinguish bloom pages among anothers */
 #define BLOOM_MAGICK_NUMBER (0xDBAC0DED)
 
 /* Number of blocks numbers fit in BloomMetaPageData */
-#define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
+#define BloomMetaBlockN		(MAXALIGN_DOWN(cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData)) \
+													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) \
+								  ) / sizeof(BlockNumber))
 
 #define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))
 
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 9d79319e90..89aa31ec73 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -430,10 +430,10 @@ BloomFillMetapage(Relation index, Page metaPage)
 	 */
 	BloomInitPage(metaPage, BLOOM_META);
 	metadata = BloomPageGetMeta(metaPage);
-	memset(metadata, 0, sizeof(BloomMetaPageData));
+	memset(metadata, 0, SizeOfBloomMetaPageData);
 	metadata->magickNumber = BLOOM_MAGICK_NUMBER;
 	metadata->opts = *opts;
-	((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
+	((PageHeader) metaPage)->pd_lower += SizeOfBloomMetaPageData;
 
 	/* If this fails, probably FreeBlockNumberArray size calc is wrong: */
 	Assert(((PageHeader) metaPage)->pd_lower <= ((PageHeader) metaPage)->pd_upper);
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
index 2340d49e00..373227af98 100644
--- a/contrib/bloom/blvacuum.c
+++ b/contrib/bloom/blvacuum.c
@@ -37,7 +37,7 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	Relation	index = info->index;
 	BlockNumber blkno,
 				npages;
-	FreeBlockNumberArray notFullPage;
+	BlockNumber notFullPage[CalcFreeBlockNumberElems(MAX_BLOCK_SIZE)];
 	int			countPage = 0;
 	BloomState	state;
 	Buffer		buffer;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index c265b6e309..13a3a73ebd 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -840,7 +840,7 @@ fileAnalyzeForeignTable(Relation relation,
 	 * Convert size to pages.  Must return at least 1 so that we can tell
 	 * later on that pg_class.relpages is not default.
 	 */
-	*totalpages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
+	*totalpages = (stat_buf.st_size + (cluster_block_size - 1)) >> cluster_block_bits;
 	if (*totalpages < 1)
 		*totalpages = 1;
 
@@ -1015,7 +1015,7 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel,
 	/*
 	 * Convert size to pages for use in I/O cost estimate later.
 	 */
-	pages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
+	pages = (stat_buf.st_size + (cluster_block_size - 1)) >> cluster_block_bits;
 	if (pages < 1)
 		pages = 1;
 	fdw_private->pages = pages;
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index 2527501078..bdeeb027d3 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -357,7 +357,7 @@ page_checksum_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 	if (PageIsNew(page))
 		PG_RETURN_NULL();
 
-	PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
+	PG_RETURN_INT16(pg_checksum_page((char *) page, blkno, cluster_block_size));
 }
 
 Datum
diff --git a/contrib/pg_surgery/heap_surgery.c b/contrib/pg_surgery/heap_surgery.c
index 88a40ab7d3..58c4819675 100644
--- a/contrib/pg_surgery/heap_surgery.c
+++ b/contrib/pg_surgery/heap_surgery.c
@@ -89,7 +89,7 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
 	Relation	rel;
 	OffsetNumber curr_start_ptr,
 				next_start_ptr;
-	bool		include_this_tid[MaxHeapTuplesPerPage];
+	bool		include_this_tid[MaxHeapTuplesPerPageLimit];
 
 	if (RecoveryInProgress())
 		ereport(ERROR,
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 2f7400c83a..c419dd3d78 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -10578,12 +10578,13 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </term>
       <listitem>
        <para>
-        Reports the size of a disk block.  It is determined by the value
-        of <literal>cluster_block_size</literal> when building the server. The default
-        value is 8192 bytes.  The meaning of some configuration
+        Reports the size of a disk block for this cluster.
+        The default value is 8192 bytes.  The meaning of some configuration
         variables (such as <xref linkend="guc-shared-buffers"/>) is
         influenced by <varname>block_size</varname>. See <xref
-        linkend="runtime-config-resource"/> for information.
+        linkend="runtime-config-resource"/> for information on GUC configuration
+        or <xref linkend="app-initdb-blocksize"/> for more information on
+        selecting a cluster's block size.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 92fc70869c..ffcaf8849c 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -2703,7 +2703,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        </para>
        <para>
         Relation data reads, writes, and extends are done in
-        <varname>block_size</varname> units, derived from the build-time
+        <varname>block_size</varname> units, derived from the cluster init-time
         parameter <symbol>cluster_block_size</symbol>, which is <literal>8192</literal> by
         default.
        </para>
diff --git a/doc/src/sgml/pgfreespacemap.sgml b/doc/src/sgml/pgfreespacemap.sgml
index 0da94e5f63..1777c044ab 100644
--- a/doc/src/sgml/pgfreespacemap.sgml
+++ b/doc/src/sgml/pgfreespacemap.sgml
@@ -60,7 +60,8 @@
 
   <para>
    The values stored in the free space map are not exact. They're rounded
-   to precision of 1/256th of <symbol>cluster_block_size</symbol> (32 bytes with default <symbol>cluster_block_size</symbol>), and
+   to precision of 1/256th of <symbol>cluster_block_size</symbol> (32 bytes
+   with default <symbol>cluster_block_size</symbol>), and
    they're not kept fully up-to-date as tuples are inserted and updated.
   </para>
 
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 8a09c5c438..a23b479484 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -277,6 +277,22 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry id="app-initdb-blocksize" xreflabel="blocksize">
+      <term><option>-b <replaceable>blocksize</replaceable></option></term>
+      <term><option>--block-size=<replaceable>blocksize</replaceable></option></term>
+      <listitem>
+       <para>
+        Sets the cluster's database block size
+        to <replaceable>blocksize</replaceable> kilobytes.  This should be one
+        of 1, 2, 4, 8, 16, or 32.  Many database limits are defined relative
+        to this parameter.  If unspecified, defaults to 8.
+       </para>
+       <para>
+        This used to be the compile-time parameter <literal>cluster_block_size</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="app-initdb-option-locale">
       <term><option>--locale=<replaceable>locale</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_resetwal.sgml b/doc/src/sgml/ref/pg_resetwal.sgml
index 08cd3ce5fc..4796d7d6fb 100644
--- a/doc/src/sgml/ref/pg_resetwal.sgml
+++ b/doc/src/sgml/ref/pg_resetwal.sgml
@@ -170,6 +170,25 @@ PostgreSQL documentation
   </para>
 
   <variablelist>
+   <varlistentry>
+    <term><option>-b <replaceable class="parameter">blocksize</replaceable></option></term>
+    <term><option>--block-size=<replaceable class="parameter">blocksize</replaceable></option></term>
+    <listitem>
+     <para>
+      Sets the cluster's database block size
+      to <replaceable>blocksize</replaceable> kilobytes.  This should be one
+      of 1, 2, 4, 8, 16, or 32.  Many database limits are defined relative
+      to this parameter.
+     </para>
+     <para>
+      This <emphasis>must</emphasis> be the same value selected
+      at <command>initdb</command> time, or this will result in database
+      corruption.  This option should <emphasis>only</emphasis> be provided
+      when <filename>pg_control</filename> is corrupted or missing.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><option>-c <replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
     <term><option>--commit-timestamp-ids=<replaceable class="parameter">xid</replaceable>,<replaceable class="parameter">xid</replaceable></option></term>
diff --git a/meson.build b/meson.build
index 5422885b0a..9794be4793 100644
--- a/meson.build
+++ b/meson.build
@@ -444,14 +444,15 @@ else
   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
 endif
 
-cdata.set('BLCKSZ', blocksize, description:
-'''Size of a disk block --- this also limits the size of a tuple. You can set
+cdata.set('DEFAULT_BLOCK_SIZE', blocksize, description:
+'''Default size of a disk block --- this also limits the size of a tuple. You can set
    it bigger if you need bigger tuples (although TOAST should reduce the need
    to have large tuples, since fields can be spread across multiple tuples).
-   BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
+   DEFAULT_BLOCK_SIZE must be a power of 2. The maximum possible value of DEFAULT_BLOCK_SIZE is
    currently 2^15 (32768). This is determined by the 15-bit widths of the
    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
-   Changing BLCKSZ requires an initdb.''')
+   Changing DEFAULT_BLOCK_SIZE requires an initdb, however individual clusters
+   can have different values for block size (cluster_block_size)''')
 
 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
 cdata.set('RELSEG_SIZE', segsize)
@@ -3336,7 +3337,7 @@ if meson.version().version_compare('>=0.57')
 
   summary(
     {
-      'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
+      'data block size': '@0@ kB'.format(cdata.get('DEFAULT_BLOCK_SIZE') / 1024),
       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
       'segment size': get_option('segsize_blocks') != 0 ?
         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 52d4ee6834..9d8a16f6ad 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -28,6 +28,7 @@
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
 #include "commands/view.h"
+#include "common/blocksize.h"
 #include "nodes/makefuncs.h"
 #include "postmaster/postmaster.h"
 #include "utils/array.h"
@@ -328,7 +329,8 @@ static relopt_int intRelOpts[] =
 			RELOPT_KIND_HEAP,
 			ShareUpdateExclusiveLock
 		},
-		cluster_toast_tuple_target, 128, toast_tuple_target_main
+		/* NOTE: these limits are dynamically updated */
+		0, 0, 0
 	},
 	{
 		{
@@ -563,6 +565,8 @@ static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 								int text_len, bool validate);
 
+static void update_dynamic_reloptions(void);
+
 /*
  * Get the length of a string reloption (either default or the user-defined
  * value).  This is used for allocation purposes when building a set of
@@ -572,6 +576,26 @@ static void parse_one_reloption(relopt_value *option, char *text_str,
 	((option).isset ? strlen((option).values.string_val) : \
 	 ((relopt_string *) (option).gen)->default_len)
 
+/*
+ * handle adjustments to the config table based on dynamic parameters' limits
+ */
+
+static void
+update_dynamic_reloptions(void)
+{
+	int i;
+
+	for (i = 0; intRelOpts[i].gen.name; i++)
+	{
+		if (strcmp("toast_tuple_target", intRelOpts[i].gen.name) == 0)
+		{
+			intRelOpts[i].min = 128;
+			intRelOpts[i].default_val = cluster_toast_tuple_target;
+			intRelOpts[i].max = toast_tuple_target_main;
+		}
+	}
+}
+
 /*
  * initialize_reloptions
  *		initialization routine, must be called before parsing
@@ -584,6 +608,13 @@ initialize_reloptions(void)
 	int			i;
 	int			j;
 
+	/*
+	 * Set the dynamic limits based on block size; if we get multiple can make
+	 * more sophisticated.
+	 */
+
+	update_dynamic_reloptions();
+
 	j = 0;
 	for (i = 0; boolRelOpts[i].gen.name; i++)
 	{
diff --git a/src/backend/access/common/syncscan.c b/src/backend/access/common/syncscan.c
index 3aa54f28d4..1d8e4b35a9 100644
--- a/src/backend/access/common/syncscan.c
+++ b/src/backend/access/common/syncscan.c
@@ -80,7 +80,7 @@ bool		trace_syncscan = false;
  * the buffer cache anyway, and on the other hand the page is most likely
  * still in the OS cache.
  */
-#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / cluster_block_size)
+#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 >> cluster_block_bits)
 
 
 /*
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 63edaa8192..dee327f1ea 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -133,7 +133,7 @@ toast_save_datum(Relation rel, Datum value,
 	{
 		struct varlena hdr;
 		/* this is to make the union big enough for a chunk: */
-		char		data[cluster_toast_max_chunk_size + VARHDRSZ];
+		char		data[TOAST_MAX_CHUNK_SIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			chunk_data;
diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
index 19eb645794..61ab3bf271 100644
--- a/src/backend/access/gin/gindatapage.c
+++ b/src/backend/access/gin/gindatapage.c
@@ -1259,7 +1259,7 @@ dataSplitPageInternal(GinBtree btree, Buffer origbuf,
 	Page		lpage;
 	Page		rpage;
 	OffsetNumber separator;
-	PostingItem allitems[(cluster_block_size / sizeof(PostingItem)) + 1];
+	PostingItem allitems[(MAX_BLOCK_SIZE / sizeof(PostingItem)) + 1];
 
 	lpage = PageGetTempPage(oldpage);
 	rpage = PageGetTempPage(oldpage);
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index e104519085..9215287e85 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1628,9 +1628,9 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
 	 */
 	for (;;)
 	{
-		Datum		datum[cluster_block_size / sizeof(IndexTupleData)];
-		GinNullCategory category[cluster_block_size / sizeof(IndexTupleData)];
-		bool		datumExtracted[cluster_block_size / sizeof(IndexTupleData)];
+		Datum		datum[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
+		GinNullCategory category[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
+		bool		datumExtracted[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
 
 		Assert(pos->lastOffset > pos->firstOffset);
 		memset(datumExtracted + pos->firstOffset - 1, 0,
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index 628d0f3a40..c7198d85c9 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -569,7 +569,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	BlockNumber blkno = GIN_ROOT_BLKNO;
 	GinVacuumState gvs;
 	Buffer		buffer;
-	BlockNumber rootOfPostingTree[cluster_block_size / (sizeof(IndexTupleData) + sizeof(ItemId))];
+	BlockNumber rootOfPostingTree[MAX_BLOCK_SIZE / (sizeof(IndexTupleData) + sizeof(ItemId))];
 	uint32		nRoot;
 
 	gvs.tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8ef5fa0329..0301944974 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -1662,7 +1662,7 @@ freeGISTstate(GISTSTATE *giststate)
 static void
 gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
 {
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				maxoff;
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index 3f60d3274d..53a29e55b3 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -309,7 +309,7 @@ restart:
 	}
 	else if (GistPageIsLeaf(page))
 	{
-		OffsetNumber todelete[MaxOffsetNumber];
+		OffsetNumber todelete[MaxOffsetNumberLimit];
 		int			ntodelete = 0;
 		int			nremain;
 		GISTPageOpaque opaque = GistPageGetOpaque(page);
@@ -476,8 +476,8 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistVacState *vstate)
 		Page		page;
 		OffsetNumber off,
 					maxoff;
-		OffsetNumber todelete[MaxOffsetNumber];
-		BlockNumber leafs_to_delete[MaxOffsetNumber];
+		OffsetNumber todelete[MaxOffsetNumberLimit];
+		BlockNumber leafs_to_delete[MaxOffsetNumberLimit];
 		int			ntodelete;
 		int			deleted;
 
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index ea78dc81c2..452119220d 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -152,7 +152,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * one page.  Also, "initial index size" accounting does not include the
 	 * metapage, nor the first bitmap page.
 	 */
-	sort_threshold = (maintenance_work_mem * 1024L) / cluster_block_size;
+	sort_threshold = (maintenance_work_mem * 1024L) >> cluster_block_bits;
 	if (index->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
 		sort_threshold = Min(sort_threshold, NBuffers);
 	else
@@ -709,7 +709,7 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf,
 		OffsetNumber maxoffno;
 		Buffer		next_buf;
 		Page		page;
-		OffsetNumber deletable[MaxOffsetNumber];
+		OffsetNumber deletable[MaxOffsetNumberLimit];
 		int			ndeletable = 0;
 		bool		retain_pin = false;
 		bool		clear_dead_marking = false;
diff --git a/src/backend/access/hash/hashinsert.c b/src/backend/access/hash/hashinsert.c
index 22656b24e2..48d986272a 100644
--- a/src/backend/access/hash/hashinsert.c
+++ b/src/backend/access/hash/hashinsert.c
@@ -371,7 +371,7 @@ _hash_pgaddmultitup(Relation rel, Buffer buf, IndexTuple *itups,
 static void
 _hash_vacuum_one_page(Relation rel, Relation hrel, Buffer metabuf, Buffer buf)
 {
-	OffsetNumber deletable[MaxOffsetNumber];
+	OffsetNumber deletable[MaxOffsetNumberLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				maxoff;
diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.c
index 39bb2cb9f6..dd414c557e 100644
--- a/src/backend/access/hash/hashovfl.c
+++ b/src/backend/access/hash/hashovfl.c
@@ -866,10 +866,10 @@ _hash_squeezebucket(Relation rel,
 	{
 		OffsetNumber roffnum;
 		OffsetNumber maxroffnum;
-		OffsetNumber deletable[MaxOffsetNumber];
-		IndexTuple	itups[MaxIndexTuplesPerPage];
-		Size		tups_size[MaxIndexTuplesPerPage];
-		OffsetNumber itup_offsets[MaxIndexTuplesPerPage];
+		OffsetNumber deletable[MaxOffsetNumberLimit];
+		IndexTuple	itups[MaxIndexTuplesPerPageLimit];
+		Size		tups_size[MaxIndexTuplesPerPageLimit];
+		OffsetNumber itup_offsets[MaxIndexTuplesPerPageLimit];
 		uint16		ndeletable = 0;
 		uint16		nitups = 0;
 		Size		all_tups_size = 0;
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 2a77d13feb..1cc99ae27a 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -1087,8 +1087,8 @@ _hash_splitbucket(Relation rel,
 	Page		npage;
 	HashPageOpaque oopaque;
 	HashPageOpaque nopaque;
-	OffsetNumber itup_offsets[MaxIndexTuplesPerPage];
-	IndexTuple	itups[MaxIndexTuplesPerPage];
+	OffsetNumber itup_offsets[MaxIndexTuplesPerPageLimit];
+	IndexTuple	itups[MaxIndexTuplesPerPageLimit];
 	Size		all_tups_size = 0;
 	int			i;
 	uint16		nitups = 0;
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index a5975efe3e..3f8a5cbe82 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -6732,8 +6732,8 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
 	/* Now WAL-log freezing if necessary */
 	if (RelationNeedsWAL(rel))
 	{
-		xl_heap_freeze_plan plans[MaxHeapTuplesPerPage];
-		OffsetNumber offsets[MaxHeapTuplesPerPage];
+		xl_heap_freeze_plan plans[MaxHeapTuplesPerPageLimit];
+		OffsetNumber offsets[MaxHeapTuplesPerPageLimit];
 		int			nplans;
 		xl_heap_freeze_page xlrec;
 		XLogRecPtr	recptr;
@@ -9213,7 +9213,7 @@ heap_xlog_insert(XLogReaderState *record)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	HeapTupleHeader htup;
 	xl_heap_header xlhdr;
@@ -9335,7 +9335,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	HeapTupleHeader htup;
 	uint32		newlen;
@@ -9492,7 +9492,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	union
 	{
 		HeapTupleHeaderData hdr;
-		char		data[MaxHeapTupleSize];
+		char		data[MaxHeapTupleSizeLimit];
 	}			tbuf;
 	xl_heap_header xlhdr;
 	uint32		newlen;
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 2e08383400..5361edda4a 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -1191,7 +1191,7 @@ heapam_index_build_range_scan(Relation heapRelation,
 	TransactionId OldestXmin;
 	BlockNumber previous_blkno = InvalidBlockNumber;
 	BlockNumber root_blkno = InvalidBlockNumber;
-	OffsetNumber root_offsets[MaxHeapTuplesPerPage];
+	OffsetNumber root_offsets[MaxHeapTuplesPerPageLimit];
 
 	/*
 	 * sanity checks
@@ -1754,8 +1754,8 @@ heapam_index_validate_scan(Relation heapRelation,
 	EState	   *estate;
 	ExprContext *econtext;
 	BlockNumber root_blkno = InvalidBlockNumber;
-	OffsetNumber root_offsets[MaxHeapTuplesPerPage];
-	bool		in_index[MaxHeapTuplesPerPage];
+	OffsetNumber root_offsets[MaxHeapTuplesPerPageLimit];
+	bool		in_index[MaxHeapTuplesPerPageLimit];
 	BlockNumber previous_blkno = InvalidBlockNumber;
 
 	/* state variables for the merge */
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 98b0a701e5..6c2d1c9dc1 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -770,7 +770,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 			chcpyend = (sliceoffset + slicelength - 1) % cluster_toast_max_chunk_size;
 
 		memcpy(VARDATA(result) +
-			   (curchunk * cluster_toast_max_chunk_size - sliceoffset) + chcpystrt,
+			   ((int32)(curchunk * cluster_toast_max_chunk_size) - sliceoffset) + chcpystrt,
 			   chunkdata + chcpystrt,
 			   (chcpyend - chcpystrt) + 1);
 
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index caa62708aa..4f2018410a 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -534,7 +534,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("row is too big: size %zu, maximum size %zu",
-						len, MaxHeapTupleSize)));
+						len, (Size)MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
 	saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 3942e80696..a6cda85978 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -42,9 +42,9 @@ typedef struct
 	int			ndead;
 	int			nunused;
 	/* arrays that accumulate indexes of items to be changed */
-	OffsetNumber redirected[MaxHeapTuplesPerPage * 2];
-	OffsetNumber nowdead[MaxHeapTuplesPerPage];
-	OffsetNumber nowunused[MaxHeapTuplesPerPage];
+	OffsetNumber redirected[MaxHeapTuplesPerPageLimit * 2];
+	OffsetNumber nowdead[MaxHeapTuplesPerPageLimit];
+	OffsetNumber nowunused[MaxHeapTuplesPerPageLimit];
 
 	/*
 	 * marked[i] is true if item i is entered in one of the above arrays.
@@ -52,7 +52,7 @@ typedef struct
 	 * This needs to be MaxHeapTuplesPerPage + 1 long as FirstOffsetNumber is
 	 * 1. Otherwise every access would need to subtract 1.
 	 */
-	bool		marked[MaxHeapTuplesPerPage + 1];
+	bool		marked[MaxHeapTuplesPerPageLimit + 1];
 
 	/*
 	 * Tuple visibility is only computed once for each tuple, for correctness
@@ -62,7 +62,7 @@ typedef struct
 	 *
 	 * Same indexing as ->marked.
 	 */
-	int8		htsv[MaxHeapTuplesPerPage + 1];
+	int8		htsv[MaxHeapTuplesPerPageLimit + 1];
 } PruneState;
 
 /* Local functions */
@@ -483,7 +483,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
 	OffsetNumber latestdead = InvalidOffsetNumber,
 				maxoff = PageGetMaxOffsetNumber(dp),
 				offnum;
-	OffsetNumber chainitems[MaxHeapTuplesPerPage];
+	OffsetNumber chainitems[MaxHeapTuplesPerPageLimit];
 	int			nchain = 0,
 				i;
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 3402625d6d..9f9c90d6ea 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -657,7 +657,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("row is too big: size %zu, maximum size %zu",
-						len, MaxHeapTupleSize)));
+						len, (Size)MaxHeapTupleSize)));
 
 	/* Compute desired extra freespace due to fillfactor option */
 	saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index d0011073b3..52fa1832ba 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -98,7 +98,7 @@
  * (Note that this is deliberately kept to a power-of-two, usually 2^19.)
  */
 #define failsafe_every_pages \
-	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / cluster_block_size))
+	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) >> cluster_block_bits))
 
 /*
  * When a table has no indexes, vacuum the FSM after every 8GB, approximately
@@ -107,7 +107,7 @@
  * and we vacuum FSM after each index/heap cleaning pass.
  */
 #define vacuum_fsm_every_pages \
-	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / cluster_block_size))
+	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) >> cluster_block_bits))
 
 /*
  * Before we consider skipping a page that's marked as clean in
@@ -1551,8 +1551,8 @@ lazy_scan_prune(LVRelState *vacrel,
 				recently_dead_tuples;
 	HeapPageFreeze pagefrz;
 	int64		fpi_before = pgWalUsage.wal_fpi;
-	OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
-	HeapTupleFreeze frozen[MaxHeapTuplesPerPage];
+	OffsetNumber deadoffsets[MaxHeapTuplesPerPageLimit];
+	HeapTupleFreeze frozen[MaxHeapTuplesPerPageLimit];
 
 	Assert(BufferGetBlockNumber(buf) == blkno);
 
@@ -1967,7 +1967,7 @@ lazy_scan_noprune(LVRelState *vacrel,
 	HeapTupleHeader tupleheader;
 	TransactionId NoFreezePageRelfrozenXid = vacrel->NewRelfrozenXid;
 	MultiXactId NoFreezePageRelminMxid = vacrel->NewRelminMxid;
-	OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
+	OffsetNumber deadoffsets[MaxHeapTuplesPerPageLimit];
 
 	Assert(BufferGetBlockNumber(buf) == blkno);
 
@@ -2521,7 +2521,7 @@ lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer,
 {
 	VacDeadItems *dead_items = vacrel->dead_items;
 	Page		page = BufferGetPage(buffer);
-	OffsetNumber unused[MaxHeapTuplesPerPage];
+	OffsetNumber unused[MaxHeapTuplesPerPageLimit];
 	int			nunused = 0;
 	TransactionId visibility_cutoff_xid;
 	bool		all_frozen;
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 833774c164..ab4d38fbe6 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -111,7 +111,7 @@
 #define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
 
 /* Number of heap blocks we can represent in one visibility map page. */
-#define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE)
+#define HEAPBLOCKS_PER_PAGE (MAPSIZE << BITS_PER_HEAPBLOCK)
 
 /* Mapping from heap block number to the right bit in the visibility map */
 #define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
@@ -414,8 +414,8 @@ visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_fro
 		 */
 		map = (uint64 *) PageGetContents(BufferGetPage(mapBuffer));
 
-		StaticAssertStmt(MAPSIZE % sizeof(uint64) == 0,
-						 "unsupported MAPSIZE");
+		Assert(MAPSIZE % sizeof(uint64) == 0);
+
 		if (all_frozen == NULL)
 		{
 			for (i = 0; i < MAPSIZE / sizeof(uint64); i++)
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 9cff4f2931..a75de71659 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -2685,7 +2685,7 @@ _bt_delete_or_dedup_one_page(Relation rel, Relation heapRel,
 							 bool simpleonly, bool checkingunique,
 							 bool uniquedup, bool indexUnchanged)
 {
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 	int			ndeletable = 0;
 	OffsetNumber offnum,
 				minoff,
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index db248af32a..4fea55edde 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -1160,7 +1160,7 @@ _bt_delitems_vacuum(Relation rel, Buffer buf,
 	bool		needswal = RelationNeedsWAL(rel);
 	char	   *updatedbuf = NULL;
 	Size		updatedbuflen = 0;
-	OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
+	OffsetNumber updatedoffsets[MaxIndexTuplesPerPageLimit];
 
 	/* Shouldn't be called unless there's something to do */
 	Assert(ndeletable > 0 || nupdatable > 0);
@@ -1291,7 +1291,7 @@ _bt_delitems_delete(Relation rel, Buffer buf,
 	bool		needswal = RelationNeedsWAL(rel);
 	char	   *updatedbuf = NULL;
 	Size		updatedbuflen = 0;
-	OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
+	OffsetNumber updatedoffsets[MaxIndexTuplesPerPageLimit];
 
 	/* Shouldn't be called unless there's something to do */
 	Assert(ndeletable > 0 || nupdatable > 0);
@@ -1524,8 +1524,8 @@ _bt_delitems_delete_check(Relation rel, Buffer buf, Relation heapRel,
 	OffsetNumber postingidxoffnum = InvalidOffsetNumber;
 	int			ndeletable = 0,
 				nupdatable = 0;
-	OffsetNumber deletable[MaxIndexTuplesPerPage];
-	BTVacuumPosting updatable[MaxIndexTuplesPerPage];
+	OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
+	BTVacuumPosting updatable[MaxIndexTuplesPerPageLimit];
 
 	/* Use tableam interface to determine which tuples to delete first */
 	snapshotConflictHorizon = table_index_delete_tuples(heapRel, delstate);
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index f4d8aae306..e5df2a3da5 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -1157,9 +1157,9 @@ backtrack:
 	}
 	else if (P_ISLEAF(opaque))
 	{
-		OffsetNumber deletable[MaxIndexTuplesPerPage];
+		OffsetNumber deletable[MaxIndexTuplesPerPageLimit];
 		int			ndeletable;
-		BTVacuumPosting updatable[MaxIndexTuplesPerPage];
+		BTVacuumPosting updatable[MaxIndexTuplesPerPageLimit];
 		int			nupdatable;
 		OffsetNumber offnum,
 					minoff,
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index c87e46ed66..7035665eaf 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -40,8 +40,8 @@ _bt_restore_page(Page page, char *from, int len)
 	IndexTupleData itupdata;
 	Size		itemsz;
 	char	   *end = from + len;
-	Item		items[MaxIndexTuplesPerPage];
-	uint16		itemsizes[MaxIndexTuplesPerPage];
+	Item		items[MaxIndexTuplesPerPageLimit];
+	uint16		itemsizes[MaxIndexTuplesPerPageLimit];
 	int			i;
 	int			nitems;
 
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index d8c2752e85..02c5b31907 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -135,7 +135,7 @@ spgPageIndexMultiDelete(SpGistState *state, Page page,
 						BlockNumber blkno, OffsetNumber offnum)
 {
 	OffsetNumber firstItem;
-	OffsetNumber sortednos[MaxIndexTuplesPerPage];
+	OffsetNumber sortednos[MaxIndexTuplesPerPageLimit];
 	SpGistDeadTuple tuple = NULL;
 	int			i;
 
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index 8c00b724db..a9f1b0eedb 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -755,8 +755,8 @@ spgGetNextQueueItem(SpGistScanOpaque so)
 enum SpGistSpecialOffsetNumbers
 {
 	SpGistBreakOffsetNumber = InvalidOffsetNumber,
-	SpGistRedirectOffsetNumber = MaxOffsetNumber + 1,
-	SpGistErrorOffsetNumber = MaxOffsetNumber + 2
+	SpGistRedirectOffsetNumber = MaxOffsetNumberLimit + 1,
+	SpGistErrorOffsetNumber = MaxOffsetNumberLimit + 2
 };
 
 static OffsetNumber
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index 8a5b540c80..f561ff6e2b 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -128,14 +128,14 @@ vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer,
 {
 	Page		page = BufferGetPage(buffer);
 	spgxlogVacuumLeaf xlrec;
-	OffsetNumber toDead[MaxIndexTuplesPerPage];
-	OffsetNumber toPlaceholder[MaxIndexTuplesPerPage];
-	OffsetNumber moveSrc[MaxIndexTuplesPerPage];
-	OffsetNumber moveDest[MaxIndexTuplesPerPage];
-	OffsetNumber chainSrc[MaxIndexTuplesPerPage];
-	OffsetNumber chainDest[MaxIndexTuplesPerPage];
-	OffsetNumber predecessor[MaxIndexTuplesPerPage + 1];
-	bool		deletable[MaxIndexTuplesPerPage + 1];
+	OffsetNumber toDead[MaxIndexTuplesPerPageLimit];
+	OffsetNumber toPlaceholder[MaxIndexTuplesPerPageLimit];
+	OffsetNumber moveSrc[MaxIndexTuplesPerPageLimit];
+	OffsetNumber moveDest[MaxIndexTuplesPerPageLimit];
+	OffsetNumber chainSrc[MaxIndexTuplesPerPageLimit];
+	OffsetNumber chainDest[MaxIndexTuplesPerPageLimit];
+	OffsetNumber predecessor[MaxIndexTuplesPerPageLimit + 1];
+	bool		deletable[MaxIndexTuplesPerPageLimit + 1];
 	int			nDeletable;
 	OffsetNumber i,
 				max = PageGetMaxOffsetNumber(page);
@@ -408,7 +408,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
 {
 	Page		page = BufferGetPage(buffer);
 	spgxlogVacuumRoot xlrec;
-	OffsetNumber toDelete[MaxIndexTuplesPerPage];
+	OffsetNumber toDelete[MaxIndexTuplesPerPageLimit];
 	OffsetNumber i,
 				max = PageGetMaxOffsetNumber(page);
 
@@ -498,8 +498,8 @@ vacuumRedirectAndPlaceholder(Relation index, Relation heaprel, Buffer buffer)
 				firstPlaceholder = InvalidOffsetNumber;
 	bool		hasNonPlaceholder = false;
 	bool		hasUpdate = false;
-	OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPage];
-	OffsetNumber itemnos[MaxIndexTuplesPerPage];
+	OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPageLimit];
+	OffsetNumber itemnos[MaxIndexTuplesPerPageLimit];
 	spgxlogVacuumRedirect xlrec;
 	GlobalVisState *vistest;
 
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 771dde3419..ba98cad70b 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -45,7 +45,7 @@
  */
 #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
 #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
-#define MAX_DELTA_SIZE			(cluster_block_size + 2 * FRAGMENT_HEADER_SIZE)
+#define MAX_DELTA_SIZE			(MAX_BLOCK_SIZE + 2 * FRAGMENT_HEADER_SIZE)
 
 /* Struct of generic xlog data for single page */
 typedef struct
diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c
index 8b4ed3d053..24319d1aeb 100644
--- a/src/backend/access/transam/timeline.c
+++ b/src/backend/access/transam/timeline.c
@@ -307,7 +307,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
 	char		path[MAXPGPATH];
 	char		tmppath[MAXPGPATH];
 	char		histfname[MAXFNAMELEN];
-	char		buffer[cluster_block_size];
+	char		buffer[DEFAULT_BLOCK_SIZE];
 	int			srcfd;
 	int			fd;
 	int			nbytes;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b0b56c81d9..4f6437980f 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -67,6 +67,7 @@
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_database.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_utils.h"
 #include "executor/instrument.h"
@@ -3923,7 +3924,7 @@ WriteControlFile(void)
 	ControlFile->floatFormat = FLOATFORMAT_VALUE;
 
 	ControlFile->blcksz = cluster_block_size;
-	ControlFile->relseg_size = RELSEG_SIZE;
+	ControlFile->relseg_size = cluster_relseg_size;
 	ControlFile->xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile->xlog_seg_size = wal_segment_size;
 
@@ -3989,14 +3990,104 @@ WriteControlFile(void)
 						XLOG_CONTROL_FILE)));
 }
 
+extern int block_size;
+
+/*
+ * This routine reads and returns the block size from the control file as
+ * needed; since this is the only field we care about and it needs to be
+ * handled early in the process, we don't worry about locks and the like; this
+ * is static for the life of the cluster.
+ */
+uint32
+ClusterBlockSize(void)
+{
+	pg_crc32c	crc;
+	int fd, r;
+	ControlFileData cluster_control;
+
+	if (block_size)
+		return block_size;
+
+	if (ControlFile)
+		return ControlFile->blcksz;
+
+	// TODO: check for bootstrap mode, since that's passed in the param?
+
+	/* neither shortcut worked, so go ahead and open the control file and
+	 * parse out only basic field */
+	fd = BasicOpenFile(XLOG_CONTROL_FILE,
+					   O_RDONLY | PG_BINARY);
+	if (fd < 0)
+		ereport(PANIC,
+				(errcode_for_file_access(),
+				 errmsg("could not open file \"%s\": %m",
+						XLOG_CONTROL_FILE)));
+
+	r = read(fd, &cluster_control, sizeof(ControlFileData));
+	if (r != sizeof(ControlFileData))
+	{
+		if (r < 0)
+			ereport(PANIC,
+					(errcode_for_file_access(),
+					 errmsg("could not read file \"%s\": %m",
+							XLOG_CONTROL_FILE)));
+		else
+			ereport(PANIC,
+					(errcode(ERRCODE_DATA_CORRUPTED),
+					 errmsg("could not read file \"%s\": read %d of %zu",
+							XLOG_CONTROL_FILE, r, sizeof(ControlFileData))));
+	}
+	close(fd);
+
+	/*
+	 * Check for expected pg_control format version.  If this is wrong, the
+	 * CRC check will likely fail because we'll be checking the wrong number
+	 * of bytes.  Complaining about wrong version will probably be more
+	 * enlightening than complaining about wrong CRC.
+	 */
+
+	if (cluster_control.pg_control_version != PG_CONTROL_VERSION && cluster_control.pg_control_version % 65536 == 0 && cluster_control.pg_control_version / 65536 != 0)
+		ereport(FATAL,
+				(errmsg("database files are incompatible with server"),
+				 errdetail("The database cluster was initialized with PG_CONTROL_VERSION %d (0x%08x),"
+						   " but the server was compiled with PG_CONTROL_VERSION %d (0x%08x).",
+						   cluster_control.pg_control_version, cluster_control.pg_control_version,
+						   PG_CONTROL_VERSION, PG_CONTROL_VERSION),
+				 errhint("This could be a problem of mismatched byte ordering.  It looks like you need to initdb.")));
+
+	if (cluster_control.pg_control_version != PG_CONTROL_VERSION)
+		ereport(FATAL,
+				(errmsg("database files are incompatible with server"),
+				 errdetail("The database cluster was initialized with PG_CONTROL_VERSION %d,"
+						   " but the server was compiled with PG_CONTROL_VERSION %d.",
+						   cluster_control.pg_control_version, PG_CONTROL_VERSION),
+				 errhint("It looks like you need to initdb.")));
+
+	/* Now check the CRC. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc,
+				(char *) &cluster_control,
+				offsetof(ControlFileData, crc));
+	FIN_CRC32C(crc);
+
+	if (!EQ_CRC32C(crc, cluster_control.crc))
+		ereport(FATAL,
+				(errmsg("incorrect checksum in control file")));
+
+	block_size = cluster_control.blcksz;
+
+	return block_size;
+}
+
 static void
 ReadControlFile(void)
 {
 	pg_crc32c	crc;
 	int			fd;
 	static char wal_segsz_str[20];
+	static char block_size_str[20];
 	int			r;
-
+	int block_size;
 	/*
 	 * Read data...
 	 */
@@ -4062,6 +4153,26 @@ ReadControlFile(void)
 		ereport(FATAL,
 				(errmsg("incorrect checksum in control file")));
 
+	/*
+	 * Block size computations affect a number of things that are later
+	 * checked, so ensure that we calculate as soon as CRC has been validated
+	 * before checking other things that may depend on it.
+	 */
+
+	block_size = ControlFile->blcksz;
+
+	if (!IsValidBlockSize(block_size))
+		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg_plural("Block size must be a power of two between 1k and 32k, but the control file specifies %d byte",
+									  "Block size must be a power of two between 1k and 32k, but the control file specifies %d bytes",
+									  block_size,
+									  block_size)));
+
+	BlockSizeInit(block_size);
+	snprintf(block_size_str, sizeof(block_size_str), "%d", block_size);
+	SetConfigOption("block_size", block_size_str, PGC_INTERNAL,
+					PGC_S_DYNAMIC_DEFAULT);
+
 	/*
 	 * Do compatibility checking immediately.  If the database isn't
 	 * compatible with the backend executable, we want to abort before we can
@@ -4086,19 +4197,12 @@ ReadControlFile(void)
 				(errmsg("database files are incompatible with server"),
 				 errdetail("The database cluster appears to use a different floating-point number format than the server executable."),
 				 errhint("It looks like you need to initdb.")));
-	if (ControlFile->blcksz != cluster_block_size)
-		ereport(FATAL,
-				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with cluster_block_size %d,"
-						   " but the server was compiled with cluster_block_size %d.",
-						   ControlFile->blcksz, cluster_block_size),
-				 errhint("It looks like you need to recompile or initdb.")));
-	if (ControlFile->relseg_size != RELSEG_SIZE)
+	if (ControlFile->relseg_size != cluster_relseg_size)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with RELSEG_SIZE %d,"
-						   " but the server was compiled with RELSEG_SIZE %d.",
-						   ControlFile->relseg_size, RELSEG_SIZE),
+				 errdetail("The database cluster was initialized with cluster_relseg_size %d,"
+						   " but the server was compiled with cluster_relseg_size %d.",
+						   ControlFile->relseg_size, cluster_relseg_size),
 				 errhint("It looks like you need to recompile or initdb.")));
 	if (ControlFile->xlog_blcksz != XLOG_BLCKSZ)
 		ereport(FATAL,
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 87e04d6e03..d833a5734a 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -46,18 +46,18 @@
  * backup block image.
  */
 #ifdef USE_LZ4
-#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(cluster_block_size)
+#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(MAX_BLOCK_SIZE)
 #else
 #define LZ4_MAX_BLCKSZ		0
 #endif
 
 #ifdef USE_ZSTD
-#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(cluster_block_size)
+#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(MAX_BLOCK_SIZE)
 #else
 #define ZSTD_MAX_BLCKSZ		0
 #endif
 
-#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(cluster_block_size)
+#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(MAX_BLOCK_SIZE)
 
 /* Buffer size required to store a compressed version of backup block image */
 #define COMPRESS_BUFSIZE	Max(Max(PGLZ_MAX_BLCKSZ, LZ4_MAX_BLCKSZ), ZSTD_MAX_BLCKSZ)
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 627ae9e659..38e9a06d09 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -23,6 +23,7 @@
 #include "backup/basebackup_sink.h"
 #include "backup/basebackup_target.h"
 #include "commands/defrem.h"
+#include "common/blocksize.h"
 #include "common/compression.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -370,7 +371,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 			else
 			{
 				/* Properly terminate the tarfile. */
-				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= MAX_BLOCK_SIZE,
 								 "cluster_block_size too small for 2 tar blocks");
 				memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 				bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
@@ -623,7 +624,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 		}
 
 		/* Properly terminate the tar file. */
-		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= MAX_BLOCK_SIZE,
 						 "cluster_block_size too small for 2 tar blocks");
 		memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 		bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
@@ -1580,7 +1581,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 
 		if (verify_checksum)
 		{
-			for (i = 0; i < cnt / cluster_block_size; i++)
+			for (i = 0; i < cnt >> cluster_block_bits; i++)
 			{
 				page = sink->bbs_buffer + cluster_block_size * i;
 
@@ -1594,7 +1595,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 				 */
 				if (!PageIsNew(page) && PageGetLSN(page) < sink->bbs_state->startptr)
 				{
-					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE);
+					checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE, cluster_block_size);
 					phdr = (PageHeader) page;
 					if (phdr->pd_checksum != checksum)
 					{
@@ -1749,8 +1750,8 @@ _tarWriteHeader(bbsink *sink, const char *filename, const char *linktarget,
 		 * large enough to fit an entire tar block. We double-check by means
 		 * of these assertions.
 		 */
-		StaticAssertDecl(TAR_BLOCK_SIZE <= cluster_block_size,
-						 "cluster_block_size too small for tar block");
+		StaticAssertDecl(TAR_BLOCK_SIZE <= DEFAULT_BLOCK_SIZE,
+						 "DEFAULT_BLOCK_SIZE too small for tar block");
 		Assert(sink->bbs_buffer_length >= TAR_BLOCK_SIZE);
 
 		rc = tarCreateHeader(sink->bbs_buffer, filename, linktarget,
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 5810f8825e..91ace7613d 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -28,6 +28,7 @@
 #include "catalog/index.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
+#include "common/blocksize.h"
 #include "common/link-canary.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -46,6 +47,7 @@
 #include "utils/relmapper.h"
 
 uint32		bootstrap_data_checksum_version = 0;	/* No checksum */
+uint32		bootstrap_blocksize = DEFAULT_BLOCK_SIZE;
 
 
 static void CheckerModeMain(void);
@@ -221,10 +223,18 @@ 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:B:c:d:D:Fkr:X:-:")) != -1)
 	{
 		switch (flag)
 		{
+			case 'b':
+				bootstrap_blocksize = strtol(optarg, NULL, 0);
+				if (!IsValidBlockSize(bootstrap_blocksize))
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid block size: %s; must be power of two between 1k and 32k",
+									optarg)));
+				break;
 			case 'B':
 				SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
 				break;
@@ -300,6 +310,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
 	if (!SelectConfigFiles(userDoption, progname))
 		proc_exit(1);
 
+	BlockSizeInit(bootstrap_blocksize);
+
 	/*
 	 * Validate we have been given a reasonable-looking DataDir and change
 	 * into it
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index c7d19f4ee0..6a1ae24955 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -163,7 +163,7 @@
  * than that, so changes in that data structure won't affect user-visible
  * restrictions.
  */
-#define NOTIFY_PAYLOAD_MAX_LENGTH	(cluster_block_size - NAMEDATALEN - 128)
+#define NOTIFY_PAYLOAD_MAX_LENGTH	(DEFAULT_BLOCK_SIZE - NAMEDATALEN - 128)
 
 /*
  * Struct representing an entry in the global notify queue
@@ -311,7 +311,7 @@ static AsyncQueueControl *asyncQueueControl;
 static SlruCtlData NotifyCtlData;
 
 #define NotifyCtl					(&NotifyCtlData)
-#define QUEUE_PAGESIZE				cluster_block_size
+#define QUEUE_PAGESIZE				DEFAULT_BLOCK_SIZE
 #define QUEUE_FULL_WARN_INTERVAL	5000	/* warn at most once every 5s */
 
 /*
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index 971c29c428..5c7593617a 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -53,7 +53,7 @@
  * the per-page bitmaps variable size.  We just legislate that the size
  * is this:
  */
-#define MAX_TUPLES_PER_PAGE  MaxHeapTuplesPerPage
+#define MAX_TUPLES_PER_PAGE  MaxHeapTuplesPerPageLimit
 
 /*
  * When we have to switch over to lossy storage, we use a data structure
@@ -70,7 +70,7 @@
  * too different.  But we also want PAGES_PER_CHUNK to be a power of 2 to
  * avoid expensive integer remainder operations.  So, define it like this:
  */
-#define PAGES_PER_CHUNK  (cluster_block_size / 32)
+#define PAGES_PER_CHUNK  (MAX_BLOCK_SIZE / 32)
 
 /* We use BITS_PER_BITMAPWORD and typedef bitmapword from nodes/bitmapset.h */
 
diff --git a/src/backend/po/de.po b/src/backend/po/de.po
index ac2cb6e4cf..be75aea0a4 100644
--- a/src/backend/po/de.po
+++ b/src/backend/po/de.po
@@ -22030,7 +22030,7 @@ msgstr "debug_io_direct wird für WAL nicht unterstützt, weil XLOG_BLCKSZ zu kl
 #: storage/file/fd.c:3951
 #, c-format
 msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
-msgstr "debug_io_direct wird für Daten nicht unterstützt, weil cluster_block_size zu klein ist"
+msgstr "debug_io_direct wird für Daten nicht unterstützt, weil cluster_block_sizes zu klein ist"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 012de6481c..28f9f6ff4c 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3500,7 +3500,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 
 		szbytes = table_relation_size(relation, forkNum);
 
-		return (szbytes + (cluster_block_size - 1)) / cluster_block_size;
+		return (szbytes + (cluster_block_size - 1)) >> cluster_block_bits;
 	}
 	else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
 	{
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index a748a3a67f..6ee3957ead 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -745,7 +745,7 @@ GetLocalBufferStorage(void)
 		/* But not more than what we need for all remaining local bufs */
 		num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
 		/* And don't overflow MaxAllocSize, either */
-		num_bufs = Min(num_bufs, MaxAllocSize / cluster_block_size);
+		num_bufs = Min(num_bufs, MaxAllocSize >> cluster_block_bits);
 
 		/* Buffers should be I/O aligned. */
 		cur_block = (char *)
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index cf5d980026..d817a6ec5a 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -60,7 +60,7 @@
  * tablespaces when available.
  */
 #define MAX_PHYSICAL_FILESIZE	0x40000000
-#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / cluster_block_size)
+#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE >> cluster_block_bits)
 
 /*
  * This data structure represents a buffered file that consists of one or
@@ -867,7 +867,7 @@ BufFileTellBlock(BufFile *file)
 {
 	long		blknum;
 
-	blknum = (file->curOffset + file->pos) / cluster_block_size;
+	blknum = (file->curOffset + file->pos) >> cluster_block_bits;
 	blknum += file->curFile * BUFFILE_SEG_SIZE;
 	return blknum;
 }
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index ab4ede5665..0cc624b3d3 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3943,10 +3943,10 @@ check_debug_io_direct(char **newval, void **extra, GucSource source)
 		result = false;
 	}
 #endif
-#if cluster_block_size < PG_IO_ALIGN_SIZE
+#if DEFAULT_BLOCK_SIZE < PG_IO_ALIGN_SIZE
 	if (result && (flags & IO_DIRECT_DATA))
 	{
-		GUC_check_errdetail("debug_io_direct is not supported for data because cluster_block_size is too small");
+		GUC_check_errdetail("debug_io_direct is not supported for data because DEFAULT_BLOCK_SIZE is too small");
 		result = false;
 	}
 #endif
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index f17fe758d6..c89ed0c23f 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -87,8 +87,8 @@ typedef struct
 	int			logpageno;		/* page number within the level */
 } FSMAddress;
 
-/* Address of the root page. */
-static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0};
+/* Address of the root page. Level is adjusted by FreeSpaceMapInit()  */
+static FSMAddress FSM_ROOT_ADDRESS = {0,0};
 
 /* functions to navigate the tree */
 static FSMAddress fsm_get_child(FSMAddress parent, uint16 slot);
@@ -116,6 +116,17 @@ static uint8 fsm_vacuum_page(Relation rel, FSMAddress addr,
 
 /******** Public API ********/
 
+/*
+ * FreeSpaceMapInit - initialize the FSM system with the cluster block size
+ *
+ */
+
+void
+FreeSpaceMapInit(void)
+{
+	FSM_ROOT_ADDRESS.level = FSM_ROOT_LEVEL;
+}
+
 /*
  * GetPageWithFreeSpace - try to find a page in the given relation with
  *		at least the specified amount of free space.
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index d604d590e7..4569f99f24 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -598,7 +598,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[cluster_loblksize + VARHDRSZ];
+		char		data[LOBLKSIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
@@ -789,7 +789,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[cluster_loblksize + VARHDRSZ];
+		char		data[LOBLKSIZE_LIMIT + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 07a664355f..6c6217bb00 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -102,7 +102,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 	{
 		if (DataChecksumsEnabled())
 		{
-			checksum = pg_checksum_page((char *) page, blkno);
+			checksum = pg_checksum_page((char *) page, blkno, cluster_block_size);
 
 			if (checksum != p->pd_checksum)
 				checksum_failure = true;
@@ -702,7 +702,7 @@ PageRepairFragmentation(Page page)
 	Offset		pd_upper = ((PageHeader) page)->pd_upper;
 	Offset		pd_special = ((PageHeader) page)->pd_special;
 	Offset		last_offset;
-	itemIdCompactData itemidbase[MaxHeapTuplesPerPage];
+	itemIdCompactData itemidbase[MaxHeapTuplesPerPageLimit];
 	itemIdCompact itemidptr;
 	ItemId		lp;
 	int			nline,
@@ -1165,8 +1165,8 @@ PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
 	Offset		pd_upper = phdr->pd_upper;
 	Offset		pd_special = phdr->pd_special;
 	Offset		last_offset;
-	itemIdCompactData itemidbase[MaxIndexTuplesPerPage];
-	ItemIdData	newitemids[MaxIndexTuplesPerPage];
+	itemIdCompactData itemidbase[MaxIndexTuplesPerPageLimit];
+	ItemIdData	newitemids[MaxIndexTuplesPerPageLimit];
 	itemIdCompact itemidptr;
 	ItemId		lp;
 	int			nline,
@@ -1528,7 +1528,7 @@ PageSetChecksumCopy(Page page, BlockNumber blkno)
 											 0);
 
 	memcpy(pageCopy, (char *) page, cluster_block_size);
-	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno);
+	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno, cluster_block_size);
 	return pageCopy;
 }
 
@@ -1545,5 +1545,5 @@ PageSetChecksumInplace(Page page, BlockNumber blkno)
 	if (PageIsNew(page) || !DataChecksumsEnabled())
 		return;
 
-	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno);
+	((PageHeader) page)->pd_checksum = pg_checksum_page((char *) page, blkno, cluster_block_size);
 }
diff --git a/src/backend/storage/smgr/README b/src/backend/storage/smgr/README
index cf3aa5632b..22515ab823 100644
--- a/src/backend/storage/smgr/README
+++ b/src/backend/storage/smgr/README
@@ -50,3 +50,55 @@ to relfilelocator and block number, to identify which relation fork you want to
 access. Since most code wants to access the main fork, a shortcut version of
 ReadBuffer that accesses MAIN_FORKNUM is provided in the buffer manager for
 convenience.
+
+
+Adjustments for Variable Block Sizes
+====================================
+
+Prior to 17, the disk block size (aka cluster_block_size) was set at compile-time. While
+each database cluster still has a fixed block size, this is now selected at
+`initdb` time, meaning that a single PostgreSQL binary installation can support
+clusters of varying sizes.
+
+As this is now a per-cluster setting, the block size is set in the pg_control
+file. There are multiple computed settings which rely on the cluster block size,
+so these also need to be initialized at cluster start.  This is managed via
+BlockSizeInit() in src/common/blocksize.c.  This routine computes a table for
+all values which would have been computed at compile time.
+
+The expressions used to calculate the values in this table have been moved from
+their original locations and parameterized based on blocksize; these are named
+CalcXXXX(blocksize), and are stored in src/include/common/blocksize.h.
+
+In order to minimize code changes, we have kept the names of former constants
+(such as MaxHeapTuplesPerPage) and replaced their definitions with an
+appropriate lookup in this table.  Since these lookups are now handled at
+runtime, it is important to initialize these as early as possible so any code
+usage of these constants uses the computed value after it has been initialized
+instead of 0.  This applies to any piece of the code which needs to know about
+the block size or uses any value which was based on it.
+
+To ensure that any code that depended on the old behavior is properly adjusted,
+we have removed cluster_block_size as a constant, which will ensure that external code will
+fail to compile.  When writing new code which cases about the block size, you
+should use one of the following values:
+
+- cluster_block_size - evaluates to the cluster's block size as initialized by
+  BlockSizeInit().
+
+- DEFAULT_BLOCK_SIZE - the compiled-in default block size; 8k
+
+- MIN_BLOCK_SIZE - the smallest supported block size; 1k
+
+- MAX_BLOCK_SIZE - the largest supported block size; 32k
+
+In general, you should use cluster_block_size unless you are allocating space on
+the stack, and even then you should be careful to allocate based on
+MAX_BLOCK_SIZE, but still limit accesses to these structs based on the /actual/
+runtime cluster block size.  The core code has bene modified to follow these
+standards.
+
+Occasionally, we had been using cluster_block_size as a "big enough buffer" rather than
+caring about any specific size correlated with the cluster.  In those cases, you
+should just use DEFAULT_BLOCK_SIZE.
+
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 319e8d8655..15f1d9849e 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -47,13 +47,13 @@
  * easier to support relations that are larger than the operating
  * system's file size limit (often 2GBytes).  In order to do that,
  * we break relations up into "segment" files that are each shorter than
- * the OS file size limit.  The segment size is set by the RELSEG_SIZE
+ * the OS file size limit.  The segment size is set by the cluster_relseg_size
  * configuration constant in pg_config.h.
  *
  * On disk, a relation must consist of consecutively numbered segment
  * files in the pattern
- *	-- Zero or more full segments of exactly RELSEG_SIZE blocks each
- *	-- Exactly one partial segment of size 0 <= size < RELSEG_SIZE blocks
+ *	-- Zero or more full segments of exactly cluster_relseg_size blocks each
+ *	-- Exactly one partial segment of size 0 <= size < cluster_relseg_size blocks
  *	-- Optionally, any number of inactive segments of size 0 blocks.
  * The full and partial segments are collectively the "active" segments.
  * Inactive segments are those that once contained data but are currently
@@ -110,7 +110,7 @@ static MemoryContext MdCxt;		/* context for all MdfdVec objects */
 #define EXTENSION_CREATE_RECOVERY	(1 << 3)
 /*
  * Allow opening segments which are preceded by segments smaller than
- * RELSEG_SIZE, e.g. inactive segments (see above). Note that this breaks
+ * cluster_relseg_size, e.g. inactive segments (see above). Note that this breaks
  * mdnblocks() and related functionality henceforth - which currently is ok,
  * because this is only required in the checkpointer which never uses
  * mdnblocks().
@@ -490,9 +490,9 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync, EXTENSION_CREATE);
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != cluster_block_size)
 	{
@@ -514,7 +514,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	if (!skipFsync && !SmgrIsTemp(reln))
 		register_dirty_segment(reln, forknum, v);
 
-	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) cluster_relseg_size));
 }
 
 /*
@@ -552,19 +552,19 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 
 	while (remblocks > 0)
 	{
-		BlockNumber segstartblock = curblocknum % ((BlockNumber) RELSEG_SIZE);
+		BlockNumber segstartblock = curblocknum % ((BlockNumber) cluster_relseg_size);
 		off_t		seekpos = (off_t) cluster_block_size * segstartblock;
 		int			numblocks;
 
-		if (segstartblock + remblocks > RELSEG_SIZE)
-			numblocks = RELSEG_SIZE - segstartblock;
+		if (segstartblock + remblocks > cluster_relseg_size)
+			numblocks = cluster_relseg_size - segstartblock;
 		else
 			numblocks = remblocks;
 
 		v = _mdfd_getseg(reln, forknum, curblocknum, skipFsync, EXTENSION_CREATE);
 
-		Assert(segstartblock < RELSEG_SIZE);
-		Assert(segstartblock + numblocks <= RELSEG_SIZE);
+		Assert(segstartblock < cluster_relseg_size);
+		Assert(segstartblock + numblocks <= cluster_relseg_size);
 
 		/*
 		 * If available and useful, use posix_fallocate() (via
@@ -618,7 +618,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 		if (!skipFsync && !SmgrIsTemp(reln))
 			register_dirty_segment(reln, forknum, v);
 
-		Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+		Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) cluster_relseg_size));
 
 		remblocks -= numblocks;
 		curblocknum += numblocks;
@@ -670,7 +670,7 @@ mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
 	mdfd->mdfd_vfd = fd;
 	mdfd->mdfd_segno = 0;
 
-	Assert(_mdnblocks(reln, forknum, mdfd) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, mdfd) <= ((BlockNumber) cluster_relseg_size));
 
 	return mdfd;
 }
@@ -726,9 +726,9 @@ mdprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 	if (v == NULL)
 		return false;
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	(void) FilePrefetch(v->mdfd_vfd, seekpos, cluster_block_size, WAIT_EVENT_DATA_FILE_PREFETCH);
 #endif							/* USE_PREFETCH */
@@ -760,9 +760,9 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, false,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	nbytes = FileRead(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_READ);
 
@@ -834,9 +834,9 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
-	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * cluster_relseg_size);
 
 	nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
 
@@ -907,17 +907,17 @@ mdwriteback(SMgrRelation reln, ForkNumber forknum,
 			return;
 
 		/* compute offset inside the current segment */
-		segnum_start = blocknum / RELSEG_SIZE;
+		segnum_start = blocknum / cluster_relseg_size;
 
 		/* compute number of desired writes within the current segment */
-		segnum_end = (blocknum + nblocks - 1) / RELSEG_SIZE;
+		segnum_end = (blocknum + nblocks - 1) / cluster_relseg_size;
 		if (segnum_start != segnum_end)
-			nflush = RELSEG_SIZE - (blocknum % ((BlockNumber) RELSEG_SIZE));
+			nflush = cluster_relseg_size - (blocknum % ((BlockNumber) cluster_relseg_size));
 
 		Assert(nflush >= 1);
 		Assert(nflush <= nblocks);
 
-		seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
+		seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) cluster_relseg_size));
 
 		FileWriteback(v->mdfd_vfd, seekpos, (off_t) cluster_block_size * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
 
@@ -948,7 +948,7 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 
 	/*
 	 * Start from the last open segments, to avoid redundant seeks.  We have
-	 * previously verified that these segments are exactly RELSEG_SIZE long,
+	 * previously verified that these segments are exactly cluster_relseg_size long,
 	 * and it's useless to recheck that each time.
 	 *
 	 * NOTE: this assumption could only be wrong if another backend has
@@ -965,13 +965,13 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 	for (;;)
 	{
 		nblocks = _mdnblocks(reln, forknum, v);
-		if (nblocks > ((BlockNumber) RELSEG_SIZE))
+		if (nblocks > ((BlockNumber) cluster_relseg_size))
 			elog(FATAL, "segment too big");
-		if (nblocks < ((BlockNumber) RELSEG_SIZE))
-			return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks;
+		if (nblocks < ((BlockNumber) cluster_relseg_size))
+			return (segno * ((BlockNumber) cluster_relseg_size)) + nblocks;
 
 		/*
-		 * If segment is exactly RELSEG_SIZE, advance to next one.
+		 * If segment is exactly cluster_relseg_size, advance to next one.
 		 */
 		segno++;
 
@@ -984,7 +984,7 @@ mdnblocks(SMgrRelation reln, ForkNumber forknum)
 		 */
 		v = _mdfd_openseg(reln, forknum, segno, 0);
 		if (v == NULL)
-			return segno * ((BlockNumber) RELSEG_SIZE);
+			return segno * ((BlockNumber) cluster_relseg_size);
 	}
 }
 
@@ -1025,7 +1025,7 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 	{
 		MdfdVec    *v;
 
-		priorblocks = (curopensegs - 1) * RELSEG_SIZE;
+		priorblocks = (curopensegs - 1) * cluster_relseg_size;
 
 		v = &reln->md_seg_fds[forknum][curopensegs - 1];
 
@@ -1050,12 +1050,12 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 			FileClose(v->mdfd_vfd);
 			_fdvec_resize(reln, forknum, curopensegs - 1);
 		}
-		else if (priorblocks + ((BlockNumber) RELSEG_SIZE) > nblocks)
+		else if (priorblocks + ((BlockNumber) cluster_relseg_size) > nblocks)
 		{
 			/*
 			 * This is the last segment we want to keep. Truncate the file to
 			 * the right length. NOTE: if nblocks is exactly a multiple K of
-			 * RELSEG_SIZE, we will truncate the K+1st segment to 0 length but
+			 * cluster_relseg_size, we will truncate the K+1st segment to 0 length but
 			 * keep it. This adheres to the invariant given in the header
 			 * comments.
 			 */
@@ -1372,7 +1372,7 @@ _mdfd_openseg(SMgrRelation reln, ForkNumber forknum, BlockNumber segno,
 	v->mdfd_vfd = fd;
 	v->mdfd_segno = segno;
 
-	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
+	Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) cluster_relseg_size));
 
 	/* all done */
 	return v;
@@ -1399,7 +1399,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 		   (EXTENSION_FAIL | EXTENSION_CREATE | EXTENSION_RETURN_NULL |
 			EXTENSION_DONT_OPEN));
 
-	targetseg = blkno / ((BlockNumber) RELSEG_SIZE);
+	targetseg = blkno / ((BlockNumber) cluster_relseg_size);
 
 	/* if an existing and opened segment, we're done */
 	if (targetseg < reln->md_num_open_segs[forknum])
@@ -1436,7 +1436,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 
 		Assert(nextsegno == v->mdfd_segno + 1);
 
-		if (nblocks > ((BlockNumber) RELSEG_SIZE))
+		if (nblocks > ((BlockNumber) cluster_relseg_size))
 			elog(FATAL, "segment too big");
 
 		if ((behavior & EXTENSION_CREATE) ||
@@ -1451,30 +1451,30 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 			 * ahead and create the segments so we can finish out the replay.
 			 *
 			 * We have to maintain the invariant that segments before the last
-			 * active segment are of size RELSEG_SIZE; therefore, if
+			 * active segment are of size cluster_relseg_size; therefore, if
 			 * extending, pad them out with zeroes if needed.  (This only
 			 * matters if in recovery, or if the caller is extending the
 			 * relation discontiguously, but that can happen in hash indexes.)
 			 */
-			if (nblocks < ((BlockNumber) RELSEG_SIZE))
+			if (nblocks < ((BlockNumber) cluster_relseg_size))
 			{
 				char	   *zerobuf = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE,
 													 MCXT_ALLOC_ZERO);
 
 				mdextend(reln, forknum,
-						 nextsegno * ((BlockNumber) RELSEG_SIZE) - 1,
+						 nextsegno * ((BlockNumber) cluster_relseg_size) - 1,
 						 zerobuf, skipFsync);
 				pfree(zerobuf);
 			}
 			flags = O_CREAT;
 		}
 		else if (!(behavior & EXTENSION_DONT_CHECK_SIZE) &&
-				 nblocks < ((BlockNumber) RELSEG_SIZE))
+				 nblocks < ((BlockNumber) cluster_relseg_size))
 		{
 			/*
 			 * When not extending (or explicitly including truncated
 			 * segments), only open the next segment if the current one is
-			 * exactly RELSEG_SIZE.  If not (this branch), either return NULL
+			 * exactly cluster_relseg_size.  If not (this branch), either return NULL
 			 * or fail.
 			 */
 			if (behavior & EXTENSION_RETURN_NULL)
@@ -1529,7 +1529,7 @@ _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 				 errmsg("could not seek to end of file \"%s\": %m",
 						FilePathName(seg->mdfd_vfd))));
 	/* note that this calculation will ignore any partial block at EOF */
-	return (BlockNumber) (len / cluster_block_size);
+	return (BlockNumber) (len >> cluster_block_bits);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 37d91cb902..da82e96496 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1501,8 +1501,8 @@ AddToDataDirLockFile(int target_line, const char *str)
 	int			lineno;
 	char	   *srcptr;
 	char	   *destptr;
-	char		srcbuffer[cluster_block_size];
-	char		destbuffer[cluster_block_size];
+	char		srcbuffer[DEFAULT_BLOCK_SIZE];
+	char		destbuffer[DEFAULT_BLOCK_SIZE];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
@@ -1626,7 +1626,7 @@ RecheckDataDirLockFile(void)
 	int			fd;
 	int			len;
 	long		file_pid;
-	char		buffer[cluster_block_size];
+	char		buffer[DEFAULT_BLOCK_SIZE];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index d29b98e7d8..4310b7cf81 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -30,6 +30,7 @@
 
 #include "access/xact.h"
 #include "access/xlog.h"
+#include "access/xlog_internal.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_parameter_acl.h"
@@ -105,8 +106,8 @@ typedef struct
 } unit_conversion;
 
 /* Ensure that the constants in the tables don't overflow or underflow */
-#if cluster_block_size < 1024 || cluster_block_size > (1024*1024)
-#error cluster_block_size must be between 1KB and 1MB
+#if DEFAULT_BLOCK_SIZE < 1024 || DEFAULT_BLOCK_SIZE > (1024*1024)
+#error DEFAULT_BLOCK_SIZE must be between 1KB and 1MB
 #endif
 #if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
 #error XLOG_BLCKSZ must be between 1KB and 1MB
@@ -134,11 +135,11 @@ static const unit_conversion memory_unit_conversion_table[] =
 	{"kB", GUC_UNIT_MB, 1.0 / 1024.0},
 	{"B", GUC_UNIT_MB, 1.0 / (1024.0 * 1024.0)},
 
-	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (cluster_block_size / 1024)},
-	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (cluster_block_size / 1024)},
-	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (cluster_block_size / 1024)},
-	{"kB", GUC_UNIT_BLOCKS, 1.0 / (cluster_block_size / 1024)},
-	{"B", GUC_UNIT_BLOCKS, 1.0 / cluster_block_size},
+	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"kB", GUC_UNIT_BLOCKS, 1.0 / (DEFAULT_BLOCK_SIZE / 1024)},
+	{"B", GUC_UNIT_BLOCKS, 1.0 / DEFAULT_BLOCK_SIZE},
 
 	{"TB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
 	{"GB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
@@ -1503,6 +1504,16 @@ InitializeGUCOptions(void)
 	 */
 	pg_timezone_initialize();
 
+	/* Load our block size -- no valid in Bootstrap or init mode, since control file hasn't been written */
+	if (!(IsInitProcessingMode() || IsBootstrapProcessingMode()))
+	{
+		BlockSizeInit(ClusterBlockSize());
+		/*
+		 * Ensure GUCs with dynamic limits have been properly initialized
+		 */
+		update_dynamic_gucs();
+	}
+
 	/*
 	 * Create GUCMemoryContext and build hash table of all GUC variables.
 	 */
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index ffb72cc177..8dcc2b53ef 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -42,6 +42,7 @@
 #include "commands/trigger.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
+#include "common/blocksize.h"
 #include "common/file_utils.h"
 #include "common/scram-common.h"
 #include "jit/jit.h"
@@ -506,8 +507,11 @@ bool		log_btree_build_stats = false;
 char	   *event_source;
 
 bool		row_security;
+
 bool		check_function_bodies = true;
 
+int        	block_size;
+
 /*
  * This GUC exists solely for backward compatibility, check its definition for
  * details.
@@ -590,7 +594,6 @@ static char *session_authorization_string;
 static int	max_function_args;
 static int	max_index_keys;
 static int	max_identifier_length;
-static int	block_size;
 static int	segment_size;
 static int	shared_memory_size_mb;
 static int	shared_memory_size_in_huge_pages;
@@ -3130,7 +3133,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&block_size,
-		cluster_block_size, cluster_block_size, cluster_block_size,
+		DEFAULT_BLOCK_SIZE, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE,
 		NULL, NULL, NULL
 	},
 
@@ -3369,7 +3372,8 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_table_scan_size,
-		(8 * 1024 * 1024) / cluster_block_size, 0, INT_MAX / 3,
+		/* This is set dynamically based on cluster_block_size, but works for a default */
+		(8 * 1024 * 1024) / DEFAULT_BLOCK_SIZE, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -3380,7 +3384,8 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_index_scan_size,
-		(512 * 1024) / cluster_block_size, 0, INT_MAX / 3,
+		/* This is set dynamically based on cluster_block_size, but works for a default */
+		(512 * 1024) / DEFAULT_BLOCK_SIZE, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -4987,3 +4992,29 @@ struct config_enum ConfigureNamesEnum[] =
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
 	}
 };
+
+
+
+/*
+ * Update the specific GUCs which have dynamic limits.  Due to having variable
+ * block sizes, what were originally constants now depend on the runtime block
+ * size, so handle the adjustments to those boot values here.
+ */
+void
+update_dynamic_gucs(void)
+{
+	int i;
+
+	for (i = 0; ConfigureNamesInt[i].gen.name; i++)
+	{
+		if (strcmp("min_parallel_table_scan_size", ConfigureNamesInt[i].gen.name) == 0)
+		{
+			ConfigureNamesInt[i].boot_val = (8 * 1024 * 1024) >> cluster_block_bits;
+		}
+		else if (strcmp("min_parallel_index_scan_size", ConfigureNamesInt[i].gen.name) == 0)
+		{
+			ConfigureNamesInt[i].boot_val = (512 * 1024) >> cluster_block_bits;
+		}
+	}
+
+}
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index 4b54256f09..443420a9ff 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -640,7 +640,7 @@ LogicalTapeImport(LogicalTapeSet *lts, int worker, TapeShare *shared)
 	}
 	/* Don't allocate more for read buffer than could possibly help */
 	lt->max_size = Min(MaxAllocSize, filesize);
-	tapeblocks = filesize / cluster_block_size;
+	tapeblocks = filesize >> cluster_block_bits;
 
 	/*
 	 * Update # of allocated blocks and # blocks written to reflect the
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 8b00ecd43b..f86f71377f 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -69,6 +69,7 @@
 #include "catalog/pg_class_d.h" /* pgrminclude ignore */
 #include "catalog/pg_collation_d.h"
 #include "catalog/pg_database_d.h"	/* pgrminclude ignore */
+#include "common/blocksize.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
@@ -166,6 +167,8 @@ static bool data_checksums = false;
 static char *xlog_dir = NULL;
 static int	wal_segment_size_mb = (DEFAULT_XLOG_SEG_SIZE) / (1024 * 1024);
 static DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
+static char *str_block_size = NULL;
+int block_size = DEFAULT_BLOCK_SIZE;
 
 
 /* internal vars */
@@ -1109,8 +1112,8 @@ test_config_settings(void)
 
 	for (i = 0; i < bufslen; i++)
 	{
-		/* Use same amount of memory, independent of cluster_block_size */
-		test_buffs = (trial_bufs[i] * 8192) / cluster_block_size;
+		/* Use same amount of memory, independent of block_size */
+		test_buffs = (trial_bufs[i] * 8192) / block_size;
 		if (test_buffs <= ok_buffers)
 		{
 			test_buffs = ok_buffers;
@@ -1122,10 +1125,10 @@ test_config_settings(void)
 	}
 	n_buffers = test_buffs;
 
-	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
-		printf("%dMB\n", (n_buffers * (cluster_block_size / 1024)) / 1024);
+	if ((n_buffers * (block_size / 1024)) % 1024 == 0)
+		printf("%dMB\n", (n_buffers * (block_size / 1024)) / 1024);
 	else
-		printf("%dkB\n", n_buffers * (cluster_block_size / 1024));
+		printf("%dkB\n", n_buffers * (block_size / 1024));
 
 	printf(_("selecting default time zone ... "));
 	fflush(stdout);
@@ -1148,11 +1151,12 @@ test_specific_config_settings(int test_conns, int test_buffs)
 
 	/* Set up the test postmaster invocation */
 	printfPQExpBuffer(&cmd,
-					  "\"%s\" --check %s %s "
+					  "\"%s\" --check %s %s -b %d "
 					  "-c max_connections=%d "
 					  "-c shared_buffers=%d "
 					  "-c dynamic_shared_memory_type=%s",
 					  backend_exec, boot_options, extra_options,
+					  block_size,
 					  test_conns, test_buffs,
 					  dynamic_shared_memory_type);
 
@@ -1217,12 +1221,12 @@ setup_config(void)
 	conflines = replace_guc_value(conflines, "max_connections",
 								  repltok, false);
 
-	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
+	if ((n_buffers * (block_size / 1024)) % 1024 == 0)
 		snprintf(repltok, sizeof(repltok), "%dMB",
-				 (n_buffers * (cluster_block_size / 1024)) / 1024);
+				 (n_buffers * (block_size / 1024)) / 1024);
 	else
 		snprintf(repltok, sizeof(repltok), "%dkB",
-				 n_buffers * (cluster_block_size / 1024));
+				 n_buffers * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "shared_buffers",
 								  repltok, false);
 
@@ -1297,21 +1301,21 @@ setup_config(void)
 
 #if DEFAULT_BACKEND_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BACKEND_FLUSH_AFTER * (cluster_block_size / 1024));
+			 DEFAULT_BACKEND_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "backend_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_BGWRITER_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BGWRITER_FLUSH_AFTER * (cluster_block_size / 1024));
+			 DEFAULT_BGWRITER_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "bgwriter_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_CHECKPOINT_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (cluster_block_size / 1024));
+			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (block_size / 1024));
 	conflines = replace_guc_value(conflines, "checkpoint_flush_after",
 								  repltok, true);
 #endif
@@ -1538,12 +1542,12 @@ bootstrap_template1(void)
 
 	printfPQExpBuffer(&cmd, "\"%s\" --boot %s %s", backend_exec, boot_options, extra_options);
 	appendPQExpBuffer(&cmd, " -X %d", wal_segment_size_mb * (1024 * 1024));
+	appendPQExpBuffer(&cmd, " -b %d", block_size);
 	if (data_checksums)
 		appendPQExpBuffer(&cmd, " -k");
 	if (debug)
 		appendPQExpBuffer(&cmd, " -d 5");
 
-
 	PG_CMD_OPEN(cmd.data);
 
 	for (line = bki_lines; *line != NULL; line++)
@@ -2437,6 +2441,7 @@ usage(const char *progname)
 	printf(_("  -A, --auth=METHOD         default authentication method for local connections\n"));
 	printf(_("      --auth-host=METHOD    default authentication method for local TCP/IP connections\n"));
 	printf(_("      --auth-local=METHOD   default authentication method for local-socket connections\n"));
+	printf(_("  -b, --block-size=SIZE     size of database blocks, in kilobytes\n"));
 	printf(_(" [-D, --pgdata=]DATADIR     location for this database cluster\n"));
 	printf(_("  -E, --encoding=ENCODING   set default encoding for new databases\n"));
 	printf(_("  -g, --allow-group-access  allow group read/execute on data directory\n"));
@@ -3102,6 +3107,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"waldir", required_argument, NULL, 'X'},
 		{"wal-segsize", required_argument, NULL, 12},
+		{"block-size", required_argument, NULL, 'b'},
 		{"data-checksums", no_argument, NULL, 'k'},
 		{"allow-group-access", no_argument, NULL, 'g'},
 		{"discard-caches", no_argument, NULL, 14},
@@ -3150,7 +3156,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "A:c:dD:E:gkL:nNsST:U:WX:",
+	while ((c = getopt_long(argc, argv, "A:b:c:dD:E:gkL:nNsST:U:WX:",
 							long_options, &option_index)) != -1)
 	{
 		switch (c)
@@ -3174,6 +3180,9 @@ main(int argc, char *argv[])
 			case 11:
 				authmethodhost = pg_strdup(optarg);
 				break;
+			case 'b':
+				str_block_size = pg_strdup(optarg);
+				break;
 			case 'c':
 				{
 					char	   *buf = pg_strdup(optarg);
@@ -3359,6 +3368,26 @@ main(int argc, char *argv[])
 	if (!IsValidWalSegSize(wal_segment_size_mb * 1024 * 1024))
 		pg_fatal("argument of %s must be a power of two between 1 and 1024", "--wal-segsize");
 
+	if (str_block_size == NULL)
+		block_size = DEFAULT_BLOCK_SIZE;
+	else
+	{
+		char	   *endptr;
+
+		/* check that the argument is a number */
+		block_size = strtol(str_block_size, &endptr, 10);
+
+		/* verify that the  segment size is valid */
+		if (endptr == str_block_size || *endptr != '\0')
+			pg_fatal("argument of --block-size must be a number");
+		block_size *= 1024;
+		/* check for valid block_size; last is bitwise power of two check */
+		if (!IsValidBlockSize(block_size))
+			pg_fatal("argument of --block-size must be a power of 2 between 1 and 32");
+	}
+
+	BlockSizeInit(block_size);
+
 	get_restricted_token();
 
 	setup_pgdata();
@@ -3392,6 +3421,8 @@ main(int argc, char *argv[])
 	else
 		printf(_("Data page checksums are disabled.\n"));
 
+	printf(_("Selected server block size: %d\n"), block_size);
+
 	if (pwprompt || pwfilename)
 		get_su_pwd();
 
diff --git a/src/bin/initdb/meson.build b/src/bin/initdb/meson.build
index 49743630aa..e2b8d88bff 100644
--- a/src/bin/initdb/meson.build
+++ b/src/bin/initdb/meson.build
@@ -31,6 +31,7 @@ tests += {
     'env': {'with_icu': icu.found() ? 'yes' : 'no'},
     'tests': [
       't/001_initdb.pl',
+      't/002_blocksize.pl',
     ],
   },
 }
diff --git a/src/bin/initdb/t/002_blocksize.pl b/src/bin/initdb/t/002_blocksize.pl
new file mode 100644
index 0000000000..d8d4208fed
--- /dev/null
+++ b/src/bin/initdb/t/002_blocksize.pl
@@ -0,0 +1,24 @@
+
+# Copyright (c) 2023, PostgreSQL Global Development Group
+
+
+use strict;
+use warnings;
+use Fcntl ':mode';
+use File::stat qw{lstat};
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+for my $blocksize (1,2,4,8,16,32) {
+	my $node1 = PostgreSQL::Test::Cluster->new('node' . $blocksize);
+	$node1->init(extra => ['--block-size='.($blocksize)] );
+	$node1->start;
+
+	is($node1->safe_psql('postgres',q{SELECT current_setting('block_size')}),
+	   1024 * $blocksize, "initdb with blocksize " . $blocksize . "k");
+
+	$node1->stop;
+}
+
+done_testing();
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 1a8cef345d..fed188a4cc 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -28,6 +28,7 @@
 #include "access/xlog_internal.h"
 #include "backup/basebackup.h"
 #include "bbstreamer.h"
+#include "common/blocksize.h"
 #include "common/compression.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -2744,6 +2745,12 @@ main(int argc, char **argv)
 	if (!RetrieveWalSegSize(conn))
 		exit(1);
 
+	/* detect and set remote server's block size */
+	if (!RetrieveBlockSize(conn))
+		exit(1);
+
+	BlockSizeInit(BlockSize);
+
 	/* Create pg_wal symlink, if required */
 	if (xlog_dir)
 	{
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index d0a4079d50..e5f5c804ee 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -897,6 +897,10 @@ main(int argc, char **argv)
 	/* determine remote server's xlog segment size */
 	if (!RetrieveWalSegSize(conn))
 		exit(1);
+	if (!RetrieveBlockSize(conn))
+		exit(1);
+
+	BlockSizeInit(BlockSize);
 
 	/*
 	 * Don't close the connection here so that subsequent StreamLog() can
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index dbd08ab172..e194048cc5 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -32,6 +32,7 @@
 #define ERRCODE_DUPLICATE_OBJECT  "42710"
 
 int			WalSegSz;
+int			BlockSize;
 
 static bool RetrieveDataDirCreatePerm(PGconn *conn);
 
@@ -339,6 +340,64 @@ RetrieveWalSegSize(PGconn *conn)
 	return true;
 }
 
+/*
+ * Use SHOW block_size since ControlFile is not accessible here.
+ */
+bool
+RetrieveBlockSize(PGconn *conn)
+{
+	PGresult   *res;
+
+	/* check connection existence */
+	Assert(conn != NULL);
+
+	/* for previous versions set the default xlog seg size */
+	if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_SHOW_CMD)
+	{
+		BlockSize = DEFAULT_BLOCK_SIZE;
+		return true;
+	}
+
+	res = PQexec(conn, "SHOW block_size");
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+	{
+		pg_log_error("could not send replication command \"%s\": %s",
+					 "SHOW block_size", PQerrorMessage(conn));
+
+		PQclear(res);
+		return false;
+	}
+	if (PQntuples(res) != 1 || PQnfields(res) < 1)
+	{
+		pg_log_error("could not fetch block size: got %d rows and %d fields, expected %d rows and %d or more fields",
+					 PQntuples(res), PQnfields(res), 1, 1);
+
+		PQclear(res);
+		return false;
+	}
+
+	/* fetch xlog value and unit from the result */
+	if (sscanf(PQgetvalue(res, 0, 0), "%d", &BlockSize) != 1)
+	{
+		pg_log_error("block size could not be parsed");
+		PQclear(res);
+		return false;
+	}
+
+	PQclear(res);
+
+	if (!IsValidBlockSize(BlockSize))
+	{
+		pg_log_error(ngettext("Block size must be a power of two between 1k and 32k, but the remote server reported a value of %d byte",
+							  "Block size must be a power of two between 1k and 32k, but the remote server reported a value of %d bytes",
+							  BlockSize),
+					 BlockSize);
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * RetrieveDataDirCreatePerm
  *
diff --git a/src/bin/pg_basebackup/streamutil.h b/src/bin/pg_basebackup/streamutil.h
index 268c163213..c97f9fa969 100644
--- a/src/bin/pg_basebackup/streamutil.h
+++ b/src/bin/pg_basebackup/streamutil.h
@@ -13,6 +13,7 @@
 #define STREAMUTIL_H
 
 #include "access/xlogdefs.h"
+#include "common/blocksize.h"
 #include "datatype/timestamp.h"
 #include "libpq-fe.h"
 #include "pqexpbuffer.h"
@@ -25,6 +26,7 @@ extern char *dbport;
 extern char *dbname;
 extern int	dbgetpassword;
 extern int	WalSegSz;
+extern int	BlockSize;
 
 /* Connection kept global so we can disconnect easily */
 extern PGconn *conn;
@@ -56,6 +58,7 @@ extern bool GetSlotInformation(PGconn *conn, const char *slot_name,
 							   XLogRecPtr *restart_lsn,
 							   TimeLineID *restart_tli);
 extern bool RetrieveWalSegSize(PGconn *conn);
+extern bool RetrieveBlockSize(PGconn *conn);
 extern TimestampTz feGetCurrentTimestamp(void);
 extern void feTimestampDifference(TimestampTz start_time, TimestampTz stop_time,
 								  long *secs, int *microsecs);
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 2c6469b6ed..b0ccfa9bf2 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "access/xlog_internal.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -223,14 +224,14 @@ scan_file(const char *fn, int segmentno)
 		if (PageIsNew(buf.data))
 			continue;
 
-		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE);
+		csum = pg_checksum_page(buf.data, blockno + segmentno * RELSEG_SIZE, cluster_block_size);
 		if (mode == PG_MODE_CHECK)
 		{
 			if (csum != header->pd_checksum)
 			{
 				if (ControlFile->data_checksum_version == PG_DATA_CHECKSUM_VERSION)
-					pg_log_error("checksum verification failed in file \"%s\", block %u: calculated checksum %X but block contains %X",
-								 fn, blockno, csum, header->pd_checksum);
+					pg_log_error("checksum verification failed in file \"%s\", block %u, block_size: %u: calculated checksum %X but block contains %X",
+								 fn, blockno, cluster_block_size, csum, header->pd_checksum);
 				badblocks++;
 			}
 		}
@@ -251,7 +252,7 @@ scan_file(const char *fn, int segmentno)
 			header->pd_checksum = csum;
 
 			/* Seek back to beginning of block */
-			if (lseek(f, -cluster_block_size, SEEK_CUR) < 0)
+			if (lseek(f, -(int)cluster_block_size, SEEK_CUR) < 0)
 				pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
 
 			/* Write block with checksum */
@@ -548,14 +549,6 @@ main(int argc, char *argv[])
 	if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
 		pg_fatal("cluster is not compatible with this version of pg_checksums");
 
-	if (ControlFile->blcksz != cluster_block_size)
-	{
-		pg_log_error("database cluster is not compatible");
-		pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
-							ControlFile->blcksz, cluster_block_size);
-		exit(1);
-	}
-
 	/*
 	 * Check if cluster is running.  A clean shutdown is required to avoid
 	 * random checksum failures caused by torn pages.  Note that this doesn't
@@ -577,6 +570,11 @@ main(int argc, char *argv[])
 		mode == PG_MODE_ENABLE)
 		pg_fatal("data checksums are already enabled in cluster");
 
+	if (IsValidBlockSize(ControlFile->blcksz))
+		BlockSizeInit(ControlFile->blcksz);
+	else
+		pg_fatal("invalid cluster block size in control file");
+
 	/* Operate on all files if checking or enabling checksums */
 	if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
 	{
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 01dcf3be63..d954d542b7 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -48,6 +48,7 @@
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
@@ -75,6 +76,7 @@ static TimeLineID minXlogTli = 0;
 static XLogSegNo minXlogSegNo = 0;
 static int	WalSegSz;
 static int	set_wal_segsize;
+static int	set_blocksize = 0;
 
 static void CheckDataVersion(void);
 static bool read_controlfile(void);
@@ -93,6 +95,7 @@ int
 main(int argc, char *argv[])
 {
 	static struct option long_options[] = {
+		{"block-size", required_argument, NULL, 'b'},
 		{"commit-timestamp-ids", required_argument, NULL, 'c'},
 		{"pgdata", required_argument, NULL, 'D'},
 		{"epoch", required_argument, NULL, 'e'},
@@ -137,10 +140,19 @@ main(int argc, char *argv[])
 	}
 
 
-	while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
+	while ((c = getopt_long(argc, argv, "b:c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
 	{
 		switch (c)
 		{
+			case 'b':
+				errno = 0;
+				set_blocksize = strtol(optarg, &endptr, 10);
+				if (endptr == optarg || *endptr != '\0' || errno != 0)
+					pg_fatal("argument of --block-size must be a number");
+				set_blocksize *= 1024;
+				if (!IsValidBlockSize(set_blocksize))
+					pg_fatal("argument of --block-size must be a power of 2 between 1 and 32");
+				break;
 			case 'D':
 				DataDir = optarg;
 				break;
@@ -390,6 +402,33 @@ main(int argc, char *argv[])
 	else
 		WalSegSz = ControlFile.xlog_seg_size;
 
+	/*
+	 * If a blocksize was specified, compare to existing ControlFile; if we
+	 * are wrong, we won't be able to read the data.  We will only want to set
+	 * it if we guessed.
+	 */
+	if (set_blocksize == 0)
+	{
+		if (guessed)
+			pg_fatal("Cannot determine cluster block size; provide explicitly via --block-size");
+	}
+	else
+	{
+		if (!guessed && set_blocksize != ControlFile.blcksz)
+			pg_fatal("Cannot change blocksize in cluster");
+
+		/* hope this is right, but by default we don't know; likely this is
+		 * DEFAULT_BLOCK_SIZE */
+		ControlFile.blcksz = set_blocksize;
+	}
+
+	/*
+	 * Set some dependent calculated fields stored in pg_control
+	 */
+	BlockSizeInit(ControlFile.blcksz);
+	ControlFile.toast_max_chunk_size = cluster_toast_max_chunk_size;
+	ControlFile.loblksize = cluster_loblksize;
+
 	if (log_fname != NULL)
 		XLogFromFileName(log_fname, &minXlogTli, &minXlogSegNo, WalSegSz);
 
@@ -615,6 +654,16 @@ read_controlfile(void)
 			return false;
 		}
 
+		/* return false if block size is not valid */
+		if (!IsValidBlockSize(ControlFile.blcksz))
+		{
+			pg_log_warning(ngettext("pg_control specifies invalid block size (%d byte); proceed with caution",
+									"pg_control specifies invalid block size (%d bytes); proceed with caution",
+									ControlFile.blcksz),
+						   ControlFile.blcksz);
+			return false;
+		}
+
 		return true;
 	}
 
@@ -687,7 +736,7 @@ GuessControlValues(void)
 
 	ControlFile.maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile.floatFormat = FLOATFORMAT_VALUE;
-	ControlFile.blcksz = cluster_block_size;
+	ControlFile.blcksz = DEFAULT_BLOCK_SIZE;
 	ControlFile.relseg_size = RELSEG_SIZE;
 	ControlFile.xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
@@ -1142,6 +1191,7 @@ usage(void)
 	printf(_("  -?, --help             show this help, then exit\n"));
 
 	printf(_("\nOptions to override control file values:\n"));
+	printf(_("      --block-size=SIZE            cluster block size, in bytes\n"));
 	printf(_("  -c, --commit-timestamp-ids=XID,XID\n"
 			 "                                   set oldest and newest transactions bearing\n"
 			 "                                   commit timestamp (zero means no change)\n"));
diff --git a/src/bin/pg_resetwal/t/002_corrupted.pl b/src/bin/pg_resetwal/t/002_corrupted.pl
index 6d19a1efd5..84ed4e92b0 100644
--- a/src/bin/pg_resetwal/t/002_corrupted.pl
+++ b/src/bin/pg_resetwal/t/002_corrupted.pl
@@ -31,7 +31,7 @@ print $fh pack("x[$size]");
 close $fh;
 
 command_checks_all(
-	[ 'pg_resetwal', '-n', $node->data_dir ],
+	[ 'pg_resetwal', '-b', '8', '-n', $node->data_dir ],
 	0,
 	[qr/pg_control version number/],
 	[
@@ -47,7 +47,7 @@ print $fh $data, pack("x[" . ($size - 16) . "]");
 close $fh;
 
 command_checks_all(
-	[ 'pg_resetwal', '-n', $node->data_dir ],
+	[ 'pg_resetwal', '-b', '8', '-n', $node->data_dir ],
 	0,
 	[qr/pg_control version number/],
 	[
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index f0d0c44c85..2c9c94ecea 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "catalog/pg_tablespace_d.h"
+#include "common/blocksize.h"
 #include "common/file_utils.h"
 #include "common/hashfn.h"
 #include "common/string.h"
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index 9c84bc1ac7..be9085f9b1 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -18,6 +18,7 @@
 #include "access/xlog_internal.h"
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
+#include "common/blocksize.h"
 #include "common/controldata_utils.h"
 #include "common/file_perm.h"
 #include "common/restricted_token.h"
@@ -349,6 +350,13 @@ main(int argc, char **argv)
 
 	sanityChecks();
 
+	if (IsValidBlockSize(ControlFile_source.blcksz) &&
+		ControlFile_source.blcksz == ControlFile_target.blcksz)
+		BlockSizeInit(ControlFile_source.blcksz);
+	else
+		pg_fatal("cluster block sizes do not match or are invalid: %d",
+				 ControlFile_source.blcksz);
+
 	/*
 	 * Usually, the TLI can be found in the latest checkpoint record. But if
 	 * the source server is just being promoted (or it's a standby that's
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 3143cf28aa..db16a420ba 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -19,14 +19,17 @@
 #include <linux/fs.h>
 #endif
 
+
 #include "access/visibilitymapdefs.h"
+#include "common/blocksize.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
+#undef cluster_block_size
+#define cluster_block_size BlockSize
 #include "storage/bufpage.h"
 #include "storage/checksum.h"
 #include "storage/checksum_impl.h"
 
-
 /*
  * cloneFile()
  *
@@ -96,7 +99,7 @@ copyFile(const char *src, const char *dst,
 				 schemaName, relName, dst, strerror(errno));
 
 	/* copy in fairly large chunks for best efficiency */
-#define COPY_BUF_SIZE (50 * cluster_block_size)
+#define COPY_BUF_SIZE (50 * BlockSize)
 
 	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
 
@@ -187,7 +190,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 	struct stat statbuf;
 
 	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (cluster_block_size - SizeOfPageHeaderData) / 2;
+	rewriteVmBytesPerPage = (BlockSize - SizeOfPageHeaderData) / 2;
 
 	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
 		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
@@ -220,7 +223,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 		PageHeaderData pageheader;
 		bool		old_lastblk;
 
-		if ((bytesRead = read(src_fd, buffer.data, cluster_block_size)) != cluster_block_size)
+		if ((bytesRead = read(src_fd, buffer.data, BlockSize)) != BlockSize)
 		{
 			if (bytesRead < 0)
 				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
@@ -230,7 +233,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 						 schemaName, relName, fromfile);
 		}
 
-		totalBytesRead += cluster_block_size;
+		totalBytesRead += BlockSize;
 		old_lastblk = (totalBytesRead == src_filesize);
 
 		/* Save the page header data */
@@ -293,10 +296,10 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 			/* Set new checksum for visibility map page, if enabled */
 			if (new_cluster.controldata.data_checksum_version != 0)
 				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
+					pg_checksum_page(new_vmbuf.data, new_blkno, BlockSize);
 
 			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, cluster_block_size) != cluster_block_size)
+			if (write(dst_fd, new_vmbuf.data, BlockSize) != BlockSize)
 			{
 				/* if write didn't set errno, assume problem is no disk space */
 				if (errno == 0)
diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index 12a97f84e2..06b26e54a5 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -1,5 +1,7 @@
 # Copyright (c) 2022-2023, PostgreSQL Global Development Group
 
+fs = import('fs')
+
 pg_upgrade_sources = files(
   'check.c',
   'controldata.c',
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 96bfb67167..aef12f42a7 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -63,6 +63,7 @@ static void setup(char *argv0, bool *live_check);
 ClusterInfo old_cluster,
 			new_cluster;
 OSInfo		os_info;
+uint32		BlockSize;
 
 char	   *output_files[] = {
 	SERVER_LOG_FILE,
@@ -126,6 +127,8 @@ main(int argc, char **argv)
 
 	check_cluster_compatibility(live_check);
 
+	BlockSize = old_cluster.controldata.blocksz;
+
 	check_and_dump_old_cluster(live_check);
 
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 842f3b6cd3..e4313aa32b 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -337,7 +337,7 @@ extern UserOpts user_opts;
 extern ClusterInfo old_cluster,
 			new_cluster;
 extern OSInfo os_info;
-
+extern uint32 BlockSize;
 
 /* check.c */
 
diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index 059836f0e6..dc187a9f4d 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -841,8 +841,8 @@ parse_required_wal(verifier_context *context, char *pg_waldump_path,
 	{
 		char	   *pg_waldump_cmd;
 
-		pg_waldump_cmd = psprintf("\"%s\" --quiet --path=\"%s\" --timeline=%u --start=%X/%X --end=%X/%X\n",
-								  pg_waldump_path, wal_directory, this_wal_range->tli,
+		pg_waldump_cmd = psprintf("\"%s\" --quiet --pgdata=\"%s\" --path=\"%s\" --timeline=%u --start=%X/%X --end=%X/%X\n",
+								  pg_waldump_path, context->backup_directory, wal_directory, this_wal_range->tli,
 								  LSN_FORMAT_ARGS(this_wal_range->start_lsn),
 								  LSN_FORMAT_ARGS(this_wal_range->end_lsn));
 		fflush(NULL);
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index cc0247018a..4614bd8252 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -23,6 +23,8 @@
 #include "access/xlogreader.h"
 #include "access/xlogrecord.h"
 #include "access/xlogstats.h"
+#include "common/blocksize.h"
+#include "common/controldata_utils.h"
 #include "common/fe_memutils.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
@@ -38,8 +40,10 @@
  */
 
 static const char *progname;
+static char *pgdata;
 
 static int	WalSegSz;
+static int	BlockSize;
 static volatile sig_atomic_t time_to_stop = false;
 
 static const RelFileLocator emptyRelFileLocator = {0, 0, 0};
@@ -531,7 +535,7 @@ XLogRecordSaveFPWs(XLogReaderState *record, const char *savepath)
 		if (!file)
 			pg_fatal("could not open file \"%s\": %m", filename);
 
-		if (fwrite(page, cluster_block_size, 1, file) != 1)
+		if (fwrite(page, BlockSize, 1, file) != 1)
 			pg_fatal("could not write file \"%s\": %m", filename);
 
 		if (fclose(file) != 0)
@@ -801,6 +805,8 @@ main(int argc, char **argv)
 	XLogRecPtr	first_record;
 	char	   *waldir = NULL;
 	char	   *errormsg;
+	ControlFileData *ControlFile;
+	bool	crc_ok_p;
 
 	static struct option long_options[] = {
 		{"bkp-details", no_argument, NULL, 'b'},
@@ -812,6 +818,7 @@ main(int argc, char **argv)
 		{"help", no_argument, NULL, '?'},
 		{"limit", required_argument, NULL, 'n'},
 		{"path", required_argument, NULL, 'p'},
+		{"pgdata", required_argument, NULL, 'D'},
 		{"quiet", no_argument, NULL, 'q'},
 		{"relation", required_argument, NULL, 'R'},
 		{"rmgr", required_argument, NULL, 'r'},
@@ -885,7 +892,7 @@ main(int argc, char **argv)
 		goto bad_argument;
 	}
 
-	while ((option = getopt_long(argc, argv, "bB:e:fF:n:p:qr:R:s:t:wx:z",
+	while ((option = getopt_long(argc, argv, "bB:D:e:fF:n:p:qr:R:s:t:wx:z",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -903,6 +910,9 @@ main(int argc, char **argv)
 				config.filter_by_relation_block_enabled = true;
 				config.filter_by_extended = true;
 				break;
+			case 'D':
+				pgdata = pg_strdup(optarg);
+				break;
 			case 'e':
 				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
 				{
@@ -1106,6 +1116,23 @@ main(int argc, char **argv)
 		}
 	}
 
+	if (!pgdata)
+		pgdata = getenv("PGDATA");
+	if (!pgdata || strlen(pgdata) == 0)
+		pg_fatal("Must provide data directory via -D or PGDATA envvar");
+
+	ControlFile = get_controlfile(pgdata, &crc_ok_p);
+
+	if (!ControlFile)
+		pg_fatal("Could not locate control file");
+
+	BlockSize = ControlFile->blcksz;
+
+	if (!IsValidBlockSize(BlockSize))
+		pg_fatal("read invalid block size from control file");
+
+	BlockSizeInit(BlockSize);
+
 	if (config.save_fullpage_path != NULL)
 		create_fullpage_directory(config.save_fullpage_path);
 
diff --git a/src/bin/pg_waldump/t/001_basic.pl b/src/bin/pg_waldump/t/001_basic.pl
index 029a0d0521..4b33fe45a6 100644
--- a/src/bin/pg_waldump/t/001_basic.pl
+++ b/src/bin/pg_waldump/t/001_basic.pl
@@ -66,6 +66,8 @@ wal_level=logical
 });
 $node->start;
 
+$ENV{PGDATA} = $node->data_dir;
+
 my ($start_lsn, $start_walfile) = split /\|/, $node->safe_psql('postgres', q{SELECT pg_current_wal_insert_lsn(), pg_walfile_name(pg_current_wal_insert_lsn())});
 
 $node->safe_psql('postgres', q{
diff --git a/src/bin/pg_waldump/t/002_save_fullpage.pl b/src/bin/pg_waldump/t/002_save_fullpage.pl
index f0725805f2..d136324968 100644
--- a/src/bin/pg_waldump/t/002_save_fullpage.pl
+++ b/src/bin/pg_waldump/t/002_save_fullpage.pl
@@ -38,6 +38,8 @@ max_wal_senders = 4
 });
 $node->start;
 
+$ENV{PGDATA} = $node->data_dir;
+
 # Generate data/WAL to examine that will have full pages in them.
 $node->safe_psql(
 	'postgres',
@@ -73,6 +75,7 @@ $node->command_ok(
 	[
 		'pg_waldump', '--quiet',
 		'--save-fullpage', "$tmp_folder/raw",
+		'-D', $node->data_dir,
 		'--relation', $relation,
 		$walfile
 	],
diff --git a/src/common/Makefile b/src/common/Makefile
index cc5c54dcee..91a9fa4b5c 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -49,6 +49,7 @@ OBJS_COMMON = \
 	archive.o \
 	base64.o \
 	binaryheap.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..640b1516d2
--- /dev/null
+++ b/src/common/blocksize.c
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.c
+ *	  This file contains methods to calculate various size constants for variable-sized blocks.
+ *
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/common/clustersizes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+#include "access/heaptoast.h"
+#include "access/htup_details.h"
+#include "access/itup.h"
+#include "access/nbtree_int.h"
+#include "common/blocksize.h"
+#ifndef FRONTEND
+#include "storage/freespace.h"
+#endif
+
+PGDLLIMPORT BlockSizeIdent cluster_block_setting = BLOCK_SIZE_UNSET;
+
+/*
+ * 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).
+ */
+
+void
+BlockSizeInit(Size rawblocksize)
+{
+	uint32 bits = 0;
+	Size blocksize = rawblocksize;
+
+	Assert(IsValidBlockSize(rawblocksize));
+
+	// calculate max number of bits in the passed-in size
+	while (blocksize >>= 1)
+		bits++;
+
+	// our smallest block size, 1k, is 2^10, and we want this to be 1 if initialized
+	cluster_block_setting = (BlockSizeIdent)(bits - 10) + 1;
+
+	#ifndef FRONTEND
+	/* also setup the FreeSpaceMap internal sizing */
+	FreeSpaceMapInit();
+	#endif
+}
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 7876625db9..9b58df6221 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -659,7 +659,7 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
 ssize_t
 pg_pwrite_zeros(int fd, size_t size, off_t offset)
 {
-	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth cluster_block_size */
+	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth MAX_BLOCK_SIZE */
 	void	   *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
 	struct iovec iov[PG_IOV_MAX];
 	size_t		remaining_size = size;
@@ -677,10 +677,10 @@ pg_pwrite_zeros(int fd, size_t size, off_t offset)
 
 			iov[iovcnt].iov_base = zerobuf_addr;
 
-			if (remaining_size < cluster_block_size)
+			if (remaining_size < sizeof(zbuffer.data))
 				this_iov_size = remaining_size;
 			else
-				this_iov_size = cluster_block_size;
+				this_iov_size = sizeof(zbuffer.data);
 
 			iov[iovcnt].iov_len = this_iov_size;
 			remaining_size -= this_iov_size;
diff --git a/src/common/meson.build b/src/common/meson.build
index 3b97497d1a..38b7f3f49e 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -4,6 +4,7 @@ common_sources = files(
   'archive.c',
   'base64.c',
   'binaryheap.c',
+  'blocksize.c',
   'checksum_helper.c',
   'compression.c',
   'controldata_utils.c',
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 4c1dd74db7..939b8ef3f3 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -171,7 +171,7 @@ typedef struct GISTScanOpaqueData
 	GistNSN		curPageLSN;		/* pos in the WAL stream when page was read */
 
 	/* In a non-ordered search, returnable heap items are stored here: */
-	GISTSearchHeapItem pageData[cluster_block_size / sizeof(IndexTupleData)];
+	GISTSearchHeapItem pageData[MAX_BLOCK_SIZE / sizeof(IndexTupleData)];
 	OffsetNumber nPageData;		/* number of valid items in array */
 	OffsetNumber curPageData;	/* next item to return */
 	MemoryContext pageDataCxt;	/* context holding the fetched tuples, for
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 4f6df08020..7bab4c0105 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -124,7 +124,7 @@ typedef struct HashScanPosData
 	int			lastItem;		/* last valid index in items[] */
 	int			itemIndex;		/* current index in items[] */
 
-	HashScanPosItem items[MaxIndexTuplesPerPage];	/* MUST BE LAST */
+	HashScanPosItem items[MaxIndexTuplesPerPageLimit];	/* MUST BE LAST */
 } HashScanPosData;
 
 #define HashScanPosIsPinned(scanpos) \
@@ -227,7 +227,7 @@ typedef HashScanOpaqueData *HashScanOpaque;
  * the block size boundary.  So we use cluster_block_size to determine the maximum number
  * of bitmaps.
  */
-#define HASH_MAX_BITMAPS			Min(cluster_block_size / 8, 1024)
+#define HASH_MAX_BITMAPS			Min(DEFAULT_BLOCK_SIZE / 8, 1024)
 
 #define HASH_SPLITPOINT_PHASE_BITS	2
 #define HASH_SPLITPOINT_PHASES_PER_GRP	(1 << HASH_SPLITPOINT_PHASE_BITS)
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 2d3f149e4f..1f2b19ce07 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -75,7 +75,7 @@ typedef struct HeapScanDescData
 	/* these fields only used in page-at-a-time mode and for bitmap scans */
 	int			rs_cindex;		/* current tuple's index in vistuples */
 	int			rs_ntuples;		/* number of visible tuples on page */
-	OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
+	OffsetNumber rs_vistuples[MaxHeapTuplesPerPageLimit];	/* their offsets */
 }			HeapScanDescData;
 typedef struct HeapScanDescData *HeapScanDesc;
 
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index 3658856a60..1aaf08a166 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -20,10 +20,8 @@
 /*
  * Find the maximum size of a tuple if there are to be N tuples per page.
  */
-#define MaximumBytesPerTuple(tuplesPerPage) \
-	MAXALIGN_DOWN((cluster_block_size - \
-				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
-				  / (tuplesPerPage))
+#define MaximumBytesPerTuple(tuplesPerPage) CalcMaximumBytesPerTuple(cluster_block_size,tuplesPerPage)
+BlockSizeDecl2(CalcMaximumBytesPerTuple);
 
 /*
  * These symbols control toaster activation.  If a tuple is larger than
@@ -45,7 +43,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE	4
 
-#define toast_tuple_threshold	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
+#define toast_tuple_threshold	CalcMaximumBytesPerTuple(cluster_block_size,TOAST_TUPLES_PER_PAGE)
 
 #define cluster_toast_tuple_target		toast_tuple_threshold
 
@@ -58,7 +56,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE_MAIN	1
 
-#define toast_tuple_target_main MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
+#define toast_tuple_target_main CalcMaximumBytesPerTuple(cluster_block_size,TOAST_TUPLES_PER_PAGE_MAIN)
 
 /*
  * If an index value is larger than TOAST_INDEX_TARGET, we will try to
@@ -81,12 +79,10 @@
 
 #define EXTERN_TUPLE_MAX_SIZE	MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE)
 
-#define cluster_toast_max_chunk_size	\
-	(EXTERN_TUPLE_MAX_SIZE -							\
-	 MAXALIGN(SizeofHeapTupleHeader) -					\
-	 sizeof(Oid) -										\
-	 sizeof(int32) -									\
-	 VARHDRSZ)
+#define TOAST_MAX_CHUNK_SIZE_LIMIT CalcToastMaxChunkSize(MAX_BLOCK_SIZE)
+#define cluster_toast_max_chunk_size BlockSizeCalc(cluster_block_setting,CalcToastMaxChunkSize)
+BlockSizeDecl(CalcToastMaxChunkSize);
+
 
 /* ----------
  * heap_toast_insert_or_update -
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 005a530e0a..7acb40386b 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -18,6 +18,7 @@
 #include "access/transam.h"
 #include "access/tupdesc.h"
 #include "access/tupmacs.h"
+#include "common/blocksize.h"
 #include "storage/bufpage.h"
 #include "varatt.h"
 
@@ -427,8 +428,8 @@ do { \
 	(tup)->t_choice.t_heap.t_field3.t_xvac = (xid); \
 } while (0)
 
-StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
-				 "invalid speculative token constant");
+/* StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber, */
+/* 				 "invalid speculative token constant"); */
 
 #define HeapTupleHeaderIsSpeculative(tup) \
 ( \
@@ -560,8 +561,10 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * ItemIds and tuples have different alignment requirements, don't assume that
  * you can, say, fit 2 tuples of size MaxHeapTupleSize/2 on the same page.
  */
-#define MaxHeapTupleSize  (cluster_block_size - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+#define MaxHeapTupleSize BlockSizeCalc(cluster_block_setting,CalcMaxHeapTupleSize)
+#define MaxHeapTupleSizeLimit CalcMaxHeapTupleSize(MAX_BLOCK_SIZE)
 #define MinHeapTupleSize  MAXALIGN(SizeofHeapTupleHeader)
+BlockSizeDecl(CalcMaxHeapTupleSize);
 
 /*
  * MaxHeapTuplesPerPage is an upper bound on the number of tuples that can
@@ -574,9 +577,10 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * pointers to this anyway, to avoid excessive line-pointer bloat and not
  * require increases in the size of work arrays.
  */
-#define MaxHeapTuplesPerPage	\
-	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
-			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
+
+#define MaxHeapTuplesPerPage BlockSizeCalc(cluster_block_setting,CalcMaxHeapTuplesPerPage)
+#define MaxHeapTuplesPerPageLimit CalcMaxHeapTuplesPerPage(MAX_BLOCK_SIZE)
+BlockSizeDecl(CalcMaxHeapTuplesPerPage);
 
 /*
  * MaxAttrSize is a somewhat arbitrary upper limit on the declared size of
diff --git a/src/include/access/itup.h b/src/include/access/itup.h
index 8a68b9405b..90fce7ea15 100644
--- a/src/include/access/itup.h
+++ b/src/include/access/itup.h
@@ -163,8 +163,8 @@ index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
  * estimated here, seemingly allowing one more tuple than estimated here.
  * But such a page always has at least MAXALIGN special space, so we're safe.
  */
-#define MaxIndexTuplesPerPage	\
-	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
-			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
+#define MaxIndexTuplesPerPage BlockSizeCalc(cluster_block_setting,CalcMaxIndexTuplesPerPage)
+#define MaxIndexTuplesPerPageLimit CalcMaxIndexTuplesPerPage(MAX_BLOCK_SIZE)
+BlockSizeDecl(CalcMaxIndexTuplesPerPage);
 
 #endif							/* ITUP_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 794a3da1b2..a81b809330 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -15,6 +15,7 @@
 #define NBTREE_H
 
 #include "access/amapi.h"
+#include "access/nbtree_int.h"
 #include "access/itup.h"
 #include "access/sdir.h"
 #include "access/tableam.h"
@@ -25,208 +26,6 @@
 #include "storage/bufmgr.h"
 #include "storage/shm_toc.h"
 
-/* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
-typedef uint16 BTCycleId;
-
-/*
- *	BTPageOpaqueData -- At the end of every page, we store a pointer
- *	to both siblings in the tree.  This is used to do forward/backward
- *	index scans.  The next-page link is also critical for recovery when
- *	a search has navigated to the wrong page due to concurrent page splits
- *	or deletions; see src/backend/access/nbtree/README for more info.
- *
- *	In addition, we store the page's btree level (counting upwards from
- *	zero at a leaf page) as well as some flag bits indicating the page type
- *	and status.  If the page is deleted, a BTDeletedPageData struct is stored
- *	in the page's tuple area, while a standard BTPageOpaqueData struct is
- *	stored in the page special area.
- *
- *	We also store a "vacuum cycle ID".  When a page is split while VACUUM is
- *	processing the index, a nonzero value associated with the VACUUM run is
- *	stored into both halves of the split page.  (If VACUUM is not running,
- *	both pages receive zero cycleids.)	This allows VACUUM to detect whether
- *	a page was split since it started, with a small probability of false match
- *	if the page was last split some exact multiple of MAX_BT_CYCLE_ID VACUUMs
- *	ago.  Also, during a split, the BTP_SPLIT_END flag is cleared in the left
- *	(original) page, and set in the right page, but only if the next page
- *	to its right has a different cycleid.
- *
- *	NOTE: the BTP_LEAF flag bit is redundant since level==0 could be tested
- *	instead.
- *
- *	NOTE: the btpo_level field used to be a union type in order to allow
- *	deleted pages to store a 32-bit safexid in the same field.  We now store
- *	64-bit/full safexid values using BTDeletedPageData instead.
- */
-
-typedef struct BTPageOpaqueData
-{
-	BlockNumber btpo_prev;		/* left sibling, or P_NONE if leftmost */
-	BlockNumber btpo_next;		/* right sibling, or P_NONE if rightmost */
-	uint32		btpo_level;		/* tree level --- zero for leaf pages */
-	uint16		btpo_flags;		/* flag bits, see below */
-	BTCycleId	btpo_cycleid;	/* vacuum cycle ID of latest split */
-} BTPageOpaqueData;
-
-typedef BTPageOpaqueData *BTPageOpaque;
-
-#define BTPageGetOpaque(page) ((BTPageOpaque) PageGetSpecialPointer(page))
-
-/* Bits defined in btpo_flags */
-#define BTP_LEAF		(1 << 0)	/* leaf page, i.e. not internal page */
-#define BTP_ROOT		(1 << 1)	/* root page (has no parent) */
-#define BTP_DELETED		(1 << 2)	/* page has been deleted from tree */
-#define BTP_META		(1 << 3)	/* meta-page */
-#define BTP_HALF_DEAD	(1 << 4)	/* empty, but still in tree */
-#define BTP_SPLIT_END	(1 << 5)	/* rightmost page of split group */
-#define BTP_HAS_GARBAGE (1 << 6)	/* page has LP_DEAD tuples (deprecated) */
-#define BTP_INCOMPLETE_SPLIT (1 << 7)	/* right sibling's downlink is missing */
-#define BTP_HAS_FULLXID	(1 << 8)	/* contains BTDeletedPageData */
-
-/*
- * The max allowed value of a cycle ID is a bit less than 64K.  This is
- * for convenience of pg_filedump and similar utilities: we want to use
- * the last 2 bytes of special space as an index type indicator, and
- * restricting cycle ID lets btree use that space for vacuum cycle IDs
- * while still allowing index type to be identified.
- */
-#define MAX_BT_CYCLE_ID		0xFF7F
-
-
-/*
- * The Meta page is always the first page in the btree index.
- * Its primary purpose is to point to the location of the btree root page.
- * We also point to the "fast" root, which is the current effective root;
- * see README for discussion.
- */
-
-typedef struct BTMetaPageData
-{
-	uint32		btm_magic;		/* should contain BTREE_MAGIC */
-	uint32		btm_version;	/* nbtree version (always <= BTREE_VERSION) */
-	BlockNumber btm_root;		/* current root location */
-	uint32		btm_level;		/* tree level of the root page */
-	BlockNumber btm_fastroot;	/* current "fast" root location */
-	uint32		btm_fastlevel;	/* tree level of the "fast" root page */
-	/* remaining fields only valid when btm_version >= BTREE_NOVAC_VERSION */
-
-	/* number of deleted, non-recyclable pages during last cleanup */
-	uint32		btm_last_cleanup_num_delpages;
-	/* number of heap tuples during last cleanup (deprecated) */
-	float8		btm_last_cleanup_num_heap_tuples;
-
-	bool		btm_allequalimage;	/* are all columns "equalimage"? */
-} BTMetaPageData;
-
-#define BTPageGetMeta(p) \
-	((BTMetaPageData *) PageGetContents(p))
-
-/*
- * The current Btree version is 4.  That's what you'll get when you create
- * a new index.
- *
- * Btree version 3 was used in PostgreSQL v11.  It is mostly the same as
- * version 4, but heap TIDs were not part of the keyspace.  Index tuples
- * with duplicate keys could be stored in any order.  We continue to
- * support reading and writing Btree versions 2 and 3, so that they don't
- * need to be immediately re-indexed at pg_upgrade.  In order to get the
- * new heapkeyspace semantics, however, a REINDEX is needed.
- *
- * Deduplication is safe to use when the btm_allequalimage field is set to
- * true.  It's safe to read the btm_allequalimage field on version 3, but
- * only version 4 indexes make use of deduplication.  Even version 4
- * indexes created on PostgreSQL v12 will need a REINDEX to make use of
- * deduplication, though, since there is no other way to set
- * btm_allequalimage to true (pg_upgrade hasn't been taught to set the
- * metapage field).
- *
- * Btree version 2 is mostly the same as version 3.  There are two new
- * fields in the metapage that were introduced in version 3.  A version 2
- * metapage will be automatically upgraded to version 3 on the first
- * insert to it.  INCLUDE indexes cannot use version 2.
- */
-#define BTREE_METAPAGE	0		/* first page is meta */
-#define BTREE_MAGIC		0x053162	/* magic number in metapage */
-#define BTREE_VERSION	4		/* current version number */
-#define BTREE_MIN_VERSION	2	/* minimum supported version */
-#define BTREE_NOVAC_VERSION	3	/* version with all meta fields set */
-
-/*
- * Maximum size of a btree index entry, including its tuple header.
- *
- * We actually need to be able to fit three items on every page,
- * so restrict any one item to 1/3 the per-page available space.
- *
- * There are rare cases where _bt_truncate() will need to enlarge
- * a heap index tuple to make space for a tiebreaker heap TID
- * attribute, which we account for here.
- */
-#define BTMaxItemSize(page) \
-	(MAXALIGN_DOWN((PageGetPageSize(page) - \
-					MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
-					MAXALIGN(sizeof(BTPageOpaqueData))) / 3) - \
-					MAXALIGN(sizeof(ItemPointerData)))
-#define BTMaxItemSizeNoHeapTid(page) \
-	MAXALIGN_DOWN((PageGetPageSize(page) - \
-				   MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
-				   MAXALIGN(sizeof(BTPageOpaqueData))) / 3)
-
-/*
- * MaxTIDsPerBTreePage is an upper bound on the number of heap TIDs tuples
- * that may be stored on a btree leaf page.  It is used to size the
- * per-page temporary buffers.
- *
- * Note: we don't bother considering per-tuple overheads here to keep
- * things simple (value is based on how many elements a single array of
- * heap TIDs must have to fill the space between the page header and
- * special area).  The value is slightly higher (i.e. more conservative)
- * than necessary as a result, which is considered acceptable.
- */
-#define MaxTIDsPerBTreePage \
-	(int) ((cluster_block_size - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
-		   sizeof(ItemPointerData))
-
-/*
- * The leaf-page fillfactor defaults to 90% but is user-adjustable.
- * For pages above the leaf level, we use a fixed 70% fillfactor.
- * The fillfactor is applied during index build and when splitting
- * a rightmost page; when splitting non-rightmost pages we try to
- * divide the data equally.  When splitting a page that's entirely
- * filled with a single value (duplicates), the effective leaf-page
- * fillfactor is 96%, regardless of whether the page is a rightmost
- * page.
- */
-#define BTREE_MIN_FILLFACTOR		10
-#define BTREE_DEFAULT_FILLFACTOR	90
-#define BTREE_NONLEAF_FILLFACTOR	70
-#define BTREE_SINGLEVAL_FILLFACTOR	96
-
-/*
- *	In general, the btree code tries to localize its knowledge about
- *	page layout to a couple of routines.  However, we need a special
- *	value to indicate "no page number" in those places where we expect
- *	page numbers.  We can use zero for this because we never need to
- *	make a pointer to the metadata page.
- */
-
-#define P_NONE			0
-
-/*
- * Macros to test whether a page is leftmost or rightmost on its tree level,
- * as well as other state info kept in the opaque data.
- */
-#define P_LEFTMOST(opaque)		((opaque)->btpo_prev == P_NONE)
-#define P_RIGHTMOST(opaque)		((opaque)->btpo_next == P_NONE)
-#define P_ISLEAF(opaque)		(((opaque)->btpo_flags & BTP_LEAF) != 0)
-#define P_ISROOT(opaque)		(((opaque)->btpo_flags & BTP_ROOT) != 0)
-#define P_ISDELETED(opaque)		(((opaque)->btpo_flags & BTP_DELETED) != 0)
-#define P_ISMETA(opaque)		(((opaque)->btpo_flags & BTP_META) != 0)
-#define P_ISHALFDEAD(opaque)	(((opaque)->btpo_flags & BTP_HALF_DEAD) != 0)
-#define P_IGNORE(opaque)		(((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD)) != 0)
-#define P_HAS_GARBAGE(opaque)	(((opaque)->btpo_flags & BTP_HAS_GARBAGE) != 0)
-#define P_INCOMPLETE_SPLIT(opaque)	(((opaque)->btpo_flags & BTP_INCOMPLETE_SPLIT) != 0)
-#define P_HAS_FULLXID(opaque)	(((opaque)->btpo_flags & BTP_HAS_FULLXID) != 0)
-
 /*
  * BTDeletedPageData is the page contents of a deleted page
  */
@@ -892,7 +691,7 @@ typedef struct BTDedupStateData
 	 * are implicitly unchanged by deduplication pass).
 	 */
 	int			nintervals;		/* current number of intervals in array */
-	BTDedupInterval intervals[MaxIndexTuplesPerPage];
+	BTDedupInterval intervals[MaxIndexTuplesPerPageLimit];
 } BTDedupStateData;
 
 typedef BTDedupStateData *BTDedupState;
@@ -987,7 +786,7 @@ typedef struct BTScanPosData
 	int			lastItem;		/* last valid index in items[] */
 	int			itemIndex;		/* current index in items[] */
 
-	BTScanPosItem items[MaxTIDsPerBTreePage];	/* MUST BE LAST */
+	BTScanPosItem items[MaxTIDsPerBTreePageLimit];	/* MUST BE LAST */
 } BTScanPosData;
 
 typedef BTScanPosData *BTScanPos;
diff --git a/src/include/access/nbtree_int.h b/src/include/access/nbtree_int.h
new file mode 100644
index 0000000000..923cc5cb29
--- /dev/null
+++ b/src/include/access/nbtree_int.h
@@ -0,0 +1,223 @@
+/*-------------------------------------------------------------------------
+ *
+ * nbtree_int.h
+ *	  internal defs for header file for postgres btree access method implementation.
+ *
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/nbtree_int.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NBTREE_INT_H
+#define NBTREE_INT_H
+
+#include "storage/block.h"
+#include "storage/bufpage.h"
+#include "storage/itemptr.h"
+
+/* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
+typedef uint16 BTCycleId;
+
+/*
+ *	BTPageOpaqueData -- At the end of every page, we store a pointer
+ *	to both siblings in the tree.  This is used to do forward/backward
+ *	index scans.  The next-page link is also critical for recovery when
+ *	a search has navigated to the wrong page due to concurrent page splits
+ *	or deletions; see src/backend/access/nbtree/README for more info.
+ *
+ *	In addition, we store the page's btree level (counting upwards from
+ *	zero at a leaf page) as well as some flag bits indicating the page type
+ *	and status.  If the page is deleted, a BTDeletedPageData struct is stored
+ *	in the page's tuple area, while a standard BTPageOpaqueData struct is
+ *	stored in the page special area.
+ *
+ *	We also store a "vacuum cycle ID".  When a page is split while VACUUM is
+ *	processing the index, a nonzero value associated with the VACUUM run is
+ *	stored into both halves of the split page.  (If VACUUM is not running,
+ *	both pages receive zero cycleids.)	This allows VACUUM to detect whether
+ *	a page was split since it started, with a small probability of false match
+ *	if the page was last split some exact multiple of MAX_BT_CYCLE_ID VACUUMs
+ *	ago.  Also, during a split, the BTP_SPLIT_END flag is cleared in the left
+ *	(original) page, and set in the right page, but only if the next page
+ *	to its right has a different cycleid.
+ *
+ *	NOTE: the BTP_LEAF flag bit is redundant since level==0 could be tested
+ *	instead.
+ *
+ *	NOTE: the btpo_level field used to be a union type in order to allow
+ *	deleted pages to store a 32-bit safexid in the same field.  We now store
+ *	64-bit/full safexid values using BTDeletedPageData instead.
+ */
+
+typedef struct BTPageOpaqueData
+{
+	BlockNumber btpo_prev;		/* left sibling, or P_NONE if leftmost */
+	BlockNumber btpo_next;		/* right sibling, or P_NONE if rightmost */
+	uint32		btpo_level;		/* tree level --- zero for leaf pages */
+	uint16		btpo_flags;		/* flag bits, see below */
+	BTCycleId	btpo_cycleid;	/* vacuum cycle ID of latest split */
+} BTPageOpaqueData;
+
+typedef BTPageOpaqueData *BTPageOpaque;
+
+#define BTPageGetOpaque(page) ((BTPageOpaque) PageGetSpecialPointer(page))
+
+/* Bits defined in btpo_flags */
+#define BTP_LEAF		(1 << 0)	/* leaf page, i.e. not internal page */
+#define BTP_ROOT		(1 << 1)	/* root page (has no parent) */
+#define BTP_DELETED		(1 << 2)	/* page has been deleted from tree */
+#define BTP_META		(1 << 3)	/* meta-page */
+#define BTP_HALF_DEAD	(1 << 4)	/* empty, but still in tree */
+#define BTP_SPLIT_END	(1 << 5)	/* rightmost page of split group */
+#define BTP_HAS_GARBAGE (1 << 6)	/* page has LP_DEAD tuples (deprecated) */
+#define BTP_INCOMPLETE_SPLIT (1 << 7)	/* right sibling's downlink is missing */
+#define BTP_HAS_FULLXID	(1 << 8)	/* contains BTDeletedPageData */
+
+/*
+ * The max allowed value of a cycle ID is a bit less than 64K.  This is
+ * for convenience of pg_filedump and similar utilities: we want to use
+ * the last 2 bytes of special space as an index type indicator, and
+ * restricting cycle ID lets btree use that space for vacuum cycle IDs
+ * while still allowing index type to be identified.
+ */
+#define MAX_BT_CYCLE_ID		0xFF7F
+
+
+/*
+ * The Meta page is always the first page in the btree index.
+ * Its primary purpose is to point to the location of the btree root page.
+ * We also point to the "fast" root, which is the current effective root;
+ * see README for discussion.
+ */
+
+typedef struct BTMetaPageData
+{
+	uint32		btm_magic;		/* should contain BTREE_MAGIC */
+	uint32		btm_version;	/* nbtree version (always <= BTREE_VERSION) */
+	BlockNumber btm_root;		/* current root location */
+	uint32		btm_level;		/* tree level of the root page */
+	BlockNumber btm_fastroot;	/* current "fast" root location */
+	uint32		btm_fastlevel;	/* tree level of the "fast" root page */
+	/* remaining fields only valid when btm_version >= BTREE_NOVAC_VERSION */
+
+	/* number of deleted, non-recyclable pages during last cleanup */
+	uint32		btm_last_cleanup_num_delpages;
+	/* number of heap tuples during last cleanup (deprecated) */
+	float8		btm_last_cleanup_num_heap_tuples;
+
+	bool		btm_allequalimage;	/* are all columns "equalimage"? */
+} BTMetaPageData;
+
+#define BTPageGetMeta(p) \
+	((BTMetaPageData *) PageGetContents(p))
+
+/*
+ * The current Btree version is 4.  That's what you'll get when you create
+ * a new index.
+ *
+ * Btree version 3 was used in PostgreSQL v11.  It is mostly the same as
+ * version 4, but heap TIDs were not part of the keyspace.  Index tuples
+ * with duplicate keys could be stored in any order.  We continue to
+ * support reading and writing Btree versions 2 and 3, so that they don't
+ * need to be immediately re-indexed at pg_upgrade.  In order to get the
+ * new heapkeyspace semantics, however, a REINDEX is needed.
+ *
+ * Deduplication is safe to use when the btm_allequalimage field is set to
+ * true.  It's safe to read the btm_allequalimage field on version 3, but
+ * only version 4 indexes make use of deduplication.  Even version 4
+ * indexes created on PostgreSQL v12 will need a REINDEX to make use of
+ * deduplication, though, since there is no other way to set
+ * btm_allequalimage to true (pg_upgrade hasn't been taught to set the
+ * metapage field).
+ *
+ * Btree version 2 is mostly the same as version 3.  There are two new
+ * fields in the metapage that were introduced in version 3.  A version 2
+ * metapage will be automatically upgraded to version 3 on the first
+ * insert to it.  INCLUDE indexes cannot use version 2.
+ */
+#define BTREE_METAPAGE	0		/* first page is meta */
+#define BTREE_MAGIC		0x053162	/* magic number in metapage */
+#define BTREE_VERSION	4		/* current version number */
+#define BTREE_MIN_VERSION	2	/* minimum supported version */
+#define BTREE_NOVAC_VERSION	3	/* version with all meta fields set */
+
+/*
+ * Maximum size of a btree index entry, including its tuple header.
+ *
+ * We actually need to be able to fit three items on every page,
+ * so restrict any one item to 1/3 the per-page available space.
+ *
+ * There are rare cases where _bt_truncate() will need to enlarge
+ * a heap index tuple to make space for a tiebreaker heap TID
+ * attribute, which we account for here.
+ */
+#define BTMaxItemSize(page) \
+	(MAXALIGN_DOWN((PageGetPageSize(page) - \
+					MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
+					MAXALIGN(sizeof(BTPageOpaqueData))) / 3) - \
+					MAXALIGN(sizeof(ItemPointerData)))
+#define BTMaxItemSizeNoHeapTid(page) \
+	MAXALIGN_DOWN((PageGetPageSize(page) - \
+				   MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \
+				   MAXALIGN(sizeof(BTPageOpaqueData))) / 3)
+
+/*
+ * MaxTIDsPerBTreePage is an upper bound on the number of heap TIDs tuples
+ * that may be stored on a btree leaf page.  It is used to size the
+ * per-page temporary buffers.
+ *
+ * Note: we don't bother considering per-tuple overheads here to keep
+ * things simple (value is based on how many elements a single array of
+ * heap TIDs must have to fill the space between the page header and
+ * special area).  The value is slightly higher (i.e. more conservative)
+ * than necessary as a result, which is considered acceptable.
+ */
+#define MaxTIDsPerBTreePage BlockSizeCalc(cluster_block_setting,CalcMaxTIDsPerBTreePage)
+#define MaxTIDsPerBTreePageLimit CalcMaxTIDsPerBTreePage(MAX_BLOCK_SIZE)
+BlockSizeDecl(CalcMaxTIDsPerBTreePage);
+
+/*
+ * The leaf-page fillfactor defaults to 90% but is user-adjustable.
+ * For pages above the leaf level, we use a fixed 70% fillfactor.
+ * The fillfactor is applied during index build and when splitting
+ * a rightmost page; when splitting non-rightmost pages we try to
+ * divide the data equally.  When splitting a page that's entirely
+ * filled with a single value (duplicates), the effective leaf-page
+ * fillfactor is 96%, regardless of whether the page is a rightmost
+ * page.
+ */
+#define BTREE_MIN_FILLFACTOR		10
+#define BTREE_DEFAULT_FILLFACTOR	90
+#define BTREE_NONLEAF_FILLFACTOR	70
+#define BTREE_SINGLEVAL_FILLFACTOR	96
+
+/*
+ *	In general, the btree code tries to localize its knowledge about
+ *	page layout to a couple of routines.  However, we need a special
+ *	value to indicate "no page number" in those places where we expect
+ *	page numbers.  We can use zero for this because we never need to
+ *	make a pointer to the metadata page.
+ */
+
+#define P_NONE			0
+
+/*
+ * Macros to test whether a page is leftmost or rightmost on its tree level,
+ * as well as other state info kept in the opaque data.
+ */
+#define P_LEFTMOST(opaque)		((opaque)->btpo_prev == P_NONE)
+#define P_RIGHTMOST(opaque)		((opaque)->btpo_next == P_NONE)
+#define P_ISLEAF(opaque)		(((opaque)->btpo_flags & BTP_LEAF) != 0)
+#define P_ISROOT(opaque)		(((opaque)->btpo_flags & BTP_ROOT) != 0)
+#define P_ISDELETED(opaque)		(((opaque)->btpo_flags & BTP_DELETED) != 0)
+#define P_ISMETA(opaque)		(((opaque)->btpo_flags & BTP_META) != 0)
+#define P_ISHALFDEAD(opaque)	(((opaque)->btpo_flags & BTP_HALF_DEAD) != 0)
+#define P_IGNORE(opaque)		(((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD)) != 0)
+#define P_HAS_GARBAGE(opaque)	(((opaque)->btpo_flags & BTP_HAS_GARBAGE) != 0)
+#define P_INCOMPLETE_SPLIT(opaque)	(((opaque)->btpo_flags & BTP_INCOMPLETE_SPLIT) != 0)
+#define P_HAS_FULLXID(opaque)	(((opaque)->btpo_flags & BTP_HAS_FULLXID) != 0)
+
+#endif
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index ea803eccd8..89102369c0 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -226,14 +226,14 @@ typedef struct SpGistScanOpaqueData
 	TupleDesc	reconTupDesc;	/* if so, descriptor for reconstructed tuples */
 	int			nPtrs;			/* number of TIDs found on current page */
 	int			iPtr;			/* index for scanning through same */
-	ItemPointerData heapPtrs[MaxIndexTuplesPerPage];	/* TIDs from cur page */
-	bool		recheck[MaxIndexTuplesPerPage]; /* their recheck flags */
-	bool		recheckDistances[MaxIndexTuplesPerPage];	/* distance recheck
+	ItemPointerData heapPtrs[MaxIndexTuplesPerPageLimit];	/* TIDs from cur page */
+	bool		recheck[MaxIndexTuplesPerPageLimit]; /* their recheck flags */
+	bool		recheckDistances[MaxIndexTuplesPerPageLimit];	/* distance recheck
 															 * flags */
-	HeapTuple	reconTups[MaxIndexTuplesPerPage];	/* reconstructed tuples */
+	HeapTuple	reconTups[MaxIndexTuplesPerPageLimit];	/* reconstructed tuples */
 
 	/* distances (for recheck) */
-	IndexOrderByDistance *distances[MaxIndexTuplesPerPage];
+	IndexOrderByDistance *distances[MaxIndexTuplesPerPageLimit];
 
 	/*
 	 * Note: using MaxIndexTuplesPerPage above is a bit hokey since
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index b0fd338a00..3bc419ed78 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -391,6 +391,7 @@ extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli);
 extern void XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 								   bool detailed_format, StringInfo buf,
 								   uint32 *fpi_len);
+extern uint32 ClusterBlockSize(void);
 
 /*
  * Exported for the functions in timeline.c and xlogarchive.c.  Only valid
diff --git a/src/include/c.h b/src/include/c.h
index f156886798..2b0835b988 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1123,7 +1123,7 @@ extern void ExceptionalCondition(const char *conditionName,
 	((var) = ((var) < 0) ? 1 : -(var))
 
 /*
- * Use this, not "char buf[cluster_block_size]", to declare a field or local variable
+ * Use this, not "char buf[MAX_BLOCK_SIZE]", to declare a field or local variable
  * holding a page buffer, if that page might be accessed as a page.  Otherwise
  * the variable might be under-aligned, causing problems on alignment-picky
  * hardware.  We include both "double" and "int64" in the union to ensure that
@@ -1132,7 +1132,7 @@ extern void ExceptionalCondition(const char *conditionName,
  */
 typedef union PGAlignedBlock
 {
-	char		data[cluster_block_size];
+	char		data[MAX_BLOCK_SIZE];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGAlignedBlock;
@@ -1150,7 +1150,7 @@ typedef union PGIOAlignedBlock
 #ifdef pg_attribute_aligned
 	pg_attribute_aligned(PG_IO_ALIGN_SIZE)
 #endif
-	char		data[cluster_block_size];
+	char		data[MAX_BLOCK_SIZE];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGIOAlignedBlock;
diff --git a/src/include/common/blocksize.h b/src/include/common/blocksize.h
new file mode 100644
index 0000000000..b5fe75ea7b
--- /dev/null
+++ b/src/include/common/blocksize.h
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+ *
+ * blocksize.h
+ *	  defintions for cluster-specific limits/structure defs
+ *
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION: src/include/clustersizes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef BLOCKSIZE_H
+#define BLOCKSIZE_H
+
+#ifndef DEFAULT_BLOCK_SIZE
+#define DEFAULT_BLOCK_SIZE 8192
+#endif
+
+#ifndef MIN_BLOCK_SIZE
+#define MIN_BLOCK_SIZE 1024
+#endif
+
+#ifndef MAX_BLOCK_SIZE
+#define MAX_BLOCK_SIZE 32*1024
+#endif
+
+#define IsValidBlockSize(size) ((size) >= MIN_BLOCK_SIZE && \
+								(size) <= MAX_BLOCK_SIZE && \
+								((size)&((size)-1)) == 0)
+
+/* identifiers only */
+typedef enum {
+	BLOCK_SIZE_UNSET = 0,
+	BLOCK_SIZE_1K,
+	BLOCK_SIZE_2K,
+	BLOCK_SIZE_4K,
+	BLOCK_SIZE_8K,
+	BLOCK_SIZE_16K,
+	BLOCK_SIZE_32K,
+} BlockSizeIdent;
+
+extern PGDLLIMPORT BlockSizeIdent cluster_block_setting;
+
+void BlockSizeInit(Size rawblocksize);
+#define cluster_block_bits (cluster_block_setting+9)
+#define cluster_block_size (1<<cluster_block_bits)
+// TODO: make this calculate using use DEFAULT_BLOCK_SIZE instead?
+#define DEFAULT_BLOCK_SIZE_BITS 13 
+#define cluster_relseg_size (RELSEG_SIZE << DEFAULT_BLOCK_SIZE_BITS >> cluster_block_bits)
+
+/* Specific calculations' now parameterized sources */
+
+/* originally in heaptoast.h */
+
+#define CalcMaximumBytesPerTuple(blocksize,tuplesPerPage)	\
+	MAXALIGN_DOWN((blocksize - \
+				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
+				  / (tuplesPerPage))
+
+#define CalcToastMaxChunkSize(blocksize) \
+	(CalcMaximumBytesPerTuple(blocksize,EXTERN_TUPLES_PER_PAGE) -	\
+	 MAXALIGN(SizeofHeapTupleHeader) -					\
+	 sizeof(Oid) -										\
+	 sizeof(int32) -									\
+	 VARHDRSZ)
+
+/* originally in htup_details.h */
+
+#define CalcMaxHeapTupleSize(size)  (size - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+
+#define CalcMaxHeapTuplesPerPage(size)									\
+	((int) (((size) - SizeOfPageHeaderData) /							\
+			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
+
+/* originally in itup.h */
+
+#define CalcMaxIndexTuplesPerPage(size)		\
+	((int) ((size - SizeOfPageHeaderData) / \
+			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
+
+/* originally in nbtree_int.h */
+
+#define CalcMaxTIDsPerBTreePage(size)									\
+	(int) ((size - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
+		   sizeof(ItemPointerData))
+
+/* originally in bloom.h */
+#define CalcFreeBlockNumberElems(size) (MAXALIGN_DOWN(size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData)) \
+													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions)) \
+									 ) / sizeof(BlockNumber))
+
+#define BlockSizeDecl(calc) \
+	static inline unsigned int _block_size_##calc(BlockSizeIdent bsi) {	\
+	switch(bsi){														\
+	case BLOCK_SIZE_1K: return calc(1024); break;						\
+	case BLOCK_SIZE_2K: return calc(2048); break;						\
+	case BLOCK_SIZE_4K: return calc(4096); break;						\
+	case BLOCK_SIZE_8K: return calc(8192); break;						\
+	case BLOCK_SIZE_16K: return calc(16384); break;						\
+	case BLOCK_SIZE_32K: return calc(32768); break;						\
+	default: return 0;}}
+
+#define BlockSizeDecl2(calc)											\
+	static inline unsigned int _block_size_##calc(BlockSizeIdent bsi, unsigned int arg) { \
+	switch(bsi){														\
+	case BLOCK_SIZE_1K: return calc(1024,arg); break;						\
+	case BLOCK_SIZE_2K: return calc(2048,arg); break;						\
+	case BLOCK_SIZE_4K: return calc(4096,arg); break;						\
+	case BLOCK_SIZE_8K: return calc(8192,arg); break;						\
+	case BLOCK_SIZE_16K: return calc(16384,arg); break;					\
+	case BLOCK_SIZE_32K: return calc(32768,arg); break;					\
+	default: return 0;}}
+
+#define BlockSizeCalc(bsi,calc) _block_size_##calc(bsi)
+
+
+#endif
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index d8a2985567..acac7282b1 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -21,14 +21,15 @@
 /* The normal alignment of `short', in bytes. */
 #undef ALIGNOF_SHORT
 
-/* Size of a disk block --- this also limits the size of a tuple. You can set
-   it bigger if you need bigger tuples (although TOAST should reduce the need
-   to have large tuples, since fields can be spread across multiple tuples).
-   BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
-   currently 2^15 (32768). This is determined by the 15-bit widths of the
-   lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
-   Changing BLCKSZ requires an initdb. */
-#undef BLCKSZ
+/* Default size of a disk block --- this also limits the size of a tuple. You
+   can set it bigger if you need bigger tuples (although TOAST should reduce
+   the need to have large tuples, since fields can be spread across multiple
+   tuples).  DEFAULT_BLOCK_SIZE must be a power of 2. The maximum possible
+   value of DEFAULT_BLOCK_SIZE is currently 2^15 (32768). This is determined
+   by the 15-bit widths of the lp_off and lp_len fields in ItemIdData (see
+   include/storage/itemid.h).  Changing DEFAULT_BLOCK_SIZE requires an
+   initdb. */
+#undef DEFAULT_BLOCK_SIZE
 
 /* Saved arguments from configure */
 #undef CONFIGURE_ARGS
@@ -650,16 +651,16 @@
 #undef PTHREAD_CREATE_JOINABLE
 
 /* RELSEG_SIZE is the maximum number of blocks allowed in one disk file. Thus,
-   the maximum size of a single file is RELSEG_SIZE * BLCKSZ; relations bigger
-   than that are divided into multiple files. RELSEG_SIZE * BLCKSZ must be
-   less than your OS' limit on file size. This is often 2 GB or 4GB in a
-   32-bit operating system, unless you have large file support enabled. By
-   default, we make the limit 1 GB to avoid any possible integer-overflow
-   problems within the OS. A limit smaller than necessary only means we divide
-   a large relation into more chunks than necessary, so it seems best to err
-   in the direction of a small limit. A power-of-2 value is recommended to
-   save a few cycles in md.c, but is not absolutely required. Changing
-   RELSEG_SIZE requires an initdb. */
+   the maximum size of a single file is RELSEG_SIZE * cluster_block_size;
+   relations bigger than that are divided into multiple files. RELSEG_SIZE *
+   cluster_block_size must be less than your OS' limit on file size. This is
+   often 2 GB or 4GB in a 32-bit operating system, unless you have large file
+   support enabled. By default, we make the limit 1 GB to avoid any possible
+   integer-overflow problems within the OS. A limit smaller than necessary
+   only means we divide a large relation into more chunks than necessary, so
+   it seems best to err in the direction of a small limit. A power-of-2 value
+   is recommended to save a few cycles in md.c, but is not absolutely
+   required. Changing RELSEG_SIZE requires an initdb. */
 #undef RELSEG_SIZE
 
 /* The size of `bool', as computed by sizeof. */
@@ -774,7 +775,7 @@
 # endif
 #endif
 
-/* Size of a WAL file block. This need have no particular relation to BLCKSZ.
+/* Size of a WAL file block. This need have no particular relation to cluster_block_size.
    XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O,
    XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O
    buffers, else direct I/O may fail. Changing XLOG_BLCKSZ requires an initdb.
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 0446bfb407..70bddf2c7b 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -233,6 +233,14 @@
  */
 #define PG_IO_ALIGN_SIZE		4096
 
+/*
+ * Blocksize-related constants
+ */
+
+#define MIN_BLOCK_SIZE 1024
+#define MAX_BLOCK_SIZE 32768
+/* #define DEFAULT_BLOCK_SIZE 8192 */
+
 /*
  *------------------------------------------------------------------------
  * The following symbols are for enabling debugging code, not for
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 8a028ff789..4651389fc2 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -45,6 +45,7 @@
 #include "c.h"
 #include "utils/elog.h"
 #include "utils/palloc.h"
+#include "common/blocksize.h"
 
 /* ----------------------------------------------------------------
  *				Section 1:	Datum type + support functions
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index 1ed3d694ee..240ad4e93b 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -70,5 +70,4 @@ typedef PG_INT64_TYPE pg_int64;
 #define PG_DIAG_SOURCE_LINE		'L'
 #define PG_DIAG_SOURCE_FUNCTION 'R'
 
-#define cluster_block_size BLCKSZ
 #endif							/* POSTGRES_EXT_H */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 20d8d7277a..a997380707 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -475,15 +475,15 @@ do { \
 	PageIsVerifiedExtended(page, blkno, \
 						   PIV_LOG_WARNING | PIV_REPORT_STAT)
 
-/*
- * Check that cluster_block_size is a multiple of sizeof(size_t).  In
- * PageIsVerifiedExtended(), it is much faster to check if a page is
- * full of zeroes using the native word size.  Note that this assertion
- * is kept within a header to make sure that StaticAssertDecl() works
- * across various combinations of platforms and compilers.
- */
-StaticAssertDecl(cluster_block_size == ((cluster_block_size / sizeof(size_t)) * sizeof(size_t)),
-				 "cluster_block_size has to be a multiple of sizeof(size_t)");
+/* /\* */
+/*  * Check that cluster_block_size is a multiple of sizeof(size_t).  In */
+/*  * PageIsVerifiedExtended(), it is much faster to check if a page is */
+/*  * full of zeroes using the native word size.  Note that this assertion */
+/*  * is kept within a header to make sure that StaticAssertDecl() works */
+/*  * across various combinations of platforms and compilers. */
+/*  *\/ */
+/* StaticAssertDecl(cluster_block_size == ((cluster_block_size / sizeof(size_t)) * sizeof(size_t)), */
+/* 				 "cluster_block_size has to be a multiple of sizeof(size_t)"); */
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
 extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
diff --git a/src/include/storage/checksum.h b/src/include/storage/checksum.h
index 4afd25a0af..bea1cbd5b6 100644
--- a/src/include/storage/checksum.h
+++ b/src/include/storage/checksum.h
@@ -19,6 +19,6 @@
  * Compute the checksum for a Postgres page.  The page must be aligned on a
  * 4-byte boundary.
  */
-extern uint16 pg_checksum_page(char *page, BlockNumber blkno);
+extern uint16 pg_checksum_page(char *page, BlockNumber blkno, Size pagesize);
 
 #endif							/* CHECKSUM_H */
diff --git a/src/include/storage/checksum_impl.h b/src/include/storage/checksum_impl.h
index 18951c36c7..e51e04ec4a 100644
--- a/src/include/storage/checksum_impl.h
+++ b/src/include/storage/checksum_impl.h
@@ -101,6 +101,7 @@
  */
 
 #include "storage/bufpage.h"
+#include "common/blocksize.h"
 
 /* number of checksums to calculate in parallel */
 #define N_SUMS 32
@@ -111,7 +112,7 @@
 typedef union
 {
 	PageHeaderData phdr;
-	uint32		data[cluster_block_size / (sizeof(uint32) * N_SUMS)][N_SUMS];
+	uint32		data[MAX_BLOCK_SIZE / (sizeof(uint32) * N_SUMS)][N_SUMS];
 } PGChecksummablePage;
 
 /*
@@ -143,7 +144,7 @@ do { \
  * (at least on 4-byte boundary).
  */
 static uint32
-pg_checksum_block(const PGChecksummablePage *page)
+pg_checksum_block(const PGChecksummablePage *page, Size blocksize)
 {
 	uint32		sums[N_SUMS];
 	uint32		result = 0;
@@ -151,13 +152,13 @@ pg_checksum_block(const PGChecksummablePage *page)
 				j;
 
 	/* ensure that the size is compatible with the algorithm */
-	Assert(sizeof(PGChecksummablePage) == cluster_block_size);
+	Assert(cluster_block_size == blocksize);
 
 	/* initialize partial checksums to their corresponding offsets */
 	memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets));
 
 	/* main checksum calculation */
-	for (i = 0; i < (uint32) (cluster_block_size / (sizeof(uint32) * N_SUMS)); i++)
+	for (i = 0; i < (uint32) (blocksize / (sizeof(uint32) * N_SUMS)); i++)
 		for (j = 0; j < N_SUMS; j++)
 			CHECKSUM_COMP(sums[j], page->data[i][j]);
 
@@ -184,7 +185,7 @@ pg_checksum_block(const PGChecksummablePage *page)
  * checksum itself), and the page data.
  */
 uint16
-pg_checksum_page(char *page, BlockNumber blkno)
+pg_checksum_page(char *page, BlockNumber blkno, Size pagesize)
 {
 	PGChecksummablePage *cpage = (PGChecksummablePage *) page;
 	uint16		save_checksum;
@@ -201,7 +202,7 @@ pg_checksum_page(char *page, BlockNumber blkno)
 	 */
 	save_checksum = cpage->phdr.pd_checksum;
 	cpage->phdr.pd_checksum = 0;
-	checksum = pg_checksum_block(cpage);
+	checksum = pg_checksum_block(cpage, pagesize);
 	cpage->phdr.pd_checksum = save_checksum;
 
 	/* Mix in the block number to detect transposed pages */
diff --git a/src/include/storage/freespace.h b/src/include/storage/freespace.h
index 9e1a85a141..4bbad4aaad 100644
--- a/src/include/storage/freespace.h
+++ b/src/include/storage/freespace.h
@@ -19,6 +19,7 @@
 #include "utils/relcache.h"
 
 /* prototypes for public functions in freespace.c */
+extern void FreeSpaceMapInit(void);
 extern Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk);
 extern BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded);
 extern BlockNumber RecordAndGetPageWithFreeSpace(Relation rel,
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
index 9fab334c7b..9515df5f05 100644
--- a/src/include/storage/large_object.h
+++ b/src/include/storage/large_object.h
@@ -67,7 +67,9 @@ typedef struct LargeObjectDesc
  *
  * NB: Changing cluster_loblksize requires an initdb.
  */
+#define CalcLOBLKSIZE(size) (size/4)
 #define cluster_loblksize		(cluster_block_size / 4)
+#define LOBLKSIZE_LIMIT		CalcLOBLKSIZE(MAX_BLOCK_SIZE)
 
 /*
  * Maximum length in bytes for a large object.  To make this larger, we'd
diff --git a/src/include/storage/off.h b/src/include/storage/off.h
index 7fbd45a084..bfcd0a431f 100644
--- a/src/include/storage/off.h
+++ b/src/include/storage/off.h
@@ -25,7 +25,9 @@ typedef uint16 OffsetNumber;
 
 #define InvalidOffsetNumber		((OffsetNumber) 0)
 #define FirstOffsetNumber		((OffsetNumber) 1)
-#define MaxOffsetNumber			((OffsetNumber) (cluster_block_size / sizeof(ItemIdData)))
+#define CalcMaxOffsetNumber(size)			((OffsetNumber) (size / sizeof(ItemIdData)))
+#define MaxOffsetNumber			((OffsetNumber)CalcMaxOffsetNumber(cluster_block_size))
+#define MaxOffsetNumberLimit			((OffsetNumber)CalcMaxOffsetNumber(MAX_BLOCK_SIZE))
 
 /* ----------------
  *		support macros
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index d5a0880678..a47b6b6c2d 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -298,6 +298,7 @@ extern bool ConfigOptionIsVisible(struct config_generic *conf);
 /* get the current set of variables */
 extern struct config_generic **get_guc_variables(int *num_vars);
 
+extern void update_dynamic_gucs(void);
 extern void build_guc_variables(void);
 
 /* search in enum options */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index db242c9205..7832c00b18 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -135,7 +135,7 @@ sub mkvcbuild
 	}
 
 	our @pgcommonallfiles = qw(
-	  archive.c base64.c binaryheap.c checksum_helper.c compression.c
+	  archive.c base64.c binaryheap.c blocksize.c checksum_helper.c compression.c
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
 	  keywords.c kwlookup.c link-canary.c md5_common.c percentrepl.c
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index f6a40ae730..d7717d2bbc 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -212,10 +212,10 @@ sub GenerateFiles
 		ALIGNOF_PG_INT128_TYPE => undef,
 		ALIGNOF_SHORT => 2,
 		AC_APPLE_UNIVERSAL_BUILD => undef,
-		cluster_block_size => 1024 * $self->{options}->{blocksize},
 		CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"',
 		DEF_PGPORT => $port,
 		DEF_PGPORT_STR => qq{"$port"},
+		DEFAULT_BLOCK_SIZE => 1024 * $self->{options}->{blocksize},
 		DLSUFFIX => '".dll"',
 		ENABLE_GSS => $self->{options}->{gss} ? 1 : undef,
 		ENABLE_NLS => $self->{options}->{nls} ? 1 : undef,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8de90c4958..25584219f0 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -894,7 +894,6 @@ Form_pg_ts_template
 Form_pg_type
 Form_pg_user_mapping
 FormatNode
-FreeBlockNumberArray
 FreeListData
 FreePageBtree
 FreePageBtreeHeader
-- 
2.40.1

v3-0003-Capture-rename-of-symbols.patchapplication/octet-stream; name=v3-0003-Capture-rename-of-symbols.patchDownload
From 0eb57b14d48c6593a4e3c7ea89490ae45c898377 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Tue, 29 Aug 2023 16:09:37 -0400
Subject: [PATCH v3 3/8] Capture rename of symbols

---
 contrib/amcheck/expected/check_btree.out    |  2 +-
 contrib/amcheck/sql/check_btree.sql         |  2 +-
 contrib/amcheck/verify_heapam.c             | 12 ++--
 contrib/amcheck/verify_nbtree.c             |  6 +-
 contrib/bloom/blinsert.c                    |  2 +-
 contrib/bloom/bloom.h                       |  4 +-
 contrib/bloom/blutils.c                     |  2 +-
 contrib/file_fdw/file_fdw.c                 |  6 +-
 contrib/pageinspect/btreefuncs.c            |  6 +-
 contrib/pageinspect/expected/checksum.out   |  2 +-
 contrib/pageinspect/expected/checksum_1.out |  2 +-
 contrib/pageinspect/rawpage.c               | 10 ++--
 contrib/pageinspect/sql/checksum.sql        |  2 +-
 contrib/pg_walinspect/pg_walinspect.c       |  6 +-
 contrib/pgstattuple/pgstatapprox.c          |  6 +-
 contrib/pgstattuple/pgstatindex.c           |  4 +-
 contrib/pgstattuple/pgstattuple.c           | 10 ++--
 contrib/postgres_fdw/deparse.c              |  2 +-
 contrib/postgres_fdw/postgres_fdw.c         |  2 +-
 doc/src/sgml/catalogs.sgml                  | 12 ++--
 doc/src/sgml/config.sgml                    | 32 +++++------
 doc/src/sgml/limits.sgml                    |  2 +-
 doc/src/sgml/monitoring.sgml                |  2 +-
 doc/src/sgml/pgfreespacemap.sgml            |  2 +-
 doc/src/sgml/storage.sgml                   |  8 +--
 src/backend/access/brin/brin_bloom.c        |  2 +-
 src/backend/access/brin/brin_pageops.c      |  4 +-
 src/backend/access/common/bufmask.c         |  4 +-
 src/backend/access/common/reloptions.c      |  2 +-
 src/backend/access/common/syncscan.c        |  2 +-
 src/backend/access/common/toast_internals.c |  4 +-
 src/backend/access/gin/ginbtree.c           | 12 ++--
 src/backend/access/gin/gindatapage.c        | 18 +++---
 src/backend/access/gin/ginfast.c            |  8 +--
 src/backend/access/gin/ginget.c             |  6 +-
 src/backend/access/gin/ginvacuum.c          |  2 +-
 src/backend/access/gin/ginxlog.c            |  4 +-
 src/backend/access/gist/gistbuild.c         | 16 +++---
 src/backend/access/gist/gistbuildbuffers.c  | 10 ++--
 src/backend/access/gist/gistutil.c          |  2 +-
 src/backend/access/hash/hash.c              |  2 +-
 src/backend/access/hash/hashpage.c          |  2 +-
 src/backend/access/heap/README.HOT          |  2 +-
 src/backend/access/heap/heapam.c            | 20 +++----
 src/backend/access/heap/heapam_handler.c    | 10 ++--
 src/backend/access/heap/heaptoast.c         | 20 +++----
 src/backend/access/heap/pruneheap.c         |  2 +-
 src/backend/access/heap/rewriteheap.c       |  6 +-
 src/backend/access/heap/vacuumlazy.c        | 18 +++---
 src/backend/access/heap/visibilitymap.c     |  4 +-
 src/backend/access/nbtree/nbtdedup.c        |  8 +--
 src/backend/access/nbtree/nbtpage.c         | 14 ++---
 src/backend/access/nbtree/nbtree.c          |  4 +-
 src/backend/access/nbtree/nbtsort.c         | 12 ++--
 src/backend/access/rmgrdesc/xlogdesc.c      |  2 +-
 src/backend/access/spgist/spgdoinsert.c     | 24 ++++----
 src/backend/access/spgist/spgtextproc.c     | 10 ++--
 src/backend/access/spgist/spgutils.c        | 12 ++--
 src/backend/access/table/tableam.c          |  2 +-
 src/backend/access/transam/README           |  2 +-
 src/backend/access/transam/clog.c           |  6 +-
 src/backend/access/transam/commit_ts.c      | 24 ++++----
 src/backend/access/transam/generic_xlog.c   | 18 +++---
 src/backend/access/transam/multixact.c      | 54 +++++++++---------
 src/backend/access/transam/slru.c           | 22 ++++----
 src/backend/access/transam/subtrans.c       | 20 +++----
 src/backend/access/transam/timeline.c       |  2 +-
 src/backend/access/transam/varsup.c         |  2 +-
 src/backend/access/transam/xlog.c           | 30 +++++-----
 src/backend/access/transam/xloginsert.c     | 24 ++++----
 src/backend/access/transam/xlogprefetcher.c |  4 +-
 src/backend/access/transam/xlogreader.c     | 28 +++++-----
 src/backend/access/transam/xlogrecovery.c   | 10 ++--
 src/backend/backup/basebackup.c             | 40 ++++++-------
 src/backend/backup/basebackup_lz4.c         |  6 +-
 src/backend/backup/basebackup_zstd.c        |  6 +-
 src/backend/catalog/storage.c               |  2 +-
 src/backend/commands/analyze.c              |  4 +-
 src/backend/commands/async.c                |  6 +-
 src/backend/commands/vacuumparallel.c       |  2 +-
 src/backend/executor/nodeAgg.c              | 30 +++++-----
 src/backend/nodes/tidbitmap.c               |  4 +-
 src/backend/optimizer/path/costsize.c       | 12 ++--
 src/backend/optimizer/util/plancat.c        |  2 +-
 src/backend/po/de.po                        | 16 +++---
 src/backend/po/es.po                        | 14 ++---
 src/backend/po/fr.po                        | 18 +++---
 src/backend/po/id.po                        |  8 +--
 src/backend/po/it.po                        | 12 ++--
 src/backend/po/ja.po                        | 16 +++---
 src/backend/po/ko.po                        | 22 ++++----
 src/backend/po/pl.po                        | 12 ++--
 src/backend/po/pt_BR.po                     | 10 ++--
 src/backend/po/ru.po                        | 24 ++++----
 src/backend/po/sv.po                        | 14 ++---
 src/backend/po/tr.po                        | 12 ++--
 src/backend/po/uk.po                        | 12 ++--
 src/backend/po/zh_CN.po                     | 12 ++--
 src/backend/replication/logical/worker.c    |  2 +-
 src/backend/storage/buffer/buf_init.c       |  4 +-
 src/backend/storage/buffer/bufmgr.c         | 22 ++++----
 src/backend/storage/buffer/freelist.c       |  2 +-
 src/backend/storage/buffer/localbuf.c       |  8 +--
 src/backend/storage/file/buffile.c          | 14 ++---
 src/backend/storage/file/copydir.c          |  2 +-
 src/backend/storage/file/fd.c               |  4 +-
 src/backend/storage/freespace/README        |  8 +--
 src/backend/storage/freespace/freespace.c   | 22 ++++----
 src/backend/storage/freespace/indexfsm.c    |  6 +-
 src/backend/storage/large_object/inv_api.c  | 30 +++++-----
 src/backend/storage/lmgr/predicate.c        |  4 +-
 src/backend/storage/page/bufpage.c          | 22 ++++----
 src/backend/storage/smgr/md.c               | 62 ++++++++++-----------
 src/backend/utils/adt/pgstatfuncs.c         |  4 +-
 src/backend/utils/adt/selfuncs.c            |  2 +-
 src/backend/utils/init/miscinit.c           |  6 +-
 src/backend/utils/misc/guc.c                | 16 +++---
 src/backend/utils/misc/guc_tables.c         |  6 +-
 src/backend/utils/sort/logtape.c            | 48 ++++++++--------
 src/backend/utils/sort/sharedtuplestore.c   | 12 ++--
 src/backend/utils/sort/tuplesort.c          |  6 +-
 src/bin/initdb/initdb.c                     | 22 ++++----
 src/bin/pg_checksums/pg_checksums.c         | 18 +++---
 src/bin/pg_resetwal/pg_resetwal.c           |  6 +-
 src/bin/pg_rewind/filemap.c                 |  4 +-
 src/bin/pg_rewind/pg_rewind.c               |  4 +-
 src/bin/pg_upgrade/file.c                   | 10 ++--
 src/bin/pg_waldump/pg_waldump.c             |  2 +-
 src/common/file_utils.c                     |  6 +-
 src/include/access/brin_page.h              |  6 +-
 src/include/access/ginblock.h               |  6 +-
 src/include/access/gist.h                   |  2 +-
 src/include/access/gist_private.h           |  6 +-
 src/include/access/hash.h                   |  6 +-
 src/include/access/heaptoast.h              | 20 +++----
 src/include/access/htup_details.h           |  6 +-
 src/include/access/itup.h                   |  2 +-
 src/include/access/nbtree.h                 |  6 +-
 src/include/access/slru.h                   |  2 +-
 src/include/access/spgist_private.h         |  6 +-
 src/include/access/xlogrecord.h             |  6 +-
 src/include/backup/basebackup_sink.h        |  4 +-
 src/include/c.h                             |  6 +-
 src/include/pg_config_manual.h              |  2 +-
 src/include/postgres_ext.h                  |  1 +
 src/include/storage/bufmgr.h                |  4 +-
 src/include/storage/bufpage.h               |  8 +--
 src/include/storage/checksum_impl.h         |  6 +-
 src/include/storage/fsm_internals.h         |  4 +-
 src/include/storage/large_object.h          |  8 +--
 src/include/storage/off.h                   |  2 +-
 src/include/utils/rel.h                     |  4 +-
 src/test/modules/test_slru/test_slru.c      |  2 +-
 src/test/regress/expected/btree_index.out   |  4 +-
 src/test/regress/expected/largeobject.out   |  2 +-
 src/test/regress/expected/largeobject_1.out |  2 +-
 src/test/regress/sql/btree_index.sql        |  4 +-
 src/test/regress/sql/largeobject.sql        |  2 +-
 src/tools/msvc/Solution.pm                  |  2 +-
 159 files changed, 752 insertions(+), 751 deletions(-)

diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out
index 38791bbc1f..b677475ae9 100644
--- a/contrib/amcheck/expected/check_btree.out
+++ b/contrib/amcheck/expected/check_btree.out
@@ -168,7 +168,7 @@ WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
  x
 (1 row)
 
--- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
+-- Insert compressible heap tuple (comfortably exceeds toast_tuple_threshold):
 INSERT INTO toast_bug SELECT repeat('a', 2200);
 -- Should not get false positive report of corruption:
 SELECT bt_index_check('toasty', true);
diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql
index 033c04b4d0..6bf3f02c22 100644
--- a/contrib/amcheck/sql/check_btree.sql
+++ b/contrib/amcheck/sql/check_btree.sql
@@ -110,7 +110,7 @@ WHERE attrelid = 'toasty'::regclass AND attname = 'buggy';
 SELECT attstorage FROM pg_attribute
 WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
 
--- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
+-- Insert compressible heap tuple (comfortably exceeds toast_tuple_threshold):
 INSERT INTO toast_bug SELECT repeat('a', 2200);
 -- Should not get false positive report of corruption:
 SELECT bt_index_check('toasty', true);
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 97f3253522..004aa26b7c 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -540,13 +540,13 @@ verify_heapam(PG_FUNCTION_ARGS)
 										   (unsigned) MAXALIGN(SizeofHeapTupleHeader)));
 				continue;
 			}
-			if (ctx.lp_off + ctx.lp_len > BLCKSZ)
+			if (ctx.lp_off + ctx.lp_len > cluster_block_size)
 			{
 				report_corruption(&ctx,
 								  psprintf("line pointer to page offset %u with length %u ends beyond maximum page offset %u",
 										   ctx.lp_off,
 										   ctx.lp_len,
-										   (unsigned) BLCKSZ));
+										   (unsigned) cluster_block_size));
 				continue;
 			}
 
@@ -1460,7 +1460,7 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
 				  uint32 extsize)
 {
 	int32		chunk_seq;
-	int32		last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
+	int32		last_chunk_seq = (extsize - 1) / cluster_toast_max_chunk_size;
 	Pointer		chunk;
 	bool		isnull;
 	int32		chunksize;
@@ -1530,8 +1530,8 @@ check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx,
 		return;
 	}
 
-	expected_size = chunk_seq < last_chunk_seq ? TOAST_MAX_CHUNK_SIZE
-		: extsize - (last_chunk_seq * TOAST_MAX_CHUNK_SIZE);
+	expected_size = chunk_seq < last_chunk_seq ? cluster_toast_max_chunk_size
+		: extsize - (last_chunk_seq * cluster_toast_max_chunk_size);
 
 	if (chunksize != expected_size)
 		report_toast_corruption(ctx, ta,
@@ -1773,7 +1773,7 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
 	int32		last_chunk_seq;
 
 	extsize = VARATT_EXTERNAL_GET_EXTSIZE(ta->toast_pointer);
-	last_chunk_seq = (extsize - 1) / TOAST_MAX_CHUNK_SIZE;
+	last_chunk_seq = (extsize - 1) / cluster_toast_max_chunk_size;
 
 	/*
 	 * Setup a scan key to find chunks in toast table with matching va_valueid
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index dbb83d80f8..28465e0934 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -2962,7 +2962,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
 	BTPageOpaque opaque;
 	OffsetNumber maxoffset;
 
-	page = palloc(BLCKSZ);
+	page = palloc(cluster_block_size);
 
 	/*
 	 * We copy the page into local storage to avoid holding pin on the buffer
@@ -2979,7 +2979,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
 	_bt_checkpage(state->rel, buffer);
 
 	/* Only use copy of page in palloc()'d memory */
-	memcpy(page, BufferGetPage(buffer), BLCKSZ);
+	memcpy(page, BufferGetPage(buffer), cluster_block_size);
 	UnlockReleaseBuffer(buffer);
 
 	opaque = BTPageGetOpaque(page);
@@ -3163,7 +3163,7 @@ PageGetItemIdCareful(BtreeCheckState *state, BlockNumber block, Page page,
 	ItemId		itemid = PageGetItemId(page, offset);
 
 	if (ItemIdGetOffset(itemid) + ItemIdGetLength(itemid) >
-		BLCKSZ - MAXALIGN(sizeof(BTPageOpaqueData)))
+		cluster_block_size - MAXALIGN(sizeof(BTPageOpaqueData)))
 		ereport(ERROR,
 				(errcode(ERRCODE_INDEX_CORRUPTED),
 				 errmsg("line pointer points past end of tuple space in index \"%s\"",
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index b90145148d..32a339469a 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -52,7 +52,7 @@ flushCachedPage(Relation index, BloomBuildState *buildstate)
 
 	state = GenericXLogStart(index);
 	page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
-	memcpy(page, buildstate->data.data, BLCKSZ);
+	memcpy(page, buildstate->data.data, cluster_block_size);
 	GenericXLogFinish(state);
 	UnlockReleaseBuffer(buffer);
 }
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 330811ec60..d08fe1de26 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -112,7 +112,7 @@ typedef struct BloomOptions
  */
 typedef BlockNumber FreeBlockNumberArray[
 										 MAXALIGN_DOWN(
-													   BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
+													   cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
 													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
 													   ) / sizeof(BlockNumber)
 ];
@@ -150,7 +150,7 @@ typedef struct BloomState
 } BloomState;
 
 #define BloomPageGetFreeSpace(state, page) \
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+	(cluster_block_size - MAXALIGN(SizeOfPageHeaderData) \
 		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
 		- MAXALIGN(sizeof(BloomPageOpaqueData)))
 
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index f23fbb1d9e..9d79319e90 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -400,7 +400,7 @@ BloomInitPage(Page page, uint16 flags)
 {
 	BloomPageOpaque opaque;
 
-	PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
+	PageInit(page, cluster_block_size, sizeof(BloomPageOpaqueData));
 
 	opaque = BloomPageGetOpaque(page);
 	opaque->flags = flags;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2189be8a3c..c265b6e309 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -840,7 +840,7 @@ fileAnalyzeForeignTable(Relation relation,
 	 * Convert size to pages.  Must return at least 1 so that we can tell
 	 * later on that pg_class.relpages is not default.
 	 */
-	*totalpages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
+	*totalpages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
 	if (*totalpages < 1)
 		*totalpages = 1;
 
@@ -1010,12 +1010,12 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel,
 	 * back to the default if using a program as the input.
 	 */
 	if (fdw_private->is_program || stat(fdw_private->filename, &stat_buf) < 0)
-		stat_buf.st_size = 10 * BLCKSZ;
+		stat_buf.st_size = 10 * cluster_block_size;
 
 	/*
 	 * Convert size to pages for use in I/O cost estimate later.
 	 */
-	pages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
+	pages = (stat_buf.st_size + (cluster_block_size - 1)) / cluster_block_size;
 	if (pages < 1)
 		pages = 1;
 	fdw_private->pages = pages;
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 9cdc8e182b..77b00ef2b9 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -116,7 +116,7 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
 
 	stat->blkno = blkno;
 
-	stat->max_avail = BLCKSZ - (BLCKSZ - phdr->pd_special + SizeOfPageHeaderData);
+	stat->max_avail = cluster_block_size - (cluster_block_size - phdr->pd_special + SizeOfPageHeaderData);
 
 	stat->dead_items = stat->live_items = 0;
 
@@ -663,8 +663,8 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 
 		uargs = palloc(sizeof(ua_page_items));
 
-		uargs->page = palloc(BLCKSZ);
-		memcpy(uargs->page, BufferGetPage(buffer), BLCKSZ);
+		uargs->page = palloc(cluster_block_size);
+		memcpy(uargs->page, BufferGetPage(buffer), cluster_block_size);
 
 		UnlockReleaseBuffer(buffer);
 		relation_close(rel, AccessShareLock);
diff --git a/contrib/pageinspect/expected/checksum.out b/contrib/pageinspect/expected/checksum.out
index a85388e158..236df5be64 100644
--- a/contrib/pageinspect/expected/checksum.out
+++ b/contrib/pageinspect/expected/checksum.out
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	cluster_block_size	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pageinspect/expected/checksum_1.out b/contrib/pageinspect/expected/checksum_1.out
index 6fb1b1b04d..ce7a30f3c1 100644
--- a/contrib/pageinspect/expected/checksum_1.out
+++ b/contrib/pageinspect/expected/checksum_1.out
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	cluster_block_size	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index b25a63cbd6..2527501078 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -179,8 +179,8 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 						blkno, RelationGetRelationName(rel))));
 
 	/* Initialize buffer to copy to */
-	raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
-	SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
+	raw_page = (bytea *) palloc(cluster_block_size + VARHDRSZ);
+	SET_VARSIZE(raw_page, cluster_block_size + VARHDRSZ);
 	raw_page_data = VARDATA(raw_page);
 
 	/* Take a verbatim copy of the page */
@@ -188,7 +188,7 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 	buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
 	LockBuffer(buf, BUFFER_LOCK_SHARE);
 
-	memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
+	memcpy(raw_page_data, BufferGetPage(buf), cluster_block_size);
 
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	ReleaseBuffer(buf);
@@ -219,12 +219,12 @@ get_page_from_raw(bytea *raw_page)
 
 	raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
 
-	if (raw_page_size != BLCKSZ)
+	if (raw_page_size != cluster_block_size)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("invalid page size"),
 				 errdetail("Expected %d bytes, got %d.",
-						   BLCKSZ, raw_page_size)));
+						   cluster_block_size, raw_page_size)));
 
 	page = palloc(raw_page_size);
 
diff --git a/contrib/pageinspect/sql/checksum.sql b/contrib/pageinspect/sql/checksum.sql
index b877db0611..2afe04d0d2 100644
--- a/contrib/pageinspect/sql/checksum.sql
+++ b/contrib/pageinspect/sql/checksum.sql
@@ -6,7 +6,7 @@
 -- on the configured block size.  This test has several different expected
 -- results files to handle the following possibilities:
 --
---	BLCKSZ	end	file
+--	cluster_block_size	end	file
 --	8K	LE	checksum.out
 --	8K	BE	checksum_1.out
 --
diff --git a/contrib/pg_walinspect/pg_walinspect.c b/contrib/pg_walinspect/pg_walinspect.c
index 796a74f322..318c55abac 100644
--- a/contrib/pg_walinspect/pg_walinspect.c
+++ b/contrib/pg_walinspect/pg_walinspect.c
@@ -386,9 +386,9 @@ GetWALBlockInfo(FunctionCallInfo fcinfo, XLogReaderState *record,
 						(errcode(ERRCODE_INTERNAL_ERROR),
 						 errmsg_internal("%s", record->errormsg_buf)));
 
-			block_fpi_data = (bytea *) palloc(BLCKSZ + VARHDRSZ);
-			SET_VARSIZE(block_fpi_data, BLCKSZ + VARHDRSZ);
-			memcpy(VARDATA(block_fpi_data), page, BLCKSZ);
+			block_fpi_data = (bytea *) palloc(cluster_block_size + VARHDRSZ);
+			SET_VARSIZE(block_fpi_data, cluster_block_size + VARHDRSZ);
+			memcpy(VARDATA(block_fpi_data), page, cluster_block_size);
 			values[i++] = PointerGetDatum(block_fpi_data);
 		}
 		else
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index f601dc6121..0ce0b4f9a9 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -94,7 +94,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		if (VM_ALL_VISIBLE(rel, blkno, &vmbuffer))
 		{
 			freespace = GetRecordedFreeSpace(rel, blkno);
-			stat->tuple_len += BLCKSZ - freespace;
+			stat->tuple_len += cluster_block_size - freespace;
 			stat->free_space += freespace;
 			continue;
 		}
@@ -113,7 +113,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		if (!PageIsNew(page))
 			stat->free_space += PageGetHeapFreeSpace(page);
 		else
-			stat->free_space += BLCKSZ - SizeOfPageHeaderData;
+			stat->free_space += cluster_block_size - SizeOfPageHeaderData;
 
 		/* We may count the page as scanned even if it's new/empty */
 		scanned++;
@@ -182,7 +182,7 @@ statapprox_heap(Relation rel, output_type *stat)
 		UnlockReleaseBuffer(buf);
 	}
 
-	stat->table_len = (uint64) nblocks * BLCKSZ;
+	stat->table_len = (uint64) nblocks * cluster_block_size;
 
 	/*
 	 * We don't know how many tuples are in the pages we didn't scan, so
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index d69ac1c93d..9391bd540b 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -297,7 +297,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 		{
 			int			max_avail;
 
-			max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
+			max_avail = cluster_block_size - (cluster_block_size - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
 			indexStat.max_avail += max_avail;
 			indexStat.free_space += PageGetFreeSpace(page);
 
@@ -342,7 +342,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 								indexStat.leaf_pages +
 								indexStat.internal_pages +
 								indexStat.deleted_pages +
-								indexStat.empty_pages) * BLCKSZ);
+								indexStat.empty_pages) * cluster_block_size);
 		values[j++] = psprintf("%u", indexStat.root_blkno);
 		values[j++] = psprintf(INT64_FORMAT, indexStat.internal_pages);
 		values[j++] = psprintf(INT64_FORMAT, indexStat.leaf_pages);
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 93b7834b77..6a1c6bc6aa 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -386,7 +386,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
 	table_endscan(scan);
 	relation_close(rel, AccessShareLock);
 
-	stat.table_len = (uint64) nblocks * BLCKSZ;
+	stat.table_len = (uint64) nblocks * cluster_block_size;
 
 	return build_pgstattuple_type(&stat, fcinfo);
 }
@@ -409,7 +409,7 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 	if (PageIsNew(page))
 	{
 		/* fully empty page */
-		stat->free_space += BLCKSZ;
+		stat->free_space += cluster_block_size;
 	}
 	else
 	{
@@ -419,7 +419,7 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 		if (P_IGNORE(opaque))
 		{
 			/* deleted or half-dead page */
-			stat->free_space += BLCKSZ;
+			stat->free_space += cluster_block_size;
 		}
 		else if (P_ISLEAF(opaque))
 		{
@@ -456,7 +456,7 @@ pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
 		switch (opaque->hasho_flag & LH_PAGE_TYPE)
 		{
 			case LH_UNUSED_PAGE:
-				stat->free_space += BLCKSZ;
+				stat->free_space += cluster_block_size;
 				break;
 			case LH_BUCKET_PAGE:
 			case LH_OVERFLOW_PAGE:
@@ -531,7 +531,7 @@ pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn,
 		/* Quit if we've scanned the whole relation */
 		if (blkno >= nblocks)
 		{
-			stat.table_len = (uint64) nblocks * BLCKSZ;
+			stat.table_len = (uint64) nblocks * cluster_block_size;
 
 			break;
 		}
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..22cdddba67 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -2364,7 +2364,7 @@ deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
 
 	appendStringInfoString(buf, "SELECT pg_catalog.pg_relation_size(");
 	deparseStringLiteral(buf, relname.data);
-	appendStringInfo(buf, "::pg_catalog.regclass) / %d", BLCKSZ);
+	appendStringInfo(buf, "::pg_catalog.regclass) / %d", cluster_block_size);
 }
 
 /*
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..6c55044f64 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -755,7 +755,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 		{
 			baserel->pages = 10;
 			baserel->tuples =
-				(10 * BLCKSZ) / (baserel->reltarget->width +
+				(10 * cluster_block_size) / (baserel->reltarget->width +
 								 MAXALIGN(SizeofHeapTupleHeader));
 		}
 
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index e09adb45e4..9a62b7c0b4 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2023,7 +2023,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </para>
       <para>
        Size of the on-disk representation of this table in pages (of size
-       <symbol>BLCKSZ</symbol>).  This is only an estimate used by the
+       <symbol>cluster_block_size</symbol>).  This is only an estimate used by the
        planner.  It is updated by <link linkend="sql-vacuum"><command>VACUUM</command></link>,
        <link linkend="sql-analyze"><command>ANALYZE</command></link>, and a few DDL commands such as
        <link linkend="sql-createindex"><command>CREATE INDEX</command></link>.
@@ -4884,8 +4884,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
    assigned when it is created.  Each large object is broken into
    segments or <quote>pages</quote> small enough to be conveniently stored as rows
    in <structname>pg_largeobject</structname>.
-   The amount of data per page is defined to be <symbol>LOBLKSIZE</symbol> (which is currently
-   <literal>BLCKSZ/4</literal>, or typically 2 kB).
+   The amount of data per page is defined to be <symbol>cluster_loblksize</symbol> (which is currently
+   <literal>cluster_block_size/4</literal>, or typically 2 kB).
   </para>
 
   <para>
@@ -4939,7 +4939,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </para>
       <para>
        Actual data stored in the large object.
-       This will never be more than <symbol>LOBLKSIZE</symbol> bytes and might be less.
+       This will never be more than <symbol>cluster_loblksize</symbol> bytes and might be less.
       </para></entry>
      </row>
     </tbody>
@@ -4949,9 +4949,9 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
   <para>
    Each row of <structname>pg_largeobject</structname> holds data
    for one page of a large object, beginning at
-   byte offset (<literal>pageno * LOBLKSIZE</literal>) within the object.  The implementation
+   byte offset (<literal>pageno * cluster_loblksize</literal>) within the object.  The implementation
    allows sparse storage: pages might be missing, and might be shorter than
-   <literal>LOBLKSIZE</literal> bytes even if they are not the last page of the object.
+   <literal>cluster_loblksize</literal> bytes even if they are not the last page of the object.
    Missing regions within a large object read as zeroes.
   </para>
 
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 924309af26..2f7400c83a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1636,8 +1636,8 @@ include_dir 'conf.d'
         settings significantly higher than the minimum are usually needed
         for good performance.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
-        (Non-default values of <symbol>BLCKSZ</symbol> change the minimum
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
+        (Non-default values of <symbol>cluster_block_size</symbol> change the minimum
         value.)
         This parameter can only be set at server start.
        </para>
@@ -1767,9 +1767,9 @@ include_dir 'conf.d'
         each database session.  These are session-local buffers used only
         for access to temporary tables.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is eight megabytes (<literal>8MB</literal>).
-        (If <symbol>BLCKSZ</symbol> is not 8kB, the default value scales
+        (If <symbol>cluster_block_size</symbol> is not 8kB, the default value scales
         proportionally to it.)
         This setting can be changed within individual
         sessions, but only before the first use of temporary tables
@@ -1784,7 +1784,7 @@ include_dir 'conf.d'
         buffers is only a buffer descriptor, or about 64 bytes, per
         increment in <varname>temp_buffers</varname>.  However if a buffer is
         actually used an additional 8192 bytes will be consumed for it
-        (or in general, <symbol>BLCKSZ</symbol> bytes).
+        (or in general, <symbol>cluster_block_size</symbol> bytes).
        </para>
       </listitem>
      </varlistentry>
@@ -2441,11 +2441,11 @@ include_dir 'conf.d'
          cache, where performance might degrade.  This setting may have no
          effect on some platforms.
          If this value is specified without units, it is taken as blocks,
-         that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+         that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
          The valid range is between
          <literal>0</literal>, which disables forced writeback, and
          <literal>2MB</literal>.  The default is <literal>512kB</literal> on Linux,
-         <literal>0</literal> elsewhere.  (If <symbol>BLCKSZ</symbol> is not 8kB,
+         <literal>0</literal> elsewhere.  (If <symbol>cluster_block_size</symbol> is not 8kB,
          the default and maximum values scale proportionally to it.)
          This parameter can only be set in the <filename>postgresql.conf</filename>
          file or on the server command line.
@@ -2487,11 +2487,11 @@ include_dir 'conf.d'
          than the OS's page cache, where performance might degrade.  This
          setting may have no effect on some platforms.
          If this value is specified without units, it is taken as blocks,
-         that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+         that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
          The valid range is
          between <literal>0</literal>, which disables forced writeback,
          and <literal>2MB</literal>.  The default is <literal>0</literal>, i.e., no
-         forced writeback.  (If <symbol>BLCKSZ</symbol> is not 8kB,
+         forced writeback.  (If <symbol>cluster_block_size</symbol> is not 8kB,
          the maximum value scales proportionally to it.)
         </para>
        </listitem>
@@ -3435,11 +3435,11 @@ include_dir 'conf.d'
         than the OS's page cache, where performance might degrade.  This
         setting may have no effect on some platforms.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The valid range is
         between <literal>0</literal>, which disables forced writeback,
         and <literal>2MB</literal>.  The default is <literal>256kB</literal> on
-        Linux, <literal>0</literal> elsewhere.  (If <symbol>BLCKSZ</symbol> is not
+        Linux, <literal>0</literal> elsewhere.  (If <symbol>cluster_block_size</symbol> is not
         8kB, the default and maximum values scale proportionally to it.)
         This parameter can only be set in the <filename>postgresql.conf</filename>
         file or on the server command line.
@@ -5551,7 +5551,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         table, but when indexes are used the amount of table data
         scanned will normally be less.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is 8 megabytes (<literal>8MB</literal>).
        </para>
       </listitem>
@@ -5573,7 +5573,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         particular index can participate in a parallel vacuum.  See
         <xref linkend="sql-vacuum"/>.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is 512 kilobytes (<literal>512kB</literal>).
        </para>
       </listitem>
@@ -5605,9 +5605,9 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         purposes.  The system also does not assume data remains in
         the disk cache between queries.
         If this value is specified without units, it is taken as blocks,
-        that is <symbol>BLCKSZ</symbol> bytes, typically 8kB.
+        that is <symbol>cluster_block_size</symbol> bytes, typically 8kB.
         The default is 4 gigabytes (<literal>4GB</literal>).
-        (If <symbol>BLCKSZ</symbol> is not 8kB, the default value scales
+        (If <symbol>cluster_block_size</symbol> is not 8kB, the default value scales
         proportionally to it.)
        </para>
       </listitem>
@@ -10579,7 +10579,7 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       <listitem>
        <para>
         Reports the size of a disk block.  It is determined by the value
-        of <literal>BLCKSZ</literal> when building the server. The default
+        of <literal>cluster_block_size</literal> when building the server. The default
         value is 8192 bytes.  The meaning of some configuration
         variables (such as <xref linkend="guc-shared-buffers"/>) is
         influenced by <varname>block_size</varname>. See <xref
diff --git a/doc/src/sgml/limits.sgml b/doc/src/sgml/limits.sgml
index d5b2b627dd..bbea9bc733 100644
--- a/doc/src/sgml/limits.sgml
+++ b/doc/src/sgml/limits.sgml
@@ -46,7 +46,7 @@
     <row>
      <entry>relation size</entry>
      <entry>32 TB</entry>
-     <entry>with the default <symbol>BLCKSZ</symbol> of 8192 bytes</entry>
+     <entry>with the default <symbol>cluster_block_size</symbol> of 8192 bytes</entry>
     </row>
 
     <row>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9c4930e9ae..92fc70869c 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -2704,7 +2704,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        <para>
         Relation data reads, writes, and extends are done in
         <varname>block_size</varname> units, derived from the build-time
-        parameter <symbol>BLCKSZ</symbol>, which is <literal>8192</literal> by
+        parameter <symbol>cluster_block_size</symbol>, which is <literal>8192</literal> by
         default.
        </para>
       </entry>
diff --git a/doc/src/sgml/pgfreespacemap.sgml b/doc/src/sgml/pgfreespacemap.sgml
index 829ad60f32..0da94e5f63 100644
--- a/doc/src/sgml/pgfreespacemap.sgml
+++ b/doc/src/sgml/pgfreespacemap.sgml
@@ -60,7 +60,7 @@
 
   <para>
    The values stored in the free space map are not exact. They're rounded
-   to precision of 1/256th of <symbol>BLCKSZ</symbol> (32 bytes with default <symbol>BLCKSZ</symbol>), and
+   to precision of 1/256th of <symbol>cluster_block_size</symbol> (32 bytes with default <symbol>cluster_block_size</symbol>), and
    they're not kept fully up-to-date as tuples are inserted and updated.
   </para>
 
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 148fb1b49d..4cf7460b0a 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -415,7 +415,7 @@ described in more detail below.
 
 <para>
 Out-of-line values are divided (after compression if used) into chunks of at
-most <symbol>TOAST_MAX_CHUNK_SIZE</symbol> bytes (by default this value is chosen
+most <symbol>cluster_toast_max_chunk_size</symbol> bytes (by default this value is chosen
 so that four chunk rows will fit on a page, making it about 2000 bytes).
 Each chunk is stored as a separate row in the <acronym>TOAST</acronym> table
 belonging to the owning table.  Every
@@ -438,10 +438,10 @@ bytes regardless of the actual size of the represented value.
 <para>
 The <acronym>TOAST</acronym> management code is triggered only
 when a row value to be stored in a table is wider than
-<symbol>TOAST_TUPLE_THRESHOLD</symbol> bytes (normally 2 kB).
+<symbol>toast_tuple_threshold</symbol> bytes (normally 2 kB).
 The <acronym>TOAST</acronym> code will compress and/or move
 field values out-of-line until the row value is shorter than
-<symbol>TOAST_TUPLE_TARGET</symbol> bytes (also normally 2 kB, adjustable)
+<symbol>cluster_toast_tuple_target</symbol> bytes (also normally 2 kB, adjustable)
 or no more gains can be had.  During an UPDATE
 operation, values of unchanged fields are normally preserved as-is; so an
 UPDATE of a row with out-of-line values incurs no <acronym>TOAST</acronym> costs if
@@ -496,7 +496,7 @@ with <link linkend="sql-altertable"><command>ALTER TABLE ... SET STORAGE</comman
 </para>
 
 <para>
-<symbol>TOAST_TUPLE_TARGET</symbol> can be adjusted for each table using
+<symbol>cluster_toast_tuple_target</symbol> can be adjusted for each table using
 <link linkend="sql-altertable"><command>ALTER TABLE ... SET (toast_tuple_target = N)</command></link>
 </para>
 
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index 56534cf29c..b3b0633d73 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -212,7 +212,7 @@ typedef struct BloomOptions
  * be larger because the index has multiple columns.
  */
 #define BloomMaxFilterSize \
-	MAXALIGN_DOWN(BLCKSZ - \
+	MAXALIGN_DOWN(cluster_block_size - \
 				  (MAXALIGN(SizeOfPageHeaderData + \
 							sizeof(ItemIdData)) + \
 				   MAXALIGN(sizeof(BrinSpecialSpace)) + \
diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c
index b578d25954..11103ff25d 100644
--- a/src/backend/access/brin/brin_pageops.c
+++ b/src/backend/access/brin/brin_pageops.c
@@ -27,7 +27,7 @@
  * a single item per page, unlike other index AMs.
  */
 #define BrinMaxItemSize \
-	MAXALIGN_DOWN(BLCKSZ - \
+	MAXALIGN_DOWN(cluster_block_size - \
 				  (MAXALIGN(SizeOfPageHeaderData + \
 							sizeof(ItemIdData)) + \
 				   MAXALIGN(sizeof(BrinSpecialSpace))))
@@ -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, cluster_block_size, sizeof(BrinSpecialSpace));
 
 	BrinPageType(page) = type;
 }
diff --git a/src/backend/access/common/bufmask.c b/src/backend/access/common/bufmask.c
index 5e392dab1e..4e6e9eb7e1 100644
--- a/src/backend/access/common/bufmask.c
+++ b/src/backend/access/common/bufmask.c
@@ -76,7 +76,7 @@ mask_unused_space(Page page)
 
 	/* Sanity check */
 	if (pd_lower > pd_upper || pd_special < pd_upper ||
-		pd_lower < SizeOfPageHeaderData || pd_special > BLCKSZ)
+		pd_lower < SizeOfPageHeaderData || pd_special > cluster_block_size)
 	{
 		elog(ERROR, "invalid page pd_lower %u pd_upper %u pd_special %u",
 			 pd_lower, pd_upper, pd_special);
@@ -120,7 +120,7 @@ mask_page_content(Page page)
 {
 	/* Mask Page Content */
 	memset(page + SizeOfPageHeaderData, MASK_MARKER,
-		   BLCKSZ - SizeOfPageHeaderData);
+		   cluster_block_size - SizeOfPageHeaderData);
 
 	/* Mask pd_lower and pd_upper */
 	memset(&((PageHeader) page)->pd_lower, MASK_MARKER,
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 469de9bb49..52d4ee6834 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -328,7 +328,7 @@ static relopt_int intRelOpts[] =
 			RELOPT_KIND_HEAP,
 			ShareUpdateExclusiveLock
 		},
-		TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
+		cluster_toast_tuple_target, 128, toast_tuple_target_main
 	},
 	{
 		{
diff --git a/src/backend/access/common/syncscan.c b/src/backend/access/common/syncscan.c
index 2bc6828883..3aa54f28d4 100644
--- a/src/backend/access/common/syncscan.c
+++ b/src/backend/access/common/syncscan.c
@@ -80,7 +80,7 @@ bool		trace_syncscan = false;
  * the buffer cache anyway, and on the other hand the page is most likely
  * still in the OS cache.
  */
-#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / BLCKSZ)
+#define SYNC_SCAN_REPORT_INTERVAL (128 * 1024 / cluster_block_size)
 
 
 /*
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 371515395a..63edaa8192 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -133,7 +133,7 @@ toast_save_datum(Relation rel, Datum value,
 	{
 		struct varlena hdr;
 		/* this is to make the union big enough for a chunk: */
-		char		data[TOAST_MAX_CHUNK_SIZE + VARHDRSZ];
+		char		data[cluster_toast_max_chunk_size + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			chunk_data;
@@ -311,7 +311,7 @@ toast_save_datum(Relation rel, Datum value,
 		/*
 		 * Calculate the size of this chunk
 		 */
-		chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);
+		chunk_size = Min(cluster_toast_max_chunk_size, data_todo);
 
 		/*
 		 * Build a tuple and store it
diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c
index 06e97a7fbb..4aa30b92f6 100644
--- a/src/backend/access/gin/ginbtree.c
+++ b/src/backend/access/gin/ginbtree.c
@@ -508,7 +508,7 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
 			 * critical section yet.)
 			 */
 			newrootpg = PageGetTempPage(newrpage);
-			GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), BLCKSZ);
+			GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), cluster_block_size);
 
 			btree->fillRoot(btree, newrootpg,
 							BufferGetBlockNumber(lbuffer), newlpage,
@@ -565,15 +565,15 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
 		{
 			/* Splitting the root, three pages to update */
 			MarkBufferDirty(lbuffer);
-			memcpy(page, newrootpg, BLCKSZ);
-			memcpy(BufferGetPage(lbuffer), newlpage, BLCKSZ);
-			memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
+			memcpy(page, newrootpg, cluster_block_size);
+			memcpy(BufferGetPage(lbuffer), newlpage, cluster_block_size);
+			memcpy(BufferGetPage(rbuffer), newrpage, cluster_block_size);
 		}
 		else
 		{
 			/* Normal split, only two pages to update */
-			memcpy(page, newlpage, BLCKSZ);
-			memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
+			memcpy(page, newlpage, cluster_block_size);
+			memcpy(BufferGetPage(rbuffer), newrpage, cluster_block_size);
 		}
 
 		/* We also clear childbuf's INCOMPLETE_SPLIT flag, if passed */
diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
index 344e1c5e6b..19eb645794 100644
--- a/src/backend/access/gin/gindatapage.c
+++ b/src/backend/access/gin/gindatapage.c
@@ -655,7 +655,7 @@ dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
 						break;
 					if (append)
 					{
-						if ((leaf->lsize - segsize) < (BLCKSZ * 3) / 4)
+						if ((leaf->lsize - segsize) < (cluster_block_size * 3) / 4)
 							break;
 					}
 
@@ -681,8 +681,8 @@ dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
 		/*
 		 * Now allocate a couple of temporary page images, and fill them.
 		 */
-		*newlpage = palloc(BLCKSZ);
-		*newrpage = palloc(BLCKSZ);
+		*newlpage = palloc(cluster_block_size);
+		*newrpage = palloc(cluster_block_size);
 
 		dataPlaceToPageLeafSplit(leaf, lbound, rbound,
 								 *newlpage, *newrpage);
@@ -887,7 +887,7 @@ computeLeafRecompressWALData(disassembledLeaf *leaf)
 
 	walbufbegin =
 		palloc(sizeof(ginxlogRecompressDataLeaf) +
-			   BLCKSZ +			/* max size needed to hold the segment data */
+			   cluster_block_size +			/* max size needed to hold the segment data */
 			   nmodified * 2	/* (segno + action) per action */
 		);
 	walbufend = walbufbegin;
@@ -1041,8 +1041,8 @@ dataPlaceToPageLeafSplit(disassembledLeaf *leaf,
 	leafSegmentInfo *seginfo;
 
 	/* Initialize temporary pages to hold the new left and right pages */
-	GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
-	GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
+	GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, cluster_block_size);
+	GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, cluster_block_size);
 
 	/*
 	 * Copy the segments that go to the left page.
@@ -1259,7 +1259,7 @@ dataSplitPageInternal(GinBtree btree, Buffer origbuf,
 	Page		lpage;
 	Page		rpage;
 	OffsetNumber separator;
-	PostingItem allitems[(BLCKSZ / sizeof(PostingItem)) + 1];
+	PostingItem allitems[(cluster_block_size / sizeof(PostingItem)) + 1];
 
 	lpage = PageGetTempPage(oldpage);
 	rpage = PageGetTempPage(oldpage);
@@ -1779,8 +1779,8 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
 	bool		is_build = (buildStats != NULL);
 
 	/* Construct the new root page in memory first. */
-	tmppage = (Page) palloc(BLCKSZ);
-	GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
+	tmppage = (Page) palloc(cluster_block_size);
+	GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, cluster_block_size);
 	GinPageGetOpaque(tmppage)->rightlink = InvalidBlockNumber;
 
 	/*
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index eb6c554831..9a816e5bfe 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -38,8 +38,8 @@
 /* GUC parameter */
 int			gin_pending_list_limit = 0;
 
-#define GIN_PAGE_FREESIZE \
-	( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
+#define gin_page_freesize \
+	( cluster_block_size - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
 
 typedef struct KeyArray
 {
@@ -92,7 +92,7 @@ writeListPage(Relation index, Buffer buffer,
 		off++;
 	}
 
-	Assert(size <= BLCKSZ);		/* else we overran workspace */
+	Assert(size <= cluster_block_size);		/* else we overran workspace */
 
 	GinPageGetOpaque(page)->rightlink = rightlink;
 
@@ -455,7 +455,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 	 * ginInsertCleanup() should not be called inside our CRIT_SECTION.
 	 */
 	cleanupSize = GinGetPendingListCleanupSize(index);
-	if (metadata->nPendingPages * GIN_PAGE_FREESIZE > cleanupSize * 1024L)
+	if (metadata->nPendingPages * gin_page_freesize > cleanupSize * 1024L)
 		needCleanup = true;
 
 	UnlockReleaseBuffer(metabuffer);
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 88d8d0c04b..e104519085 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1628,9 +1628,9 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
 	 */
 	for (;;)
 	{
-		Datum		datum[BLCKSZ / sizeof(IndexTupleData)];
-		GinNullCategory category[BLCKSZ / sizeof(IndexTupleData)];
-		bool		datumExtracted[BLCKSZ / sizeof(IndexTupleData)];
+		Datum		datum[cluster_block_size / sizeof(IndexTupleData)];
+		GinNullCategory category[cluster_block_size / sizeof(IndexTupleData)];
+		bool		datumExtracted[cluster_block_size / sizeof(IndexTupleData)];
 
 		Assert(pos->lastOffset > pos->firstOffset);
 		memset(datumExtracted + pos->firstOffset - 1, 0,
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index e5d310d836..628d0f3a40 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -569,7 +569,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	BlockNumber blkno = GIN_ROOT_BLKNO;
 	GinVacuumState gvs;
 	Buffer		buffer;
-	BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
+	BlockNumber rootOfPostingTree[cluster_block_size / (sizeof(IndexTupleData) + sizeof(ItemId))];
 	uint32		nRoot;
 
 	gvs.tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index f7c84beef8..9adaf5e80c 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -146,7 +146,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
 			GinPostingList *plist;
 
 			plist = ginCompressPostingList(uncompressed, nuncompressed,
-										   BLCKSZ, &npacked);
+										   cluster_block_size, &npacked);
 			totalsize = SizeOfGinPostingList(plist);
 
 			Assert(npacked == nuncompressed);
@@ -236,7 +236,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
 			Assert(nnewitems == nolditems + nitems);
 
 			newseg = ginCompressPostingList(newitems, nnewitems,
-											BLCKSZ, &npacked);
+											cluster_block_size, &npacked);
 			Assert(npacked == nnewitems);
 
 			newsegsize = SizeOfGinPostingList(newseg);
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index 5e0c1447f9..8f4bf77616 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -255,7 +255,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * Calculate target amount of free space to leave on pages.
 	 */
 	fillfactor = options ? options->fillfactor : GIST_DEFAULT_FILLFACTOR;
-	buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
+	buildstate.freespace = cluster_block_size * (100 - fillfactor) / 100;
 
 	/*
 	 * Build the index using the chosen strategy.
@@ -415,7 +415,7 @@ gist_indexsortbuild(GISTBuildState *state)
 	 * Write an empty page as a placeholder for the root page. It will be
 	 * replaced with the real root page at the end.
 	 */
-	page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
+	page = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
 	smgrextend(RelationGetSmgr(state->indexrel), MAIN_FORKNUM, GIST_ROOT_BLKNO,
 			   page, true);
 	state->pages_allocated++;
@@ -510,7 +510,7 @@ gist_indexsortbuild_levelstate_add(GISTBuildState *state,
 
 		if (levelstate->pages[levelstate->current_page] == NULL)
 			levelstate->pages[levelstate->current_page] =
-				palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+				palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 
 		newPage = levelstate->pages[levelstate->current_page];
 		gistinitpage(newPage, old_page_flags);
@@ -580,7 +580,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
 
 		/* Create page and copy data */
 		data = (char *) (dist->list);
-		target = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
+		target = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, MCXT_ALLOC_ZERO);
 		gistinitpage(target, isleaf ? F_LEAF : 0);
 		for (int i = 0; i < dist->block.num; i++)
 		{
@@ -631,7 +631,7 @@ gist_indexsortbuild_levelstate_flush(GISTBuildState *state,
 		if (parent == NULL)
 		{
 			parent = palloc0(sizeof(GistSortedBuildLevelState));
-			parent->pages[0] = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+			parent->pages[0] = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 			parent->parent = NULL;
 			gistinitpage(parent->pages[0], 0);
 
@@ -702,7 +702,7 @@ gistInitBuffering(GISTBuildState *buildstate)
 	int			levelStep;
 
 	/* Calc space of index page which is available for index tuples */
-	pageFreeSpace = BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
+	pageFreeSpace = cluster_block_size - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
 		- sizeof(ItemIdData)
 		- buildstate->freespace;
 
@@ -799,7 +799,7 @@ gistInitBuffering(GISTBuildState *buildstate)
 			break;
 
 		/* each node in the lowest level of a subtree has one page in memory */
-		if (maxlowestlevelpages > ((double) maintenance_work_mem * 1024) / BLCKSZ)
+		if (maxlowestlevelpages > ((double) maintenance_work_mem * 1024) / cluster_block_size)
 			break;
 
 		/* Good, we can handle this levelStep. See if we can go one higher. */
@@ -858,7 +858,7 @@ calculatePagesPerBuffer(GISTBuildState *buildstate, int levelStep)
 	Size		pageFreeSpace;
 
 	/* Calc space of index page which is available for index tuples */
-	pageFreeSpace = BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
+	pageFreeSpace = cluster_block_size - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)
 		- sizeof(ItemIdData)
 		- buildstate->freespace;
 
diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c
index 1423b4b047..bdedd12ce2 100644
--- a/src/backend/access/gist/gistbuildbuffers.c
+++ b/src/backend/access/gist/gistbuildbuffers.c
@@ -187,11 +187,11 @@ gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb)
 	GISTNodeBufferPage *pageBuffer;
 
 	pageBuffer = (GISTNodeBufferPage *) MemoryContextAllocZero(gfbb->context,
-															   BLCKSZ);
+															   cluster_block_size);
 	pageBuffer->prev = InvalidBlockNumber;
 
 	/* Set page free space */
-	PAGE_FREE_SPACE(pageBuffer) = BLCKSZ - BUFFER_PAGE_DATA_OFFSET;
+	PAGE_FREE_SPACE(pageBuffer) = cluster_block_size - BUFFER_PAGE_DATA_OFFSET;
 	return pageBuffer;
 }
 
@@ -379,7 +379,7 @@ gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer,
 		 * the new page by storing its block number in the prev-link.
 		 */
 		PAGE_FREE_SPACE(nodeBuffer->pageBuffer) =
-			BLCKSZ - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
+			cluster_block_size - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
 		nodeBuffer->pageBuffer->prev = blkno;
 
 		/* We've just added one more page */
@@ -755,7 +755,7 @@ ReadTempFileBlock(BufFile *file, long blknum, void *ptr)
 {
 	if (BufFileSeekBlock(file, blknum) != 0)
 		elog(ERROR, "could not seek to block %ld in temporary file", blknum);
-	BufFileReadExact(file, ptr, BLCKSZ);
+	BufFileReadExact(file, ptr, cluster_block_size);
 }
 
 static void
@@ -763,5 +763,5 @@ WriteTempFileBlock(BufFile *file, long blknum, const void *ptr)
 {
 	if (BufFileSeekBlock(file, blknum) != 0)
 		elog(ERROR, "could not seek to block %ld in temporary file", blknum);
-	BufFileWrite(file, ptr, BLCKSZ);
+	BufFileWrite(file, ptr, cluster_block_size);
 }
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index b6bc8c2c56..ae9ab2931c 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, cluster_block_size, sizeof(GISTPageOpaqueData));
 
 	opaque = GistPageGetOpaque(page);
 	opaque->rightlink = InvalidBlockNumber;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index fc5d97f606..ea78dc81c2 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -152,7 +152,7 @@ hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	 * one page.  Also, "initial index size" accounting does not include the
 	 * metapage, nor the first bitmap page.
 	 */
-	sort_threshold = (maintenance_work_mem * 1024L) / BLCKSZ;
+	sort_threshold = (maintenance_work_mem * 1024L) / cluster_block_size;
 	if (index->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
 		sort_threshold = Min(sort_threshold, NBuffers);
 	else
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 0c6e79f1bd..2a77d13feb 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -1012,7 +1012,7 @@ _hash_alloc_buckets(Relation rel, BlockNumber firstblock, uint32 nblocks)
 	 * _hash_freeovflpage for similar usage.  We take care to make the special
 	 * space valid for the benefit of tools such as pageinspect.
 	 */
-	_hash_pageinit(page, BLCKSZ);
+	_hash_pageinit(page, cluster_block_size);
 
 	ovflopaque = HashPageGetOpaque(page);
 
diff --git a/src/backend/access/heap/README.HOT b/src/backend/access/heap/README.HOT
index 74e407f375..25995e7b3b 100644
--- a/src/backend/access/heap/README.HOT
+++ b/src/backend/access/heap/README.HOT
@@ -254,7 +254,7 @@ large enough to accept any extra maintenance burden for.
 The currently planned heuristic is to prune and defrag when first accessing
 a page that potentially has prunable tuples (as flagged by the pd_prune_xid
 page hint field) and that either has free space less than MAX(fillfactor
-target free space, BLCKSZ/10) *or* has recently had an UPDATE fail to
+target free space, cluster_block_size/10) *or* has recently had an UPDATE fail to
 find enough free space to store an updated tuple version.  (These rules
 are subject to change.)
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 88a123d38a..a5975efe3e 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2039,7 +2039,7 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
 		Assert(!HeapTupleHasExternal(tup));
 		return tup;
 	}
-	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
+	else if (HeapTupleHasExternal(tup) || tup->t_len > toast_tuple_threshold)
 		return heap_toast_insert_or_update(relation, tup, NULL, options);
 	else
 		return tup;
@@ -2053,7 +2053,7 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
 static int
 heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveFreeSpace)
 {
-	size_t		page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+	size_t		page_avail = cluster_block_size - SizeOfPageHeaderData - saveFreeSpace;
 	int			npages = 1;
 
 	for (int i = done; i < ntuples; i++)
@@ -2063,7 +2063,7 @@ heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveF
 		if (page_avail < tup_sz)
 		{
 			npages++;
-			page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace;
+			page_avail = cluster_block_size - SizeOfPageHeaderData - saveFreeSpace;
 		}
 		page_avail -= tup_sz;
 	}
@@ -2324,7 +2324,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
 				scratchptr += datalen;
 			}
 			totaldatalen = scratchptr - tupledata;
-			Assert((scratchptr - scratch.data) < BLCKSZ);
+			Assert((scratchptr - scratch.data) < cluster_block_size);
 
 			if (need_tuple_data)
 				xlrec->flags |= XLH_INSERT_CONTAINS_NEW_TUPLE;
@@ -3481,7 +3481,7 @@ l2:
 	else
 		need_toast = (HeapTupleHasExternal(&oldtup) ||
 					  HeapTupleHasExternal(newtup) ||
-					  newtup->t_len > TOAST_TUPLE_THRESHOLD);
+					  newtup->t_len > toast_tuple_threshold);
 
 	pagefree = PageGetHeapFreeSpace(page);
 
@@ -7981,7 +7981,7 @@ index_delete_sort(TM_IndexDeleteOp *delstate)
 	 * Shellsort gap sequence (taken from Sedgewick-Incerpi paper).
 	 *
 	 * This implementation is fast with array sizes up to ~4500.  This covers
-	 * all supported BLCKSZ values.
+	 * all supported cluster_block_size values.
 	 */
 	const int	gaps[9] = {1968, 861, 336, 112, 48, 21, 7, 3, 1};
 
@@ -9011,7 +9011,7 @@ heap_xlog_visible(XLogReaderState *record)
 
 		/* initialize the page if it was read as zeros */
 		if (PageIsNew(vmpage))
-			PageInit(vmpage, BLCKSZ, 0);
+			PageInit(vmpage, cluster_block_size, 0);
 
 		/* remove VISIBILITYMAP_XLOG_* */
 		vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS;
@@ -9316,7 +9316,7 @@ heap_xlog_insert(XLogReaderState *record)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
+	if (action == BLK_NEEDS_REDO && freespace < cluster_block_size / 5)
 		XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
 }
 
@@ -9463,7 +9463,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
+	if (action == BLK_NEEDS_REDO && freespace < cluster_block_size / 5)
 		XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
 }
 
@@ -9738,7 +9738,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
 	 * don't bother to update the FSM in that case, it doesn't need to be
 	 * totally accurate anyway.
 	 */
-	if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
+	if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < cluster_block_size / 5)
 		XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
 }
 
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 7c28dafb72..2e08383400 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -2026,7 +2026,7 @@ heapam_scan_get_blocks_done(HeapScanDesc hscan)
 /*
  * Check to see whether the table needs a TOAST table.  It does only if
  * (1) there are any toastable attributes, and (2) the maximum length
- * of a tuple could exceed TOAST_TUPLE_THRESHOLD.  (We don't want to
+ * of a tuple could exceed toast_tuple_threshold.  (We don't want to
  * create a toast table for something like "f1 varchar(20)".)
  */
 static bool
@@ -2071,7 +2071,7 @@ heapam_relation_needs_toast_table(Relation rel)
 	tuple_length = MAXALIGN(SizeofHeapTupleHeader +
 							BITMAPLEN(tupdesc->natts)) +
 		MAXALIGN(data_length);
-	return (tuple_length > TOAST_TUPLE_THRESHOLD);
+	return (tuple_length > toast_tuple_threshold);
 }
 
 /*
@@ -2091,8 +2091,8 @@ heapam_relation_toast_am(Relation rel)
 
 #define HEAP_OVERHEAD_BYTES_PER_TUPLE \
 	(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
-#define HEAP_USABLE_BYTES_PER_PAGE \
-	(BLCKSZ - SizeOfPageHeaderData)
+#define heap_usable_bytes_per_page \
+	(cluster_block_size - SizeOfPageHeaderData)
 
 static void
 heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
@@ -2102,7 +2102,7 @@ heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
 	table_block_relation_estimate_size(rel, attr_widths, pages,
 									   tuples, allvisfrac,
 									   HEAP_OVERHEAD_BYTES_PER_TUPLE,
-									   HEAP_USABLE_BYTES_PER_PAGE);
+									   heap_usable_bytes_per_page);
 }
 
 
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 52ecd45654..98b0a701e5 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -174,7 +174,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		hoff += BITMAPLEN(numAttrs);
 	hoff = MAXALIGN(hoff);
 	/* now convert to a limit on the tuple data size */
-	maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
+	maxDataLen = RelationGetToastTupleTarget(rel, cluster_toast_tuple_target) - hoff;
 
 	/*
 	 * Look for attributes with attstorage EXTENDED to compress.  Also find
@@ -255,7 +255,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 	 * increase the target tuple size, so that MAIN attributes aren't stored
 	 * externally unless really necessary.
 	 */
-	maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
+	maxDataLen = toast_tuple_target_main - hoff;
 
 	while (heap_compute_data_size(tupleDesc,
 								  toast_values, toast_isnull) > maxDataLen &&
@@ -634,7 +634,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 	SysScanDesc toastscan;
 	HeapTuple	ttup;
 	int32		expectedchunk;
-	int32		totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
+	int32		totalchunks = ((attrsize - 1) / cluster_toast_max_chunk_size) + 1;
 	int			startchunk;
 	int			endchunk;
 	int			num_indexes;
@@ -647,8 +647,8 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 									&toastidxs,
 									&num_indexes);
 
-	startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
-	endchunk = (sliceoffset + slicelength - 1) / TOAST_MAX_CHUNK_SIZE;
+	startchunk = sliceoffset / cluster_toast_max_chunk_size;
+	endchunk = (sliceoffset + slicelength - 1) / cluster_toast_max_chunk_size;
 	Assert(endchunk <= totalchunks);
 
 	/* Set up a scan key to fetch from the index. */
@@ -749,8 +749,8 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 									 curchunk,
 									 startchunk, endchunk, valueid,
 									 RelationGetRelationName(toastrel))));
-		expected_size = curchunk < totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
-			: attrsize - ((totalchunks - 1) * TOAST_MAX_CHUNK_SIZE);
+		expected_size = curchunk < totalchunks - 1 ? cluster_toast_max_chunk_size
+			: attrsize - ((totalchunks - 1) * cluster_toast_max_chunk_size);
 		if (chunksize != expected_size)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
@@ -765,12 +765,12 @@ heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize,
 		chcpystrt = 0;
 		chcpyend = chunksize - 1;
 		if (curchunk == startchunk)
-			chcpystrt = sliceoffset % TOAST_MAX_CHUNK_SIZE;
+			chcpystrt = sliceoffset % cluster_toast_max_chunk_size;
 		if (curchunk == endchunk)
-			chcpyend = (sliceoffset + slicelength - 1) % TOAST_MAX_CHUNK_SIZE;
+			chcpyend = (sliceoffset + slicelength - 1) % cluster_toast_max_chunk_size;
 
 		memcpy(VARDATA(result) +
-			   (curchunk * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
+			   (curchunk * cluster_toast_max_chunk_size - sliceoffset) + chcpystrt,
 			   chunkdata + chcpystrt,
 			   (chcpyend - chcpystrt) + 1);
 
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d5892a2db4..3942e80696 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -140,7 +140,7 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
 	 */
 	minfree = RelationGetTargetPageFreeSpace(relation,
 											 HEAP_DEFAULT_FILLFACTOR);
-	minfree = Max(minfree, BLCKSZ / 10);
+	minfree = Max(minfree, cluster_block_size / 10);
 
 	if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
 	{
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 424958912c..3402625d6d 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -255,7 +255,7 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm
 
 	state->rs_old_rel = old_heap;
 	state->rs_new_rel = new_heap;
-	state->rs_buffer = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	state->rs_buffer = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 	/* new_heap needn't be empty, just locked */
 	state->rs_blockno = RelationGetNumberOfBlocks(new_heap);
 	state->rs_buffer_valid = false;
@@ -631,7 +631,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 		Assert(!HeapTupleHasExternal(tup));
 		heaptup = tup;
 	}
-	else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
+	else if (HeapTupleHasExternal(tup) || tup->t_len > toast_tuple_threshold)
 	{
 		int			options = HEAP_INSERT_SKIP_FSM;
 
@@ -702,7 +702,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
 	if (!state->rs_buffer_valid)
 	{
 		/* Initialize a new empty page */
-		PageInit(page, BLCKSZ, 0);
+		PageInit(page, cluster_block_size, 0);
 		state->rs_buffer_valid = true;
 	}
 
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index fa77ef7f4a..d0011073b3 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -97,8 +97,8 @@
  * Perform a failsafe check each time we scan another 4GB of pages.
  * (Note that this is deliberately kept to a power-of-two, usually 2^19.)
  */
-#define FAILSAFE_EVERY_PAGES \
-	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / BLCKSZ))
+#define failsafe_every_pages \
+	((BlockNumber) (((uint64) 4 * 1024 * 1024 * 1024) / cluster_block_size))
 
 /*
  * When a table has no indexes, vacuum the FSM after every 8GB, approximately
@@ -106,8 +106,8 @@
  * that has some removable tuples).  When there are indexes, this is ignored,
  * and we vacuum FSM after each index/heap cleaning pass.
  */
-#define VACUUM_FSM_EVERY_PAGES \
-	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / BLCKSZ))
+#define vacuum_fsm_every_pages \
+	((BlockNumber) (((uint64) 8 * 1024 * 1024 * 1024) / cluster_block_size))
 
 /*
  * Before we consider skipping a page that's marked as clean in
@@ -749,9 +749,9 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
 			}
 			if (secs_dur > 0 || usecs_dur > 0)
 			{
-				read_rate = (double) BLCKSZ * PageMissOp / (1024 * 1024) /
+				read_rate = (double) cluster_block_size * PageMissOp / (1024 * 1024) /
 					(secs_dur + usecs_dur / 1000000.0);
-				write_rate = (double) BLCKSZ * PageDirtyOp / (1024 * 1024) /
+				write_rate = (double) cluster_block_size * PageDirtyOp / (1024 * 1024) /
 					(secs_dur + usecs_dur / 1000000.0);
 			}
 			appendStringInfo(&buf, _("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n"),
@@ -900,7 +900,7 @@ lazy_scan_heap(LVRelState *vacrel)
 		 * one-pass strategy, and the two-pass strategy with the index_cleanup
 		 * param set to 'off'.
 		 */
-		if (vacrel->scanned_pages % FAILSAFE_EVERY_PAGES == 0)
+		if (vacrel->scanned_pages % failsafe_every_pages == 0)
 			lazy_check_wraparound_failsafe(vacrel);
 
 		/*
@@ -1051,7 +1051,7 @@ lazy_scan_heap(LVRelState *vacrel)
 				 * space visible on upper FSM pages.  Note we have not yet
 				 * performed FSM processing for blkno.
 				 */
-				if (blkno - next_fsm_block_to_vacuum >= VACUUM_FSM_EVERY_PAGES)
+				if (blkno - next_fsm_block_to_vacuum >= vacuum_fsm_every_pages)
 				{
 					FreeSpaceMapVacuumRange(vacrel->rel, next_fsm_block_to_vacuum,
 											blkno);
@@ -1439,7 +1439,7 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno,
 
 		if (GetRecordedFreeSpace(vacrel->rel, blkno) == 0)
 		{
-			freespace = BLCKSZ - SizeOfPageHeaderData;
+			freespace = cluster_block_size - SizeOfPageHeaderData;
 
 			RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
 		}
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 2e18cd88bc..833774c164 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -105,7 +105,7 @@
  * extra headers, so the whole page minus the standard page header is
  * used for the bitmap.
  */
-#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData))
+#define MAPSIZE (cluster_block_size - MAXALIGN(SizeOfPageHeaderData))
 
 /* Number of heap blocks we can represent in one byte */
 #define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
@@ -613,7 +613,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), cluster_block_size, 0);
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	}
 	return buf;
diff --git a/src/backend/access/nbtree/nbtdedup.c b/src/backend/access/nbtree/nbtdedup.c
index d4db0b28f2..6d1b02b17c 100644
--- a/src/backend/access/nbtree/nbtdedup.c
+++ b/src/backend/access/nbtree/nbtdedup.c
@@ -324,7 +324,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 	state = (BTDedupState) palloc(sizeof(BTDedupStateData));
 	state->deduplicate = true;
 	state->nmaxitems = 0;
-	state->maxpostingsize = BLCKSZ; /* We're not really deduplicating */
+	state->maxpostingsize = cluster_block_size; /* We're not really deduplicating */
 	state->base = NULL;
 	state->baseoff = InvalidOffsetNumber;
 	state->basetupsize = 0;
@@ -353,7 +353,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 	delstate.irel = rel;
 	delstate.iblknum = BufferGetBlockNumber(buf);
 	delstate.bottomup = true;
-	delstate.bottomupfreespace = Max(BLCKSZ / 16, newitemsz);
+	delstate.bottomupfreespace = Max(cluster_block_size / 16, newitemsz);
 	delstate.ndeltids = 0;
 	delstate.deltids = palloc(MaxTIDsPerBTreePage * sizeof(TM_IndexDelete));
 	delstate.status = palloc(MaxTIDsPerBTreePage * sizeof(TM_IndexStatus));
@@ -417,7 +417,7 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
 		return true;
 
 	/* Don't dedup when we won't end up back here any time soon anyway */
-	return PageGetExactFreeSpace(page) >= Max(BLCKSZ / 24, newitemsz);
+	return PageGetExactFreeSpace(page) >= Max(cluster_block_size / 24, newitemsz);
 }
 
 /*
@@ -597,7 +597,7 @@ _bt_dedup_finish_pending(Page newpage, BTDedupState state)
 		spacesaving = state->phystupsize - (tuplesz + sizeof(ItemIdData));
 		/* Increment nintervals, since we wrote a new posting list tuple */
 		state->nintervals++;
-		Assert(spacesaving > 0 && spacesaving < BLCKSZ);
+		Assert(spacesaving > 0 && spacesaving < cluster_block_size);
 	}
 
 	/* Reset state for next pending posting list */
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index b7660a459e..db248af32a 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -70,7 +70,7 @@ _bt_initmetapage(Page page, BlockNumber rootbknum, uint32 level,
 	BTMetaPageData *metad;
 	BTPageOpaque metaopaque;
 
-	_bt_pageinit(page, BLCKSZ);
+	_bt_pageinit(page, cluster_block_size);
 
 	metad = BTPageGetMeta(page);
 	metad->btm_magic = BTREE_MAGIC;
@@ -977,7 +977,7 @@ _bt_allocbuf(Relation rel, Relation heaprel)
 	 */
 	buf = ExtendBufferedRel(BMR_REL(rel), MAIN_FORKNUM, NULL, EB_LOCK_FIRST);
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	/* Initialize the new page before returning it */
 	page = BufferGetPage(buf);
@@ -1060,7 +1060,7 @@ _bt_lockbuf(Relation rel, Buffer buf, int access)
 	 * lock/pin held, though.
 	 */
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), cluster_block_size);
 }
 
 /*
@@ -1073,13 +1073,13 @@ _bt_unlockbuf(Relation rel, Buffer buf)
 	 * Buffer is pinned and locked, which means that it is expected to be
 	 * defined and addressable.  Check that proactively.
 	 */
-	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), BLCKSZ);
+	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	/* LockBuffer() asserts that pin is held by this backend */
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_NOACCESS(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_NOACCESS(BufferGetPage(buf), cluster_block_size);
 }
 
 /*
@@ -1097,7 +1097,7 @@ _bt_conditionallockbuf(Relation rel, Buffer buf)
 		return false;
 
 	if (!RelationUsesLocalBuffers(rel))
-		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	return true;
 }
@@ -1112,7 +1112,7 @@ _bt_upgradelockbufcleanup(Relation rel, Buffer buf)
 	 * Buffer is pinned and locked, which means that it is expected to be
 	 * defined and addressable.  Check that proactively.
 	 */
-	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), BLCKSZ);
+	VALGRIND_CHECK_MEM_IS_DEFINED(BufferGetPage(buf), cluster_block_size);
 
 	/* LockBuffer() asserts that pin is held by this backend */
 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 6c5b5c69ce..f4d8aae306 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -428,8 +428,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
 	 */
 	if (scan->xs_want_itup && so->currTuples == NULL)
 	{
-		so->currTuples = (char *) palloc(BLCKSZ * 2);
-		so->markTuples = so->currTuples + BLCKSZ;
+		so->currTuples = (char *) palloc(cluster_block_size * 2);
+		so->markTuples = so->currTuples + cluster_block_size;
 	}
 
 	/*
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index c2665fce41..b701e492fe 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -619,10 +619,10 @@ _bt_blnewpage(uint32 level)
 	Page		page;
 	BTPageOpaque opaque;
 
-	page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	page = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 
 	/* Zero the page and set up standard page header info */
-	_bt_pageinit(page, BLCKSZ);
+	_bt_pageinit(page, cluster_block_size);
 
 	/* Initialize BT opaque state */
 	opaque = BTPageGetOpaque(page);
@@ -660,7 +660,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
 	while (blkno > wstate->btws_pages_written)
 	{
 		if (!wstate->btws_zeropage)
-			wstate->btws_zeropage = (Page) palloc_aligned(BLCKSZ,
+			wstate->btws_zeropage = (Page) palloc_aligned(cluster_block_size,
 														  PG_IO_ALIGN_SIZE,
 														  MCXT_ALLOC_ZERO);
 		/* don't set checksum for all-zero page */
@@ -715,7 +715,7 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
 	state->btps_level = level;
 	/* set "full" threshold based on level.  See notes at head of file. */
 	if (level > 0)
-		state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
+		state->btps_full = (cluster_block_size * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
 	else
 		state->btps_full = BTGetTargetPageFreeSpace(wstate->index);
 
@@ -1172,7 +1172,7 @@ _bt_uppershutdown(BTWriteState *wstate, BTPageState *state)
 	 * set to point to "P_NONE").  This changes the index to the "valid" state
 	 * by filling in a valid magic number in the metapage.
 	 */
-	metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
+	metapage = (Page) palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE, 0);
 	_bt_initmetapage(metapage, rootblkno, rootlevel,
 					 wstate->inskey->allequalimage);
 	_bt_blwritepage(wstate, metapage, BTREE_METAPAGE);
@@ -1350,7 +1350,7 @@ _bt_load(BTWriteState *wstate, BTSpool *btspool, BTSpool *btspool2)
 				 * leaf pages full with few very large tuples doesn't seem
 				 * like a useful goal.)
 				 */
-				dstate->maxpostingsize = MAXALIGN_DOWN((BLCKSZ * 10 / 100)) -
+				dstate->maxpostingsize = MAXALIGN_DOWN((cluster_block_size * 10 / 100)) -
 					sizeof(ItemIdData);
 				Assert(dstate->maxpostingsize <= BTMaxItemSize(state->btps_page) &&
 					   dstate->maxpostingsize <= INDEX_SIZE_MASK);
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index f390c177e4..b9dd2b7e5d 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -271,7 +271,7 @@ XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty,
 									 "" : " for WAL verification",
 									 XLogRecGetBlock(record, block_id)->hole_offset,
 									 XLogRecGetBlock(record, block_id)->hole_length,
-									 BLCKSZ -
+									 cluster_block_size -
 									 XLogRecGetBlock(record, block_id)->hole_length -
 									 XLogRecGetBlock(record, block_id)->bimg_len,
 									 method);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 3554edcc9a..d8c2752e85 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -341,8 +341,8 @@ checkSplitConditions(Relation index, SpGistState *state,
 	if (SpGistBlockIsRoot(current->blkno))
 	{
 		/* return impossible values to force split */
-		*nToSplit = BLCKSZ;
-		return BLCKSZ;
+		*nToSplit = cluster_block_size;
+		return cluster_block_size;
 	}
 
 	i = current->offnum;
@@ -899,7 +899,7 @@ doPickSplit(Relation index, SpGistState *state,
 	 * fit on one page.
 	 */
 	allTheSame = checkAllTheSame(&in, &out,
-								 totalLeafSizes > SPGIST_PAGE_CAPACITY,
+								 totalLeafSizes > spgist_page_capacity,
 								 &includeNew);
 
 	/*
@@ -1030,7 +1030,7 @@ doPickSplit(Relation index, SpGistState *state,
 		for (i = 0; i < nToInsert; i++)
 			leafPageSelect[i] = 0;	/* signifies current page */
 	}
-	else if (in.nTuples == 1 && totalLeafSizes > SPGIST_PAGE_CAPACITY)
+	else if (in.nTuples == 1 && totalLeafSizes > spgist_page_capacity)
 	{
 		/*
 		 * We're trying to split up a long value by repeated suffixing, but
@@ -1051,7 +1051,7 @@ doPickSplit(Relation index, SpGistState *state,
 		newLeafBuffer = SpGistGetBuffer(index,
 										GBUF_LEAF | (isNulls ? GBUF_NULLS : 0),
 										Min(totalLeafSizes,
-											SPGIST_PAGE_CAPACITY),
+											spgist_page_capacity),
 										&xlrec.initDest);
 
 		/*
@@ -1995,13 +1995,13 @@ spgdoinsert(Relation index, SpGistState *state,
 	 * If it isn't gonna fit, and the opclass can't reduce the datum size by
 	 * suffixing, bail out now rather than doing a lot of useless work.
 	 */
-	if (leafSize > SPGIST_PAGE_CAPACITY &&
+	if (leafSize > spgist_page_capacity &&
 		(isnull || !state->config.longValuesOK))
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
 						leafSize - sizeof(ItemIdData),
-						SPGIST_PAGE_CAPACITY - sizeof(ItemIdData),
+						spgist_page_capacity - sizeof(ItemIdData),
 						RelationGetRelationName(index)),
 				 errhint("Values larger than a buffer page cannot be indexed.")));
 	bestLeafSize = leafSize;
@@ -2059,7 +2059,7 @@ spgdoinsert(Relation index, SpGistState *state,
 			current.buffer =
 				SpGistGetBuffer(index,
 								GBUF_LEAF | (isnull ? GBUF_NULLS : 0),
-								Min(leafSize, SPGIST_PAGE_CAPACITY),
+								Min(leafSize, spgist_page_capacity),
 								&isNew);
 			current.blkno = BufferGetBlockNumber(current.buffer);
 		}
@@ -2122,9 +2122,9 @@ spgdoinsert(Relation index, SpGistState *state,
 			}
 			else if ((sizeToSplit =
 					  checkSplitConditions(index, state, &current,
-										   &nToSplit)) < SPGIST_PAGE_CAPACITY / 2 &&
+										   &nToSplit)) < spgist_page_capacity / 2 &&
 					 nToSplit < 64 &&
-					 leafTuple->size + sizeof(ItemIdData) + sizeToSplit <= SPGIST_PAGE_CAPACITY)
+					 leafTuple->size + sizeof(ItemIdData) + sizeToSplit <= spgist_page_capacity)
 			{
 				/*
 				 * the amount of data is pretty small, so just move the whole
@@ -2258,7 +2258,7 @@ spgdoinsert(Relation index, SpGistState *state,
 					 * than MAXALIGN, to accommodate opclasses that trim one
 					 * byte from the leaf datum per pass.)
 					 */
-					if (leafSize > SPGIST_PAGE_CAPACITY)
+					if (leafSize > spgist_page_capacity)
 					{
 						bool		ok = false;
 
@@ -2278,7 +2278,7 @@ spgdoinsert(Relation index, SpGistState *state,
 									(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 									 errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
 											leafSize - sizeof(ItemIdData),
-											SPGIST_PAGE_CAPACITY - sizeof(ItemIdData),
+											spgist_page_capacity - sizeof(ItemIdData),
 											RelationGetRelationName(index)),
 									 errhint("Values larger than a buffer page cannot be indexed.")));
 					}
diff --git a/src/backend/access/spgist/spgtextproc.c b/src/backend/access/spgist/spgtextproc.c
index 03a7afdbab..0b247d4789 100644
--- a/src/backend/access/spgist/spgtextproc.c
+++ b/src/backend/access/spgist/spgtextproc.c
@@ -53,20 +53,20 @@
  * In the worst case, an inner tuple in a text radix tree could have as many
  * as 258 nodes (one for each possible byte value, plus the two special
  * cases).  Each node can take 16 bytes on MAXALIGN=8 machines.  The inner
- * tuple must fit on an index page of size BLCKSZ.  Rather than assuming we
+ * tuple must fit on an index page of size cluster_block_size.  Rather than assuming we
  * know the exact amount of overhead imposed by page headers, tuple headers,
  * etc, we leave 100 bytes for that (the actual overhead should be no more
  * than 56 bytes at this writing, so there is slop in this number).
- * So we can safely create prefixes up to BLCKSZ - 258 * 16 - 100 bytes long.
+ * So we can safely create prefixes up to cluster_block_size - 258 * 16 - 100 bytes long.
  * Unfortunately, because 258 * 16 is over 4K, there is no safe prefix length
- * when BLCKSZ is less than 8K; it is always possible to get "SPGiST inner
+ * when cluster_block_size is less than 8K; it is always possible to get "SPGiST inner
  * tuple size exceeds maximum" if there are too many distinct next-byte values
  * at a given place in the tree.  Since use of nonstandard block sizes appears
  * to be negligible in the field, we just live with that fact for now,
- * choosing a max prefix size of 32 bytes when BLCKSZ is configured smaller
+ * choosing a max prefix size of 32 bytes when cluster_block_size is configured smaller
  * than default.
  */
-#define SPGIST_MAX_PREFIX_LENGTH	Max((int) (BLCKSZ - 258 * 16 - 100), 32)
+#define SPGIST_MAX_PREFIX_LENGTH	Max((int) (cluster_block_size - 258 * 16 - 100), 32)
 
 /*
  * Strategy for collation aware operator on text is equal to btree strategy
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 8f32e46fb8..6dfd875bc9 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -544,7 +544,7 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
 	SpGistLastUsedPage *lup;
 
 	/* Bail out if even an empty page wouldn't meet the demand */
-	if (needSpace > SPGIST_PAGE_CAPACITY)
+	if (needSpace > spgist_page_capacity)
 		elog(ERROR, "desired SPGiST tuple size is too big");
 
 	/*
@@ -555,7 +555,7 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
 	 * error for requests that would otherwise be legal.
 	 */
 	needSpace += SpGistGetTargetPageFreeSpace(index);
-	needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
+	needSpace = Min(needSpace, spgist_page_capacity);
 
 	/* Get the cache entry for this flags setting */
 	lup = GET_LUP(cache, flags);
@@ -681,7 +681,7 @@ SpGistInitPage(Page page, uint16 f)
 {
 	SpGistPageOpaque opaque;
 
-	PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
+	PageInit(page, cluster_block_size, sizeof(SpGistPageOpaqueData));
 	opaque = SpGistPageGetOpaque(page);
 	opaque->flags = f;
 	opaque->spgist_page_id = SPGIST_PAGE_ID;
@@ -693,7 +693,7 @@ SpGistInitPage(Page page, uint16 f)
 void
 SpGistInitBuffer(Buffer b, uint16 f)
 {
-	Assert(BufferGetPageSize(b) == BLCKSZ);
+	Assert(BufferGetPageSize(b) == cluster_block_size);
 	SpGistInitPage(BufferGetPage(b), f);
 }
 
@@ -1002,12 +1002,12 @@ spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix,
 	/*
 	 * Inner tuple should be small enough to fit on a page
 	 */
-	if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
+	if (size > spgist_page_capacity - sizeof(ItemIdData))
 		ereport(ERROR,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
 						(Size) size,
-						SPGIST_PAGE_CAPACITY - sizeof(ItemIdData)),
+						spgist_page_capacity - sizeof(ItemIdData)),
 				 errhint("Values larger than a buffer page cannot be indexed.")));
 
 	/*
diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
index c6bdb7e1c6..6ca676143c 100644
--- a/src/backend/access/table/tableam.c
+++ b/src/backend/access/table/tableam.c
@@ -636,7 +636,7 @@ table_block_relation_size(Relation rel, ForkNumber forkNumber)
 	else
 		nblocks = smgrnblocks(RelationGetSmgr(rel), forkNumber);
 
-	return nblocks * BLCKSZ;
+	return nblocks * cluster_block_size;
 }
 
 /*
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index 6de2378748..57e3cbc389 100644
--- a/src/backend/access/transam/README
+++ b/src/backend/access/transam/README
@@ -851,7 +851,7 @@ we won't be able to hint its outputs until the second xact is sync'd, up to
 three walwriter cycles later.  This argues for keeping N (the group size)
 as small as possible.  For the moment we are setting the group size to 32,
 which makes the LSN cache space the same size as the actual clog buffer
-space (independently of BLCKSZ).
+space (independently of cluster_block_size).
 
 It is useful that we can run both synchronous and asynchronous commit
 transactions concurrently, but the safety of this is perhaps not
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 4a431d5876..fc9118487d 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -45,7 +45,7 @@
 #include "storage/sync.h"
 
 /*
- * Defines for CLOG page sizes.  A page is the same BLCKSZ as is used
+ * Defines for CLOG page sizes.  A page is the same cluster_block_size as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
@@ -59,7 +59,7 @@
 /* We need two bits per xact, so four xacts fit in a byte */
 #define CLOG_BITS_PER_XACT	2
 #define CLOG_XACTS_PER_BYTE 4
-#define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
+#define CLOG_XACTS_PER_PAGE (cluster_block_size * CLOG_XACTS_PER_BYTE)
 #define CLOG_XACT_BITMASK	((1 << CLOG_BITS_PER_XACT) - 1)
 
 #define TransactionIdToPage(xid)	((xid) / (TransactionId) CLOG_XACTS_PER_PAGE)
@@ -802,7 +802,7 @@ TrimCLOG(void)
 		/* Zero so-far-unused positions in the current byte */
 		*byteptr &= (1 << bshift) - 1;
 		/* Zero the rest of the page */
-		MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
+		MemSet(byteptr + 1, 0, cluster_block_size - byteno - 1);
 
 		XactCtl->shared->page_dirty[slotno] = true;
 	}
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index b897fabc70..e9fb797c82 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -37,13 +37,13 @@
 #include "utils/timestamp.h"
 
 /*
- * Defines for CommitTs page sizes.  A page is the same BLCKSZ as is used
+ * Defines for CommitTs page sizes.  A page is the same cluster_block_size as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
  * CommitTs page numbering also wraps around at
- * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE, and CommitTs segment numbering at
- * 0xFFFFFFFF/COMMIT_TS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need take no
+ * 0xFFFFFFFF/commit_ts_xacts_per_page, and CommitTs segment numbering at
+ * 0xFFFFFFFF/commit_ts_xacts_per_page/SLRU_PAGES_PER_SEGMENT.  We need take no
  * explicit notice of that fact in this module, except when comparing segment
  * and page numbers in TruncateCommitTs (see CommitTsPagePrecedes).
  */
@@ -62,13 +62,13 @@ typedef struct CommitTimestampEntry
 #define SizeOfCommitTimestampEntry (offsetof(CommitTimestampEntry, nodeid) + \
 									sizeof(RepOriginId))
 
-#define COMMIT_TS_XACTS_PER_PAGE \
-	(BLCKSZ / SizeOfCommitTimestampEntry)
+#define commit_ts_xacts_per_page \
+	(cluster_block_size / SizeOfCommitTimestampEntry)
 
 #define TransactionIdToCTsPage(xid) \
-	((xid) / (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
+	((xid) / (TransactionId) commit_ts_xacts_per_page)
 #define TransactionIdToCTsEntry(xid)	\
-	((xid) % (TransactionId) COMMIT_TS_XACTS_PER_PAGE)
+	((xid) % (TransactionId) commit_ts_xacts_per_page)
 
 /*
  * Link to shared-memory data structures for CommitTs control
@@ -524,7 +524,7 @@ CommitTsShmemInit(void)
 				  CommitTsSLRULock, "pg_commit_ts",
 				  LWTRANCHE_COMMITTS_BUFFER,
 				  SYNC_HANDLER_COMMIT_TS);
-	SlruPagePrecedesUnitTests(CommitTsCtl, COMMIT_TS_XACTS_PER_PAGE);
+	SlruPagePrecedesUnitTests(CommitTsCtl, commit_ts_xacts_per_page);
 
 	commitTsShared = ShmemInitStruct("CommitTs shared",
 									 sizeof(CommitTimestampShared),
@@ -898,7 +898,7 @@ AdvanceOldestCommitTsXid(TransactionId oldestXact)
  * Decide whether a commitTS page number is "older" for truncation purposes.
  * Analogous to CLOGPagePrecedes().
  *
- * At default BLCKSZ, (1 << 31) % COMMIT_TS_XACTS_PER_PAGE == 128.  This
+ * At default cluster_block_size, (1 << 31) % commit_ts_xacts_per_page == 128.  This
  * introduces differences compared to CLOG and the other SLRUs having (1 <<
  * 31) % per_page == 0.  This function never tests exactly
  * TransactionIdPrecedes(x-2^31, x).  When the system reaches xidStopLimit,
@@ -923,13 +923,13 @@ CommitTsPagePrecedes(int page1, int page2)
 	TransactionId xid1;
 	TransactionId xid2;
 
-	xid1 = ((TransactionId) page1) * COMMIT_TS_XACTS_PER_PAGE;
+	xid1 = ((TransactionId) page1) * commit_ts_xacts_per_page;
 	xid1 += FirstNormalTransactionId + 1;
-	xid2 = ((TransactionId) page2) * COMMIT_TS_XACTS_PER_PAGE;
+	xid2 = ((TransactionId) page2) * commit_ts_xacts_per_page;
 	xid2 += FirstNormalTransactionId + 1;
 
 	return (TransactionIdPrecedes(xid1, xid2) &&
-			TransactionIdPrecedes(xid1, xid2 + COMMIT_TS_XACTS_PER_PAGE - 1));
+			TransactionIdPrecedes(xid1, xid2 + commit_ts_xacts_per_page - 1));
 }
 
 
diff --git a/src/backend/access/transam/generic_xlog.c b/src/backend/access/transam/generic_xlog.c
index 6c68191ca6..771dde3419 100644
--- a/src/backend/access/transam/generic_xlog.c
+++ b/src/backend/access/transam/generic_xlog.c
@@ -45,7 +45,7 @@
  */
 #define FRAGMENT_HEADER_SIZE	(2 * sizeof(OffsetNumber))
 #define MATCH_THRESHOLD			FRAGMENT_HEADER_SIZE
-#define MAX_DELTA_SIZE			(BLCKSZ + 2 * FRAGMENT_HEADER_SIZE)
+#define MAX_DELTA_SIZE			(cluster_block_size + 2 * FRAGMENT_HEADER_SIZE)
 
 /* Struct of generic xlog data for single page */
 typedef struct
@@ -241,8 +241,8 @@ computeDelta(PageData *pageData, Page curpage, Page targetpage)
 					   0, curLower);
 	/* ... and for upper part, ignoring what's between */
 	computeRegionDelta(pageData, curpage, targetpage,
-					   targetUpper, BLCKSZ,
-					   curUpper, BLCKSZ);
+					   targetUpper, cluster_block_size,
+					   curUpper, cluster_block_size);
 
 	/*
 	 * If xlog debug is enabled, then check produced delta.  Result of delta
@@ -253,11 +253,11 @@ computeDelta(PageData *pageData, Page curpage, Page targetpage)
 	{
 		PGAlignedBlock tmp;
 
-		memcpy(tmp.data, curpage, BLCKSZ);
+		memcpy(tmp.data, curpage, cluster_block_size);
 		applyPageRedo(tmp.data, pageData->delta, pageData->deltaLen);
 		if (memcmp(tmp.data, targetpage, targetLower) != 0 ||
 			memcmp(tmp.data + targetUpper, targetpage + targetUpper,
-				   BLCKSZ - targetUpper) != 0)
+				   cluster_block_size - targetUpper) != 0)
 			elog(ERROR, "result of generic xlog apply does not match");
 	}
 #endif
@@ -311,7 +311,7 @@ GenericXLogRegisterBuffer(GenericXLogState *state, Buffer buffer, int flags)
 			/* Empty slot, so use it (there cannot be a match later) */
 			page->buffer = buffer;
 			page->flags = flags;
-			memcpy(page->image, BufferGetPage(buffer), BLCKSZ);
+			memcpy(page->image, BufferGetPage(buffer), cluster_block_size);
 			return (Page) page->image;
 		}
 		else if (page->buffer == buffer)
@@ -373,7 +373,7 @@ GenericXLogFinish(GenericXLogState *state)
 					   pageHeader->pd_upper - pageHeader->pd_lower);
 				memcpy(page + pageHeader->pd_upper,
 					   pageData->image + pageHeader->pd_upper,
-					   BLCKSZ - pageHeader->pd_upper);
+					   cluster_block_size - pageHeader->pd_upper);
 
 				XLogRegisterBuffer(i, pageData->buffer,
 								   REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
@@ -392,7 +392,7 @@ GenericXLogFinish(GenericXLogState *state)
 					   pageHeader->pd_upper - pageHeader->pd_lower);
 				memcpy(page + pageHeader->pd_upper,
 					   pageData->image + pageHeader->pd_upper,
-					   BLCKSZ - pageHeader->pd_upper);
+					   cluster_block_size - pageHeader->pd_upper);
 
 				XLogRegisterBuffer(i, pageData->buffer, REGBUF_STANDARD);
 				XLogRegisterBufData(i, pageData->delta, pageData->deltaLen);
@@ -426,7 +426,7 @@ GenericXLogFinish(GenericXLogState *state)
 				continue;
 			memcpy(BufferGetPage(pageData->buffer),
 				   pageData->image,
-				   BLCKSZ);
+				   cluster_block_size);
 			/* We don't worry about zeroing the "hole" in this case */
 			MarkBufferDirty(pageData->buffer);
 		}
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index abb022e067..ef135140e3 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -93,25 +93,25 @@
 
 
 /*
- * Defines for MultiXactOffset page sizes.  A page is the same BLCKSZ as is
+ * Defines for MultiXactOffset page sizes.  A page is the same cluster_block_size as is
  * used everywhere else in Postgres.
  *
  * Note: because MultiXactOffsets are 32 bits and wrap around at 0xFFFFFFFF,
  * MultiXact page numbering also wraps around at
- * 0xFFFFFFFF/MULTIXACT_OFFSETS_PER_PAGE, and segment numbering at
- * 0xFFFFFFFF/MULTIXACT_OFFSETS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need
+ * 0xFFFFFFFF/multixact_offsets_per_page, and segment numbering at
+ * 0xFFFFFFFF/multixact_offsets_per_page/SLRU_PAGES_PER_SEGMENT.  We need
  * take no explicit notice of that fact in this module, except when comparing
  * segment and page numbers in TruncateMultiXact (see
  * MultiXactOffsetPagePrecedes).
  */
 
 /* We need four bytes per offset */
-#define MULTIXACT_OFFSETS_PER_PAGE (BLCKSZ / sizeof(MultiXactOffset))
+#define multixact_offsets_per_page (cluster_block_size / sizeof(MultiXactOffset))
 
 #define MultiXactIdToOffsetPage(xid) \
-	((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
+	((xid) / (MultiXactOffset) multixact_offsets_per_page)
 #define MultiXactIdToOffsetEntry(xid) \
-	((xid) % (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
+	((xid) % (MultiXactOffset) multixact_offsets_per_page)
 #define MultiXactIdToOffsetSegment(xid) (MultiXactIdToOffsetPage(xid) / SLRU_PAGES_PER_SEGMENT)
 
 /*
@@ -119,7 +119,7 @@
  * additional flag bits for each TransactionId.  To do this without getting
  * into alignment issues, we store four bytes of flags, and then the
  * corresponding 4 Xids.  Each such 5-word (20-byte) set we call a "group", and
- * are stored as a whole in pages.  Thus, with 8kB BLCKSZ, we keep 409 groups
+ * are stored as a whole in pages.  Thus, with 8kB cluster_block_size, we keep 409 groups
  * per page.  This wastes 12 bytes per page, but that's OK -- simplicity (and
  * performance) trumps space efficiency here.
  *
@@ -138,9 +138,9 @@
 /* size in bytes of a complete group */
 #define MULTIXACT_MEMBERGROUP_SIZE \
 	(sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)
-#define MULTIXACT_MEMBERGROUPS_PER_PAGE (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)
-#define MULTIXACT_MEMBERS_PER_PAGE	\
-	(MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
+#define multixact_membergroups_per_page (cluster_block_size / MULTIXACT_MEMBERGROUP_SIZE)
+#define multixact_members_per_page	\
+	(multixact_membergroups_per_page * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
 
 /*
  * Because the number of items per page is not a divisor of the last item
@@ -153,16 +153,16 @@
  * This constant is the number of members in the last page of the last segment.
  */
 #define MAX_MEMBERS_IN_LAST_MEMBERS_PAGE \
-		((uint32) ((0xFFFFFFFF % MULTIXACT_MEMBERS_PER_PAGE) + 1))
+		((uint32) ((0xFFFFFFFF % multixact_members_per_page) + 1))
 
 /* page in which a member is to be found */
-#define MXOffsetToMemberPage(xid) ((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
+#define MXOffsetToMemberPage(xid) ((xid) / (TransactionId) multixact_members_per_page)
 #define MXOffsetToMemberSegment(xid) (MXOffsetToMemberPage(xid) / SLRU_PAGES_PER_SEGMENT)
 
 /* Location (byte offset within page) of flag word for a given member */
 #define MXOffsetToFlagsOffset(xid) \
 	((((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) % \
-	  (TransactionId) MULTIXACT_MEMBERGROUPS_PER_PAGE) * \
+	  (TransactionId) multixact_membergroups_per_page) * \
 	 (TransactionId) MULTIXACT_MEMBERGROUP_SIZE)
 #define MXOffsetToFlagsBitShift(xid) \
 	(((xid) % (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) * \
@@ -1152,7 +1152,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
 	if (MultiXactState->oldestOffsetKnown &&
 		MultiXactOffsetWouldWrap(MultiXactState->offsetStopLimit,
 								 nextOffset,
-								 nmembers + MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT * OFFSET_WARN_SEGMENTS))
+								 nmembers + multixact_members_per_page * SLRU_PAGES_PER_SEGMENT * OFFSET_WARN_SEGMENTS))
 		ereport(WARNING,
 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 				 errmsg_plural("database with OID %u must be vacuumed before %d more multixact member is used",
@@ -1855,7 +1855,7 @@ MultiXactShmemInit(void)
 				  MultiXactOffsetSLRULock, "pg_multixact/offsets",
 				  LWTRANCHE_MULTIXACTOFFSET_BUFFER,
 				  SYNC_HANDLER_MULTIXACT_OFFSET);
-	SlruPagePrecedesUnitTests(MultiXactOffsetCtl, MULTIXACT_OFFSETS_PER_PAGE);
+	SlruPagePrecedesUnitTests(MultiXactOffsetCtl, multixact_offsets_per_page);
 	SimpleLruInit(MultiXactMemberCtl,
 				  "MultiXactMember", NUM_MULTIXACTMEMBER_BUFFERS, 0,
 				  MultiXactMemberSLRULock, "pg_multixact/members",
@@ -2072,7 +2072,7 @@ TrimMultiXact(void)
 		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 		offptr += entryno;
 
-		MemSet(offptr, 0, BLCKSZ - (entryno * sizeof(MultiXactOffset)));
+		MemSet(offptr, 0, cluster_block_size - (entryno * sizeof(MultiXactOffset)));
 
 		MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
 	}
@@ -2104,7 +2104,7 @@ TrimMultiXact(void)
 		xidptr = (TransactionId *)
 			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
-		MemSet(xidptr, 0, BLCKSZ - memberoff);
+		MemSet(xidptr, 0, cluster_block_size - memberoff);
 
 		/*
 		 * Note: we don't need to zero out the flag bits in the remaining
@@ -2480,7 +2480,7 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
 			difference = MaxMultiXactOffset - offset + 1;
 		}
 		else
-			difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE;
+			difference = multixact_members_per_page - offset % multixact_members_per_page;
 
 		/*
 		 * Advance to next page, taking care to properly handle the wraparound
@@ -2634,10 +2634,10 @@ SetOffsetVacuumLimit(bool is_startup)
 	{
 		/* move back to start of the corresponding segment */
 		offsetStopLimit = oldestOffset - (oldestOffset %
-										  (MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT));
+										  (multixact_members_per_page * SLRU_PAGES_PER_SEGMENT));
 
 		/* always leave one segment before the wraparound point */
-		offsetStopLimit -= (MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT);
+		offsetStopLimit -= (multixact_members_per_page * SLRU_PAGES_PER_SEGMENT);
 
 		if (!prevOldestOffsetKnown && !is_startup)
 			ereport(LOG,
@@ -2997,7 +2997,7 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
 	 */
 	trunc.earliestExistingPage = -1;
 	SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
-	earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;
+	earliest = trunc.earliestExistingPage * multixact_offsets_per_page;
 	if (earliest < FirstMultiXactId)
 		earliest = FirstMultiXactId;
 
@@ -3118,14 +3118,14 @@ MultiXactOffsetPagePrecedes(int page1, int page2)
 	MultiXactId multi1;
 	MultiXactId multi2;
 
-	multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE;
+	multi1 = ((MultiXactId) page1) * multixact_offsets_per_page;
 	multi1 += FirstMultiXactId + 1;
-	multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE;
+	multi2 = ((MultiXactId) page2) * multixact_offsets_per_page;
 	multi2 += FirstMultiXactId + 1;
 
 	return (MultiXactIdPrecedes(multi1, multi2) &&
 			MultiXactIdPrecedes(multi1,
-								multi2 + MULTIXACT_OFFSETS_PER_PAGE - 1));
+								multi2 + multixact_offsets_per_page - 1));
 }
 
 /*
@@ -3138,12 +3138,12 @@ MultiXactMemberPagePrecedes(int page1, int page2)
 	MultiXactOffset offset1;
 	MultiXactOffset offset2;
 
-	offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE;
-	offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE;
+	offset1 = ((MultiXactOffset) page1) * multixact_members_per_page;
+	offset2 = ((MultiXactOffset) page2) * multixact_members_per_page;
 
 	return (MultiXactOffsetPrecedes(offset1, offset2) &&
 			MultiXactOffsetPrecedes(offset1,
-									offset2 + MULTIXACT_MEMBERS_PER_PAGE - 1));
+									offset2 + multixact_members_per_page - 1));
 }
 
 /*
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 71ac70fb40..675eae6361 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -169,7 +169,7 @@ SimpleLruShmemSize(int nslots, int nlsns)
 	if (nlsns > 0)
 		sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));	/* group_lsn[] */
 
-	return BUFFERALIGN(sz) + BLCKSZ * nslots;
+	return BUFFERALIGN(sz) + cluster_block_size * nslots;
 }
 
 /*
@@ -251,7 +251,7 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
 			shared->page_dirty[slotno] = false;
 			shared->page_lru_count[slotno] = 0;
-			ptr += BLCKSZ;
+			ptr += cluster_block_size;
 		}
 
 		/* Should fit to estimated shmem size */
@@ -297,7 +297,7 @@ SimpleLruZeroPage(SlruCtl ctl, int pageno)
 	SlruRecentlyUsed(shared, slotno);
 
 	/* Set the buffer to zeroes */
-	MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+	MemSet(shared->page_buffer[slotno], 0, cluster_block_size);
 
 	/* Set the LSNs for this new page to zero */
 	SimpleLruZeroLSNs(ctl, slotno);
@@ -628,7 +628,7 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
 {
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	int			offset = rpageno * BLCKSZ;
+	int			offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 	int			fd;
 	bool		result;
@@ -659,7 +659,7 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
 		SlruReportIOError(ctl, pageno, 0);
 	}
 
-	result = endpos >= (off_t) (offset + BLCKSZ);
+	result = endpos >= (off_t) (offset + cluster_block_size);
 
 	if (CloseTransientFile(fd) != 0)
 	{
@@ -687,7 +687,7 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
 	SlruShared	shared = ctl->shared;
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	off_t		offset = rpageno * BLCKSZ;
+	off_t		offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 	int			fd;
 
@@ -713,13 +713,13 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
 		ereport(LOG,
 				(errmsg("file \"%s\" doesn't exist, reading as zeroes",
 						path)));
-		MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+		MemSet(shared->page_buffer[slotno], 0, cluster_block_size);
 		return true;
 	}
 
 	errno = 0;
 	pgstat_report_wait_start(WAIT_EVENT_SLRU_READ);
-	if (pg_pread(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	if (pg_pread(fd, shared->page_buffer[slotno], cluster_block_size, offset) != cluster_block_size)
 	{
 		pgstat_report_wait_end();
 		slru_errcause = SLRU_READ_FAILED;
@@ -759,7 +759,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
 	SlruShared	shared = ctl->shared;
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	off_t		offset = rpageno * BLCKSZ;
+	off_t		offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 	int			fd = -1;
 
@@ -874,7 +874,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
 
 	errno = 0;
 	pgstat_report_wait_start(WAIT_EVENT_SLRU_WRITE);
-	if (pg_pwrite(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	if (pg_pwrite(fd, shared->page_buffer[slotno], cluster_block_size, offset) != cluster_block_size)
 	{
 		pgstat_report_wait_end();
 		/* if write didn't set errno, assume problem is no disk space */
@@ -933,7 +933,7 @@ SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
 {
 	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	int			offset = rpageno * BLCKSZ;
+	int			offset = rpageno * cluster_block_size;
 	char		path[MAXPGPATH];
 
 	SlruFileName(ctl, path, segno);
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index 62bb610167..c7f3246164 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -36,23 +36,23 @@
 
 
 /*
- * Defines for SubTrans page sizes.  A page is the same BLCKSZ as is used
+ * Defines for SubTrans page sizes.  A page is the same cluster_block_size as is used
  * everywhere else in Postgres.
  *
  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
  * SubTrans page numbering also wraps around at
- * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE, and segment numbering at
- * 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need take no
+ * 0xFFFFFFFF/subtrans_xacts_per_page, and segment numbering at
+ * 0xFFFFFFFF/subtrans_xacts_per_page/SLRU_PAGES_PER_SEGMENT.  We need take no
  * explicit notice of that fact in this module, except when comparing segment
  * and page numbers in TruncateSUBTRANS (see SubTransPagePrecedes) and zeroing
  * them in StartupSUBTRANS.
  */
 
 /* We need four bytes per xact */
-#define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
+#define subtrans_xacts_per_page (cluster_block_size / sizeof(TransactionId))
 
-#define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
-#define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
+#define TransactionIdToPage(xid) ((xid) / (TransactionId) subtrans_xacts_per_page)
+#define TransactionIdToEntry(xid) ((xid) % (TransactionId) subtrans_xacts_per_page)
 
 
 /*
@@ -194,7 +194,7 @@ SUBTRANSShmemInit(void)
 	SimpleLruInit(SubTransCtl, "Subtrans", NUM_SUBTRANS_BUFFERS, 0,
 				  SubtransSLRULock, "pg_subtrans",
 				  LWTRANCHE_SUBTRANS_BUFFER, SYNC_HANDLER_NONE);
-	SlruPagePrecedesUnitTests(SubTransCtl, SUBTRANS_XACTS_PER_PAGE);
+	SlruPagePrecedesUnitTests(SubTransCtl, subtrans_xacts_per_page);
 }
 
 /*
@@ -364,11 +364,11 @@ SubTransPagePrecedes(int page1, int page2)
 	TransactionId xid1;
 	TransactionId xid2;
 
-	xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
+	xid1 = ((TransactionId) page1) * subtrans_xacts_per_page;
 	xid1 += FirstNormalTransactionId + 1;
-	xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
+	xid2 = ((TransactionId) page2) * subtrans_xacts_per_page;
 	xid2 += FirstNormalTransactionId + 1;
 
 	return (TransactionIdPrecedes(xid1, xid2) &&
-			TransactionIdPrecedes(xid1, xid2 + SUBTRANS_XACTS_PER_PAGE - 1));
+			TransactionIdPrecedes(xid1, xid2 + subtrans_xacts_per_page - 1));
 }
diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c
index 94e152694e..8b4ed3d053 100644
--- a/src/backend/access/transam/timeline.c
+++ b/src/backend/access/transam/timeline.c
@@ -307,7 +307,7 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
 	char		path[MAXPGPATH];
 	char		tmppath[MAXPGPATH];
 	char		histfname[MAXFNAMELEN];
-	char		buffer[BLCKSZ];
+	char		buffer[cluster_block_size];
 	int			srcfd;
 	int			fd;
 	int			nbytes;
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 334adac09e..ff375874b1 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -370,7 +370,7 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 	 * being significant compared to total XID space. (VACUUM requires an XID
 	 * if it truncates at wal_level!=minimal.  "VACUUM (ANALYZE)", which a DBA
 	 * might do by reflex, assigns an XID.  Hence, we had better be sure
-	 * there's lots of XIDs left...)  Also, at default BLCKSZ, this leaves two
+	 * there's lots of XIDs left...)  Also, at default cluster_block_size, this leaves two
 	 * completely-idle segments.  In the event of edge-case bugs involving
 	 * page or segment arithmetic, idle segments render the bugs unreachable
 	 * outside of single-user mode.
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index fcbde10529..b0b56c81d9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -3922,7 +3922,7 @@ WriteControlFile(void)
 	ControlFile->maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile->floatFormat = FLOATFORMAT_VALUE;
 
-	ControlFile->blcksz = BLCKSZ;
+	ControlFile->blcksz = cluster_block_size;
 	ControlFile->relseg_size = RELSEG_SIZE;
 	ControlFile->xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile->xlog_seg_size = wal_segment_size;
@@ -3930,8 +3930,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 = cluster_toast_max_chunk_size;
+	ControlFile->loblksize = cluster_loblksize;
 
 	ControlFile->float8ByVal = FLOAT8PASSBYVAL;
 
@@ -4086,12 +4086,12 @@ ReadControlFile(void)
 				(errmsg("database files are incompatible with server"),
 				 errdetail("The database cluster appears to use a different floating-point number format than the server executable."),
 				 errhint("It looks like you need to initdb.")));
-	if (ControlFile->blcksz != BLCKSZ)
+	if (ControlFile->blcksz != cluster_block_size)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with BLCKSZ %d,"
-						   " but the server was compiled with BLCKSZ %d.",
-						   ControlFile->blcksz, BLCKSZ),
+				 errdetail("The database cluster was initialized with cluster_block_size %d,"
+						   " but the server was compiled with cluster_block_size %d.",
+						   ControlFile->blcksz, cluster_block_size),
 				 errhint("It looks like you need to recompile or initdb.")));
 	if (ControlFile->relseg_size != RELSEG_SIZE)
 		ereport(FATAL,
@@ -4121,19 +4121,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 != cluster_toast_max_chunk_size)
 		ereport(FATAL,
 				(errmsg("database files are incompatible with server"),
-				 errdetail("The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d,"
-						   " but the server was compiled with TOAST_MAX_CHUNK_SIZE %d.",
-						   ControlFile->toast_max_chunk_size, (int) TOAST_MAX_CHUNK_SIZE),
+				 errdetail("The database cluster was initialized with cluster_toast_max_chunk_size %d,"
+						   " but the server was compiled with cluster_toast_max_chunk_size %d.",
+						   ControlFile->toast_max_chunk_size, (int) cluster_toast_max_chunk_size),
 				 errhint("It looks like you need to recompile or initdb.")));
-	if (ControlFile->loblksize != LOBLKSIZE)
+	if (ControlFile->loblksize != cluster_loblksize)
 		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),
+				 errdetail("The database cluster was initialized with cluster_loblksize %d,"
+						   " but the server was compiled with cluster_loblksize %d.",
+						   ControlFile->loblksize, (int) cluster_loblksize),
 				 errhint("It looks like you need to recompile or initdb.")));
 
 #ifdef USE_FLOAT8_BYVAL
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c
index 258cbd7035..87e04d6e03 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -46,18 +46,18 @@
  * backup block image.
  */
 #ifdef USE_LZ4
-#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(BLCKSZ)
+#define	LZ4_MAX_BLCKSZ		LZ4_COMPRESSBOUND(cluster_block_size)
 #else
 #define LZ4_MAX_BLCKSZ		0
 #endif
 
 #ifdef USE_ZSTD
-#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(BLCKSZ)
+#define ZSTD_MAX_BLCKSZ		ZSTD_COMPRESSBOUND(cluster_block_size)
 #else
 #define ZSTD_MAX_BLCKSZ		0
 #endif
 
-#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(BLCKSZ)
+#define PGLZ_MAX_BLCKSZ		PGLZ_MAX_OUTPUT(cluster_block_size)
 
 /* Buffer size required to store a compressed version of backup block image */
 #define COMPRESS_BUFSIZE	Max(Max(PGLZ_MAX_BLCKSZ, LZ4_MAX_BLCKSZ), ZSTD_MAX_BLCKSZ)
@@ -383,7 +383,7 @@ XLogRegisterData(char *data, uint32 len)
  * block_id, the data is appended.
  *
  * The maximum amount of data that can be registered per block is 65535
- * bytes. That should be plenty; if you need more than BLCKSZ bytes to
+ * bytes. That should be plenty; if you need more than cluster_block_size bytes to
  * reconstruct the changes to the page, you might as well just log a full
  * copy of it. (the "main data" that's not associated with a block is not
  * limited)
@@ -650,7 +650,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 
 				if (lower >= SizeOfPageHeaderData &&
 					upper > lower &&
-					upper <= BLCKSZ)
+					upper <= cluster_block_size)
 				{
 					bimg.hole_offset = lower;
 					cbimg.hole_length = upper - lower;
@@ -746,12 +746,12 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 			}
 			else
 			{
-				bimg.length = BLCKSZ - cbimg.hole_length;
+				bimg.length = cluster_block_size - cbimg.hole_length;
 
 				if (cbimg.hole_length == 0)
 				{
 					rdt_datas_last->data = page;
-					rdt_datas_last->len = BLCKSZ;
+					rdt_datas_last->len = cluster_block_size;
 				}
 				else
 				{
@@ -765,7 +765,7 @@ XLogRecordAssemble(RmgrId rmid, uint8 info,
 					rdt_datas_last->data =
 						page + (bimg.hole_offset + cbimg.hole_length);
 					rdt_datas_last->len =
-						BLCKSZ - (bimg.hole_offset + cbimg.hole_length);
+						cluster_block_size - (bimg.hole_offset + cbimg.hole_length);
 				}
 			}
 
@@ -932,7 +932,7 @@ static bool
 XLogCompressBackupBlock(char *page, uint16 hole_offset, uint16 hole_length,
 						char *dest, uint16 *dlen)
 {
-	int32		orig_len = BLCKSZ - hole_length;
+	int32		orig_len = cluster_block_size - hole_length;
 	int32		len = -1;
 	int32		extra_bytes = 0;
 	char	   *source;
@@ -945,7 +945,7 @@ XLogCompressBackupBlock(char *page, uint16 hole_offset, uint16 hole_length,
 		memcpy(source, page, hole_offset);
 		memcpy(source + hole_offset,
 			   page + (hole_offset + hole_length),
-			   BLCKSZ - (hole_length + hole_offset));
+			   cluster_block_size - (hole_length + hole_offset));
 
 		/*
 		 * Extra data needs to be stored in WAL record for the compressed
@@ -1096,10 +1096,10 @@ XLogSaveBufferForHint(Buffer buffer, bool buffer_std)
 			uint16		upper = ((PageHeader) page)->pd_upper;
 
 			memcpy(copied_buffer.data, origdata, lower);
-			memcpy(copied_buffer.data + upper, origdata + upper, BLCKSZ - upper);
+			memcpy(copied_buffer.data + upper, origdata + upper, cluster_block_size - upper);
 		}
 		else
-			memcpy(copied_buffer.data, origdata, BLCKSZ);
+			memcpy(copied_buffer.data, origdata, cluster_block_size);
 
 		XLogBeginInsert();
 
diff --git a/src/backend/access/transam/xlogprefetcher.c b/src/backend/access/transam/xlogprefetcher.c
index 539928cb85..93026d5847 100644
--- a/src/backend/access/transam/xlogprefetcher.c
+++ b/src/backend/access/transam/xlogprefetcher.c
@@ -51,7 +51,7 @@
  * Every time we process this much WAL, we'll update the values in
  * pg_stat_recovery_prefetch.
  */
-#define XLOGPREFETCHER_STATS_DISTANCE BLCKSZ
+#define xlogprefetcher_stats_distance cluster_block_size
 
 /*
  * To detect repeated access to the same block and skip useless extra system
@@ -442,7 +442,7 @@ XLogPrefetcherComputeStats(XLogPrefetcher *prefetcher)
 	SharedStats->wal_distance = wal_distance;
 
 	prefetcher->next_stats_shm_lsn =
-		prefetcher->reader->ReadRecPtr + XLOGPREFETCHER_STATS_DISTANCE;
+		prefetcher->reader->ReadRecPtr + xlogprefetcher_stats_distance;
 }
 
 /*
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index a17263df20..8e3ae6aea6 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -189,7 +189,7 @@ XLogReaderFree(XLogReaderState *state)
  * readRecordBufSize is set to the new buffer size.
  *
  * To avoid useless small increases, round its size to a multiple of
- * XLOG_BLCKSZ, and make sure it's at least 5*Max(BLCKSZ, XLOG_BLCKSZ) to start
+ * XLOG_BLCKSZ, and make sure it's at least 5*Max(cluster_block_size, XLOG_BLCKSZ) to start
  * with.  (That is enough for all "normal" records, but very large commit or
  * abort records might need more space.)
  *
@@ -202,7 +202,7 @@ allocate_recordbuf(XLogReaderState *state, uint32 reclength)
 	uint32		newSize = reclength;
 
 	newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
-	newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ));
+	newSize = Max(newSize, 5 * Max(cluster_block_size, XLOG_BLCKSZ));
 
 	if (state->readRecordBuf)
 		pfree(state->readRecordBuf);
@@ -1808,17 +1808,17 @@ DecodeXLogRecord(XLogReaderState *state,
 						blk->hole_length = 0;
 				}
 				else
-					blk->hole_length = BLCKSZ - blk->bimg_len;
+					blk->hole_length = cluster_block_size - blk->bimg_len;
 				datatotal += blk->bimg_len;
 
 				/*
 				 * cross-check that hole_offset > 0, hole_length > 0 and
-				 * bimg_len < BLCKSZ if the HAS_HOLE flag is set.
+				 * bimg_len < cluster_block_size if the HAS_HOLE flag is set.
 				 */
 				if ((blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
 					(blk->hole_offset == 0 ||
 					 blk->hole_length == 0 ||
-					 blk->bimg_len == BLCKSZ))
+					 blk->bimg_len == cluster_block_size))
 				{
 					report_invalid_record(state,
 										  "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X",
@@ -1845,10 +1845,10 @@ DecodeXLogRecord(XLogReaderState *state,
 				}
 
 				/*
-				 * Cross-check that bimg_len < BLCKSZ if it is compressed.
+				 * Cross-check that bimg_len < cluster_block_size if it is compressed.
 				 */
 				if (BKPIMAGE_COMPRESSED(blk->bimg_info) &&
-					blk->bimg_len == BLCKSZ)
+					blk->bimg_len == cluster_block_size)
 				{
 					report_invalid_record(state,
 										  "BKPIMAGE_COMPRESSED set, but block image length %u at %X/%X",
@@ -1858,12 +1858,12 @@ DecodeXLogRecord(XLogReaderState *state,
 				}
 
 				/*
-				 * cross-check that bimg_len = BLCKSZ if neither HAS_HOLE is
+				 * cross-check that bimg_len = cluster_block_size if neither HAS_HOLE is
 				 * set nor COMPRESSED().
 				 */
 				if (!(blk->bimg_info & BKPIMAGE_HAS_HOLE) &&
 					!BKPIMAGE_COMPRESSED(blk->bimg_info) &&
-					blk->bimg_len != BLCKSZ)
+					blk->bimg_len != cluster_block_size)
 				{
 					report_invalid_record(state,
 										  "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_COMPRESSED set, but block image length is %u at %X/%X",
@@ -2095,14 +2095,14 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		if ((bkpb->bimg_info & BKPIMAGE_COMPRESS_PGLZ) != 0)
 		{
 			if (pglz_decompress(ptr, bkpb->bimg_len, tmp.data,
-								BLCKSZ - bkpb->hole_length, true) < 0)
+								cluster_block_size - bkpb->hole_length, true) < 0)
 				decomp_success = false;
 		}
 		else if ((bkpb->bimg_info & BKPIMAGE_COMPRESS_LZ4) != 0)
 		{
 #ifdef USE_LZ4
 			if (LZ4_decompress_safe(ptr, tmp.data,
-									bkpb->bimg_len, BLCKSZ - bkpb->hole_length) <= 0)
+									bkpb->bimg_len, cluster_block_size - bkpb->hole_length) <= 0)
 				decomp_success = false;
 #else
 			report_invalid_record(record, "could not restore image at %X/%X compressed with %s not supported by build, block %d",
@@ -2116,7 +2116,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		{
 #ifdef USE_ZSTD
 			size_t		decomp_result = ZSTD_decompress(tmp.data,
-														BLCKSZ - bkpb->hole_length,
+														cluster_block_size - bkpb->hole_length,
 														ptr, bkpb->bimg_len);
 
 			if (ZSTD_isError(decomp_result))
@@ -2151,7 +2151,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 	/* generate page, taking into account hole if necessary */
 	if (bkpb->hole_length == 0)
 	{
-		memcpy(page, ptr, BLCKSZ);
+		memcpy(page, ptr, cluster_block_size);
 	}
 	else
 	{
@@ -2160,7 +2160,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
 		MemSet(page + bkpb->hole_offset, 0, bkpb->hole_length);
 		memcpy(page + (bkpb->hole_offset + bkpb->hole_length),
 			   ptr + bkpb->hole_offset,
-			   BLCKSZ - (bkpb->hole_offset + bkpb->hole_length));
+			   cluster_block_size - (bkpb->hole_offset + bkpb->hole_length));
 	}
 
 	return true;
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index becc2bda62..0ac856c945 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -293,7 +293,7 @@ static bool backupEndRequired = false;
  */
 bool		reachedConsistency = false;
 
-/* Buffers dedicated to consistency checks of size BLCKSZ */
+/* Buffers dedicated to consistency checks of size cluster_block_size */
 static char *replay_image_masked = NULL;
 static char *primary_image_masked = NULL;
 
@@ -606,8 +606,8 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
 	 * (2) a static char array isn't guaranteed to have any particular
 	 * alignment, whereas palloc() will provide MAXALIGN'd storage.
 	 */
-	replay_image_masked = (char *) palloc(BLCKSZ);
-	primary_image_masked = (char *) palloc(BLCKSZ);
+	replay_image_masked = (char *) palloc(cluster_block_size);
+	primary_image_masked = (char *) palloc(cluster_block_size);
 
 	if (read_backup_label(&CheckPointLoc, &CheckPointTLI, &backupEndRequired,
 						  &backupFromStandby))
@@ -2432,7 +2432,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 		 * Take a copy of the local page where WAL has been applied to have a
 		 * comparison base before masking it...
 		 */
-		memcpy(replay_image_masked, page, BLCKSZ);
+		memcpy(replay_image_masked, page, cluster_block_size);
 
 		/* No need for this page anymore now that a copy is in. */
 		UnlockReleaseBuffer(buf);
@@ -2467,7 +2467,7 @@ verifyBackupPageConsistency(XLogReaderState *record)
 		}
 
 		/* Time to compare the primary and replay images. */
-		if (memcmp(replay_image_masked, primary_image_masked, BLCKSZ) != 0)
+		if (memcmp(replay_image_masked, primary_image_masked, cluster_block_size) != 0)
 		{
 			elog(FATAL,
 				 "inconsistent page found, rel %u/%u/%u, forknum %u, blkno %u",
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 5d66014499..627ae9e659 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -55,7 +55,7 @@
  * NB: The buffer size is required to be a multiple of the system block
  * size, so use that value instead if it's bigger than our preference.
  */
-#define SINK_BUFFER_LENGTH			Max(32768, BLCKSZ)
+#define sink_buffer_length			Max(32768, cluster_block_size)
 
 typedef struct
 {
@@ -306,7 +306,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 		}
 
 		/* notify basebackup sink about start of backup */
-		bbsink_begin_backup(sink, &state, SINK_BUFFER_LENGTH);
+		bbsink_begin_backup(sink, &state, sink_buffer_length);
 
 		/* Send off our tablespaces one by one */
 		foreach(lc, state.tablespaces)
@@ -370,8 +370,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 			else
 			{
 				/* Properly terminate the tarfile. */
-				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= BLCKSZ,
-								 "BLCKSZ too small for 2 tar blocks");
+				StaticAssertDecl(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+								 "cluster_block_size too small for 2 tar blocks");
 				memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 				bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
 
@@ -623,8 +623,8 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
 		}
 
 		/* Properly terminate the tar file. */
-		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= BLCKSZ,
-						 "BLCKSZ too small for 2 tar blocks");
+		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= cluster_block_size,
+						 "cluster_block_size too small for 2 tar blocks");
 		memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
 		bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
 
@@ -1562,27 +1562,27 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 
 		/*
 		 * The checksums are verified at block level, so we iterate over the
-		 * buffer in chunks of BLCKSZ, after making sure that
-		 * TAR_SEND_SIZE/buf is divisible by BLCKSZ and we read a multiple of
-		 * BLCKSZ bytes.
+		 * buffer in chunks of cluster_block_size, after making sure that
+		 * TAR_SEND_SIZE/buf is divisible by cluster_block_size and we read a multiple of
+		 * cluster_block_size bytes.
 		 */
-		Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
+		Assert((sink->bbs_buffer_length % cluster_block_size) == 0);
 
-		if (verify_checksum && (cnt % BLCKSZ != 0))
+		if (verify_checksum && (cnt % cluster_block_size != 0))
 		{
 			ereport(WARNING,
 					(errmsg("could not verify checksum in file \"%s\", block "
 							"%u: read buffer size %d and page size %d "
 							"differ",
-							readfilename, blkno, (int) cnt, BLCKSZ)));
+							readfilename, blkno, (int) cnt, cluster_block_size)));
 			verify_checksum = false;
 		}
 
 		if (verify_checksum)
 		{
-			for (i = 0; i < cnt / BLCKSZ; i++)
+			for (i = 0; i < cnt / cluster_block_size; i++)
 			{
-				page = sink->bbs_buffer + BLCKSZ * i;
+				page = sink->bbs_buffer + cluster_block_size * i;
 
 				/*
 				 * Only check pages which have not been modified since the
@@ -1625,8 +1625,8 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 							/* Reread the failed block */
 							reread_cnt =
 								basebackup_read_file(fd,
-													 sink->bbs_buffer + BLCKSZ * i,
-													 BLCKSZ, len + BLCKSZ * i,
+													 sink->bbs_buffer + cluster_block_size * i,
+													 cluster_block_size, len + cluster_block_size * i,
 													 readfilename,
 													 false);
 							if (reread_cnt == 0)
@@ -1639,7 +1639,7 @@ sendFile(bbsink *sink, const char *readfilename, const char *tarfilename,
 								 * code that handles that case. (We must fix
 								 * up cnt first, though.)
 								 */
-								cnt = BLCKSZ * i;
+								cnt = cluster_block_size * i;
 								break;
 							}
 
@@ -1745,12 +1745,12 @@ _tarWriteHeader(bbsink *sink, const char *filename, const char *linktarget,
 		/*
 		 * As of this writing, the smallest supported block size is 1kB, which
 		 * is twice TAR_BLOCK_SIZE. Since the buffer size is required to be a
-		 * multiple of BLCKSZ, it should be safe to assume that the buffer is
+		 * multiple of cluster_block_size, it should be safe to assume that the buffer is
 		 * large enough to fit an entire tar block. We double-check by means
 		 * of these assertions.
 		 */
-		StaticAssertDecl(TAR_BLOCK_SIZE <= BLCKSZ,
-						 "BLCKSZ too small for tar block");
+		StaticAssertDecl(TAR_BLOCK_SIZE <= cluster_block_size,
+						 "cluster_block_size too small for tar block");
 		Assert(sink->bbs_buffer_length >= TAR_BLOCK_SIZE);
 
 		rc = tarCreateHeader(sink->bbs_buffer, filename, linktarget,
diff --git a/src/backend/backup/basebackup_lz4.c b/src/backend/backup/basebackup_lz4.c
index 7acb606564..25f23bd8d3 100644
--- a/src/backend/backup/basebackup_lz4.c
+++ b/src/backend/backup/basebackup_lz4.c
@@ -117,10 +117,10 @@ bbsink_lz4_begin_backup(bbsink *sink)
 											 &mysink->prefs);
 
 	/*
-	 * The buffer length is expected to be a multiple of BLCKSZ, so round up.
+	 * The buffer length is expected to be a multiple of cluster_block_size, so round up.
 	 */
-	output_buffer_bound = output_buffer_bound + BLCKSZ -
-		(output_buffer_bound % BLCKSZ);
+	output_buffer_bound = output_buffer_bound + cluster_block_size -
+		(output_buffer_bound % cluster_block_size);
 
 	bbsink_begin_backup(sink->bbs_next, sink->bbs_state, output_buffer_bound);
 }
diff --git a/src/backend/backup/basebackup_zstd.c b/src/backend/backup/basebackup_zstd.c
index 6a078cdf48..62d73e4b22 100644
--- a/src/backend/backup/basebackup_zstd.c
+++ b/src/backend/backup/basebackup_zstd.c
@@ -143,10 +143,10 @@ bbsink_zstd_begin_backup(bbsink *sink)
 	output_buffer_bound = ZSTD_compressBound(mysink->base.bbs_buffer_length);
 
 	/*
-	 * The buffer length is expected to be a multiple of BLCKSZ, so round up.
+	 * The buffer length is expected to be a multiple of cluster_block_size, so round up.
 	 */
-	output_buffer_bound = output_buffer_bound + BLCKSZ -
-		(output_buffer_bound % BLCKSZ);
+	output_buffer_bound = output_buffer_bound + cluster_block_size -
+		(output_buffer_bound % cluster_block_size);
 
 	bbsink_begin_backup(sink->bbs_next, sink->bbs_state, output_buffer_bound);
 }
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c
index 93f07e49b7..59cc830dd9 100644
--- a/src/backend/catalog/storage.c
+++ b/src/backend/catalog/storage.c
@@ -800,7 +800,7 @@ smgrDoPendingSyncs(bool isCommit, bool isParallelWorker)
 		 * main fork is longer than ever but FSM fork gets shorter.
 		 */
 		if (pendingsync->is_truncated ||
-			total_blocks * BLCKSZ / 1024 >= wal_skip_threshold)
+			total_blocks * cluster_block_size / 1024 >= wal_skip_threshold)
 		{
 			/* allocate the initial array, or extend it, if needed */
 			if (maxrels == 0)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index bfd981aa3f..871101e550 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -772,9 +772,9 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 
 			if (delay_in_ms > 0)
 			{
-				read_rate = (double) BLCKSZ * AnalyzePageMiss / (1024 * 1024) /
+				read_rate = (double) cluster_block_size * AnalyzePageMiss / (1024 * 1024) /
 					(delay_in_ms / 1000.0);
-				write_rate = (double) BLCKSZ * AnalyzePageDirty / (1024 * 1024) /
+				write_rate = (double) cluster_block_size * AnalyzePageDirty / (1024 * 1024) /
 					(delay_in_ms / 1000.0);
 			}
 
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index d148d10850..c7d19f4ee0 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -163,7 +163,7 @@
  * than that, so changes in that data structure won't affect user-visible
  * restrictions.
  */
-#define NOTIFY_PAYLOAD_MAX_LENGTH	(BLCKSZ - NAMEDATALEN - 128)
+#define NOTIFY_PAYLOAD_MAX_LENGTH	(cluster_block_size - NAMEDATALEN - 128)
 
 /*
  * Struct representing an entry in the global notify queue
@@ -311,7 +311,7 @@ static AsyncQueueControl *asyncQueueControl;
 static SlruCtlData NotifyCtlData;
 
 #define NotifyCtl					(&NotifyCtlData)
-#define QUEUE_PAGESIZE				BLCKSZ
+#define QUEUE_PAGESIZE				cluster_block_size
 #define QUEUE_FULL_WARN_INTERVAL	5000	/* warn at most once every 5s */
 
 /*
@@ -322,7 +322,7 @@ static SlruCtlData NotifyCtlData;
  *
  * The most data we can have in the queue at a time is QUEUE_MAX_PAGE/2
  * pages, because more than that would confuse slru.c into thinking there
- * was a wraparound condition.  With the default BLCKSZ this means there
+ * was a wraparound condition.  With the default cluster_block_size this means there
  * can be up to 8GB of queued-and-not-read data.
  *
  * Note: it's possible to redefine QUEUE_MAX_PAGE with a smaller multiple of
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index 351ab4957a..1263603df9 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -1036,7 +1036,7 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
 
 	/* Each parallel VACUUM worker gets its own access strategy. */
 	pvs.bstrategy = GetAccessStrategyWithSize(BAS_VACUUM,
-											  shared->ring_nbuffers * (BLCKSZ / 1024));
+											  shared->ring_nbuffers * (cluster_block_size / 1024));
 
 	/* Setup error traceback support for ereport() */
 	errcallback.callback = parallel_vacuum_error_callback;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index f154f28902..d1c36bfff9 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -216,8 +216,8 @@
  *
  *	  Tapes' buffers can take up substantial memory when many tapes are open at
  *	  once. We only need one tape open at a time in read mode (using a buffer
- *	  that's a multiple of BLCKSZ); but we need one tape open in write mode (each
- *	  requiring a buffer of size BLCKSZ) for each partition.
+ *	  that's a multiple of cluster_block_size); but we need one tape open in write mode (each
+ *	  requiring a buffer of size cluster_block_size) for each partition.
  *
  *	  Note that it's possible for transition states to start small but then
  *	  grow very large; for instance in the case of ARRAY_AGG. In such cases,
@@ -299,12 +299,12 @@
 
 /*
  * For reading from tapes, the buffer size must be a multiple of
- * BLCKSZ. Larger values help when reading from multiple tapes concurrently,
- * but that doesn't happen in HashAgg, so we simply use BLCKSZ. Writing to a
- * tape always uses a buffer of size BLCKSZ.
+ * cluster_block_size. Larger values help when reading from multiple tapes concurrently,
+ * but that doesn't happen in HashAgg, so we simply use cluster_block_size. Writing to a
+ * tape always uses a buffer of size cluster_block_size.
  */
-#define HASHAGG_READ_BUFFER_SIZE BLCKSZ
-#define HASHAGG_WRITE_BUFFER_SIZE BLCKSZ
+#define hashagg_read_buffer_size cluster_block_size
+#define hashagg_write_buffer_size cluster_block_size
 
 /*
  * HyperLogLog is used for estimating the cardinality of the spilled tuples in
@@ -1827,8 +1827,8 @@ hash_agg_set_limits(double hashentrysize, double input_groups, int used_bits,
 		*num_partitions = npartitions;
 
 	partition_mem =
-		HASHAGG_READ_BUFFER_SIZE +
-		HASHAGG_WRITE_BUFFER_SIZE * npartitions;
+		hashagg_read_buffer_size +
+		hashagg_write_buffer_size * npartitions;
 
 	/*
 	 * Don't set the limit below 3/4 of hash_mem. In that case, we are at the
@@ -1933,9 +1933,9 @@ hash_agg_update_metrics(AggState *aggstate, bool from_tape, int npartitions)
 	hashkey_mem = MemoryContextMemAllocated(aggstate->hashcontext->ecxt_per_tuple_memory, true);
 
 	/* memory for read/write tape buffers, if spilled */
-	buffer_mem = npartitions * HASHAGG_WRITE_BUFFER_SIZE;
+	buffer_mem = npartitions * hashagg_write_buffer_size;
 	if (from_tape)
-		buffer_mem += HASHAGG_READ_BUFFER_SIZE;
+		buffer_mem += hashagg_read_buffer_size;
 
 	/* update peak mem */
 	total_mem = meta_mem + hashkey_mem + buffer_mem;
@@ -1945,7 +1945,7 @@ hash_agg_update_metrics(AggState *aggstate, bool from_tape, int npartitions)
 	/* update disk usage */
 	if (aggstate->hash_tapeset != NULL)
 	{
-		uint64		disk_used = LogicalTapeSetBlocks(aggstate->hash_tapeset) * (BLCKSZ / 1024);
+		uint64		disk_used = LogicalTapeSetBlocks(aggstate->hash_tapeset) * (cluster_block_size / 1024);
 
 		if (aggstate->hash_disk_used < disk_used)
 			aggstate->hash_disk_used = disk_used;
@@ -2004,8 +2004,8 @@ hash_choose_num_partitions(double input_groups, double hashentrysize,
 	 * open partition files are greater than 1/4 of hash_mem.
 	 */
 	partition_limit =
-		(hash_mem_limit * 0.25 - HASHAGG_READ_BUFFER_SIZE) /
-		HASHAGG_WRITE_BUFFER_SIZE;
+		(hash_mem_limit * 0.25 - hashagg_read_buffer_size) /
+		hashagg_write_buffer_size;
 
 	mem_wanted = HASHAGG_PARTITION_FACTOR * input_groups * hashentrysize;
 
@@ -3113,7 +3113,7 @@ hashagg_spill_finish(AggState *aggstate, HashAggSpill *spill, int setno)
 		freeHyperLogLog(&spill->hll_card[i]);
 
 		/* rewinding frees the buffer while not in use */
-		LogicalTapeRewindForRead(tape, HASHAGG_READ_BUFFER_SIZE);
+		LogicalTapeRewindForRead(tape, hashagg_read_buffer_size);
 
 		new_batch = hashagg_batch_new(tape, setno,
 									  spill->ntuples[i], cardinality,
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index 29a1858441..971c29c428 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -13,7 +13,7 @@
  * fact that a particular page needs to be visited.
  *
  * The "lossy" storage uses one bit per disk page, so at the standard 8K
- * BLCKSZ, we can represent all pages in 64Gb of disk space in about 1Mb
+ * cluster_block_size, we can represent all pages in 64Gb of disk space in about 1Mb
  * of memory.  People pushing around tables of that size should have a
  * couple of Mb to spare, so we don't worry about providing a second level
  * of lossiness.  In theory we could fall back to page ranges at some
@@ -70,7 +70,7 @@
  * too different.  But we also want PAGES_PER_CHUNK to be a power of 2 to
  * avoid expensive integer remainder operations.  So, define it like this:
  */
-#define PAGES_PER_CHUNK  (BLCKSZ / 32)
+#define PAGES_PER_CHUNK  (cluster_block_size / 32)
 
 /* We use BITS_PER_BITMAPWORD and typedef bitmapword from nodes/bitmapset.h */
 
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index d6ceafd51c..2a66cdd355 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1890,7 +1890,7 @@ cost_tuplesort(Cost *startup_cost, Cost *run_cost,
 		/*
 		 * We'll have to use a disk-based sort of all the tuples
 		 */
-		double		npages = ceil(input_bytes / BLCKSZ);
+		double		npages = ceil(input_bytes / cluster_block_size);
 		double		nruns = input_bytes / sort_mem_bytes;
 		double		mergeorder = tuplesort_merge_order(sort_mem_bytes);
 		double		log_runs;
@@ -2455,7 +2455,7 @@ cost_material(Path *path,
 	 */
 	if (nbytes > work_mem_bytes)
 	{
-		double		npages = ceil(nbytes / BLCKSZ);
+		double		npages = ceil(nbytes / cluster_block_size);
 
 		run_cost += seq_page_cost * npages;
 	}
@@ -2764,7 +2764,7 @@ cost_agg(Path *path, PlannerInfo *root,
 		 * Estimate number of pages read and written. For each level of
 		 * recursion, a tuple must be written and then later read.
 		 */
-		pages = relation_byte_size(input_tuples, input_width) / BLCKSZ;
+		pages = relation_byte_size(input_tuples, input_width) / cluster_block_size;
 		pages_written = pages_read = pages * depth;
 
 		/*
@@ -4551,7 +4551,7 @@ cost_rescan(PlannerInfo *root, Path *path,
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
-					double		npages = ceil(nbytes / BLCKSZ);
+					double		npages = ceil(nbytes / cluster_block_size);
 
 					run_cost += seq_page_cost * npages;
 				}
@@ -4578,7 +4578,7 @@ cost_rescan(PlannerInfo *root, Path *path,
 				if (nbytes > work_mem_bytes)
 				{
 					/* It will spill, so account for re-read cost */
-					double		npages = ceil(nbytes / BLCKSZ);
+					double		npages = ceil(nbytes / cluster_block_size);
 
 					run_cost += seq_page_cost * npages;
 				}
@@ -6356,7 +6356,7 @@ relation_byte_size(double tuples, int width)
 static double
 page_size(double tuples, int width)
 {
-	return ceil(relation_byte_size(tuples, width) / BLCKSZ);
+	return ceil(relation_byte_size(tuples, width) / cluster_block_size);
 }
 
 /*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 243c8fb1e4..cfe03dedff 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1089,7 +1089,7 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
 			tuple_width += MAXALIGN(SizeofHeapTupleHeader);
 			tuple_width += sizeof(ItemIdData);
 			/* note: integer division is intentional here */
-			density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
+			density = (cluster_block_size - SizeOfPageHeaderData) / tuple_width;
 		}
 		*tuples = rint(density * (double) curpages);
 
diff --git a/src/backend/po/de.po b/src/backend/po/de.po
index 0a9e668c38..ac2cb6e4cf 100644
--- a/src/backend/po/de.po
+++ b/src/backend/po/de.po
@@ -2329,8 +2329,8 @@ msgstr "Der Datenbank-Cluster verwendet anscheinend ein anderes Fließkommazahle
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Der Datenbank-Cluster wurde mit BLCKSZ %d initialisiert, aber der Server wurde mit BLCKSZ %d kompiliert."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Der Datenbank-Cluster wurde mit cluster_block_size %d initialisiert, aber der Server wurde mit cluster_block_size %d kompiliert."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -2363,13 +2363,13 @@ msgstr "Der Datenbank-Cluster wurde mit INDEX_MAX_KEYS %d initialisiert, aber de
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Der Datenbank-Cluster wurde mit TOAST_MAX_CHUNK_SIZE %d initialisiert, aber der Server wurde mit TOAST_MAX_CHUNK_SIZE %d kompiliert."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Der Datenbank-Cluster wurde mit cluster_toast_max_chunk_size %d initialisiert, aber der Server wurde mit cluster_toast_max_chunk_size %d kompiliert."
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Der Datenbank-Cluster wurde mit LOBLKSIZE %d initialisiert, aber der Server wurde mit LOBLKSIZE %d kompiliert."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Der Datenbank-Cluster wurde mit cluster_loblksize %d initialisiert, aber der Server wurde mit cluster_loblksize %d kompiliert."
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -22029,8 +22029,8 @@ msgstr "debug_io_direct wird für WAL nicht unterstützt, weil XLOG_BLCKSZ zu kl
 
 #: storage/file/fd.c:3951
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
-msgstr "debug_io_direct wird für Daten nicht unterstützt, weil BLCKSZ zu klein ist"
+msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
+msgstr "debug_io_direct wird für Daten nicht unterstützt, weil cluster_block_size zu klein ist"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/po/es.po b/src/backend/po/es.po
index e50a935033..b420226eb9 100644
--- a/src/backend/po/es.po
+++ b/src/backend/po/es.po
@@ -2387,8 +2387,8 @@ msgstr "Los archivos de la base de datos parecen usar un formato de número de c
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Los archivos de base de datos fueron inicializados con BLCKSZ %d, pero el servidor fue compilado con BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Los archivos de base de datos fueron inicializados con cluster_block_size %d, pero el servidor fue compilado con cluster_block_size %d."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -2421,13 +2421,13 @@ msgstr "Los archivos de la base de datos fueron inicializados con INDEX_MAX_KEYS
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Los archivos de la base de datos fueron inicializados con TOAST_MAX_CHUNK_SIZE %d, pero el servidor fue compilado con TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Los archivos de la base de datos fueron inicializados con cluster_toast_max_chunk_size %d, pero el servidor fue compilado con cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Los archivos de base de datos fueron inicializados con LOBLKSIZE %d, pero el servidor fue compilado con LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Los archivos de base de datos fueron inicializados con cluster_loblksize %d, pero el servidor fue compilado con cluster_loblksize %d."
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -21808,7 +21808,7 @@ msgstr ""
 
 #: storage/file/fd.c:3901
 #, c-format
-msgid "io_direct is not supported for data because BLCKSZ is too small"
+msgid "io_direct is not supported for data because cluster_block_size is too small"
 msgstr ""
 
 #: storage/file/reinit.c:145
diff --git a/src/backend/po/fr.po b/src/backend/po/fr.po
index fd51500b93..2b73cd3fdc 100644
--- a/src/backend/po/fr.po
+++ b/src/backend/po/fr.po
@@ -2380,10 +2380,10 @@ msgstr ""
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
 msgstr ""
-"Le cluster de base de données a été initialisé avec un BLCKSZ à %d alors que\n"
-"le serveur a été compilé avec un BLCKSZ à %d."
+"Le cluster de base de données a été initialisé avec un cluster_block_size à %d alors que\n"
+"le serveur a été compilé avec un cluster_block_size à %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
@@ -2424,17 +2424,17 @@ msgstr ""
 
 #: access/transam/xlog.c:4131
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
 msgstr ""
-"Le cluster de bases de données a été initialisé avec un TOAST_MAX_CHUNK_SIZE\n"
-"à %d alors que le serveur a été compilé avec un TOAST_MAX_CHUNK_SIZE à %d."
+"Le cluster de bases de données a été initialisé avec un cluster_toast_max_chunk_size\n"
+"à %d alors que le serveur a été compilé avec un cluster_toast_max_chunk_size à %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
 msgstr ""
-"Le cluster de base de données a été initialisé avec un LOBLKSIZE à %d alors que\n"
-"le serveur a été compilé avec un LOBLKSIZE à %d."
+"Le cluster de base de données a été initialisé avec un cluster_loblksize à %d alors que\n"
+"le serveur a été compilé avec un cluster_loblksize à %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/id.po b/src/backend/po/id.po
index d5d484132b..e503bc4117 100644
--- a/src/backend/po/id.po
+++ b/src/backend/po/id.po
@@ -1089,8 +1089,8 @@ msgstr "cluster database sepertinya menggunakan format nomor floating-point yang
 
 #: access/transam/xlog.c:3648
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "cluster database telah diinisialkan dengan BLCKSZ %d, tapi server telah dikompilasi dengan BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "cluster database telah diinisialkan dengan cluster_block_size %d, tapi server telah dikompilasi dengan cluster_block_size %d."
 
 #: access/transam/xlog.c:3651 access/transam/xlog.c:3658 access/transam/xlog.c:3665 access/transam/xlog.c:3672 access/transam/xlog.c:3679 access/transam/xlog.c:3686 access/transam/xlog.c:3693 access/transam/xlog.c:3701 access/transam/xlog.c:3708 access/transam/xlog.c:3717 access/transam/xlog.c:3724 access/transam/xlog.c:3733 access/transam/xlog.c:3740
 #, c-format
@@ -1124,8 +1124,8 @@ msgstr "cluster database telah diinisialkan dengan INDEX_MAX_KEYS %d, tapi serve
 
 #: access/transam/xlog.c:3690
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "cluster database telah diinisialkan dengan TOAST_MAX_CHUNK_SIZE %d, tapi server telah dikompilasi dengan TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "cluster database telah diinisialkan dengan cluster_toast_max_chunk_size %d, tapi server telah dikompilasi dengan cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:3699
 #, c-format
diff --git a/src/backend/po/it.po b/src/backend/po/it.po
index 673e2aaf00..5cd524e120 100644
--- a/src/backend/po/it.po
+++ b/src/backend/po/it.po
@@ -2067,8 +2067,8 @@ msgstr "Il cluster di database sta usando un formato per i numeri in virgola mob
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Il cluster di database è stato inizializzato con BLCKSZ %d, ma il server è stato compilato con BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Il cluster di database è stato inizializzato con cluster_block_size %d, ma il server è stato compilato con cluster_block_size %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106 access/transam/xlog.c:4113 access/transam/xlog.c:4120 access/transam/xlog.c:4127 access/transam/xlog.c:4134 access/transam/xlog.c:4141 access/transam/xlog.c:4149 access/transam/xlog.c:4156
 #, c-format
@@ -2097,13 +2097,13 @@ msgstr "Il cluster di database è stato inizializzato con INDEX_MAX_KEYS %d, ma
 
 #: access/transam/xlog.c:4131
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Il cluster di database è stato inizializzato con TOAST_MAX_CHUNK_SIZE %d, ma il server è stato compilato con TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Il cluster di database è stato inizializzato con cluster_toast_max_chunk_size %d, ma il server è stato compilato con cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Il cluster di database è stato inizializzato con LOBLKSIZE %d, ma il server è stato compilato con LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Il cluster di database è stato inizializzato con cluster_loblksize %d, ma il server è stato compilato con cluster_loblksize %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/ja.po b/src/backend/po/ja.po
index 1ab9f7f68f..c7a8044f23 100644
--- a/src/backend/po/ja.po
+++ b/src/backend/po/ja.po
@@ -2096,8 +2096,8 @@ msgstr "データベースクラスタはサーバー実行ファイルと異な
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "データベースクラスタは BLCKSZ %d で初期化されましたが、サーバーは BLCKSZ %d でコンパイルされています。"
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "データベースクラスタは cluster_block_size %d で初期化されましたが、サーバーは cluster_block_size %d でコンパイルされています。"
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080 access/transam/xlog.c:4087 access/transam/xlog.c:4094 access/transam/xlog.c:4101 access/transam/xlog.c:4108 access/transam/xlog.c:4115 access/transam/xlog.c:4123 access/transam/xlog.c:4130
 #, c-format
@@ -2126,13 +2126,13 @@ msgstr "データベースクラスタは INDEX_MAX_KEYS %d で初期化され
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "データベースクラスタは TOAST_MAX_CHUNK_SIZE %d で初期化されましたが、サーバーは TOAST_MAX_CHUNK_SIZE %d でコンパイルされています。"
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "データベースクラスタは cluster_toast_max_chunk_size %d で初期化されましたが、サーバーは cluster_toast_max_chunk_size %d でコンパイルされています。"
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "データベースクラスタは LOBLKSIZE %d で初期化されましたが、サーバーは LOBLKSIZE %d でコンパイルされています。"
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "データベースクラスタは cluster_loblksize %d で初期化されましたが、サーバーは cluster_loblksize %d でコンパイルされています。"
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -21403,8 +21403,8 @@ msgstr "XLOG_BLCKSZが小さすぎるためdebug_io_directはWALに対しては
 
 #: storage/file/fd.c:3951
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
-msgstr "BLCKSZが小さすぎるためdebug_io_directはデータに対してサポートされません"
+msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
+msgstr "cluster_block_sizeが小さすぎるためdebug_io_directはデータに対してサポートされません"
 
 #: storage/file/reinit.c:145
 #, c-format
diff --git a/src/backend/po/ko.po b/src/backend/po/ko.po
index f330f3da7e..825dae75cf 100644
--- a/src/backend/po/ko.po
+++ b/src/backend/po/ko.po
@@ -2348,10 +2348,10 @@ msgstr ""
 #: access/transam/xlog.c:4802
 #, c-format
 msgid ""
-"The database cluster was initialized with BLCKSZ %d, but the server was "
-"compiled with BLCKSZ %d."
+"The database cluster was initialized with cluster_block_size %d, but the server was "
+"compiled with cluster_block_size %d."
 msgstr ""
-"이 데이터베이스 클러스터는 BLCKSZ %d (으)로 초기화 되었지만, 서버는 BLCKSZ "
+"이 데이터베이스 클러스터는 cluster_block_size %d (으)로 초기화 되었지만, 서버는 cluster_block_size "
 "%d (으)로 컴파일 되어있습니다."
 
 #: access/transam/xlog.c:4805 access/transam/xlog.c:4812
@@ -2404,20 +2404,20 @@ msgstr ""
 #: access/transam/xlog.c:4837
 #, c-format
 msgid ""
-"The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the "
-"server was compiled with TOAST_MAX_CHUNK_SIZE %d."
+"The database cluster was initialized with cluster_toast_max_chunk_size %d, but the "
+"server was compiled with cluster_toast_max_chunk_size %d."
 msgstr ""
-"데이터베이스 클러스터는 TOAST_MAX_CHUNK_SIZE %d(으)로 초기화되었지만 서버는 "
-"TOAST_MAX_CHUNK_SIZE %d(으)로 컴파일 되었습니다."
+"데이터베이스 클러스터는 cluster_toast_max_chunk_size %d(으)로 초기화되었지만 서버는 "
+"cluster_toast_max_chunk_size %d(으)로 컴파일 되었습니다."
 
 #: access/transam/xlog.c:4844
 #, c-format
 msgid ""
-"The database cluster was initialized with LOBLKSIZE %d, but the server was "
-"compiled with LOBLKSIZE %d."
+"The database cluster was initialized with cluster_loblksize %d, but the server was "
+"compiled with cluster_loblksize %d."
 msgstr ""
-"이 데이터베이스 클러스터는 LOBLKSIZE %d(으)로 초기화 되었지만, 서버는 "
-"LOBLKSIZE %d (으)로 컴파일 되어있습니다."
+"이 데이터베이스 클러스터는 cluster_loblksize %d(으)로 초기화 되었지만, 서버는 "
+"cluster_loblksize %d (으)로 컴파일 되어있습니다."
 
 #: access/transam/xlog.c:4853
 #, c-format
diff --git a/src/backend/po/pl.po b/src/backend/po/pl.po
index 3ac9d0451c..08fdc7a3d5 100644
--- a/src/backend/po/pl.po
+++ b/src/backend/po/pl.po
@@ -1927,8 +1927,8 @@ msgstr "Klaster bazy danych wydaje się używać innego formatu liczb zmiennoprz
 
 #: access/transam/xlog.c:4550
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Klaster bazy danych został zainicjowany z BLCKSZ %d, ale serwer był skompilowany z BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Klaster bazy danych został zainicjowany z cluster_block_size %d, ale serwer był skompilowany z cluster_block_size %d."
 
 #: access/transam/xlog.c:4553 access/transam/xlog.c:4560
 #: access/transam/xlog.c:4567 access/transam/xlog.c:4574
@@ -1967,13 +1967,13 @@ msgstr "Klaster bazy danych został zainicjowany z INDEX_MAX_KEYS %d, ale serwer
 
 #: access/transam/xlog.c:4592
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Klaster bazy danych został zainicjowany z TOAST_MAX_CHUNK_SIZE %d, ale serwer był skompilowany z TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Klaster bazy danych został zainicjowany z cluster_toast_max_chunk_size %d, ale serwer był skompilowany z cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4599
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Klaster bazy danych został zainicjowany z LOBLKSIZE %d, ale serwer był skompilowany z LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Klaster bazy danych został zainicjowany z cluster_loblksize %d, ale serwer był skompilowany z cluster_loblksize %d."
 
 #: access/transam/xlog.c:4608
 #, c-format
diff --git a/src/backend/po/pt_BR.po b/src/backend/po/pt_BR.po
index 37e4a28f07..7e30e2119d 100644
--- a/src/backend/po/pt_BR.po
+++ b/src/backend/po/pt_BR.po
@@ -1264,7 +1264,7 @@ msgstr "O agrupamento de banco de dados parece utilizar um formato de número de
 
 #: access/transam/xlog.c:4506
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
 msgstr "O agrupamento de banco de dados foi inicializado com BLCSZ %d, mas o servidor foi compilado com BLCSZ %d."
 
 #: access/transam/xlog.c:4509 access/transam/xlog.c:4516
@@ -1305,13 +1305,13 @@ msgstr "O agrupamento de banco de dados foi inicializado com INDEX_MAX_KEYS %d,
 
 #: access/transam/xlog.c:4548
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "O agrupamento de banco de dados foi inicializado com TOAST_MAX_CHUNK_SIZE %d, mas o servidor foi compilado com TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "O agrupamento de banco de dados foi inicializado com cluster_toast_max_chunk_size %d, mas o servidor foi compilado com cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4555
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "O agrupamento de banco de dados foi inicializado com LOBLKSIZE %d, mas o servidor foi compilado com LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "O agrupamento de banco de dados foi inicializado com cluster_loblksize %d, mas o servidor foi compilado com cluster_loblksize %d."
 
 #: access/transam/xlog.c:4564
 #, c-format
diff --git a/src/backend/po/ru.po b/src/backend/po/ru.po
index ae9c50eed7..97ee086a7b 100644
--- a/src/backend/po/ru.po
+++ b/src/backend/po/ru.po
@@ -2619,11 +2619,11 @@ msgstr ""
 #: access/transam/xlog.c:4096
 #, c-format
 msgid ""
-"The database cluster was initialized with BLCKSZ %d, but the server was "
-"compiled with BLCKSZ %d."
+"The database cluster was initialized with cluster_block_size %d, but the server was "
+"compiled with cluster_block_size %d."
 msgstr ""
-"Кластер баз данных был инициализирован с BLCKSZ %d, но сервер скомпилирован "
-"с BLCKSZ %d."
+"Кластер баз данных был инициализирован с cluster_block_size %d, но сервер скомпилирован "
+"с cluster_block_size %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
@@ -2673,20 +2673,20 @@ msgstr ""
 #: access/transam/xlog.c:4131
 #, c-format
 msgid ""
-"The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the "
-"server was compiled with TOAST_MAX_CHUNK_SIZE %d."
+"The database cluster was initialized with cluster_toast_max_chunk_size %d, but the "
+"server was compiled with cluster_toast_max_chunk_size %d."
 msgstr ""
-"Кластер баз данных был инициализирован с TOAST_MAX_CHUNK_SIZE %d, но сервер "
-"скомпилирован с TOAST_MAX_CHUNK_SIZE %d."
+"Кластер баз данных был инициализирован с cluster_toast_max_chunk_size %d, но сервер "
+"скомпилирован с cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
 msgid ""
-"The database cluster was initialized with LOBLKSIZE %d, but the server was "
-"compiled with LOBLKSIZE %d."
+"The database cluster was initialized with cluster_loblksize %d, but the server was "
+"compiled with cluster_loblksize %d."
 msgstr ""
-"Кластер баз данных был инициализирован с LOBLKSIZE %d, но сервер "
-"скомпилирован с LOBLKSIZE %d."
+"Кластер баз данных был инициализирован с cluster_loblksize %d, но сервер "
+"скомпилирован с cluster_loblksize %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/sv.po b/src/backend/po/sv.po
index 0da20b6d43..39060c459e 100644
--- a/src/backend/po/sv.po
+++ b/src/backend/po/sv.po
@@ -2345,8 +2345,8 @@ msgstr "Databasklustret verkar använda en annan flyttalsrepresentation än vad
 
 #: access/transam/xlog.c:4070
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Databasklustret initierades med BLCKSZ %d, men servern kompilerades med BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Databasklustret initierades med cluster_block_size %d, men servern kompilerades med cluster_block_size %d."
 
 #: access/transam/xlog.c:4073 access/transam/xlog.c:4080
 #: access/transam/xlog.c:4087 access/transam/xlog.c:4094
@@ -2379,13 +2379,13 @@ msgstr "Databasklustret initierades med INDEX_MAX_KEYS %d, men servern kompilera
 
 #: access/transam/xlog.c:4105
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Databasklustret initierades med TOAST_MAX_CHUNK_SIZE %d, men servern kompilerades med TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Databasklustret initierades med cluster_toast_max_chunk_size %d, men servern kompilerades med cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4112
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Databasklustret initierades med LOBLKSIZE %d, men servern kompilerades med LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Databasklustret initierades med cluster_loblksize %d, men servern kompilerades med cluster_loblksize %d."
 
 #: access/transam/xlog.c:4121
 #, c-format
@@ -22134,7 +22134,7 @@ msgstr ""
 
 #: storage/file/fd.c:3951
 #, c-format
-msgid "debug_io_direct is not supported for data because BLCKSZ is too small"
+msgid "debug_io_direct is not supported for data because cluster_block_size is too small"
 msgstr ""
 
 #: storage/file/reinit.c:145
diff --git a/src/backend/po/tr.po b/src/backend/po/tr.po
index b791e886b9..695ee51478 100644
--- a/src/backend/po/tr.po
+++ b/src/backend/po/tr.po
@@ -1761,8 +1761,8 @@ msgstr "Veritabanı dosyaları, sunucu programından farklı ondalık sayı biç
 
 #: access/transam/xlog.c:4676
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Veritabanı clusteri BLCKSZ %d ile ilklendirilmiştir, ancak sunucu  BLCKSZ %d ile derlenmiştir."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Veritabanı clusteri cluster_block_size %d ile ilklendirilmiştir, ancak sunucu  cluster_block_size %d ile derlenmiştir."
 
 #: access/transam/xlog.c:4679 access/transam/xlog.c:4686 access/transam/xlog.c:4693 access/transam/xlog.c:4700 access/transam/xlog.c:4707 access/transam/xlog.c:4714 access/transam/xlog.c:4721 access/transam/xlog.c:4729 access/transam/xlog.c:4736 access/transam/xlog.c:4745 access/transam/xlog.c:4752
 #, c-format
@@ -1791,13 +1791,13 @@ msgstr "Veritabanı clusteri INDEX_MAX_KEYS %d ile ilklendirilmiştir, ancak sun
 
 #: access/transam/xlog.c:4711
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Veritabanı clusteri TOAST_MAX_CHUNK_SIZE %d ile ilklendirilmiştir, ancak sunucu  TOAST_MAX_CHUNK_SIZE %d ile derlenmiştir."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Veritabanı clusteri cluster_toast_max_chunk_size %d ile ilklendirilmiştir, ancak sunucu  cluster_toast_max_chunk_size %d ile derlenmiştir."
 
 #: access/transam/xlog.c:4718
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Veritabanı clusteri LOBLKSIZE %d ile ilklendirilmiştir, ancak sunucu  LOBLKSIZE %d ile derlenmiştir."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Veritabanı clusteri cluster_loblksize %d ile ilklendirilmiştir, ancak sunucu  cluster_loblksize %d ile derlenmiştir."
 
 #: access/transam/xlog.c:4727
 #, c-format
diff --git a/src/backend/po/uk.po b/src/backend/po/uk.po
index 1095fd9139..abb8139f4d 100644
--- a/src/backend/po/uk.po
+++ b/src/backend/po/uk.po
@@ -2288,8 +2288,8 @@ msgstr "Здається, в кластері баз даних і в прогр
 
 #: access/transam/xlog.c:4096
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "Кластер бази даних було ініціалізовано з BLCKSZ %d,  але сервер було скомпільовано з BLCKSZ %d."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "Кластер бази даних було ініціалізовано з cluster_block_size %d,  але сервер було скомпільовано з cluster_block_size %d."
 
 #: access/transam/xlog.c:4099 access/transam/xlog.c:4106
 #: access/transam/xlog.c:4113 access/transam/xlog.c:4120
@@ -2322,13 +2322,13 @@ msgstr "Кластер бази даних було ініціалізовано
 
 #: access/transam/xlog.c:4131
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "Кластер бази даних було ініціалізовано з TOAST_MAX_CHUNK_SIZE %d, але сервер було скомпільовано з TOAST_MAX_CHUNK_SIZE %d."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "Кластер бази даних було ініціалізовано з cluster_toast_max_chunk_size %d, але сервер було скомпільовано з cluster_toast_max_chunk_size %d."
 
 #: access/transam/xlog.c:4138
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "Кластер бази даних було ініціалізовано з LOBLKSIZE %d, але сервер було скомпільовано з LOBLKSIZE %d."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "Кластер бази даних було ініціалізовано з cluster_loblksize %d, але сервер було скомпільовано з cluster_loblksize %d."
 
 #: access/transam/xlog.c:4147
 #, c-format
diff --git a/src/backend/po/zh_CN.po b/src/backend/po/zh_CN.po
index 574684d775..b576a6e015 100644
--- a/src/backend/po/zh_CN.po
+++ b/src/backend/po/zh_CN.po
@@ -1852,8 +1852,8 @@ msgstr "数据库集群在使用与服务器执行部分不同的浮点数格式
 
 #: access/transam/xlog.c:4677
 #, c-format
-msgid "The database cluster was initialized with BLCKSZ %d, but the server was compiled with BLCKSZ %d."
-msgstr "数据库簇是以 BLCKSZ %d 初始化的, 但是 服务器是以 BLCKSZ %d 编译的."
+msgid "The database cluster was initialized with cluster_block_size %d, but the server was compiled with cluster_block_size %d."
+msgstr "数据库簇是以 cluster_block_size %d 初始化的, 但是 服务器是以 cluster_block_size %d 编译的."
 
 #: access/transam/xlog.c:4680 access/transam/xlog.c:4687
 #: access/transam/xlog.c:4694 access/transam/xlog.c:4701
@@ -1887,13 +1887,13 @@ msgstr "数据库集群是以 INDEX_MAX_KEYS  %d 初始化的, 但是 服务器
 
 #: access/transam/xlog.c:4712
 #, c-format
-msgid "The database cluster was initialized with TOAST_MAX_CHUNK_SIZE %d, but the server was compiled with TOAST_MAX_CHUNK_SIZE %d."
-msgstr "数据库集群是以 TOAST_MAX_CHUNK_SIZE %d 初始化的, 但是 服务器是以 TOAST_MAX_CHUNK_SIZE %d 编译的."
+msgid "The database cluster was initialized with cluster_toast_max_chunk_size %d, but the server was compiled with cluster_toast_max_chunk_size %d."
+msgstr "数据库集群是以 cluster_toast_max_chunk_size %d 初始化的, 但是 服务器是以 cluster_toast_max_chunk_size %d 编译的."
 
 #: access/transam/xlog.c:4719
 #, c-format
-msgid "The database cluster was initialized with LOBLKSIZE %d, but the server was compiled with LOBLKSIZE %d."
-msgstr "数据库簇是以 LOBLKSIZE %d 初始化的, 但是 服务器是以 LOBLKSIZE %d 编译的."
+msgid "The database cluster was initialized with cluster_loblksize %d, but the server was compiled with cluster_loblksize %d."
+msgstr "数据库簇是以 cluster_loblksize %d 初始化的, 但是 服务器是以 cluster_loblksize %d 编译的."
 
 #: access/transam/xlog.c:4728
 #, c-format
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 597947410f..2653f68363 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -2056,7 +2056,7 @@ apply_spooled_messages(FileSet *stream_fileset, TransactionId xid,
 
 	CurrentResourceOwner = oldowner;
 
-	buffer = palloc(BLCKSZ);
+	buffer = palloc(cluster_block_size);
 	initStringInfo(&s2);
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index 0057443f0c..69f21908e3 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -82,7 +82,7 @@ InitBufferPool(void)
 	BufferBlocks = (char *)
 		TYPEALIGN(PG_IO_ALIGN_SIZE,
 				  ShmemInitStruct("Buffer Blocks",
-								  NBuffers * (Size) BLCKSZ + PG_IO_ALIGN_SIZE,
+								  NBuffers * (Size) cluster_block_size + PG_IO_ALIGN_SIZE,
 								  &foundBufs));
 
 	/* Align condition variables to cacheline boundary. */
@@ -168,7 +168,7 @@ BufferShmemSize(void)
 
 	/* size of data pages, plus alignment padding */
 	size = add_size(size, PG_IO_ALIGN_SIZE);
-	size = add_size(size, mul_size(NBuffers, BLCKSZ));
+	size = add_size(size, mul_size(NBuffers, cluster_block_size));
 
 	/* size of stuff controlled by freelist.c */
 	size = add_size(size, StrategyShmemSize());
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 8b96759b84..012de6481c 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -60,7 +60,7 @@
 
 
 /* Note: these two macros only work on shared buffers, not local ones! */
-#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * BLCKSZ))
+#define BufHdrGetBlock(bufHdr)	((Block) (BufferBlocks + ((Size) (bufHdr)->buf_id) * cluster_block_size))
 #define BufferGetLSN(bufHdr)	(PageGetLSN(BufHdrGetBlock(bufHdr)))
 
 /* Note: this macro only works on local buffers, not shared ones! */
@@ -1116,7 +1116,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	 * wants us to allocate a buffer.
 	 */
 	if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
-		MemSet((char *) bufBlock, 0, BLCKSZ);
+		MemSet((char *) bufBlock, 0, cluster_block_size);
 	else
 	{
 		instr_time	io_start = pgstat_prepare_io_time();
@@ -1137,7 +1137,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 						 errmsg("invalid page in block %u of relation %s; zeroing out page",
 								blockNum,
 								relpath(smgr->smgr_rlocator, forkNum))));
-				MemSet((char *) bufBlock, 0, BLCKSZ);
+				MemSet((char *) bufBlock, 0, cluster_block_size);
 			}
 			else
 				ereport(ERROR,
@@ -1856,7 +1856,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
 		buf_block = BufHdrGetBlock(GetBufferDescriptor(buffers[i] - 1));
 
 		/* new buffers are zero-filled */
-		MemSet((char *) buf_block, 0, BLCKSZ);
+		MemSet((char *) buf_block, 0, cluster_block_size);
 	}
 
 	/* in case we need to pin an existing buffer below */
@@ -2285,7 +2285,7 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy)
 				 * not generally guaranteed to be marked undefined or
 				 * non-accessible in any case.
 				 */
-				VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
+				VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), cluster_block_size);
 				break;
 			}
 		}
@@ -2350,7 +2350,7 @@ PinBuffer_Locked(BufferDesc *buf)
 	 * Valgrind (this is similar to the PinBuffer() case where the backend
 	 * doesn't already have a buffer pin)
 	 */
-	VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
+	VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), cluster_block_size);
 
 	/*
 	 * Since we hold the buffer spinlock, we can update the buffer state and
@@ -2403,7 +2403,7 @@ UnpinBuffer(BufferDesc *buf)
 		 * within access method code that enforces that buffers are only
 		 * accessed while a buffer lock is held.
 		 */
-		VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), BLCKSZ);
+		VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), cluster_block_size);
 
 		/* I'd better not still hold the buffer content lock */
 		Assert(!LWLockHeldByMe(BufferDescriptorGetContentLock(buf)));
@@ -3491,7 +3491,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 	if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
 	{
 		/*
-		 * Not every table AM uses BLCKSZ wide fixed size blocks. Therefore
+		 * Not every table AM uses cluster_block_size wide fixed size blocks. Therefore
 		 * tableam returns the size in bytes - but for the purpose of this
 		 * routine, we want the number of blocks. Therefore divide, rounding
 		 * up.
@@ -3500,7 +3500,7 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
 
 		szbytes = table_relation_size(relation, forkNum);
 
-		return (szbytes + (BLCKSZ - 1)) / BLCKSZ;
+		return (szbytes + (cluster_block_size - 1)) / cluster_block_size;
 	}
 	else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
 	{
@@ -4288,7 +4288,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
 	 * Bulk extend the destination relation of the same size as the source
 	 * relation before starting to copy block by block.
 	 */
-	memset(buf.data, 0, BLCKSZ);
+	memset(buf.data, 0, cluster_block_size);
 	smgrextend(smgropen(dstlocator, InvalidBackendId), forkNum, nblocks - 1,
 			   buf.data, true);
 
@@ -4316,7 +4316,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
 		START_CRIT_SECTION();
 
 		/* Copy page data from the source to the destination. */
-		memcpy(dstPage, srcPage, BLCKSZ);
+		memcpy(dstPage, srcPage, cluster_block_size);
 		MarkBufferDirty(dstBuf);
 
 		/* WAL-log the copied page. */
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index 1c804fd2f5..e7365030e9 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -589,7 +589,7 @@ GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)
 	Assert(ring_size_kb >= 0);
 
 	/* Figure out how many buffers ring_size_kb is */
-	ring_buffers = ring_size_kb / (BLCKSZ / 1024);
+	ring_buffers = ring_size_kb / (cluster_block_size / 1024);
 
 	/* 0 means unlimited, so no BufferAccessStrategy required */
 	if (ring_buffers == 0)
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 9f20dca121..a748a3a67f 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -335,7 +335,7 @@ ExtendBufferedRelLocal(BufferManagerRelation bmr,
 		buf_block = LocalBufHdrGetBlock(buf_hdr);
 
 		/* new buffers are zero-filled */
-		MemSet((char *) buf_block, 0, BLCKSZ);
+		MemSet((char *) buf_block, 0, cluster_block_size);
 	}
 
 	first_block = smgrnblocks(bmr.smgr, fork);
@@ -745,19 +745,19 @@ GetLocalBufferStorage(void)
 		/* But not more than what we need for all remaining local bufs */
 		num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
 		/* And don't overflow MaxAllocSize, either */
-		num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
+		num_bufs = Min(num_bufs, MaxAllocSize / cluster_block_size);
 
 		/* Buffers should be I/O aligned. */
 		cur_block = (char *)
 			TYPEALIGN(PG_IO_ALIGN_SIZE,
 					  MemoryContextAlloc(LocalBufferContext,
-										 num_bufs * BLCKSZ + PG_IO_ALIGN_SIZE));
+										 num_bufs * cluster_block_size + PG_IO_ALIGN_SIZE));
 		next_buf_in_block = 0;
 		num_bufs_in_block = num_bufs;
 	}
 
 	/* Allocate next buffer in current memory block */
-	this_buf = cur_block + next_buf_in_block * BLCKSZ;
+	this_buf = cur_block + next_buf_in_block * cluster_block_size;
 	next_buf_in_block++;
 	total_bufs_allocated++;
 
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index 41ab64100e..cf5d980026 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -60,7 +60,7 @@
  * tablespaces when available.
  */
 #define MAX_PHYSICAL_FILESIZE	0x40000000
-#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / BLCKSZ)
+#define BUFFILE_SEG_SIZE		(MAX_PHYSICAL_FILESIZE / cluster_block_size)
 
 /*
  * This data structure represents a buffered file that consists of one or
@@ -681,7 +681,7 @@ BufFileWrite(BufFile *file, const void *ptr, size_t size)
 
 	while (size > 0)
 	{
-		if (file->pos >= BLCKSZ)
+		if (file->pos >= cluster_block_size)
 		{
 			/* Buffer full, dump it out */
 			if (file->dirty)
@@ -695,7 +695,7 @@ BufFileWrite(BufFile *file, const void *ptr, size_t size)
 			}
 		}
 
-		nthistime = BLCKSZ - file->pos;
+		nthistime = cluster_block_size - file->pos;
 		if (nthistime > size)
 			nthistime = size;
 		Assert(nthistime > 0);
@@ -839,9 +839,9 @@ BufFileTell(BufFile *file, int *fileno, off_t *offset)
 /*
  * BufFileSeekBlock --- block-oriented seek
  *
- * Performs absolute seek to the start of the n'th BLCKSZ-sized block of
+ * Performs absolute seek to the start of the n'th cluster_block_size-sized block of
  * the file.  Note that users of this interface will fail if their files
- * exceed BLCKSZ * LONG_MAX bytes, but that is quite a lot; we don't work
+ * exceed cluster_block_size * LONG_MAX bytes, but that is quite a lot; we don't work
  * with tables bigger than that, either...
  *
  * Result is 0 if OK, EOF if not.  Logical position is not moved if an
@@ -852,7 +852,7 @@ BufFileSeekBlock(BufFile *file, long blknum)
 {
 	return BufFileSeek(file,
 					   (int) (blknum / BUFFILE_SEG_SIZE),
-					   (off_t) (blknum % BUFFILE_SEG_SIZE) * BLCKSZ,
+					   (off_t) (blknum % BUFFILE_SEG_SIZE) * cluster_block_size,
 					   SEEK_SET);
 }
 
@@ -867,7 +867,7 @@ BufFileTellBlock(BufFile *file)
 {
 	long		blknum;
 
-	blknum = (file->curOffset + file->pos) / BLCKSZ;
+	blknum = (file->curOffset + file->pos) / cluster_block_size;
 	blknum += file->curFile * BUFFILE_SEG_SIZE;
 	return blknum;
 }
diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c
index e04bc3941a..327bd43d36 100644
--- a/src/backend/storage/file/copydir.c
+++ b/src/backend/storage/file/copydir.c
@@ -124,7 +124,7 @@ copy_file(const char *fromfile, const char *tofile)
 	off_t		flush_offset;
 
 	/* Size of copy buffer (read and write requests) */
-#define COPY_BUF_SIZE (8 * BLCKSZ)
+#define COPY_BUF_SIZE (8 * cluster_block_size)
 
 	/*
 	 * Size of data flush requests.  It seems beneficial on most platforms to
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 3fed475c38..ab4ede5665 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3943,10 +3943,10 @@ check_debug_io_direct(char **newval, void **extra, GucSource source)
 		result = false;
 	}
 #endif
-#if BLCKSZ < PG_IO_ALIGN_SIZE
+#if cluster_block_size < PG_IO_ALIGN_SIZE
 	if (result && (flags & IO_DIRECT_DATA))
 	{
-		GUC_check_errdetail("debug_io_direct is not supported for data because BLCKSZ is too small");
+		GUC_check_errdetail("debug_io_direct is not supported for data because cluster_block_size is too small");
 		result = false;
 	}
 #endif
diff --git a/src/backend/storage/freespace/README b/src/backend/storage/freespace/README
index e7ff23b76f..4fa6bacc83 100644
--- a/src/backend/storage/freespace/README
+++ b/src/backend/storage/freespace/README
@@ -14,8 +14,8 @@ It is important to keep the map small so that it can be searched rapidly.
 Therefore, we don't attempt to record the exact free space on a page.
 We allocate one map byte to each page, allowing us to record free space
 at a granularity of 1/256th of a page.  Another way to say it is that
-the stored value is the free space divided by BLCKSZ/256 (rounding down).
-We assume that the free space must always be less than BLCKSZ, since
+the stored value is the free space divided by cluster_block_size/256 (rounding down).
+We assume that the free space must always be less than cluster_block_size, since
 all pages have some overhead; so the maximum map value is 255.
 
 To assist in fast searching, the map isn't simply an array of per-page
@@ -97,7 +97,7 @@ has the same value as the corresponding leaf node on its parent page.
 The root page is always stored at physical block 0.
 
 For example, assuming each FSM page can hold information about 4 pages (in
-reality, it holds (BLCKSZ - headers) / 2, or ~4000 with default BLCKSZ),
+reality, it holds (cluster_block_size - headers) / 2, or ~4000 with default cluster_block_size),
 we get a disk layout like this:
 
  0     <-- page 0 at level 2 (root page)
@@ -136,7 +136,7 @@ and so forth.
 
 To keep things simple, the tree is always constant height. To cover the
 maximum relation size of 2^32-1 blocks, three levels is enough with the default
-BLCKSZ (4000^3 > 2^32).
+cluster_block_size (4000^3 > 2^32).
 
 Addressing
 ----------
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index fb9440ff72..f17fe758d6 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -38,11 +38,11 @@
  * divide the amount of free space a page can have into 256 different
  * categories. The highest category, 255, represents a page with at least
  * MaxFSMRequestSize bytes of free space, and the second highest category
- * represents the range from 254 * FSM_CAT_STEP, inclusive, to
+ * represents the range from 254 * fsm_cat_step, inclusive, to
  * MaxFSMRequestSize, exclusive.
  *
- * MaxFSMRequestSize depends on the architecture and BLCKSZ, but assuming
- * default 8k BLCKSZ, and that MaxFSMRequestSize is 8164 bytes, the
+ * MaxFSMRequestSize depends on the architecture and cluster_block_size, but assuming
+ * default 8k cluster_block_size, and that MaxFSMRequestSize is 8164 bytes, the
  * categories look like this:
  *
  *
@@ -62,14 +62,14 @@
  * request of exactly MaxFSMRequestSize bytes.
  */
 #define FSM_CATEGORIES	256
-#define FSM_CAT_STEP	(BLCKSZ / FSM_CATEGORIES)
+#define fsm_cat_step	(cluster_block_size / FSM_CATEGORIES)
 #define MaxFSMRequestSize	MaxHeapTupleSize
 
 /*
  * Depth of the on-disk tree. We need to be able to address 2^32-1 blocks,
  * and 1626 is the smallest number that satisfies X^3 >= 2^32-1. Likewise,
  * 256 is the smallest number that satisfies X^4 >= 2^32-1. In practice,
- * this means that 4096 bytes is the smallest BLCKSZ that we can get away
+ * this means that 4096 bytes is the smallest cluster_block_size that we can get away
  * with a 3-level tree, and 512 is the smallest we support.
  */
 #define FSM_TREE_DEPTH	((SlotsPerFSMPage >= 1626) ? 3 : 4)
@@ -217,7 +217,7 @@ XLogRecordPageWithFreeSpace(RelFileLocator rlocator, BlockNumber heapBlk,
 
 	page = BufferGetPage(buf);
 	if (PageIsNew(page))
-		PageInit(page, BLCKSZ, 0);
+		PageInit(page, cluster_block_size, 0);
 
 	if (fsm_set_avail(page, slot, new_cat))
 		MarkBufferDirtyHint(buf, false);
@@ -370,12 +370,12 @@ fsm_space_avail_to_cat(Size avail)
 {
 	int			cat;
 
-	Assert(avail < BLCKSZ);
+	Assert(avail < cluster_block_size);
 
 	if (avail >= MaxFSMRequestSize)
 		return 255;
 
-	cat = avail / FSM_CAT_STEP;
+	cat = avail / fsm_cat_step;
 
 	/*
 	 * The highest category, 255, is reserved for MaxFSMRequestSize bytes or
@@ -398,7 +398,7 @@ fsm_space_cat_to_avail(uint8 cat)
 	if (cat == 255)
 		return MaxFSMRequestSize;
 	else
-		return cat * FSM_CAT_STEP;
+		return cat * fsm_cat_step;
 }
 
 /*
@@ -417,7 +417,7 @@ fsm_space_needed_to_cat(Size needed)
 	if (needed == 0)
 		return 1;
 
-	cat = (needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP;
+	cat = (needed + fsm_cat_step - 1) / fsm_cat_step;
 
 	if (cat > 255)
 		cat = 255;
@@ -598,7 +598,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), cluster_block_size, 0);
 		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
 	}
 	return buf;
diff --git a/src/backend/storage/freespace/indexfsm.c b/src/backend/storage/freespace/indexfsm.c
index fff8f4fbfb..0047793d7c 100644
--- a/src/backend/storage/freespace/indexfsm.c
+++ b/src/backend/storage/freespace/indexfsm.c
@@ -16,7 +16,7 @@
  *	This is similar to the FSM used for heap, in freespace.c, but instead
  *	of tracking the amount of free space on pages, we only track whether
  *	pages are completely free or in-use. We use the same FSM implementation
- *	as for heaps, using BLCKSZ - 1 to denote used pages, and 0 for unused.
+ *	as for heaps, using cluster_block_size - 1 to denote used pages, and 0 for unused.
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,7 @@
 BlockNumber
 GetFreeIndexPage(Relation rel)
 {
-	BlockNumber blkno = GetPageWithFreeSpace(rel, BLCKSZ / 2);
+	BlockNumber blkno = GetPageWithFreeSpace(rel, cluster_block_size / 2);
 
 	if (blkno != InvalidBlockNumber)
 		RecordUsedIndexPage(rel, blkno);
@@ -51,7 +51,7 @@ GetFreeIndexPage(Relation rel)
 void
 RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
 {
-	RecordPageWithFreeSpace(rel, freeBlock, BLCKSZ - 1);
+	RecordPageWithFreeSpace(rel, freeBlock, cluster_block_size - 1);
 }
 
 
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index cc9c335a92..d604d590e7 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -185,7 +185,7 @@ getdatafield(Form_pg_largeobject tuple,
 		freeit = true;
 	}
 	len = VARSIZE(datafield) - VARHDRSZ;
-	if (len < 0 || len > LOBLKSIZE)
+	if (len < 0 || len > cluster_loblksize)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
 				 errmsg("pg_largeobject entry for OID %u, page %d has invalid data field size %d",
@@ -414,7 +414,7 @@ inv_getsize(LargeObjectDesc *obj_desc)
 			elog(ERROR, "null field found in pg_largeobject");
 		data = (Form_pg_largeobject) GETSTRUCT(tuple);
 		getdatafield(data, &datafield, &len, &pfreeit);
-		lastbyte = (uint64) data->pageno * LOBLKSIZE + len;
+		lastbyte = (uint64) data->pageno * cluster_loblksize + len;
 		if (pfreeit)
 			pfree(datafield);
 	}
@@ -493,7 +493,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 	int64		n;
 	int64		off;
 	int			len;
-	int32		pageno = (int32) (obj_desc->offset / LOBLKSIZE);
+	int32		pageno = (int32) (obj_desc->offset / cluster_loblksize);
 	uint64		pageoff;
 	ScanKeyData skey[2];
 	SysScanDesc sd;
@@ -541,7 +541,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 		 * there may be missing pages if the LO contains unwritten "holes". We
 		 * want missing sections to read out as zeroes.
 		 */
-		pageoff = ((uint64) data->pageno) * LOBLKSIZE;
+		pageoff = ((uint64) data->pageno) * cluster_loblksize;
 		if (pageoff > obj_desc->offset)
 		{
 			n = pageoff - obj_desc->offset;
@@ -555,7 +555,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 		{
 			Assert(obj_desc->offset >= pageoff);
 			off = (int) (obj_desc->offset - pageoff);
-			Assert(off >= 0 && off < LOBLKSIZE);
+			Assert(off >= 0 && off < cluster_loblksize);
 
 			getdatafield(data, &datafield, &len, &pfreeit);
 			if (len > off)
@@ -586,7 +586,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	int			n;
 	int			off;
 	int			len;
-	int32		pageno = (int32) (obj_desc->offset / LOBLKSIZE);
+	int32		pageno = (int32) (obj_desc->offset / cluster_loblksize);
 	ScanKeyData skey[2];
 	SysScanDesc sd;
 	HeapTuple	oldtuple;
@@ -598,7 +598,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[LOBLKSIZE + VARHDRSZ];
+		char		data[cluster_loblksize + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
@@ -687,14 +687,14 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 			/*
 			 * Fill any hole
 			 */
-			off = (int) (obj_desc->offset % LOBLKSIZE);
+			off = (int) (obj_desc->offset % cluster_loblksize);
 			if (off > len)
 				MemSet(workb + len, 0, off - len);
 
 			/*
 			 * Insert appropriate portion of new data
 			 */
-			n = LOBLKSIZE - off;
+			n = cluster_loblksize - off;
 			n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
 			memcpy(workb + off, buf + nwritten, n);
 			nwritten += n;
@@ -732,14 +732,14 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 			 *
 			 * First, fill any hole
 			 */
-			off = (int) (obj_desc->offset % LOBLKSIZE);
+			off = (int) (obj_desc->offset % cluster_loblksize);
 			if (off > 0)
 				MemSet(workb, 0, off);
 
 			/*
 			 * Insert appropriate portion of new data
 			 */
-			n = LOBLKSIZE - off;
+			n = cluster_loblksize - off;
 			n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
 			memcpy(workb + off, buf + nwritten, n);
 			nwritten += n;
@@ -779,7 +779,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 void
 inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 {
-	int32		pageno = (int32) (len / LOBLKSIZE);
+	int32		pageno = (int32) (len / cluster_loblksize);
 	int32		off;
 	ScanKeyData skey[2];
 	SysScanDesc sd;
@@ -789,7 +789,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 	{
 		bytea		hdr;
 		/* this is to make the union big enough for a LO data chunk: */
-		char		data[LOBLKSIZE + VARHDRSZ];
+		char		data[cluster_loblksize + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
 	}			workbuf;
@@ -872,7 +872,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		/*
 		 * Fill any hole
 		 */
-		off = len % LOBLKSIZE;
+		off = len % cluster_loblksize;
 		if (off > pagelen)
 			MemSet(workb + pagelen, 0, off - pagelen);
 
@@ -911,7 +911,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		 *
 		 * Fill the hole up to the truncation point
 		 */
-		off = len % LOBLKSIZE;
+		off = len % cluster_loblksize;
 		if (off > 0)
 			MemSet(workb, 0, off);
 
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 1af41213b4..a16509089a 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -321,9 +321,9 @@ static SlruCtlData SerialSlruCtlData;
 
 #define SerialSlruCtl			(&SerialSlruCtlData)
 
-#define SERIAL_PAGESIZE			BLCKSZ
+#define serial_pagesize			cluster_block_size
 #define SERIAL_ENTRYSIZE			sizeof(SerCommitSeqNo)
-#define SERIAL_ENTRIESPERPAGE	(SERIAL_PAGESIZE / SERIAL_ENTRYSIZE)
+#define SERIAL_ENTRIESPERPAGE	(serial_pagesize / SERIAL_ENTRYSIZE)
 
 /*
  * Set maximum pages based on the number needed to track all transactions.
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 9a302ddc30..07a664355f 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -45,7 +45,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
 
 	specialSize = MAXALIGN(specialSize);
 
-	Assert(pageSize == BLCKSZ);
+	Assert(pageSize == cluster_block_size);
 	Assert(pageSize > specialSize + SizeOfPageHeaderData);
 
 	/* Make sure all fields of page are zero, as well as unused space */
@@ -117,7 +117,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 		if ((p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
 			p->pd_lower <= p->pd_upper &&
 			p->pd_upper <= p->pd_special &&
-			p->pd_special <= BLCKSZ &&
+			p->pd_special <= cluster_block_size &&
 			p->pd_special == MAXALIGN(p->pd_special))
 			header_sane = true;
 
@@ -128,7 +128,7 @@ PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags)
 	/* Check all-zeroes case */
 	all_zeroes = true;
 	pagebytes = (size_t *) page;
-	for (i = 0; i < (BLCKSZ / sizeof(size_t)); i++)
+	for (i = 0; i < (cluster_block_size / sizeof(size_t)); i++)
 	{
 		if (pagebytes[i] != 0)
 		{
@@ -211,7 +211,7 @@ PageAddItemExtended(Page page,
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ)
+		phdr->pd_special > cluster_block_size)
 		ereport(PANIC,
 				(errcode(ERRCODE_DATA_CORRUPTED),
 				 errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
@@ -723,7 +723,7 @@ PageRepairFragmentation(Page page)
 	if (pd_lower < SizeOfPageHeaderData ||
 		pd_lower > pd_upper ||
 		pd_upper > pd_special ||
-		pd_special > BLCKSZ ||
+		pd_special > cluster_block_size ||
 		pd_special != MAXALIGN(pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1066,7 +1066,7 @@ PageIndexTupleDelete(Page page, OffsetNumber offnum)
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > cluster_block_size ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1201,7 +1201,7 @@ PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
 	if (pd_lower < SizeOfPageHeaderData ||
 		pd_lower > pd_upper ||
 		pd_upper > pd_special ||
-		pd_special > BLCKSZ ||
+		pd_special > cluster_block_size ||
 		pd_special != MAXALIGN(pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1307,7 +1307,7 @@ PageIndexTupleDeleteNoCompact(Page page, OffsetNumber offnum)
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > cluster_block_size ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1419,7 +1419,7 @@ PageIndexTupleOverwrite(Page page, OffsetNumber offnum,
 	if (phdr->pd_lower < SizeOfPageHeaderData ||
 		phdr->pd_lower > phdr->pd_upper ||
 		phdr->pd_upper > phdr->pd_special ||
-		phdr->pd_special > BLCKSZ ||
+		phdr->pd_special > cluster_block_size ||
 		phdr->pd_special != MAXALIGN(phdr->pd_special))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
@@ -1523,11 +1523,11 @@ PageSetChecksumCopy(Page page, BlockNumber blkno)
 	 */
 	if (pageCopy == NULL)
 		pageCopy = MemoryContextAllocAligned(TopMemoryContext,
-											 BLCKSZ,
+											 cluster_block_size,
 											 PG_IO_ALIGN_SIZE,
 											 0);
 
-	memcpy(pageCopy, (char *) page, BLCKSZ);
+	memcpy(pageCopy, (char *) page, cluster_block_size);
 	((PageHeader) pageCopy)->pd_checksum = pg_checksum_page(pageCopy, blkno);
 	return pageCopy;
 }
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index fdecbad170..319e8d8655 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -467,7 +467,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= cluster_block_size)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	/* This assert is too expensive to have on normally ... */
@@ -490,11 +490,11 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync, EXTENSION_CREATE);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != BLCKSZ)
+	if ((nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != cluster_block_size)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -507,7 +507,7 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 				(errcode(ERRCODE_DISK_FULL),
 				 errmsg("could not extend file \"%s\": wrote only %d of %d bytes at block %u",
 						FilePathName(v->mdfd_vfd),
-						nbytes, BLCKSZ, blocknum),
+						nbytes, cluster_block_size, blocknum),
 				 errhint("Check free disk space.")));
 	}
 
@@ -553,7 +553,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 	while (remblocks > 0)
 	{
 		BlockNumber segstartblock = curblocknum % ((BlockNumber) RELSEG_SIZE);
-		off_t		seekpos = (off_t) BLCKSZ * segstartblock;
+		off_t		seekpos = (off_t) cluster_block_size * segstartblock;
 		int			numblocks;
 
 		if (segstartblock + remblocks > RELSEG_SIZE)
@@ -582,7 +582,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 			int			ret;
 
 			ret = FileFallocate(v->mdfd_vfd,
-								seekpos, (off_t) BLCKSZ * numblocks,
+								seekpos, (off_t) cluster_block_size * numblocks,
 								WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret != 0)
 			{
@@ -605,7 +605,7 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum,
 			 * whole length of the extension.
 			 */
 			ret = FileZero(v->mdfd_vfd,
-						   seekpos, (off_t) BLCKSZ * numblocks,
+						   seekpos, (off_t) cluster_block_size * numblocks,
 						   WAIT_EVENT_DATA_FILE_EXTEND);
 			if (ret < 0)
 				ereport(ERROR,
@@ -726,11 +726,11 @@ mdprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 	if (v == NULL)
 		return false;
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	(void) FilePrefetch(v->mdfd_vfd, seekpos, BLCKSZ, WAIT_EVENT_DATA_FILE_PREFETCH);
+	(void) FilePrefetch(v->mdfd_vfd, seekpos, cluster_block_size, WAIT_EVENT_DATA_FILE_PREFETCH);
 #endif							/* USE_PREFETCH */
 
 	return true;
@@ -748,7 +748,7 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= cluster_block_size)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	TRACE_POSTGRESQL_SMGR_MD_READ_START(forknum, blocknum,
@@ -760,11 +760,11 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, false,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_READ);
+	nbytes = FileRead(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_READ);
 
 	TRACE_POSTGRESQL_SMGR_MD_READ_DONE(forknum, blocknum,
 									   reln->smgr_rlocator.locator.spcOid,
@@ -772,9 +772,9 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 									   reln->smgr_rlocator.locator.relNumber,
 									   reln->smgr_rlocator.backend,
 									   nbytes,
-									   BLCKSZ);
+									   cluster_block_size);
 
-	if (nbytes != BLCKSZ)
+	if (nbytes != cluster_block_size)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -791,13 +791,13 @@ mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 		 * update a block that was later truncated away.
 		 */
 		if (zero_damaged_pages || InRecovery)
-			MemSet(buffer, 0, BLCKSZ);
+			MemSet(buffer, 0, cluster_block_size);
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
 							blocknum, FilePathName(v->mdfd_vfd),
-							nbytes, BLCKSZ)));
+							nbytes, cluster_block_size)));
 	}
 }
 
@@ -817,7 +817,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	MdfdVec    *v;
 
 	/* If this build supports direct I/O, the buffer must be I/O aligned. */
-	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= BLCKSZ)
+	if (PG_O_DIRECT != 0 && PG_IO_ALIGN_SIZE <= cluster_block_size)
 		Assert((uintptr_t) buffer == TYPEALIGN(PG_IO_ALIGN_SIZE, buffer));
 
 	/* This assert is too expensive to have on normally ... */
@@ -834,11 +834,11 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 	v = _mdfd_getseg(reln, forknum, blocknum, skipFsync,
 					 EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
 
-	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+	seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-	Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
+	Assert(seekpos < (off_t) cluster_block_size * RELSEG_SIZE);
 
-	nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
+	nbytes = FileWrite(v->mdfd_vfd, buffer, cluster_block_size, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
 
 	TRACE_POSTGRESQL_SMGR_MD_WRITE_DONE(forknum, blocknum,
 										reln->smgr_rlocator.locator.spcOid,
@@ -846,9 +846,9 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 										reln->smgr_rlocator.locator.relNumber,
 										reln->smgr_rlocator.backend,
 										nbytes,
-										BLCKSZ);
+										cluster_block_size);
 
-	if (nbytes != BLCKSZ)
+	if (nbytes != cluster_block_size)
 	{
 		if (nbytes < 0)
 			ereport(ERROR,
@@ -861,7 +861,7 @@ mdwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
 				 errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
 						blocknum,
 						FilePathName(v->mdfd_vfd),
-						nbytes, BLCKSZ),
+						nbytes, cluster_block_size),
 				 errhint("Check free disk space.")));
 	}
 
@@ -917,9 +917,9 @@ mdwriteback(SMgrRelation reln, ForkNumber forknum,
 		Assert(nflush >= 1);
 		Assert(nflush <= nblocks);
 
-		seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
+		seekpos = (off_t) cluster_block_size * (blocknum % ((BlockNumber) RELSEG_SIZE));
 
-		FileWriteback(v->mdfd_vfd, seekpos, (off_t) BLCKSZ * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
+		FileWriteback(v->mdfd_vfd, seekpos, (off_t) cluster_block_size * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
 
 		nblocks -= nflush;
 		blocknum += nflush;
@@ -1061,7 +1061,7 @@ mdtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 			 */
 			BlockNumber lastsegblocks = nblocks - priorblocks;
 
-			if (FileTruncate(v->mdfd_vfd, (off_t) lastsegblocks * BLCKSZ, WAIT_EVENT_DATA_FILE_TRUNCATE) < 0)
+			if (FileTruncate(v->mdfd_vfd, (off_t) lastsegblocks * cluster_block_size, WAIT_EVENT_DATA_FILE_TRUNCATE) < 0)
 				ereport(ERROR,
 						(errcode_for_file_access(),
 						 errmsg("could not truncate file \"%s\" to %u blocks: %m",
@@ -1458,7 +1458,7 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
 			 */
 			if (nblocks < ((BlockNumber) RELSEG_SIZE))
 			{
-				char	   *zerobuf = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE,
+				char	   *zerobuf = palloc_aligned(cluster_block_size, PG_IO_ALIGN_SIZE,
 													 MCXT_ALLOC_ZERO);
 
 				mdextend(reln, forknum,
@@ -1529,7 +1529,7 @@ _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 				 errmsg("could not seek to end of file \"%s\": %m",
 						FilePathName(seg->mdfd_vfd))));
 	/* note that this calculation will ignore any partial block at EOF */
-	return (BlockNumber) (len / BLCKSZ);
+	return (BlockNumber) (len / cluster_block_size);
 }
 
 /*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 3b44af8006..38a40d5cd9 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1401,12 +1401,12 @@ pg_stat_get_io(PG_FUNCTION_ARGS)
 				values[IO_COL_RESET_TIME] = TimestampTzGetDatum(reset_time);
 
 				/*
-				 * Hard-code this to the value of BLCKSZ for now. Future
+				 * Hard-code this to the value of cluster_block_size for now. Future
 				 * values could include XLOG_BLCKSZ, once WAL IO is tracked,
 				 * and constant multipliers, once non-block-oriented IO (e.g.
 				 * temporary file IO) is tracked.
 				 */
-				values[IO_COL_CONVERSION] = Int64GetDatum(BLCKSZ);
+				values[IO_COL_CONVERSION] = Int64GetDatum(cluster_block_size);
 
 				for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
 				{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c4fcd0076e..83577060f8 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -7760,7 +7760,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 	 * around 3 bytes per item is fairly typical.
 	 */
 	dataPagesFetchedBySel = ceil(*indexSelectivity *
-								 (numTuples / (BLCKSZ / 3)));
+								 (numTuples / (cluster_block_size / 3)));
 	if (dataPagesFetchedBySel > dataPagesFetched)
 		dataPagesFetched = dataPagesFetchedBySel;
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 1e671c560c..37d91cb902 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1501,8 +1501,8 @@ AddToDataDirLockFile(int target_line, const char *str)
 	int			lineno;
 	char	   *srcptr;
 	char	   *destptr;
-	char		srcbuffer[BLCKSZ];
-	char		destbuffer[BLCKSZ];
+	char		srcbuffer[cluster_block_size];
+	char		destbuffer[cluster_block_size];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
@@ -1626,7 +1626,7 @@ RecheckDataDirLockFile(void)
 	int			fd;
 	int			len;
 	long		file_pid;
-	char		buffer[BLCKSZ];
+	char		buffer[cluster_block_size];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c25c697a06..d29b98e7d8 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -105,8 +105,8 @@ typedef struct
 } unit_conversion;
 
 /* Ensure that the constants in the tables don't overflow or underflow */
-#if BLCKSZ < 1024 || BLCKSZ > (1024*1024)
-#error BLCKSZ must be between 1KB and 1MB
+#if cluster_block_size < 1024 || cluster_block_size > (1024*1024)
+#error cluster_block_size must be between 1KB and 1MB
 #endif
 #if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
 #error XLOG_BLCKSZ must be between 1KB and 1MB
@@ -134,11 +134,11 @@ static const unit_conversion memory_unit_conversion_table[] =
 	{"kB", GUC_UNIT_MB, 1.0 / 1024.0},
 	{"B", GUC_UNIT_MB, 1.0 / (1024.0 * 1024.0)},
 
-	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (BLCKSZ / 1024)},
-	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (BLCKSZ / 1024)},
-	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (BLCKSZ / 1024)},
-	{"kB", GUC_UNIT_BLOCKS, 1.0 / (BLCKSZ / 1024)},
-	{"B", GUC_UNIT_BLOCKS, 1.0 / BLCKSZ},
+	{"TB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0 * 1024.0) / (cluster_block_size / 1024)},
+	{"GB", GUC_UNIT_BLOCKS, (1024.0 * 1024.0) / (cluster_block_size / 1024)},
+	{"MB", GUC_UNIT_BLOCKS, 1024.0 / (cluster_block_size / 1024)},
+	{"kB", GUC_UNIT_BLOCKS, 1.0 / (cluster_block_size / 1024)},
+	{"B", GUC_UNIT_BLOCKS, 1.0 / cluster_block_size},
 
 	{"TB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
 	{"GB", GUC_UNIT_XBLOCKS, (1024.0 * 1024.0) / (XLOG_BLCKSZ / 1024)},
@@ -2782,7 +2782,7 @@ get_config_unit_name(int flags)
 
 				/* initialize if first time through */
 				if (bbuf[0] == '\0')
-					snprintf(bbuf, sizeof(bbuf), "%dkB", BLCKSZ / 1024);
+					snprintf(bbuf, sizeof(bbuf), "%dkB", cluster_block_size / 1024);
 				return bbuf;
 			}
 		case GUC_UNIT_XBLOCKS:
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 16ec6c5ef0..ffb72cc177 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -3130,7 +3130,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&block_size,
-		BLCKSZ, BLCKSZ, BLCKSZ,
+		cluster_block_size, cluster_block_size, cluster_block_size,
 		NULL, NULL, NULL
 	},
 
@@ -3369,7 +3369,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_table_scan_size,
-		(8 * 1024 * 1024) / BLCKSZ, 0, INT_MAX / 3,
+		(8 * 1024 * 1024) / cluster_block_size, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
@@ -3380,7 +3380,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_BLOCKS | GUC_EXPLAIN,
 		},
 		&min_parallel_index_scan_size,
-		(512 * 1024) / BLCKSZ, 0, INT_MAX / 3,
+		(512 * 1024) / cluster_block_size, 0, INT_MAX / 3,
 		NULL, NULL, NULL
 	},
 
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index f31878bdee..4b54256f09 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -27,7 +27,7 @@
  * larger size than the underlying OS may support.
  *
  * For simplicity, we allocate and release space in the underlying file
- * in BLCKSZ-size blocks.  Space allocation boils down to keeping track
+ * in cluster_block_size-size blocks.  Space allocation boils down to keeping track
  * of which blocks in the underlying file belong to which logical tape,
  * plus any blocks that are free (recycled and not yet reused).
  * The blocks in each logical tape form a chain, with a prev- and next-
@@ -86,7 +86,7 @@
 #include "utils/memutils.h"
 
 /*
- * A TapeBlockTrailer is stored at the end of each BLCKSZ block.
+ * A TapeBlockTrailer is stored at the end of each cluster_block_size block.
  *
  * The first block of a tape has prev == -1.  The last block of a tape
  * stores the number of valid bytes on the block, inverted, in 'next'
@@ -100,7 +100,7 @@ typedef struct TapeBlockTrailer
 								 * bytes on last block (if < 0) */
 } TapeBlockTrailer;
 
-#define TapeBlockPayloadSize  (BLCKSZ - sizeof(TapeBlockTrailer))
+#define TapeBlockPayloadSize  (cluster_block_size - sizeof(TapeBlockTrailer))
 #define TapeBlockGetTrailer(buf) \
 	((TapeBlockTrailer *) ((char *) buf + TapeBlockPayloadSize))
 
@@ -192,7 +192,7 @@ struct LogicalTapeSet
 
 	/*
 	 * File size tracking.  nBlocksWritten is the size of the underlying file,
-	 * in BLCKSZ blocks.  nBlocksAllocated is the number of blocks allocated
+	 * in cluster_block_size blocks.  nBlocksAllocated is the number of blocks allocated
 	 * by ltsReleaseBlock(), and it is always greater than or equal to
 	 * nBlocksWritten.  Blocks between nBlocksAllocated and nBlocksWritten are
 	 * blocks that have been allocated for a tape, but have not been written
@@ -265,7 +265,7 @@ ltsWriteBlock(LogicalTapeSet *lts, long blocknum, const void *buffer)
 				(errcode_for_file_access(),
 				 errmsg("could not seek to block %ld of temporary file",
 						blocknum)));
-	BufFileWrite(lts->pfile, buffer, BLCKSZ);
+	BufFileWrite(lts->pfile, buffer, cluster_block_size);
 
 	/* Update nBlocksWritten, if we extended the file */
 	if (blocknum == lts->nBlocksWritten)
@@ -286,7 +286,7 @@ ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
 				(errcode_for_file_access(),
 				 errmsg("could not seek to block %ld of temporary file",
 						blocknum)));
-	BufFileReadExact(lts->pfile, buffer, BLCKSZ);
+	BufFileReadExact(lts->pfile, buffer, cluster_block_size);
 }
 
 /*
@@ -328,7 +328,7 @@ ltsReadFillBuffer(LogicalTape *lt)
 			lt->nextBlockNumber = TapeBlockGetTrailer(thisbuf)->next;
 
 		/* Advance to next block, if we have buffer space left */
-	} while (lt->buffer_size - lt->nbytes > BLCKSZ);
+	} while (lt->buffer_size - lt->nbytes > cluster_block_size);
 
 	return (lt->nbytes > 0);
 }
@@ -640,7 +640,7 @@ LogicalTapeImport(LogicalTapeSet *lts, int worker, TapeShare *shared)
 	}
 	/* Don't allocate more for read buffer than could possibly help */
 	lt->max_size = Min(MaxAllocSize, filesize);
-	tapeblocks = filesize / BLCKSZ;
+	tapeblocks = filesize / cluster_block_size;
 
 	/*
 	 * Update # of allocated blocks and # blocks written to reflect the
@@ -769,8 +769,8 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
 	/* Allocate data buffer and first block on first write */
 	if (lt->buffer == NULL)
 	{
-		lt->buffer = (char *) palloc(BLCKSZ);
-		lt->buffer_size = BLCKSZ;
+		lt->buffer = (char *) palloc(cluster_block_size);
+		lt->buffer_size = cluster_block_size;
 	}
 	if (lt->curBlockNumber == -1)
 	{
@@ -783,7 +783,7 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
 		TapeBlockGetTrailer(lt->buffer)->prev = -1L;
 	}
 
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 	while (size > 0)
 	{
 		if (lt->pos >= (int) TapeBlockPayloadSize)
@@ -837,9 +837,9 @@ LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
  *
  * 'buffer_size' specifies how much memory to use for the read buffer.
  * Regardless of the argument, the actual amount of memory used is between
- * BLCKSZ and MaxAllocSize, and is a multiple of BLCKSZ.  The given value is
+ * cluster_block_size and MaxAllocSize, and is a multiple of cluster_block_size.  The given value is
  * rounded down and truncated to fit those constraints, if necessary.  If the
- * tape is frozen, the 'buffer_size' argument is ignored, and a small BLCKSZ
+ * tape is frozen, the 'buffer_size' argument is ignored, and a small cluster_block_size
  * byte buffer is used.
  */
 void
@@ -851,19 +851,19 @@ LogicalTapeRewindForRead(LogicalTape *lt, size_t buffer_size)
 	 * Round and cap buffer_size if needed.
 	 */
 	if (lt->frozen)
-		buffer_size = BLCKSZ;
+		buffer_size = cluster_block_size;
 	else
 	{
 		/* need at least one block */
-		if (buffer_size < BLCKSZ)
-			buffer_size = BLCKSZ;
+		if (buffer_size < cluster_block_size)
+			buffer_size = cluster_block_size;
 
 		/* palloc() larger than max_size is unlikely to be helpful */
 		if (buffer_size > lt->max_size)
 			buffer_size = lt->max_size;
 
-		/* round down to BLCKSZ boundary */
-		buffer_size -= buffer_size % BLCKSZ;
+		/* round down to cluster_block_size boundary */
+		buffer_size -= buffer_size % cluster_block_size;
 	}
 
 	if (lt->writing)
@@ -1015,12 +1015,12 @@ LogicalTapeFreeze(LogicalTape *lt, TapeShare *share)
 	 * we're reading from multiple tapes.  But at the end of a sort, when a
 	 * tape is frozen, we only read from a single tape anyway.
 	 */
-	if (!lt->buffer || lt->buffer_size != BLCKSZ)
+	if (!lt->buffer || lt->buffer_size != cluster_block_size)
 	{
 		if (lt->buffer)
 			pfree(lt->buffer);
-		lt->buffer = palloc(BLCKSZ);
-		lt->buffer_size = BLCKSZ;
+		lt->buffer = palloc(cluster_block_size);
+		lt->buffer_size = cluster_block_size;
 	}
 
 	/* Read the first block, or reset if tape is empty */
@@ -1064,7 +1064,7 @@ LogicalTapeBackspace(LogicalTape *lt, size_t size)
 	size_t		seekpos = 0;
 
 	Assert(lt->frozen);
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 
 	if (lt->buffer == NULL)
 		ltsInitReadBuffer(lt);
@@ -1134,7 +1134,7 @@ LogicalTapeSeek(LogicalTape *lt, long blocknum, int offset)
 {
 	Assert(lt->frozen);
 	Assert(offset >= 0 && offset <= TapeBlockPayloadSize);
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 
 	if (lt->buffer == NULL)
 		ltsInitReadBuffer(lt);
@@ -1167,7 +1167,7 @@ LogicalTapeTell(LogicalTape *lt, long *blocknum, int *offset)
 	Assert(lt->offsetBlockNumber == 0L);
 
 	/* With a larger buffer, 'pos' wouldn't be the same as offset within page */
-	Assert(lt->buffer_size == BLCKSZ);
+	Assert(lt->buffer_size == cluster_block_size);
 
 	*blocknum = lt->curBlockNumber;
 	*offset = lt->pos;
diff --git a/src/backend/utils/sort/sharedtuplestore.c b/src/backend/utils/sort/sharedtuplestore.c
index 236be65f22..b34bed7466 100644
--- a/src/backend/utils/sort/sharedtuplestore.c
+++ b/src/backend/utils/sort/sharedtuplestore.c
@@ -37,7 +37,7 @@
  */
 #define STS_CHUNK_PAGES 4
 #define STS_CHUNK_HEADER_SIZE offsetof(SharedTuplestoreChunk, data)
-#define STS_CHUNK_DATA_SIZE (STS_CHUNK_PAGES * BLCKSZ - STS_CHUNK_HEADER_SIZE)
+#define STS_CHUNK_DATA_SIZE (STS_CHUNK_PAGES * cluster_block_size - STS_CHUNK_HEADER_SIZE)
 
 /* Chunk written to disk. */
 typedef struct SharedTuplestoreChunk
@@ -198,7 +198,7 @@ sts_flush_chunk(SharedTuplestoreAccessor *accessor)
 {
 	size_t		size;
 
-	size = STS_CHUNK_PAGES * BLCKSZ;
+	size = STS_CHUNK_PAGES * cluster_block_size;
 	BufFileWrite(accessor->write_file, accessor->write_chunk, size);
 	memset(accessor->write_chunk, 0, size);
 	accessor->write_pointer = &accessor->write_chunk->data[0];
@@ -332,11 +332,11 @@ sts_puttuple(SharedTuplestoreAccessor *accessor, void *meta_data,
 			/* First time through.  Allocate chunk. */
 			accessor->write_chunk = (SharedTuplestoreChunk *)
 				MemoryContextAllocZero(accessor->context,
-									   STS_CHUNK_PAGES * BLCKSZ);
+									   STS_CHUNK_PAGES * cluster_block_size);
 			accessor->write_chunk->ntuples = 0;
 			accessor->write_pointer = &accessor->write_chunk->data[0];
 			accessor->write_end = (char *)
-				accessor->write_chunk + STS_CHUNK_PAGES * BLCKSZ;
+				accessor->write_chunk + STS_CHUNK_PAGES * cluster_block_size;
 		}
 		else
 		{
@@ -445,7 +445,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
 	}
 	remaining_size = size - sizeof(uint32);
 	this_chunk_size = Min(remaining_size,
-						  BLCKSZ * STS_CHUNK_PAGES - accessor->read_bytes);
+						  cluster_block_size * STS_CHUNK_PAGES - accessor->read_bytes);
 	destination = accessor->read_buffer + sizeof(uint32);
 	BufFileReadExact(accessor->read_file, destination, this_chunk_size);
 	accessor->read_bytes += this_chunk_size;
@@ -468,7 +468,7 @@ sts_read_tuple(SharedTuplestoreAccessor *accessor, void *meta_data)
 					 errdetail_internal("Expected overflow chunk.")));
 		accessor->read_next_page += STS_CHUNK_PAGES;
 		this_chunk_size = Min(remaining_size,
-							  BLCKSZ * STS_CHUNK_PAGES -
+							  cluster_block_size * STS_CHUNK_PAGES -
 							  STS_CHUNK_HEADER_SIZE);
 		BufFileReadExact(accessor->read_file, destination, this_chunk_size);
 		accessor->read_bytes += this_chunk_size;
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index c7a6c03f97..f6f98bb273 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -179,8 +179,8 @@ typedef enum
  */
 #define MINORDER		6		/* minimum merge order */
 #define MAXORDER		500		/* maximum merge order */
-#define TAPE_BUFFER_OVERHEAD		BLCKSZ
-#define MERGE_BUFFER_SIZE			(BLCKSZ * 32)
+#define TAPE_BUFFER_OVERHEAD		cluster_block_size
+#define MERGE_BUFFER_SIZE			(cluster_block_size * 32)
 
 
 /*
@@ -1000,7 +1000,7 @@ tuplesort_updatemax(Tuplesortstate *state)
 	if (state->tapeset)
 	{
 		isSpaceDisk = true;
-		spaceUsed = LogicalTapeSetBlocks(state->tapeset) * BLCKSZ;
+		spaceUsed = LogicalTapeSetBlocks(state->tapeset) * cluster_block_size;
 	}
 	else
 	{
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 0c6f5ceb0a..8b00ecd43b 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1109,8 +1109,8 @@ test_config_settings(void)
 
 	for (i = 0; i < bufslen; i++)
 	{
-		/* Use same amount of memory, independent of BLCKSZ */
-		test_buffs = (trial_bufs[i] * 8192) / BLCKSZ;
+		/* Use same amount of memory, independent of cluster_block_size */
+		test_buffs = (trial_bufs[i] * 8192) / cluster_block_size;
 		if (test_buffs <= ok_buffers)
 		{
 			test_buffs = ok_buffers;
@@ -1122,10 +1122,10 @@ test_config_settings(void)
 	}
 	n_buffers = test_buffs;
 
-	if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
-		printf("%dMB\n", (n_buffers * (BLCKSZ / 1024)) / 1024);
+	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
+		printf("%dMB\n", (n_buffers * (cluster_block_size / 1024)) / 1024);
 	else
-		printf("%dkB\n", n_buffers * (BLCKSZ / 1024));
+		printf("%dkB\n", n_buffers * (cluster_block_size / 1024));
 
 	printf(_("selecting default time zone ... "));
 	fflush(stdout);
@@ -1217,12 +1217,12 @@ setup_config(void)
 	conflines = replace_guc_value(conflines, "max_connections",
 								  repltok, false);
 
-	if ((n_buffers * (BLCKSZ / 1024)) % 1024 == 0)
+	if ((n_buffers * (cluster_block_size / 1024)) % 1024 == 0)
 		snprintf(repltok, sizeof(repltok), "%dMB",
-				 (n_buffers * (BLCKSZ / 1024)) / 1024);
+				 (n_buffers * (cluster_block_size / 1024)) / 1024);
 	else
 		snprintf(repltok, sizeof(repltok), "%dkB",
-				 n_buffers * (BLCKSZ / 1024));
+				 n_buffers * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "shared_buffers",
 								  repltok, false);
 
@@ -1297,21 +1297,21 @@ setup_config(void)
 
 #if DEFAULT_BACKEND_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BACKEND_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_BACKEND_FLUSH_AFTER * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "backend_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_BGWRITER_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_BGWRITER_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_BGWRITER_FLUSH_AFTER * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "bgwriter_flush_after",
 								  repltok, true);
 #endif
 
 #if DEFAULT_CHECKPOINT_FLUSH_AFTER > 0
 	snprintf(repltok, sizeof(repltok), "%dkB",
-			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (BLCKSZ / 1024));
+			 DEFAULT_CHECKPOINT_FLUSH_AFTER * (cluster_block_size / 1024));
 	conflines = replace_guc_value(conflines, "checkpoint_flush_after",
 								  repltok, true);
 #endif
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index e009ba5e0b..2c6469b6ed 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -196,18 +196,18 @@ scan_file(const char *fn, int segmentno)
 	for (blockno = 0;; blockno++)
 	{
 		uint16		csum;
-		int			r = read(f, buf.data, BLCKSZ);
+		int			r = read(f, buf.data, cluster_block_size);
 
 		if (r == 0)
 			break;
-		if (r != BLCKSZ)
+		if (r != cluster_block_size)
 		{
 			if (r < 0)
 				pg_fatal("could not read block %u in file \"%s\": %m",
 						 blockno, fn);
 			else
 				pg_fatal("could not read block %u in file \"%s\": read %d of %d",
-						 blockno, fn, r, BLCKSZ);
+						 blockno, fn, r, cluster_block_size);
 		}
 		blocks_scanned++;
 
@@ -251,19 +251,19 @@ scan_file(const char *fn, int segmentno)
 			header->pd_checksum = csum;
 
 			/* Seek back to beginning of block */
-			if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
+			if (lseek(f, -cluster_block_size, SEEK_CUR) < 0)
 				pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
 
 			/* Write block with checksum */
-			w = write(f, buf.data, BLCKSZ);
-			if (w != BLCKSZ)
+			w = write(f, buf.data, cluster_block_size);
+			if (w != cluster_block_size)
 			{
 				if (w < 0)
 					pg_fatal("could not write block %u in file \"%s\": %m",
 							 blockno, fn);
 				else
 					pg_fatal("could not write block %u in file \"%s\": wrote %d of %d",
-							 blockno, fn, w, BLCKSZ);
+							 blockno, fn, w, cluster_block_size);
 			}
 		}
 
@@ -548,11 +548,11 @@ main(int argc, char *argv[])
 	if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
 		pg_fatal("cluster is not compatible with this version of pg_checksums");
 
-	if (ControlFile->blcksz != BLCKSZ)
+	if (ControlFile->blcksz != cluster_block_size)
 	{
 		pg_log_error("database cluster is not compatible");
 		pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
-							ControlFile->blcksz, BLCKSZ);
+							ControlFile->blcksz, cluster_block_size);
 		exit(1);
 	}
 
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 04567f349d..01dcf3be63 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -687,14 +687,14 @@ GuessControlValues(void)
 
 	ControlFile.maxAlign = MAXIMUM_ALIGNOF;
 	ControlFile.floatFormat = FLOATFORMAT_VALUE;
-	ControlFile.blcksz = BLCKSZ;
+	ControlFile.blcksz = cluster_block_size;
 	ControlFile.relseg_size = RELSEG_SIZE;
 	ControlFile.xlog_blcksz = XLOG_BLCKSZ;
 	ControlFile.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
 	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 = cluster_toast_max_chunk_size;
+	ControlFile.loblksize = cluster_loblksize;
 	ControlFile.float8ByVal = FLOAT8PASSBYVAL;
 
 	/*
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index ecadd69dc5..f0d0c44c85 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -333,7 +333,7 @@ process_target_wal_block_change(ForkNumber forknum, RelFileLocator rlocator,
 			{
 				off_t		end_offset;
 
-				end_offset = (blkno_inseg + 1) * BLCKSZ;
+				end_offset = (blkno_inseg + 1) * cluster_block_size;
 				if (end_offset <= entry->source_size && end_offset <= entry->target_size)
 					datapagemap_add(&entry->target_pages_to_overwrite, blkno_inseg);
 			}
@@ -468,7 +468,7 @@ calculate_totals(filemap_t *filemap)
 
 			iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
 			while (datapagemap_next(iter, &blk))
-				filemap->fetch_size += BLCKSZ;
+				filemap->fetch_size += cluster_block_size;
 
 			pg_free(iter);
 		}
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index bfd44a284e..9c84bc1ac7 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -580,8 +580,8 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
 			iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
 			while (datapagemap_next(iter, &blkno))
 			{
-				offset = blkno * BLCKSZ;
-				source->queue_fetch_range(source, entry->path, offset, BLCKSZ);
+				offset = blkno * cluster_block_size;
+				source->queue_fetch_range(source, entry->path, offset, cluster_block_size);
 			}
 			pg_free(iter);
 		}
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index d173602882..3143cf28aa 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -96,7 +96,7 @@ copyFile(const char *src, const char *dst,
 				 schemaName, relName, dst, strerror(errno));
 
 	/* copy in fairly large chunks for best efficiency */
-#define COPY_BUF_SIZE (50 * BLCKSZ)
+#define COPY_BUF_SIZE (50 * cluster_block_size)
 
 	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
 
@@ -187,7 +187,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 	struct stat statbuf;
 
 	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
+	rewriteVmBytesPerPage = (cluster_block_size - SizeOfPageHeaderData) / 2;
 
 	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
 		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
@@ -220,7 +220,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 		PageHeaderData pageheader;
 		bool		old_lastblk;
 
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
+		if ((bytesRead = read(src_fd, buffer.data, cluster_block_size)) != cluster_block_size)
 		{
 			if (bytesRead < 0)
 				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
@@ -230,7 +230,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 						 schemaName, relName, fromfile);
 		}
 
-		totalBytesRead += BLCKSZ;
+		totalBytesRead += cluster_block_size;
 		old_lastblk = (totalBytesRead == src_filesize);
 
 		/* Save the page header data */
@@ -296,7 +296,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 					pg_checksum_page(new_vmbuf.data, new_blkno);
 
 			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
+			if (write(dst_fd, new_vmbuf.data, cluster_block_size) != cluster_block_size)
 			{
 				/* if write didn't set errno, assume problem is no disk space */
 				if (errno == 0)
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index a3535bdfa9..cc0247018a 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -531,7 +531,7 @@ XLogRecordSaveFPWs(XLogReaderState *record, const char *savepath)
 		if (!file)
 			pg_fatal("could not open file \"%s\": %m", filename);
 
-		if (fwrite(page, BLCKSZ, 1, file) != 1)
+		if (fwrite(page, cluster_block_size, 1, file) != 1)
 			pg_fatal("could not write file \"%s\": %m", filename);
 
 		if (fclose(file) != 0)
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index abe5129412..7876625db9 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -659,7 +659,7 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
 ssize_t
 pg_pwrite_zeros(int fd, size_t size, off_t offset)
 {
-	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth BLCKSZ */
+	static const PGIOAlignedBlock zbuffer = {{0}};	/* worth cluster_block_size */
 	void	   *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
 	struct iovec iov[PG_IOV_MAX];
 	size_t		remaining_size = size;
@@ -677,10 +677,10 @@ pg_pwrite_zeros(int fd, size_t size, off_t offset)
 
 			iov[iovcnt].iov_base = zerobuf_addr;
 
-			if (remaining_size < BLCKSZ)
+			if (remaining_size < cluster_block_size)
 				this_iov_size = remaining_size;
 			else
-				this_iov_size = BLCKSZ;
+				this_iov_size = cluster_block_size;
 
 			iov[iovcnt].iov_len = this_iov_size;
 			remaining_size -= this_iov_size;
diff --git a/src/include/access/brin_page.h b/src/include/access/brin_page.h
index 3670ca6010..a4881cb971 100644
--- a/src/include/access/brin_page.h
+++ b/src/include/access/brin_page.h
@@ -85,12 +85,12 @@ typedef struct RevmapContents
 	ItemPointerData rm_tids[1];
 } RevmapContents;
 
-#define REVMAP_CONTENT_SIZE \
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
+#define revmap_content_size \
+	(cluster_block_size - MAXALIGN(SizeOfPageHeaderData) - \
 	 offsetof(RevmapContents, rm_tids) - \
 	 MAXALIGN(sizeof(BrinSpecialSpace)))
 /* max num of items in the array */
 #define REVMAP_PAGE_MAXITEMS \
-	(REVMAP_CONTENT_SIZE / sizeof(ItemPointerData))
+	(revmap_content_size / sizeof(ItemPointerData))
 
 #endif							/* BRIN_PAGE_H */
diff --git a/src/include/access/ginblock.h b/src/include/access/ginblock.h
index c59790ec5a..191471cb1c 100644
--- a/src/include/access/ginblock.h
+++ b/src/include/access/ginblock.h
@@ -248,7 +248,7 @@ typedef signed char GinNullCategory;
  */
 #define GinMaxItemSize \
 	Min(INDEX_SIZE_MASK, \
-		MAXALIGN_DOWN(((BLCKSZ - \
+		MAXALIGN_DOWN(((cluster_block_size - \
 						MAXALIGN(SizeOfPageHeaderData + 3 * sizeof(ItemIdData)) - \
 						MAXALIGN(sizeof(GinPageOpaqueData))) / 3)))
 
@@ -318,7 +318,7 @@ typedef signed char GinNullCategory;
 	 GinPageGetOpaque(page)->maxoff * sizeof(PostingItem))
 
 #define GinDataPageMaxDataSize	\
-	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+	(cluster_block_size - MAXALIGN(SizeOfPageHeaderData) \
 	 - MAXALIGN(sizeof(ItemPointerData)) \
 	 - MAXALIGN(sizeof(GinPageOpaqueData)))
 
@@ -326,7 +326,7 @@ typedef signed char GinNullCategory;
  * List pages
  */
 #define GinListPageSize  \
-	( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
+	( cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
 
 /*
  * A compressed posting list.
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 0235716c06..5fff12eb65 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -96,7 +96,7 @@ typedef GISTPageOpaqueData *GISTPageOpaque;
  * key size using opclass parameters.
  */
 #define GISTMaxIndexTupleSize	\
-	MAXALIGN_DOWN((BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
+	MAXALIGN_DOWN((cluster_block_size - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
 				  4 - sizeof(ItemIdData))
 
 #define GISTMaxIndexKeySize	\
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 3edc740a3f..4c1dd74db7 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -54,7 +54,7 @@ typedef struct
 /* Returns free space in node buffer page */
 #define PAGE_FREE_SPACE(nbp) (nbp->freespace)
 /* Checks if node buffer page is empty */
-#define PAGE_IS_EMPTY(nbp) (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)
+#define PAGE_IS_EMPTY(nbp) (nbp->freespace == cluster_block_size - BUFFER_PAGE_DATA_OFFSET)
 /* Checks if node buffers page don't contain sufficient space for index tuple */
 #define PAGE_NO_SPACE(nbp, itup) (PAGE_FREE_SPACE(nbp) < \
 										MAXALIGN(IndexTupleSize(itup)))
@@ -171,7 +171,7 @@ typedef struct GISTScanOpaqueData
 	GistNSN		curPageLSN;		/* pos in the WAL stream when page was read */
 
 	/* In a non-ordered search, returnable heap items are stored here: */
-	GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
+	GISTSearchHeapItem pageData[cluster_block_size / sizeof(IndexTupleData)];
 	OffsetNumber nPageData;		/* number of valid items in array */
 	OffsetNumber curPageData;	/* next item to return */
 	MemoryContext pageDataCxt;	/* context holding the fetched tuples, for
@@ -474,7 +474,7 @@ extern void gistadjustmembers(Oid opfamilyoid,
 /* gistutil.c */
 
 #define GiSTPageSize   \
-	( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
+	( cluster_block_size - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
 
 #define GIST_MIN_FILLFACTOR			10
 #define GIST_DEFAULT_FILLFACTOR		90
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 9e035270a1..4f6df08020 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -224,10 +224,10 @@ typedef HashScanOpaqueData *HashScanOpaque;
  * needing to fit into the metapage.  (With 8K block size, 1024 bitmaps
  * limit us to 256 GB of overflow space...).  For smaller block size we
  * can not use 1024 bitmaps as it will lead to the meta page data crossing
- * the block size boundary.  So we use BLCKSZ to determine the maximum number
+ * the block size boundary.  So we use cluster_block_size to determine the maximum number
  * of bitmaps.
  */
-#define HASH_MAX_BITMAPS			Min(BLCKSZ / 8, 1024)
+#define HASH_MAX_BITMAPS			Min(cluster_block_size / 8, 1024)
 
 #define HASH_SPLITPOINT_PHASE_BITS	2
 #define HASH_SPLITPOINT_PHASES_PER_GRP	(1 << HASH_SPLITPOINT_PHASE_BITS)
@@ -279,7 +279,7 @@ typedef struct HashOptions
 	 ((HashOptions *) (relation)->rd_options)->fillfactor :	\
 	 HASH_DEFAULT_FILLFACTOR)
 #define HashGetTargetPageUsage(relation) \
-	(BLCKSZ * HashGetFillFactor(relation) / 100)
+	(cluster_block_size * HashGetFillFactor(relation) / 100)
 
 /*
  * Maximum size of a hash index item (it's okay to have only one per page)
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index 5c0a796f66..3658856a60 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -21,14 +21,14 @@
  * Find the maximum size of a tuple if there are to be N tuples per page.
  */
 #define MaximumBytesPerTuple(tuplesPerPage) \
-	MAXALIGN_DOWN((BLCKSZ - \
+	MAXALIGN_DOWN((cluster_block_size - \
 				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
 				  / (tuplesPerPage))
 
 /*
  * These symbols control toaster activation.  If a tuple is larger than
- * TOAST_TUPLE_THRESHOLD, we will try to toast it down to no more than
- * TOAST_TUPLE_TARGET bytes through compressing compressible fields and
+ * toast_tuple_threshold, we will try to toast it down to no more than
+ * cluster_toast_tuple_target bytes through compressing compressible fields and
  * moving EXTENDED and EXTERNAL data out-of-line.
  *
  * The numbers need not be the same, though they currently are.  It doesn't
@@ -40,14 +40,14 @@
  *
  * XXX while these can be modified without initdb, some thought needs to be
  * given to needs_toast_table() in toasting.c before unleashing random
- * changes.  Also see LOBLKSIZE in large_object.h, which can *not* be
+ * changes.  Also see cluster_loblksize in large_object.h, which can *not* be
  * changed without initdb.
  */
 #define TOAST_TUPLES_PER_PAGE	4
 
-#define TOAST_TUPLE_THRESHOLD	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
+#define toast_tuple_threshold	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)
 
-#define TOAST_TUPLE_TARGET		TOAST_TUPLE_THRESHOLD
+#define cluster_toast_tuple_target		toast_tuple_threshold
 
 /*
  * The code will also consider moving MAIN data out-of-line, but only as a
@@ -58,7 +58,7 @@
  */
 #define TOAST_TUPLES_PER_PAGE_MAIN	1
 
-#define TOAST_TUPLE_TARGET_MAIN MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
+#define toast_tuple_target_main MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)
 
 /*
  * If an index value is larger than TOAST_INDEX_TARGET, we will try to
@@ -69,19 +69,19 @@
 
 /*
  * When we store an oversize datum externally, we divide it into chunks
- * containing at most TOAST_MAX_CHUNK_SIZE data bytes.  This number *must*
+ * containing at most cluster_toast_max_chunk_size data bytes.  This number *must*
  * be small enough that the completed toast-table tuple (including the
  * ID and sequence fields and all overhead) will fit on a page.
  * The coding here sets the size on the theory that we want to fit
  * EXTERN_TUPLES_PER_PAGE tuples of maximum size onto a page.
  *
- * NB: Changing TOAST_MAX_CHUNK_SIZE requires an initdb.
+ * NB: Changing cluster_toast_max_chunk_size requires an initdb.
  */
 #define EXTERN_TUPLES_PER_PAGE	4	/* tweak only this */
 
 #define EXTERN_TUPLE_MAX_SIZE	MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE)
 
-#define TOAST_MAX_CHUNK_SIZE	\
+#define cluster_toast_max_chunk_size	\
 	(EXTERN_TUPLE_MAX_SIZE -							\
 	 MAXALIGN(SizeofHeapTupleHeader) -					\
 	 sizeof(Oid) -										\
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index e01f4f35c8..005a530e0a 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -551,7 +551,7 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
 
 /*
  * MaxHeapTupleSize is the maximum allowed size of a heap tuple, including
- * header and MAXALIGN alignment padding.  Basically it's BLCKSZ minus the
+ * header and MAXALIGN alignment padding.  Basically it's cluster_block_size minus the
  * other stuff that has to be on a disk page.  Since heap pages use no
  * "special space", there's no deduction for that.
  *
@@ -560,7 +560,7 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * ItemIds and tuples have different alignment requirements, don't assume that
  * you can, say, fit 2 tuples of size MaxHeapTupleSize/2 on the same page.
  */
-#define MaxHeapTupleSize  (BLCKSZ - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+#define MaxHeapTupleSize  (cluster_block_size - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
 #define MinHeapTupleSize  MAXALIGN(SizeofHeapTupleHeader)
 
 /*
@@ -575,7 +575,7 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * require increases in the size of work arrays.
  */
 #define MaxHeapTuplesPerPage	\
-	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
+	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
 			(MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))))
 
 /*
diff --git a/src/include/access/itup.h b/src/include/access/itup.h
index 2e2b8c7a47..8a68b9405b 100644
--- a/src/include/access/itup.h
+++ b/src/include/access/itup.h
@@ -164,7 +164,7 @@ index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
  * But such a page always has at least MAXALIGN special space, so we're safe.
  */
 #define MaxIndexTuplesPerPage	\
-	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
+	((int) ((cluster_block_size - SizeOfPageHeaderData) / \
 			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))
 
 #endif							/* ITUP_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 6345e16d78..794a3da1b2 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -183,7 +183,7 @@ typedef struct BTMetaPageData
  * than necessary as a result, which is considered acceptable.
  */
 #define MaxTIDsPerBTreePage \
-	(int) ((BLCKSZ - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
+	(int) ((cluster_block_size - SizeOfPageHeaderData - sizeof(BTPageOpaqueData)) / \
 		   sizeof(ItemPointerData))
 
 /*
@@ -1059,7 +1059,7 @@ typedef struct BTScanOpaqueData
 	/*
 	 * If we are doing an index-only scan, these are the tuple storage
 	 * workspaces for the currPos and markPos respectively.  Each is of size
-	 * BLCKSZ, so it can hold as much as a full page's worth of tuples.
+	 * cluster_block_size, so it can hold as much as a full page's worth of tuples.
 	 */
 	char	   *currTuples;		/* tuple storage for currPos */
 	char	   *markTuples;		/* tuple storage for markPos */
@@ -1106,7 +1106,7 @@ typedef struct BTOptions
 	 ((BTOptions *) (relation)->rd_options)->fillfactor : \
 	 BTREE_DEFAULT_FILLFACTOR)
 #define BTGetTargetPageFreeSpace(relation) \
-	(BLCKSZ * (100 - BTGetFillFactor(relation)) / 100)
+	(cluster_block_size * (100 - BTGetFillFactor(relation)) / 100)
 #define BTGetDeduplicateItems(relation) \
 	(AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
 				 relation->rd_rel->relam == BTREE_AM_OID), \
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index a8a424d92d..f008a01acb 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -19,7 +19,7 @@
 
 
 /*
- * Define SLRU segment size.  A page is the same BLCKSZ as is used everywhere
+ * Define SLRU segment size.  A page is the same cluster_block_size as is used everywhere
  * else in Postgres.  The segment size can be chosen somewhat arbitrarily;
  * we make it 32 pages by default, or 256Kb, i.e. 1M transactions for CLOG
  * or 64K transactions for SUBTRANS.
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index c6ef46fc20..ea803eccd8 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -36,7 +36,7 @@ typedef struct SpGistOptions
 	 ((SpGistOptions *) (relation)->rd_options)->fillfactor : \
 	 SPGIST_DEFAULT_FILLFACTOR)
 #define SpGistGetTargetPageFreeSpace(relation) \
-	(BLCKSZ * (100 - SpGistGetFillFactor(relation)) / 100)
+	(cluster_block_size * (100 - SpGistGetFillFactor(relation)) / 100)
 
 
 /* SPGiST leaf tuples have one key column, optionally have included columns */
@@ -444,8 +444,8 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
  */
 
 /* Page capacity after allowing for fixed header and special space */
-#define SPGIST_PAGE_CAPACITY  \
-	MAXALIGN_DOWN(BLCKSZ - \
+#define spgist_page_capacity  \
+	MAXALIGN_DOWN(cluster_block_size - \
 				  SizeOfPageHeaderData - \
 				  MAXALIGN(sizeof(SpGistPageOpaqueData)))
 
diff --git a/src/include/access/xlogrecord.h b/src/include/access/xlogrecord.h
index f355e08e1d..5395678786 100644
--- a/src/include/access/xlogrecord.h
+++ b/src/include/access/xlogrecord.h
@@ -122,21 +122,21 @@ typedef struct XLogRecordBlockHeader
  * in the middle, which contains only zero bytes.  Since we know that the
  * "hole" is all zeros, we remove it from the stored data (and it's not counted
  * in the XLOG record's CRC, either).  Hence, the amount of block data actually
- * present is (BLCKSZ - <length of "hole" bytes>).
+ * present is (cluster_block_size - <length of "hole" bytes>).
  *
  * Additionally, when wal_compression is enabled, we will try to compress full
  * page images using one of the supported algorithms, after removing the
  * "hole". This can reduce the WAL volume, but at some extra cost of CPU spent
  * on the compression during WAL logging. In this case, since the "hole"
  * length cannot be calculated by subtracting the number of page image bytes
- * from BLCKSZ, basically it needs to be stored as an extra information.
+ * from cluster_block_size, basically it needs to be stored as an extra information.
  * But when no "hole" exists, we can assume that the "hole" length is zero
  * and no such an extra information needs to be stored. Note that
  * the original version of page image is stored in WAL instead of the
  * compressed one if the number of bytes saved by compression is less than
  * the length of extra information. Hence, when a page image is successfully
  * compressed, the amount of block data actually present is less than
- * BLCKSZ - the length of "hole" bytes - the length of extra information.
+ * cluster_block_size - the length of "hole" bytes - the length of extra information.
  */
 typedef struct XLogRecordBlockImageHeader
 {
diff --git a/src/include/backup/basebackup_sink.h b/src/include/backup/basebackup_sink.h
index 224732e333..53b0e6e485 100644
--- a/src/include/backup/basebackup_sink.h
+++ b/src/include/backup/basebackup_sink.h
@@ -80,7 +80,7 @@ typedef struct bbsink_state
  * 'bbs_ops' is the relevant callback table.
  *
  * 'bbs_buffer' is the buffer into which data destined for the bbsink
- * should be stored. It must be a multiple of BLCKSZ.
+ * should be stored. It must be a multiple of cluster_block_size.
  *
  * 'bbs_buffer_length' is the allocated length of the buffer.
  *
@@ -183,7 +183,7 @@ bbsink_begin_backup(bbsink *sink, bbsink_state *state, int buffer_length)
 	sink->bbs_ops->begin_backup(sink);
 
 	Assert(sink->bbs_buffer != NULL);
-	Assert((sink->bbs_buffer_length % BLCKSZ) == 0);
+	Assert((sink->bbs_buffer_length % cluster_block_size) == 0);
 }
 
 /* Begin an archive. */
diff --git a/src/include/c.h b/src/include/c.h
index 82f8e9d4c7..f156886798 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1123,7 +1123,7 @@ extern void ExceptionalCondition(const char *conditionName,
 	((var) = ((var) < 0) ? 1 : -(var))
 
 /*
- * Use this, not "char buf[BLCKSZ]", to declare a field or local variable
+ * Use this, not "char buf[cluster_block_size]", to declare a field or local variable
  * holding a page buffer, if that page might be accessed as a page.  Otherwise
  * the variable might be under-aligned, causing problems on alignment-picky
  * hardware.  We include both "double" and "int64" in the union to ensure that
@@ -1132,7 +1132,7 @@ extern void ExceptionalCondition(const char *conditionName,
  */
 typedef union PGAlignedBlock
 {
-	char		data[BLCKSZ];
+	char		data[cluster_block_size];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGAlignedBlock;
@@ -1150,7 +1150,7 @@ typedef union PGIOAlignedBlock
 #ifdef pg_attribute_aligned
 	pg_attribute_aligned(PG_IO_ALIGN_SIZE)
 #endif
-	char		data[BLCKSZ];
+	char		data[cluster_block_size];
 	double		force_align_d;
 	int64		force_align_i64;
 } PGIOAlignedBlock;
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 8a6e67a445..0446bfb407 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -33,7 +33,7 @@
  *
  * The minimum value is 8 (GIN indexes use 8-argument support functions).
  * The maximum possible value is around 600 (limited by index tuple size in
- * pg_proc's index; BLCKSZ larger than 8K would allow more).  Values larger
+ * pg_proc's index; cluster_block_size larger than 8K would allow more).  Values larger
  * than needed will waste memory and processing time, but do not directly
  * cost disk space.
  *
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index 240ad4e93b..1ed3d694ee 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -70,4 +70,5 @@ typedef PG_INT64_TYPE pg_int64;
 #define PG_DIAG_SOURCE_LINE		'L'
 #define PG_DIAG_SOURCE_FUNCTION 'R'
 
+#define cluster_block_size BLCKSZ
 #endif							/* POSTGRES_EXT_H */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index d89021f918..3e75807e71 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -321,7 +321,7 @@ BufferGetBlock(Buffer buffer)
 	if (BufferIsLocal(buffer))
 		return LocalBufferBlockPointers[-buffer - 1];
 	else
-		return (Block) (BufferBlocks + ((Size) (buffer - 1)) * BLCKSZ);
+		return (Block) (BufferBlocks + ((Size) (buffer - 1)) * cluster_block_size);
 }
 
 /*
@@ -339,7 +339,7 @@ static inline Size
 BufferGetPageSize(Buffer buffer)
 {
 	AssertMacro(BufferIsValid(buffer));
-	return (Size) BLCKSZ;
+	return (Size) cluster_block_size;
 }
 
 /*
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 424ecba028..20d8d7277a 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -325,7 +325,7 @@ static inline void
 PageValidateSpecialPointer(Page page)
 {
 	Assert(page);
-	Assert(((PageHeader) page)->pd_special <= BLCKSZ);
+	Assert(((PageHeader) page)->pd_special <= cluster_block_size);
 	Assert(((PageHeader) page)->pd_special >= SizeOfPageHeaderData);
 }
 
@@ -476,14 +476,14 @@ do { \
 						   PIV_LOG_WARNING | PIV_REPORT_STAT)
 
 /*
- * Check that BLCKSZ is a multiple of sizeof(size_t).  In
+ * Check that cluster_block_size is a multiple of sizeof(size_t).  In
  * PageIsVerifiedExtended(), it is much faster to check if a page is
  * full of zeroes using the native word size.  Note that this assertion
  * is kept within a header to make sure that StaticAssertDecl() works
  * across various combinations of platforms and compilers.
  */
-StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),
-				 "BLCKSZ has to be a multiple of sizeof(size_t)");
+StaticAssertDecl(cluster_block_size == ((cluster_block_size / sizeof(size_t)) * sizeof(size_t)),
+				 "cluster_block_size has to be a multiple of sizeof(size_t)");
 
 extern void PageInit(Page page, Size pageSize, Size specialSize);
 extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
diff --git a/src/include/storage/checksum_impl.h b/src/include/storage/checksum_impl.h
index 7b157161a2..18951c36c7 100644
--- a/src/include/storage/checksum_impl.h
+++ b/src/include/storage/checksum_impl.h
@@ -111,7 +111,7 @@
 typedef union
 {
 	PageHeaderData phdr;
-	uint32		data[BLCKSZ / (sizeof(uint32) * N_SUMS)][N_SUMS];
+	uint32		data[cluster_block_size / (sizeof(uint32) * N_SUMS)][N_SUMS];
 } PGChecksummablePage;
 
 /*
@@ -151,13 +151,13 @@ pg_checksum_block(const PGChecksummablePage *page)
 				j;
 
 	/* ensure that the size is compatible with the algorithm */
-	Assert(sizeof(PGChecksummablePage) == BLCKSZ);
+	Assert(sizeof(PGChecksummablePage) == cluster_block_size);
 
 	/* initialize partial checksums to their corresponding offsets */
 	memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets));
 
 	/* main checksum calculation */
-	for (i = 0; i < (uint32) (BLCKSZ / (sizeof(uint32) * N_SUMS)); i++)
+	for (i = 0; i < (uint32) (cluster_block_size / (sizeof(uint32) * N_SUMS)); i++)
 		for (j = 0; j < N_SUMS; j++)
 			CHECKSUM_COMP(sums[j], page->data[i][j]);
 
diff --git a/src/include/storage/fsm_internals.h b/src/include/storage/fsm_internals.h
index 9e314c83fa..3e96d2335a 100644
--- a/src/include/storage/fsm_internals.h
+++ b/src/include/storage/fsm_internals.h
@@ -48,10 +48,10 @@ typedef FSMPageData *FSMPage;
  * Number of non-leaf and leaf nodes, and nodes in total, on an FSM page.
  * These definitions are internal to fsmpage.c.
  */
-#define NodesPerPage (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
+#define NodesPerPage (cluster_block_size - MAXALIGN(SizeOfPageHeaderData) - \
 					  offsetof(FSMPageData, fp_nodes))
 
-#define NonLeafNodesPerPage (BLCKSZ / 2 - 1)
+#define NonLeafNodesPerPage (cluster_block_size / 2 - 1)
 #define LeafNodesPerPage (NodesPerPage - NonLeafNodesPerPage)
 
 /*
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
index db521f23eb..9fab334c7b 100644
--- a/src/include/storage/large_object.h
+++ b/src/include/storage/large_object.h
@@ -54,7 +54,7 @@ typedef struct LargeObjectDesc
 /*
  * Each "page" (tuple) of a large object can hold this much data
  *
- * We could set this as high as BLCKSZ less some overhead, but it seems
+ * We could set this as high as cluster_block_size less some overhead, but it seems
  * better to make it a smaller value, so that not as much space is used
  * up when a page-tuple is updated.  Note that the value is deliberately
  * chosen large enough to trigger the tuple toaster, so that we will
@@ -65,15 +65,15 @@ typedef struct LargeObjectDesc
  * since clients will often be written to send data in power-of-2 blocks.
  * This avoids unnecessary tuple updates caused by partial-page writes.
  *
- * NB: Changing LOBLKSIZE requires an initdb.
+ * NB: Changing cluster_loblksize requires an initdb.
  */
-#define LOBLKSIZE		(BLCKSZ / 4)
+#define cluster_loblksize		(cluster_block_size / 4)
 
 /*
  * Maximum length in bytes for a large object.  To make this larger, we'd
  * have to widen pg_largeobject.pageno as well as various internal variables.
  */
-#define MAX_LARGE_OBJECT_SIZE	((int64) INT_MAX * LOBLKSIZE)
+#define MAX_LARGE_OBJECT_SIZE	((int64) INT_MAX * cluster_loblksize)
 
 
 /*
diff --git a/src/include/storage/off.h b/src/include/storage/off.h
index 3540308069..7fbd45a084 100644
--- a/src/include/storage/off.h
+++ b/src/include/storage/off.h
@@ -25,7 +25,7 @@ typedef uint16 OffsetNumber;
 
 #define InvalidOffsetNumber		((OffsetNumber) 0)
 #define FirstOffsetNumber		((OffsetNumber) 1)
-#define MaxOffsetNumber			((OffsetNumber) (BLCKSZ / sizeof(ItemIdData)))
+#define MaxOffsetNumber			((OffsetNumber) (cluster_block_size / sizeof(ItemIdData)))
 
 /* ----------------
  *		support macros
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1426a353cd..08256699b4 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -368,14 +368,14 @@ typedef struct StdRdOptions
  *		Returns the relation's desired space usage per page in bytes.
  */
 #define RelationGetTargetPageUsage(relation, defaultff) \
-	(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
+	(cluster_block_size * RelationGetFillFactor(relation, defaultff) / 100)
 
 /*
  * RelationGetTargetPageFreeSpace
  *		Returns the relation's desired freespace per page in bytes.
  */
 #define RelationGetTargetPageFreeSpace(relation, defaultff) \
-	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+	(cluster_block_size * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
 
 /*
  * RelationIsUsedAsCatalogTable
diff --git a/src/test/modules/test_slru/test_slru.c b/src/test/modules/test_slru/test_slru.c
index ae21444c47..1d64fe0e69 100644
--- a/src/test/modules/test_slru/test_slru.c
+++ b/src/test/modules/test_slru/test_slru.c
@@ -77,7 +77,7 @@ test_slru_page_write(PG_FUNCTION_ARGS)
 
 	/* write given data to the page, up to the limit of the page */
 	strncpy(TestSlruCtl->shared->page_buffer[slotno], data,
-			BLCKSZ - 1);
+			cluster_block_size - 1);
 
 	SimpleLruWritePage(TestSlruCtl, slotno);
 	LWLockRelease(TestSLRULock);
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 93ed5e8cc0..12dc4aae8a 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -320,7 +320,7 @@ CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicat
 CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off);
 -- Generate enough garbage tuples in index to ensure that even the unique index
 -- with deduplication enabled has to check multiple leaf pages during unique
--- checking (at least with a BLCKSZ of 8192 or less)
+-- checking (at least with a cluster_block_size of 8192 or less)
 DO $$
 BEGIN
     FOR r IN 1..1350 LOOP
@@ -331,7 +331,7 @@ END$$;
 -- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple.
 -- The implementation prefers deleting existing items to merging any duplicate
 -- tuples into a posting list, so we need an explicit test to make sure we get
--- coverage (note that this test also assumes BLCKSZ is 8192 or less):
+-- coverage (note that this test also assumes cluster_block_size is 8192 or less):
 DROP INDEX plain_unique;
 DELETE FROM dedup_unique_test_table WHERE a = 1;
 INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i;
diff --git a/src/test/regress/expected/largeobject.out b/src/test/regress/expected/largeobject.out
index bdcede6728..81bfe9887a 100644
--- a/src/test/regress/expected/largeobject.out
+++ b/src/test/regress/expected/largeobject.out
@@ -353,7 +353,7 @@ SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
    670800
 (1 row)
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default cluster_block_size, cluster_loblksize = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
  lo_lseek 
diff --git a/src/test/regress/expected/largeobject_1.out b/src/test/regress/expected/largeobject_1.out
index d700910c35..b78dc77f7a 100644
--- a/src/test/regress/expected/largeobject_1.out
+++ b/src/test/regress/expected/largeobject_1.out
@@ -353,7 +353,7 @@ SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
    680800
 (1 row)
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default cluster_block_size, cluster_loblksize = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
  lo_lseek 
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 239f4a4755..f4e2ddf4f8 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -173,7 +173,7 @@ CREATE UNIQUE INDEX dedup_unique ON dedup_unique_test_table (a) WITH (deduplicat
 CREATE UNIQUE INDEX plain_unique ON dedup_unique_test_table (a) WITH (deduplicate_items=off);
 -- Generate enough garbage tuples in index to ensure that even the unique index
 -- with deduplication enabled has to check multiple leaf pages during unique
--- checking (at least with a BLCKSZ of 8192 or less)
+-- checking (at least with a cluster_block_size of 8192 or less)
 DO $$
 BEGIN
     FOR r IN 1..1350 LOOP
@@ -185,7 +185,7 @@ END$$;
 -- Exercise the LP_DEAD-bit-set tuple deletion code with a posting list tuple.
 -- The implementation prefers deleting existing items to merging any duplicate
 -- tuples into a posting list, so we need an explicit test to make sure we get
--- coverage (note that this test also assumes BLCKSZ is 8192 or less):
+-- coverage (note that this test also assumes cluster_block_size is 8192 or less):
 DROP INDEX plain_unique;
 DELETE FROM dedup_unique_test_table WHERE a = 1;
 INSERT INTO dedup_unique_test_table SELECT i FROM generate_series(0,450) i;
diff --git a/src/test/regress/sql/largeobject.sql b/src/test/regress/sql/largeobject.sql
index 800e4fcc6a..9dd258210b 100644
--- a/src/test/regress/sql/largeobject.sql
+++ b/src/test/regress/sql/largeobject.sql
@@ -191,7 +191,7 @@ UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS inte
 -- verify length of large object
 SELECT lo_lseek(fd, 0, 2) FROM lotest_stash_values;
 
--- with the default BLCKSZ, LOBLKSIZE = 2048, so this positions us for a block
+-- with the default cluster_block_size, cluster_loblksize = 2048, so this positions us for a block
 -- edge case
 SELECT lo_lseek(fd, 2030, 0) FROM lotest_stash_values;
 
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index a50f730260..f6a40ae730 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -212,7 +212,7 @@ sub GenerateFiles
 		ALIGNOF_PG_INT128_TYPE => undef,
 		ALIGNOF_SHORT => 2,
 		AC_APPLE_UNIVERSAL_BUILD => undef,
-		BLCKSZ => 1024 * $self->{options}->{blocksize},
+		cluster_block_size => 1024 * $self->{options}->{blocksize},
 		CONFIGURE_ARGS => '"' . $self->GetFakeConfigure() . '"',
 		DEF_PGPORT => $port,
 		DEF_PGPORT_STR => qq{"$port"},
-- 
2.40.1

v3-0006-Use-fastdiv-code-in-visibility-map.patchapplication/octet-stream; name=v3-0006-Use-fastdiv-code-in-visibility-map.patchDownload
From cb3d29862851cc2525282bb46b90e263a5d4a2d5 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Fri, 29 Sep 2023 15:03:00 -0400
Subject: [PATCH v3 6/8] Use fastdiv code in visibility map

---
 src/backend/access/heap/visibilitymap.c | 93 ++++++++++++++++++-------
 1 file changed, 68 insertions(+), 25 deletions(-)

diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index ab4d38fbe6..7c80df680c 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -105,18 +105,25 @@
  * extra headers, so the whole page minus the standard page header is
  * used for the bitmap.
  */
-#define MAPSIZE (cluster_block_size - MAXALIGN(SizeOfPageHeaderData))
+#define MAPSIZE(blocksize) (blocksize - MAXALIGN(SizeOfPageHeaderData))
 
 /* Number of heap blocks we can represent in one byte */
-#define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
+#define HEAPBLOCKS_PER_BYTE(blocksize) (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
+
+/* Init routine for our fastmath */
+#define MAPBLOCK_INIT if (unlikely(!mapblock_size))		\
+	{													\
+		mapblock_size = MAPSIZE(cluster_block_size);	\
+		mapblock_inv = pg_fastinverse(mapblock_size);	\
+	}
 
 /* Number of heap blocks we can represent in one visibility map page. */
-#define HEAPBLOCKS_PER_PAGE (MAPSIZE << BITS_PER_HEAPBLOCK)
+#define HEAPBLOCKS_PER_PAGE(blocksize) (MAPSIZE(blocksize) << BITS_PER_HEAPBLOCK)
 
 /* Mapping from heap block number to the right bit in the visibility map */
-#define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
-#define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE)
-#define HEAPBLK_TO_OFFSET(x) (((x) % HEAPBLOCKS_PER_BYTE) * BITS_PER_HEAPBLOCK)
+#define HEAPBLK_TO_MAPBLOCK(x) (pg_fastdiv((x),mapblock_size,mapblock_inv))
+#define HEAPBLK_TO_MAPBYTE(x) (pg_fastmod((x),mapblock_size,mapblock_inv) >> 2) /* always 4 blocks per byte */
+#define HEAPBLK_TO_OFFSET(x) (((x) & 0x3) << 1) /* always 2 bits per entry */
 
 /* Masks for counting subsets of bits in the visibility map. */
 #define VISIBLE_MASK64	UINT64CONST(0x5555555555555555) /* The lower bit of each
@@ -128,6 +135,9 @@
 static Buffer vm_readbuf(Relation rel, BlockNumber blkno, bool extend);
 static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks);
 
+/* storage for the fast div/mod inverse */
+static uint64 mapblock_inv = 0;
+static uint32 mapblock_size = 0;
 
 /*
  *	visibilitymap_clear - clear specified bits for one page in visibility map
@@ -139,13 +149,20 @@ static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks);
 bool
 visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
-	int			mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
-	int			mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
-	uint8		mask = flags << mapOffset;
+	BlockNumber mapBlock;
+	int			mapByte;
+	int			mapOffset;
+	uint8		mask;
 	char	   *map;
 	bool		cleared = false;
 
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
+	mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+	mask = flags << mapOffset;
+
 	/* Must never clear all_visible bit while leaving all_frozen bit set */
 	Assert(flags & VISIBILITYMAP_VALID_BITS);
 	Assert(flags != VISIBILITYMAP_ALL_VISIBLE);
@@ -192,7 +209,11 @@ visibilitymap_clear(Relation rel, BlockNumber heapBlk, Buffer vmbuf, uint8 flags
 void
 visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	BlockNumber mapBlock;
+
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
 
 	/* Reuse the old pinned buffer if possible */
 	if (BufferIsValid(*vmbuf))
@@ -216,7 +237,11 @@ visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
 bool
 visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	BlockNumber mapBlock;
+
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
 
 	return BufferIsValid(vmbuf) && BufferGetBlockNumber(vmbuf) == mapBlock;
 }
@@ -247,12 +272,18 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
 				  XLogRecPtr recptr, Buffer vmBuf, TransactionId cutoff_xid,
 				  uint8 flags)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
-	uint32		mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
-	uint8		mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+	BlockNumber mapBlock;
+	uint32		mapByte;
+	uint8		mapOffset;
 	Page		page;
 	uint8	   *map;
 
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
+	mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+
 #ifdef TRACE_VISIBILITYMAP
 	elog(DEBUG1, "vm_set %s %d", RelationGetRelationName(rel), heapBlk);
 #endif
@@ -337,12 +368,18 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf,
 uint8
 visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
 {
-	BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
-	uint32		mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
-	uint8		mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+	BlockNumber mapBlock;
+	uint32		mapByte;
+	uint8		mapOffset;
 	char	   *map;
 	uint8		result;
 
+	MAPBLOCK_INIT;
+
+	mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
+	mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
+	mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
+
 #ifdef TRACE_VISIBILITYMAP
 	elog(DEBUG1, "vm_get_status %s %d", RelationGetRelationName(rel), heapBlk);
 #endif
@@ -414,16 +451,16 @@ visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_fro
 		 */
 		map = (uint64 *) PageGetContents(BufferGetPage(mapBuffer));
 
-		Assert(MAPSIZE % sizeof(uint64) == 0);
+		Assert(mapblock_size % sizeof(uint64) == 0);
 
 		if (all_frozen == NULL)
 		{
-			for (i = 0; i < MAPSIZE / sizeof(uint64); i++)
+			for (i = 0; i < mapblock_size / sizeof(uint64); i++)
 				nvisible += pg_popcount64(map[i] & VISIBLE_MASK64);
 		}
 		else
 		{
-			for (i = 0; i < MAPSIZE / sizeof(uint64); i++)
+			for (i = 0; i < mapblock_size / sizeof(uint64); i++)
 			{
 				nvisible += pg_popcount64(map[i] & VISIBLE_MASK64);
 				nfrozen += pg_popcount64(map[i] & FROZEN_MASK64);
@@ -455,9 +492,15 @@ visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks)
 	BlockNumber newnblocks;
 
 	/* last remaining block, byte, and bit */
-	BlockNumber truncBlock = HEAPBLK_TO_MAPBLOCK(nheapblocks);
-	uint32		truncByte = HEAPBLK_TO_MAPBYTE(nheapblocks);
-	uint8		truncOffset = HEAPBLK_TO_OFFSET(nheapblocks);
+	BlockNumber truncBlock;
+	uint32		truncByte;
+	uint8		truncOffset;
+
+	MAPBLOCK_INIT;
+
+	truncBlock = HEAPBLK_TO_MAPBLOCK(nheapblocks);
+	truncByte = HEAPBLK_TO_MAPBYTE(nheapblocks);
+	truncOffset = HEAPBLK_TO_OFFSET(nheapblocks);
 
 #ifdef TRACE_VISIBILITYMAP
 	elog(DEBUG1, "vm_truncate %s %d", RelationGetRelationName(rel), nheapblocks);
@@ -501,7 +544,7 @@ visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks)
 		START_CRIT_SECTION();
 
 		/* Clear out the unwanted bytes. */
-		MemSet(&map[truncByte + 1], 0, MAPSIZE - (truncByte + 1));
+		MemSet(&map[truncByte + 1], 0, mapblock_size - (truncByte + 1));
 
 		/*----
 		 * Mask out the unwanted bits of the last remaining byte.
-- 
2.40.1

v3-0007-Use-libdivide-for-fast-division.patchapplication/octet-stream; name=v3-0007-Use-libdivide-for-fast-division.patchDownload
From f9f303325e7bb1eeca2d47a136602b5e41d0720a Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Fri, 29 Sep 2023 15:04:00 -0400
Subject: [PATCH v3 7/8] Use libdivide for fast division

---
 src/backend/access/heap/visibilitymap.c |   15 +-
 src/include/common/libdivide.h          | 3269 +++++++++++++++++++++++
 2 files changed, 3278 insertions(+), 6 deletions(-)
 create mode 100644 src/include/common/libdivide.h

diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 7c80df680c..c5aafdbfca 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -90,6 +90,7 @@
 #include "access/visibilitymap.h"
 #include "access/xloginsert.h"
 #include "access/xlogutils.h"
+#include "common/libdivide.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
 #include "storage/bufmgr.h"
@@ -111,17 +112,18 @@
 #define HEAPBLOCKS_PER_BYTE(blocksize) (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
 
 /* Init routine for our fastmath */
-#define MAPBLOCK_INIT if (unlikely(!mapblock_size))		\
-	{													\
-		mapblock_size = MAPSIZE(cluster_block_size);	\
-		mapblock_inv = pg_fastinverse(mapblock_size);	\
+#define MAPBLOCK_INIT if (unlikely(!mapblock_size))			\
+	{														\
+		mapblock_size = MAPSIZE(cluster_block_size);		\
+		mapblock_div = libdivide_u32_gen(mapblock_size);	\
+		mapblock_inv = pg_fastinverse(mapblock_size);		\
 	}
 
 /* Number of heap blocks we can represent in one visibility map page. */
 #define HEAPBLOCKS_PER_PAGE(blocksize) (MAPSIZE(blocksize) << BITS_PER_HEAPBLOCK)
 
 /* Mapping from heap block number to the right bit in the visibility map */
-#define HEAPBLK_TO_MAPBLOCK(x) (pg_fastdiv((x),mapblock_size,mapblock_inv))
+#define HEAPBLK_TO_MAPBLOCK(x) (libdivide_u32_do((x),&mapblock_div))
 #define HEAPBLK_TO_MAPBYTE(x) (pg_fastmod((x),mapblock_size,mapblock_inv) >> 2) /* always 4 blocks per byte */
 #define HEAPBLK_TO_OFFSET(x) (((x) & 0x3) << 1) /* always 2 bits per entry */
 
@@ -136,8 +138,9 @@ static Buffer vm_readbuf(Relation rel, BlockNumber blkno, bool extend);
 static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks);
 
 /* storage for the fast div/mod inverse */
-static uint64 mapblock_inv = 0;
 static uint32 mapblock_size = 0;
+static uint64 mapblock_inv;
+static struct libdivide_u32_t mapblock_div;
 
 /*
  *	visibilitymap_clear - clear specified bits for one page in visibility map
diff --git a/src/include/common/libdivide.h b/src/include/common/libdivide.h
new file mode 100644
index 0000000000..96dd272119
--- /dev/null
+++ b/src/include/common/libdivide.h
@@ -0,0 +1,3269 @@
+// libdivide.h - Optimized integer division
+// https://libdivide.com
+//
+// Copyright (C) 2010 - 2022 ridiculous_fish, <libdivide@ridiculousfish.com>
+// Copyright (C) 2016 - 2022 Kim Walisch, <kim.walisch@gmail.com>
+//
+// libdivide is dual-licensed under the Boost or zlib licenses.
+// You may use libdivide under the terms of either of these.
+// See LICENSE.txt for more details.
+
+#ifndef LIBDIVIDE_H
+#define LIBDIVIDE_H
+
+#define LIBDIVIDE_VERSION "5.0"
+#define LIBDIVIDE_VERSION_MAJOR 5
+#define LIBDIVIDE_VERSION_MINOR 0
+
+#include <stdint.h>
+#if !defined(__AVR__)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#if defined(LIBDIVIDE_SSE2)
+#include <emmintrin.h>
+#endif
+#if defined(LIBDIVIDE_AVX2) || defined(LIBDIVIDE_AVX512)
+#include <immintrin.h>
+#endif
+#if defined(LIBDIVIDE_NEON)
+#include <arm_neon.h>
+#endif
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#pragma warning(push)
+// disable warning C4146: unary minus operator applied
+// to unsigned type, result still unsigned
+#pragma warning(disable : 4146)
+// disable warning C4204: nonstandard extension used : non-constant aggregate
+// initializer
+//
+// It's valid C99
+#pragma warning(disable : 4204)
+#define LIBDIVIDE_VC
+#endif
+
+#if !defined(__has_builtin)
+#define __has_builtin(x) 0
+#endif
+
+#if defined(__SIZEOF_INT128__)
+#define HAS_INT128_T
+// clang-cl on Windows does not yet support 128-bit division
+#if !(defined(__clang__) && defined(LIBDIVIDE_VC))
+#define HAS_INT128_DIV
+#endif
+#endif
+
+#if defined(__x86_64__) || defined(_M_X64)
+#define LIBDIVIDE_X86_64
+#endif
+
+#if defined(__i386__)
+#define LIBDIVIDE_i386
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define LIBDIVIDE_GCC_STYLE_ASM
+#endif
+
+#if defined(__cplusplus) || defined(LIBDIVIDE_VC)
+#define LIBDIVIDE_FUNCTION __FUNCTION__
+#else
+#define LIBDIVIDE_FUNCTION __func__
+#endif
+
+// Set up forced inlining if possible.
+// We need both the attribute and keyword to avoid "might not be inlineable" warnings.
+#ifdef __has_attribute
+#if __has_attribute(always_inline)
+#define LIBDIVIDE_INLINE __attribute__((always_inline)) inline
+#endif
+#endif
+#ifndef LIBDIVIDE_INLINE
+#define LIBDIVIDE_INLINE inline
+#endif
+
+#if defined(__AVR__)
+#define LIBDIVIDE_ERROR(msg)
+#else
+#define LIBDIVIDE_ERROR(msg)                                                                     \
+    do {                                                                                         \
+        fprintf(stderr, "libdivide.h:%d: %s(): Error: %s\n", __LINE__, LIBDIVIDE_FUNCTION, msg); \
+        abort();                                                                                 \
+    } while (0)
+#endif
+
+#if defined(LIBDIVIDE_ASSERTIONS_ON) && !defined(__AVR__)
+#define LIBDIVIDE_ASSERT(x)                                                           \
+    do {                                                                              \
+        if (!(x)) {                                                                   \
+            fprintf(stderr, "libdivide.h:%d: %s(): Assertion failed: %s\n", __LINE__, \
+                LIBDIVIDE_FUNCTION, #x);                                              \
+            abort();                                                                  \
+        }                                                                             \
+    } while (0)
+#else
+#define LIBDIVIDE_ASSERT(x)
+#endif
+
+#ifdef __cplusplus
+namespace libdivide {
+#endif
+
+// pack divider structs to prevent compilers from padding.
+// This reduces memory usage by up to 43% when using a large
+// array of libdivide dividers and improves performance
+// by up to 10% because of reduced memory bandwidth.
+#pragma pack(push, 1)
+
+struct libdivide_u16_t {
+    uint16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s16_t {
+    int16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u32_t {
+    uint32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s32_t {
+    int32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u64_t {
+    uint64_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s64_t {
+    int64_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u16_branchfree_t {
+    uint16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s16_branchfree_t {
+    int16_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u32_branchfree_t {
+    uint32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s32_branchfree_t {
+    int32_t magic;
+    uint8_t more;
+};
+
+struct libdivide_u64_branchfree_t {
+    uint64_t magic;
+    uint8_t more;
+};
+
+struct libdivide_s64_branchfree_t {
+    int64_t magic;
+    uint8_t more;
+};
+
+#pragma pack(pop)
+
+// Explanation of the "more" field:
+//
+// * Bits 0-5 is the shift value (for shift path or mult path).
+// * Bit 6 is the add indicator for mult path.
+// * Bit 7 is set if the divisor is negative. We use bit 7 as the negative
+//   divisor indicator so that we can efficiently use sign extension to
+//   create a bitmask with all bits set to 1 (if the divisor is negative)
+//   or 0 (if the divisor is positive).
+//
+// u32: [0-4] shift value
+//      [5] ignored
+//      [6] add indicator
+//      magic number of 0 indicates shift path
+//
+// s32: [0-4] shift value
+//      [5] ignored
+//      [6] add indicator
+//      [7] indicates negative divisor
+//      magic number of 0 indicates shift path
+//
+// u64: [0-5] shift value
+//      [6] add indicator
+//      magic number of 0 indicates shift path
+//
+// s64: [0-5] shift value
+//      [6] add indicator
+//      [7] indicates negative divisor
+//      magic number of 0 indicates shift path
+//
+// In s32 and s64 branchfree modes, the magic number is negated according to
+// whether the divisor is negated. In branchfree strategy, it is not negated.
+
+enum {
+    LIBDIVIDE_16_SHIFT_MASK = 0x1F,
+    LIBDIVIDE_32_SHIFT_MASK = 0x1F,
+    LIBDIVIDE_64_SHIFT_MASK = 0x3F,
+    LIBDIVIDE_ADD_MARKER = 0x40,
+    LIBDIVIDE_NEGATIVE_DIVISOR = 0x80
+};
+
+static LIBDIVIDE_INLINE struct libdivide_s16_t libdivide_s16_gen(int16_t d);
+static LIBDIVIDE_INLINE struct libdivide_u16_t libdivide_u16_gen(uint16_t d);
+static LIBDIVIDE_INLINE struct libdivide_s32_t libdivide_s32_gen(int32_t d);
+static LIBDIVIDE_INLINE struct libdivide_u32_t libdivide_u32_gen(uint32_t d);
+static LIBDIVIDE_INLINE struct libdivide_s64_t libdivide_s64_gen(int64_t d);
+static LIBDIVIDE_INLINE struct libdivide_u64_t libdivide_u64_gen(uint64_t d);
+
+static LIBDIVIDE_INLINE struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen(int16_t d);
+static LIBDIVIDE_INLINE struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen(uint16_t d);
+static LIBDIVIDE_INLINE struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d);
+static LIBDIVIDE_INLINE struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d);
+static LIBDIVIDE_INLINE struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d);
+static LIBDIVIDE_INLINE struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_do_raw(int16_t numer, int16_t magic, uint8_t more);
+static LIBDIVIDE_INLINE int16_t libdivide_s16_do(
+    int16_t numer, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_do_raw(uint16_t numer, uint16_t magic, uint8_t more);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_do(
+    uint16_t numer, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_do(
+    int32_t numer, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_do(
+    uint32_t numer, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_do(
+    int64_t numer, const struct libdivide_s64_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_do(
+    uint64_t numer, const struct libdivide_u64_t *denom);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_branchfree_do(
+    int16_t numer, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_branchfree_do(
+    uint16_t numer, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_branchfree_do(
+    int32_t numer, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_branchfree_do(
+    uint32_t numer, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_branchfree_do(
+    int64_t numer, const struct libdivide_s64_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_branchfree_do(
+    uint64_t numer, const struct libdivide_u64_branchfree_t *denom);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_recover(const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_recover(const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom);
+
+static LIBDIVIDE_INLINE int16_t libdivide_s16_branchfree_recover(
+    const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint16_t libdivide_u16_branchfree_recover(
+    const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE int32_t libdivide_s32_branchfree_recover(
+    const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint32_t libdivide_u32_branchfree_recover(
+    const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE int64_t libdivide_s64_branchfree_recover(
+    const struct libdivide_s64_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint64_t libdivide_u64_branchfree_recover(
+    const struct libdivide_u64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+static LIBDIVIDE_INLINE uint16_t libdivide_mullhi_u16(uint16_t x, uint16_t y) {
+    uint32_t xl = x, yl = y;
+    uint32_t rl = xl * yl;
+    return (uint16_t)(rl >> 16);
+}
+
+static LIBDIVIDE_INLINE int16_t libdivide_mullhi_s16(int16_t x, int16_t y) {
+    int32_t xl = x, yl = y;
+    int32_t rl = xl * yl;
+    // needs to be arithmetic shift
+    return (int16_t)(rl >> 16);
+}
+
+static LIBDIVIDE_INLINE uint32_t libdivide_mullhi_u32(uint32_t x, uint32_t y) {
+    uint64_t xl = x, yl = y;
+    uint64_t rl = xl * yl;
+    return (uint32_t)(rl >> 32);
+}
+
+static LIBDIVIDE_INLINE int32_t libdivide_mullhi_s32(int32_t x, int32_t y) {
+    int64_t xl = x, yl = y;
+    int64_t rl = xl * yl;
+    // needs to be arithmetic shift
+    return (int32_t)(rl >> 32);
+}
+
+static LIBDIVIDE_INLINE uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) {
+#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
+    return __umulh(x, y);
+#elif defined(HAS_INT128_T)
+    __uint128_t xl = x, yl = y;
+    __uint128_t rl = xl * yl;
+    return (uint64_t)(rl >> 64);
+#else
+    // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64)
+    uint32_t mask = 0xFFFFFFFF;
+    uint32_t x0 = (uint32_t)(x & mask);
+    uint32_t x1 = (uint32_t)(x >> 32);
+    uint32_t y0 = (uint32_t)(y & mask);
+    uint32_t y1 = (uint32_t)(y >> 32);
+    uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0);
+    uint64_t x0y1 = x0 * (uint64_t)y1;
+    uint64_t x1y0 = x1 * (uint64_t)y0;
+    uint64_t x1y1 = x1 * (uint64_t)y1;
+    uint64_t temp = x1y0 + x0y0_hi;
+    uint64_t temp_lo = temp & mask;
+    uint64_t temp_hi = temp >> 32;
+
+    return x1y1 + temp_hi + ((temp_lo + x0y1) >> 32);
+#endif
+}
+
+static LIBDIVIDE_INLINE int64_t libdivide_mullhi_s64(int64_t x, int64_t y) {
+#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
+    return __mulh(x, y);
+#elif defined(HAS_INT128_T)
+    __int128_t xl = x, yl = y;
+    __int128_t rl = xl * yl;
+    return (int64_t)(rl >> 64);
+#else
+    // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64)
+    uint32_t mask = 0xFFFFFFFF;
+    uint32_t x0 = (uint32_t)(x & mask);
+    uint32_t y0 = (uint32_t)(y & mask);
+    int32_t x1 = (int32_t)(x >> 32);
+    int32_t y1 = (int32_t)(y >> 32);
+    uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0);
+    int64_t t = x1 * (int64_t)y0 + x0y0_hi;
+    int64_t w1 = x0 * (int64_t)y1 + (t & mask);
+
+    return x1 * (int64_t)y1 + (t >> 32) + (w1 >> 32);
+#endif
+}
+
+static LIBDIVIDE_INLINE int16_t libdivide_count_leading_zeros16(uint16_t val) {
+#if defined(__AVR__)
+    // Fast way to count leading zeros
+    // On the AVR 8-bit architecture __builtin_clz() works on a int16_t.
+    return __builtin_clz(val);
+#elif defined(__GNUC__) || __has_builtin(__builtin_clz)
+    // Fast way to count leading zeros
+    return __builtin_clz(val) - 16;
+#elif defined(LIBDIVIDE_VC)
+    unsigned long result;
+    if (_BitScanReverse(&result, (unsigned long)val)) {
+        return (int16_t)(15 - result);
+    }
+    return 0;
+#else
+    if (val == 0) return 16;
+    int16_t result = 4;
+    uint16_t hi = 0xFU << 12;
+    while ((val & hi) == 0) {
+        hi >>= 4;
+        result += 4;
+    }
+    while (val & hi) {
+        result -= 1;
+        hi <<= 1;
+    }
+    return result;
+#endif
+}
+
+static LIBDIVIDE_INLINE int32_t libdivide_count_leading_zeros32(uint32_t val) {
+#if defined(__AVR__)
+    // Fast way to count leading zeros
+    return __builtin_clzl(val);
+#elif defined(__GNUC__) || __has_builtin(__builtin_clz)
+    // Fast way to count leading zeros
+    return __builtin_clz(val);
+#elif defined(LIBDIVIDE_VC)
+    unsigned long result;
+    if (_BitScanReverse(&result, val)) {
+        return 31 - result;
+    }
+    return 0;
+#else
+    if (val == 0) return 32;
+    int32_t result = 8;
+    uint32_t hi = 0xFFU << 24;
+    while ((val & hi) == 0) {
+        hi >>= 8;
+        result += 8;
+    }
+    while (val & hi) {
+        result -= 1;
+        hi <<= 1;
+    }
+    return result;
+#endif
+}
+
+static LIBDIVIDE_INLINE int32_t libdivide_count_leading_zeros64(uint64_t val) {
+#if defined(__GNUC__) || __has_builtin(__builtin_clzll)
+    // Fast way to count leading zeros
+    return __builtin_clzll(val);
+#elif defined(LIBDIVIDE_VC) && defined(_WIN64)
+    unsigned long result;
+    if (_BitScanReverse64(&result, val)) {
+        return 63 - result;
+    }
+    return 0;
+#else
+    uint32_t hi = val >> 32;
+    uint32_t lo = val & 0xFFFFFFFF;
+    if (hi != 0) return libdivide_count_leading_zeros32(hi);
+    return 32 + libdivide_count_leading_zeros32(lo);
+#endif
+}
+
+// libdivide_32_div_16_to_16: divides a 32-bit uint {u1, u0} by a 16-bit
+// uint {v}. The result must fit in 16 bits.
+// Returns the quotient directly and the remainder in *r
+static LIBDIVIDE_INLINE uint16_t libdivide_32_div_16_to_16(
+    uint16_t u1, uint16_t u0, uint16_t v, uint16_t *r) {
+    uint32_t n = ((uint32_t)u1 << 16) | u0;
+    uint16_t result = (uint16_t)(n / v);
+    *r = (uint16_t)(n - result * (uint32_t)v);
+    return result;
+}
+
+// libdivide_64_div_32_to_32: divides a 64-bit uint {u1, u0} by a 32-bit
+// uint {v}. The result must fit in 32 bits.
+// Returns the quotient directly and the remainder in *r
+static LIBDIVIDE_INLINE uint32_t libdivide_64_div_32_to_32(
+    uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) {
+#if (defined(LIBDIVIDE_i386) || defined(LIBDIVIDE_X86_64)) && defined(LIBDIVIDE_GCC_STYLE_ASM)
+    uint32_t result;
+    __asm__("divl %[v]" : "=a"(result), "=d"(*r) : [v] "r"(v), "a"(u0), "d"(u1));
+    return result;
+#else
+    uint64_t n = ((uint64_t)u1 << 32) | u0;
+    uint32_t result = (uint32_t)(n / v);
+    *r = (uint32_t)(n - result * (uint64_t)v);
+    return result;
+#endif
+}
+
+// libdivide_128_div_64_to_64: divides a 128-bit uint {numhi, numlo} by a 64-bit uint {den}. The
+// result must fit in 64 bits. Returns the quotient directly and the remainder in *r
+static LIBDIVIDE_INLINE uint64_t libdivide_128_div_64_to_64(
+    uint64_t numhi, uint64_t numlo, uint64_t den, uint64_t *r) {
+    // N.B. resist the temptation to use __uint128_t here.
+    // In LLVM compiler-rt, it performs a 128/128 -> 128 division which is many times slower than
+    // necessary. In gcc it's better but still slower than the divlu implementation, perhaps because
+    // it's not LIBDIVIDE_INLINEd.
+#if defined(LIBDIVIDE_X86_64) && defined(LIBDIVIDE_GCC_STYLE_ASM)
+    uint64_t result;
+    __asm__("divq %[v]" : "=a"(result), "=d"(*r) : [v] "r"(den), "a"(numlo), "d"(numhi));
+    return result;
+#else
+    // We work in base 2**32.
+    // A uint32 holds a single digit. A uint64 holds two digits.
+    // Our numerator is conceptually [num3, num2, num1, num0].
+    // Our denominator is [den1, den0].
+    const uint64_t b = ((uint64_t)1 << 32);
+
+    // The high and low digits of our computed quotient.
+    uint32_t q1;
+    uint32_t q0;
+
+    // The normalization shift factor.
+    int shift;
+
+    // The high and low digits of our denominator (after normalizing).
+    // Also the low 2 digits of our numerator (after normalizing).
+    uint32_t den1;
+    uint32_t den0;
+    uint32_t num1;
+    uint32_t num0;
+
+    // A partial remainder.
+    uint64_t rem;
+
+    // The estimated quotient, and its corresponding remainder (unrelated to true remainder).
+    uint64_t qhat;
+    uint64_t rhat;
+
+    // Variables used to correct the estimated quotient.
+    uint64_t c1;
+    uint64_t c2;
+
+    // Check for overflow and divide by 0.
+    if (numhi >= den) {
+        if (r != NULL) *r = ~0ull;
+        return ~0ull;
+    }
+
+    // Determine the normalization factor. We multiply den by this, so that its leading digit is at
+    // least half b. In binary this means just shifting left by the number of leading zeros, so that
+    // there's a 1 in the MSB.
+    // We also shift numer by the same amount. This cannot overflow because numhi < den.
+    // The expression (-shift & 63) is the same as (64 - shift), except it avoids the UB of shifting
+    // by 64. The funny bitwise 'and' ensures that numlo does not get shifted into numhi if shift is
+    // 0. clang 11 has an x86 codegen bug here: see LLVM bug 50118. The sequence below avoids it.
+    shift = libdivide_count_leading_zeros64(den);
+    den <<= shift;
+    numhi <<= shift;
+    numhi |= (numlo >> (-shift & 63)) & (-(int64_t)shift >> 63);
+    numlo <<= shift;
+
+    // Extract the low digits of the numerator and both digits of the denominator.
+    num1 = (uint32_t)(numlo >> 32);
+    num0 = (uint32_t)(numlo & 0xFFFFFFFFu);
+    den1 = (uint32_t)(den >> 32);
+    den0 = (uint32_t)(den & 0xFFFFFFFFu);
+
+    // We wish to compute q1 = [n3 n2 n1] / [d1 d0].
+    // Estimate q1 as [n3 n2] / [d1], and then correct it.
+    // Note while qhat may be 2 digits, q1 is always 1 digit.
+    qhat = numhi / den1;
+    rhat = numhi % den1;
+    c1 = qhat * den0;
+    c2 = rhat * b + num1;
+    if (c1 > c2) qhat -= (c1 - c2 > den) ? 2 : 1;
+    q1 = (uint32_t)qhat;
+
+    // Compute the true (partial) remainder.
+    rem = numhi * b + num1 - q1 * den;
+
+    // We wish to compute q0 = [rem1 rem0 n0] / [d1 d0].
+    // Estimate q0 as [rem1 rem0] / [d1] and correct it.
+    qhat = rem / den1;
+    rhat = rem % den1;
+    c1 = qhat * den0;
+    c2 = rhat * b + num0;
+    if (c1 > c2) qhat -= (c1 - c2 > den) ? 2 : 1;
+    q0 = (uint32_t)qhat;
+
+    // Return remainder if requested.
+    if (r != NULL) *r = (rem * b + num0 - q0 * den) >> shift;
+    return ((uint64_t)q1 << 32) | q0;
+#endif
+}
+
+#if !(defined(HAS_INT128_T) && \
+      defined(HAS_INT128_DIV))
+
+// Bitshift a u128 in place, left (signed_shift > 0) or right (signed_shift < 0)
+static LIBDIVIDE_INLINE void libdivide_u128_shift(
+    uint64_t *u1, uint64_t *u0, int32_t signed_shift) {
+    if (signed_shift > 0) {
+        uint32_t shift = signed_shift;
+        *u1 <<= shift;
+        *u1 |= *u0 >> (64 - shift);
+        *u0 <<= shift;
+    } else if (signed_shift < 0) {
+        uint32_t shift = -signed_shift;
+        *u0 >>= shift;
+        *u0 |= *u1 << (64 - shift);
+        *u1 >>= shift;
+    }
+}
+
+#endif
+
+// Computes a 128 / 128 -> 64 bit division, with a 128 bit remainder.
+static LIBDIVIDE_INLINE uint64_t libdivide_128_div_128_to_64(
+    uint64_t u_hi, uint64_t u_lo, uint64_t v_hi, uint64_t v_lo, uint64_t *r_hi, uint64_t *r_lo) {
+#if defined(HAS_INT128_T) && defined(HAS_INT128_DIV)
+    __uint128_t ufull = u_hi;
+    __uint128_t vfull = v_hi;
+    ufull = (ufull << 64) | u_lo;
+    vfull = (vfull << 64) | v_lo;
+    uint64_t res = (uint64_t)(ufull / vfull);
+    __uint128_t remainder = ufull - (vfull * res);
+    *r_lo = (uint64_t)remainder;
+    *r_hi = (uint64_t)(remainder >> 64);
+    return res;
+#else
+    // Adapted from "Unsigned Doubleword Division" in Hacker's Delight
+    // We want to compute u / v
+    typedef struct {
+        uint64_t hi;
+        uint64_t lo;
+    } u128_t;
+    u128_t u = {u_hi, u_lo};
+    u128_t v = {v_hi, v_lo};
+
+    if (v.hi == 0) {
+        // divisor v is a 64 bit value, so we just need one 128/64 division
+        // Note that we are simpler than Hacker's Delight here, because we know
+        // the quotient fits in 64 bits whereas Hacker's Delight demands a full
+        // 128 bit quotient
+        *r_hi = 0;
+        return libdivide_128_div_64_to_64(u.hi, u.lo, v.lo, r_lo);
+    }
+    // Here v >= 2**64
+    // We know that v.hi != 0, so count leading zeros is OK
+    // We have 0 <= n <= 63
+    uint32_t n = libdivide_count_leading_zeros64(v.hi);
+
+    // Normalize the divisor so its MSB is 1
+    u128_t v1t = v;
+    libdivide_u128_shift(&v1t.hi, &v1t.lo, n);
+    uint64_t v1 = v1t.hi;  // i.e. v1 = v1t >> 64
+
+    // To ensure no overflow
+    u128_t u1 = u;
+    libdivide_u128_shift(&u1.hi, &u1.lo, -1);
+
+    // Get quotient from divide unsigned insn.
+    uint64_t rem_ignored;
+    uint64_t q1 = libdivide_128_div_64_to_64(u1.hi, u1.lo, v1, &rem_ignored);
+
+    // Undo normalization and division of u by 2.
+    u128_t q0 = {0, q1};
+    libdivide_u128_shift(&q0.hi, &q0.lo, n);
+    libdivide_u128_shift(&q0.hi, &q0.lo, -63);
+
+    // Make q0 correct or too small by 1
+    // Equivalent to `if (q0 != 0) q0 = q0 - 1;`
+    if (q0.hi != 0 || q0.lo != 0) {
+        q0.hi -= (q0.lo == 0);  // borrow
+        q0.lo -= 1;
+    }
+
+    // Now q0 is correct.
+    // Compute q0 * v as q0v
+    // = (q0.hi << 64 + q0.lo) * (v.hi << 64 + v.lo)
+    // = (q0.hi * v.hi << 128) + (q0.hi * v.lo << 64) +
+    //   (q0.lo * v.hi <<  64) + q0.lo * v.lo)
+    // Each term is 128 bit
+    // High half of full product (upper 128 bits!) are dropped
+    u128_t q0v = {0, 0};
+    q0v.hi = q0.hi * v.lo + q0.lo * v.hi + libdivide_mullhi_u64(q0.lo, v.lo);
+    q0v.lo = q0.lo * v.lo;
+
+    // Compute u - q0v as u_q0v
+    // This is the remainder
+    u128_t u_q0v = u;
+    u_q0v.hi -= q0v.hi + (u.lo < q0v.lo);  // second term is borrow
+    u_q0v.lo -= q0v.lo;
+
+    // Check if u_q0v >= v
+    // This checks if our remainder is larger than the divisor
+    if ((u_q0v.hi > v.hi) || (u_q0v.hi == v.hi && u_q0v.lo >= v.lo)) {
+        // Increment q0
+        q0.lo += 1;
+        q0.hi += (q0.lo == 0);  // carry
+
+        // Subtract v from remainder
+        u_q0v.hi -= v.hi + (u_q0v.lo < v.lo);
+        u_q0v.lo -= v.lo;
+    }
+
+    *r_hi = u_q0v.hi;
+    *r_lo = u_q0v.lo;
+
+    LIBDIVIDE_ASSERT(q0.hi == 0);
+    return q0.lo;
+#endif
+}
+
+////////// UINT16
+
+static LIBDIVIDE_INLINE struct libdivide_u16_t libdivide_internal_u16_gen(
+    uint16_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_u16_t result;
+    uint8_t floor_log_2_d = (uint8_t)(15 - libdivide_count_leading_zeros16(d));
+
+    // Power of 2
+    if ((d & (d - 1)) == 0) {
+        // We need to subtract 1 from the shift value in case of an unsigned
+        // branchfree divider because there is a hardcoded right shift by 1
+        // in its division algorithm. Because of this we also need to add back
+        // 1 in its recovery algorithm.
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d - (branchfree != 0));
+    } else {
+        uint8_t more;
+        uint16_t rem, proposed_m;
+        proposed_m = libdivide_32_div_16_to_16((uint16_t)1 << floor_log_2_d, 0, d, &rem);
+
+        LIBDIVIDE_ASSERT(rem > 0 && rem < d);
+        const uint16_t e = d - rem;
+
+        // This power works if e < 2**floor_log_2_d.
+        if (!branchfree && (e < ((uint16_t)1 << floor_log_2_d))) {
+            // This power works
+            more = floor_log_2_d;
+        } else {
+            // We have to use the general 17-bit algorithm.  We need to compute
+            // (2**power) / d. However, we already have (2**(power-1))/d and
+            // its remainder.  By doubling both, and then correcting the
+            // remainder, we can compute the larger division.
+            // don't care about overflow here - in fact, we expect it
+            proposed_m += proposed_m;
+            const uint16_t twice_rem = rem + rem;
+            if (twice_rem >= d || twice_rem < rem) proposed_m += 1;
+            more = floor_log_2_d | LIBDIVIDE_ADD_MARKER;
+        }
+        result.magic = 1 + proposed_m;
+        result.more = more;
+        // result.more's shift should in general be ceil_log_2_d. But if we
+        // used the smaller power, we subtract one from the shift because we're
+        // using the smaller power. If we're using the larger power, we
+        // subtract one from the shift because it's taken care of by the add
+        // indicator. So floor_log_2_d happens to be correct in both cases.
+    }
+    return result;
+}
+
+struct libdivide_u16_t libdivide_u16_gen(uint16_t d) {
+    return libdivide_internal_u16_gen(d, 0);
+}
+
+struct libdivide_u16_branchfree_t libdivide_u16_branchfree_gen(uint16_t d) {
+    if (d == 1) {
+        LIBDIVIDE_ERROR("branchfree divider must be != 1");
+    }
+    struct libdivide_u16_t tmp = libdivide_internal_u16_gen(d, 1);
+    struct libdivide_u16_branchfree_t ret = {
+        tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_16_SHIFT_MASK)};
+    return ret;
+}
+
+// The original libdivide_u16_do takes a const pointer. However, this cannot be used
+// with a compile time constant libdivide_u16_t: it will generate a warning about
+// taking the address of a temporary. Hence this overload.
+uint16_t libdivide_u16_do_raw(uint16_t numer, uint16_t magic, uint8_t more) {
+    if (!magic) {
+        return numer >> more;
+    } else {
+        uint16_t q = libdivide_mullhi_u16(magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            uint16_t t = ((numer - q) >> 1) + q;
+            return t >> (more & LIBDIVIDE_16_SHIFT_MASK);
+        } else {
+            // All upper bits are 0,
+            // don't need to mask them off.
+            return q >> more;
+        }
+    }
+}
+
+uint16_t libdivide_u16_do(uint16_t numer, const struct libdivide_u16_t *denom) {
+    return libdivide_u16_do_raw(numer, denom->magic, denom->more);
+}
+
+uint16_t libdivide_u16_branchfree_do(
+    uint16_t numer, const struct libdivide_u16_branchfree_t *denom) {
+    uint16_t q = libdivide_mullhi_u16(denom->magic, numer);
+    uint16_t t = ((numer - q) >> 1) + q;
+    return t >> denom->more;
+}
+
+uint16_t libdivide_u16_recover(const struct libdivide_u16_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint16_t)1 << shift;
+    } else if (!(more & LIBDIVIDE_ADD_MARKER)) {
+        // We compute q = n/d = n*m / 2^(16 + shift)
+        // Therefore we have d = 2^(16 + shift) / m
+        // We need to ceil it.
+        // We know d is not a power of 2, so m is not a power of 2,
+        // so we can just add 1 to the floor
+        uint16_t hi_dividend = (uint16_t)1 << shift;
+        uint16_t rem_ignored;
+        return 1 + libdivide_32_div_16_to_16(hi_dividend, 0, denom->magic, &rem_ignored);
+    } else {
+        // Here we wish to compute d = 2^(16+shift+1)/(m+2^16).
+        // Notice (m + 2^16) is a 17 bit number. Use 32 bit division for now
+        // Also note that shift may be as high as 15, so shift + 1 will
+        // overflow. So we have to compute it as 2^(16+shift)/(m+2^16), and
+        // then double the quotient and remainder.
+        uint32_t half_n = (uint32_t)1 << (16 + shift);
+        uint32_t d = ((uint32_t)1 << 16) | denom->magic;
+        // Note that the quotient is guaranteed <= 16 bits, but the remainder
+        // may need 17!
+        uint16_t half_q = (uint16_t)(half_n / d);
+        uint32_t rem = half_n % d;
+        // We computed 2^(16+shift)/(m+2^16)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 17 bits
+        uint16_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+uint16_t libdivide_u16_branchfree_recover(const struct libdivide_u16_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint16_t)1 << (shift + 1);
+    } else {
+        // Here we wish to compute d = 2^(16+shift+1)/(m+2^16).
+        // Notice (m + 2^16) is a 17 bit number. Use 32 bit division for now
+        // Also note that shift may be as high as 15, so shift + 1 will
+        // overflow. So we have to compute it as 2^(16+shift)/(m+2^16), and
+        // then double the quotient and remainder.
+        uint32_t half_n = (uint32_t)1 << (16 + shift);
+        uint32_t d = ((uint32_t)1 << 16) | denom->magic;
+        // Note that the quotient is guaranteed <= 16 bits, but the remainder
+        // may need 17!
+        uint16_t half_q = (uint16_t)(half_n / d);
+        uint32_t rem = half_n % d;
+        // We computed 2^(16+shift)/(m+2^16)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
+        uint16_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+////////// UINT32
+
+static LIBDIVIDE_INLINE struct libdivide_u32_t libdivide_internal_u32_gen(
+    uint32_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_u32_t result;
+    uint32_t floor_log_2_d = 31 - libdivide_count_leading_zeros32(d);
+
+    // Power of 2
+    if ((d & (d - 1)) == 0) {
+        // We need to subtract 1 from the shift value in case of an unsigned
+        // branchfree divider because there is a hardcoded right shift by 1
+        // in its division algorithm. Because of this we also need to add back
+        // 1 in its recovery algorithm.
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d - (branchfree != 0));
+    } else {
+        uint8_t more;
+        uint32_t rem, proposed_m;
+        proposed_m = libdivide_64_div_32_to_32((uint32_t)1 << floor_log_2_d, 0, d, &rem);
+
+        LIBDIVIDE_ASSERT(rem > 0 && rem < d);
+        const uint32_t e = d - rem;
+
+        // This power works if e < 2**floor_log_2_d.
+        if (!branchfree && (e < ((uint32_t)1 << floor_log_2_d))) {
+            // This power works
+            more = (uint8_t)floor_log_2_d;
+        } else {
+            // We have to use the general 33-bit algorithm.  We need to compute
+            // (2**power) / d. However, we already have (2**(power-1))/d and
+            // its remainder.  By doubling both, and then correcting the
+            // remainder, we can compute the larger division.
+            // don't care about overflow here - in fact, we expect it
+            proposed_m += proposed_m;
+            const uint32_t twice_rem = rem + rem;
+            if (twice_rem >= d || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+        result.magic = 1 + proposed_m;
+        result.more = more;
+        // result.more's shift should in general be ceil_log_2_d. But if we
+        // used the smaller power, we subtract one from the shift because we're
+        // using the smaller power. If we're using the larger power, we
+        // subtract one from the shift because it's taken care of by the add
+        // indicator. So floor_log_2_d happens to be correct in both cases.
+    }
+    return result;
+}
+
+struct libdivide_u32_t libdivide_u32_gen(uint32_t d) {
+    return libdivide_internal_u32_gen(d, 0);
+}
+
+struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) {
+    if (d == 1) {
+        LIBDIVIDE_ERROR("branchfree divider must be != 1");
+    }
+    struct libdivide_u32_t tmp = libdivide_internal_u32_gen(d, 1);
+    struct libdivide_u32_branchfree_t ret = {
+        tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_32_SHIFT_MASK)};
+    return ret;
+}
+
+uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return numer >> more;
+    } else {
+        uint32_t q = libdivide_mullhi_u32(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            uint32_t t = ((numer - q) >> 1) + q;
+            return t >> (more & LIBDIVIDE_32_SHIFT_MASK);
+        } else {
+            // All upper bits are 0,
+            // don't need to mask them off.
+            return q >> more;
+        }
+    }
+}
+
+uint32_t libdivide_u32_branchfree_do(
+    uint32_t numer, const struct libdivide_u32_branchfree_t *denom) {
+    uint32_t q = libdivide_mullhi_u32(denom->magic, numer);
+    uint32_t t = ((numer - q) >> 1) + q;
+    return t >> denom->more;
+}
+
+uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint32_t)1 << shift;
+    } else if (!(more & LIBDIVIDE_ADD_MARKER)) {
+        // We compute q = n/d = n*m / 2^(32 + shift)
+        // Therefore we have d = 2^(32 + shift) / m
+        // We need to ceil it.
+        // We know d is not a power of 2, so m is not a power of 2,
+        // so we can just add 1 to the floor
+        uint32_t hi_dividend = (uint32_t)1 << shift;
+        uint32_t rem_ignored;
+        return 1 + libdivide_64_div_32_to_32(hi_dividend, 0, denom->magic, &rem_ignored);
+    } else {
+        // Here we wish to compute d = 2^(32+shift+1)/(m+2^32).
+        // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now
+        // Also note that shift may be as high as 31, so shift + 1 will
+        // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and
+        // then double the quotient and remainder.
+        uint64_t half_n = (uint64_t)1 << (32 + shift);
+        uint64_t d = ((uint64_t)1 << 32) | denom->magic;
+        // Note that the quotient is guaranteed <= 32 bits, but the remainder
+        // may need 33!
+        uint32_t half_q = (uint32_t)(half_n / d);
+        uint64_t rem = half_n % d;
+        // We computed 2^(32+shift)/(m+2^32)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
+        uint32_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint32_t)1 << (shift + 1);
+    } else {
+        // Here we wish to compute d = 2^(32+shift+1)/(m+2^32).
+        // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now
+        // Also note that shift may be as high as 31, so shift + 1 will
+        // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and
+        // then double the quotient and remainder.
+        uint64_t half_n = (uint64_t)1 << (32 + shift);
+        uint64_t d = ((uint64_t)1 << 32) | denom->magic;
+        // Note that the quotient is guaranteed <= 32 bits, but the remainder
+        // may need 33!
+        uint32_t half_q = (uint32_t)(half_n / d);
+        uint64_t rem = half_n % d;
+        // We computed 2^(32+shift)/(m+2^32)
+        // Need to double it, and then add 1 to the quotient if doubling th
+        // remainder would increase the quotient.
+        // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
+        uint32_t full_q = half_q + half_q + ((rem << 1) >= d);
+
+        // We rounded down in gen (hence +1)
+        return full_q + 1;
+    }
+}
+
+/////////// UINT64
+
+static LIBDIVIDE_INLINE struct libdivide_u64_t libdivide_internal_u64_gen(
+    uint64_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_u64_t result;
+    uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(d);
+
+    // Power of 2
+    if ((d & (d - 1)) == 0) {
+        // We need to subtract 1 from the shift value in case of an unsigned
+        // branchfree divider because there is a hardcoded right shift by 1
+        // in its division algorithm. Because of this we also need to add back
+        // 1 in its recovery algorithm.
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d - (branchfree != 0));
+    } else {
+        uint64_t proposed_m, rem;
+        uint8_t more;
+        // (1 << (64 + floor_log_2_d)) / d
+        proposed_m = libdivide_128_div_64_to_64((uint64_t)1 << floor_log_2_d, 0, d, &rem);
+
+        LIBDIVIDE_ASSERT(rem > 0 && rem < d);
+        const uint64_t e = d - rem;
+
+        // This power works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint64_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)floor_log_2_d;
+        } else {
+            // We have to use the general 65-bit algorithm.  We need to compute
+            // (2**power) / d. However, we already have (2**(power-1))/d and
+            // its remainder. By doubling both, and then correcting the
+            // remainder, we can compute the larger division.
+            // don't care about overflow here - in fact, we expect it
+            proposed_m += proposed_m;
+            const uint64_t twice_rem = rem + rem;
+            if (twice_rem >= d || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+        result.magic = 1 + proposed_m;
+        result.more = more;
+        // result.more's shift should in general be ceil_log_2_d. But if we
+        // used the smaller power, we subtract one from the shift because we're
+        // using the smaller power. If we're using the larger power, we
+        // subtract one from the shift because it's taken care of by the add
+        // indicator. So floor_log_2_d happens to be correct in both cases,
+        // which is why we do it outside of the if statement.
+    }
+    return result;
+}
+
+struct libdivide_u64_t libdivide_u64_gen(uint64_t d) {
+    return libdivide_internal_u64_gen(d, 0);
+}
+
+struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) {
+    if (d == 1) {
+        LIBDIVIDE_ERROR("branchfree divider must be != 1");
+    }
+    struct libdivide_u64_t tmp = libdivide_internal_u64_gen(d, 1);
+    struct libdivide_u64_branchfree_t ret = {
+        tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_64_SHIFT_MASK)};
+    return ret;
+}
+
+uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return numer >> more;
+    } else {
+        uint64_t q = libdivide_mullhi_u64(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            uint64_t t = ((numer - q) >> 1) + q;
+            return t >> (more & LIBDIVIDE_64_SHIFT_MASK);
+        } else {
+            // All upper bits are 0,
+            // don't need to mask them off.
+            return q >> more;
+        }
+    }
+}
+
+uint64_t libdivide_u64_branchfree_do(
+    uint64_t numer, const struct libdivide_u64_branchfree_t *denom) {
+    uint64_t q = libdivide_mullhi_u64(denom->magic, numer);
+    uint64_t t = ((numer - q) >> 1) + q;
+    return t >> denom->more;
+}
+
+uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint64_t)1 << shift;
+    } else if (!(more & LIBDIVIDE_ADD_MARKER)) {
+        // We compute q = n/d = n*m / 2^(64 + shift)
+        // Therefore we have d = 2^(64 + shift) / m
+        // We need to ceil it.
+        // We know d is not a power of 2, so m is not a power of 2,
+        // so we can just add 1 to the floor
+        uint64_t hi_dividend = (uint64_t)1 << shift;
+        uint64_t rem_ignored;
+        return 1 + libdivide_128_div_64_to_64(hi_dividend, 0, denom->magic, &rem_ignored);
+    } else {
+        // Here we wish to compute d = 2^(64+shift+1)/(m+2^64).
+        // Notice (m + 2^64) is a 65 bit number. This gets hairy. See
+        // libdivide_u32_recover for more on what we do here.
+        // TODO: do something better than 128 bit math
+
+        // Full n is a (potentially) 129 bit value
+        // half_n is a 128 bit value
+        // Compute the hi half of half_n. Low half is 0.
+        uint64_t half_n_hi = (uint64_t)1 << shift, half_n_lo = 0;
+        // d is a 65 bit value. The high bit is always set to 1.
+        const uint64_t d_hi = 1, d_lo = denom->magic;
+        // Note that the quotient is guaranteed <= 64 bits,
+        // but the remainder may need 65!
+        uint64_t r_hi, r_lo;
+        uint64_t half_q =
+            libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
+        // We computed 2^(64+shift)/(m+2^64)
+        // Double the remainder ('dr') and check if that is larger than d
+        // Note that d is a 65 bit value, so r1 is small and so r1 + r1
+        // cannot overflow
+        uint64_t dr_lo = r_lo + r_lo;
+        uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo);  // last term is carry
+        int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo);
+        uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0);
+        return full_q + 1;
+    }
+}
+
+uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+
+    if (!denom->magic) {
+        return (uint64_t)1 << (shift + 1);
+    } else {
+        // Here we wish to compute d = 2^(64+shift+1)/(m+2^64).
+        // Notice (m + 2^64) is a 65 bit number. This gets hairy. See
+        // libdivide_u32_recover for more on what we do here.
+        // TODO: do something better than 128 bit math
+
+        // Full n is a (potentially) 129 bit value
+        // half_n is a 128 bit value
+        // Compute the hi half of half_n. Low half is 0.
+        uint64_t half_n_hi = (uint64_t)1 << shift, half_n_lo = 0;
+        // d is a 65 bit value. The high bit is always set to 1.
+        const uint64_t d_hi = 1, d_lo = denom->magic;
+        // Note that the quotient is guaranteed <= 64 bits,
+        // but the remainder may need 65!
+        uint64_t r_hi, r_lo;
+        uint64_t half_q =
+            libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
+        // We computed 2^(64+shift)/(m+2^64)
+        // Double the remainder ('dr') and check if that is larger than d
+        // Note that d is a 65 bit value, so r1 is small and so r1 + r1
+        // cannot overflow
+        uint64_t dr_lo = r_lo + r_lo;
+        uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo);  // last term is carry
+        int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo);
+        uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0);
+        return full_q + 1;
+    }
+}
+
+/////////// SINT16
+
+static LIBDIVIDE_INLINE struct libdivide_s16_t libdivide_internal_s16_gen(
+    int16_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_s16_t result;
+
+    // If d is a power of 2, or negative a power of 2, we have to use a shift.
+    // This is especially important because the magic algorithm fails for -1.
+    // To check if d is a power of 2 or its inverse, it suffices to check
+    // whether its absolute value has exactly one bit set. This works even for
+    // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set
+    // and is a power of 2.
+    uint16_t ud = (uint16_t)d;
+    uint16_t absD = (d < 0) ? -ud : ud;
+    uint16_t floor_log_2_d = 15 - libdivide_count_leading_zeros16(absD);
+    // check if exactly one bit is set,
+    // don't care if absD is 0 since that's divide by zero
+    if ((absD & (absD - 1)) == 0) {
+        // Branchfree and normal paths are exactly the same
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0));
+    } else {
+        LIBDIVIDE_ASSERT(floor_log_2_d >= 1);
+
+        uint8_t more;
+        // the dividend here is 2**(floor_log_2_d + 31), so the low 16 bit word
+        // is 0 and the high word is floor_log_2_d - 1
+        uint16_t rem, proposed_m;
+        proposed_m = libdivide_32_div_16_to_16((uint16_t)1 << (floor_log_2_d - 1), 0, absD, &rem);
+        const uint16_t e = absD - rem;
+
+        // We are going to start with a power of floor_log_2_d - 1.
+        // This works if works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint16_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)(floor_log_2_d - 1);
+        } else {
+            // We need to go one higher. This should not make proposed_m
+            // overflow, but it will make it negative when interpreted as an
+            // int16_t.
+            proposed_m += proposed_m;
+            const uint16_t twice_rem = rem + rem;
+            if (twice_rem >= absD || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+
+        proposed_m += 1;
+        int16_t magic = (int16_t)proposed_m;
+
+        // Mark if we are negative. Note we only negate the magic number in the
+        // branchfull case.
+        if (d < 0) {
+            more |= LIBDIVIDE_NEGATIVE_DIVISOR;
+            if (!branchfree) {
+                magic = -magic;
+            }
+        }
+
+        result.more = more;
+        result.magic = magic;
+    }
+    return result;
+}
+
+struct libdivide_s16_t libdivide_s16_gen(int16_t d) {
+    return libdivide_internal_s16_gen(d, 0);
+}
+
+struct libdivide_s16_branchfree_t libdivide_s16_branchfree_gen(int16_t d) {
+    struct libdivide_s16_t tmp = libdivide_internal_s16_gen(d, 1);
+    struct libdivide_s16_branchfree_t result = {tmp.magic, tmp.more};
+    return result;
+}
+
+// The original libdivide_s16_do takes a const pointer. However, this cannot be used
+// with a compile time constant libdivide_s16_t: it will generate a warning about
+// taking the address of a temporary. Hence this overload.
+int16_t libdivide_s16_do_raw(int16_t numer, int16_t magic, uint8_t more) {
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+
+    if (!magic) {
+        uint16_t sign = (int8_t)more >> 7;
+        uint16_t mask = ((uint16_t)1 << shift) - 1;
+        uint16_t uq = numer + ((numer >> 15) & mask);
+        int16_t q = (int16_t)uq;
+        q >>= shift;
+        q = (q ^ sign) - sign;
+        return q;
+    } else {
+        uint16_t uq = (uint16_t)libdivide_mullhi_s16(magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift and then sign extend
+            int16_t sign = (int8_t)more >> 7;
+            // q += (more < 0 ? -numer : numer)
+            // cast required to avoid UB
+            uq += ((uint16_t)numer ^ sign) - sign;
+        }
+        int16_t q = (int16_t)uq;
+        q >>= shift;
+        q += (q < 0);
+        return q;
+    }
+}
+
+int16_t libdivide_s16_do(int16_t numer, const struct libdivide_s16_t *denom) {
+    return libdivide_s16_do_raw(numer, denom->magic, denom->more);
+}
+
+int16_t libdivide_s16_branchfree_do(int16_t numer, const struct libdivide_s16_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    // must be arithmetic shift and then sign extend
+    int16_t sign = (int8_t)more >> 7;
+    int16_t magic = denom->magic;
+    int16_t q = libdivide_mullhi_s16(magic, numer);
+    q += numer;
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is a power of
+    // 2, or (2**shift) if it is not a power of 2
+    uint16_t is_power_of_2 = (magic == 0);
+    uint16_t q_sign = (uint16_t)(q >> 15);
+    q += q_sign & (((uint16_t)1 << shift) - is_power_of_2);
+
+    // Now arithmetic right shift
+    q >>= shift;
+    // Negate if needed
+    q = (q ^ sign) - sign;
+
+    return q;
+}
+
+int16_t libdivide_s16_recover(const struct libdivide_s16_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    if (!denom->magic) {
+        uint16_t absD = (uint16_t)1 << shift;
+        if (more & LIBDIVIDE_NEGATIVE_DIVISOR) {
+            absD = -absD;
+        }
+        return (int16_t)absD;
+    } else {
+        // Unsigned math is much easier
+        // We negate the magic number only in the branchfull case, and we don't
+        // know which case we're in. However we have enough information to
+        // determine the correct sign of the magic number. The divisor was
+        // negative if LIBDIVIDE_NEGATIVE_DIVISOR is set. If ADD_MARKER is set,
+        // the magic number's sign is opposite that of the divisor.
+        // We want to compute the positive magic number.
+        int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
+        int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
+
+        // Handle the power of 2 case (including branchfree)
+        if (denom->magic == 0) {
+            int16_t result = (uint16_t)1 << shift;
+            return negative_divisor ? -result : result;
+        }
+
+        uint16_t d = (uint16_t)(magic_was_negated ? -denom->magic : denom->magic);
+        uint32_t n = (uint32_t)1 << (16 + shift);  // this shift cannot exceed 30
+        uint16_t q = (uint16_t)(n / d);
+        int16_t result = (int16_t)q;
+        result += 1;
+        return negative_divisor ? -result : result;
+    }
+}
+
+int16_t libdivide_s16_branchfree_recover(const struct libdivide_s16_branchfree_t *denom) {
+    return libdivide_s16_recover((const struct libdivide_s16_t *)denom);
+}
+
+/////////// SINT32
+
+static LIBDIVIDE_INLINE struct libdivide_s32_t libdivide_internal_s32_gen(
+    int32_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_s32_t result;
+
+    // If d is a power of 2, or negative a power of 2, we have to use a shift.
+    // This is especially important because the magic algorithm fails for -1.
+    // To check if d is a power of 2 or its inverse, it suffices to check
+    // whether its absolute value has exactly one bit set. This works even for
+    // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set
+    // and is a power of 2.
+    uint32_t ud = (uint32_t)d;
+    uint32_t absD = (d < 0) ? -ud : ud;
+    uint32_t floor_log_2_d = 31 - libdivide_count_leading_zeros32(absD);
+    // check if exactly one bit is set,
+    // don't care if absD is 0 since that's divide by zero
+    if ((absD & (absD - 1)) == 0) {
+        // Branchfree and normal paths are exactly the same
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0));
+    } else {
+        LIBDIVIDE_ASSERT(floor_log_2_d >= 1);
+
+        uint8_t more;
+        // the dividend here is 2**(floor_log_2_d + 31), so the low 32 bit word
+        // is 0 and the high word is floor_log_2_d - 1
+        uint32_t rem, proposed_m;
+        proposed_m = libdivide_64_div_32_to_32((uint32_t)1 << (floor_log_2_d - 1), 0, absD, &rem);
+        const uint32_t e = absD - rem;
+
+        // We are going to start with a power of floor_log_2_d - 1.
+        // This works if works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint32_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)(floor_log_2_d - 1);
+        } else {
+            // We need to go one higher. This should not make proposed_m
+            // overflow, but it will make it negative when interpreted as an
+            // int32_t.
+            proposed_m += proposed_m;
+            const uint32_t twice_rem = rem + rem;
+            if (twice_rem >= absD || twice_rem < rem) proposed_m += 1;
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+
+        proposed_m += 1;
+        int32_t magic = (int32_t)proposed_m;
+
+        // Mark if we are negative. Note we only negate the magic number in the
+        // branchfull case.
+        if (d < 0) {
+            more |= LIBDIVIDE_NEGATIVE_DIVISOR;
+            if (!branchfree) {
+                magic = -magic;
+            }
+        }
+
+        result.more = more;
+        result.magic = magic;
+    }
+    return result;
+}
+
+struct libdivide_s32_t libdivide_s32_gen(int32_t d) {
+    return libdivide_internal_s32_gen(d, 0);
+}
+
+struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) {
+    struct libdivide_s32_t tmp = libdivide_internal_s32_gen(d, 1);
+    struct libdivide_s32_branchfree_t result = {tmp.magic, tmp.more};
+    return result;
+}
+
+int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+
+    if (!denom->magic) {
+        uint32_t sign = (int8_t)more >> 7;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        uint32_t uq = numer + ((numer >> 31) & mask);
+        int32_t q = (int32_t)uq;
+        q >>= shift;
+        q = (q ^ sign) - sign;
+        return q;
+    } else {
+        uint32_t uq = (uint32_t)libdivide_mullhi_s32(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift and then sign extend
+            int32_t sign = (int8_t)more >> 7;
+            // q += (more < 0 ? -numer : numer)
+            // cast required to avoid UB
+            uq += ((uint32_t)numer ^ sign) - sign;
+        }
+        int32_t q = (int32_t)uq;
+        q >>= shift;
+        q += (q < 0);
+        return q;
+    }
+}
+
+int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift and then sign extend
+    int32_t sign = (int8_t)more >> 7;
+    int32_t magic = denom->magic;
+    int32_t q = libdivide_mullhi_s32(magic, numer);
+    q += numer;
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is a power of
+    // 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    uint32_t q_sign = (uint32_t)(q >> 31);
+    q += q_sign & (((uint32_t)1 << shift) - is_power_of_2);
+
+    // Now arithmetic right shift
+    q >>= shift;
+    // Negate if needed
+    q = (q ^ sign) - sign;
+
+    return q;
+}
+
+int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    if (!denom->magic) {
+        uint32_t absD = (uint32_t)1 << shift;
+        if (more & LIBDIVIDE_NEGATIVE_DIVISOR) {
+            absD = -absD;
+        }
+        return (int32_t)absD;
+    } else {
+        // Unsigned math is much easier
+        // We negate the magic number only in the branchfull case, and we don't
+        // know which case we're in. However we have enough information to
+        // determine the correct sign of the magic number. The divisor was
+        // negative if LIBDIVIDE_NEGATIVE_DIVISOR is set. If ADD_MARKER is set,
+        // the magic number's sign is opposite that of the divisor.
+        // We want to compute the positive magic number.
+        int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
+        int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
+
+        // Handle the power of 2 case (including branchfree)
+        if (denom->magic == 0) {
+            int32_t result = (uint32_t)1 << shift;
+            return negative_divisor ? -result : result;
+        }
+
+        uint32_t d = (uint32_t)(magic_was_negated ? -denom->magic : denom->magic);
+        uint64_t n = (uint64_t)1 << (32 + shift);  // this shift cannot exceed 30
+        uint32_t q = (uint32_t)(n / d);
+        int32_t result = (int32_t)q;
+        result += 1;
+        return negative_divisor ? -result : result;
+    }
+}
+
+int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom) {
+    return libdivide_s32_recover((const struct libdivide_s32_t *)denom);
+}
+
+///////////// SINT64
+
+static LIBDIVIDE_INLINE struct libdivide_s64_t libdivide_internal_s64_gen(
+    int64_t d, int branchfree) {
+    if (d == 0) {
+        LIBDIVIDE_ERROR("divider must be != 0");
+    }
+
+    struct libdivide_s64_t result;
+
+    // If d is a power of 2, or negative a power of 2, we have to use a shift.
+    // This is especially important because the magic algorithm fails for -1.
+    // To check if d is a power of 2 or its inverse, it suffices to check
+    // whether its absolute value has exactly one bit set.  This works even for
+    // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set
+    // and is a power of 2.
+    uint64_t ud = (uint64_t)d;
+    uint64_t absD = (d < 0) ? -ud : ud;
+    uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(absD);
+    // check if exactly one bit is set,
+    // don't care if absD is 0 since that's divide by zero
+    if ((absD & (absD - 1)) == 0) {
+        // Branchfree and non-branchfree cases are the same
+        result.magic = 0;
+        result.more = (uint8_t)(floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0));
+    } else {
+        // the dividend here is 2**(floor_log_2_d + 63), so the low 64 bit word
+        // is 0 and the high word is floor_log_2_d - 1
+        uint8_t more;
+        uint64_t rem, proposed_m;
+        proposed_m = libdivide_128_div_64_to_64((uint64_t)1 << (floor_log_2_d - 1), 0, absD, &rem);
+        const uint64_t e = absD - rem;
+
+        // We are going to start with a power of floor_log_2_d - 1.
+        // This works if works if e < 2**floor_log_2_d.
+        if (!branchfree && e < ((uint64_t)1 << floor_log_2_d)) {
+            // This power works
+            more = (uint8_t)(floor_log_2_d - 1);
+        } else {
+            // We need to go one higher. This should not make proposed_m
+            // overflow, but it will make it negative when interpreted as an
+            // int32_t.
+            proposed_m += proposed_m;
+            const uint64_t twice_rem = rem + rem;
+            if (twice_rem >= absD || twice_rem < rem) proposed_m += 1;
+            // note that we only set the LIBDIVIDE_NEGATIVE_DIVISOR bit if we
+            // also set ADD_MARKER this is an annoying optimization that
+            // enables algorithm #4 to avoid the mask. However we always set it
+            // in the branchfree case
+            more = (uint8_t)(floor_log_2_d | LIBDIVIDE_ADD_MARKER);
+        }
+        proposed_m += 1;
+        int64_t magic = (int64_t)proposed_m;
+
+        // Mark if we are negative
+        if (d < 0) {
+            more |= LIBDIVIDE_NEGATIVE_DIVISOR;
+            if (!branchfree) {
+                magic = -magic;
+            }
+        }
+
+        result.more = more;
+        result.magic = magic;
+    }
+    return result;
+}
+
+struct libdivide_s64_t libdivide_s64_gen(int64_t d) {
+    return libdivide_internal_s64_gen(d, 0);
+}
+
+struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d) {
+    struct libdivide_s64_t tmp = libdivide_internal_s64_gen(d, 1);
+    struct libdivide_s64_branchfree_t ret = {tmp.magic, tmp.more};
+    return ret;
+}
+
+int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+
+    if (!denom->magic) {  // shift path
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        uint64_t uq = numer + ((numer >> 63) & mask);
+        int64_t q = (int64_t)uq;
+        q >>= shift;
+        // must be arithmetic shift and then sign-extend
+        int64_t sign = (int8_t)more >> 7;
+        q = (q ^ sign) - sign;
+        return q;
+    } else {
+        uint64_t uq = (uint64_t)libdivide_mullhi_s64(denom->magic, numer);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift and then sign extend
+            int64_t sign = (int8_t)more >> 7;
+            // q += (more < 0 ? -numer : numer)
+            // cast required to avoid UB
+            uq += ((uint64_t)numer ^ sign) - sign;
+        }
+        int64_t q = (int64_t)uq;
+        q >>= shift;
+        q += (q < 0);
+        return q;
+    }
+}
+
+int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift and then sign extend
+    int64_t sign = (int8_t)more >> 7;
+    int64_t magic = denom->magic;
+    int64_t q = libdivide_mullhi_s64(magic, numer);
+    q += numer;
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is a power of
+    // 2, or (2**shift) if it is not a power of 2.
+    uint64_t is_power_of_2 = (magic == 0);
+    uint64_t q_sign = (uint64_t)(q >> 63);
+    q += q_sign & (((uint64_t)1 << shift) - is_power_of_2);
+
+    // Arithmetic right shift
+    q >>= shift;
+    // Negate if needed
+    q = (q ^ sign) - sign;
+
+    return q;
+}
+
+int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    if (denom->magic == 0) {  // shift path
+        uint64_t absD = (uint64_t)1 << shift;
+        if (more & LIBDIVIDE_NEGATIVE_DIVISOR) {
+            absD = -absD;
+        }
+        return (int64_t)absD;
+    } else {
+        // Unsigned math is much easier
+        int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
+        int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
+
+        uint64_t d = (uint64_t)(magic_was_negated ? -denom->magic : denom->magic);
+        uint64_t n_hi = (uint64_t)1 << shift, n_lo = 0;
+        uint64_t rem_ignored;
+        uint64_t q = libdivide_128_div_64_to_64(n_hi, n_lo, d, &rem_ignored);
+        int64_t result = (int64_t)(q + 1);
+        if (negative_divisor) {
+            result = -result;
+        }
+        return result;
+    }
+}
+
+int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom) {
+    return libdivide_s64_recover((const struct libdivide_s64_t *)denom);
+}
+
+// Simplest possible vector type division: treat the vector type as an array
+// of underlying native type.
+//
+// Use a union to read a vector via pointer-to-integer, without violating strict
+// aliasing.
+#define SIMPLE_VECTOR_DIVISION(IntT, VecT, Algo)                          \
+    const size_t count = sizeof(VecT) / sizeof(IntT);                     \
+    union type_pun_vec {                                                  \
+        VecT vec;                                                         \
+        IntT arr[sizeof(VecT) / sizeof(IntT)];                            \
+    };                                                                    \
+    union type_pun_vec result;                                            \
+    union type_pun_vec input;                                             \
+    input.vec = numers;                                                   \
+    for (size_t loop = 0; loop < count; ++loop) {                         \
+        result.arr[loop] = libdivide_##Algo##_do(input.arr[loop], denom); \
+    }                                                                     \
+    return result.vec;
+
+#if defined(LIBDIVIDE_NEON)
+
+static LIBDIVIDE_INLINE uint16x8_t libdivide_u16_do_vec128(
+    uint16x8_t numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE int16x8_t libdivide_s16_do_vec128(
+    int16x8_t numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE uint32x4_t libdivide_u32_do_vec128(
+    uint32x4_t numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE int32x4_t libdivide_s32_do_vec128(
+    int32x4_t numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE uint64x2_t libdivide_u64_do_vec128(
+    uint64x2_t numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_do_vec128(
+    int64x2_t numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE uint16x8_t libdivide_u16_branchfree_do_vec128(
+    uint16x8_t numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE int16x8_t libdivide_s16_branchfree_do_vec128(
+    int16x8_t numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint32x4_t libdivide_u32_branchfree_do_vec128(
+    uint32x4_t numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE int32x4_t libdivide_s32_branchfree_do_vec128(
+    int32x4_t numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE uint64x2_t libdivide_u64_branchfree_do_vec128(
+    uint64x2_t numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_branchfree_do_vec128(
+    int64x2_t numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+// Logical right shift by runtime value.
+// NEON implements right shift as left shits by negative values.
+static LIBDIVIDE_INLINE uint32x4_t libdivide_u32_neon_srl(uint32x4_t v, uint8_t amt) {
+    int32_t wamt = (int32_t)(amt);
+    return vshlq_u32(v, vdupq_n_s32(-wamt));
+}
+
+static LIBDIVIDE_INLINE uint64x2_t libdivide_u64_neon_srl(uint64x2_t v, uint8_t amt) {
+    int64_t wamt = (int64_t)(amt);
+    return vshlq_u64(v, vdupq_n_s64(-wamt));
+}
+
+// Arithmetic right shift by runtime value.
+static LIBDIVIDE_INLINE int32x4_t libdivide_s32_neon_sra(int32x4_t v, uint8_t amt) {
+    int32_t wamt = (int32_t)(amt);
+    return vshlq_s32(v, vdupq_n_s32(-wamt));
+}
+
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_neon_sra(int64x2_t v, uint8_t amt) {
+    int64_t wamt = (int64_t)(amt);
+    return vshlq_s64(v, vdupq_n_s64(-wamt));
+}
+
+static LIBDIVIDE_INLINE int64x2_t libdivide_s64_signbits(int64x2_t v) { return vshrq_n_s64(v, 63); }
+
+static LIBDIVIDE_INLINE uint32x4_t libdivide_mullhi_u32_vec128(uint32x4_t a, uint32_t b) {
+    // Desire is [x0, x1, x2, x3]
+    uint32x4_t w1 = vreinterpretq_u32_u64(vmull_n_u32(vget_low_u32(a), b));  // [_, x0, _, x1]
+    uint32x4_t w2 = vreinterpretq_u32_u64(vmull_high_n_u32(a, b));           //[_, x2, _, x3]
+    return vuzp2q_u32(w1, w2);                                               // [x0, x1, x2, x3]
+}
+
+static LIBDIVIDE_INLINE int32x4_t libdivide_mullhi_s32_vec128(int32x4_t a, int32_t b) {
+    int32x4_t w1 = vreinterpretq_s32_s64(vmull_n_s32(vget_low_s32(a), b));  // [_, x0, _, x1]
+    int32x4_t w2 = vreinterpretq_s32_s64(vmull_high_n_s32(a, b));           //[_, x2, _, x3]
+    return vuzp2q_s32(w1, w2);                                              // [x0, x1, x2, x3]
+}
+
+static LIBDIVIDE_INLINE uint64x2_t libdivide_mullhi_u64_vec128(uint64x2_t x, uint64_t sy) {
+    // full 128 bits product is:
+    // x0*y0 + (x0*y1 << 32) + (x1*y0 << 32) + (x1*y1 << 64)
+    // Note x0,y0,x1,y1 are all conceptually uint32, products are 32x32->64.
+
+    // Get low and high words. x0 contains low 32 bits, x1 is high 32 bits.
+    uint64x2_t y = vdupq_n_u64(sy);
+    uint32x2_t x0 = vmovn_u64(x);
+    uint32x2_t y0 = vmovn_u64(y);
+    uint32x2_t x1 = vshrn_n_u64(x, 32);
+    uint32x2_t y1 = vshrn_n_u64(y, 32);
+
+    // Compute x0*y0.
+    uint64x2_t x0y0 = vmull_u32(x0, y0);
+    uint64x2_t x0y0_hi = vshrq_n_u64(x0y0, 32);
+
+    // Compute other intermediate products.
+    uint64x2_t temp = vmlal_u32(x0y0_hi, x1, y0);  // temp = x0y0_hi + x1*y0;
+    // We want to split temp into its low 32 bits and high 32 bits, both
+    // in the low half of 64 bit registers.
+    // Use shifts to avoid needing a reg for the mask.
+    uint64x2_t temp_lo = vshrq_n_u64(vshlq_n_u64(temp, 32), 32);  // temp_lo = temp & 0xFFFFFFFF;
+    uint64x2_t temp_hi = vshrq_n_u64(temp, 32);                   // temp_hi = temp >> 32;
+
+    temp_lo = vmlal_u32(temp_lo, x0, y1);  // temp_lo += x0*y0
+    temp_lo = vshrq_n_u64(temp_lo, 32);    // temp_lo >>= 32
+    temp_hi = vmlal_u32(temp_hi, x1, y1);  // temp_hi += x1*y1
+    uint64x2_t result = vaddq_u64(temp_hi, temp_lo);
+    return result;
+}
+
+static LIBDIVIDE_INLINE int64x2_t libdivide_mullhi_s64_vec128(int64x2_t x, int64_t sy) {
+    int64x2_t p = vreinterpretq_s64_u64(
+        libdivide_mullhi_u64_vec128(vreinterpretq_u64_s64(x), (uint64_t)(sy)));
+    int64x2_t y = vdupq_n_s64(sy);
+    int64x2_t t1 = vandq_s64(libdivide_s64_signbits(x), y);
+    int64x2_t t2 = vandq_s64(libdivide_s64_signbits(y), x);
+    p = vsubq_s64(p, t1);
+    p = vsubq_s64(p, t2);
+    return p;
+}
+
+////////// UINT16
+
+uint16x8_t libdivide_u16_do_vec128(uint16x8_t numers, const struct libdivide_u16_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, uint16x8_t, u16)}
+
+uint16x8_t libdivide_u16_branchfree_do_vec128(
+    uint16x8_t numers, const struct libdivide_u16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, uint16x8_t, u16_branchfree)}
+
+////////// UINT32
+
+uint32x4_t libdivide_u32_do_vec128(uint32x4_t numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return libdivide_u32_neon_srl(numers, more);
+    } else {
+        uint32x4_t q = libdivide_mullhi_u32_vec128(numers, denom->magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            // Note we can use halving-subtract to avoid the shift.
+            uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            uint32x4_t t = vaddq_u32(vhsubq_u32(numers, q), q);
+            return libdivide_u32_neon_srl(t, shift);
+        } else {
+            return libdivide_u32_neon_srl(q, more);
+        }
+    }
+}
+
+uint32x4_t libdivide_u32_branchfree_do_vec128(
+    uint32x4_t numers, const struct libdivide_u32_branchfree_t *denom) {
+    uint32x4_t q = libdivide_mullhi_u32_vec128(numers, denom->magic);
+    uint32x4_t t = vaddq_u32(vhsubq_u32(numers, q), q);
+    return libdivide_u32_neon_srl(t, denom->more);
+}
+
+////////// UINT64
+
+uint64x2_t libdivide_u64_do_vec128(uint64x2_t numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return libdivide_u64_neon_srl(numers, more);
+    } else {
+        uint64x2_t q = libdivide_mullhi_u64_vec128(numers, denom->magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            // No 64-bit halving subtracts in NEON :(
+            uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            uint64x2_t t = vaddq_u64(vshrq_n_u64(vsubq_u64(numers, q), 1), q);
+            return libdivide_u64_neon_srl(t, shift);
+        } else {
+            return libdivide_u64_neon_srl(q, more);
+        }
+    }
+}
+
+uint64x2_t libdivide_u64_branchfree_do_vec128(
+    uint64x2_t numers, const struct libdivide_u64_branchfree_t *denom) {
+    uint64x2_t q = libdivide_mullhi_u64_vec128(numers, denom->magic);
+    uint64x2_t t = vaddq_u64(vshrq_n_u64(vsubq_u64(numers, q), 1), q);
+    return libdivide_u64_neon_srl(t, denom->more);
+}
+
+////////// SINT16
+
+int16x8_t libdivide_s16_do_vec128(int16x8_t numers, const struct libdivide_s16_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, int16x8_t, s16)}
+
+int16x8_t libdivide_s16_branchfree_do_vec128(
+    int16x8_t numers, const struct libdivide_s16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, int16x8_t, s16_branchfree)}
+
+////////// SINT32
+
+int32x4_t libdivide_s32_do_vec128(int32x4_t numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        int32x4_t roundToZeroTweak = vdupq_n_s32((int)mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        int32x4_t q = vaddq_s32(numers, vandq_s32(vshrq_n_s32(numers, 31), roundToZeroTweak));
+        q = libdivide_s32_neon_sra(q, shift);
+        int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = vsubq_s32(veorq_s32(q, sign), sign);
+        return q;
+    } else {
+        int32x4_t q = libdivide_mullhi_s32_vec128(numers, denom->magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = vaddq_s32(q, vsubq_s32(veorq_s32(numers, sign), sign));
+        }
+        // q >>= shift
+        q = libdivide_s32_neon_sra(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = vaddq_s32(
+            q, vreinterpretq_s32_u32(vshrq_n_u32(vreinterpretq_u32_s32(q), 31)));  // q += (q < 0)
+        return q;
+    }
+}
+
+int32x4_t libdivide_s32_branchfree_do_vec128(
+    int32x4_t numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+    int32x4_t q = libdivide_mullhi_s32_vec128(numers, magic);
+    q = vaddq_s32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    int32x4_t q_sign = vshrq_n_s32(q, 31);  // q_sign = q >> 31
+    int32x4_t mask = vdupq_n_s32(((uint32_t)1 << shift) - is_power_of_2);
+    q = vaddq_s32(q, vandq_s32(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s32_neon_sra(q, shift);       // q >>= shift
+    q = vsubq_s32(veorq_s32(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+int64x2_t libdivide_s64_do_vec128(int64x2_t numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        int64x2_t roundToZeroTweak = vdupq_n_s64(mask);  // TODO: no need to sign extend
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        int64x2_t q =
+            vaddq_s64(numers, vandq_s64(libdivide_s64_signbits(numers), roundToZeroTweak));
+        q = libdivide_s64_neon_sra(q, shift);
+        // q = (q ^ sign) - sign;
+        int64x2_t sign = vreinterpretq_s64_s8(vdupq_n_s8((int8_t)more >> 7));
+        q = vsubq_s64(veorq_s64(q, sign), sign);
+        return q;
+    } else {
+        int64x2_t q = libdivide_mullhi_s64_vec128(numers, magic);
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            int64x2_t sign = vdupq_n_s64((int8_t)more >> 7);  // TODO: no need to widen
+            // q += ((numer ^ sign) - sign);
+            q = vaddq_s64(q, vsubq_s64(veorq_s64(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_neon_sra(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = vaddq_s64(
+            q, vreinterpretq_s64_u64(vshrq_n_u64(vreinterpretq_u64_s64(q), 63)));  // q += (q < 0)
+        return q;
+    }
+}
+
+int64x2_t libdivide_s64_branchfree_do_vec128(
+    int64x2_t numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    int64x2_t sign = vdupq_n_s64((int8_t)more >> 7);  // TODO: avoid sign extend
+
+    // libdivide_mullhi_s64(numers, magic);
+    int64x2_t q = libdivide_mullhi_s64_vec128(numers, magic);
+    q = vaddq_s64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    int64x2_t q_sign = libdivide_s64_signbits(q);  // q_sign = q >> 63
+    int64x2_t mask = vdupq_n_s64(((uint64_t)1 << shift) - is_power_of_2);
+    q = vaddq_s64(q, vandq_s64(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_neon_sra(q, shift);       // q >>= shift
+    q = vsubq_s64(veorq_s64(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+#if defined(LIBDIVIDE_AVX512)
+
+static LIBDIVIDE_INLINE __m512i libdivide_u16_do_vec512(
+    __m512i numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s16_do_vec512(
+    __m512i numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u32_do_vec512(
+    __m512i numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s32_do_vec512(
+    __m512i numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u64_do_vec512(
+    __m512i numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s64_do_vec512(
+    __m512i numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE __m512i libdivide_u16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_u64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m512i libdivide_s64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+static LIBDIVIDE_INLINE __m512i libdivide_s64_signbits_vec512(__m512i v) {
+    ;
+    return _mm512_srai_epi64(v, 63);
+}
+
+static LIBDIVIDE_INLINE __m512i libdivide_s64_shift_right_vec512(__m512i v, int amt) {
+    return _mm512_srai_epi64(v, amt);
+}
+
+// Here, b is assumed to contain one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_u32_vec512(__m512i a, __m512i b) {
+    __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epu32(a, b), 32);
+    __m512i a1X3X = _mm512_srli_epi64(a, 32);
+    __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0);
+    __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epu32(a1X3X, b), mask);
+    return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// b is one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_s32_vec512(__m512i a, __m512i b) {
+    __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epi32(a, b), 32);
+    __m512i a1X3X = _mm512_srli_epi64(a, 32);
+    __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0);
+    __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epi32(a1X3X, b), mask);
+    return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// Here, y is assumed to contain one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_u64_vec512(__m512i x, __m512i y) {
+    // see m128i variant for comments.
+    __m512i x0y0 = _mm512_mul_epu32(x, y);
+    __m512i x0y0_hi = _mm512_srli_epi64(x0y0, 32);
+
+    __m512i x1 = _mm512_shuffle_epi32(x, (_MM_PERM_ENUM)_MM_SHUFFLE(3, 3, 1, 1));
+    __m512i y1 = _mm512_shuffle_epi32(y, (_MM_PERM_ENUM)_MM_SHUFFLE(3, 3, 1, 1));
+
+    __m512i x0y1 = _mm512_mul_epu32(x, y1);
+    __m512i x1y0 = _mm512_mul_epu32(x1, y);
+    __m512i x1y1 = _mm512_mul_epu32(x1, y1);
+
+    __m512i mask = _mm512_set1_epi64(0xFFFFFFFF);
+    __m512i temp = _mm512_add_epi64(x1y0, x0y0_hi);
+    __m512i temp_lo = _mm512_and_si512(temp, mask);
+    __m512i temp_hi = _mm512_srli_epi64(temp, 32);
+
+    temp_lo = _mm512_srli_epi64(_mm512_add_epi64(temp_lo, x0y1), 32);
+    temp_hi = _mm512_add_epi64(x1y1, temp_hi);
+    return _mm512_add_epi64(temp_lo, temp_hi);
+}
+
+// y is one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m512i libdivide_mullhi_s64_vec512(__m512i x, __m512i y) {
+    __m512i p = libdivide_mullhi_u64_vec512(x, y);
+    __m512i t1 = _mm512_and_si512(libdivide_s64_signbits_vec512(x), y);
+    __m512i t2 = _mm512_and_si512(libdivide_s64_signbits_vec512(y), x);
+    p = _mm512_sub_epi64(p, t1);
+    p = _mm512_sub_epi64(p, t2);
+    return p;
+}
+
+////////// UINT16
+
+__m512i libdivide_u16_do_vec512(__m512i numers, const struct libdivide_u16_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, __m512i, u16)}
+
+__m512i libdivide_u16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(uint16_t, __m512i, u16_branchfree)}
+
+////////// UINT32
+
+__m512i libdivide_u32_do_vec512(__m512i numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm512_srli_epi32(numers, more);
+    } else {
+        __m512i q = libdivide_mullhi_u32_vec512(numers, _mm512_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q);
+            return _mm512_srli_epi32(t, shift);
+        } else {
+            return _mm512_srli_epi32(q, more);
+        }
+    }
+}
+
+__m512i libdivide_u32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u32_branchfree_t *denom) {
+    __m512i q = libdivide_mullhi_u32_vec512(numers, _mm512_set1_epi32(denom->magic));
+    __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q);
+    return _mm512_srli_epi32(t, denom->more);
+}
+
+////////// UINT64
+
+__m512i libdivide_u64_do_vec512(__m512i numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm512_srli_epi64(numers, more);
+    } else {
+        __m512i q = libdivide_mullhi_u64_vec512(numers, _mm512_set1_epi64(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q);
+            return _mm512_srli_epi64(t, shift);
+        } else {
+            return _mm512_srli_epi64(q, more);
+        }
+    }
+}
+
+__m512i libdivide_u64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_u64_branchfree_t *denom) {
+    __m512i q = libdivide_mullhi_u64_vec512(numers, _mm512_set1_epi64(denom->magic));
+    __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q);
+    return _mm512_srli_epi64(t, denom->more);
+}
+
+////////// SINT16
+
+__m512i libdivide_s16_do_vec512(__m512i numers, const struct libdivide_s16_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, __m512i, s16)}
+
+__m512i libdivide_s16_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s16_branchfree_t *denom){
+    SIMPLE_VECTOR_DIVISION(int16_t, __m512i, s16_branchfree)}
+
+////////// SINT32
+
+__m512i libdivide_s32_do_vec512(__m512i numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        __m512i roundToZeroTweak = _mm512_set1_epi32(mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        __m512i q = _mm512_add_epi32(
+            numers, _mm512_and_si512(_mm512_srai_epi32(numers, 31), roundToZeroTweak));
+        q = _mm512_srai_epi32(q, shift);
+        __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign);
+        return q;
+    } else {
+        __m512i q = libdivide_mullhi_s32_vec512(numers, _mm512_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm512_add_epi32(q, _mm512_sub_epi32(_mm512_xor_si512(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm512_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = _mm512_add_epi32(q, _mm512_srli_epi32(q, 31));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m512i libdivide_s32_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+    __m512i q = libdivide_mullhi_s32_vec512(numers, _mm512_set1_epi32(magic));
+    q = _mm512_add_epi32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    __m512i q_sign = _mm512_srai_epi32(q, 31);  // q_sign = q >> 31
+    __m512i mask = _mm512_set1_epi32(((uint32_t)1 << shift) - is_power_of_2);
+    q = _mm512_add_epi32(q, _mm512_and_si512(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm512_srai_epi32(q, shift);                          // q >>= shift
+    q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+__m512i libdivide_s64_do_vec512(__m512i numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        __m512i roundToZeroTweak = _mm512_set1_epi64(mask);
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        __m512i q = _mm512_add_epi64(
+            numers, _mm512_and_si512(libdivide_s64_signbits_vec512(numers), roundToZeroTweak));
+        q = libdivide_s64_shift_right_vec512(q, shift);
+        __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign);
+        return q;
+    } else {
+        __m512i q = libdivide_mullhi_s64_vec512(numers, _mm512_set1_epi64(magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm512_add_epi64(q, _mm512_sub_epi64(_mm512_xor_si512(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_shift_right_vec512(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = _mm512_add_epi64(q, _mm512_srli_epi64(q, 63));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m512i libdivide_s64_branchfree_do_vec512(
+    __m512i numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    __m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
+
+    // libdivide_mullhi_s64(numers, magic);
+    __m512i q = libdivide_mullhi_s64_vec512(numers, _mm512_set1_epi64(magic));
+    q = _mm512_add_epi64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    __m512i q_sign = libdivide_s64_signbits_vec512(q);  // q_sign = q >> 63
+    __m512i mask = _mm512_set1_epi64(((uint64_t)1 << shift) - is_power_of_2);
+    q = _mm512_add_epi64(q, _mm512_and_si512(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_shift_right_vec512(q, shift);           // q >>= shift
+    q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+#if defined(LIBDIVIDE_AVX2)
+
+static LIBDIVIDE_INLINE __m256i libdivide_u16_do_vec256(
+    __m256i numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s16_do_vec256(
+    __m256i numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u32_do_vec256(
+    __m256i numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s32_do_vec256(
+    __m256i numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u64_do_vec256(
+    __m256i numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s64_do_vec256(
+    __m256i numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE __m256i libdivide_u16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_u64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m256i libdivide_s64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+// Implementation of _mm256_srai_epi64(v, 63) (from AVX512).
+static LIBDIVIDE_INLINE __m256i libdivide_s64_signbits_vec256(__m256i v) {
+    __m256i hiBitsDuped = _mm256_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1));
+    __m256i signBits = _mm256_srai_epi32(hiBitsDuped, 31);
+    return signBits;
+}
+
+// Implementation of _mm256_srai_epi64 (from AVX512).
+static LIBDIVIDE_INLINE __m256i libdivide_s64_shift_right_vec256(__m256i v, int amt) {
+    const int b = 64 - amt;
+    __m256i m = _mm256_set1_epi64x((uint64_t)1 << (b - 1));
+    __m256i x = _mm256_srli_epi64(v, amt);
+    __m256i result = _mm256_sub_epi64(_mm256_xor_si256(x, m), m);
+    return result;
+}
+
+// Here, b is assumed to contain one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_u32_vec256(__m256i a, __m256i b) {
+    __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epu32(a, b), 32);
+    __m256i a1X3X = _mm256_srli_epi64(a, 32);
+    __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0);
+    __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epu32(a1X3X, b), mask);
+    return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// b is one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_s32_vec256(__m256i a, __m256i b) {
+    __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epi32(a, b), 32);
+    __m256i a1X3X = _mm256_srli_epi64(a, 32);
+    __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0);
+    __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epi32(a1X3X, b), mask);
+    return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// Here, y is assumed to contain one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_u64_vec256(__m256i x, __m256i y) {
+    // see m128i variant for comments.
+    __m256i x0y0 = _mm256_mul_epu32(x, y);
+    __m256i x0y0_hi = _mm256_srli_epi64(x0y0, 32);
+
+    __m256i x1 = _mm256_shuffle_epi32(x, _MM_SHUFFLE(3, 3, 1, 1));
+    __m256i y1 = _mm256_shuffle_epi32(y, _MM_SHUFFLE(3, 3, 1, 1));
+
+    __m256i x0y1 = _mm256_mul_epu32(x, y1);
+    __m256i x1y0 = _mm256_mul_epu32(x1, y);
+    __m256i x1y1 = _mm256_mul_epu32(x1, y1);
+
+    __m256i mask = _mm256_set1_epi64x(0xFFFFFFFF);
+    __m256i temp = _mm256_add_epi64(x1y0, x0y0_hi);
+    __m256i temp_lo = _mm256_and_si256(temp, mask);
+    __m256i temp_hi = _mm256_srli_epi64(temp, 32);
+
+    temp_lo = _mm256_srli_epi64(_mm256_add_epi64(temp_lo, x0y1), 32);
+    temp_hi = _mm256_add_epi64(x1y1, temp_hi);
+    return _mm256_add_epi64(temp_lo, temp_hi);
+}
+
+// y is one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m256i libdivide_mullhi_s64_vec256(__m256i x, __m256i y) {
+    __m256i p = libdivide_mullhi_u64_vec256(x, y);
+    __m256i t1 = _mm256_and_si256(libdivide_s64_signbits_vec256(x), y);
+    __m256i t2 = _mm256_and_si256(libdivide_s64_signbits_vec256(y), x);
+    p = _mm256_sub_epi64(p, t1);
+    p = _mm256_sub_epi64(p, t2);
+    return p;
+}
+
+////////// UINT16
+
+__m256i libdivide_u16_do_vec256(__m256i numers, const struct libdivide_u16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm256_srli_epi16(numers, more);
+    } else {
+        __m256i q = _mm256_mulhi_epu16(numers, _mm256_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            __m256i t = _mm256_adds_epu16(_mm256_srli_epi16(_mm256_subs_epu16(numers, q), 1), q);
+            return _mm256_srli_epi16(t, (more & LIBDIVIDE_16_SHIFT_MASK));
+        } else {
+            return _mm256_srli_epi16(q, more);
+        }
+    }
+}
+
+__m256i libdivide_u16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u16_branchfree_t *denom) {
+    __m256i q = _mm256_mulhi_epu16(numers, _mm256_set1_epi16(denom->magic));
+    __m256i t = _mm256_adds_epu16(_mm256_srli_epi16(_mm256_subs_epu16(numers, q), 1), q);
+    return _mm256_srli_epi16(t, denom->more);
+}
+
+////////// UINT32
+
+__m256i libdivide_u32_do_vec256(__m256i numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm256_srli_epi32(numers, more);
+    } else {
+        __m256i q = libdivide_mullhi_u32_vec256(numers, _mm256_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q);
+            return _mm256_srli_epi32(t, shift);
+        } else {
+            return _mm256_srli_epi32(q, more);
+        }
+    }
+}
+
+__m256i libdivide_u32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u32_branchfree_t *denom) {
+    __m256i q = libdivide_mullhi_u32_vec256(numers, _mm256_set1_epi32(denom->magic));
+    __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q);
+    return _mm256_srli_epi32(t, denom->more);
+}
+
+////////// UINT64
+
+__m256i libdivide_u64_do_vec256(__m256i numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm256_srli_epi64(numers, more);
+    } else {
+        __m256i q = libdivide_mullhi_u64_vec256(numers, _mm256_set1_epi64x(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q);
+            return _mm256_srli_epi64(t, shift);
+        } else {
+            return _mm256_srli_epi64(q, more);
+        }
+    }
+}
+
+__m256i libdivide_u64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_u64_branchfree_t *denom) {
+    __m256i q = libdivide_mullhi_u64_vec256(numers, _mm256_set1_epi64x(denom->magic));
+    __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q);
+    return _mm256_srli_epi64(t, denom->more);
+}
+
+////////// SINT16
+
+__m256i libdivide_s16_do_vec256(__m256i numers, const struct libdivide_s16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint16_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+        uint16_t mask = ((uint16_t)1 << shift) - 1;
+        __m256i roundToZeroTweak = _mm256_set1_epi16(mask);
+        // q = numer + ((numer >> 15) & roundToZeroTweak);
+        __m256i q = _mm256_add_epi16(
+            numers, _mm256_and_si256(_mm256_srai_epi16(numers, 15), roundToZeroTweak));
+        q = _mm256_srai_epi16(q, shift);
+        __m256i sign = _mm256_set1_epi16((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm256_sub_epi16(_mm256_xor_si256(q, sign), sign);
+        return q;
+    } else {
+        __m256i q = _mm256_mulhi_epi16(numers, _mm256_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m256i sign = _mm256_set1_epi16((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm256_add_epi16(q, _mm256_sub_epi16(_mm256_xor_si256(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm256_srai_epi16(q, more & LIBDIVIDE_16_SHIFT_MASK);
+        q = _mm256_add_epi16(q, _mm256_srli_epi16(q, 15));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m256i libdivide_s16_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s16_branchfree_t *denom) {
+    int16_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    // must be arithmetic shift
+    __m256i sign = _mm256_set1_epi16((int8_t)more >> 7);
+    __m256i q = _mm256_mulhi_epi16(numers, _mm256_set1_epi16(magic));
+    q = _mm256_add_epi16(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint16_t is_power_of_2 = (magic == 0);
+    __m256i q_sign = _mm256_srai_epi16(q, 15);  // q_sign = q >> 15
+    __m256i mask = _mm256_set1_epi16(((uint16_t)1 << shift) - is_power_of_2);
+    q = _mm256_add_epi16(q, _mm256_and_si256(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm256_srai_epi16(q, shift);                          // q >>= shift
+    q = _mm256_sub_epi16(_mm256_xor_si256(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT32
+
+__m256i libdivide_s32_do_vec256(__m256i numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        __m256i roundToZeroTweak = _mm256_set1_epi32(mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        __m256i q = _mm256_add_epi32(
+            numers, _mm256_and_si256(_mm256_srai_epi32(numers, 31), roundToZeroTweak));
+        q = _mm256_srai_epi32(q, shift);
+        __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign);
+        return q;
+    } else {
+        __m256i q = libdivide_mullhi_s32_vec256(numers, _mm256_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm256_add_epi32(q, _mm256_sub_epi32(_mm256_xor_si256(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm256_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = _mm256_add_epi32(q, _mm256_srli_epi32(q, 31));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m256i libdivide_s32_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+    __m256i q = libdivide_mullhi_s32_vec256(numers, _mm256_set1_epi32(magic));
+    q = _mm256_add_epi32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    __m256i q_sign = _mm256_srai_epi32(q, 31);  // q_sign = q >> 31
+    __m256i mask = _mm256_set1_epi32(((uint32_t)1 << shift) - is_power_of_2);
+    q = _mm256_add_epi32(q, _mm256_and_si256(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm256_srai_epi32(q, shift);                          // q >>= shift
+    q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+__m256i libdivide_s64_do_vec256(__m256i numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        __m256i roundToZeroTweak = _mm256_set1_epi64x(mask);
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        __m256i q = _mm256_add_epi64(
+            numers, _mm256_and_si256(libdivide_s64_signbits_vec256(numers), roundToZeroTweak));
+        q = libdivide_s64_shift_right_vec256(q, shift);
+        __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign);
+        return q;
+    } else {
+        __m256i q = libdivide_mullhi_s64_vec256(numers, _mm256_set1_epi64x(magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm256_add_epi64(q, _mm256_sub_epi64(_mm256_xor_si256(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_shift_right_vec256(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = _mm256_add_epi64(q, _mm256_srli_epi64(q, 63));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m256i libdivide_s64_branchfree_do_vec256(
+    __m256i numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    __m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
+
+    // libdivide_mullhi_s64(numers, magic);
+    __m256i q = libdivide_mullhi_s64_vec256(numers, _mm256_set1_epi64x(magic));
+    q = _mm256_add_epi64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    __m256i q_sign = libdivide_s64_signbits_vec256(q);  // q_sign = q >> 63
+    __m256i mask = _mm256_set1_epi64x(((uint64_t)1 << shift) - is_power_of_2);
+    q = _mm256_add_epi64(q, _mm256_and_si256(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_shift_right_vec256(q, shift);           // q >>= shift
+    q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+#if defined(LIBDIVIDE_SSE2)
+
+static LIBDIVIDE_INLINE __m128i libdivide_u16_do_vec128(
+    __m128i numers, const struct libdivide_u16_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s16_do_vec128(
+    __m128i numers, const struct libdivide_s16_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u32_do_vec128(
+    __m128i numers, const struct libdivide_u32_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s32_do_vec128(
+    __m128i numers, const struct libdivide_s32_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u64_do_vec128(
+    __m128i numers, const struct libdivide_u64_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s64_do_vec128(
+    __m128i numers, const struct libdivide_s64_t *denom);
+
+static LIBDIVIDE_INLINE __m128i libdivide_u16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s16_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s32_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_u64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u64_branchfree_t *denom);
+static LIBDIVIDE_INLINE __m128i libdivide_s64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+// Implementation of _mm_srai_epi64(v, 63) (from AVX512).
+static LIBDIVIDE_INLINE __m128i libdivide_s64_signbits_vec128(__m128i v) {
+    __m128i hiBitsDuped = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1));
+    __m128i signBits = _mm_srai_epi32(hiBitsDuped, 31);
+    return signBits;
+}
+
+// Implementation of _mm_srai_epi64 (from AVX512).
+static LIBDIVIDE_INLINE __m128i libdivide_s64_shift_right_vec128(__m128i v, int amt) {
+    const int b = 64 - amt;
+    __m128i m = _mm_set1_epi64x((uint64_t)1 << (b - 1));
+    __m128i x = _mm_srli_epi64(v, amt);
+    __m128i result = _mm_sub_epi64(_mm_xor_si128(x, m), m);
+    return result;
+}
+
+// Here, b is assumed to contain one 32-bit value repeated.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_u32_vec128(__m128i a, __m128i b) {
+    __m128i hi_product_0Z2Z = _mm_srli_epi64(_mm_mul_epu32(a, b), 32);
+    __m128i a1X3X = _mm_srli_epi64(a, 32);
+    __m128i mask = _mm_set_epi32(-1, 0, -1, 0);
+    __m128i hi_product_Z1Z3 = _mm_and_si128(_mm_mul_epu32(a1X3X, b), mask);
+    return _mm_or_si128(hi_product_0Z2Z, hi_product_Z1Z3);
+}
+
+// SSE2 does not have a signed multiplication instruction, but we can convert
+// unsigned to signed pretty efficiently. Again, b is just a 32 bit value
+// repeated four times.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_s32_vec128(__m128i a, __m128i b) {
+    __m128i p = libdivide_mullhi_u32_vec128(a, b);
+    // t1 = (a >> 31) & y, arithmetic shift
+    __m128i t1 = _mm_and_si128(_mm_srai_epi32(a, 31), b);
+    __m128i t2 = _mm_and_si128(_mm_srai_epi32(b, 31), a);
+    p = _mm_sub_epi32(p, t1);
+    p = _mm_sub_epi32(p, t2);
+    return p;
+}
+
+// Here, y is assumed to contain one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_u64_vec128(__m128i x, __m128i y) {
+    // full 128 bits product is:
+    // x0*y0 + (x0*y1 << 32) + (x1*y0 << 32) + (x1*y1 << 64)
+    // Note x0,y0,x1,y1 are all conceptually uint32, products are 32x32->64.
+
+    // Compute x0*y0.
+    // Note x1, y1 are ignored by mul_epu32.
+    __m128i x0y0 = _mm_mul_epu32(x, y);
+    __m128i x0y0_hi = _mm_srli_epi64(x0y0, 32);
+
+    // Get x1, y1 in the low bits.
+    // We could shuffle or right shift. Shuffles are preferred as they preserve
+    // the source register for the next computation.
+    __m128i x1 = _mm_shuffle_epi32(x, _MM_SHUFFLE(3, 3, 1, 1));
+    __m128i y1 = _mm_shuffle_epi32(y, _MM_SHUFFLE(3, 3, 1, 1));
+
+    // No need to mask off top 32 bits for mul_epu32.
+    __m128i x0y1 = _mm_mul_epu32(x, y1);
+    __m128i x1y0 = _mm_mul_epu32(x1, y);
+    __m128i x1y1 = _mm_mul_epu32(x1, y1);
+
+    // Mask here selects low bits only.
+    __m128i mask = _mm_set1_epi64x(0xFFFFFFFF);
+    __m128i temp = _mm_add_epi64(x1y0, x0y0_hi);
+    __m128i temp_lo = _mm_and_si128(temp, mask);
+    __m128i temp_hi = _mm_srli_epi64(temp, 32);
+
+    temp_lo = _mm_srli_epi64(_mm_add_epi64(temp_lo, x0y1), 32);
+    temp_hi = _mm_add_epi64(x1y1, temp_hi);
+    return _mm_add_epi64(temp_lo, temp_hi);
+}
+
+// y is one 64-bit value repeated.
+static LIBDIVIDE_INLINE __m128i libdivide_mullhi_s64_vec128(__m128i x, __m128i y) {
+    __m128i p = libdivide_mullhi_u64_vec128(x, y);
+    __m128i t1 = _mm_and_si128(libdivide_s64_signbits_vec128(x), y);
+    __m128i t2 = _mm_and_si128(libdivide_s64_signbits_vec128(y), x);
+    p = _mm_sub_epi64(p, t1);
+    p = _mm_sub_epi64(p, t2);
+    return p;
+}
+
+////////// UINT26
+
+__m128i libdivide_u16_do_vec128(__m128i numers, const struct libdivide_u16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm_srli_epi16(numers, more);
+    } else {
+        __m128i q = _mm_mulhi_epu16(numers, _mm_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            __m128i t = _mm_adds_epu16(_mm_srli_epi16(_mm_subs_epu16(numers, q), 1), q);
+            return _mm_srli_epi16(t, (more & LIBDIVIDE_16_SHIFT_MASK));
+        } else {
+            return _mm_srli_epi16(q, more);
+        }
+    }
+}
+
+__m128i libdivide_u16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u16_branchfree_t *denom) {
+    __m128i q = _mm_mulhi_epu16(numers, _mm_set1_epi16(denom->magic));
+    __m128i t = _mm_adds_epu16(_mm_srli_epi16(_mm_subs_epu16(numers, q), 1), q);
+    return _mm_srli_epi16(t, denom->more);
+}
+
+////////// UINT32
+
+__m128i libdivide_u32_do_vec128(__m128i numers, const struct libdivide_u32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm_srli_epi32(numers, more);
+    } else {
+        __m128i q = libdivide_mullhi_u32_vec128(numers, _mm_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+            __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q);
+            return _mm_srli_epi32(t, shift);
+        } else {
+            return _mm_srli_epi32(q, more);
+        }
+    }
+}
+
+__m128i libdivide_u32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u32_branchfree_t *denom) {
+    __m128i q = libdivide_mullhi_u32_vec128(numers, _mm_set1_epi32(denom->magic));
+    __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q);
+    return _mm_srli_epi32(t, denom->more);
+}
+
+////////// UINT64
+
+__m128i libdivide_u64_do_vec128(__m128i numers, const struct libdivide_u64_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        return _mm_srli_epi64(numers, more);
+    } else {
+        __m128i q = libdivide_mullhi_u64_vec128(numers, _mm_set1_epi64x(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // uint32_t t = ((numer - q) >> 1) + q;
+            // return t >> denom->shift;
+            uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+            __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q);
+            return _mm_srli_epi64(t, shift);
+        } else {
+            return _mm_srli_epi64(q, more);
+        }
+    }
+}
+
+__m128i libdivide_u64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_u64_branchfree_t *denom) {
+    __m128i q = libdivide_mullhi_u64_vec128(numers, _mm_set1_epi64x(denom->magic));
+    __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q);
+    return _mm_srli_epi64(t, denom->more);
+}
+
+////////// SINT16
+
+__m128i libdivide_s16_do_vec128(__m128i numers, const struct libdivide_s16_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint16_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+        uint16_t mask = ((uint16_t)1 << shift) - 1;
+        __m128i roundToZeroTweak = _mm_set1_epi16(mask);
+        // q = numer + ((numer >> 15) & roundToZeroTweak);
+        __m128i q =
+            _mm_add_epi16(numers, _mm_and_si128(_mm_srai_epi16(numers, 15), roundToZeroTweak));
+        q = _mm_srai_epi16(q, shift);
+        __m128i sign = _mm_set1_epi16((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm_sub_epi16(_mm_xor_si128(q, sign), sign);
+        return q;
+    } else {
+        __m128i q = _mm_mulhi_epi16(numers, _mm_set1_epi16(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m128i sign = _mm_set1_epi16((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm_add_epi16(q, _mm_sub_epi16(_mm_xor_si128(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm_srai_epi16(q, more & LIBDIVIDE_16_SHIFT_MASK);
+        q = _mm_add_epi16(q, _mm_srli_epi16(q, 15));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m128i libdivide_s16_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s16_branchfree_t *denom) {
+    int16_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_16_SHIFT_MASK;
+    // must be arithmetic shift
+    __m128i sign = _mm_set1_epi16((int8_t)more >> 7);
+    __m128i q = _mm_mulhi_epi16(numers, _mm_set1_epi16(magic));
+    q = _mm_add_epi16(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint16_t is_power_of_2 = (magic == 0);
+    __m128i q_sign = _mm_srai_epi16(q, 15);  // q_sign = q >> 15
+    __m128i mask = _mm_set1_epi16(((uint16_t)1 << shift) - is_power_of_2);
+    q = _mm_add_epi16(q, _mm_and_si128(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm_srai_epi16(q, shift);                       // q >>= shift
+    q = _mm_sub_epi16(_mm_xor_si128(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT32
+
+__m128i libdivide_s32_do_vec128(__m128i numers, const struct libdivide_s32_t *denom) {
+    uint8_t more = denom->more;
+    if (!denom->magic) {
+        uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+        uint32_t mask = ((uint32_t)1 << shift) - 1;
+        __m128i roundToZeroTweak = _mm_set1_epi32(mask);
+        // q = numer + ((numer >> 31) & roundToZeroTweak);
+        __m128i q =
+            _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak));
+        q = _mm_srai_epi32(q, shift);
+        __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign);
+        return q;
+    } else {
+        __m128i q = libdivide_mullhi_s32_vec128(numers, _mm_set1_epi32(denom->magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm_add_epi32(q, _mm_sub_epi32(_mm_xor_si128(numers, sign), sign));
+        }
+        // q >>= shift
+        q = _mm_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
+        q = _mm_add_epi32(q, _mm_srli_epi32(q, 31));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m128i libdivide_s32_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s32_branchfree_t *denom) {
+    int32_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+    // must be arithmetic shift
+    __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+    __m128i q = libdivide_mullhi_s32_vec128(numers, _mm_set1_epi32(magic));
+    q = _mm_add_epi32(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2
+    uint32_t is_power_of_2 = (magic == 0);
+    __m128i q_sign = _mm_srai_epi32(q, 31);  // q_sign = q >> 31
+    __m128i mask = _mm_set1_epi32(((uint32_t)1 << shift) - is_power_of_2);
+    q = _mm_add_epi32(q, _mm_and_si128(q_sign, mask));  // q = q + (q_sign & mask)
+    q = _mm_srai_epi32(q, shift);                       // q >>= shift
+    q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+////////// SINT64
+
+__m128i libdivide_s64_do_vec128(__m128i numers, const struct libdivide_s64_t *denom) {
+    uint8_t more = denom->more;
+    int64_t magic = denom->magic;
+    if (magic == 0) {  // shift path
+        uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+        uint64_t mask = ((uint64_t)1 << shift) - 1;
+        __m128i roundToZeroTweak = _mm_set1_epi64x(mask);
+        // q = numer + ((numer >> 63) & roundToZeroTweak);
+        __m128i q = _mm_add_epi64(
+            numers, _mm_and_si128(libdivide_s64_signbits_vec128(numers), roundToZeroTweak));
+        q = libdivide_s64_shift_right_vec128(q, shift);
+        __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+        // q = (q ^ sign) - sign;
+        q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign);
+        return q;
+    } else {
+        __m128i q = libdivide_mullhi_s64_vec128(numers, _mm_set1_epi64x(magic));
+        if (more & LIBDIVIDE_ADD_MARKER) {
+            // must be arithmetic shift
+            __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+            // q += ((numer ^ sign) - sign);
+            q = _mm_add_epi64(q, _mm_sub_epi64(_mm_xor_si128(numers, sign), sign));
+        }
+        // q >>= denom->mult_path.shift
+        q = libdivide_s64_shift_right_vec128(q, more & LIBDIVIDE_64_SHIFT_MASK);
+        q = _mm_add_epi64(q, _mm_srli_epi64(q, 63));  // q += (q < 0)
+        return q;
+    }
+}
+
+__m128i libdivide_s64_branchfree_do_vec128(
+    __m128i numers, const struct libdivide_s64_branchfree_t *denom) {
+    int64_t magic = denom->magic;
+    uint8_t more = denom->more;
+    uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+    // must be arithmetic shift
+    __m128i sign = _mm_set1_epi32((int8_t)more >> 7);
+
+    // libdivide_mullhi_s64(numers, magic);
+    __m128i q = libdivide_mullhi_s64_vec128(numers, _mm_set1_epi64x(magic));
+    q = _mm_add_epi64(q, numers);  // q += numers
+
+    // If q is non-negative, we have nothing to do.
+    // If q is negative, we want to add either (2**shift)-1 if d is
+    // a power of 2, or (2**shift) if it is not a power of 2.
+    uint32_t is_power_of_2 = (magic == 0);
+    __m128i q_sign = libdivide_s64_signbits_vec128(q);  // q_sign = q >> 63
+    __m128i mask = _mm_set1_epi64x(((uint64_t)1 << shift) - is_power_of_2);
+    q = _mm_add_epi64(q, _mm_and_si128(q_sign, mask));  // q = q + (q_sign & mask)
+    q = libdivide_s64_shift_right_vec128(q, shift);     // q >>= shift
+    q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign);    // q = (q ^ sign) - sign
+    return q;
+}
+
+#endif
+
+/////////// C++ stuff
+
+#ifdef __cplusplus
+
+enum Branching {
+    BRANCHFULL,  // use branching algorithms
+    BRANCHFREE   // use branchfree algorithms
+};
+
+namespace detail {
+enum Signedness {
+    SIGNED,
+    UNSIGNED,
+};
+
+#if defined(LIBDIVIDE_NEON)
+// Helper to deduce NEON vector type for integral type.
+template <int _WIDTH, Signedness _SIGN>
+struct NeonVec {};
+
+template <>
+struct NeonVec<16, UNSIGNED> {
+    typedef uint16x8_t type;
+};
+
+template <>
+struct NeonVec<16, SIGNED> {
+    typedef int16x8_t type;
+};
+
+template <>
+struct NeonVec<32, UNSIGNED> {
+    typedef uint32x4_t type;
+};
+
+template <>
+struct NeonVec<32, SIGNED> {
+    typedef int32x4_t type;
+};
+
+template <>
+struct NeonVec<64, UNSIGNED> {
+    typedef uint64x2_t type;
+};
+
+template <>
+struct NeonVec<64, SIGNED> {
+    typedef int64x2_t type;
+};
+
+template <typename T>
+struct NeonVecFor {
+    // See 'class divider' for an explanation of these template parameters.
+    typedef typename NeonVec<sizeof(T) * 8, (((T)0 >> 0) > (T)(-1) ? SIGNED : UNSIGNED)>::type type;
+};
+
+#define LIBDIVIDE_DIVIDE_NEON(ALGO, INT_TYPE)                    \
+    LIBDIVIDE_INLINE typename NeonVecFor<INT_TYPE>::type divide( \
+        typename NeonVecFor<INT_TYPE>::type n) const {           \
+        return libdivide_##ALGO##_do_vec128(n, &denom);          \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_NEON(ALGO, INT_TYPE)
+#endif
+
+#if defined(LIBDIVIDE_SSE2)
+#define LIBDIVIDE_DIVIDE_SSE2(ALGO)                     \
+    LIBDIVIDE_INLINE __m128i divide(__m128i n) const {  \
+        return libdivide_##ALGO##_do_vec128(n, &denom); \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_SSE2(ALGO)
+#endif
+
+#if defined(LIBDIVIDE_AVX2)
+#define LIBDIVIDE_DIVIDE_AVX2(ALGO)                     \
+    LIBDIVIDE_INLINE __m256i divide(__m256i n) const {  \
+        return libdivide_##ALGO##_do_vec256(n, &denom); \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_AVX2(ALGO)
+#endif
+
+#if defined(LIBDIVIDE_AVX512)
+#define LIBDIVIDE_DIVIDE_AVX512(ALGO)                   \
+    LIBDIVIDE_INLINE __m512i divide(__m512i n) const {  \
+        return libdivide_##ALGO##_do_vec512(n, &denom); \
+    }
+#else
+#define LIBDIVIDE_DIVIDE_AVX512(ALGO)
+#endif
+
+// The DISPATCHER_GEN() macro generates C++ methods (for the given integer
+// and algorithm types) that redirect to libdivide's C API.
+#define DISPATCHER_GEN(T, ALGO)                                                       \
+    libdivide_##ALGO##_t denom;                                                       \
+    LIBDIVIDE_INLINE dispatcher() {}                                                  \
+    LIBDIVIDE_INLINE dispatcher(T d) : denom(libdivide_##ALGO##_gen(d)) {}            \
+    LIBDIVIDE_INLINE T divide(T n) const { return libdivide_##ALGO##_do(n, &denom); } \
+    LIBDIVIDE_INLINE T recover() const { return libdivide_##ALGO##_recover(&denom); } \
+    LIBDIVIDE_DIVIDE_NEON(ALGO, T)                                                    \
+    LIBDIVIDE_DIVIDE_SSE2(ALGO)                                                       \
+    LIBDIVIDE_DIVIDE_AVX2(ALGO)                                                       \
+    LIBDIVIDE_DIVIDE_AVX512(ALGO)
+
+// The dispatcher selects a specific division algorithm for a given
+// width, signedness, and ALGO using partial template specialization.
+template <int _WIDTH, Signedness _SIGN, Branching _ALGO>
+struct dispatcher {};
+
+template <>
+struct dispatcher<16, SIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(int16_t, s16)
+};
+template <>
+struct dispatcher<16, SIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(int16_t, s16_branchfree)
+};
+template <>
+struct dispatcher<16, UNSIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(uint16_t, u16)
+};
+template <>
+struct dispatcher<16, UNSIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(uint16_t, u16_branchfree)
+};
+template <>
+struct dispatcher<32, SIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(int32_t, s32)
+};
+template <>
+struct dispatcher<32, SIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(int32_t, s32_branchfree)
+};
+template <>
+struct dispatcher<32, UNSIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(uint32_t, u32)
+};
+template <>
+struct dispatcher<32, UNSIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(uint32_t, u32_branchfree)
+};
+template <>
+struct dispatcher<64, SIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(int64_t, s64)
+};
+template <>
+struct dispatcher<64, SIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(int64_t, s64_branchfree)
+};
+template <>
+struct dispatcher<64, UNSIGNED, BRANCHFULL> {
+    DISPATCHER_GEN(uint64_t, u64)
+};
+template <>
+struct dispatcher<64, UNSIGNED, BRANCHFREE> {
+    DISPATCHER_GEN(uint64_t, u64_branchfree)
+};
+}  // namespace detail
+
+#if defined(LIBDIVIDE_NEON)
+// Allow NeonVecFor outside of detail namespace.
+template <typename T>
+struct NeonVecFor {
+    typedef typename detail::NeonVecFor<T>::type type;
+};
+#endif
+
+// This is the main divider class for use by the user (C++ API).
+// The actual division algorithm is selected using the dispatcher struct
+// based on the integer width and algorithm template parameters.
+template <typename T, Branching ALGO = BRANCHFULL>
+class divider {
+   private:
+    // Dispatch based on the size and signedness.
+    // We avoid using type_traits as it's not available in AVR.
+    // Detect signedness by checking if T(-1) is less than T(0).
+    // Also throw in a shift by 0, which prevents floating point types from being passed.
+    typedef detail::dispatcher<sizeof(T) * 8,
+        (((T)0 >> 0) > (T)(-1) ? detail::SIGNED : detail::UNSIGNED), ALGO>
+        dispatcher_t;
+
+   public:
+    // We leave the default constructor empty so that creating
+    // an array of dividers and then initializing them
+    // later doesn't slow us down.
+    divider() {}
+
+    // Constructor that takes the divisor as a parameter
+    LIBDIVIDE_INLINE divider(T d) : div(d) {}
+
+    // Divides n by the divisor
+    LIBDIVIDE_INLINE T divide(T n) const { return div.divide(n); }
+
+    // Recovers the divisor, returns the value that was
+    // used to initialize this divider object.
+    T recover() const { return div.recover(); }
+
+    bool operator==(const divider<T, ALGO> &other) const {
+        return div.denom.magic == other.denom.magic && div.denom.more == other.denom.more;
+    }
+
+    bool operator!=(const divider<T, ALGO> &other) const { return !(*this == other); }
+
+    // Vector variants treat the input as packed integer values with the same type as the divider
+    // (e.g. s32, u32, s64, u64) and divides each of them by the divider, returning the packed
+    // quotients.
+#if defined(LIBDIVIDE_SSE2)
+    LIBDIVIDE_INLINE __m128i divide(__m128i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_AVX2)
+    LIBDIVIDE_INLINE __m256i divide(__m256i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_AVX512)
+    LIBDIVIDE_INLINE __m512i divide(__m512i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_NEON)
+    LIBDIVIDE_INLINE typename NeonVecFor<T>::type divide(typename NeonVecFor<T>::type n) const {
+        return div.divide(n);
+    }
+#endif
+
+   private:
+    // Storage for the actual divisor
+    dispatcher_t div;
+};
+
+// Overload of operator / for scalar division
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE T operator/(T n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+// Overload of operator /= for scalar division
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE T &operator/=(T &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+
+// Overloads for vector types.
+#if defined(LIBDIVIDE_SSE2)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m128i operator/(__m128i n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m128i operator/=(__m128i &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+#if defined(LIBDIVIDE_AVX2)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m256i operator/(__m256i n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m256i operator/=(__m256i &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+#if defined(LIBDIVIDE_AVX512)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m512i operator/(__m512i n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE __m512i operator/=(__m512i &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+
+#if defined(LIBDIVIDE_NEON)
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE typename NeonVecFor<T>::type operator/(
+    typename NeonVecFor<T>::type n, const divider<T, ALGO> &div) {
+    return div.divide(n);
+}
+
+template <typename T, Branching ALGO>
+LIBDIVIDE_INLINE typename NeonVecFor<T>::type operator/=(
+    typename NeonVecFor<T>::type &n, const divider<T, ALGO> &div) {
+    n = div.divide(n);
+    return n;
+}
+#endif
+
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
+// libdivide::branchfree_divider<T>
+template <typename T>
+using branchfree_divider = divider<T, BRANCHFREE>;
+#endif
+
+}  // namespace libdivide
+
+#endif  // __cplusplus
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#endif  // LIBDIVIDE_H
-- 
2.40.1

v3-0008-Add-pragmas-to-libdivide-to-make-header-check-hap.patchapplication/octet-stream; name=v3-0008-Add-pragmas-to-libdivide-to-make-header-check-hap.patchDownload
From 1a414bbd98fd1a7b6902fc5e7a46c22ea0844b1b Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Fri, 29 Sep 2023 15:05:00 -0400
Subject: [PATCH v3 8/8] Add pragmas to libdivide to make header check happy

---
 src/include/common/libdivide.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/include/common/libdivide.h b/src/include/common/libdivide.h
index 96dd272119..340f9a168f 100644
--- a/src/include/common/libdivide.h
+++ b/src/include/common/libdivide.h
@@ -1,3 +1,7 @@
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
+#undef __cplusplus
+
 // libdivide.h - Optimized integer division
 // https://libdivide.com
 //
-- 
2.40.1