From 7b2949390ab32ec4d3c77dc1c1e239325bd3ec58 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
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/1127261.1769649624@sss.pgh.pa.us
---
 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

