Here's a v2 responding to your suggestions.  0001 refactors
att_align_nominal(), and then 0002 is nearly the same as the
prior patch except for rebasing over that change.  I added
a test case for alignment = max too (the other cases seem
covered already, somewhat indirectly via pg_upgrade testing).

I think this might be about ready to go, unless somebody has
a better idea than 'l' for the catalog representation of
TYPALIGN_INT64.

                        regards, tom lane

From 7b2949390ab32ec4d3c77dc1c1e239325bd3ec58 Mon Sep 17 00:00:00 2001
From: Tom Lane <[email protected]>
Date: Thu, 29 Jan 2026 18:34:26 -0500
Subject: [PATCH v2 1/2] Refactor att_align_nominal() to improve performance.

Separate att_align_nominal() into two macros, similarly to what
was already done with att_align_datum() and att_align_pointer().
The inner macro att_nominal_alignby() is really just TYPEALIGN(),
while att_align_nominal() retains its previous API by mapping
TYPALIGN_xxx values to numbers of bytes to align to and then
calling att_nominal_alignby().  In support of this, split out
tupdesc.c's logic to do that mapping into a publicly visible
function typalign_to_alignby().

Having done that, we can replace performance-critical uses of
att_align_nominal() with att_nominal_alignby(), where the
typalign_to_alignby() mapping is done just once outside the loop.

In most places I settled for doing typalign_to_alignby() once
per function.  We could in many places pass the alignby value
in from the caller if we wanted to change function APIs for this
purpose; but I'm a bit loath to do that, especially for exported
APIs that extensions might call.  Replacing a char typalign
argument by a uint8 typalignby argument would be an API change
that compilers would fail to warn about, thus silently breaking
code in hard-to-debug ways.  I did revise the APIs of array_iter_setup
and array_iter_next, moving the element type attribute arguments to
the former; if any external code uses those, the argument-count
change will cause visible compile failures.

I've not bothered to try to measure the actual performance benefit
from doing this; it may not be measurable on normal workloads.
The main point is to make sure that an upcoming patch that will
complicate the typalign_to_alignby mapping will not cause any
performance decrease.

Discussion: https://postgr.es/m/[email protected]
---
 contrib/dblink/dblink.c                 |   4 +-
 src/backend/access/common/tupdesc.c     |  21 +---
 src/backend/executor/execExprInterp.c   |   8 +-
 src/backend/utils/adt/array_expanded.c  |   4 +-
 src/backend/utils/adt/arrayfuncs.c      | 149 +++++++++++++-----------
 src/backend/utils/adt/multirangetypes.c |  16 +--
 src/backend/utils/adt/varlena.c         |   4 +-
 src/include/access/tupmacs.h            |  51 ++++++--
 src/include/utils/arrayaccess.h         |  25 ++--
 src/pl/plpython/plpy_typeio.c           |   3 +-
 10 files changed, 166 insertions(+), 119 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 8cb3166495c..2498d80c8e7 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -2069,6 +2069,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
 	int16		typlen;
 	bool		typbyval;
 	char		typalign;
+	uint8		typalignby;
 	char	  **values;
 	char	   *ptr;
 	bits8	   *bitmap;
@@ -2081,6 +2082,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
 
 	get_typlenbyvalalign(ARR_ELEMTYPE(array),
 						 &typlen, &typbyval, &typalign);
+	typalignby = typalign_to_alignby(typalign);
 
 	values = palloc_array(char *, nitems);
 
@@ -2098,7 +2100,7 @@ get_text_array_contents(ArrayType *array, int *numitems)
 		{
 			values[i] = TextDatumGetCString(PointerGetDatum(ptr));
 			ptr = att_addlength_pointer(ptr, typlen, ptr);
-			ptr = (char *) att_align_nominal(ptr, typalign);
+			ptr = (char *) att_nominal_alignby(ptr, typalignby);
 		}
 
 		/* advance bitmap pointer if any */
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 94b4f1f9975..b69d10f0a45 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -86,25 +86,8 @@ populate_compact_attribute_internal(Form_pg_attribute src,
 		IsCatalogRelationOid(src->attrelid) ? ATTNULLABLE_VALID :
 		ATTNULLABLE_UNKNOWN;
 
-	switch (src->attalign)
-	{
-		case TYPALIGN_INT:
-			dst->attalignby = ALIGNOF_INT;
-			break;
-		case TYPALIGN_CHAR:
-			dst->attalignby = sizeof(char);
-			break;
-		case TYPALIGN_DOUBLE:
-			dst->attalignby = ALIGNOF_DOUBLE;
-			break;
-		case TYPALIGN_SHORT:
-			dst->attalignby = ALIGNOF_SHORT;
-			break;
-		default:
-			dst->attalignby = 0;
-			elog(ERROR, "invalid attalign value: %c", src->attalign);
-			break;
-	}
+	/* Compute numeric alignment requirement, too */
+	dst->attalignby = typalign_to_alignby(src->attalign);
 }
 
 /*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index a7a5ac1e83b..61ff5ddc74c 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4032,6 +4032,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 	int16		typlen;
 	bool		typbyval;
 	char		typalign;
+	uint8		typalignby;
 	char	   *s;
 	bits8	   *bitmap;
 	int			bitmask;
@@ -4086,6 +4087,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 	typlen = op->d.scalararrayop.typlen;
 	typbyval = op->d.scalararrayop.typbyval;
 	typalign = op->d.scalararrayop.typalign;
+	typalignby = typalign_to_alignby(typalign);
 
 	/* Initialize result appropriately depending on useOr */
 	result = BoolGetDatum(!useOr);
@@ -4111,7 +4113,7 @@ ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
 		{
 			elt = fetch_att(s, typbyval, typlen);
 			s = att_addlength_pointer(s, typlen, s);
-			s = (char *) att_align_nominal(s, typalign);
+			s = (char *) att_nominal_alignby(s, typalignby);
 			fcinfo->args[1].value = elt;
 			fcinfo->args[1].isnull = false;
 		}
@@ -4255,6 +4257,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
 		int16		typlen;
 		bool		typbyval;
 		char		typalign;
+		uint8		typalignby;
 		int			nitems;
 		bool		has_nulls = false;
 		char	   *s;
@@ -4272,6 +4275,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
 							 &typlen,
 							 &typbyval,
 							 &typalign);
+		typalignby = typalign_to_alignby(typalign);
 
 		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
 
@@ -4318,7 +4322,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
 
 				element = fetch_att(s, typbyval, typlen);
 				s = att_addlength_pointer(s, typlen, s);
-				s = (char *) att_align_nominal(s, typalign);
+				s = (char *) att_nominal_alignby(s, typalignby);
 
 				saophash_insert(elements_tab->hashtab, element, &hashfound);
 			}
diff --git a/src/backend/utils/adt/array_expanded.c b/src/backend/utils/adt/array_expanded.c
index 01e3dddcbbb..7e8352af52b 100644
--- a/src/backend/utils/adt/array_expanded.c
+++ b/src/backend/utils/adt/array_expanded.c
@@ -238,6 +238,7 @@ EA_get_flat_size(ExpandedObjectHeader *eohptr)
 	Datum	   *dvalues;
 	bool	   *dnulls;
 	Size		nbytes;
+	uint8		typalignby;
 	int			i;
 
 	Assert(eah->ea_magic == EA_MAGIC);
@@ -261,12 +262,13 @@ EA_get_flat_size(ExpandedObjectHeader *eohptr)
 	dvalues = eah->dvalues;
 	dnulls = eah->dnulls;
 	nbytes = 0;
+	typalignby = typalign_to_alignby(eah->typalign);
 	for (i = 0; i < nelems; i++)
 	{
 		if (dnulls && dnulls[i])
 			continue;
 		nbytes = att_addlength_datum(nbytes, eah->typlen, dvalues[i]);
-		nbytes = att_align_nominal(nbytes, eah->typalign);
+		nbytes = att_nominal_alignby(nbytes, typalignby);
 		/* check for overflow of total request */
 		if (!AllocSizeIsValid(nbytes))
 			ereport(ERROR,
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index e71d32773b5..da68915ee20 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -75,6 +75,7 @@ typedef struct ArrayIteratorData
 	int16		typlen;			/* element type's length */
 	bool		typbyval;		/* element type's byval property */
 	char		typalign;		/* element type's align property */
+	uint8		typalignby;		/* typalign mapped to numeric alignment */
 
 	/* information about the requested slice size */
 	int			slice_ndim;		/* slice dimension, or 0 if not slicing */
@@ -123,7 +124,7 @@ static bool array_get_isnull(const bits8 *nullbitmap, int offset);
 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
 static Datum ArrayCast(char *value, bool byval, int len);
 static int	ArrayCastAndSet(Datum src,
-							int typlen, bool typbyval, char typalign,
+							int typlen, bool typbyval, uint8 typalignby,
 							char *dest);
 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 						int typlen, bool typbyval, char typalign);
@@ -187,6 +188,7 @@ array_in(PG_FUNCTION_ARGS)
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	uint8		typalignby;
 	char		typdelim;
 	Oid			typioparam;
 	char	   *p;
@@ -232,6 +234,7 @@ array_in(PG_FUNCTION_ARGS)
 	typlen = my_extra->typlen;
 	typbyval = my_extra->typbyval;
 	typalign = my_extra->typalign;
+	typalignby = typalign_to_alignby(typalign);
 	typdelim = my_extra->typdelim;
 	typioparam = my_extra->typioparam;
 
@@ -328,7 +331,7 @@ array_in(PG_FUNCTION_ARGS)
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 			nbytes = att_addlength_datum(nbytes, typlen, values[i]);
-			nbytes = att_align_nominal(nbytes, typalign);
+			nbytes = att_nominal_alignby(nbytes, typalignby);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(nbytes))
 				ereturn(escontext, (Datum) 0,
@@ -972,6 +975,7 @@ CopyArrayEls(ArrayType *array,
 	bits8	   *bitmap = ARR_NULLBITMAP(array);
 	int			bitval = 0;
 	int			bitmask = 1;
+	uint8		typalignby = typalign_to_alignby(typalign);
 	int			i;
 
 	if (typbyval)
@@ -988,7 +992,7 @@ CopyArrayEls(ArrayType *array,
 		else
 		{
 			bitval |= bitmask;
-			p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
+			p += ArrayCastAndSet(values[i], typlen, typbyval, typalignby, p);
 			if (freedata)
 				pfree(DatumGetPointer(values[i]));
 		}
@@ -1112,7 +1116,7 @@ array_out(PG_FUNCTION_ARGS)
 	needquotes = (bool *) palloc(nitems * sizeof(bool));
 	overall_length = 0;
 
-	array_iter_setup(&iter, v);
+	array_iter_setup(&iter, v, typlen, typbyval, typalign);
 
 	for (i = 0; i < nitems; i++)
 	{
@@ -1121,8 +1125,7 @@ array_out(PG_FUNCTION_ARGS)
 		bool		needquote;
 
 		/* Get source element, checking for NULL */
-		itemvalue = array_iter_next(&iter, &isnull, i,
-									typlen, typbyval, typalign);
+		itemvalue = array_iter_next(&iter, &isnull, i);
 
 		if (isnull)
 		{
@@ -1468,6 +1471,7 @@ ReadArrayBinary(StringInfo buf,
 	int			i;
 	bool		hasnull;
 	int32		totbytes;
+	uint8		typalignby = typalign_to_alignby(typalign);
 
 	for (i = 0; i < nitems; i++)
 	{
@@ -1526,7 +1530,7 @@ ReadArrayBinary(StringInfo buf,
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 			totbytes = att_addlength_datum(totbytes, typlen, values[i]);
-			totbytes = att_align_nominal(totbytes, typalign);
+			totbytes = att_nominal_alignby(totbytes, typalignby);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(totbytes))
 				ereport(ERROR,
@@ -1614,7 +1618,7 @@ array_send(PG_FUNCTION_ARGS)
 	}
 
 	/* Send the array elements using the element's own sendproc */
-	array_iter_setup(&iter, v);
+	array_iter_setup(&iter, v, typlen, typbyval, typalign);
 
 	for (i = 0; i < nitems; i++)
 	{
@@ -1622,8 +1626,7 @@ array_send(PG_FUNCTION_ARGS)
 		bool		isnull;
 
 		/* Get source element, checking for NULL */
-		itemvalue = array_iter_next(&iter, &isnull, i,
-									typlen, typbyval, typalign);
+		itemvalue = array_iter_next(&iter, &isnull, i);
 
 		if (isnull)
 		{
@@ -2231,6 +2234,7 @@ array_set_element(Datum arraydatum,
 				addedafter,
 				lenbefore,
 				lenafter;
+	uint8		elmalignby = typalign_to_alignby(elmalign);
 
 	if (arraytyplen > 0)
 	{
@@ -2258,7 +2262,7 @@ array_set_element(Datum arraydatum,
 		resultarray = (char *) palloc(arraytyplen);
 		memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
 		elt_ptr = resultarray + indx[0] * elmlen;
-		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
+		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby, elt_ptr);
 		return PointerGetDatum(resultarray);
 	}
 
@@ -2416,7 +2420,7 @@ array_set_element(Datum arraydatum,
 		else
 		{
 			olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
-			olditemlen = att_align_nominal(olditemlen, elmalign);
+			olditemlen = att_nominal_alignby(olditemlen, elmalignby);
 		}
 		lenafter = olddatasize - lenbefore - olditemlen;
 	}
@@ -2426,7 +2430,7 @@ array_set_element(Datum arraydatum,
 	else
 	{
 		newitemlen = att_addlength_datum(0, elmlen, dataValue);
-		newitemlen = att_align_nominal(newitemlen, elmalign);
+		newitemlen = att_nominal_alignby(newitemlen, elmalignby);
 	}
 
 	newsize = overheadlen + lenbefore + newitemlen + lenafter;
@@ -2449,7 +2453,7 @@ array_set_element(Datum arraydatum,
 		   (char *) array + oldoverheadlen,
 		   lenbefore);
 	if (!isNull)
-		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
+		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby,
 						(char *) newarray + overheadlen + lenbefore);
 	memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
 		   (char *) array + oldoverheadlen + lenbefore + olditemlen,
@@ -3221,6 +3225,7 @@ array_map(Datum arrayd,
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	uint8		typalignby;
 	array_iter	iter;
 	ArrayMetaState *inp_extra;
 	ArrayMetaState *ret_extra;
@@ -3270,21 +3275,21 @@ array_map(Datum arrayd,
 	typlen = ret_extra->typlen;
 	typbyval = ret_extra->typbyval;
 	typalign = ret_extra->typalign;
+	typalignby = typalign_to_alignby(typalign);
 
 	/* Allocate temporary arrays for new values */
 	values = (Datum *) palloc(nitems * sizeof(Datum));
 	nulls = (bool *) palloc(nitems * sizeof(bool));
 
 	/* Loop over source data */
-	array_iter_setup(&iter, v);
+	array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign);
 	hasnulls = false;
 
 	for (i = 0; i < nitems; i++)
 	{
 		/* Get source element, checking for NULL */
 		*transform_source =
-			array_iter_next(&iter, transform_source_isnull, i,
-							inp_typlen, inp_typbyval, inp_typalign);
+			array_iter_next(&iter, transform_source_isnull, i);
 
 		/* Apply the given expression to source element */
 		values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
@@ -3298,7 +3303,7 @@ array_map(Datum arrayd,
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 			/* Update total result size */
 			nbytes = att_addlength_datum(nbytes, typlen, values[i]);
-			nbytes = att_align_nominal(nbytes, typalign);
+			nbytes = att_nominal_alignby(nbytes, typalignby);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(nbytes))
 				ereport(ERROR,
@@ -3505,6 +3510,7 @@ construct_md_array(Datum *elems,
 	int32		dataoffset;
 	int			i;
 	int			nelems;
+	uint8		elmalignby = typalign_to_alignby(elmalign);
 
 	if (ndims < 0)				/* we do allow zero-dimension arrays */
 		ereport(ERROR,
@@ -3538,7 +3544,7 @@ construct_md_array(Datum *elems,
 		if (elmlen == -1)
 			elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
 		nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
-		nbytes = att_align_nominal(nbytes, elmalign);
+		nbytes = att_nominal_alignby(nbytes, elmalignby);
 		/* check for overflow of total request */
 		if (!AllocSizeIsValid(nbytes))
 			ereport(ERROR,
@@ -3641,6 +3647,7 @@ deconstruct_array(const ArrayType *array,
 	bits8	   *bitmap;
 	int			bitmask;
 	int			i;
+	uint8		elmalignby = typalign_to_alignby(elmalign);
 
 	Assert(ARR_ELEMTYPE(array) == elmtype);
 
@@ -3673,7 +3680,7 @@ deconstruct_array(const ArrayType *array,
 		{
 			elems[i] = fetch_att(p, elmbyval, elmlen);
 			p = att_addlength_pointer(p, elmlen, p);
-			p = (char *) att_align_nominal(p, elmalign);
+			p = (char *) att_nominal_alignby(p, elmalignby);
 		}
 
 		/* advance bitmap pointer if any */
@@ -3878,8 +3885,8 @@ array_eq(PG_FUNCTION_ARGS)
 
 		/* Loop over source data */
 		nitems = ArrayGetNItems(ndims1, dims1);
-		array_iter_setup(&it1, array1);
-		array_iter_setup(&it2, array2);
+		array_iter_setup(&it1, array1, typlen, typbyval, typalign);
+		array_iter_setup(&it2, array2, typlen, typbyval, typalign);
 
 		for (i = 0; i < nitems; i++)
 		{
@@ -3890,10 +3897,8 @@ array_eq(PG_FUNCTION_ARGS)
 			bool		oprresult;
 
 			/* Get elements, checking for NULL */
-			elt1 = array_iter_next(&it1, &isnull1, i,
-								   typlen, typbyval, typalign);
-			elt2 = array_iter_next(&it2, &isnull2, i,
-								   typlen, typbyval, typalign);
+			elt1 = array_iter_next(&it1, &isnull1, i);
+			elt2 = array_iter_next(&it2, &isnull2, i);
 
 			/*
 			 * We consider two NULLs equal; NULL and not-NULL are unequal.
@@ -4042,8 +4047,8 @@ array_cmp(FunctionCallInfo fcinfo)
 
 	/* Loop over source data */
 	min_nitems = Min(nitems1, nitems2);
-	array_iter_setup(&it1, array1);
-	array_iter_setup(&it2, array2);
+	array_iter_setup(&it1, array1, typlen, typbyval, typalign);
+	array_iter_setup(&it2, array2, typlen, typbyval, typalign);
 
 	for (i = 0; i < min_nitems; i++)
 	{
@@ -4054,8 +4059,8 @@ array_cmp(FunctionCallInfo fcinfo)
 		int32		cmpresult;
 
 		/* Get elements, checking for NULL */
-		elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
-		elt2 = array_iter_next(&it2, &isnull2, i, typlen, typbyval, typalign);
+		elt1 = array_iter_next(&it1, &isnull1, i);
+		elt2 = array_iter_next(&it2, &isnull2, i);
 
 		/*
 		 * We consider two NULLs equal; NULL > not-NULL.
@@ -4238,7 +4243,7 @@ hash_array(PG_FUNCTION_ARGS)
 
 	/* Loop over source data */
 	nitems = ArrayGetNItems(ndims, dims);
-	array_iter_setup(&iter, array);
+	array_iter_setup(&iter, array, typlen, typbyval, typalign);
 
 	for (i = 0; i < nitems; i++)
 	{
@@ -4247,7 +4252,7 @@ hash_array(PG_FUNCTION_ARGS)
 		uint32		elthash;
 
 		/* Get element, checking for NULL */
-		elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
+		elt = array_iter_next(&iter, &isnull, i);
 
 		if (isnull)
 		{
@@ -4328,7 +4333,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
 
 	/* Loop over source data */
 	nitems = ArrayGetNItems(ndims, dims);
-	array_iter_setup(&iter, array);
+	array_iter_setup(&iter, array, typlen, typbyval, typalign);
 
 	for (i = 0; i < nitems; i++)
 	{
@@ -4337,7 +4342,7 @@ hash_array_extended(PG_FUNCTION_ARGS)
 		uint64		elthash;
 
 		/* Get element, checking for NULL */
-		elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
+		elt = array_iter_next(&iter, &isnull, i);
 
 		if (isnull)
 		{
@@ -4451,7 +4456,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 
 	/* Loop over source data */
 	nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
-	array_iter_setup(&it1, array1);
+	array_iter_setup(&it1, array1, typlen, typbyval, typalign);
 
 	for (i = 0; i < nelems1; i++)
 	{
@@ -4459,7 +4464,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 		bool		isnull1;
 
 		/* Get element, checking for NULL */
-		elt1 = array_iter_next(&it1, &isnull1, i, typlen, typbyval, typalign);
+		elt1 = array_iter_next(&it1, &isnull1, i);
 
 		/*
 		 * We assume that the comparison operator is strict, so a NULL can't
@@ -4626,6 +4631,7 @@ array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
 							 &iterator->typlen,
 							 &iterator->typbyval,
 							 &iterator->typalign);
+	iterator->typalignby = typalign_to_alignby(iterator->typalign);
 
 	/*
 	 * Remember the slicing parameters.
@@ -4700,7 +4706,7 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
 
 			/* Move our data pointer forward to the next element */
 			p = att_addlength_pointer(p, iterator->typlen, p);
-			p = (char *) att_align_nominal(p, iterator->typalign);
+			p = (char *) att_nominal_alignby(p, iterator->typalignby);
 			iterator->data_ptr = p;
 		}
 	}
@@ -4730,7 +4736,7 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
 
 				/* Move our data pointer forward to the next element */
 				p = att_addlength_pointer(p, iterator->typlen, p);
-				p = (char *) att_align_nominal(p, iterator->typalign);
+				p = (char *) att_nominal_alignby(p, iterator->typalignby);
 			}
 		}
 
@@ -4828,7 +4834,7 @@ static int
 ArrayCastAndSet(Datum src,
 				int typlen,
 				bool typbyval,
-				char typalign,
+				uint8 typalignby,
 				char *dest)
 {
 	int			inc;
@@ -4839,14 +4845,14 @@ ArrayCastAndSet(Datum src,
 			store_att_byval(dest, src, typlen);
 		else
 			memmove(dest, DatumGetPointer(src), typlen);
-		inc = att_align_nominal(typlen, typalign);
+		inc = att_nominal_alignby(typlen, typalignby);
 	}
 	else
 	{
 		Assert(!typbyval);
 		inc = att_addlength_datum(0, typlen, src);
 		memmove(dest, DatumGetPointer(src), inc);
-		inc = att_align_nominal(inc, typalign);
+		inc = att_nominal_alignby(inc, typalignby);
 	}
 
 	return inc;
@@ -4867,12 +4873,13 @@ static char *
 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 		   int typlen, bool typbyval, char typalign)
 {
+	uint8		typalignby = typalign_to_alignby(typalign);
 	int			bitmask;
 	int			i;
 
 	/* easy if fixed-size elements and no NULLs */
 	if (typlen > 0 && !nullbitmap)
-		return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
+		return ptr + nitems * ((Size) att_nominal_alignby(typlen, typalignby));
 
 	/* seems worth having separate loops for NULL and no-NULLs cases */
 	if (nullbitmap)
@@ -4885,7 +4892,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 			if (*nullbitmap & bitmask)
 			{
 				ptr = att_addlength_pointer(ptr, typlen, ptr);
-				ptr = (char *) att_align_nominal(ptr, typalign);
+				ptr = (char *) att_nominal_alignby(ptr, typalignby);
 			}
 			bitmask <<= 1;
 			if (bitmask == 0x100)
@@ -4900,7 +4907,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 		for (i = 0; i < nitems; i++)
 		{
 			ptr = att_addlength_pointer(ptr, typlen, ptr);
-			ptr = (char *) att_align_nominal(ptr, typalign);
+			ptr = (char *) att_nominal_alignby(ptr, typalignby);
 		}
 	}
 	return ptr;
@@ -5050,12 +5057,13 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 				j,
 				inc;
 	int			count = 0;
+	uint8		typalignby = typalign_to_alignby(typalign);
 
 	mda_get_range(ndim, span, st, endp);
 
 	/* Pretty easy for fixed element length without nulls ... */
 	if (typlen > 0 && !arraynullsptr)
-		return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
+		return ArrayGetNItems(ndim, span) * att_nominal_alignby(typlen, typalignby);
 
 	/* Else gotta do it the hard way */
 	src_offset = ArrayGetOffset(ndim, dim, lb, st);
@@ -5077,7 +5085,7 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 		if (!array_get_isnull(arraynullsptr, src_offset))
 		{
 			inc = att_addlength_pointer(0, typlen, ptr);
-			inc = att_align_nominal(inc, typalign);
+			inc = att_nominal_alignby(inc, typalignby);
 			ptr += inc;
 			count += inc;
 		}
@@ -6096,6 +6104,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	uint8		elmalignby;
 	ArrayMetaState *my_extra;
 
 	/*
@@ -6190,6 +6199,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
 	elmlen = my_extra->typlen;
 	elmbyval = my_extra->typbyval;
 	elmalign = my_extra->typalign;
+	elmalignby = typalign_to_alignby(elmalign);
 
 	/* compute required space */
 	if (!isnull)
@@ -6204,7 +6214,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
 			value = PointerGetDatum(PG_DETOAST_DATUM(value));
 
 		nbytes = att_addlength_datum(0, elmlen, value);
-		nbytes = att_align_nominal(nbytes, elmalign);
+		nbytes = att_nominal_alignby(nbytes, elmalignby);
 		Assert(nbytes > 0);
 
 		totbytes = nbytes * nitems;
@@ -6228,7 +6238,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
 
 		p = ARR_DATA_PTR(result);
 		for (i = 0; i < nitems; i++)
-			p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
+			p += ArrayCastAndSet(value, elmlen, elmbyval, elmalignby, p);
 	}
 	else
 	{
@@ -6259,9 +6269,6 @@ array_unnest(PG_FUNCTION_ARGS)
 		array_iter	iter;
 		int			nextelem;
 		int			numelems;
-		int16		elmlen;
-		bool		elmbyval;
-		char		elmalign;
 	} array_unnest_fctx;
 
 	FuncCallContext *funcctx;
@@ -6272,6 +6279,9 @@ array_unnest(PG_FUNCTION_ARGS)
 	if (SRF_IS_FIRSTCALL())
 	{
 		AnyArrayType *arr;
+		int16		elmlen;
+		bool		elmbyval;
+		char		elmalign;
 
 		/* create a function context for cross-call persistence */
 		funcctx = SRF_FIRSTCALL_INIT();
@@ -6293,23 +6303,24 @@ array_unnest(PG_FUNCTION_ARGS)
 		/* allocate memory for user context */
 		fctx = palloc_object(array_unnest_fctx);
 
-		/* initialize state */
-		array_iter_setup(&fctx->iter, arr);
-		fctx->nextelem = 0;
-		fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
-
+		/* get element-type data */
 		if (VARATT_IS_EXPANDED_HEADER(arr))
 		{
 			/* we can just grab the type data from expanded array */
-			fctx->elmlen = arr->xpn.typlen;
-			fctx->elmbyval = arr->xpn.typbyval;
-			fctx->elmalign = arr->xpn.typalign;
+			elmlen = arr->xpn.typlen;
+			elmbyval = arr->xpn.typbyval;
+			elmalign = arr->xpn.typalign;
 		}
 		else
 			get_typlenbyvalalign(AARR_ELEMTYPE(arr),
-								 &fctx->elmlen,
-								 &fctx->elmbyval,
-								 &fctx->elmalign);
+								 &elmlen,
+								 &elmbyval,
+								 &elmalign);
+
+		/* initialize state */
+		array_iter_setup(&fctx->iter, arr, elmlen, elmbyval, elmalign);
+		fctx->nextelem = 0;
+		fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
 
 		funcctx->user_fctx = fctx;
 		MemoryContextSwitchTo(oldcontext);
@@ -6324,8 +6335,7 @@ array_unnest(PG_FUNCTION_ARGS)
 		int			offset = fctx->nextelem++;
 		Datum		elem;
 
-		elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset,
-							   fctx->elmlen, fctx->elmbyval, fctx->elmalign);
+		elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset);
 
 		SRF_RETURN_NEXT(funcctx, elem);
 	}
@@ -6401,6 +6411,7 @@ array_replace_internal(ArrayType *array,
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	uint8		typalignby;
 	char	   *arraydataptr;
 	bits8	   *bitmap;
 	int			bitmask;
@@ -6445,6 +6456,7 @@ array_replace_internal(ArrayType *array,
 	typlen = typentry->typlen;
 	typbyval = typentry->typbyval;
 	typalign = typentry->typalign;
+	typalignby = typalign_to_alignby(typalign);
 
 	/*
 	 * Detoast values if they are toasted.  The replacement value must be
@@ -6506,7 +6518,7 @@ array_replace_internal(ArrayType *array,
 			isNull = false;
 			elt = fetch_att(arraydataptr, typbyval, typlen);
 			arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
-			arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
+			arraydataptr = (char *) att_nominal_alignby(arraydataptr, typalignby);
 
 			if (search_isnull)
 			{
@@ -6553,7 +6565,7 @@ array_replace_internal(ArrayType *array,
 			{
 				/* Update total result size */
 				nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
-				nbytes = att_align_nominal(nbytes, typalign);
+				nbytes = att_nominal_alignby(nbytes, typalignby);
 				/* check for overflow of total request */
 				if (!AllocSizeIsValid(nbytes))
 					ereport(ERROR,
@@ -6860,6 +6872,7 @@ width_bucket_array_variable(Datum operand,
 	int			typlen = typentry->typlen;
 	bool		typbyval = typentry->typbyval;
 	char		typalign = typentry->typalign;
+	uint8		typalignby = typalign_to_alignby(typalign);
 	int			left;
 	int			right;
 
@@ -6883,7 +6896,7 @@ width_bucket_array_variable(Datum operand,
 		for (i = left; i < mid; i++)
 		{
 			ptr = att_addlength_pointer(ptr, typlen, ptr);
-			ptr = (char *) att_align_nominal(ptr, typalign);
+			ptr = (char *) att_nominal_alignby(ptr, typalignby);
 		}
 
 		locfcinfo->args[0].value = operand;
@@ -6908,7 +6921,7 @@ width_bucket_array_variable(Datum operand,
 			 * ensures we do only O(N) array indexing work, not O(N^2).
 			 */
 			ptr = att_addlength_pointer(ptr, typlen, ptr);
-			thresholds_data = (char *) att_align_nominal(ptr, typalign);
+			thresholds_data = (char *) att_nominal_alignby(ptr, typalignby);
 		}
 	}
 
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 07e2a81d46a..b1942387dc5 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -572,21 +572,22 @@ multirange_size_estimate(TypeCacheEntry *rangetyp, int32 range_count,
 						 RangeType **ranges)
 {
 	char		elemalign = rangetyp->rngelemtype->typalign;
+	uint8		elemalignby = typalign_to_alignby(elemalign);
 	Size		size;
 	int32		i;
 
 	/*
 	 * Count space for MultirangeType struct, items and flags.
 	 */
-	size = att_align_nominal(sizeof(MultirangeType) +
-							 Max(range_count - 1, 0) * sizeof(uint32) +
-							 range_count * sizeof(uint8), elemalign);
+	size = att_nominal_alignby(sizeof(MultirangeType) +
+							   Max(range_count - 1, 0) * sizeof(uint32) +
+							   range_count * sizeof(uint8), elemalignby);
 
 	/* Count space for range bounds */
 	for (i = 0; i < range_count; i++)
-		size += att_align_nominal(VARSIZE(ranges[i]) -
-								  sizeof(RangeType) -
-								  sizeof(char), elemalign);
+		size += att_nominal_alignby(VARSIZE(ranges[i]) -
+									sizeof(RangeType) -
+									sizeof(char), elemalignby);
 
 	return size;
 }
@@ -605,6 +606,7 @@ write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp,
 	const char *begin;
 	char	   *ptr;
 	char		elemalign = rangetyp->rngelemtype->typalign;
+	uint8		elemalignby = typalign_to_alignby(elemalign);
 
 	items = MultirangeGetItemsPtr(multirange);
 	flags = MultirangeGetFlagsPtr(multirange);
@@ -630,7 +632,7 @@ write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp,
 		flags[i] = *((char *) ranges[i] + VARSIZE(ranges[i]) - sizeof(char));
 		len = VARSIZE(ranges[i]) - sizeof(RangeType) - sizeof(char);
 		memcpy(ptr, ranges[i] + 1, len);
-		ptr += att_align_nominal(len, elemalign);
+		ptr += att_nominal_alignby(len, elemalignby);
 	}
 }
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 6c1ebb0866d..552ac0c61d3 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -3898,6 +3898,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	uint8		typalignby;
 	StringInfoData buf;
 	bool		printed = false;
 	char	   *p;
@@ -3947,6 +3948,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
 	typlen = my_extra->typlen;
 	typbyval = my_extra->typbyval;
 	typalign = my_extra->typalign;
+	typalignby = typalign_to_alignby(typalign);
 
 	p = ARR_DATA_PTR(v);
 	bitmap = ARR_NULLBITMAP(v);
@@ -3983,7 +3985,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
 			printed = true;
 
 			p = att_addlength_pointer(p, typlen, p);
-			p = (char *) att_align_nominal(p, typalign);
+			p = (char *) att_nominal_alignby(p, typalignby);
 		}
 
 		/* advance bitmap pointer if any */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 3e5530658c9..d64c18b950b 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -71,6 +71,43 @@ fetch_att(const void *T, bool attbyval, int attlen)
 }
 #endif							/* FRONTEND */
 
+/*
+ * typalign_to_alignby: map a TYPALIGN_xxx value to the numeric alignment
+ * value it represents.  (We store TYPALIGN_xxx codes not the real alignment
+ * values mainly so that initial catalog contents can be machine-independent.)
+ */
+static inline uint8
+typalign_to_alignby(char typalign)
+{
+	uint8		alignby;
+
+	switch (typalign)
+	{
+		case TYPALIGN_CHAR:
+			alignby = sizeof(char);
+			break;
+		case TYPALIGN_SHORT:
+			alignby = ALIGNOF_SHORT;
+			break;
+		case TYPALIGN_INT:
+			alignby = ALIGNOF_INT;
+			break;
+		case TYPALIGN_DOUBLE:
+			alignby = ALIGNOF_DOUBLE;
+			break;
+		default:
+#ifndef FRONTEND
+			elog(ERROR, "invalid typalign value: %c", typalign);
+#else
+			fprintf(stderr, "invalid typalign value: %c\n", typalign);
+			exit(1);
+#endif
+			alignby = 0;
+			break;
+	}
+	return alignby;
+}
+
 /*
  * att_align_datum aligns the given offset as needed for a datum of alignment
  * requirement attalign and typlen attlen.  attdatum is the Datum variable
@@ -139,19 +176,11 @@ fetch_att(const void *T, bool attbyval, int attlen)
  *	* within arrays and multiranges, we unconditionally align varlenas (XXX this
  *	  should be revisited, probably).
  *
- * The attalign cases are tested in what is hopefully something like their
- * frequency of occurrence.
+ * In performance-critical loops, avoid using this macro; instead use
+ * att_nominal_alignby with a pre-computed alignby value.
  */
 #define att_align_nominal(cur_offset, attalign) \
-( \
-	((attalign) == TYPALIGN_INT) ? INTALIGN(cur_offset) : \
-	 (((attalign) == TYPALIGN_CHAR) ? (uintptr_t) (cur_offset) : \
-	  (((attalign) == TYPALIGN_DOUBLE) ? DOUBLEALIGN(cur_offset) : \
-	   ( \
-			AssertMacro((attalign) == TYPALIGN_SHORT), \
-			SHORTALIGN(cur_offset) \
-	   ))) \
-)
+	att_nominal_alignby(cur_offset, typalign_to_alignby(attalign))
 
 /*
  * Similar to att_align_nominal, but accepts a number of bytes, typically from
diff --git a/src/include/utils/arrayaccess.h b/src/include/utils/arrayaccess.h
index abb8659de02..a325ae52574 100644
--- a/src/include/utils/arrayaccess.h
+++ b/src/include/utils/arrayaccess.h
@@ -22,8 +22,8 @@
  * Functions for iterating through elements of a flat or expanded array.
  * These require a state struct "array_iter iter".
  *
- * Use "array_iter_setup(&iter, arrayptr);" to prepare to iterate, and
- * "datumvar = array_iter_next(&iter, &isnullvar, index, ...);" to fetch
+ * Use "array_iter_setup(&iter, arrayptr, ...);" to prepare to iterate,
+ * and "datumvar = array_iter_next(&iter, &isnullvar, index);" to fetch
  * the next element into datumvar/isnullvar.
  * "index" must be the zero-origin element number; we make caller provide
  * this since caller is generally counting the elements anyway.  Despite
@@ -42,11 +42,17 @@ typedef struct array_iter
 	char	   *dataptr;		/* Current spot in the data area */
 	bits8	   *bitmapptr;		/* Current byte of the nulls bitmap, or NULL */
 	int			bitmask;		/* mask for current bit in nulls bitmap */
+
+	/* Fields used in both cases: data about array's element type */
+	int			elmlen;
+	bool		elmbyval;
+	uint8		elmalignby;
 } array_iter;
 
 
 static inline void
-array_iter_setup(array_iter *it, AnyArrayType *a)
+array_iter_setup(array_iter *it, AnyArrayType *a,
+				 int elmlen, bool elmbyval, char elmalign)
 {
 	if (VARATT_IS_EXPANDED_HEADER(a))
 	{
@@ -75,11 +81,13 @@ array_iter_setup(array_iter *it, AnyArrayType *a)
 		it->bitmapptr = ARR_NULLBITMAP((ArrayType *) a);
 	}
 	it->bitmask = 1;
+	it->elmlen = elmlen;
+	it->elmbyval = elmbyval;
+	it->elmalignby = typalign_to_alignby(elmalign);
 }
 
 static inline Datum
-array_iter_next(array_iter *it, bool *isnull, int i,
-				int elmlen, bool elmbyval, char elmalign)
+array_iter_next(array_iter *it, bool *isnull, int i)
 {
 	Datum		ret;
 
@@ -98,10 +106,11 @@ array_iter_next(array_iter *it, bool *isnull, int i,
 		else
 		{
 			*isnull = false;
-			ret = fetch_att(it->dataptr, elmbyval, elmlen);
-			it->dataptr = att_addlength_pointer(it->dataptr, elmlen,
+			ret = fetch_att(it->dataptr, it->elmbyval, it->elmlen);
+			it->dataptr = att_addlength_pointer(it->dataptr, it->elmlen,
 												it->dataptr);
-			it->dataptr = (char *) att_align_nominal(it->dataptr, elmalign);
+			it->dataptr = (char *) att_nominal_alignby(it->dataptr,
+													   it->elmalignby);
 		}
 		it->bitmask <<= 1;
 		if (it->bitmask == 0x100)
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 1f69109b081..44055de6aeb 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -735,6 +735,7 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
 		char	   *dataptr = *dataptr_p;
 		bits8	   *bitmap = *bitmap_p;
 		int			bitmask = *bitmask_p;
+		uint8		typalignby = typalign_to_alignby(elm->typalign);
 
 		for (i = 0; i < dims[dim]; i++)
 		{
@@ -751,7 +752,7 @@ PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
 				itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen);
 				PyList_SetItem(list, i, elm->func(elm, itemvalue));
 				dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr);
-				dataptr = (char *) att_align_nominal(dataptr, elm->typalign);
+				dataptr = (char *) att_nominal_alignby(dataptr, typalignby);
 			}
 
 			/* advance bitmap pointer if any */
-- 
2.43.7

From 9a98723f4ba74543e304c5b76213c05cde809df6 Mon Sep 17 00:00:00 2001
From: Tom Lane <[email protected]>
Date: Thu, 29 Jan 2026 20:11:28 -0500
Subject: [PATCH v2 2/2] Decouple our alignment assumptions about int64 and
 double.

Up to now we have assumed that int64 and double have the same
alignment requirement, but there are platforms on which that's
not true (notably AIX).  This assumption is a bit of a wart anyway,
since it leads to confusion about which alignment setting to use.
Let's break that apart.

We actually need to split TYPALIGN_DOUBLE into three values not
two, because we need a representation for "the maximum alignment
on this platform"; composite types need to use that setting.
So this patch invents TYPALIGN_INT64 and TYPALIGN_MAX, and
propagates those into all the necessary places.  It's pretty
straightforward really, though it's certainly possible I missed
a few places to change.

One of the concerns that prevented this from being done long
ago was not wanting to add overhead to tuple forming/deforming.
However that concern seems gone now, because we map TYPALIGN_xxx
values to numeric alignments in typalign_to_alignby() which
we can usually avoid using within performance-critical loops.

An issue not resolved here is that we are not consistent
about the pg_type typlen/typalign values for polymorphic
types (the "any*" types).  Those shouldn't actually matter,
but why aren't they all alike?

Side notes:
I got rid of LONGALIGN[_DOWN] along with the configure probes
for ALIGNOF_LONG.  We were not using those anywhere and it
seems highly unlikely that we'd do so in future.
bootstrap.c turns out to have had the wrong alignment value
for _aclitem; seems that was missed when we widened AclMode
to 64 bits.  AFAICT the value wasn't used so there were no
ill effects.
I fixed a couple of places that were using hard-coded values
when they could have used TYPALIGN_xxx macros.

This will require a catversion bump.

Author: Tom Lane <[email protected]>
Reviewed-by: Andres Freund <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 configure                                     | 64 +++----------------
 configure.ac                                  | 30 +++------
 doc/src/sgml/catalogs.sgml                    |  8 ++-
 doc/src/sgml/ref/create_type.sgml             |  9 ++-
 meson.build                                   | 31 ++++-----
 src/backend/access/common/tupdesc.c           |  2 +-
 src/backend/bootstrap/bootstrap.c             |  4 +-
 src/backend/catalog/Catalog.pm                | 10 ++-
 src/backend/catalog/heap.c                    |  4 +-
 src/backend/catalog/pg_type.c                 |  9 ++-
 src/backend/commands/typecmds.c               | 38 +++++++++--
 src/backend/tsearch/ts_typanalyze.c           |  2 +-
 src/backend/utils/adt/arrayfuncs.c            |  2 +-
 src/backend/utils/adt/orderedsetaggs.c        |  2 +-
 src/backend/utils/adt/rangetypes_typanalyze.c |  2 +-
 src/bin/initdb/initdb.c                       |  2 +-
 src/bin/pg_dump/pg_dump.c                     |  4 ++
 src/include/access/tupmacs.h                  |  6 ++
 src/include/c.h                               |  4 +-
 src/include/catalog/pg_type.dat               | 62 +++++++++---------
 src/include/catalog/pg_type.h                 |  2 +
 src/include/pg_config.h.in                    |  3 -
 src/pl/plpython/plpy_typeio.c                 |  4 +-
 src/test/regress/expected/create_type.out     |  1 +
 src/test/regress/expected/float8.out          |  8 ++-
 src/test/regress/expected/type_sanity.out     |  8 ++-
 src/test/regress/sql/create_type.sql          |  1 +
 src/test/regress/sql/float8.sql               |  6 +-
 src/test/regress/sql/type_sanity.sql          |  8 ++-
 29 files changed, 172 insertions(+), 164 deletions(-)

diff --git a/configure b/configure
index a10a2c85c6a..5adfb3eba45 100755
--- a/configure
+++ b/configure
@@ -17095,41 +17095,6 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
-# The cast to long int works around a bug in the HP C Compiler,
-# see AC_CHECK_SIZEOF for more information.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of long" >&5
-$as_echo_n "checking alignment of long... " >&6; }
-if ${ac_cv_alignof_long+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof_long"        "$ac_includes_default
-#ifndef offsetof
-# define offsetof(type, member) ((char *) &((type *) 0)->member - (char *) 0)
-#endif
-typedef struct { char x; long y; } ac__type_alignof_;"; then :
-
-else
-  if test "$ac_cv_type_long" = yes; then
-     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error 77 "cannot compute alignment of long
-See \`config.log' for more details" "$LINENO" 5; }
-   else
-     ac_cv_alignof_long=0
-   fi
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof_long" >&5
-$as_echo "$ac_cv_alignof_long" >&6; }
-
-
-
-cat >>confdefs.h <<_ACEOF
-#define ALIGNOF_LONG $ac_cv_alignof_long
-_ACEOF
-
-
 # The cast to long int works around a bug in the HP C Compiler,
 # see AC_CHECK_SIZEOF for more information.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of int64_t" >&5
@@ -17203,27 +17168,16 @@ _ACEOF
 
 # Compute maximum alignment of any basic type.
 #
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
+# We assume without checking that the maximum alignment requirement is that
+# of int64_t and/or double.  (On most platforms those are the same, but not
+# everywhere.)  Note that we intentionally do not consider any types wider
+# than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much
+# of a penalty for disk and memory space.
 
-MAX_ALIGNOF=$ac_cv_alignof_double
-
-if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'long' is greater than the alignment of 'double'" "$LINENO" 5
-fi
-if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'int64_t' is greater than the alignment of 'double'" "$LINENO" 5
+if test $ac_cv_alignof_int64_t -gt $ac_cv_alignof_double ; then
+  MAX_ALIGNOF=$ac_cv_alignof_int64_t
+else
+  MAX_ALIGNOF=$ac_cv_alignof_double
 fi
 
 cat >>confdefs.h <<_ACEOF
diff --git a/configure.ac b/configure.ac
index 814e64a967e..a5eab2e5fff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2031,33 +2031,21 @@ AC_CHECK_SIZEOF([intmax_t])
 
 AC_CHECK_ALIGNOF(short)
 AC_CHECK_ALIGNOF(int)
-AC_CHECK_ALIGNOF(long)
 AC_CHECK_ALIGNOF(int64_t)
 AC_CHECK_ALIGNOF(double)
 
 # Compute maximum alignment of any basic type.
 #
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
-
-MAX_ALIGNOF=$ac_cv_alignof_double
+# We assume without checking that the maximum alignment requirement is that
+# of int64_t and/or double.  (On most platforms those are the same, but not
+# everywhere.)  Note that we intentionally do not consider any types wider
+# than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much
+# of a penalty for disk and memory space.
 
-if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
-  AC_MSG_ERROR([alignment of 'long' is greater than the alignment of 'double'])
-fi
-if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
-  AC_MSG_ERROR([alignment of 'int64_t' is greater than the alignment of 'double'])
+if test $ac_cv_alignof_int64_t -gt $ac_cv_alignof_double ; then
+  MAX_ALIGNOF=$ac_cv_alignof_int64_t
+else
+  MAX_ALIGNOF=$ac_cv_alignof_double
 fi
 AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignment requirement of any C data type.])
 
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 332193565e2..12608d00a18 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -9567,7 +9567,13 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
          <para><literal>i</literal> = <type>int</type> alignment (4 bytes on most machines).</para>
         </listitem>
         <listitem>
-         <para><literal>d</literal> = <type>double</type> alignment (8 bytes on many machines, but by no means all).</para>
+         <para><literal>l</literal> = <type>int64</type> alignment (8 bytes on most machines, but by no means all).</para>
+        </listitem>
+        <listitem>
+         <para><literal>d</literal> = <type>double</type> alignment (8 bytes on most machines, but by no means all).</para>
+        </listitem>
+        <listitem>
+         <para><literal>m</literal> = maximum alignment (8 bytes on most machines).</para>
         </listitem>
        </itemizedlist>
       </para></entry>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 994dfc65268..006db624094 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -748,8 +748,15 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
      <para>
       The storage alignment requirement of the data type.  If specified,
       it must be <literal>char</literal>, <literal>int2</literal>,
-      <literal>int4</literal>, or <literal>double</literal>; the
+      <literal>int4</literal>, <literal>int8</literal>,
+      <literal>double</literal>, or <literal>max</literal>; the
       default is <literal>int4</literal>.
+      <literal>int8</literal> and <literal>double</literal> usually
+      have the same behavior, but there are platforms on which they are
+      different because integer and floating-point values have different
+      requirements.  <literal>max</literal> means the maximum alignment
+      requirement for any of these types; it is often used for container
+      types such as records.
      </para>
     </listitem>
    </varlistentry>
diff --git a/meson.build b/meson.build
index df907b62da3..bb17859fee4 100644
--- a/meson.build
+++ b/meson.build
@@ -1798,32 +1798,27 @@ endif
 
 # Determine memory alignment requirements for the basic C data types.
 
-alignof_types = ['short', 'int', 'long', 'double']
+alignof_types = ['short', 'int', 'int64_t', 'double']
 foreach t : alignof_types
-  align = cc.alignment(t, args: test_c_args)
+  align = cc.alignment(t, args: test_c_args, prefix: '#include <stdint.h>')
   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
 endforeach
 
 # Compute maximum alignment of any basic type.
 #
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that int64_t's alignment is at least as strong
-# as long, char, short, or int.  Note that we intentionally do not consider
-# any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
-# would be too much of a penalty for disk and memory space.
+# We assume without checking that the maximum alignment requirement is that
+# of int64_t and/or double.  (On most platforms those are the same, but not
+# everywhere.)  Note that we intentionally do not consider any types wider
+# than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much
+# of a penalty for disk and memory space.
+
+alignof_int64_t = cdata.get('ALIGNOF_INT64_T')
 alignof_double = cdata.get('ALIGNOF_DOUBLE')
-if cc.alignment('int64_t', args: test_c_args, prefix: '#include <stdint.h>') > alignof_double
-  error('alignment of int64_t is greater than the alignment of double')
+if alignof_int64_t > alignof_double
+  cdata.set('MAXIMUM_ALIGNOF', alignof_int64_t)
+else
+  cdata.set('MAXIMUM_ALIGNOF', alignof_double)
 endif
-cdata.set('MAXIMUM_ALIGNOF', alignof_double)
 
 cdata.set('SIZEOF_LONG', cc.sizeof('long', args: test_c_args))
 cdata.set('SIZEOF_LONG_LONG', cc.sizeof('long long', args: test_c_args))
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index b69d10f0a45..47b9f8b5459 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -977,7 +977,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
 		case INT8OID:
 			att->attlen = 8;
 			att->attbyval = true;
-			att->attalign = TYPALIGN_DOUBLE;
+			att->attalign = TYPALIGN_INT64;
 			att->attstorage = TYPSTORAGE_PLAIN;
 			att->attcompression = InvalidCompressionMethod;
 			att->attcollation = InvalidOid;
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index dd57624b4f9..7a3a13a6ec7 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -115,7 +115,7 @@ static const struct typinfo TypInfo[] = {
 	F_TEXTIN, F_TEXTOUT},
 	{"oid", OIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
 	F_OIDIN, F_OIDOUT},
-	{"oid8", OID8OID, 0, 8, true, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid,
+	{"oid8", OID8OID, 0, 8, true, TYPALIGN_INT64, TYPSTORAGE_PLAIN, InvalidOid,
 	F_OID8IN, F_OID8OUT},
 	{"tid", TIDOID, 0, 6, false, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
 	F_TIDIN, F_TIDOUT},
@@ -137,7 +137,7 @@ static const struct typinfo TypInfo[] = {
 	F_ARRAY_IN, F_ARRAY_OUT},
 	{"_char", 1002, CHAROID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
 	F_ARRAY_IN, F_ARRAY_OUT},
-	{"_aclitem", 1034, ACLITEMOID, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, InvalidOid,
+	{"_aclitem", 1034, ACLITEMOID, -1, false, TYPALIGN_INT64, TYPSTORAGE_EXTENDED, InvalidOid,
 	F_ARRAY_IN, F_ARRAY_OUT}
 };
 
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index 219af5884d9..20884f45bd8 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -464,9 +464,13 @@ sub GenerateArrayTypes
 		$array_type{typname} = '_' . $elem_type->{typname};
 		$array_type{typelem} = $elem_type->{typname};
 
-		# Arrays require INT alignment, unless the element type requires
-		# DOUBLE alignment.
-		$array_type{typalign} = $elem_type->{typalign} eq 'd' ? 'd' : 'i';
+		# Arrays require INT alignment, unless the element type requires more.
+		$array_type{typalign} =
+		  $elem_type->{typalign} eq 'l' ? 'l'
+		  : (
+			$elem_type->{typalign} eq 'd' ? 'd'
+			: ( $elem_type->{typalign} eq 'm' ? 'm'
+				: 'i'));
 
 		# Fill in the rest of the array entry's fields.
 		foreach my $column (@$pgtype_schema)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 606434823cf..27cc09a7680 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1075,7 +1075,7 @@ AddNewRelationType(const char *typeName,
 				   NULL,		/* default value - none */
 				   NULL,		/* default binary representation */
 				   false,		/* passed by reference */
-				   TYPALIGN_DOUBLE, /* alignment - must be the largest! */
+				   TYPALIGN_MAX,	/* alignment - must be the largest! */
 				   TYPSTORAGE_EXTENDED, /* fully TOASTable */
 				   -1,			/* typmod */
 				   0,			/* array dimensions for typBaseType */
@@ -1399,7 +1399,7 @@ heap_create_with_catalog(const char *relname,
 				   NULL,		/* default value - none */
 				   NULL,		/* default binary representation */
 				   false,		/* passed by reference */
-				   TYPALIGN_DOUBLE, /* alignment - must be the largest! */
+				   TYPALIGN_MAX,	/* alignment - must be the largest! */
 				   TYPSTORAGE_EXTENDED, /* fully TOASTable */
 				   -1,			/* typmod */
 				   0,			/* array dimensions for typBaseType */
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index fc369c35aa6..f079fa32b40 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -287,7 +287,9 @@ TypeCreate(Oid newTypeOid,
 		}
 		else if (internalSize == (int16) sizeof(int64))
 		{
-			if (alignment != TYPALIGN_DOUBLE)
+			/* We have to trust the user to get this distinction right ... */
+			if (!(alignment == TYPALIGN_INT64 ||
+				  alignment == TYPALIGN_DOUBLE))
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 						 errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
@@ -303,7 +305,10 @@ TypeCreate(Oid newTypeOid,
 	{
 		/* varlena types must have int align or better */
 		if (internalSize == -1 &&
-			!(alignment == TYPALIGN_INT || alignment == TYPALIGN_DOUBLE))
+			!(alignment == TYPALIGN_INT ||
+			  alignment == TYPALIGN_INT64 ||
+			  alignment == TYPALIGN_DOUBLE ||
+			  alignment == TYPALIGN_MAX))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("alignment \"%c\" is invalid for variable-length type",
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 288edb25f2f..2f919f764aa 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -117,6 +117,7 @@ static void makeMultirangeConstructors(const char *name, Oid namespace,
 									   Oid multirangeOid, Oid rangeOid,
 									   Oid rangeArrayOid,
 									   Oid *mltrngConstruct0_p, Oid *mltrngConstruct1_p, Oid *mltrngConstruct2_p);
+static char containerAlignment(char elemalign);
 static Oid	findTypeInputFunction(List *procname, Oid typeOid);
 static Oid	findTypeOutputFunction(List *procname, Oid typeOid);
 static Oid	findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -424,6 +425,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 			pg_strcasecmp(a, "float8") == 0 ||
 			pg_strcasecmp(a, "pg_catalog.float8") == 0)
 			alignment = TYPALIGN_DOUBLE;
+		else if (pg_strcasecmp(a, "int8") == 0 ||
+				 pg_strcasecmp(a, "pg_catalog.int8") == 0)
+			alignment = TYPALIGN_INT64;
 		else if (pg_strcasecmp(a, "int4") == 0 ||
 				 pg_strcasecmp(a, "pg_catalog.int4") == 0)
 			alignment = TYPALIGN_INT;
@@ -433,6 +437,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 		else if (pg_strcasecmp(a, "char") == 0 ||
 				 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
 			alignment = TYPALIGN_CHAR;
+		else if (pg_strcasecmp(a, "max") == 0 ||
+				 pg_strcasecmp(a, "pg_catalog.max") == 0)
+			alignment = TYPALIGN_MAX;
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -611,8 +618,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 	 */
 	array_type = makeArrayTypeName(typeName, typeNamespace);
 
-	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for arrays */
-	alignment = (alignment == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
+	/* alignment must be TYPALIGN_INT or more for arrays */
+	alignment = containerAlignment(alignment);
 
 	TypeCreate(array_oid,		/* force assignment of this type OID */
 			   array_type,		/* type name */
@@ -1095,8 +1102,8 @@ DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
 	 */
 	domainArrayName = makeArrayTypeName(domainName, domainNamespace);
 
-	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for arrays */
-	alignment = (alignment == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
+	/* alignment must be TYPALIGN_INT or more for arrays */
+	alignment = containerAlignment(alignment);
 
 	TypeCreate(domainArrayOid,	/* force assignment of this type OID */
 			   domainArrayName, /* type name */
@@ -1557,8 +1564,8 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
 	get_typlenbyvalalign(rangeSubtype,
 						 &subtyplen, &subtypbyval, &subtypalign);
 
-	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
-	alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
+	/* alignment must be TYPALIGN_INT or more for ranges */
+	alignment = containerAlignment(subtypalign);
 
 	/* Allocate OID for array type, its multirange, and its multirange array */
 	rangeArrayOid = AssignTypeArrayOid();
@@ -2005,6 +2012,25 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 	pfree(parameterModes);
 }
 
+/*
+ * Compute the appropriate typalign for a container (array or range)
+ * given the typalign of its members.  The container type requires at
+ * least int alignment, but more if the members need more.
+ */
+static char
+containerAlignment(char elemalign)
+{
+	switch (elemalign)
+	{
+		case TYPALIGN_INT64:
+		case TYPALIGN_DOUBLE:
+		case TYPALIGN_MAX:
+			return elemalign;
+		default:
+			return TYPALIGN_INT;
+	}
+}
+
 /*
  * Find suitable I/O and other support functions for a type.
  *
diff --git a/src/backend/tsearch/ts_typanalyze.c b/src/backend/tsearch/ts_typanalyze.c
index 0c513d694e7..48ee050e37f 100644
--- a/src/backend/tsearch/ts_typanalyze.c
+++ b/src/backend/tsearch/ts_typanalyze.c
@@ -444,7 +444,7 @@ compute_tsvector_stats(VacAttrStats *stats,
 			stats->statypid[0] = TEXTOID;
 			stats->statyplen[0] = -1;	/* typlen, -1 for varlena */
 			stats->statypbyval[0] = false;
-			stats->statypalign[0] = 'i';
+			stats->statypalign[0] = TYPALIGN_INT;
 		}
 	}
 	else
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index da68915ee20..08e26335a95 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3431,7 +3431,7 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 		case INT8OID:
 			elmlen = sizeof(int64);
 			elmbyval = true;
-			elmalign = TYPALIGN_DOUBLE;
+			elmalign = TYPALIGN_INT64;
 			break;
 
 		case NAMEOID:
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index 3b6da8e36ac..27b8cfa7064 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -1021,7 +1021,7 @@ percentile_cont_interval_multi_final(PG_FUNCTION_ARGS)
 	return percentile_cont_multi_final_common(fcinfo,
 											  INTERVALOID,
 	/* hard-wired info on type interval */
-											  16, false, TYPALIGN_DOUBLE,
+											  16, false, TYPALIGN_INT64,
 											  interval_lerp);
 }
 
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index 38d12dedbc5..278d4e6941a 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -398,7 +398,7 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 		stats->statypid[slot_idx] = FLOAT8OID;
 		stats->statyplen[slot_idx] = sizeof(float8);
 		stats->statypbyval[slot_idx] = true;
-		stats->statypalign[slot_idx] = 'd';
+		stats->statypalign[slot_idx] = TYPALIGN_DOUBLE;
 
 		/* Store the fraction of empty ranges */
 		emptyfrac = palloc_object(float4);
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index a3980e5535f..e550e9cf0a3 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1580,7 +1580,7 @@ bootstrap_template1(void)
 	bki_lines = replace_token(bki_lines, "SIZEOF_POINTER", buf);
 
 	bki_lines = replace_token(bki_lines, "ALIGNOF_POINTER",
-							  (sizeof(Pointer) == 4) ? "i" : "d");
+							  (sizeof(Pointer) == 4) ? "i" : "l");
 
 	bki_lines = replace_token(bki_lines, "POSTGRES",
 							  escape_quotes_bki(username));
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2bebefd0ba2..1b44c03170f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12652,8 +12652,12 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
 		appendPQExpBufferStr(q, ",\n    ALIGNMENT = int2");
 	else if (*typalign == TYPALIGN_INT)
 		appendPQExpBufferStr(q, ",\n    ALIGNMENT = int4");
+	else if (*typalign == TYPALIGN_INT64)
+		appendPQExpBufferStr(q, ",\n    ALIGNMENT = int8");
 	else if (*typalign == TYPALIGN_DOUBLE)
 		appendPQExpBufferStr(q, ",\n    ALIGNMENT = double");
+	else if (*typalign == TYPALIGN_MAX)
+		appendPQExpBufferStr(q, ",\n    ALIGNMENT = max");
 
 	if (*typstorage == TYPSTORAGE_PLAIN)
 		appendPQExpBufferStr(q, ",\n    STORAGE = plain");
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index d64c18b950b..e7db125f627 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -92,9 +92,15 @@ typalign_to_alignby(char typalign)
 		case TYPALIGN_INT:
 			alignby = ALIGNOF_INT;
 			break;
+		case TYPALIGN_INT64:
+			alignby = ALIGNOF_INT64_T;
+			break;
 		case TYPALIGN_DOUBLE:
 			alignby = ALIGNOF_DOUBLE;
 			break;
+		case TYPALIGN_MAX:
+			alignby = MAXIMUM_ALIGNOF;
+			break;
 		default:
 #ifndef FRONTEND
 			elog(ERROR, "invalid typalign value: %c", typalign);
diff --git a/src/include/c.h b/src/include/c.h
index c443e75b89f..c7956ce91bc 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -821,7 +821,7 @@ typedef NameData *Name;
 
 #define SHORTALIGN(LEN)			TYPEALIGN(ALIGNOF_SHORT, (LEN))
 #define INTALIGN(LEN)			TYPEALIGN(ALIGNOF_INT, (LEN))
-#define LONGALIGN(LEN)			TYPEALIGN(ALIGNOF_LONG, (LEN))
+#define INT64ALIGN(LEN)			TYPEALIGN(ALIGNOF_INT64_T, (LEN))
 #define DOUBLEALIGN(LEN)		TYPEALIGN(ALIGNOF_DOUBLE, (LEN))
 #define MAXALIGN(LEN)			TYPEALIGN(MAXIMUM_ALIGNOF, (LEN))
 /* MAXALIGN covers only built-in types, not buffers */
@@ -833,7 +833,7 @@ typedef NameData *Name;
 
 #define SHORTALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_SHORT, (LEN))
 #define INTALIGN_DOWN(LEN)		TYPEALIGN_DOWN(ALIGNOF_INT, (LEN))
-#define LONGALIGN_DOWN(LEN)		TYPEALIGN_DOWN(ALIGNOF_LONG, (LEN))
+#define INT64ALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_INT64_T, (LEN))
 #define DOUBLEALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_DOUBLE, (LEN))
 #define MAXALIGN_DOWN(LEN)		TYPEALIGN_DOWN(MAXIMUM_ALIGNOF, (LEN))
 #define BUFFERALIGN_DOWN(LEN)	TYPEALIGN_DOWN(ALIGNOF_BUFFER, (LEN))
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a1a753d1797..d511493f761 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -56,7 +56,7 @@
   descr => '~18 digit integer, 8-byte storage',
   typname => 'int8', typlen => '8', typbyval => 't',
   typcategory => 'N', typinput => 'int8in', typoutput => 'int8out',
-  typreceive => 'int8recv', typsend => 'int8send', typalign => 'd' },
+  typreceive => 'int8recv', typsend => 'int8send', typalign => 'l' },
 { oid => '21', array_type_oid => '1005',
   descr => '-32 thousand to 32 thousand, 2-byte storage',
   typname => 'int2', typlen => '2', typbyval => 't', typcategory => 'N',
@@ -116,22 +116,22 @@
   typname => 'pg_type', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_type', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 { oid => '75', array_type_oid => '270',
   typname => 'pg_attribute', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_attribute', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 { oid => '81', array_type_oid => '272',
   typname => 'pg_proc', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_proc', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 { oid => '83', array_type_oid => '273',
   typname => 'pg_class', typlen => '-1', typbyval => 'f', typtype => 'c',
   typcategory => 'C', typrelid => 'pg_class', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 
 # OIDS 100 - 199
 
@@ -174,7 +174,7 @@
 { oid => '5069', array_type_oid => '271', descr => 'full transaction id',
   typname => 'xid8', typlen => '8', typbyval => 't',
   typcategory => 'U', typinput => 'xid8in', typoutput => 'xid8out',
-  typreceive => 'xid8recv', typsend => 'xid8send', typalign => 'd' },
+  typreceive => 'xid8recv', typsend => 'xid8send', typalign => 'l' },
 
 # OIDS 600 - 699
 
@@ -239,7 +239,7 @@
   descr => 'monetary amounts, $d,ddd.cc',
   typname => 'money', typlen => '8', typbyval => 't',
   typcategory => 'N', typinput => 'cash_in', typoutput => 'cash_out',
-  typreceive => 'cash_recv', typsend => 'cash_send', typalign => 'd' },
+  typreceive => 'cash_recv', typsend => 'cash_send', typalign => 'l' },
 
 # OIDS 800 - 899
 
@@ -270,7 +270,7 @@
 { oid => '1033', array_type_oid => '1034', descr => 'access control list',
   typname => 'aclitem', typlen => '16', typbyval => 'f', typcategory => 'U',
   typinput => 'aclitemin', typoutput => 'aclitemout', typreceive => '-',
-  typsend => '-', typalign => 'd' },
+  typsend => '-', typalign => 'l' },
 { oid => '1042', array_type_oid => '1014',
   descr => '\'char(length)\' blank-padded string, fixed storage length',
   typname => 'bpchar', typlen => '-1', typbyval => 'f', typcategory => 'S',
@@ -293,7 +293,7 @@
   typname => 'time', typlen => '8', typbyval => 't',
   typcategory => 'D', typinput => 'time_in', typoutput => 'time_out',
   typreceive => 'time_recv', typsend => 'time_send', typmodin => 'timetypmodin',
-  typmodout => 'timetypmodout', typalign => 'd' },
+  typmodout => 'timetypmodout', typalign => 'l' },
 
 # OIDS 1100 - 1199
 
@@ -302,21 +302,21 @@
   typcategory => 'D', typinput => 'timestamp_in', typoutput => 'timestamp_out',
   typreceive => 'timestamp_recv', typsend => 'timestamp_send',
   typmodin => 'timestamptypmodin', typmodout => 'timestamptypmodout',
-  typalign => 'd' },
+  typalign => 'l' },
 { oid => '1184', array_type_oid => '1185',
   descr => 'date and time with time zone',
   typname => 'timestamptz', typlen => '8', typbyval => 't',
   typcategory => 'D', typispreferred => 't', typinput => 'timestamptz_in',
   typoutput => 'timestamptz_out', typreceive => 'timestamptz_recv',
   typsend => 'timestamptz_send', typmodin => 'timestamptztypmodin',
-  typmodout => 'timestamptztypmodout', typalign => 'd' },
+  typmodout => 'timestamptztypmodout', typalign => 'l' },
 { oid => '1186', array_type_oid => '1187',
   descr => 'time interval, format \'number units ...\'',
   typname => 'interval', typlen => '16', typbyval => 'f', typcategory => 'T',
   typispreferred => 't', typinput => 'interval_in', typoutput => 'interval_out',
   typreceive => 'interval_recv', typsend => 'interval_send',
   typmodin => 'intervaltypmodin', typmodout => 'intervaltypmodout',
-  typalign => 'd' },
+  typalign => 'l' },
 
 # OIDS 1200 - 1299
 
@@ -326,7 +326,7 @@
   typinput => 'timetz_in', typoutput => 'timetz_out',
   typreceive => 'timetz_recv', typsend => 'timetz_send',
   typmodin => 'timetztypmodin', typmodout => 'timetztypmodout',
-  typalign => 'd' },
+  typalign => 'l' },
 
 # OIDS 1500 - 1599
 
@@ -415,7 +415,7 @@
 { oid => '3220', array_type_oid => '3221', descr => 'PostgreSQL LSN',
   typname => 'pg_lsn', typlen => '8', typbyval => 't',
   typcategory => 'U', typinput => 'pg_lsn_in', typoutput => 'pg_lsn_out',
-  typreceive => 'pg_lsn_recv', typsend => 'pg_lsn_send', typalign => 'd' },
+  typreceive => 'pg_lsn_recv', typsend => 'pg_lsn_send', typalign => 'l' },
 
 # text search
 { oid => '3614', array_type_oid => '3643',
@@ -462,12 +462,12 @@
   typname => 'txid_snapshot', typlen => '-1', typbyval => 'f',
   typcategory => 'U', typinput => 'txid_snapshot_in',
   typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
-  typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'txid_snapshot_send', typalign => 'l', typstorage => 'x' },
 { oid => '5038', array_type_oid => '5039', descr => 'transaction snapshot',
   typname => 'pg_snapshot', typlen => '-1', typbyval => 'f', typcategory => 'U',
   typinput => 'pg_snapshot_in', typoutput => 'pg_snapshot_out',
   typreceive => 'pg_snapshot_recv', typsend => 'pg_snapshot_send',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 
 # range types
 { oid => '3904', array_type_oid => '3905', descr => 'range of integers',
@@ -485,13 +485,13 @@
   typname => 'tsrange', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
   typreceive => 'range_recv', typsend => 'range_send',
-  typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'range_typanalyze', typalign => 'l', typstorage => 'x' },
 { oid => '3910', array_type_oid => '3911',
   descr => 'range of timestamps with time zone',
   typname => 'tstzrange', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
   typreceive => 'range_recv', typsend => 'range_send',
-  typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'range_typanalyze', typalign => 'l', typstorage => 'x' },
 { oid => '3912', array_type_oid => '3913', descr => 'range of dates',
   typname => 'daterange', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
@@ -501,7 +501,7 @@
   typname => 'int8range', typlen => '-1', typbyval => 'f', typtype => 'r',
   typcategory => 'R', typinput => 'range_in', typoutput => 'range_out',
   typreceive => 'range_recv', typsend => 'range_send',
-  typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'range_typanalyze', typalign => 'l', typstorage => 'x' },
 
 # multirange types
 { oid => '4451', array_type_oid => '6150', descr => 'multirange of integers',
@@ -522,14 +522,14 @@
   typcategory => 'R', typinput => 'multirange_in',
   typoutput => 'multirange_out', typreceive => 'multirange_recv',
   typsend => 'multirange_send', typanalyze => 'multirange_typanalyze',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 { oid => '4534', array_type_oid => '6153',
   descr => 'multirange of timestamps with time zone',
   typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
   typcategory => 'R', typinput => 'multirange_in',
   typoutput => 'multirange_out', typreceive => 'multirange_recv',
   typsend => 'multirange_send', typanalyze => 'multirange_typanalyze',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 { oid => '4535', array_type_oid => '6155', descr => 'multirange of dates',
   typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
   typcategory => 'R', typinput => 'multirange_in',
@@ -541,7 +541,7 @@
   typcategory => 'R', typinput => 'multirange_in',
   typoutput => 'multirange_out', typreceive => 'multirange_recv',
   typsend => 'multirange_send', typanalyze => 'multirange_typanalyze',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'l', typstorage => 'x' },
 
 # pseudo-types
 # types with typtype='p' represent various special cases in the type system.
@@ -556,14 +556,14 @@
   typname => 'record', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typarray => '_record', typinput => 'record_in',
   typoutput => 'record_out', typreceive => 'record_recv',
-  typsend => 'record_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'record_send', typalign => 'm', typstorage => 'x' },
 # Arrays of records have typcategory P, so they can't be autogenerated.
 { oid => '2287',
   typname => '_record', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typsubscript => 'array_subscript_handler',
   typelem => 'record', typinput => 'array_in', typoutput => 'array_out',
   typreceive => 'array_recv', typsend => 'array_send',
-  typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' },
+  typanalyze => 'array_typanalyze', typalign => 'm', typstorage => 'x' },
 { oid => '2275', array_type_oid => '1263', descr => 'C-style string',
   typname => 'cstring', typlen => '-2', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'cstring_in', typoutput => 'cstring_out',
@@ -575,7 +575,7 @@
 { oid => '2277', descr => 'pseudo-type representing a polymorphic array type',
   typname => 'anyarray', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'anyarray_in', typoutput => 'anyarray_out',
-  typreceive => 'anyarray_recv', typsend => 'anyarray_send', typalign => 'd',
+  typreceive => 'anyarray_recv', typsend => 'anyarray_send', typalign => 'm',
   typstorage => 'x' },
 { oid => '2278',
   descr => 'pseudo-type for the result of a function with no real result',
@@ -648,7 +648,7 @@
   descr => 'pseudo-type representing a range over a polymorphic base type',
   typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
-  typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+  typreceive => '-', typsend => '-', typalign => 'm', typstorage => 'x' },
 { oid => '5077',
   descr => 'pseudo-type representing a polymorphic common type',
   typname => 'anycompatible', typlen => '4', typbyval => 't', typtype => 'p',
@@ -661,7 +661,7 @@
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblearray_in',
   typoutput => 'anycompatiblearray_out',
   typreceive => 'anycompatiblearray_recv', typsend => 'anycompatiblearray_send',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '5079',
   descr => 'pseudo-type representing a polymorphic common type that is not an array',
   typname => 'anycompatiblenonarray', typlen => '4', typbyval => 't',
@@ -673,19 +673,19 @@
   typname => 'anycompatiblerange', typlen => '-1', typbyval => 'f',
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
   typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '4537',
   descr => 'pseudo-type representing a polymorphic base type that is a multirange',
   typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
   typcategory => 'P', typinput => 'anymultirange_in',
   typoutput => 'anymultirange_out', typreceive => '-', typsend => '-',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '4538',
   descr => 'pseudo-type representing a multirange over a polymorphic common type',
   typname => 'anycompatiblemultirange', typlen => '-1', typbyval => 'f',
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
   typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
-  typalign => 'd', typstorage => 'x' },
+  typalign => 'm', typstorage => 'x' },
 { oid => '4600', descr => 'pseudo-type representing BRIN bloom summary',
   typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f',
   typcategory => 'Z', typinput => 'brin_bloom_summary_in',
@@ -704,5 +704,5 @@
   descr => 'object identifier(oid8), 8 bytes',
   typname => 'oid8', typlen => '8', typbyval => 't', typcategory => 'N',
   typinput => 'oid8in', typoutput => 'oid8out', typreceive => 'oid8recv',
-  typsend => 'oid8send', typalign => 'd' },
+  typsend => 'oid8send', typalign => 'l' },
 ]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 70d4a20c02b..b749a47d651 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -302,7 +302,9 @@ MAKE_SYSCACHE(TYPENAMENSP, pg_type_typname_nsp_index, 64);
 #define  TYPALIGN_CHAR			'c' /* char alignment (i.e. unaligned) */
 #define  TYPALIGN_SHORT			's' /* short alignment (typically 2 bytes) */
 #define  TYPALIGN_INT			'i' /* int alignment (typically 4 bytes) */
+#define  TYPALIGN_INT64			'l' /* int64 alignment (often 8 bytes) */
 #define  TYPALIGN_DOUBLE		'd' /* double alignment (often 8 bytes) */
+#define  TYPALIGN_MAX			'm' /* maximum alignment (usually 8 bytes) */
 
 #define  TYPSTORAGE_PLAIN		'p' /* type not prepared for toasting */
 #define  TYPSTORAGE_EXTERNAL	'e' /* toastable, don't try to compress */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 339268dc8ef..5699c033ade 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -12,9 +12,6 @@
 /* The normal alignment of `int64_t', in bytes. */
 #undef ALIGNOF_INT64_T
 
-/* The normal alignment of `long', in bytes. */
-#undef ALIGNOF_LONG
-
 /* The normal alignment of `PG_INT128_TYPE', in bytes. */
 #undef ALIGNOF_PG_INT128_TYPE
 
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 44055de6aeb..d4dbb6b5b85 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -329,7 +329,7 @@ PLy_output_setup_func(PLyObToDatum *arg, MemoryContext arg_mcxt,
 		/* hard-wired knowledge about type RECORD: */
 		arg->typbyval = false;
 		arg->typlen = -1;
-		arg->typalign = TYPALIGN_DOUBLE;
+		arg->typalign = TYPALIGN_MAX;
 	}
 
 	/*
@@ -452,7 +452,7 @@ PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt,
 		/* hard-wired knowledge about type RECORD: */
 		arg->typbyval = false;
 		arg->typlen = -1;
-		arg->typalign = TYPALIGN_DOUBLE;
+		arg->typalign = TYPALIGN_MAX;
 	}
 
 	/*
diff --git a/src/test/regress/expected/create_type.out b/src/test/regress/expected/create_type.out
index 5181c4290b4..07919b4fe0e 100644
--- a/src/test/regress/expected/create_type.out
+++ b/src/test/regress/expected/create_type.out
@@ -49,6 +49,7 @@ CREATE TYPE city_budget (
    output = int44out,
    element = int4,
    category = 'x',   -- just to verify the system will take it
+   alignment = max,  -- ditto (overspecifying alignment is safe)
    preferred = true  -- ditto
 );
 -- Test creation and destruction of shell types
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 9c519f1a1a1..a688cbc3c83 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -1088,8 +1088,10 @@ LINE 1: ...8 (input = xfloat8in, output = xfloat8out, like = no_such_ty...
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
-create cast (xfloat8 as bigint) without function;
-create cast (bigint as xfloat8) without function;
+-- this hack depends on bigint and float8 having the same pass-by-value-ness:
+create function bigint_xfloat8(bigint) returns xfloat8 immutable strict
+  language internal as 'int8up';
+create cast (bigint as xfloat8) with function bigint_xfloat8(bigint);
 -- float8: seeeeeee eeeeeeee eeeeeeee mmmmmmmm mmmmmmmm(x4)
 -- we don't care to assume the platform's strtod() handles subnormals
 -- correctly; those are "use at your own risk". However we do test
@@ -1503,5 +1505,5 @@ DETAIL:  drop cascades to function xfloat8in(cstring)
 drop cascades to function xfloat8out(xfloat8)
 drop cascades to cast from xfloat8 to double precision
 drop cascades to cast from double precision to xfloat8
-drop cascades to cast from xfloat8 to bigint
+drop cascades to function bigint_xfloat8(bigint)
 drop cascades to cast from bigint to xfloat8
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 1d21d3eb446..936fa36a600 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -23,7 +23,7 @@ WHERE t1.typnamespace = 0 OR
     (t1.typlen <= 0 AND t1.typlen != -1 AND t1.typlen != -2) OR
     (t1.typtype not in ('b', 'c', 'd', 'e', 'm', 'p', 'r')) OR
     NOT t1.typisdefined OR
-    (t1.typalign not in ('c', 's', 'i', 'd')) OR
+    (t1.typalign not in ('c', 's', 'i', 'l', 'd', 'm')) OR
     (t1.typstorage not in ('p', 'x', 'e', 'm'));
  oid | typname 
 -----+---------
@@ -36,7 +36,7 @@ WHERE t1.typbyval AND
     (t1.typlen != 1 OR t1.typalign != 'c') AND
     (t1.typlen != 2 OR t1.typalign != 's') AND
     (t1.typlen != 4 OR t1.typalign != 'i') AND
-    (t1.typlen != 8 OR t1.typalign != 'd');
+    (t1.typlen != 8 OR (t1.typalign != 'l' AND t1.typalign != 'd'));
  oid | typname 
 -----+---------
 (0 rows)
@@ -108,6 +108,8 @@ FROM pg_type as t1
      LEFT JOIN pg_type as t2 ON rngsubtype = t2.oid
 WHERE t1.typtype = 'r' AND
     (t1.typalign != (CASE WHEN t2.typalign = 'd' THEN 'd'::"char"
+                          WHEN t2.typalign = 'l' THEN 'l'::"char"
+                          WHEN t2.typalign = 'm' THEN 'm'::"char"
                           ELSE 'i'::"char" END)
      OR t2.oid IS NULL);
  oid | typname | typalign | typname | typalign 
@@ -423,6 +425,8 @@ SELECT t1.oid, t1.typname, t1.typalign, t2.typname, t2.typalign
 FROM pg_type AS t1, pg_type AS t2
 WHERE t1.typarray = t2.oid AND
     t2.typalign != (CASE WHEN t1.typalign = 'd' THEN 'd'::"char"
+                         WHEN t1.typalign = 'l' THEN 'l'::"char"
+                         WHEN t1.typalign = 'm' THEN 'm'::"char"
                          ELSE 'i'::"char" END);
  oid | typname | typalign | typname | typalign 
 -----+---------+----------+---------+----------
diff --git a/src/test/regress/sql/create_type.sql b/src/test/regress/sql/create_type.sql
index c25018029c2..94c5b949c25 100644
--- a/src/test/regress/sql/create_type.sql
+++ b/src/test/regress/sql/create_type.sql
@@ -47,6 +47,7 @@ CREATE TYPE city_budget (
    output = int44out,
    element = int4,
    category = 'x',   -- just to verify the system will take it
+   alignment = max,  -- ditto (overspecifying alignment is safe)
    preferred = true  -- ditto
 );
 
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 0ef271f2702..c6b59a322ab 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -355,8 +355,10 @@ create type xfloat8 (input = xfloat8in, output = xfloat8out, like = no_such_type
 create type xfloat8 (input = xfloat8in, output = xfloat8out, like = float8);
 create cast (xfloat8 as float8) without function;
 create cast (float8 as xfloat8) without function;
-create cast (xfloat8 as bigint) without function;
-create cast (bigint as xfloat8) without function;
+-- this hack depends on bigint and float8 having the same pass-by-value-ness:
+create function bigint_xfloat8(bigint) returns xfloat8 immutable strict
+  language internal as 'int8up';
+create cast (bigint as xfloat8) with function bigint_xfloat8(bigint);
 
 -- float8: seeeeeee eeeeeeee eeeeeeee mmmmmmmm mmmmmmmm(x4)
 
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 95d5b6e0915..625305c42bc 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -28,7 +28,7 @@ WHERE t1.typnamespace = 0 OR
     (t1.typlen <= 0 AND t1.typlen != -1 AND t1.typlen != -2) OR
     (t1.typtype not in ('b', 'c', 'd', 'e', 'm', 'p', 'r')) OR
     NOT t1.typisdefined OR
-    (t1.typalign not in ('c', 's', 'i', 'd')) OR
+    (t1.typalign not in ('c', 's', 'i', 'l', 'd', 'm')) OR
     (t1.typstorage not in ('p', 'x', 'e', 'm'));
 
 -- Look for "pass by value" types that can't be passed by value.
@@ -39,7 +39,7 @@ WHERE t1.typbyval AND
     (t1.typlen != 1 OR t1.typalign != 'c') AND
     (t1.typlen != 2 OR t1.typalign != 's') AND
     (t1.typlen != 4 OR t1.typalign != 'i') AND
-    (t1.typlen != 8 OR t1.typalign != 'd');
+    (t1.typlen != 8 OR (t1.typalign != 'l' AND t1.typalign != 'd'));
 
 -- Look for "toastable" types that aren't varlena.
 
@@ -90,6 +90,8 @@ FROM pg_type as t1
      LEFT JOIN pg_type as t2 ON rngsubtype = t2.oid
 WHERE t1.typtype = 'r' AND
     (t1.typalign != (CASE WHEN t2.typalign = 'd' THEN 'd'::"char"
+                          WHEN t2.typalign = 'l' THEN 'l'::"char"
+                          WHEN t2.typalign = 'm' THEN 'm'::"char"
                           ELSE 'i'::"char" END)
      OR t2.oid IS NULL);
 
@@ -302,6 +304,8 @@ SELECT t1.oid, t1.typname, t1.typalign, t2.typname, t2.typalign
 FROM pg_type AS t1, pg_type AS t2
 WHERE t1.typarray = t2.oid AND
     t2.typalign != (CASE WHEN t1.typalign = 'd' THEN 'd'::"char"
+                         WHEN t1.typalign = 'l' THEN 'l'::"char"
+                         WHEN t1.typalign = 'm' THEN 'm'::"char"
                          ELSE 'i'::"char" END);
 
 -- Check for typelem set without a handler
-- 
2.43.7

Reply via email to