Refactor construct_array() and deconstruct_array() for built-in types

Started by Peter Eisentrautover 3 years ago7 messages
#1Peter Eisentraut
peter.eisentraut@enterprisedb.com
1 attachment(s)

[for PG16]

There are many calls to construct_array() and deconstruct_array() for
built-in types, for example, when dealing with system catalog columns.
These all hardcode the type attributes necessary to pass to these functions.

To simplify this a bit, add construct_array_builtin(),
deconstruct_array_builtin() as wrappers that centralize this hardcoded
knowledge. This simplifies many call sites and reduces the amount of
hardcoded stuff that is spread around.

I also considered having genbki.pl generate lookup tables for these
hardcoded values, similar to schemapg.h, but that ultimately seemed
excessive.

Thoughts?

Attachments:

0001-Add-construct_array_builtin-deconstruct_array_builti.patchtext/plain; charset=UTF-8; name=0001-Add-construct_array_builtin-deconstruct_array_builti.patchDownload
From 35fe30fec1692a4e329e9c9b1ed40863e3b970b2 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 May 2022 10:23:26 +0200
Subject: [PATCH] Add construct_array_builtin, deconstruct_array_builtin

There were many calls to construct_array() and deconstruct_array() for
built-in types, for example, when dealing with system catalog columns.
These all hardcoded the type attributes necessary to pass to these
functions.

To simplify this a bit, add construct_array_builtin(),
deconstruct_array_builtin() as wrappers that centralize this hardcoded
knowledge.  This simplifies many call sites and reduces the amount of
hardcoded stuff that is spread around.
---
 contrib/hstore/hstore_gin.c                   |   4 +-
 contrib/hstore/hstore_gist.c                  |   8 +-
 contrib/hstore/hstore_io.c                    |  12 +-
 contrib/hstore/hstore_op.c                    |  11 +-
 contrib/pageinspect/btreefuncs.c              |   6 +-
 contrib/pageinspect/ginfuncs.c                |  10 +-
 contrib/pageinspect/gistfuncs.c               |   4 +-
 contrib/pageinspect/hashfuncs.c               |  14 +-
 contrib/pageinspect/heapfuncs.c               |   4 +-
 contrib/pg_trgm/trgm_op.c                     |   7 +-
 contrib/pgcrypto/pgp-pgsql.c                  |   9 +-
 src/backend/access/common/reloptions.c        |   9 +-
 src/backend/catalog/objectaddress.c           |  12 +-
 src/backend/catalog/pg_constraint.c           |  21 +--
 src/backend/catalog/pg_proc.c                 |   5 +-
 src/backend/catalog/pg_subscription.c         |   4 +-
 src/backend/commands/analyze.c                |   5 +-
 src/backend/commands/event_trigger.c          |   3 +-
 src/backend/commands/extension.c              |  24 +--
 src/backend/commands/functioncmds.c           |  12 +-
 src/backend/commands/policy.c                 |   9 +-
 src/backend/commands/prepare.c                |   4 +-
 src/backend/commands/statscmds.c              |   2 +-
 src/backend/commands/subscriptioncmds.c       |   3 +-
 src/backend/commands/typecmds.c               |   7 +-
 src/backend/executor/nodeTidscan.c            |   4 +-
 src/backend/parser/parse_type.c               |   4 +-
 src/backend/parser/parse_utilcmd.c            |   4 +-
 .../replication/logical/logicalfuncs.c        |   3 +-
 src/backend/statistics/extended_stats.c       |   5 +-
 src/backend/tsearch/dict.c                    |   7 +-
 src/backend/utils/adt/arrayfuncs.c            | 153 +++++++++++++++++-
 src/backend/utils/adt/arrayutils.c            |   5 +-
 src/backend/utils/adt/hbafuncs.c              |   2 +-
 src/backend/utils/adt/json.c                  |  13 +-
 src/backend/utils/adt/jsonb.c                 |  13 +-
 src/backend/utils/adt/jsonb_gin.c             |   4 +-
 src/backend/utils/adt/jsonb_op.c              |   6 +-
 src/backend/utils/adt/jsonfuncs.c             |  18 +--
 src/backend/utils/adt/lockfuncs.c             |  10 +-
 src/backend/utils/adt/name.c                  |   6 +-
 src/backend/utils/adt/orderedsetaggs.c        |  20 +--
 src/backend/utils/adt/pg_upgrade_support.c    |   4 +-
 src/backend/utils/adt/ruleutils.c             |  20 +--
 src/backend/utils/adt/tsvector_op.c           |  19 +--
 src/backend/utils/adt/xml.c                   |   6 +-
 src/backend/utils/cache/evtcache.c            |   3 +-
 src/backend/utils/fmgr/funcapi.c              |  14 +-
 src/backend/utils/misc/guc.c                  |  14 +-
 src/include/utils/array.h                     |   4 +
 .../modules/test_rls_hooks/test_rls_hooks.c   |   4 +-
 51 files changed, 276 insertions(+), 298 deletions(-)

diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 908530261a..766c00bb6a 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -118,9 +118,7 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
 					j;
 		text	   *item;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &key_datums, &key_nulls, &key_count);
+		deconstruct_array_builtin(query, TEXTOID, &key_datums, &key_nulls, &key_count);
 
 		entries = (Datum *) palloc(sizeof(Datum) * key_count);
 
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 102c9cea72..0a95628837 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -560,9 +560,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 		int			key_count;
 		int			i;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &key_datums, &key_nulls, &key_count);
+		deconstruct_array_builtin(query, TEXTOID, &key_datums, &key_nulls, &key_count);
 
 		for (i = 0; res && i < key_count; ++i)
 		{
@@ -583,9 +581,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 		int			key_count;
 		int			i;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &key_datums, &key_nulls, &key_count);
+		deconstruct_array_builtin(query, TEXTOID, &key_datums, &key_nulls, &key_count);
 
 		res = false;
 
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index b3304ff844..fb72bb6cfe 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -567,9 +567,7 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 				 errmsg("wrong number of array subscripts")));
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &key_count);
+	deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
 
 	/* see discussion in hstoreArrayToPairs() */
 	if (key_count > MaxAllocSize / sizeof(Pairs))
@@ -606,9 +604,7 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 					 errmsg("arrays must have same bounds")));
 
-		deconstruct_array(value_array,
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &value_datums, &value_nulls, &value_count);
+		deconstruct_array_builtin(value_array, TEXTOID, &value_datums, &value_nulls, &value_count);
 
 		Assert(key_count == value_count);
 	}
@@ -696,9 +692,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 					 errmsg("wrong number of array subscripts")));
 	}
 
-	deconstruct_array(in_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &in_datums, &in_nulls, &in_count);
+	deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
 
 	count = in_count / 2;
 
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index dd79d01cac..2f540d7ed6 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -80,9 +80,7 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
 	int			i,
 				j;
 
-	deconstruct_array(a,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &key_count);
+	deconstruct_array_builtin(a, TEXTOID, &key_datums, &key_nulls, &key_count);
 
 	if (key_count == 0)
 	{
@@ -582,9 +580,7 @@ hstore_slice_to_array(PG_FUNCTION_ARGS)
 	int			key_count;
 	int			i;
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &key_count);
+	deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
 
 	if (key_count == 0)
 	{
@@ -719,8 +715,7 @@ hstore_akeys(PG_FUNCTION_ARGS)
 		d[i] = PointerGetDatum(t);
 	}
 
-	a = construct_array(d, count,
-						TEXTOID, -1, false, TYPALIGN_INT);
+	a = construct_array_builtin(d, count, TEXTOID);
 
 	PG_RETURN_POINTER(a);
 }
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 62f2c1b315..9375d55e14 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -425,11 +425,7 @@ bt_page_print_tuples(struct user_args *uargs)
 		tids_datum = (Datum *) palloc(nposting * sizeof(Datum));
 		for (int i = 0; i < nposting; i++)
 			tids_datum[i] = ItemPointerGetDatum(&tids[i]);
-		values[j++] = PointerGetDatum(construct_array(tids_datum,
-													  nposting,
-													  TIDOID,
-													  sizeof(ItemPointerData),
-													  false, TYPALIGN_SHORT));
+		values[j++] = PointerGetDatum(construct_array_builtin(tids_datum, nposting, TIDOID));
 		pfree(tids_datum);
 	}
 	else
diff --git a/contrib/pageinspect/ginfuncs.c b/contrib/pageinspect/ginfuncs.c
index 31aca7b000..952e9d51a8 100644
--- a/contrib/pageinspect/ginfuncs.c
+++ b/contrib/pageinspect/ginfuncs.c
@@ -166,9 +166,7 @@ gin_page_opaque_info(PG_FUNCTION_ARGS)
 
 	values[0] = Int64GetDatum(opaq->rightlink);
 	values[1] = Int32GetDatum(opaq->maxoff);
-	values[2] = PointerGetDatum(construct_array(flags, nflags,
-												TEXTOID,
-												-1, false, TYPALIGN_INT));
+	values[2] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID));
 
 	/* Build and return the result tuple. */
 	resultTuple = heap_form_tuple(tupdesc, values, nulls);
@@ -273,11 +271,7 @@ gin_leafpage_items(PG_FUNCTION_ARGS)
 		tids_datum = (Datum *) palloc(ndecoded * sizeof(Datum));
 		for (i = 0; i < ndecoded; i++)
 			tids_datum[i] = ItemPointerGetDatum(&tids[i]);
-		values[2] = PointerGetDatum(construct_array(tids_datum,
-													ndecoded,
-													TIDOID,
-													sizeof(ItemPointerData),
-													false, TYPALIGN_SHORT));
+		values[2] = PointerGetDatum(construct_array_builtin(tids_datum, ndecoded, TIDOID));
 		pfree(tids_datum);
 		pfree(tids);
 
diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c
index 9c29fbc7aa..fb48d6feca 100644
--- a/contrib/pageinspect/gistfuncs.c
+++ b/contrib/pageinspect/gistfuncs.c
@@ -104,9 +104,7 @@ gist_page_opaque_info(PG_FUNCTION_ARGS)
 	values[0] = LSNGetDatum(PageGetLSN(page));
 	values[1] = LSNGetDatum(GistPageGetNSN(page));
 	values[2] = Int64GetDatum(opaq->rightlink);
-	values[3] = PointerGetDatum(construct_array(flags, nflags,
-												TEXTOID,
-												-1, false, TYPALIGN_INT));
+	values[3] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID));
 
 	/* Build and return the result tuple. */
 	resultTuple = heap_form_tuple(tupdesc, values, nulls);
diff --git a/contrib/pageinspect/hashfuncs.c b/contrib/pageinspect/hashfuncs.c
index 69af7b962f..6e592e3770 100644
--- a/contrib/pageinspect/hashfuncs.c
+++ b/contrib/pageinspect/hashfuncs.c
@@ -564,21 +564,11 @@ hash_metapage_info(PG_FUNCTION_ARGS)
 
 	for (i = 0; i < HASH_MAX_SPLITPOINTS; i++)
 		spares[i] = Int64GetDatum((int64) metad->hashm_spares[i]);
-	values[j++] = PointerGetDatum(construct_array(spares,
-												  HASH_MAX_SPLITPOINTS,
-												  INT8OID,
-												  sizeof(int64),
-												  FLOAT8PASSBYVAL,
-												  TYPALIGN_DOUBLE));
+	values[j++] = PointerGetDatum(construct_array_builtin(spares, lengthof(spares), INT8OID));
 
 	for (i = 0; i < HASH_MAX_BITMAPS; i++)
 		mapp[i] = Int64GetDatum((int64) metad->hashm_mapp[i]);
-	values[j++] = PointerGetDatum(construct_array(mapp,
-												  HASH_MAX_BITMAPS,
-												  INT8OID,
-												  sizeof(int64),
-												  FLOAT8PASSBYVAL,
-												  TYPALIGN_DOUBLE));
+	values[j++] = PointerGetDatum(construct_array_builtin(mapp, lengthof(mapp), INT8OID));
 
 	tuple = heap_form_tuple(tupleDesc, values, nulls);
 
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 3dd1a9bc2a..a654234c6b 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -590,7 +590,7 @@ heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
 
 	/* build value */
 	Assert(cnt <= bitcnt);
-	a = construct_array(flags, cnt, TEXTOID, -1, false, TYPALIGN_INT);
+	a = construct_array_builtin(flags, cnt, TEXTOID);
 	values[0] = PointerGetDatum(a);
 
 	/*
@@ -612,7 +612,7 @@ heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
 	if (cnt == 0)
 		a = construct_empty_array(TEXTOID);
 	else
-		a = construct_array(flags, cnt, TEXTOID, -1, false, TYPALIGN_INT);
+		a = construct_array_builtin(flags, cnt, TEXTOID);
 	pfree(flags);
 	values[1] = PointerGetDatum(a);
 
diff --git a/contrib/pg_trgm/trgm_op.c b/contrib/pg_trgm/trgm_op.c
index e9b7981619..cbcc481a17 100644
--- a/contrib/pg_trgm/trgm_op.c
+++ b/contrib/pg_trgm/trgm_op.c
@@ -977,12 +977,7 @@ show_trgm(PG_FUNCTION_ARGS)
 		d[i] = PointerGetDatum(item);
 	}
 
-	a = construct_array(d,
-						ARRNELEM(trg),
-						TEXTOID,
-						-1,
-						false,
-						TYPALIGN_INT);
+	a = construct_array_builtin(d, ARRNELEM(trg), TEXTOID);
 
 	for (i = 0; i < ARRNELEM(trg); i++)
 		pfree(DatumGetPointer(d[i]));
diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c
index 0536bfb892..d9b15b07b0 100644
--- a/contrib/pgcrypto/pgp-pgsql.c
+++ b/contrib/pgcrypto/pgp-pgsql.c
@@ -774,13 +774,8 @@ parse_key_value_arrays(ArrayType *key_array, ArrayType *val_array,
 	if (nkdims == 0)
 		return 0;
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &key_count);
-
-	deconstruct_array(val_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &val_datums, &val_nulls, &val_count);
+	deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
+	deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
 
 	if (key_count != val_count)
 		ereport(ERROR,
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 599e160ca6..2b2ef63c35 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1177,8 +1177,7 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
 		int			noldoptions;
 		int			i;
 
-		deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
-						  &oldoptions, NULL, &noldoptions);
+		deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
 
 		for (i = 0; i < noldoptions; i++)
 		{
@@ -1345,8 +1344,7 @@ untransformRelOptions(Datum options)
 
 	array = DatumGetArrayTypeP(options);
 
-	deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
-					  &optiondatums, NULL, &noptions);
+	deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
 
 	for (i = 0; i < noptions; i++)
 	{
@@ -1436,8 +1434,7 @@ parseRelOptionsInternal(Datum options, bool validate,
 	int			noptions;
 	int			i;
 
-	deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
-					  &optiondatums, NULL, &noptions);
+	deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
 
 	for (i = 0; i < noptions; i++)
 	{
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 8377b4f7d4..2d21db4690 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2099,8 +2099,7 @@ textarray_to_strvaluelist(ArrayType *arr)
 	List	   *list = NIL;
 	int			i;
 
-	deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,
-					  &elems, &nulls, &nelems);
+	deconstruct_array_builtin(arr, TEXTOID, &elems, &nulls, &nelems);
 
 	for (i = 0; i < nelems; i++)
 	{
@@ -2156,8 +2155,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 		bool	   *nulls;
 		int			nelems;
 
-		deconstruct_array(namearr, TEXTOID, -1, false, TYPALIGN_INT,
-						  &elems, &nulls, &nelems);
+		deconstruct_array_builtin(namearr, TEXTOID, &elems, &nulls, &nelems);
 		if (nelems != 1)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2174,8 +2172,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 		bool	   *nulls;
 		int			nelems;
 
-		deconstruct_array(namearr, TEXTOID, -1, false, TYPALIGN_INT,
-						  &elems, &nulls, &nelems);
+		deconstruct_array_builtin(namearr, TEXTOID, &elems, &nulls, &nelems);
 		if (nelems != 1)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2213,8 +2210,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 		int			nelems;
 		int			i;
 
-		deconstruct_array(argsarr, TEXTOID, -1, false, TYPALIGN_INT,
-						  &elems, &nulls, &nelems);
+		deconstruct_array_builtin(argsarr, TEXTOID, &elems, &nulls, &nelems);
 
 		args = NIL;
 		for (i = 0; i < nelems; i++)
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 472dbda211..454367a852 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -112,8 +112,7 @@ CreateConstraintEntry(const char *constraintName,
 		conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
 		for (i = 0; i < constraintNKeys; i++)
 			conkey[i] = Int16GetDatum(constraintKey[i]);
-		conkeyArray = construct_array(conkey, constraintNKeys,
-									  INT2OID, 2, true, TYPALIGN_SHORT);
+		conkeyArray = construct_array_builtin(conkey, constraintNKeys, INT2OID);
 	}
 	else
 		conkeyArray = NULL;
@@ -125,27 +124,22 @@ CreateConstraintEntry(const char *constraintName,
 		fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
 		for (i = 0; i < foreignNKeys; i++)
 			fkdatums[i] = Int16GetDatum(foreignKey[i]);
-		confkeyArray = construct_array(fkdatums, foreignNKeys,
-									   INT2OID, 2, true, TYPALIGN_SHORT);
+		confkeyArray = construct_array_builtin(fkdatums, foreignNKeys, INT2OID);
 		for (i = 0; i < foreignNKeys; i++)
 			fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
-		conpfeqopArray = construct_array(fkdatums, foreignNKeys,
-										 OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+		conpfeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
 		for (i = 0; i < foreignNKeys; i++)
 			fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
-		conppeqopArray = construct_array(fkdatums, foreignNKeys,
-										 OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+		conppeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
 		for (i = 0; i < foreignNKeys; i++)
 			fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
-		conffeqopArray = construct_array(fkdatums, foreignNKeys,
-										 OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+		conffeqopArray = construct_array_builtin(fkdatums, foreignNKeys, OIDOID);
 
 		if (numFkDeleteSetCols > 0)
 		{
 			for (i = 0; i < numFkDeleteSetCols; i++)
 				fkdatums[i] = Int16GetDatum(fkDeleteSetCols[i]);
-			confdelsetcolsArray = construct_array(fkdatums, numFkDeleteSetCols,
-										   INT2OID, 2, true, TYPALIGN_SHORT);
+			confdelsetcolsArray = construct_array_builtin(fkdatums, numFkDeleteSetCols, INT2OID);
 		}
 		else
 			confdelsetcolsArray = NULL;
@@ -166,8 +160,7 @@ CreateConstraintEntry(const char *constraintName,
 		opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
 		for (i = 0; i < constraintNKeys; i++)
 			opdatums[i] = ObjectIdGetDatum(exclOp[i]);
-		conexclopArray = construct_array(opdatums, constraintNKeys,
-										 OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+		conexclopArray = construct_array_builtin(opdatums, constraintNKeys, OIDOID);
 	}
 	else
 		conexclopArray = NULL;
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index d82221fdb8..a4a30bccf6 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -1193,10 +1193,7 @@ oid_array_to_list(Datum datum)
 	int			i;
 	List	   *result = NIL;
 
-	deconstruct_array(array,
-					  OIDOID,
-					  sizeof(Oid), true, TYPALIGN_INT,
-					  &values, NULL, &nelems);
+	deconstruct_array_builtin(array, OIDOID, &values, NULL, &nelems);
 	for (i = 0; i < nelems; i++)
 		result = lappend_oid(result, values[i]);
 	return result;
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index add51caadf..8856ce3b50 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -260,9 +260,7 @@ textarray_to_stringlist(ArrayType *textarray)
 				i;
 	List	   *res = NIL;
 
-	deconstruct_array(textarray,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &elems, NULL, &nelems);
+	deconstruct_array_builtin(textarray, TEXTOID, &elems, NULL, &nelems);
 
 	if (nelems == 0)
 		return NIL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 305226692a..7f8652013e 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1688,10 +1688,7 @@ update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats)
 
 				for (n = 0; n < nnum; n++)
 					numdatums[n] = Float4GetDatum(stats->stanumbers[k][n]);
-				/* XXX knows more than it should about type float4: */
-				arry = construct_array(numdatums, nnum,
-									   FLOAT4OID,
-									   sizeof(float4), true, TYPALIGN_INT);
+				arry = construct_array_builtin(numdatums, nnum, FLOAT4OID);
 				values[i++] = PointerGetDatum(arry);	/* stanumbersN */
 			}
 			else
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4642527881..f46f86474a 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -351,8 +351,7 @@ filter_list_to_array(List *filterlist)
 		pfree(result);
 	}
 
-	return PointerGetDatum(construct_array(data, l, TEXTOID,
-										   -1, false, TYPALIGN_INT));
+	return PointerGetDatum(construct_array_builtin(data, l, TEXTOID));
 }
 
 /*
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 1013790dbb..117ed80246 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2251,9 +2251,7 @@ convert_requires_to_datum(List *requires)
 		datums[ndatums++] =
 			DirectFunctionCall1(namein, CStringGetDatum(curreq));
 	}
-	a = construct_array(datums, ndatums,
-						NAMEOID,
-						NAMEDATALEN, false, TYPALIGN_CHAR);
+	a = construct_array_builtin(datums, ndatums, NAMEOID);
 	return PointerGetDatum(a);
 }
 
@@ -2433,9 +2431,7 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 		arrayLength = 0;
 		arrayIndex = 1;
 
-		a = construct_array(&elementDatum, 1,
-							OIDOID,
-							sizeof(Oid), true, TYPALIGN_INT);
+		a = construct_array_builtin(&elementDatum, 1, OIDOID);
 	}
 	else
 	{
@@ -2486,9 +2482,7 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 		if (arrayLength != 0)
 			elog(ERROR, "extconfig and extcondition arrays do not match");
 
-		a = construct_array(&elementDatum, 1,
-							TEXTOID,
-							-1, false, TYPALIGN_INT);
+		a = construct_array_builtin(&elementDatum, 1, TEXTOID);
 	}
 	else
 	{
@@ -2630,14 +2624,12 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
 		int			i;
 
 		/* We already checked there are no nulls */
-		deconstruct_array(a, OIDOID, sizeof(Oid), true, TYPALIGN_INT,
-						  &dvalues, NULL, &nelems);
+		deconstruct_array_builtin(a, OIDOID, &dvalues, NULL, &nelems);
 
 		for (i = arrayIndex; i < arrayLength - 1; i++)
 			dvalues[i] = dvalues[i + 1];
 
-		a = construct_array(dvalues, arrayLength - 1,
-							OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+		a = construct_array_builtin(dvalues, arrayLength - 1, OIDOID);
 
 		repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
 	}
@@ -2676,14 +2668,12 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
 		int			i;
 
 		/* We already checked there are no nulls */
-		deconstruct_array(a, TEXTOID, -1, false, TYPALIGN_INT,
-						  &dvalues, NULL, &nelems);
+		deconstruct_array_builtin(a, TEXTOID, &dvalues, NULL, &nelems);
 
 		for (i = arrayIndex; i < arrayLength - 1; i++)
 			dvalues[i] = dvalues[i + 1];
 
-		a = construct_array(dvalues, arrayLength - 1,
-							TEXTOID, -1, false, TYPALIGN_INT);
+		a = construct_array_builtin(dvalues, arrayLength - 1, TEXTOID);
 
 		repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
 	}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 00a6d282cf..b016eecb2c 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -468,10 +468,8 @@ interpret_function_parameter_list(ParseState *pstate,
 
 	if (outCount > 0 || varCount > 0)
 	{
-		*allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
-											 sizeof(Oid), true, TYPALIGN_INT);
-		*parameterModes = construct_array(paramModes, parameterCount, CHAROID,
-										  1, true, TYPALIGN_CHAR);
+		*allParameterTypes = construct_array_builtin(allTypes, parameterCount, OIDOID);
+		*parameterModes = construct_array_builtin(paramModes, parameterCount, CHAROID);
 		if (outCount > 1)
 			*requiredResultType = RECORDOID;
 		/* otherwise we set requiredResultType correctly above */
@@ -489,8 +487,7 @@ interpret_function_parameter_list(ParseState *pstate,
 			if (paramNames[i] == PointerGetDatum(NULL))
 				paramNames[i] = CStringGetTextDatum("");
 		}
-		*parameterNames = construct_array(paramNames, parameterCount, TEXTOID,
-										  -1, false, TYPALIGN_INT);
+		*parameterNames = construct_array_builtin(paramNames, parameterCount, TEXTOID);
 	}
 	else
 		*parameterNames = NULL;
@@ -1222,8 +1219,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 		i = 0;
 		foreach(lc, trftypes_list)
 			arr[i++] = ObjectIdGetDatum(lfirst_oid(lc));
-		trftypes = construct_array(arr, list_length(trftypes_list),
-								   OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+		trftypes = construct_array_builtin(arr, list_length(trftypes_list), OIDOID);
 	}
 	else
 	{
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index a59ee3b947..d9dff9ecaa 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -500,8 +500,7 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
 		memset(isnull, 0, sizeof(isnull));
 
 		/* This is the array for the new tuple */
-		role_ids = construct_array(role_oids, num_roles, OIDOID,
-								   sizeof(Oid), true, TYPALIGN_INT);
+		role_ids = construct_array_builtin(role_oids, num_roles, OIDOID);
 
 		replaces[Anum_pg_policy_polroles - 1] = true;
 		values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
@@ -617,8 +616,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
 
 	/* Collect role ids */
 	role_oids = policy_role_list_to_array(stmt->roles, &nitems);
-	role_ids = construct_array(role_oids, nitems, OIDOID,
-							   sizeof(Oid), true, TYPALIGN_INT);
+	role_ids = construct_array_builtin(role_oids, nitems, OIDOID);
 
 	/* Parse the supplied clause */
 	qual_pstate = make_parsestate(NULL);
@@ -801,8 +799,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 	if (stmt->roles != NULL)
 	{
 		role_oids = policy_role_list_to_array(stmt->roles, &nitems);
-		role_ids = construct_array(role_oids, nitems, OIDOID,
-								   sizeof(Oid), true, TYPALIGN_INT);
+		role_ids = construct_array_builtin(role_oids, nitems, OIDOID);
 	}
 
 	/* Get id of table.  Also handles permissions checks. */
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 80738547ed..633c9a40bb 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -722,8 +722,6 @@ build_regtype_array(Oid *param_types, int num_params)
 	for (i = 0; i < num_params; i++)
 		tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
 
-	/* XXX: this hardcodes assumptions about the regtype type */
-	result = construct_array(tmp_ary, num_params, REGTYPEOID,
-							 4, true, TYPALIGN_INT);
+	result = construct_array_builtin(tmp_ary, num_params, REGTYPEOID);
 	return PointerGetDatum(result);
 }
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 54a190722d..e54c2d90c9 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -466,7 +466,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	if (build_expressions)
 		types[ntypes++] = CharGetDatum(STATS_EXT_EXPRESSIONS);
 	Assert(ntypes > 0 && ntypes <= lengthof(types));
-	stxkind = construct_array(types, ntypes, CHAROID, 1, true, TYPALIGN_CHAR);
+	stxkind = construct_array_builtin(types, ntypes, CHAROID);
 
 	/* convert the expressions (if any) to a text datum */
 	if (stxexprs != NIL)
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index b94236f74d..2e5f7b0b55 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -494,8 +494,7 @@ publicationListToArray(List *publist)
 
 	MemoryContextSwitchTo(oldcxt);
 
-	arr = construct_array(datums, list_length(publist),
-						  TEXTOID, -1, false, TYPALIGN_INT);
+	arr = construct_array_builtin(datums, list_length(publist), TEXTOID);
 
 	MemoryContextDelete(memcxt);
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 9b92b04242..4f5e7c7ccb 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1893,12 +1893,9 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 	/* n-arg constructor - vararg */
 	argtypes = buildoidvector(&rangeArrayOid, 1);
 	allParamTypes = ObjectIdGetDatum(rangeArrayOid);
-	allParameterTypes = construct_array(&allParamTypes,
-										1, OIDOID,
-										sizeof(Oid), true, TYPALIGN_INT);
+	allParameterTypes = construct_array_builtin(&allParamTypes, 1, OIDOID);
 	paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
-	parameterModes = construct_array(&paramModes, 1, CHAROID,
-									 1, true, TYPALIGN_CHAR);
+	parameterModes = construct_array_builtin(&paramModes, 1, CHAROID);
 	myself = ProcedureCreate(name,	/* name: same as multirange type */
 							 namespace,
 							 false, /* replace */
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 4116d1f3b5..a1c6325d64 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -206,9 +206,7 @@ TidListEval(TidScanState *tidstate)
 			if (isNull)
 				continue;
 			itemarray = DatumGetArrayTypeP(arraydatum);
-			deconstruct_array(itemarray,
-							  TIDOID, sizeof(ItemPointerData), false, TYPALIGN_SHORT,
-							  &ipdatums, &ipnulls, &ndatums);
+			deconstruct_array_builtin(itemarray, TIDOID, &ipdatums, &ipnulls, &ndatums);
 			if (numTids + ndatums > numAllocTids)
 			{
 				numAllocTids = numTids + ndatums;
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 307114a30d..f7ad689459 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -411,9 +411,7 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
 		datums[n++] = CStringGetDatum(cstr);
 	}
 
-	/* hardwired knowledge about cstring's representation details here */
-	arrtypmod = construct_array(datums, n, CSTRINGOID,
-								-2, false, TYPALIGN_CHAR);
+	arrtypmod = construct_array_builtin(datums, n, CSTRINGOID);
 
 	/* arrange to report location if type's typmodin function fails */
 	setup_parser_errposition_callback(&pcbstate, pstate, typeName->location);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 1a64a52279..f889726a28 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1642,9 +1642,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 					elog(ERROR, "null conexclop for constraint %u",
 						 constraintId);
 
-				deconstruct_array(DatumGetArrayTypeP(datum),
-								  OIDOID, sizeof(Oid), true, TYPALIGN_INT,
-								  &elems, NULL, &nElems);
+				deconstruct_array_builtin(DatumGetArrayTypeP(datum), OIDOID, &elems, NULL, &nElems);
 
 				for (i = 0; i < nElems; i++)
 				{
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 6058d36e0d..7fa2b2cba7 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -172,8 +172,7 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 
 		Assert(ARR_ELEMTYPE(arr) == TEXTOID);
 
-		deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,
-						  &datum_opts, NULL, &nelems);
+		deconstruct_array_builtin(arr, TEXTOID, &datum_opts, NULL, &nelems);
 
 		if (nelems % 2 != 0)
 			ereport(ERROR,
diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c
index ca48395d5c..7c02fb279f 100644
--- a/src/backend/statistics/extended_stats.c
+++ b/src/backend/statistics/extended_stats.c
@@ -2345,10 +2345,7 @@ serialize_expr_stats(AnlExprData *exprdata, int nexprs)
 
 				for (n = 0; n < nnum; n++)
 					numdatums[n] = Float4GetDatum(stats->stanumbers[k][n]);
-				/* XXX knows more than it should about type float4: */
-				arry = construct_array(numdatums, nnum,
-									   FLOAT4OID,
-									   sizeof(float4), true, TYPALIGN_INT);
+				arry = construct_array_builtin(numdatums, nnum, FLOAT4OID);
 				values[i++] = PointerGetDatum(arry);	/* stanumbersN */
 			}
 			else
diff --git a/src/backend/tsearch/dict.c b/src/backend/tsearch/dict.c
index 8dae2b8e99..c6ea9f9269 100644
--- a/src/backend/tsearch/dict.c
+++ b/src/backend/tsearch/dict.c
@@ -68,12 +68,7 @@ ts_lexize(PG_FUNCTION_ARGS)
 		ptr++;
 	}
 
-	a = construct_array(da,
-						ptr - res,
-						TEXTOID,
-						-1,
-						false,
-						TYPALIGN_INT);
+	a = construct_array_builtin(da, ptr - res, TEXTOID);
 
 	ptr = res;
 	while (ptr->lexeme)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 78e951a6bc..9f22349a0d 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3330,6 +3330,88 @@ construct_array(Datum *elems, int nelems,
 							  elmtype, elmlen, elmbyval, elmalign);
 }
 
+/*
+ * Like construct_array(), where elmtype must be a built-in type, and
+ * elmlen/elmbyval/elmalign is looked up from hardcoded data.  This is often
+ * useful when manipulating arrays from/for system catalogs.
+ */
+ArrayType *
+construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
+{
+	int			elmlen;
+	bool		elmbyval;
+	char		elmalign;
+
+	switch (elmtype)
+	{
+		case CHAROID:
+			elmlen = 1;
+			elmbyval = true;
+			elmalign = TYPALIGN_CHAR;
+			break;
+
+		case CSTRINGOID:
+			elmlen = -2;
+			elmbyval = false;
+			elmalign = TYPALIGN_CHAR;
+			break;
+
+		case FLOAT4OID:
+			elmlen = sizeof(float4);
+			elmbyval = true;
+			elmalign = TYPALIGN_INT;
+			break;
+
+		case INT2OID:
+			elmlen = sizeof(int16);
+			elmbyval = true;
+			elmalign = TYPALIGN_SHORT;
+			break;
+
+		case INT4OID:
+			elmlen = sizeof(int32);
+			elmbyval = true;
+			elmalign = TYPALIGN_INT;
+			break;
+
+		case INT8OID:
+			elmlen = sizeof(int64);
+			elmbyval = FLOAT8PASSBYVAL;
+			elmalign = TYPALIGN_DOUBLE;
+			break;
+
+		case NAMEOID:
+			elmlen = NAMEDATALEN;
+			elmbyval = false;
+			elmalign = TYPALIGN_CHAR;
+			break;
+
+		case OIDOID:
+		case REGTYPEOID:
+			elmlen = sizeof(Oid);
+			elmbyval = true;
+			elmalign = TYPALIGN_INT;
+			break;
+
+		case TEXTOID:
+			elmlen = -1;
+			elmbyval = false;
+			elmalign = TYPALIGN_INT;
+			break;
+
+		case TIDOID:
+			elmlen = sizeof(ItemPointerData);
+			elmbyval = false;
+			elmalign = TYPALIGN_SHORT;
+			break;
+
+		default:
+			elog(ERROR, "type %u not supported here", elmtype);
+	}
+
+	return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
+}
+
 /*
  * construct_md_array	--- simple method for constructing an array object
  *							with arbitrary dimensions and possible NULLs
@@ -3483,9 +3565,9 @@ construct_empty_expanded_array(Oid element_type,
  * be pointers into the array object.
  *
  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
- * from the system catalogs, given the elmtype.  However, in most current
- * uses the type is hard-wired into the caller and so we can save a lookup
- * cycle by hard-wiring the type info as well.
+ * from the system catalogs, given the elmtype.  However, the caller is
+ * in a better position to cache this info across multiple uses, or even
+ * to hard-wire values if the element type is hard-wired.
  */
 void
 deconstruct_array(ArrayType *array,
@@ -3548,6 +3630,71 @@ deconstruct_array(ArrayType *array,
 	}
 }
 
+/*
+ * Like deconstruct_array(), where elmtype must be a built-in type, and
+ * elmlen/elmbyval/elmalign is looked up from hardcoded data.  This is often
+ * useful when manipulating arrays from/for system catalogs.
+ */
+void
+deconstruct_array_builtin(ArrayType *array,
+						  Oid elmtype,
+						  Datum **elemsp, bool **nullsp, int *nelemsp)
+{
+	int			elmlen;
+	bool		elmbyval;
+	char		elmalign;
+
+	switch (elmtype)
+	{
+		case CHAROID:
+			elmlen = 1;
+			elmbyval = true;
+			elmalign = TYPALIGN_CHAR;
+			break;
+
+		case CSTRINGOID:
+			elmlen = -2;
+			elmbyval = false;
+			elmalign = TYPALIGN_CHAR;
+			break;
+
+		case FLOAT8OID:
+			elmlen = sizeof(float8);
+			elmbyval = FLOAT8PASSBYVAL;
+			elmalign = TYPALIGN_DOUBLE;
+			break;
+
+		case INT2OID:
+			elmlen = sizeof(int16);
+			elmbyval = true;
+			elmalign = TYPALIGN_SHORT;
+			break;
+
+		case OIDOID:
+			elmlen = sizeof(Oid);
+			elmbyval = true;
+			elmalign = TYPALIGN_INT;
+			break;
+
+		case TEXTOID:
+			elmlen = -1;
+			elmbyval = false;
+			elmalign = TYPALIGN_INT;
+			break;
+
+		case TIDOID:
+			elmlen = sizeof(ItemPointerData);
+			elmbyval = false;
+			elmalign = TYPALIGN_SHORT;
+			break;
+
+		default:
+			elog(ERROR, "type %u not supported here", elmtype);
+	}
+
+	deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
+}
+
 /*
  * array_contains_nulls --- detect whether an array has any null elements
  *
diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c
index 464a37641e..051169a149 100644
--- a/src/backend/utils/adt/arrayutils.c
+++ b/src/backend/utils/adt/arrayutils.c
@@ -249,10 +249,7 @@ ArrayGetIntegerTypmods(ArrayType *arr, int *n)
 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 				 errmsg("typmod array must not contain nulls")));
 
-	/* hardwired knowledge about cstring's representation details here */
-	deconstruct_array(arr, CSTRINGOID,
-					  -2, false, TYPALIGN_CHAR,
-					  &elem_values, NULL, n);
+	deconstruct_array_builtin(arr, CSTRINGOID, &elem_values, NULL, n);
 
 	result = (int32 *) palloc(*n * sizeof(int32));
 
diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c
index 9fe7b62c9a..598259718c 100644
--- a/src/backend/utils/adt/hbafuncs.c
+++ b/src/backend/utils/adt/hbafuncs.c
@@ -151,7 +151,7 @@ get_hba_options(HbaLine *hba)
 	Assert(noptions <= MAX_HBA_OPTIONS);
 
 	if (noptions > 0)
-		return construct_array(options, noptions, TEXTOID, -1, false, TYPALIGN_INT);
+		return construct_array_builtin(options, noptions, TEXTOID);
 	else
 		return NULL;
 }
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 63649ba735..73dd35f6ec 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -1447,9 +1447,7 @@ json_object(PG_FUNCTION_ARGS)
 					 errmsg("wrong number of array subscripts")));
 	}
 
-	deconstruct_array(in_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &in_datums, &in_nulls, &in_count);
+	deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
 
 	count = in_count / 2;
 
@@ -1523,13 +1521,8 @@ json_object_two_arg(PG_FUNCTION_ARGS)
 	if (nkdims == 0)
 		PG_RETURN_DATUM(CStringGetTextDatum("{}"));
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &key_count);
-
-	deconstruct_array(val_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &val_datums, &val_nulls, &val_count);
+	deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
+	deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
 
 	if (key_count != val_count)
 		ereport(ERROR,
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 26d81366c9..f70576f1c0 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1376,9 +1376,7 @@ jsonb_object(PG_FUNCTION_ARGS)
 					 errmsg("wrong number of array subscripts")));
 	}
 
-	deconstruct_array(in_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &in_datums, &in_nulls, &in_count);
+	deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
 
 	count = in_count / 2;
 
@@ -1464,13 +1462,8 @@ jsonb_object_two_arg(PG_FUNCTION_ARGS)
 	if (nkdims == 0)
 		goto close_object;
 
-	deconstruct_array(key_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &key_count);
-
-	deconstruct_array(val_array,
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &val_datums, &val_nulls, &val_count);
+	deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
+	deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
 
 	if (key_count != val_count)
 		ereport(ERROR,
diff --git a/src/backend/utils/adt/jsonb_gin.c b/src/backend/utils/adt/jsonb_gin.c
index 5edf278cdb..c5325acde4 100644
--- a/src/backend/utils/adt/jsonb_gin.c
+++ b/src/backend/utils/adt/jsonb_gin.c
@@ -885,9 +885,7 @@ gin_extract_jsonb_query(PG_FUNCTION_ARGS)
 		int			i,
 					j;
 
-		deconstruct_array(query,
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &key_datums, &key_nulls, &key_count);
+		deconstruct_array_builtin(query, TEXTOID, &key_datums, &key_nulls, &key_count);
 
 		entries = (Datum *) palloc(sizeof(Datum) * key_count);
 
diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c
index ed37252e7f..202367e996 100644
--- a/src/backend/utils/adt/jsonb_op.c
+++ b/src/backend/utils/adt/jsonb_op.c
@@ -53,8 +53,7 @@ jsonb_exists_any(PG_FUNCTION_ARGS)
 	bool	   *key_nulls;
 	int			elem_count;
 
-	deconstruct_array(keys, TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &elem_count);
+	deconstruct_array_builtin(keys, TEXTOID, &key_datums, &key_nulls, &elem_count);
 
 	for (i = 0; i < elem_count; i++)
 	{
@@ -86,8 +85,7 @@ jsonb_exists_all(PG_FUNCTION_ARGS)
 	bool	   *key_nulls;
 	int			elem_count;
 
-	deconstruct_array(keys, TEXTOID, -1, false, TYPALIGN_INT,
-					  &key_datums, &key_nulls, &elem_count);
+	deconstruct_array_builtin(keys, TEXTOID, &key_datums, &key_nulls, &elem_count);
 
 	for (i = 0; i < elem_count; i++)
 	{
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index d1356d6416..2a3f656899 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1000,8 +1000,7 @@ get_path_all(FunctionCallInfo fcinfo, bool as_text)
 	if (array_contains_nulls(path))
 		PG_RETURN_NULL();
 
-	deconstruct_array(path, TEXTOID, -1, false, TYPALIGN_INT,
-					  &pathtext, &pathnulls, &npath);
+	deconstruct_array_builtin(path, TEXTOID, &pathtext, &pathnulls, &npath);
 
 	tpath = palloc(npath * sizeof(char *));
 	ipath = palloc(npath * sizeof(int));
@@ -1456,8 +1455,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
 	if (array_contains_nulls(path))
 		PG_RETURN_NULL();
 
-	deconstruct_array(path, TEXTOID, -1, false, TYPALIGN_INT,
-					  &pathtext, &pathnulls, &npath);
+	deconstruct_array_builtin(path, TEXTOID, &pathtext, &pathnulls, &npath);
 
 	res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
 
@@ -4369,8 +4367,7 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
 	if (JB_ROOT_COUNT(in) == 0)
 		PG_RETURN_JSONB_P(in);
 
-	deconstruct_array(keys, TEXTOID, -1, false, TYPALIGN_INT,
-					  &keys_elems, &keys_nulls, &keys_len);
+	deconstruct_array_builtin(keys, TEXTOID, &keys_elems, &keys_nulls, &keys_len);
 
 	if (keys_len == 0)
 		PG_RETURN_JSONB_P(in);
@@ -4522,8 +4519,7 @@ jsonb_set(PG_FUNCTION_ARGS)
 	if (JB_ROOT_COUNT(in) == 0 && !create)
 		PG_RETURN_JSONB_P(in);
 
-	deconstruct_array(path, TEXTOID, -1, false, TYPALIGN_INT,
-					  &path_elems, &path_nulls, &path_len);
+	deconstruct_array_builtin(path, TEXTOID, &path_elems, &path_nulls, &path_len);
 
 	if (path_len == 0)
 		PG_RETURN_JSONB_P(in);
@@ -4634,8 +4630,7 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
 	if (JB_ROOT_COUNT(in) == 0)
 		PG_RETURN_JSONB_P(in);
 
-	deconstruct_array(path, TEXTOID, -1, false, TYPALIGN_INT,
-					  &path_elems, &path_nulls, &path_len);
+	deconstruct_array_builtin(path, TEXTOID, &path_elems, &path_nulls, &path_len);
 
 	if (path_len == 0)
 		PG_RETURN_JSONB_P(in);
@@ -4680,8 +4675,7 @@ jsonb_insert(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("cannot set path in scalar")));
 
-	deconstruct_array(path, TEXTOID, -1, false, TYPALIGN_INT,
-					  &path_elems, &path_nulls, &path_len);
+	deconstruct_array_builtin(path, TEXTOID, &path_elems, &path_nulls, &path_len);
 
 	if (path_len == 0)
 		PG_RETURN_JSONB_P(in);
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 023a004ac8..dedee7af5c 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -538,10 +538,7 @@ pg_blocking_pids(PG_FUNCTION_ARGS)
 	/* Assert we didn't overrun arrayelems[] */
 	Assert(narrayelems <= lockData->nlocks);
 
-	/* Construct array, using hardwired knowledge about int4 type */
-	PG_RETURN_ARRAYTYPE_P(construct_array(arrayelems, narrayelems,
-										  INT4OID,
-										  sizeof(int32), true, TYPALIGN_INT));
+	PG_RETURN_ARRAYTYPE_P(construct_array_builtin(arrayelems, narrayelems, INT4OID));
 }
 
 
@@ -579,10 +576,7 @@ pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
 	else
 		blocker_datums = NULL;
 
-	/* Construct array, using hardwired knowledge about int4 type */
-	PG_RETURN_ARRAYTYPE_P(construct_array(blocker_datums, num_blockers,
-										  INT4OID,
-										  sizeof(int32), true, TYPALIGN_INT));
+	PG_RETURN_ARRAYTYPE_P(construct_array_builtin(blocker_datums, num_blockers, INT4OID));
 }
 
 
diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c
index e8bba3670c..d22e1f277b 100644
--- a/src/backend/utils/adt/name.c
+++ b/src/backend/utils/adt/name.c
@@ -314,11 +314,7 @@ current_schemas(PG_FUNCTION_ARGS)
 	}
 	list_free(search_path);
 
-	array = construct_array(names, i,
-							NAMEOID,
-							NAMEDATALEN,	/* sizeof(Name) */
-							false,	/* Name is not by-val */
-							TYPALIGN_CHAR); /* alignment of Name */
+	array = construct_array_builtin(names, i, NAMEOID);
 
 	PG_RETURN_POINTER(array);
 }
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index 6d4f6b7dca..185b2cb848 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -759,12 +759,10 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 	param = PG_GETARG_ARRAYTYPE_P(1);
 
-	deconstruct_array(param, FLOAT8OID,
-	/* hard-wired info on type float8 */
-					  sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE,
-					  &percentiles_datum,
-					  &percentiles_null,
-					  &num_percentiles);
+	deconstruct_array_builtin(param, FLOAT8OID,
+							  &percentiles_datum,
+							  &percentiles_null,
+							  &num_percentiles);
 
 	if (num_percentiles == 0)
 		PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
@@ -883,12 +881,10 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
 		PG_RETURN_NULL();
 	param = PG_GETARG_ARRAYTYPE_P(1);
 
-	deconstruct_array(param, FLOAT8OID,
-	/* hard-wired info on type float8 */
-					  sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE,
-					  &percentiles_datum,
-					  &percentiles_null,
-					  &num_percentiles);
+	deconstruct_array_builtin(param, FLOAT8OID,
+							  &percentiles_datum,
+							  &percentiles_null,
+							  &num_percentiles);
 
 	if (num_percentiles == 0)
 		PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 67b9675e92..65764d78a3 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -214,9 +214,7 @@ binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS)
 		int			ndatums;
 		int			i;
 
-		deconstruct_array(textArray,
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &textDatums, NULL, &ndatums);
+		deconstruct_array_builtin(textArray, TEXTOID, &textDatums, NULL, &ndatums);
 		for (i = 0; i < ndatums; i++)
 		{
 			char	   *extName = TextDatumGetCString(textDatums[i]);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5d49f564a2..4d714666d5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2399,9 +2399,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 					if (isnull)
 						elog(ERROR, "null indkey for index %u", indexId);
 
-					deconstruct_array(DatumGetArrayTypeP(cols),
-									  INT2OID, 2, true, TYPALIGN_SHORT,
-									  &keys, NULL, &nKeys);
+					deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
+											  &keys, NULL, &nKeys);
 
 					for (j = keyatts; j < nKeys; j++)
 					{
@@ -2524,9 +2523,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 					elog(ERROR, "null conexclop for constraint %u",
 						 constraintId);
 
-				deconstruct_array(DatumGetArrayTypeP(val),
-								  OIDOID, sizeof(Oid), true, TYPALIGN_INT,
-								  &elems, NULL, &nElems);
+				deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
+										  &elems, NULL, &nElems);
 
 				operators = (Oid *) palloc(nElems * sizeof(Oid));
 				for (i = 0; i < nElems; i++)
@@ -2580,9 +2578,8 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
 	int			j;
 
 	/* Extract data from array of int16 */
-	deconstruct_array(DatumGetArrayTypeP(column_index_array),
-					  INT2OID, 2, true, TYPALIGN_SHORT,
-					  &keys, NULL, &nKeys);
+	deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
+							  &keys, NULL, &nKeys);
 
 	for (j = 0; j < nKeys; j++)
 	{
@@ -12715,9 +12712,8 @@ get_reloptions(StringInfo buf, Datum reloptions)
 	int			noptions;
 	int			i;
 
-	deconstruct_array(DatumGetArrayTypeP(reloptions),
-					  TEXTOID, -1, false, TYPALIGN_INT,
-					  &options, NULL, &noptions);
+	deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
+							  &options, NULL, &noptions);
 
 	for (i = 0; i < noptions; i++)
 	{
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index addc349151..1786c18f89 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -308,8 +308,7 @@ tsvector_setweight_by_filter(PG_FUNCTION_ARGS)
 	memcpy(tsout, tsin, VARSIZE(tsin));
 	entry = ARRPTR(tsout);
 
-	deconstruct_array(lexemes, TEXTOID, -1, false, TYPALIGN_INT,
-					  &dlexemes, &nulls, &nlexemes);
+	deconstruct_array_builtin(lexemes, TEXTOID, &dlexemes, &nulls, &nlexemes);
 
 	/*
 	 * Assuming that lexemes array is significantly shorter than tsvector we
@@ -586,8 +585,7 @@ tsvector_delete_arr(PG_FUNCTION_ARGS)
 	Datum	   *dlexemes;
 	bool	   *nulls;
 
-	deconstruct_array(lexemes, TEXTOID, -1, false, TYPALIGN_INT,
-					  &dlexemes, &nulls, &nlex);
+	deconstruct_array_builtin(lexemes, TEXTOID, &dlexemes, &nulls, &nlex);
 
 	/*
 	 * In typical use case array of lexemes to delete is relatively small. So
@@ -694,10 +692,8 @@ tsvector_unnest(PG_FUNCTION_ARGS)
 																	  1));
 			}
 
-			values[1] = PointerGetDatum(construct_array(positions, posv->npos,
-														INT2OID, 2, true, TYPALIGN_SHORT));
-			values[2] = PointerGetDatum(construct_array(weights, posv->npos,
-														TEXTOID, -1, false, TYPALIGN_INT));
+			values[1] = PointerGetDatum(construct_array_builtin(positions, posv->npos, INT2OID));
+			values[2] = PointerGetDatum(construct_array_builtin(weights, posv->npos, TEXTOID));
 		}
 		else
 		{
@@ -733,7 +729,7 @@ tsvector_to_array(PG_FUNCTION_ARGS)
 															   arrin[i].len));
 	}
 
-	array = construct_array(elements, tsin->size, TEXTOID, -1, false, TYPALIGN_INT);
+	array = construct_array_builtin(elements, tsin->size, TEXTOID);
 
 	pfree(elements);
 	PG_FREE_IF_COPY(tsin, 0);
@@ -757,7 +753,7 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 				datalen = 0;
 	char	   *cur;
 
-	deconstruct_array(v, TEXTOID, -1, false, TYPALIGN_INT, &dlexemes, &nulls, &nitems);
+	deconstruct_array_builtin(v, TEXTOID, &dlexemes, &nulls, &nitems);
 
 	/*
 	 * Reject nulls and zero length strings (maybe we should just ignore them,
@@ -833,8 +829,7 @@ tsvector_filter(PG_FUNCTION_ARGS)
 	int			cur_pos = 0;
 	char		mask = 0;
 
-	deconstruct_array(weights, CHAROID, 1, true, TYPALIGN_CHAR,
-					  &dweights, &nulls, &nweights);
+	deconstruct_array_builtin(weights, CHAROID, &dweights, &nulls, &nweights);
 
 	for (i = 0; i < nweights; i++)
 	{
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 1ec6f1c2fd..60a85c4697 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4017,9 +4017,9 @@ xpath_internal(text *xpath_expr_text, xmltype *data, ArrayType *namespaces,
 
 		Assert(ARR_ELEMTYPE(namespaces) == TEXTOID);
 
-		deconstruct_array(namespaces, TEXTOID, -1, false, TYPALIGN_INT,
-						  &ns_names_uris, &ns_names_uris_nulls,
-						  &ns_count);
+		deconstruct_array_builtin(namespaces, TEXTOID,
+								  &ns_names_uris, &ns_names_uris_nulls,
+								  &ns_count);
 
 		Assert((ns_count % 2) == 0);	/* checked above */
 		ns_count /= 2;			/* count pairs only */
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 3a9c9f0c50..f7f7165f7f 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -228,8 +228,7 @@ DecodeTextArrayToBitmapset(Datum array)
 
 	if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID)
 		elog(ERROR, "expected 1-D text array");
-	deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,
-					  &elems, NULL, &nelems);
+	deconstruct_array_builtin(arr, TEXTOID, &elems, NULL, &nelems);
 
 	for (bms = NULL, i = 0; i < nelems; ++i)
 	{
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 9197b0f1e2..a1fe50ffca 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1390,9 +1390,8 @@ get_func_arg_info(HeapTuple procTup,
 		*p_argnames = NULL;
 	else
 	{
-		deconstruct_array(DatumGetArrayTypeP(proargnames),
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &elems, NULL, &nelems);
+		deconstruct_array_builtin(DatumGetArrayTypeP(proargnames), TEXTOID,
+								  &elems, NULL, &nelems);
 		if (nelems != numargs)	/* should not happen */
 			elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
 		*p_argnames = (char **) palloc(sizeof(char *) * numargs);
@@ -1506,8 +1505,7 @@ get_func_input_arg_names(Datum proargnames, Datum proargmodes,
 		ARR_HASNULL(arr) ||
 		ARR_ELEMTYPE(arr) != TEXTOID)
 		elog(ERROR, "proargnames is not a 1-D text array or it contains nulls");
-	deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,
-					  &argnames, NULL, &numargs);
+	deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &numargs);
 	if (proargmodes != PointerGetDatum(NULL))
 	{
 		arr = DatumGetArrayTypeP(proargmodes);	/* ensure not toasted */
@@ -1621,8 +1619,7 @@ get_func_result_name(Oid functionId)
 			ARR_ELEMTYPE(arr) != TEXTOID)
 			elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls",
 				 numargs);
-		deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,
-						  &argnames, NULL, &nargnames);
+		deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames);
 		Assert(nargnames == numargs);
 
 		/* scan for output argument(s) */
@@ -1770,8 +1767,7 @@ build_function_result_tupdesc_d(char prokind,
 			ARR_ELEMTYPE(arr) != TEXTOID)
 			elog(ERROR, "proargnames is not a 1-D text array of length %d or it contains nulls",
 				 numargs);
-		deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,
-						  &argnames, NULL, &nargnames);
+		deconstruct_array_builtin(arr, TEXTOID, &argnames, NULL, &nargnames);
 		Assert(nargnames == numargs);
 	}
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 8e9b71375c..23f2cb4f8e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -9958,7 +9958,7 @@ pg_settings_get_flags(PG_FUNCTION_ARGS)
 	Assert(cnt <= MAX_GUC_FLAGS);
 
 	/* Returns the record as Datum */
-	a = construct_array(flags, cnt, TEXTOID, -1, false, TYPALIGN_INT);
+	a = construct_array_builtin(flags, cnt, TEXTOID);
 	PG_RETURN_ARRAYTYPE_P(a);
 }
 
@@ -11491,9 +11491,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
 					  TYPALIGN_INT /* TEXT's typalign */ );
 	}
 	else
-		a = construct_array(&datum, 1,
-							TEXTOID,
-							-1, false, TYPALIGN_INT);
+		a = construct_array_builtin(&datum, 1, TEXTOID);
 
 	return a;
 }
@@ -11560,9 +11558,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
 								 false /* TEXT's typbyval */ ,
 								 TYPALIGN_INT /* TEXT's typalign */ );
 		else
-			newarray = construct_array(&d, 1,
-									   TEXTOID,
-									   -1, false, TYPALIGN_INT);
+			newarray = construct_array_builtin(&d, 1, TEXTOID);
 
 		index++;
 	}
@@ -11628,9 +11624,7 @@ GUCArrayReset(ArrayType *array)
 								 false /* TEXT's typbyval */ ,
 								 TYPALIGN_INT /* TEXT's typalign */ );
 		else
-			newarray = construct_array(&d, 1,
-									   TEXTOID,
-									   -1, false, TYPALIGN_INT);
+			newarray = construct_array_builtin(&d, 1, TEXTOID);
 
 		index++;
 		pfree(val);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 656c766a9a..2f794d1168 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -387,6 +387,7 @@ extern void array_bitmap_copy(bits8 *destbitmap, int destoffset,
 extern ArrayType *construct_array(Datum *elems, int nelems,
 								  Oid elmtype,
 								  int elmlen, bool elmbyval, char elmalign);
+extern ArrayType *construct_array_builtin(Datum *elems, int nelems, Oid elmtype);
 extern ArrayType *construct_md_array(Datum *elems,
 									 bool *nulls,
 									 int ndims,
@@ -401,6 +402,9 @@ extern void deconstruct_array(ArrayType *array,
 							  Oid elmtype,
 							  int elmlen, bool elmbyval, char elmalign,
 							  Datum **elemsp, bool **nullsp, int *nelemsp);
+extern void deconstruct_array_builtin(ArrayType *array,
+									  Oid elmtype,
+									  Datum **elemsp, bool **nullsp, int *nelemsp);
 extern bool array_contains_nulls(ArrayType *array);
 
 extern ArrayBuildState *initArrayResult(Oid element_type,
diff --git a/src/test/modules/test_rls_hooks/test_rls_hooks.c b/src/test/modules/test_rls_hooks/test_rls_hooks.c
index 4f862d081b..41bfc776ae 100644
--- a/src/test/modules/test_rls_hooks/test_rls_hooks.c
+++ b/src/test/modules/test_rls_hooks/test_rls_hooks.c
@@ -87,7 +87,7 @@ test_rls_hooks_permissive(CmdType cmdtype, Relation relation)
 
 	policy->policy_name = pstrdup("extension policy");
 	policy->polcmd = '*';
-	policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+	policy->roles = construct_array_builtin(&role, 1, OIDOID);
 
 	/*
 	 * policy->qual = (Expr *) makeConst(BOOLOID, -1, InvalidOid,
@@ -155,7 +155,7 @@ test_rls_hooks_restrictive(CmdType cmdtype, Relation relation)
 
 	policy->policy_name = pstrdup("extension policy");
 	policy->polcmd = '*';
-	policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true, TYPALIGN_INT);
+	policy->roles = construct_array_builtin(&role, 1, OIDOID);
 
 	n = makeFuncCall(list_make2(makeString("pg_catalog"),
 								makeString("current_user")),
-- 
2.35.1

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#1)
Re: Refactor construct_array() and deconstruct_array() for built-in types

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

There are many calls to construct_array() and deconstruct_array() for
built-in types, for example, when dealing with system catalog columns.
These all hardcode the type attributes necessary to pass to these functions.

To simplify this a bit, add construct_array_builtin(),
deconstruct_array_builtin() as wrappers that centralize this hardcoded
knowledge. This simplifies many call sites and reduces the amount of
hardcoded stuff that is spread around.

I also considered having genbki.pl generate lookup tables for these
hardcoded values, similar to schemapg.h, but that ultimately seemed
excessive.

+1 --- the added overhead of the switch statements is probably a
reasonable price to pay for the notational simplification and
bug-proofing.

One minor coding gripe is that compilers that don't know that elog(ERROR)
doesn't return will certainly generate "use of possibly-uninitialized
variable" complaints. Suggest inserting "return NULL;" or similar into
the default: cases. I'd also use more specific error wording to help
people find where they need to add code when they make use of a new type;
maybe like "type %u not supported by construct_array_builtin".

regards, tom lane

#3Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#2)
Re: Refactor construct_array() and deconstruct_array() for built-in types

On 02.05.22 16:48, Tom Lane wrote:

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

There are many calls to construct_array() and deconstruct_array() for
built-in types, for example, when dealing with system catalog columns.
These all hardcode the type attributes necessary to pass to these functions.

To simplify this a bit, add construct_array_builtin(),
deconstruct_array_builtin() as wrappers that centralize this hardcoded
knowledge. This simplifies many call sites and reduces the amount of
hardcoded stuff that is spread around.

I also considered having genbki.pl generate lookup tables for these
hardcoded values, similar to schemapg.h, but that ultimately seemed
excessive.

+1 --- the added overhead of the switch statements is probably a
reasonable price to pay for the notational simplification and
bug-proofing.

One minor coding gripe is that compilers that don't know that elog(ERROR)
doesn't return will certainly generate "use of possibly-uninitialized
variable" complaints. Suggest inserting "return NULL;" or similar into
the default: cases. I'd also use more specific error wording to help
people find where they need to add code when they make use of a new type;
maybe like "type %u not supported by construct_array_builtin".

I have pushed this with the improvements you had suggested.

In reply to: Peter Eisentraut (#3)
Re: Refactor construct_array() and deconstruct_array() for built-in types

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

On 02.05.22 16:48, Tom Lane wrote:

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

There are many calls to construct_array() and deconstruct_array() for
built-in types, for example, when dealing with system catalog columns.
These all hardcode the type attributes necessary to pass to these functions.

To simplify this a bit, add construct_array_builtin(),
deconstruct_array_builtin() as wrappers that centralize this hardcoded
knowledge. This simplifies many call sites and reduces the amount of
hardcoded stuff that is spread around.

I also considered having genbki.pl generate lookup tables for these
hardcoded values, similar to schemapg.h, but that ultimately seemed
excessive.

+1 --- the added overhead of the switch statements is probably a
reasonable price to pay for the notational simplification and
bug-proofing.
One minor coding gripe is that compilers that don't know that
elog(ERROR)
doesn't return will certainly generate "use of possibly-uninitialized
variable" complaints. Suggest inserting "return NULL;" or similar into
the default: cases. I'd also use more specific error wording to help
people find where they need to add code when they make use of a new type;
maybe like "type %u not supported by construct_array_builtin".

I have pushed this with the improvements you had suggested.

I dind't pay much attention to this thread earlier, but I was struck by
the duplication of the switch statement determining the elemlen,
elembyval, and elemalign values between the construct and deconstruct
functions. How about a common function they can both call? Something
like:

static void builtin_type_details(Oid elemtype,
int *elemlen,
bool *elembyval,
char *elemalign);

- ilmari

In reply to: Dagfinn Ilmari Mannsåker (#4)
Re: Refactor construct_array() and deconstruct_array() for built-in types

Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> writes:

I dind't pay much attention to this thread earlier, but I was struck by
the duplication of the switch statement determining the elemlen,
elembyval, and elemalign values between the construct and deconstruct
functions. How about a common function they can both call? Something
like:

static void builtin_type_details(Oid elemtype,
int *elemlen,
bool *elembyval,
char *elemalign);

I just realised that this would require the error message to not include
the function name (which isn't really that critical, since it's a
developer-facing message), but an option would to make it return false
for unknown types, so each of the calling functions can emit their own
error message.

Show quoted text

- ilmari

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dagfinn Ilmari Mannsåker (#5)
Re: Refactor construct_array() and deconstruct_array() for built-in types

=?utf-8?Q?Dagfinn_Ilmari_Manns=C3=A5ker?= <ilmari@ilmari.org> writes:

Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> writes:

I dind't pay much attention to this thread earlier, but I was struck by
the duplication of the switch statement determining the elemlen,
elembyval, and elemalign values between the construct and deconstruct
functions. How about a common function they can both call?

I was wondering about that too while reading the committed version.
However, adding an additional function call would weaken the argument
that this adds just a tolerable amount of overhead, primarily because
you'd need to return a record or introduce pointers or the like.

I just realised that this would require the error message to not include
the function name (which isn't really that critical, since it's a
developer-facing message), but an option would to make it return false
for unknown types, so each of the calling functions can emit their own
error message.

Nah, because the point of that was just to direct people to where
to fix it when they need to. So the message need only refer to
the common function, if we were to change it.

Perhaps a good compromise could be to turn the duplicated code into
a macro that's instantiated in both places? But I don't actually
see anything much wrong with the code as Peter has it.

regards, tom lane

#7Peter Eisentraut
peter.eisentraut@enterprisedb.com
In reply to: Tom Lane (#6)
Re: Refactor construct_array() and deconstruct_array() for built-in types

On 01.07.22 15:37, Tom Lane wrote:

Perhaps a good compromise could be to turn the duplicated code into
a macro that's instantiated in both places? But I don't actually
see anything much wrong with the code as Peter has it.

There are opportunities to refine this further. For example, there is
similar code in TupleDescInitBuiltinEntry(), and bootstrap.c also
contains hardcoded info on built-in types, and GetCCHashEqFuncs() is
also loosely related. As I mentioned earlier in the thread, one could
have genbki.pl generate support code for this.