v3-0002-Add-new-alignment-info-to-pg_control-and-check-it.patch
text/x-diff
Filename: v3-0002-Add-new-alignment-info-to-pg_control-and-check-it.patch
Type: text/x-diff
Part: 1
Patch
Format: format-patch
Series: patch v3-0002
Subject: Add new alignment info to pg_control, and check it during pg_upgrade.
| File | + | − |
|---|---|---|
| doc/src/sgml/func/func-info.sgml | 10 | 0 |
| src/backend/access/transam/xlog.c | 22 | 0 |
| src/backend/utils/misc/pg_controldata.c | 23 | 26 |
| src/bin/pg_controldata/pg_controldata.c | 4 | 0 |
| src/bin/pg_resetwal/pg_resetwal.c | 6 | 0 |
| src/bin/pg_upgrade/controldata.c | 60 | 7 |
| src/bin/pg_upgrade/pg_upgrade.h | 8 | 1 |
| src/include/catalog/pg_control.h | 8 | 6 |
| src/include/catalog/pg_proc.dat | 3 | 3 |
From 57382d99270442cf042bf0d481cc9233264842c5 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 2 Feb 2026 15:23:45 -0500
Subject: [PATCH v3 2/2] Add new alignment info to pg_control, and check it
during pg_upgrade.
Having decoupled ALIGNOF_INT64_T and ALIGNOF_DOUBLE from
MAXIMUM_ALIGNOF, we had better record all three values in pg_control,
and check them to verify database compatibility.
In pg_upgrade, if the source cluster's pg_controldata doesn't report
the new fields, assume they have values equal to its MAXALIGN.
This allows safe upgrading from pre-v19 databases.
This will require a catversion bump.
Discussion: https://postgr.es/m/1127261.1769649624@sss.pgh.pa.us
---
doc/src/sgml/func/func-info.sgml | 10 ++++
src/backend/access/transam/xlog.c | 22 ++++++++
src/backend/utils/misc/pg_controldata.c | 49 +++++++++---------
src/bin/pg_controldata/pg_controldata.c | 4 ++
src/bin/pg_resetwal/pg_resetwal.c | 6 +++
src/bin/pg_upgrade/controldata.c | 67 ++++++++++++++++++++++---
src/bin/pg_upgrade/pg_upgrade.h | 9 +++-
src/include/catalog/pg_control.h | 14 +++---
src/include/catalog/pg_proc.dat | 6 +--
9 files changed, 144 insertions(+), 43 deletions(-)
diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml
index 294f45e82a3..9c235f47611 100644
--- a/doc/src/sgml/func/func-info.sgml
+++ b/doc/src/sgml/func/func-info.sgml
@@ -3545,6 +3545,16 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres}
<tbody>
+ <row>
+ <entry><structfield>int8_data_alignment</structfield></entry>
+ <entry><type>integer</type></entry>
+ </row>
+
+ <row>
+ <entry><structfield>float8_data_alignment</structfield></entry>
+ <entry><type>integer</type></entry>
+ </row>
+
<row>
<entry><structfield>max_data_alignment</structfield></entry>
<entry><type>integer</type></entry>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 13ec6225b85..8cb5d033911 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4284,6 +4284,8 @@ WriteControlFile(void)
ControlFile->pg_control_version = PG_CONTROL_VERSION;
ControlFile->catalog_version_no = CATALOG_VERSION_NO;
+ ControlFile->int64Align = ALIGNOF_INT64_T;
+ ControlFile->doubleAlign = ALIGNOF_DOUBLE;
ControlFile->maxAlign = MAXIMUM_ALIGNOF;
ControlFile->floatFormat = FLOATFORMAT_VALUE;
@@ -4473,6 +4475,26 @@ ReadControlFile(void)
"CATALOG_VERSION_NO", ControlFile->catalog_version_no,
"CATALOG_VERSION_NO", CATALOG_VERSION_NO),
errhint("It looks like you need to initdb.")));
+ if (ControlFile->int64Align != ALIGNOF_INT64_T)
+ ereport(FATAL,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("database files are incompatible with server"),
+ /* translator: %s is a variable name and %d is its value */
+ errdetail("The database cluster was initialized with %s %d,"
+ " but the server was compiled with %s %d.",
+ "ALIGNOF_INT64_T", ControlFile->int64Align,
+ "ALIGNOF_INT64_T", ALIGNOF_INT64_T),
+ errhint("It looks like you need to initdb.")));
+ if (ControlFile->doubleAlign != ALIGNOF_DOUBLE)
+ ereport(FATAL,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("database files are incompatible with server"),
+ /* translator: %s is a variable name and %d is its value */
+ errdetail("The database cluster was initialized with %s %d,"
+ " but the server was compiled with %s %d.",
+ "ALIGNOF_DOUBLE", ControlFile->doubleAlign,
+ "ALIGNOF_DOUBLE", ALIGNOF_DOUBLE),
+ errhint("It looks like you need to initdb.")));
if (ControlFile->maxAlign != MAXIMUM_ALIGNOF)
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index c6d9cbb1577..1f901dacc01 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -203,8 +203,9 @@ pg_control_recovery(PG_FUNCTION_ARGS)
Datum
pg_control_init(PG_FUNCTION_ARGS)
{
- Datum values[12];
- bool nulls[12];
+ Datum values[14];
+ bool nulls[14];
+ int j = 0;
TupleDesc tupdesc;
HeapTuple htup;
ControlFileData *ControlFile;
@@ -221,41 +222,37 @@ pg_control_init(PG_FUNCTION_ARGS)
ereport(ERROR,
(errmsg("calculated CRC checksum does not match value stored in file")));
- values[0] = Int32GetDatum(ControlFile->maxAlign);
- nulls[0] = false;
+ memset(nulls, 0, sizeof(nulls));
- values[1] = Int32GetDatum(ControlFile->blcksz);
- nulls[1] = false;
+ values[j++] = Int32GetDatum(ControlFile->int64Align);
- values[2] = Int32GetDatum(ControlFile->relseg_size);
- nulls[2] = false;
+ values[j++] = Int32GetDatum(ControlFile->doubleAlign);
- values[3] = Int32GetDatum(ControlFile->xlog_blcksz);
- nulls[3] = false;
+ values[j++] = Int32GetDatum(ControlFile->maxAlign);
- values[4] = Int32GetDatum(ControlFile->xlog_seg_size);
- nulls[4] = false;
+ values[j++] = Int32GetDatum(ControlFile->blcksz);
- values[5] = Int32GetDatum(ControlFile->nameDataLen);
- nulls[5] = false;
+ values[j++] = Int32GetDatum(ControlFile->relseg_size);
- values[6] = Int32GetDatum(ControlFile->indexMaxKeys);
- nulls[6] = false;
+ values[j++] = Int32GetDatum(ControlFile->xlog_blcksz);
- values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size);
- nulls[7] = false;
+ values[j++] = Int32GetDatum(ControlFile->xlog_seg_size);
- values[8] = Int32GetDatum(ControlFile->loblksize);
- nulls[8] = false;
+ values[j++] = Int32GetDatum(ControlFile->nameDataLen);
- values[9] = BoolGetDatum(ControlFile->float8ByVal);
- nulls[9] = false;
+ values[j++] = Int32GetDatum(ControlFile->indexMaxKeys);
- values[10] = Int32GetDatum(ControlFile->data_checksum_version);
- nulls[10] = false;
+ values[j++] = Int32GetDatum(ControlFile->toast_max_chunk_size);
- values[11] = BoolGetDatum(ControlFile->default_char_signedness);
- nulls[11] = false;
+ values[j++] = Int32GetDatum(ControlFile->loblksize);
+
+ values[j++] = BoolGetDatum(ControlFile->float8ByVal);
+
+ values[j++] = Int32GetDatum(ControlFile->data_checksum_version);
+
+ values[j++] = BoolGetDatum(ControlFile->default_char_signedness);
+
+ Assert(j == lengthof(values));
htup = heap_form_tuple(tupdesc, values, nulls);
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index a4060309ae0..b93f6521326 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -317,6 +317,10 @@ main(int argc, char *argv[])
ControlFile->max_locks_per_xact);
printf(_("track_commit_timestamp setting: %s\n"),
ControlFile->track_commit_timestamp ? _("on") : _("off"));
+ printf(_("int8 data alignment: %u\n"),
+ ControlFile->int64Align);
+ printf(_("float8 data alignment: %u\n"),
+ ControlFile->doubleAlign);
printf(_("Maximum data alignment: %u\n"),
ControlFile->maxAlign);
/* we don't print floatFormat since can't say much useful about it */
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 431b83a67da..59edf644d06 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -724,6 +724,8 @@ GuessControlValues(void)
ControlFile.max_prepared_xacts = 0;
ControlFile.max_locks_per_xact = 64;
+ ControlFile.int64Align = ALIGNOF_INT64_T;
+ ControlFile.doubleAlign = ALIGNOF_DOUBLE;
ControlFile.maxAlign = MAXIMUM_ALIGNOF;
ControlFile.floatFormat = FLOATFORMAT_VALUE;
ControlFile.blcksz = BLCKSZ;
@@ -791,6 +793,10 @@ PrintControlValues(bool guessed)
ControlFile.checkPointCopy.oldestCommitTsXid);
printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
ControlFile.checkPointCopy.newestCommitTsXid);
+ printf(_("int8 data alignment: %u\n"),
+ ControlFile.int64Align);
+ printf(_("float8 data alignment: %u\n"),
+ ControlFile.doubleAlign);
printf(_("Maximum data alignment: %u\n"),
ControlFile.maxAlign);
/* we don't print floatFormat since can't say much useful about it */
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index aa6e8b4de5d..d096affa550 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -52,7 +52,9 @@ get_control_data(ClusterInfo *cluster)
bool got_mxoff = false;
bool got_nextxlogfile = false;
bool got_float8_pass_by_value = false;
- bool got_align = false;
+ bool got_int64align = false;
+ bool got_doublealign = false;
+ bool got_maxalign = false;
bool got_blocksz = false;
bool got_largesz = false;
bool got_walsz = false;
@@ -383,6 +385,28 @@ get_control_data(ClusterInfo *cluster)
cluster->controldata.float8_pass_by_value = strstr(p, "by value") != NULL;
got_float8_pass_by_value = true;
}
+ else if ((p = strstr(bufin, "int8 data alignment:")) != NULL)
+ {
+ p = strchr(p, ':');
+
+ if (p == NULL || strlen(p) <= 1)
+ pg_fatal("%d: controldata retrieval problem", __LINE__);
+
+ p++; /* remove ':' char */
+ cluster->controldata.int64align = str2uint(p);
+ got_int64align = true;
+ }
+ else if ((p = strstr(bufin, "float8 data alignment:")) != NULL)
+ {
+ p = strchr(p, ':');
+
+ if (p == NULL || strlen(p) <= 1)
+ pg_fatal("%d: controldata retrieval problem", __LINE__);
+
+ p++; /* remove ':' char */
+ cluster->controldata.doublealign = str2uint(p);
+ got_doublealign = true;
+ }
else if ((p = strstr(bufin, "Maximum data alignment:")) != NULL)
{
p = strchr(p, ':');
@@ -391,8 +415,8 @@ get_control_data(ClusterInfo *cluster)
pg_fatal("%d: controldata retrieval problem", __LINE__);
p++; /* remove ':' char */
- cluster->controldata.align = str2uint(p);
- got_align = true;
+ cluster->controldata.maxalign = str2uint(p);
+ got_maxalign = true;
}
else if ((p = strstr(bufin, "Database block size:")) != NULL)
{
@@ -598,13 +622,28 @@ get_control_data(ClusterInfo *cluster)
#endif
}
+ /*
+ * Pre-v19 database clusters will not report int64align or doublealign.
+ * Their values must be equal to maxalign in that case.
+ */
+ if (cluster->controldata.ctrl_ver < INT64_ALIGN_PG_CONTROL_VER)
+ {
+ Assert(!got_int64align);
+ cluster->controldata.int64align = cluster->controldata.maxalign;
+ got_int64align = true;
+ Assert(!got_doublealign);
+ cluster->controldata.doublealign = cluster->controldata.maxalign;
+ got_doublealign = true;
+ }
+
/* verify that we got all the mandatory pg_control data */
if (!got_xid || !got_oid ||
!got_multi || !got_oldestxid ||
(!got_oldestmulti &&
cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
!got_mxoff || (!live_check && !got_nextxlogfile) ||
- !got_float8_pass_by_value || !got_align || !got_blocksz ||
+ !got_float8_pass_by_value || !got_blocksz ||
+ !got_int64align || !got_doublealign || !got_maxalign ||
!got_largesz || !got_walsz || !got_walseg || !got_ident ||
!got_index || !got_toast ||
(!got_large_object &&
@@ -645,7 +684,13 @@ get_control_data(ClusterInfo *cluster)
if (!got_float8_pass_by_value)
pg_log(PG_REPORT, " float8 argument passing method");
- if (!got_align)
+ if (!got_int64align)
+ pg_log(PG_REPORT, " int8 alignment");
+
+ if (!got_doublealign)
+ pg_log(PG_REPORT, " float8 alignment");
+
+ if (!got_maxalign)
pg_log(PG_REPORT, " maximum alignment");
if (!got_blocksz)
@@ -698,8 +743,16 @@ void
check_control_data(ControlData *oldctrl,
ControlData *newctrl)
{
- if (oldctrl->align == 0 || oldctrl->align != newctrl->align)
- pg_fatal("old and new pg_controldata alignments are invalid or do not match.\n"
+ if (oldctrl->int64align == 0 || oldctrl->int64align != newctrl->int64align)
+ pg_fatal("old and new pg_controldata int8 alignments are invalid or do not match.\n"
+ "Likely one cluster is a 32-bit install, the other 64-bit");
+
+ if (oldctrl->doublealign == 0 || oldctrl->doublealign != newctrl->doublealign)
+ pg_fatal("old and new pg_controldata float8 alignments are invalid or do not match.\n"
+ "Likely one cluster is a 32-bit install, the other 64-bit");
+
+ if (oldctrl->maxalign == 0 || oldctrl->maxalign != newctrl->maxalign)
+ pg_fatal("old and new pg_controldata maximum alignments are invalid or do not match.\n"
"Likely one cluster is a 32-bit install, the other 64-bit");
if (oldctrl->blocksz == 0 || oldctrl->blocksz != newctrl->blocksz)
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ec018e4f292..6caa00a7bed 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -138,6 +138,11 @@ extern char *output_files[];
*/
#define DEFAULT_CHAR_SIGNEDNESS_CAT_VER 202502212
+/*
+ * Separate int64Align and doubleAlign fields were added to pg_control here
+ */
+#define INT64_ALIGN_PG_CONTROL_VER 1902
+
/*
* Each relation is represented by a relinfo structure.
*/
@@ -245,7 +250,9 @@ typedef struct
uint64 chkpnt_nxtmxoff;
uint32 chkpnt_oldstMulti;
uint32 chkpnt_oldstxid;
- uint32 align;
+ uint32 int64align;
+ uint32 doublealign;
+ uint32 maxalign;
uint32 blocksz;
uint32 largesz;
uint32 walsz;
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 7503db1af51..f4f579651ca 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -22,7 +22,7 @@
/* Version identifier for this pg_control format */
-#define PG_CONTROL_VERSION 1901
+#define PG_CONTROL_VERSION 1902
/* Nonce key length, see below */
#define MOCK_AUTH_NONCE_LEN 32
@@ -190,15 +190,17 @@ typedef struct ControlFileData
* This data is used to check for hardware-architecture compatibility of
* the database and the backend executable. We need not check endianness
* explicitly, since the pg_control version will surely look wrong to a
- * machine of different endianness, but we do need to worry about MAXALIGN
- * and floating-point format. (Note: storage layout nominally also
- * depends on SHORTALIGN and INTALIGN, but in practice these are the same
- * on all architectures of interest.)
+ * machine of different endianness, but we do need to worry about data
+ * alignment and floating-point format. (Note: storage layout nominally
+ * also depends on SHORTALIGN and INTALIGN, but in practice these are the
+ * same on all architectures of interest.)
*
* Testing just one double value is not a very bulletproof test for
* floating-point compatibility, but it will catch most cases.
*/
- uint32 maxAlign; /* alignment requirement for tuples */
+ uint8 int64Align; /* alignment requirement for 8-byte integers */
+ uint8 doubleAlign; /* alignment requirement for float8 */
+ uint8 maxAlign; /* alignment requirement for tuples */
double floatFormat; /* constant 1234567.0 */
#define FLOATFORMAT_VALUE 1234567.0
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 5e5e33f64fc..238bb96f24e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12384,9 +12384,9 @@
descr => 'pg_controldata init state information as a function',
proname => 'pg_control_init', provolatile => 'v', prorettype => 'record',
proargtypes => '',
- proallargtypes => '{int4,int4,int4,int4,int4,int4,int4,int4,int4,bool,int4,bool}',
- proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o}',
- proargnames => '{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,float8_pass_by_value,data_page_checksum_version,default_char_signedness}',
+ proallargtypes => '{int4,int4,int4,int4,int4,int4,int4,int4,int4,int4,int4,bool,int4,bool}',
+ proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+ proargnames => '{int8_data_alignment,float8_data_alignment,max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,float8_pass_by_value,data_page_checksum_version,default_char_signedness}',
prosrc => 'pg_control_init' },
# subscripting support for built-in types
--
2.43.7