From e31938294770cc997a309c97c38424a8fe31834c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 12 Nov 2025 11:55:29 +0100
Subject: [PATCH 2/4] C11 alignas instead of unions

This changes a few union members that only existed to ensure
alignments and replaces them with the C11 alignas specifier.

This change only uses fundamental alignments (meaning approximately
alignments of basic types), which all C11 compilers must support.
There are opportunities for similar changes using extended alignments,
for example in PGIOAlignedBlock, but these are not necessarily
supported by all compilers, so they are kept as a separate change.
---
 src/backend/access/common/toast_internals.c |  4 +---
 src/backend/commands/async.c                | 12 ++++--------
 src/backend/storage/large_object/inv_api.c  |  8 ++------
 src/include/c.h                             | 10 +++-------
 4 files changed, 10 insertions(+), 24 deletions(-)

diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 81dbd67c725..63b848473f8 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -287,11 +287,9 @@ toast_save_datum(Relation rel, Datum value,
 		bool		t_isnull[3] = {0};
 		union
 		{
-			struct varlena hdr;
+			alignas(int32) struct varlena hdr;
 			/* this is to make the union big enough for a chunk: */
 			char		data[TOAST_MAX_CHUNK_SIZE + VARHDRSZ];
-			/* ensure union is aligned well enough: */
-			int32		align_it;
 		}			chunk_data;
 		int32		chunk_size;
 
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 4bd37d5beb5..4bdf21c319c 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -1854,12 +1854,8 @@ asyncQueueReadAllNotifications(void)
 	QueuePosition head;
 	Snapshot	snapshot;
 
-	/* page_buffer must be adequately aligned, so use a union */
-	union
-	{
-		char		buf[QUEUE_PAGESIZE];
-		AsyncQueueEntry align;
-	}			page_buffer;
+	/* page_buffer must be adequately aligned */
+	alignas(AsyncQueueEntry) char page_buffer[QUEUE_PAGESIZE];
 
 	/* Fetch current state */
 	LWLockAcquire(NotifyQueueLock, LW_SHARED);
@@ -1957,7 +1953,7 @@ asyncQueueReadAllNotifications(void)
 				/* fetch all the rest of the page */
 				copysize = QUEUE_PAGESIZE - curoffset;
 			}
-			memcpy(page_buffer.buf + curoffset,
+			memcpy(page_buffer + curoffset,
 				   NotifyCtl->shared->page_buffer[slotno] + curoffset,
 				   copysize);
 			/* Release lock that we got from SimpleLruReadPage_ReadOnly() */
@@ -1979,7 +1975,7 @@ asyncQueueReadAllNotifications(void)
 			 * while sending the notifications to the frontend.
 			 */
 			reachedStop = asyncQueueProcessPageEntries(&pos, head,
-													   page_buffer.buf,
+													   page_buffer,
 													   snapshot);
 		} while (!reachedStop);
 	}
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index f6d2f9dba13..2bd872d6581 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -556,11 +556,9 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 	bool		pfreeit;
 	union
 	{
-		bytea		hdr;
+		alignas(int32) bytea hdr;
 		/* this is to make the union big enough for a LO data chunk: */
 		char		data[LOBLKSIZE + VARHDRSZ];
-		/* ensure union is aligned well enough: */
-		int32		align_it;
 	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
@@ -747,11 +745,9 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 	Form_pg_largeobject olddata;
 	union
 	{
-		bytea		hdr;
+		alignas(int32) bytea hdr;
 		/* this is to make the union big enough for a LO data chunk: */
 		char		data[LOBLKSIZE + VARHDRSZ];
-		/* ensure union is aligned well enough: */
-		int32		align_it;
 	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
diff --git a/src/include/c.h b/src/include/c.h
index b3bf1b412ee..250d9c1d40b 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1117,15 +1117,11 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
  * Use this, not "char buf[BLCKSZ]", 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
- * the compiler knows the value must be MAXALIGN'ed (cf. configure's
- * computation of MAXIMUM_ALIGNOF).
+ * hardware.
  */
-typedef union PGAlignedBlock
+typedef struct PGAlignedBlock
 {
-	char		data[BLCKSZ];
-	double		force_align_d;
-	int64		force_align_i64;
+	alignas(MAXIMUM_ALIGNOF) char data[BLCKSZ];
 } PGAlignedBlock;
 
 /*
-- 
2.51.0

