arrayelemhdr.patch

text/plain

Filename: arrayelemhdr.patch
Type: text/plain
Part: 0
Message: Re: stored short varlena in array

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
contrib/hstore/hstore_gin.c 1 1
contrib/hstore/hstore_gist.c 2 2
contrib/hstore/hstore_io.c 12 12
contrib/hstore/hstore_op.c 9 6
contrib/ltree/lquery_op.c 9 2
contrib/ltree/_ltree_gist.c 16 3
contrib/ltree/ltree_gist.c 13 2
contrib/ltree/ltree.h 24 0
contrib/ltree/_ltree_op.c 34 6
src/backend/access/common/reloptions.c 4 4
src/backend/access/gin/ginarrayproc.c 8 6
src/backend/access/nbtree/nbtutils.c 4 3
src/backend/bootstrap/bootstrap.c 99 0
src/backend/catalog/objectaddress.c 2 1
src/backend/catalog/pg_attrdef.c 2 1
src/backend/commands/analyze.c 2 1
src/backend/commands/extension.c 4 2
src/backend/commands/tablecmds.c 3 1
src/backend/executor/execExpr.c 5 4
src/backend/executor/execExprInterp.c 2 1
src/backend/executor/nodeIndexscan.c 4 3
src/backend/optimizer/path/indxpath.c 3 2
src/backend/optimizer/util/predtest.c 4 3
src/backend/partitioning/partbounds.c 5 2
src/backend/partitioning/partprune.c 4 3
src/backend/statistics/extended_stats.c 4 1
src/backend/statistics/mcv.c 4 3
src/backend/utils/adt/array_expanded.c 12 4
src/backend/utils/adt/arrayfuncs.c 206 129
src/backend/utils/adt/array_selfuncs.c 1 0
src/backend/utils/adt/arraysubs.c 16 8
src/backend/utils/adt/array_typanalyze.c 4 0
src/backend/utils/adt/array_userfuncs.c 31 16
src/backend/utils/adt/enum.c 1 1
src/backend/utils/adt/jsonb.c 4 3
src/backend/utils/adt/json.c 4 3
src/backend/utils/adt/multirangetypes.c 2 1
src/backend/utils/adt/orderedsetaggs.c 13 7
src/backend/utils/adt/rangetypes.c 1 4
src/backend/utils/adt/regexp.c 2 1
src/backend/utils/adt/ruleutils.c 1 0
src/backend/utils/adt/selfuncs.c 8 6
src/backend/utils/adt/tsvector_op.c 8 8
src/backend/utils/adt/varlena.c 5 7
src/backend/utils/adt/xml.c 4 2
src/backend/utils/cache/lsyscache.c 82 0
src/backend/utils/cache/relcache.c 1 0
src/backend/utils/fmgr/funcapi.c 4 3
src/backend/utils/misc/guc.c 10 3
src/include/access/tupmacs.h 29 0
src/include/bootstrap/bootstrap.h 6 0
src/include/catalog/pg_type.h 4 0
src/include/commands/vacuum.h 1 0
src/include/executor/execExpr.h 1 0
src/include/utils/array.h 20 11
src/include/utils/lsyscache.h 6 0
src/include/utils/palloc.h 6 0
src/pl/plperl/plperl.c 5 2
src/pl/plpgsql/src/pl_exec.c 3 1
src/test/modules/test_regex/test_regex.c 4 2
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 766c00bb6a7..b6bd617df45 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -127,7 +127,7 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
 			/* Nulls in the array are ignored, cf hstoreArrayToPairs */
 			if (key_nulls[i])
 				continue;
-			item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
+			item = makeitem(VARDATA_ANY(key_datums[i]), VARSIZE_ANY_EXHDR(key_datums[i]), KEYFLAG);
 			entries[j++] = PointerGetDatum(item);
 		}
 
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index a3b08af3850..9e330d7be4f 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -576,7 +576,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 			if (key_nulls[i])
 				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
+			crc = crc32_sz(VARDATA_ANY(key_datums[i]), VARSIZE_ANY_EXHDR(key_datums[i]));
 			if (!(GETBIT(sign, HASHVAL(crc, siglen))))
 				res = false;
 		}
@@ -599,7 +599,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 			if (key_nulls[i])
 				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
+			crc = crc32_sz(VARDATA_ANY(key_datums[i]), VARSIZE_ANY_EXHDR(key_datums[i]));
 			if (GETBIT(sign, HASHVAL(crc, siglen)))
 				res = true;
 		}
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 2125436e40c..51461a6ec32 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -681,22 +681,22 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 
 		if (!value_nulls || value_nulls[i])
 		{
-			pairs[i].key = VARDATA(key_datums[i]);
+			pairs[i].key = VARDATA_ANY(key_datums[i]);
 			pairs[i].val = NULL;
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
 			pairs[i].vallen = 4;
 			pairs[i].isnull = true;
 			pairs[i].needfree = false;
 		}
 		else
 		{
-			pairs[i].key = VARDATA(key_datums[i]);
-			pairs[i].val = VARDATA(value_datums[i]);
+			pairs[i].key = VARDATA_ANY(key_datums[i]);
+			pairs[i].val = VARDATA_ANY(value_datums[i]);
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
 			pairs[i].vallen =
-				hstoreCheckValLen(VARSIZE(value_datums[i]) - VARHDRSZ);
+				hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i]));
 			pairs[i].isnull = false;
 			pairs[i].needfree = false;
 		}
@@ -775,22 +775,22 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 		if (in_nulls[i * 2 + 1])
 		{
-			pairs[i].key = VARDATA(in_datums[i * 2]);
+			pairs[i].key = VARDATA_ANY(in_datums[i * 2]);
 			pairs[i].val = NULL;
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2]));
 			pairs[i].vallen = 4;
 			pairs[i].isnull = true;
 			pairs[i].needfree = false;
 		}
 		else
 		{
-			pairs[i].key = VARDATA(in_datums[i * 2]);
-			pairs[i].val = VARDATA(in_datums[i * 2 + 1]);
+			pairs[i].key = VARDATA_ANY(in_datums[i * 2]);
+			pairs[i].val = VARDATA_ANY(in_datums[i * 2 + 1]);
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2]));
 			pairs[i].vallen =
-				hstoreCheckValLen(VARSIZE(in_datums[i * 2 + 1]) - VARHDRSZ);
+				hstoreCheckValLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1]));
 			pairs[i].isnull = false;
 			pairs[i].needfree = false;
 		}
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 5e57eceffc8..da6aa12e8c4 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -107,8 +107,8 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
 	{
 		if (!key_nulls[i])
 		{
-			key_pairs[j].key = VARDATA(key_datums[i]);
-			key_pairs[j].keylen = VARSIZE(key_datums[i]) - VARHDRSZ;
+			key_pairs[j].key = VARDATA_ANY(key_datums[i]);
+			key_pairs[j].keylen = VARSIZE_ANY_EXHDR(key_datums[i]);
 			key_pairs[j].val = NULL;
 			key_pairs[j].vallen = 0;
 			key_pairs[j].needfree = 0;
@@ -599,7 +599,7 @@ hstore_slice_to_array(PG_FUNCTION_ARGS)
 		if (key_nulls[i])
 			idx = -1;
 		else
-			idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
+			idx = hstoreFindKey(hs, NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
 
 		if (idx < 0 || HSTORE_VALISNULL(entries, idx))
 		{
@@ -619,7 +619,8 @@ hstore_slice_to_array(PG_FUNCTION_ARGS)
 							  ARR_NDIM(key_array),
 							  ARR_DIMS(key_array),
 							  ARR_LBOUND(key_array),
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false,
+							  TYPALIGN_INT, TYPSTORAGE_EXTENDED);
 
 	PG_RETURN_POINTER(aout);
 }
@@ -762,7 +763,8 @@ hstore_avals(PG_FUNCTION_ARGS)
 	}
 
 	a = construct_md_array(d, nulls, 1, &count, &lb,
-						   TEXTOID, -1, false, TYPALIGN_INT);
+						   TEXTOID, -1, false,
+						   TYPALIGN_INT, TYPSTORAGE_EXTENDED);
 
 	PG_RETURN_POINTER(a);
 }
@@ -814,7 +816,8 @@ hstore_to_array_internal(HStore *hs, int ndims)
 
 	return construct_md_array(out_datums, out_nulls,
 							  ndims, out_size, lb,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false,
+							  TYPALIGN_INT, TYPSTORAGE_EXTENDED);
 }
 
 PG_FUNCTION_INFO_V1(hstore_to_array);
diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c
index 286ad24fbe8..ce84ee89d8e 100644
--- a/contrib/ltree/_ltree_gist.c
+++ b/contrib/ltree/_ltree_gist.c
@@ -26,7 +26,7 @@ PG_FUNCTION_INFO_V1(_ltree_consistent);
 PG_FUNCTION_INFO_V1(_ltree_gist_options);
 
 #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
-#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE_ANY(x) ) ) )
 
 #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
 
@@ -60,6 +60,7 @@ _ltree_compress(PG_FUNCTION_ARGS)
 		ArrayType  *val = DatumGetArrayTypeP(entry->key);
 		int			num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
 		ltree	   *item = (ltree *) ARR_DATA_PTR(val);
+		ltree	   *newitem;
 
 		if (ARR_NDIM(val) > 1)
 			ereport(ERROR,
@@ -74,7 +75,10 @@ _ltree_compress(PG_FUNCTION_ARGS)
 
 		while (num > 0)
 		{
-			hashing(LTG_SIGN(key), item, siglen);
+			newitem = (ltree *)ltree_norm_short_item((char *)item);
+
+			hashing(LTG_SIGN(key), newitem, siglen);
+			PFREE_IF_NEW(newitem, item);
 			num--;
 			item = NEXTVAL(item);
 		}
@@ -481,6 +485,7 @@ static bool
 _arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
+	lquery	   *newqry;
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
 
 	if (ARR_NDIM(_query) > 1)
@@ -494,8 +499,16 @@ _arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query, siglen))
+		newqry = (lquery *)ltree_norm_short_item((char *)query);
+
+		if (gist_qe(key, newqry, siglen))
+		{
+			PFREE_IF_NEW(newqry, query);
 			return true;
+		}
+
+		PFREE_IF_NEW(newqry, query);
+
 		num--;
 		query = (lquery *) NEXTVAL(query);
 	}
diff --git a/contrib/ltree/_ltree_op.c b/contrib/ltree/_ltree_op.c
index b4a8097328d..274f8c6bd79 100644
--- a/contrib/ltree/_ltree_op.c
+++ b/contrib/ltree/_ltree_op.c
@@ -32,13 +32,14 @@ PG_FUNCTION_INFO_V1(_lca);
 
 typedef Datum (*PGCALL2) (PG_FUNCTION_ARGS);
 
-#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE_ANY(x) ) ) )
 
 static bool
 array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree **found)
 {
 	int			num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la));
 	ltree	   *item = (ltree *) ARR_DATA_PTR(la);
+	ltree	   *newitem;
 
 	if (ARR_NDIM(la) > 1)
 		ereport(ERROR,
@@ -53,14 +54,20 @@ array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree **found)
 		*found = NULL;
 	while (num > 0)
 	{
+		newitem = (ltree *)ltree_norm_short_item((char *)item);
+
 		if (DatumGetBool(DirectFunctionCall2(callback,
-											 PointerGetDatum(item), PointerGetDatum(param))))
+											 PointerGetDatum(newitem), PointerGetDatum(param))))
 		{
+			PFREE_IF_NEW(newitem, item);
 
 			if (found)
 				*found = item;
 			return true;
 		}
+
+		PFREE_IF_NEW(newitem, item);
+
 		num--;
 		item = NEXTVAL(item);
 	}
@@ -137,6 +144,7 @@ _lt_q_regex(PG_FUNCTION_ARGS)
 	ArrayType  *_tree = PG_GETARG_ARRAYTYPE_P(0);
 	ArrayType  *_query = PG_GETARG_ARRAYTYPE_P(1);
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
+	lquery	   *newqry;
 	bool		res = false;
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
 
@@ -151,11 +159,18 @@ _lt_q_regex(PG_FUNCTION_ARGS)
 
 	while (num > 0)
 	{
-		if (array_iterator(_tree, ltq_regex, query, NULL))
+		newqry = (lquery *)ltree_norm_short_item((char *)query);
+
+		if (array_iterator(_tree, ltq_regex, newqry, NULL))
 		{
+			PFREE_IF_NEW(newqry, query);
+
 			res = true;
 			break;
 		}
+
+		PFREE_IF_NEW(newqry, query);
+
 		num--;
 		query = (lquery *) NEXTVAL(query);
 	}
@@ -297,6 +312,8 @@ _lca(PG_FUNCTION_ARGS)
 	ltree	   *item = (ltree *) ARR_DATA_PTR(la);
 	ltree	  **a,
 			   *res;
+	int			i;
+	bool	   *copied;
 
 	if (ARR_NDIM(la) > 1)
 		ereport(ERROR,
@@ -308,14 +325,25 @@ _lca(PG_FUNCTION_ARGS)
 				 errmsg("array must not contain nulls")));
 
 	a = (ltree **) palloc(sizeof(ltree *) * num);
-	while (num > 0)
+	copied = (bool *)palloc(sizeof(bool) * num);
+
+	for (i = 0; i < num; i++)
 	{
-		num--;
-		a[num] = item;
+		a[i] = (ltree *)ltree_norm_short_item((char *)item);
+		copied[i] = (a[i] != item);
 		item = NEXTVAL(item);
 	}
+
 	res = lca_inner(a, ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la)));
+
+	for (i = 0; i < num; i++)
+	{
+		if (copied[i])
+			pfree(a[i]);
+	}
+
 	pfree(a);
+	pfree(copied);
 
 	PG_FREE_IF_COPY(la, 0);
 
diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c
index a6466f575fd..a55ee51de04 100644
--- a/contrib/ltree/lquery_op.c
+++ b/contrib/ltree/lquery_op.c
@@ -19,7 +19,7 @@ PG_FUNCTION_INFO_V1(ltq_rregex);
 PG_FUNCTION_INFO_V1(lt_q_regex);
 PG_FUNCTION_INFO_V1(lt_q_rregex);
 
-#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE_ANY(x) ) ) )
 
 static char *
 getlexeme(char *start, char *end, int *len)
@@ -241,6 +241,7 @@ lt_q_regex(PG_FUNCTION_ARGS)
 	ltree	   *tree = PG_GETARG_LTREE_P(0);
 	ArrayType  *_query = PG_GETARG_ARRAYTYPE_P(1);
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
+	lquery	   *newqry;
 	bool		res = false;
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
 
@@ -255,13 +256,19 @@ lt_q_regex(PG_FUNCTION_ARGS)
 
 	while (num > 0)
 	{
+		newqry = (lquery *)ltree_norm_short_item((char *)query);
+
 		if (DatumGetBool(DirectFunctionCall2(ltq_regex,
-											 PointerGetDatum(tree), PointerGetDatum(query))))
+											 PointerGetDatum(tree), PointerGetDatum(newqry))))
 		{
+			PFREE_IF_NEW(newqry, query);
 
 			res = true;
 			break;
 		}
+
+		PFREE_IF_NEW(newqry, query);
+
 		num--;
 		query = NEXTVAL(query);
 	}
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index 5e0761641d3..28075bd3498 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -6,6 +6,7 @@
 #include "fmgr.h"
 #include "tsearch/ts_locale.h"
 #include "utils/memutils.h"
+#include "varatt.h"
 
 
 /* ltree */
@@ -212,6 +213,29 @@ bool		compare_subnode(ltree_level *t, char *qn, int len,
 ltree	   *lca_inner(ltree **a, int len);
 int			ltree_strncasecmp(const char *a, const char *b, size_t s);
 
+/* normalize ltree/lquery in array if it is short version. */
+static inline char*
+ltree_norm_short_item(char *item)
+{
+	if (VARATT_IS_SHORT(item))
+	{
+		/*
+		* This is a short-header varlena --- convert to 4-byte header format
+		*/
+		char   *newitem;
+		Size	data_size = VARSIZE_SHORT(item) - VARHDRSZ_SHORT;
+		Size	new_size = data_size + VARHDRSZ;
+
+		newitem = palloc(new_size);
+		SET_VARSIZE(newitem, new_size);
+		memcpy(VARDATA(newitem), VARDATA_SHORT(item), data_size);
+
+		return newitem;
+	}
+	else
+		return item;
+}
+
 /* fmgr macros for ltree objects */
 #define DatumGetLtreeP(X)			((ltree *) PG_DETOAST_DATUM(X))
 #define DatumGetLtreePCopy(X)		((ltree *) PG_DETOAST_DATUM_COPY(X))
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 932f69bff2d..5733d5a1c2d 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -12,7 +12,7 @@
 #include "ltree.h"
 #include "utils/array.h"
 
-#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE_ANY(x) ) ) )
 #define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
 
 PG_FUNCTION_INFO_V1(ltree_gist_in);
@@ -592,6 +592,7 @@ static bool
 arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
+	lquery	   *newqry;
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
 
 	if (ARR_NDIM(_query) > 1)
@@ -605,11 +606,21 @@ arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query, siglen) && gist_between(key, query, siglen))
+		newqry = (lquery *)ltree_norm_short_item((char *)query);
+
+		if (gist_qe(key, newqry, siglen) && gist_between(key, newqry, siglen))
+		{
+			PFREE_IF_NEW(newqry, query);
+
 			return true;
+		}
+
+		PFREE_IF_NEW(newqry, query);
+
 		num--;
 		query = NEXTVAL(query);
 	}
+
 	return false;
 }
 
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 49fd35bfc55..26e31a583a4 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1179,8 +1179,8 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
 
 		for (i = 0; i < noldoptions; i++)
 		{
-			char	   *text_str = VARDATA(oldoptions[i]);
-			int			text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
+			char	   *text_str = VARDATA_ANY(oldoptions[i]);
+			int			text_len = VARSIZE_ANY_EXHDR(oldoptions[i]);
 
 			/* Search for a match in defList */
 			foreach(cell, defList)
@@ -1436,8 +1436,8 @@ parseRelOptionsInternal(Datum options, bool validate,
 
 	for (i = 0; i < noptions; i++)
 	{
-		char	   *text_str = VARDATA(optiondatums[i]);
-		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		char	   *text_str = VARDATA_ANY(optiondatums[i]);
+		int			text_len = VARSIZE_ANY_EXHDR(optiondatums[i]);
 		int			j;
 
 		/* Search for a match in reloptions */
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index 2373e76f77e..b8311d21858 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -39,16 +39,17 @@ ginarrayextract(PG_FUNCTION_ARGS)
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 	Datum	   *elems;
 	bool	   *nulls;
 	int			nelems;
 
-	get_typlenbyvalalign(ARR_ELEMTYPE(array),
-						 &elmlen, &elmbyval, &elmalign);
+	get_type_stores(ARR_ELEMTYPE(array),
+						 &elmlen, &elmbyval, &elmalign, &elmstor);
 
 	deconstruct_array(array,
 					  ARR_ELEMTYPE(array),
-					  elmlen, elmbyval, elmalign,
+					  elmlen, elmbyval, elmalign, elmstor,
 					  &elems, &nulls, &nelems);
 
 	*nkeys = nelems;
@@ -90,16 +91,17 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 	Datum	   *elems;
 	bool	   *nulls;
 	int			nelems;
 
-	get_typlenbyvalalign(ARR_ELEMTYPE(array),
-						 &elmlen, &elmbyval, &elmalign);
+	get_type_stores(ARR_ELEMTYPE(array),
+						 &elmlen, &elmbyval, &elmalign, &elmstor);
 
 	deconstruct_array(array,
 					  ARR_ELEMTYPE(array),
-					  elmlen, elmbyval, elmalign,
+					  elmlen, elmbyval, elmalign, elmstor,
 					  &elems, &nulls, &nelems);
 
 	*nkeys = nelems;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index a531d37908a..c3aa979c572 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -340,6 +340,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan, int *new_numberOfKeys)
 		int16		elmlen;
 		bool		elmbyval;
 		char		elmalign;
+		char		elmstor;
 		int			num_elems;
 		Datum	   *elem_values;
 		bool	   *elem_nulls;
@@ -364,11 +365,11 @@ _bt_preprocess_array_keys(IndexScanDesc scan, int *new_numberOfKeys)
 		 */
 		arrayval = DatumGetArrayTypeP(cur->sk_argument);
 		/* We could cache this data, but not clear it's worth it */
-		get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
-							 &elmlen, &elmbyval, &elmalign);
+		get_type_stores(ARR_ELEMTYPE(arrayval),
+							 &elmlen, &elmbyval, &elmalign, &elmstor);
 		deconstruct_array(arrayval,
 						  ARR_ELEMTYPE(arrayval),
-						  elmlen, elmbyval, elmalign,
+						  elmlen, elmbyval, elmalign, elmstor,
 						  &elem_values, &elem_nulls, &num_elems);
 
 		/*
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e0cb70ee9da..79ebac5b78c 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -903,6 +903,105 @@ boot_get_type_io_data(Oid typid,
 	}
 }
 
+/* ----------------
+ *		boot_array_type_metadata
+ *
+ * Obtain type metadata at bootstrap time.  This intentionally has
+ * almost the same API as lsyscache.c's array_type_metadata, except that
+ * we only support obtaining the typinput and typoutput routines, not
+ * the binary I/O routines.  It is exported so that array_in and array_out
+ * can be made to work during early bootstrap.
+ * ----------------
+ */
+void
+boot_array_type_metadata(Oid typid,
+						 IOFuncSelector which_func,
+						 ArrayMetaState *metadata)
+{
+	if (Typ != NIL)
+	{
+		/* We have the boot-time contents of pg_type, so use it */
+		struct typmap *ap = NULL;
+		ListCell   *lc;
+
+		foreach(lc, Typ)
+		{
+			ap = lfirst(lc);
+			if (ap->am_oid == typid)
+				break;
+		}
+
+		if (!ap || ap->am_oid != typid)
+			elog(ERROR, "type OID %u not found in Typ list", typid);
+
+		metadata->typlen = ap->am_typ.typlen;
+		metadata->typbyval = ap->am_typ.typbyval;
+		metadata->typalign = ap->am_typ.typalign;
+		metadata->typstorage = ap->am_typ.typstorage;
+		metadata->typdelim = ap->am_typ.typdelim;
+
+		/* XXX this logic must match getTypeIOParam() */
+		if (OidIsValid(ap->am_typ.typelem))
+			metadata->typioparam = ap->am_typ.typelem;
+		else
+			metadata->typioparam = typid;
+
+		switch (which_func)
+		{
+			case IOFunc_input:
+				metadata->typiofunc = ap->am_typ.typinput;
+				break;
+			case IOFunc_output:
+				metadata->typiofunc = ap->am_typ.typoutput;
+				break;
+			default:
+				elog(ERROR, "binary I/O not supported during bootstrap");
+				break;
+		}
+
+		return;
+	}
+	else
+	{
+		/* We don't have pg_type yet, so use the hard-wired TypInfo array */
+		int			typeindex;
+
+		for (typeindex = 0; typeindex < n_types; typeindex++)
+		{
+			if (TypInfo[typeindex].oid == typid)
+				break;
+		}
+		if (typeindex >= n_types)
+			elog(ERROR, "type OID %u not found in TypInfo", typid);
+
+		metadata->typlen = TypInfo[typeindex].len;
+		metadata->typbyval = TypInfo[typeindex].byval;
+		metadata->typalign = TypInfo[typeindex].align;
+		metadata->typstorage = TypInfo[typeindex].storage;
+		/* We assume typdelim is ',' for all boot-time types */
+		metadata->typdelim = ',';
+
+		/* XXX this logic must match getTypeIOParam() */
+		if (OidIsValid(TypInfo[typeindex].elem))
+			metadata->typioparam = TypInfo[typeindex].elem;
+		else
+			metadata->typioparam = typid;
+
+		switch (which_func)
+		{
+			case IOFunc_input:
+				metadata->typiofunc = TypInfo[typeindex].inproc;
+				break;
+			case IOFunc_output:
+				metadata->typiofunc = TypInfo[typeindex].outproc;
+				break;
+			default:
+				elog(ERROR, "binary I/O not supported during bootstrap");
+				break;
+		}
+	}
+}
+
 /* ----------------
  *		AllocateAttribute
  *
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index be7e4a5dd01..c9e6bf73872 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -6126,7 +6126,8 @@ strlist_to_textarray(List *list)
 
 	lb[0] = 1;
 	arr = construct_md_array(datums, nulls, 1, &j,
-							 lb, TEXTOID, -1, false, TYPALIGN_INT);
+							 lb, TEXTOID, -1, false,
+							 TYPALIGN_INT, TYPSTORAGE_EXTENDED);
 
 	MemoryContextDelete(memcxt);
 
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
index 003ae70b4d2..ce73000e4c7 100644
--- a/src/backend/catalog/pg_attrdef.c
+++ b/src/backend/catalog/pg_attrdef.c
@@ -148,7 +148,8 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 															 defAttStruct->atttypid,
 															 defAttStruct->attlen,
 															 defAttStruct->attbyval,
-															 defAttStruct->attalign));
+															 defAttStruct->attalign,
+															 defAttStruct->attstorage));
 			}
 
 			valuesAtt[Anum_pg_attribute_atthasmissing - 1] = !missingIsNull;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 9a56de2282f..ba449b2c495 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1721,7 +1721,8 @@ update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats)
 									   stats->statypid[k],
 									   stats->statyplen[k],
 									   stats->statypbyval[k],
-									   stats->statypalign[k]);
+									   stats->statypalign[k],
+									   stats->statypstorage[k]);
 				values[i++] = PointerGetDatum(arry);	/* stavaluesN */
 			}
 			else
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index e683c520a82..b51348d65ce 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2654,7 +2654,8 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 					  -1 /* varlena array */ ,
 					  sizeof(Oid) /* OID's typlen */ ,
 					  true /* OID's typbyval */ ,
-					  TYPALIGN_INT /* OID's typalign */ );
+					  TYPALIGN_INT /* OID's typalign */,
+					  TYPSTORAGE_PLAIN /* OID's typstorage */ );
 	}
 	repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
 	repl_repl[Anum_pg_extension_extconfig - 1] = true;
@@ -2690,7 +2691,8 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
 					  -1 /* varlena array */ ,
 					  -1 /* TEXT's typlen */ ,
 					  false /* TEXT's typbyval */ ,
-					  TYPALIGN_INT /* TEXT's typalign */ );
+					  TYPALIGN_INT /* TEXT's typalign */ ,
+					  TYPSTORAGE_EXTENDED /* TEXT's typstorage */ );
 	}
 	repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
 	repl_repl[Anum_pg_extension_extcondition - 1] = true;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 49374782625..b4840d60e0a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -13750,13 +13750,15 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 										   attTup->attlen,
 										   attTup->attbyval,
 										   attTup->attalign,
+										   attTup->attstorage,
 										   &isNull);
 			missingval = PointerGetDatum(construct_array(&missingval,
 														 1,
 														 targettype,
 														 tform->typlen,
 														 tform->typbyval,
-														 tform->typalign));
+														 tform->typalign,
+														 tform->typstorage));
 
 			valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
 			replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 3d01a90bd64..5d73e2b21b0 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -1886,10 +1886,11 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid;
 
 				/* do one-time catalog lookup for type info */
-				get_typlenbyvalalign(arrayexpr->element_typeid,
-									 &scratch.d.arrayexpr.elemlength,
-									 &scratch.d.arrayexpr.elembyval,
-									 &scratch.d.arrayexpr.elemalign);
+				get_type_stores(arrayexpr->element_typeid,
+										&scratch.d.arrayexpr.elemlength,
+										&scratch.d.arrayexpr.elembyval,
+										&scratch.d.arrayexpr.elemalign,
+										&scratch.d.arrayexpr.elemstorage);
 
 				/* prepare to evaluate all arguments */
 				elemoff = 0;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index d2987663e63..9de7f7b523b 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -3211,7 +3211,8 @@ ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
 									element_type,
 									op->d.arrayexpr.elemlength,
 									op->d.arrayexpr.elembyval,
-									op->d.arrayexpr.elemalign);
+									op->d.arrayexpr.elemalign,
+									op->d.arrayexpr.elemstorage);
 	}
 	else
 	{
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 8000feff4c9..7a33d697c35 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -678,6 +678,7 @@ ExecIndexEvalArrayKeys(ExprContext *econtext,
 		int16		elmlen;
 		bool		elmbyval;
 		char		elmalign;
+		char		elmstor;
 		int			num_elems;
 		Datum	   *elem_values;
 		bool	   *elem_nulls;
@@ -696,11 +697,11 @@ ExecIndexEvalArrayKeys(ExprContext *econtext,
 		}
 		arrayval = DatumGetArrayTypeP(arraydatum);
 		/* We could cache this data, but not clear it's worth it */
-		get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
-							 &elmlen, &elmbyval, &elmalign);
+		get_type_stores(ARR_ELEMTYPE(arrayval),
+							 &elmlen, &elmbyval, &elmalign, &elmstor);
 		deconstruct_array(arrayval,
 						  ARR_ELEMTYPE(arrayval),
-						  elmlen, elmbyval, elmalign,
+						  elmlen, elmbyval, elmalign, elmstor,
 						  &elem_values, &elem_nulls, &num_elems);
 		if (num_elems <= 0)
 		{
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5d102a0d371..83ee95a2656 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3428,11 +3428,12 @@ match_orclause_to_indexcol(PlannerInfo *root,
 		int16		typlen;
 		bool		typbyval;
 		char		typalign;
+		char		typstor;
 		Datum	   *elems;
 		int			i = 0;
 		ArrayType  *arrayConst;
 
-		get_typlenbyvalalign(consttype, &typlen, &typbyval, &typalign);
+		get_type_stores(consttype, &typlen, &typbyval, &typalign, &typstor);
 
 		elems = (Datum *) palloc(sizeof(Datum) * list_length(consts));
 		foreach_node(Const, value, consts)
@@ -3443,7 +3444,7 @@ match_orclause_to_indexcol(PlannerInfo *root,
 		}
 
 		arrayConst = construct_array(elems, i, consttype,
-									 typlen, typbyval, typalign);
+									 typlen, typbyval, typalign, typstor);
 		arrayNode = (Node *) makeConst(arraytype, -1, inputcollid,
 									   -1, PointerGetDatum(arrayConst),
 									   false, false);
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 0a132610140..20e7dd220f0 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -965,6 +965,7 @@ arrayconst_startup_fn(Node *clause, PredIterInfo info)
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 
 	/* Create working state struct */
 	state = (ArrayConstIterState *) palloc(sizeof(ArrayConstIterState));
@@ -973,11 +974,11 @@ arrayconst_startup_fn(Node *clause, PredIterInfo info)
 	/* Deconstruct the array literal */
 	arrayconst = (Const *) lsecond(saop->args);
 	arrayval = DatumGetArrayTypeP(arrayconst->constvalue);
-	get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
-						 &elmlen, &elmbyval, &elmalign);
+	get_type_stores(ARR_ELEMTYPE(arrayval),
+						 &elmlen, &elmbyval, &elmalign, &elmstor);
 	deconstruct_array(arrayval,
 					  ARR_ELEMTYPE(arrayval),
-					  elmlen, elmbyval, elmalign,
+					  elmlen, elmbyval, elmalign, elmstor,
 					  &state->elem_values, &state->elem_nulls,
 					  &state->num_elems);
 
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index c28639d2e3f..3064c943254 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -4778,6 +4778,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 		int16		variadic_typlen;
 		bool		variadic_typbyval;
 		char		variadic_typalign;
+		char		variadic_typstorage;
 		Oid			partcollid[PARTITION_MAX_KEYS];
 		FmgrInfo	partsupfunc[FLEXIBLE_ARRAY_MEMBER];
 	} ColumnsHashData;
@@ -4881,10 +4882,11 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 			my_extra->relid = parentId;
 			my_extra->nkeys = key->partnatts;
 			my_extra->variadic_type = ARR_ELEMTYPE(variadic_array);
-			get_typlenbyvalalign(my_extra->variadic_type,
+			get_type_stores(my_extra->variadic_type,
 								 &my_extra->variadic_typlen,
 								 &my_extra->variadic_typbyval,
-								 &my_extra->variadic_typalign);
+								 &my_extra->variadic_typalign,
+								 &my_extra->variadic_typstorage);
 			my_extra->partcollid[0] = key->partcollation[0];
 
 			/* check argument types */
@@ -4949,6 +4951,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 						  my_extra->variadic_typlen,
 						  my_extra->variadic_typbyval,
 						  my_extra->variadic_typalign,
+						  my_extra->variadic_typstorage,
 						  &datum, &isnull, &nelems);
 
 		/* complain if wrong number of column values */
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 4e12ae5d1e3..faf825b9cd5 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -2280,6 +2280,7 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
 			int16		elemlen;
 			bool		elembyval;
 			char		elemalign;
+			char		elemstor;
 			Datum	   *elem_values;
 			bool	   *elem_nulls;
 			int			num_elems,
@@ -2290,11 +2291,11 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
 				return PARTCLAUSE_MATCH_CONTRADICT;
 
 			arrval = DatumGetArrayTypeP(arr->constvalue);
-			get_typlenbyvalalign(ARR_ELEMTYPE(arrval),
-								 &elemlen, &elembyval, &elemalign);
+			get_type_stores(ARR_ELEMTYPE(arrval),
+								 &elemlen, &elembyval, &elemalign, &elemstor);
 			deconstruct_array(arrval,
 							  ARR_ELEMTYPE(arrval),
-							  elemlen, elembyval, elemalign,
+							  elemlen, elembyval, elemalign, elemstor,
 							  &elem_values, &elem_nulls,
 							  &num_elems);
 			for (i = 0; i < num_elems; i++)
diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c
index 99fdf208dba..3c1335fd707 100644
--- a/src/backend/statistics/extended_stats.c
+++ b/src/backend/statistics/extended_stats.c
@@ -575,6 +575,7 @@ examine_attribute(Node *expr)
 		stats->statyplen[i] = stats->attrtype->typlen;
 		stats->statypbyval[i] = stats->attrtype->typbyval;
 		stats->statypalign[i] = stats->attrtype->typalign;
+		stats->statypstorage[i] = stats->attrtype->typstorage;
 	}
 
 	/*
@@ -660,6 +661,7 @@ examine_expression(Node *expr, int stattarget)
 		stats->statyplen[i] = stats->attrtype->typlen;
 		stats->statypbyval[i] = stats->attrtype->typbyval;
 		stats->statypalign[i] = stats->attrtype->typalign;
+		stats->statypstorage[i] = stats->attrtype->typstorage;
 	}
 
 	/*
@@ -2393,7 +2395,8 @@ serialize_expr_stats(AnlExprData *exprdata, int nexprs)
 									   stats->statypid[k],
 									   stats->statyplen[k],
 									   stats->statypbyval[k],
-									   stats->statypalign[k]);
+									   stats->statypalign[k],
+									   stats->statypstorage[k]);
 				values[i++] = PointerGetDatum(arry);	/* stavaluesN */
 			}
 			else
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index b0e9aead84e..c2fddf69526 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -1724,6 +1724,7 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses,
 			int16		elmlen;
 			bool		elmbyval;
 			char		elmalign;
+			char		elmstor;
 			int			num_elems;
 			Datum	   *elem_values;
 			bool	   *elem_nulls;
@@ -1745,11 +1746,11 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses,
 			if (!cst->constisnull)
 			{
 				arrayval = DatumGetArrayTypeP(cst->constvalue);
-				get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
-									 &elmlen, &elmbyval, &elmalign);
+				get_type_stores(ARR_ELEMTYPE(arrayval),
+									 &elmlen, &elmbyval, &elmalign, &elmstor);
 				deconstruct_array(arrayval,
 								  ARR_ELEMTYPE(arrayval),
-								  elmlen, elmbyval, elmalign,
+								  elmlen, elmbyval, elmalign, elmstor,
 								  &elem_values, &elem_nulls, &num_elems);
 			}
 
diff --git a/src/backend/utils/adt/array_expanded.c b/src/backend/utils/adt/array_expanded.c
index cc3713b97b7..9882f1e0df8 100644
--- a/src/backend/utils/adt/array_expanded.c
+++ b/src/backend/utils/adt/array_expanded.c
@@ -92,6 +92,7 @@ expand_array(Datum arraydatum, MemoryContext parentcontext,
 		metacache->typlen = oldeah->typlen;
 		metacache->typbyval = oldeah->typbyval;
 		metacache->typalign = oldeah->typalign;
+		metacache->typstorage = oldeah->typstorage;
 
 		/*
 		 * If element type is pass-by-value and we have a Datum-array
@@ -144,14 +145,17 @@ expand_array(Datum arraydatum, MemoryContext parentcontext,
 		eah->typlen = metacache->typlen;
 		eah->typbyval = metacache->typbyval;
 		eah->typalign = metacache->typalign;
+		eah->typstorage = metacache->typstorage;
 	}
 	else
 	{
 		/* No, so look it up */
-		get_typlenbyvalalign(eah->element_type,
+		get_type_stores(eah->element_type,
 							 &eah->typlen,
 							 &eah->typbyval,
-							 &eah->typalign);
+							 &eah->typalign,
+							 &eah->typstorage);
+
 		/* Update cache if provided */
 		if (metacache)
 		{
@@ -159,6 +163,7 @@ expand_array(Datum arraydatum, MemoryContext parentcontext,
 			metacache->typlen = eah->typlen;
 			metacache->typbyval = eah->typbyval;
 			metacache->typalign = eah->typalign;
+			metacache->typstorage = eah->typstorage;
 		}
 	}
 
@@ -203,6 +208,7 @@ copy_byval_expanded_array(ExpandedArrayHeader *eah,
 	eah->typlen = oldeah->typlen;
 	eah->typbyval = oldeah->typbyval;
 	eah->typalign = oldeah->typalign;
+	eah->typstorage = oldeah->typstorage;
 
 	/* Copy the deconstructed representation */
 	eah->dvalues = (Datum *) MemoryContextAlloc(objcxt,
@@ -333,7 +339,7 @@ EA_flatten_into(ExpandedObjectHeader *eohptr,
 
 	CopyArrayEls(aresult,
 				 eah->dvalues, eah->dnulls, nelems,
-				 eah->typlen, eah->typbyval, eah->typalign,
+				 eah->typlen, eah->typbyval, eah->typalign, eah->typstorage,
 				 false);
 }
 
@@ -384,6 +390,7 @@ DatumGetExpandedArrayX(Datum d, ArrayMetaState *metacache)
 			metacache->typlen = eah->typlen;
 			metacache->typbyval = eah->typbyval;
 			metacache->typalign = eah->typalign;
+			metacache->typstorage = eah->typstorage;
 		}
 		return eah;
 	}
@@ -433,7 +440,8 @@ deconstruct_expanded_array(ExpandedArrayHeader *eah)
 		dnulls = NULL;
 		deconstruct_array(eah->fvalue,
 						  eah->element_type,
-						  eah->typlen, eah->typbyval, eah->typalign,
+						  eah->typlen, eah->typbyval,
+						  eah->typalign, eah->typstorage,
 						  &dvalues,
 						  ARR_HASNULL(eah->fvalue) ? &dnulls : NULL,
 						  &nelems);
diff --git a/src/backend/utils/adt/array_selfuncs.c b/src/backend/utils/adt/array_selfuncs.c
index e2af89f5cc5..d1857fa66f3 100644
--- a/src/backend/utils/adt/array_selfuncs.c
+++ b/src/backend/utils/adt/array_selfuncs.c
@@ -448,6 +448,7 @@ mcelem_array_selec(ArrayType *array, TypeCacheEntry *typentry,
 					  typentry->typlen,
 					  typentry->typbyval,
 					  typentry->typalign,
+					  typentry->typstorage,
 					  &elem_values, &elem_nulls, &num_elems);
 
 	/* Collapse out any null elements */
diff --git a/src/backend/utils/adt/array_typanalyze.c b/src/backend/utils/adt/array_typanalyze.c
index 2c633bee6b1..148197694b0 100644
--- a/src/backend/utils/adt/array_typanalyze.c
+++ b/src/backend/utils/adt/array_typanalyze.c
@@ -42,6 +42,7 @@ typedef struct
 	bool		typbyval;		/* physical properties of element type */
 	int16		typlen;
 	char		typalign;
+	char		typstorage;
 
 	/*
 	 * Lookup data for element type's comparison and hash functions (these are
@@ -139,6 +140,7 @@ array_typanalyze(PG_FUNCTION_ARGS)
 	extra_data->typbyval = typentry->typbyval;
 	extra_data->typlen = typentry->typlen;
 	extra_data->typalign = typentry->typalign;
+	extra_data->typstorage = typentry->typstorage;
 	extra_data->cmp = &typentry->cmp_proc_finfo;
 	extra_data->hash = &typentry->hash_proc_finfo;
 
@@ -340,6 +342,7 @@ compute_array_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 						  extra_data->typlen,
 						  extra_data->typbyval,
 						  extra_data->typalign,
+						  extra_data->typstorage,
 						  &elem_values, &elem_nulls, &num_elems);
 
 		/*
@@ -568,6 +571,7 @@ compute_array_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 			stats->statyplen[slot_idx] = extra_data->typlen;
 			stats->statypbyval[slot_idx] = extra_data->typbyval;
 			stats->statypalign[slot_idx] = extra_data->typalign;
+			stats->statypstorage[slot_idx] = extra_data->typstorage;
 			slot_idx++;
 		}
 
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 304a93112e2..2d533a59185 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -161,8 +161,9 @@ array_append(PG_FUNCTION_ARGS)
 	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
 	result = array_set_element(EOHPGetRWDatum(&eah->hdr),
-							   1, &indx, newelem, isNull,
-							   -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
+							   1, &indx, newelem, isNull, -1,
+							   my_extra->typlen, my_extra->typbyval,
+							   my_extra->typalign, my_extra->typstorage);
 
 	PG_RETURN_DATUM(result);
 }
@@ -216,8 +217,9 @@ array_prepend(PG_FUNCTION_ARGS)
 	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
 	result = array_set_element(EOHPGetRWDatum(&eah->hdr),
-							   1, &indx, newelem, isNull,
-							   -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
+							   1, &indx, newelem, isNull, -1,
+							   my_extra->typlen, my_extra->typbyval,
+							   my_extra->typalign, my_extra->typstorage);
 
 	/* Readjust result's LB to match the input's, as expected for prepend */
 	Assert(result == EOHPGetRWDatum(&eah->hdr));
@@ -654,6 +656,9 @@ array_agg_serialize(PG_FUNCTION_ARGS)
 	/* typalign */
 	pq_sendbyte(&buf, state->typalign);
 
+	/* typstorage */
+	pq_sendbyte(&buf, state->typstorage);
+
 	/* dnulls */
 	pq_sendbytes(&buf, state->dnulls, sizeof(bool) * state->nelems);
 
@@ -749,6 +754,9 @@ array_agg_deserialize(PG_FUNCTION_ARGS)
 	/* typalign */
 	result->typalign = pq_getmsgbyte(&buf);
 
+	/* typstorage */
+	result->typstorage = pq_getmsgbyte(&buf);
+
 	/* dnulls */
 	temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
 	memcpy(result->dnulls, temp, sizeof(bool) * nelems);
@@ -1321,10 +1329,11 @@ array_position_common(FunctionCallInfo fcinfo)
 
 	if (my_extra->element_type != element_type)
 	{
-		get_typlenbyvalalign(element_type,
-							 &my_extra->typlen,
-							 &my_extra->typbyval,
-							 &my_extra->typalign);
+		get_type_stores(element_type,
+						&my_extra->typlen,
+						&my_extra->typbyval,
+						&my_extra->typalign,
+						&my_extra->typstorage);
 
 		typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
 
@@ -1464,10 +1473,12 @@ array_positions(PG_FUNCTION_ARGS)
 
 	if (my_extra->element_type != element_type)
 	{
-		get_typlenbyvalalign(element_type,
-							 &my_extra->typlen,
-							 &my_extra->typbyval,
-							 &my_extra->typalign);
+		get_type_stores(element_type,
+						&my_extra->typlen,
+						&my_extra->typbyval,
+						&my_extra->typalign,
+						&my_extra->typstorage);
+
 
 		typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
 
@@ -1548,6 +1559,7 @@ array_shuffle_n(ArrayType *array, int n, bool keep_lb,
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 	Datum	   *elms,
 			   *ielms;
 	bool	   *nuls,
@@ -1560,12 +1572,13 @@ array_shuffle_n(ArrayType *array, int n, bool keep_lb,
 	elmlen = typentry->typlen;
 	elmbyval = typentry->typbyval;
 	elmalign = typentry->typalign;
+	elmstor = typentry->typstorage;
 
 	/* If the target array is empty, exit fast */
 	if (ndim < 1 || dims[0] < 1 || n < 1)
 		return construct_empty_array(elmtyp);
 
-	deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
+	deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign, elmstor,
 					  &elms, &nuls, &nelm);
 
 	nitem = dims[0];			/* total number of items */
@@ -1608,7 +1621,7 @@ array_shuffle_n(ArrayType *array, int n, bool keep_lb,
 		rlbs[0] = 1;
 
 	result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
-								elmtyp, elmlen, elmbyval, elmalign);
+								elmtyp, elmlen, elmbyval, elmalign, elmstor);
 
 	pfree(elms);
 	pfree(nuls);
@@ -1709,6 +1722,7 @@ array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 	Datum	   *elms,
 			   *ielms;
 	bool	   *nuls,
@@ -1721,8 +1735,9 @@ array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
 	elmlen = typentry->typlen;
 	elmbyval = typentry->typbyval;
 	elmalign = typentry->typalign;
+	elmstor = typentry->typstorage;
 
-	deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
+	deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign, elmstor,
 					  &elms, &nuls, &nelm);
 
 	nitem = dims[0];			/* total number of items */
@@ -1756,7 +1771,7 @@ array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
 	rdims[0] = nitem;
 
 	result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
-								elmtyp, elmlen, elmbyval, elmalign);
+								elmtyp, elmlen, elmbyval, elmalign, elmstor);
 
 	pfree(elms);
 	pfree(nuls);
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 593775c27f3..f8dad59c27f 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -74,6 +74,7 @@ typedef struct ArrayIteratorData
 	int16		typlen;			/* element type's length */
 	bool		typbyval;		/* element type's byval property */
 	char		typalign;		/* element type's align property */
+	char		typstorage;		/* element type's storage property */
 
 	/* information about the requested slice size */
 	int			slice_ndim;		/* slice dimension, or 0 if not slicing */
@@ -105,46 +106,51 @@ static ArrayToken ReadArrayToken(char **srcptr, StringInfo elembuf, char typdeli
 								 const char *origStr, Node *escontext);
 static void ReadArrayBinary(StringInfo buf, int nitems,
 							FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
-							int typlen, bool typbyval, char typalign,
+							int typlen, bool typbyval, char typalign, char typstor,
 							Datum *values, bool *nulls,
 							bool *hasnulls, int32 *nbytes);
 static Datum array_get_element_expanded(Datum arraydatum,
 										int nSubscripts, int *indx,
 										int arraytyplen,
-										int elmlen, bool elmbyval, char elmalign,
-										bool *isNull);
+										int elmlen, bool elmbyval,
+										char elmalign, char typstor, bool *isNull);
 static Datum array_set_element_expanded(Datum arraydatum,
 										int nSubscripts, int *indx,
 										Datum dataValue, bool isNull,
 										int arraytyplen,
-										int elmlen, bool elmbyval, char elmalign);
+										int elmlen, bool elmbyval,
+										char elmalign, char typstor);
 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,
-							char *dest);
+							int typlen, bool typbyval,
+							char typalign, char typstor, char *dest);
 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
-						int typlen, bool typbyval, char typalign);
+						int typlen, bool typbyval, char typalign, char typstor);
 static int	array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
-							  int nitems, int typlen, bool typbyval, char typalign);
+							  int nitems, int typlen, bool typbyval,
+							  char typalign, char typstor);
 static int	array_copy(char *destptr, int nitems,
 					   char *srcptr, int offset, bits8 *nullbitmap,
-					   int typlen, bool typbyval, char typalign);
+					   int typlen, bool typbyval, char typalign, char typstor);
 static int	array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 							 int ndim, int *dim, int *lb,
 							 int *st, int *endp,
-							 int typlen, bool typbyval, char typalign);
+							 int typlen, bool typbyval,
+							 char typalign, char typstor);
 static void array_extract_slice(ArrayType *newarray,
 								int ndim, int *dim, int *lb,
 								char *arraydataptr, bits8 *arraynullsptr,
 								int *st, int *endp,
-								int typlen, bool typbyval, char typalign);
+								int typlen, bool typbyval,
+								char typalign, char typstor);
 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
 							   ArrayType *srcArray,
 							   int ndim, int *dim, int *lb,
 							   int *st, int *endp,
-							   int typlen, bool typbyval, char typalign);
+							   int typlen, bool typbyval,
+							   char typalign, char typstor);
 static int	array_cmp(FunctionCallInfo fcinfo);
 static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
 										Oid elmtype, int dataoffset);
@@ -187,6 +193,7 @@ array_in(PG_FUNCTION_ARGS)
 	bool		typbyval;
 	char		typalign;
 	char		typdelim;
+	char		typstor;
 	Oid			typioparam;
 	char	   *p;
 	int			nitems;
@@ -220,10 +227,7 @@ array_in(PG_FUNCTION_ARGS)
 		/*
 		 * Get info about element type, including its input conversion proc
 		 */
-		get_type_io_data(element_type, IOFunc_input,
-						 &my_extra->typlen, &my_extra->typbyval,
-						 &my_extra->typalign, &my_extra->typdelim,
-						 &my_extra->typioparam, &my_extra->typiofunc);
+		array_type_metadata(element_type, IOFunc_input, my_extra);
 		fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
 					  fcinfo->flinfo->fn_mcxt);
 		my_extra->element_type = element_type;
@@ -231,6 +235,7 @@ array_in(PG_FUNCTION_ARGS)
 	typlen = my_extra->typlen;
 	typbyval = my_extra->typbyval;
 	typalign = my_extra->typalign;
+	typstor = my_extra->typstorage;
 	typdelim = my_extra->typdelim;
 	typioparam = my_extra->typioparam;
 
@@ -326,7 +331,7 @@ array_in(PG_FUNCTION_ARGS)
 			/* let's just make sure data is not toasted */
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
-			nbytes = att_addlength_datum(nbytes, typlen, values[i]);
+			nbytes = att_addvarsize_datum(nbytes, typlen, typstor, values[i]);
 			nbytes = att_align_nominal(nbytes, typalign);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(nbytes))
@@ -366,7 +371,7 @@ array_in(PG_FUNCTION_ARGS)
 
 	CopyArrayEls(retval,
 				 values, nulls, nitems,
-				 typlen, typbyval, typalign,
+				 typlen, typbyval, typalign, typstor,
 				 true);
 
 	pfree(values);
@@ -949,7 +954,7 @@ ending_error:
  * values: array of Datums to be copied
  * nulls: array of is-null flags (can be NULL if no nulls)
  * nitems: number of Datums to be copied
- * typbyval, typlen, typalign: info about element datatype
+ * typbyval, typlen, typalign, typstor: info about element datatype
  * freedata: if true and element type is pass-by-ref, pfree data values
  * referenced by Datums after copying them.
  *
@@ -965,6 +970,7 @@ CopyArrayEls(ArrayType *array,
 			 int typlen,
 			 bool typbyval,
 			 char typalign,
+			 char typstor,
 			 bool freedata)
 {
 	char	   *p = ARR_DATA_PTR(array);
@@ -987,7 +993,8 @@ CopyArrayEls(ArrayType *array,
 		else
 		{
 			bitval |= bitmask;
-			p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
+			p += ArrayCastAndSet(values[i],
+								 typlen, typbyval, typalign, typstor, p);
 			if (freedata)
 				pfree(DatumGetPointer(values[i]));
 		}
@@ -1065,10 +1072,7 @@ array_out(PG_FUNCTION_ARGS)
 		/*
 		 * Get info about element type, including its output conversion proc
 		 */
-		get_type_io_data(element_type, IOFunc_output,
-						 &my_extra->typlen, &my_extra->typbyval,
-						 &my_extra->typalign, &my_extra->typdelim,
-						 &my_extra->typioparam, &my_extra->typiofunc);
+		array_type_metadata(element_type, IOFunc_output, my_extra);
 		fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
 					  fcinfo->flinfo->fn_mcxt);
 		my_extra->element_type = element_type;
@@ -1278,6 +1282,7 @@ array_recv(PG_FUNCTION_ARGS)
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typstor;
 	Oid			typioparam;
 	int			i,
 				nitems;
@@ -1368,10 +1373,7 @@ array_recv(PG_FUNCTION_ARGS)
 	if (my_extra->element_type != element_type)
 	{
 		/* Get info about element type, including its receive proc */
-		get_type_io_data(element_type, IOFunc_receive,
-						 &my_extra->typlen, &my_extra->typbyval,
-						 &my_extra->typalign, &my_extra->typdelim,
-						 &my_extra->typioparam, &my_extra->typiofunc);
+		array_type_metadata(element_type, IOFunc_receive, my_extra);
 		if (!OidIsValid(my_extra->typiofunc))
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1391,13 +1393,14 @@ array_recv(PG_FUNCTION_ARGS)
 	typlen = my_extra->typlen;
 	typbyval = my_extra->typbyval;
 	typalign = my_extra->typalign;
+	typstor = my_extra->typstorage;
 	typioparam = my_extra->typioparam;
 
 	dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
 	nullsPtr = (bool *) palloc(nitems * sizeof(bool));
 	ReadArrayBinary(buf, nitems,
 					&my_extra->proc, typioparam, typmod,
-					typlen, typbyval, typalign,
+					typlen, typbyval, typalign, typstor,
 					dataPtr, nullsPtr,
 					&hasnulls, &nbytes);
 	if (hasnulls)
@@ -1420,7 +1423,7 @@ array_recv(PG_FUNCTION_ARGS)
 
 	CopyArrayEls(retval,
 				 dataPtr, nullsPtr, nitems,
-				 typlen, typbyval, typalign,
+				 typlen, typbyval, typalign, typstor,
 				 true);
 
 	pfree(dataPtr);
@@ -1438,7 +1441,7 @@ array_recv(PG_FUNCTION_ARGS)
  *	nitems: total number of array elements (already read).
  *	receiveproc: type-specific receive procedure for element datatype.
  *	typioparam, typmod: auxiliary values to pass to receiveproc.
- *	typlen, typbyval, typalign: storage parameters of element datatype.
+ *	typlen, typbyval, typalign, typstor: storage parameters of element datatype.
  *
  * Outputs:
  *	values[]: filled with converted data values.
@@ -1459,6 +1462,7 @@ ReadArrayBinary(StringInfo buf,
 				int typlen,
 				bool typbyval,
 				char typalign,
+				char typstor,
 				Datum *values,
 				bool *nulls,
 				bool *hasnulls,
@@ -1524,7 +1528,7 @@ ReadArrayBinary(StringInfo buf,
 			/* let's just make sure data is not toasted */
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
-			totbytes = att_addlength_datum(totbytes, typlen, values[i]);
+			totbytes = att_addvarsize_datum(totbytes, typlen, typstor, values[i]);
 			totbytes = att_align_nominal(totbytes, typalign);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(totbytes))
@@ -1578,10 +1582,7 @@ array_send(PG_FUNCTION_ARGS)
 	if (my_extra->element_type != element_type)
 	{
 		/* Get info about element type, including its send proc */
-		get_type_io_data(element_type, IOFunc_send,
-						 &my_extra->typlen, &my_extra->typbyval,
-						 &my_extra->typalign, &my_extra->typdelim,
-						 &my_extra->typioparam, &my_extra->typiofunc);
+		array_type_metadata(element_type, IOFunc_send, my_extra);
 		if (!OidIsValid(my_extra->typiofunc))
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1811,6 +1812,7 @@ array_cardinality(PG_FUNCTION_ARGS)
  *	elmlen: pg_type.typlen for the array's element type
  *	elmbyval: pg_type.typbyval for the array's element type
  *	elmalign: pg_type.typalign for the array's element type
+ *	elmstor: pg_type.typstorage for the array's element type
  *
  * Outputs:
  *	The return value is the element Datum.
@@ -1824,6 +1826,7 @@ array_get_element(Datum arraydatum,
 				  int elmlen,
 				  bool elmbyval,
 				  char elmalign,
+				  char elmstor,
 				  bool *isNull)
 {
 	int			i,
@@ -1860,6 +1863,7 @@ array_get_element(Datum arraydatum,
 										  elmlen,
 										  elmbyval,
 										  elmalign,
+										  elmstor,
 										  isNull);
 	}
 	else
@@ -1910,7 +1914,7 @@ array_get_element(Datum arraydatum,
 	 */
 	*isNull = false;
 	retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
-						elmlen, elmbyval, elmalign);
+						elmlen, elmbyval, elmalign, elmstor);
 	return ArrayCast(retptr, elmbyval, elmlen);
 }
 
@@ -1921,7 +1925,8 @@ static Datum
 array_get_element_expanded(Datum arraydatum,
 						   int nSubscripts, int *indx,
 						   int arraytyplen,
-						   int elmlen, bool elmbyval, char elmalign,
+						   int elmlen, bool elmbyval,
+						   char elmalign, char typstor,
 						   bool *isNull)
 {
 	ExpandedArrayHeader *eah;
@@ -1941,6 +1946,7 @@ array_get_element_expanded(Datum arraydatum,
 	Assert(elmlen == eah->typlen);
 	Assert(elmbyval == eah->typbyval);
 	Assert(elmalign == eah->typalign);
+	Assert(typstor == eah->typstorage);
 
 	ndim = eah->ndims;
 	dim = eah->dims;
@@ -2015,6 +2021,7 @@ array_get_element_expanded(Datum arraydatum,
  *	elmlen: pg_type.typlen for the array's element type
  *	elmbyval: pg_type.typbyval for the array's element type
  *	elmalign: pg_type.typalign for the array's element type
+ *	elmstor: pg_type.typstorage for the array's element type
  *
  * Outputs:
  *	The return value is the new array Datum (it's never NULL)
@@ -2036,7 +2043,8 @@ array_get_slice(Datum arraydatum,
 				int arraytyplen,
 				int elmlen,
 				bool elmbyval,
-				char elmalign)
+				char elmalign,
+				char elmstor)
 {
 	ArrayType  *array;
 	ArrayType  *newarray;
@@ -2124,7 +2132,7 @@ array_get_slice(Datum arraydatum,
 	bytes = array_slice_size(arraydataptr, arraynullsptr,
 							 ndim, dim, lb,
 							 lowerIndx, upperIndx,
-							 elmlen, elmbyval, elmalign);
+							 elmlen, elmbyval, elmalign, elmstor);
 
 	/*
 	 * Currently, we put a null bitmap in the result if the source has one;
@@ -2160,7 +2168,7 @@ array_get_slice(Datum arraydatum,
 						ndim, dim, lb,
 						arraydataptr, arraynullsptr,
 						lowerIndx, upperIndx,
-						elmlen, elmbyval, elmalign);
+						elmlen, elmbyval, elmalign, elmstor);
 
 	return PointerGetDatum(newarray);
 }
@@ -2182,6 +2190,7 @@ array_get_slice(Datum arraydatum,
  *	elmlen: pg_type.typlen for the array's element type
  *	elmbyval: pg_type.typbyval for the array's element type
  *	elmalign: pg_type.typalign for the array's element type
+ *	elmstor: pg_type.typstorage for the array's element type
  *
  * Result:
  *		  A new array is returned, just like the old except for the one
@@ -2206,7 +2215,8 @@ array_set_element(Datum arraydatum,
 				  int arraytyplen,
 				  int elmlen,
 				  bool elmbyval,
-				  char elmalign)
+				  char elmalign,
+				  char elmstor)
 {
 	ArrayType  *array;
 	ArrayType  *newarray;
@@ -2257,7 +2267,7 @@ array_set_element(Datum arraydatum,
 		resultarray = (char *) palloc(arraytyplen);
 		memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
 		elt_ptr = (char *) resultarray + indx[0] * elmlen;
-		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
+		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elmstor, elt_ptr);
 		return PointerGetDatum(resultarray);
 	}
 
@@ -2281,7 +2291,8 @@ array_set_element(Datum arraydatum,
 										  arraytyplen,
 										  elmlen,
 										  elmbyval,
-										  elmalign);
+										  elmalign,
+										  elmstor);
 	}
 
 	/* detoast input array if necessary */
@@ -2307,7 +2318,8 @@ array_set_element(Datum arraydatum,
 		return PointerGetDatum(construct_md_array(&dataValue, &isNull,
 												  nSubscripts, dim, lb,
 												  elmtype,
-												  elmlen, elmbyval, elmalign));
+												  elmlen, elmbyval,
+												  elmalign, elmstor));
 	}
 
 	if (ndim != nSubscripts)
@@ -2408,13 +2420,13 @@ array_set_element(Datum arraydatum,
 	{
 		offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
 		elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
-							 elmlen, elmbyval, elmalign);
+							 elmlen, elmbyval, elmalign, elmstor);
 		lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
 		if (array_get_isnull(oldnullbitmap, offset))
 			olditemlen = 0;
 		else
 		{
-			olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
+			olditemlen = att_addvarsize_pointer(0, elmlen, elmstor, elt_ptr);
 			olditemlen = att_align_nominal(olditemlen, elmalign);
 		}
 		lenafter = (int) (olddatasize - lenbefore - olditemlen);
@@ -2424,7 +2436,7 @@ array_set_element(Datum arraydatum,
 		newitemlen = 0;
 	else
 	{
-		newitemlen = att_addlength_datum(0, elmlen, dataValue);
+		newitemlen = att_addvarsize_datum(0, elmlen, elmstor, dataValue);
 		newitemlen = att_align_nominal(newitemlen, elmalign);
 	}
 
@@ -2448,7 +2460,7 @@ array_set_element(Datum arraydatum,
 		   (char *) array + oldoverheadlen,
 		   lenbefore);
 	if (!isNull)
-		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
+		ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elmstor,
 						(char *) newarray + overheadlen + lenbefore);
 	memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
 		   (char *) array + oldoverheadlen + lenbefore + olditemlen,
@@ -2502,7 +2514,8 @@ array_set_element_expanded(Datum arraydatum,
 						   int nSubscripts, int *indx,
 						   Datum dataValue, bool isNull,
 						   int arraytyplen,
-						   int elmlen, bool elmbyval, char elmalign)
+						   int elmlen, bool elmbyval,
+						   char elmalign, char typstor)
 {
 	ExpandedArrayHeader *eah;
 	Datum	   *dvalues;
@@ -2526,6 +2539,7 @@ array_set_element_expanded(Datum arraydatum,
 	Assert(elmlen == eah->typlen);
 	Assert(elmbyval == eah->typbyval);
 	Assert(elmalign == eah->typalign);
+	Assert(typstor == eah->typstorage);
 
 	/*
 	 * Copy dimension info into local storage.  This allows us to modify the
@@ -2782,6 +2796,7 @@ array_set_element_expanded(Datum arraydatum,
  *	elmlen: pg_type.typlen for the array's element type
  *	elmbyval: pg_type.typbyval for the array's element type
  *	elmalign: pg_type.typalign for the array's element type
+ *	elmstor: pg_type.typstorage for the array's element type
  *
  * Result:
  *		  A new array is returned, just like the old except for the
@@ -2814,7 +2829,8 @@ array_set_slice(Datum arraydatum,
 				int arraytyplen,
 				int elmlen,
 				bool elmbyval,
-				char elmalign)
+				char elmalign,
+				char elmstor)
 {
 	ArrayType  *array;
 	ArrayType  *srcArray;
@@ -2875,7 +2891,7 @@ array_set_slice(Datum arraydatum,
 		int			nelems;
 		Oid			elmtype = ARR_ELEMTYPE(array);
 
-		deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+		deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign, elmstor,
 						  &dvalues, &dnulls, &nelems);
 
 		for (i = 0; i < nSubscripts; i++)
@@ -2906,7 +2922,8 @@ array_set_slice(Datum arraydatum,
 
 		return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
 												  dim, lb, elmtype,
-												  elmlen, elmbyval, elmalign));
+												  elmlen, elmbyval,
+												  elmalign, elmstor));
 	}
 
 	if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
@@ -3026,7 +3043,7 @@ array_set_slice(Datum arraydatum,
 		overheadlen = ARR_OVERHEAD_NONULLS(ndim);
 	newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
 									ARR_NULLBITMAP(srcArray), nsrcitems,
-									elmlen, elmbyval, elmalign);
+									elmlen, elmbyval, elmalign, elmstor);
 	oldoverheadlen = ARR_DATA_OFFSET(array);
 	olddatasize = ARR_SIZE(array) - oldoverheadlen;
 	if (ndim > 1)
@@ -3039,7 +3056,7 @@ array_set_slice(Datum arraydatum,
 									   ARR_NULLBITMAP(array),
 									   ndim, dim, lb,
 									   lowerIndx, upperIndx,
-									   elmlen, elmbyval, elmalign);
+									   elmlen, elmbyval, elmalign, elmstor);
 		lenbefore = lenafter = 0;	/* keep compiler quiet */
 		itemsbefore = itemsafter = nolditems = 0;
 	}
@@ -3060,7 +3077,7 @@ array_set_slice(Datum arraydatum,
 		itemsbefore = Min(slicelb, oldub + 1) - oldlb;
 		lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
 									  itemsbefore,
-									  elmlen, elmbyval, elmalign);
+									  elmlen, elmbyval, elmalign, elmstor);
 		/* count/size of old array entries that will be replaced by slice */
 		if (slicelb > sliceub)
 		{
@@ -3073,7 +3090,7 @@ array_set_slice(Datum arraydatum,
 			olditemsize = array_nelems_size(oldarraydata + lenbefore,
 											itemsbefore, oldarraybitmap,
 											nolditems,
-											elmlen, elmbyval, elmalign);
+											elmlen, elmbyval, elmalign, elmstor);
 		}
 		/* count/size of old array entries that will go after the slice */
 		itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
@@ -3099,7 +3116,7 @@ array_set_slice(Datum arraydatum,
 		array_insert_slice(newarray, array, srcArray,
 						   ndim, dim, lb,
 						   lowerIndx, upperIndx,
-						   elmlen, elmbyval, elmalign);
+						   elmlen, elmbyval, elmalign, elmstor);
 	}
 	else
 	{
@@ -3144,11 +3161,11 @@ array_set_slice(Datum arraydatum,
  */
 Datum
 array_ref(ArrayType *array, int nSubscripts, int *indx,
-		  int arraytyplen, int elmlen, bool elmbyval, char elmalign,
-		  bool *isNull)
+		  int arraytyplen, int elmlen, bool elmbyval,
+		  char elmalign, char elmstor, bool *isNull)
 {
 	return array_get_element(PointerGetDatum(array), nSubscripts, indx,
-							 arraytyplen, elmlen, elmbyval, elmalign,
+							 arraytyplen, elmlen, elmbyval, elmalign, elmstor,
 							 isNull);
 }
 
@@ -3161,14 +3178,15 @@ array_ref(ArrayType *array, int nSubscripts, int *indx,
  */
 ArrayType *
 array_set(ArrayType *array, int nSubscripts, int *indx,
-		  Datum dataValue, bool isNull,
-		  int arraytyplen, int elmlen, bool elmbyval, char elmalign)
+		  Datum dataValue, bool isNull, int arraytyplen,
+		  int elmlen, bool elmbyval, char elmalign, char elmstor)
 {
 	return DatumGetArrayTypeP(array_set_element(PointerGetDatum(array),
 												nSubscripts, indx,
 												dataValue, isNull,
 												arraytyplen,
-												elmlen, elmbyval, elmalign));
+												elmlen, elmbyval,
+												elmalign, elmstor));
 }
 
 /*
@@ -3220,6 +3238,7 @@ array_map(Datum arrayd,
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typstor;
 	array_iter	iter;
 	ArrayMetaState *inp_extra;
 	ArrayMetaState *ret_extra;
@@ -3260,15 +3279,17 @@ array_map(Datum arrayd,
 
 	if (ret_extra->element_type != retType)
 	{
-		get_typlenbyvalalign(retType,
-							 &ret_extra->typlen,
-							 &ret_extra->typbyval,
-							 &ret_extra->typalign);
+		get_type_stores(retType,
+						&ret_extra->typlen,
+						&ret_extra->typbyval,
+						&ret_extra->typalign,
+						&ret_extra->typstorage);
 		ret_extra->element_type = retType;
 	}
 	typlen = ret_extra->typlen;
 	typbyval = ret_extra->typbyval;
 	typalign = ret_extra->typalign;
+	typstor = ret_extra->typstorage;
 
 	/* Allocate temporary arrays for new values */
 	values = (Datum *) palloc(nitems * sizeof(Datum));
@@ -3296,7 +3317,7 @@ array_map(Datum arrayd,
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 			/* Update total result size */
-			nbytes = att_addlength_datum(nbytes, typlen, values[i]);
+			nbytes = att_addvarsize_datum(nbytes, typlen, typstor, values[i]);
 			nbytes = att_align_nominal(nbytes, typalign);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(nbytes))
@@ -3328,7 +3349,7 @@ array_map(Datum arrayd,
 
 	CopyArrayEls(result,
 				 values, nulls, nitems,
-				 typlen, typbyval, typalign,
+				 typlen, typbyval, typalign, typstor,
 				 false);
 
 	/*
@@ -3346,7 +3367,8 @@ array_map(Datum arrayd,
  * elems: array of Datum items to become the array contents
  *		  (NULL element values are not supported).
  * nelems: number of items
- * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
+ * elmtype, elmlen, elmbyval, elmalign, elmstor:
+ * 		  info for the datatype of the items
  *
  * A palloc'd 1-D array object is constructed and returned.  Note that
  * elem values will be copied into the object even if pass-by-ref type.
@@ -3360,7 +3382,7 @@ array_map(Datum arrayd,
 ArrayType *
 construct_array(Datum *elems, int nelems,
 				Oid elmtype,
-				int elmlen, bool elmbyval, char elmalign)
+				int elmlen, bool elmbyval, char elmalign, char elmstor)
 {
 	int			dims[1];
 	int			lbs[1];
@@ -3369,13 +3391,13 @@ construct_array(Datum *elems, int nelems,
 	lbs[0] = 1;
 
 	return construct_md_array(elems, NULL, 1, dims, lbs,
-							  elmtype, elmlen, elmbyval, elmalign);
+							  elmtype, elmlen, elmbyval, elmalign, elmstor);
 }
 
 /*
  * 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.
+ * elmlen/elmbyval/elmalign/elmstor 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)
@@ -3383,6 +3405,7 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 	int			elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 
 	switch (elmtype)
 	{
@@ -3390,48 +3413,56 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 			elmlen = 1;
 			elmbyval = true;
 			elmalign = TYPALIGN_CHAR;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case CSTRINGOID:
 			elmlen = -2;
 			elmbyval = false;
 			elmalign = TYPALIGN_CHAR;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case FLOAT4OID:
 			elmlen = sizeof(float4);
 			elmbyval = true;
 			elmalign = TYPALIGN_INT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case FLOAT8OID:
 			elmlen = sizeof(float8);
 			elmbyval = FLOAT8PASSBYVAL;
 			elmalign = TYPALIGN_DOUBLE;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case INT2OID:
 			elmlen = sizeof(int16);
 			elmbyval = true;
 			elmalign = TYPALIGN_SHORT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case INT4OID:
 			elmlen = sizeof(int32);
 			elmbyval = true;
 			elmalign = TYPALIGN_INT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case INT8OID:
 			elmlen = sizeof(int64);
 			elmbyval = FLOAT8PASSBYVAL;
 			elmalign = TYPALIGN_DOUBLE;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case NAMEOID:
 			elmlen = NAMEDATALEN;
 			elmbyval = false;
 			elmalign = TYPALIGN_CHAR;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case OIDOID:
@@ -3439,24 +3470,28 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 			elmlen = sizeof(Oid);
 			elmbyval = true;
 			elmalign = TYPALIGN_INT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case TEXTOID:
 			elmlen = -1;
 			elmbyval = false;
 			elmalign = TYPALIGN_INT;
+			elmstor = TYPSTORAGE_EXTENDED;
 			break;
 
 		case TIDOID:
 			elmlen = sizeof(ItemPointerData);
 			elmbyval = false;
 			elmalign = TYPALIGN_SHORT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case XIDOID:
 			elmlen = sizeof(TransactionId);
 			elmbyval = true;
 			elmalign = TYPALIGN_INT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		default:
@@ -3465,9 +3500,11 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
 			elmlen = 0;
 			elmbyval = false;
 			elmalign = 0;
+			elmstor = 0;
 	}
 
-	return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
+	return construct_array(elems, nelems, elmtype, elmlen, elmbyval,
+						   elmalign, elmstor);
 }
 
 /*
@@ -3479,7 +3516,8 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
  * ndims: number of dimensions
  * dims: integer array with size of each dimension
  * lbs: integer array with lower bound of each dimension
- * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
+ * elmtype, elmlen, elmbyval, elmalign, elmstor:
+ * 		  info for the datatype of the items
  *
  * A palloc'd ndims-D array object is constructed and returned.  Note that
  * elem values will be copied into the object even if pass-by-ref type.
@@ -3496,7 +3534,8 @@ construct_md_array(Datum *elems,
 				   int ndims,
 				   int *dims,
 				   int *lbs,
-				   Oid elmtype, int elmlen, bool elmbyval, char elmalign)
+				   Oid elmtype, int elmlen, bool elmbyval,
+				   char elmalign, char elmstor)
 {
 	ArrayType  *result;
 	bool		hasnulls;
@@ -3536,7 +3575,7 @@ construct_md_array(Datum *elems,
 		/* make sure data is not toasted */
 		if (elmlen == -1)
 			elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
-		nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
+		nbytes = att_addvarsize_datum(nbytes, elmlen, elmstor, elems[i]);
 		nbytes = att_align_nominal(nbytes, elmalign);
 		/* check for overflow of total request */
 		if (!AllocSizeIsValid(nbytes))
@@ -3567,7 +3606,7 @@ construct_md_array(Datum *elems,
 
 	CopyArrayEls(result,
 				 elems, nulls, nelems,
-				 elmlen, elmbyval, elmalign,
+				 elmlen, elmbyval, elmalign, elmstor,
 				 false);
 
 	return result;
@@ -3610,7 +3649,8 @@ construct_empty_expanded_array(Oid element_type,
  * deconstruct_array  --- simple method for extracting data from an array
  *
  * array: array object to examine (must not be NULL)
- * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
+ * elmtype, elmlen, elmbyval, elmalign, elmstor:
+ * 		  info for the datatype of the items
  * elemsp: return value, set to point to palloc'd array of Datum values
  * nullsp: return value, set to point to palloc'd array of isnull markers
  * nelemsp: return value, set to number of extracted values
@@ -3622,15 +3662,15 @@ construct_empty_expanded_array(Oid element_type,
  * If array elements are pass-by-ref data type, the returned Datums will
  * 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, the caller is
+ * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign/elmstor
+ * info 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,
 				  Oid elmtype,
-				  int elmlen, bool elmbyval, char elmalign,
+				  int elmlen, bool elmbyval, char elmalign, char elmstor,
 				  Datum **elemsp, bool **nullsp, int *nelemsp)
 {
 	Datum	   *elems;
@@ -3701,6 +3741,7 @@ deconstruct_array_builtin(ArrayType *array,
 	int			elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 
 	switch (elmtype)
 	{
@@ -3708,42 +3749,49 @@ deconstruct_array_builtin(ArrayType *array,
 			elmlen = 1;
 			elmbyval = true;
 			elmalign = TYPALIGN_CHAR;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case CSTRINGOID:
 			elmlen = -2;
 			elmbyval = false;
 			elmalign = TYPALIGN_CHAR;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case FLOAT8OID:
 			elmlen = sizeof(float8);
 			elmbyval = FLOAT8PASSBYVAL;
 			elmalign = TYPALIGN_DOUBLE;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case INT2OID:
 			elmlen = sizeof(int16);
 			elmbyval = true;
 			elmalign = TYPALIGN_SHORT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case OIDOID:
 			elmlen = sizeof(Oid);
 			elmbyval = true;
 			elmalign = TYPALIGN_INT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		case TEXTOID:
 			elmlen = -1;
 			elmbyval = false;
 			elmalign = TYPALIGN_INT;
+			elmstor = TYPSTORAGE_EXTENDED;
 			break;
 
 		case TIDOID:
 			elmlen = sizeof(ItemPointerData);
 			elmbyval = false;
 			elmalign = TYPALIGN_SHORT;
+			elmstor = TYPSTORAGE_PLAIN;
 			break;
 
 		default:
@@ -3752,9 +3800,11 @@ deconstruct_array_builtin(ArrayType *array,
 			elmlen = 0;
 			elmbyval = false;
 			elmalign = 0;
+			elmstor = 0;
 	}
 
-	deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
+	deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elmstor,
+					  elemsp, nullsp, nelemsp);
 }
 
 /*
@@ -4215,6 +4265,7 @@ hash_array(PG_FUNCTION_ARGS)
 			record_typentry->typlen = typentry->typlen;
 			record_typentry->typbyval = typentry->typbyval;
 			record_typentry->typalign = typentry->typalign;
+			record_typentry->typstorage = typentry->typstorage;
 			fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
 
 			MemoryContextSwitchTo(oldcontext);
@@ -4392,6 +4443,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typstor;
 	int			i;
 	int			j;
 	array_iter	it1;
@@ -4423,6 +4475,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	typlen = typentry->typlen;
 	typbyval = typentry->typbyval;
 	typalign = typentry->typalign;
+	typstor = typentry->typstorage;
 
 	/*
 	 * Since we probably will need to scan array2 multiple times, it's
@@ -4439,7 +4492,7 @@ array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
 	}
 	else
 		deconstruct_array((ArrayType *) array2,
-						  element_type, typlen, typbyval, typalign,
+						  element_type, typlen, typbyval, typalign, typstor,
 						  &values2, &nulls2, &nelems2);
 
 	/*
@@ -4619,12 +4672,14 @@ array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
 		iterator->typlen = mstate->typlen;
 		iterator->typbyval = mstate->typbyval;
 		iterator->typalign = mstate->typalign;
+		iterator->typstorage = mstate->typstorage;
 	}
 	else
-		get_typlenbyvalalign(ARR_ELEMTYPE(arr),
-							 &iterator->typlen,
-							 &iterator->typbyval,
-							 &iterator->typalign);
+		get_type_stores(ARR_ELEMTYPE(arr),
+						&iterator->typlen,
+						&iterator->typbyval,
+						&iterator->typalign,
+						&iterator->typstorage);
 
 	/*
 	 * Remember the slicing parameters.
@@ -4743,7 +4798,8 @@ array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
 									ARR_ELEMTYPE(iterator->arr),
 									iterator->typlen,
 									iterator->typbyval,
-									iterator->typalign);
+									iterator->typalign,
+									iterator->typstorage);
 
 		*isnull = false;
 		*value = PointerGetDatum(result);
@@ -4828,6 +4884,7 @@ ArrayCastAndSet(Datum src,
 				int typlen,
 				bool typbyval,
 				char typalign,
+				char typstor,
 				char *dest)
 {
 	int			inc;
@@ -4840,6 +4897,14 @@ ArrayCastAndSet(Datum src,
 			memmove(dest, DatumGetPointer(src), typlen);
 		inc = att_align_nominal(typlen, typalign);
 	}
+	else if (TYPE_IS_PACKABLE(typlen, typstor) && VARATT_CAN_MAKE_SHORT(src))
+	{
+		Assert(!typbyval);
+		inc = VARATT_CONVERTED_SHORT_SIZE(src);
+		SET_VARSIZE_SHORT(dest, inc);
+		memcpy(dest + 1, VARDATA(DatumGetPointer(src)), inc - 1);
+		inc = att_align_nominal(inc, typalign);
+	}
 	else
 	{
 		Assert(!typbyval);
@@ -4858,13 +4923,14 @@ ArrayCastAndSet(Datum src,
  * offset: 0-based linear element number of first element (the one at *ptr)
  * nullbitmap: start of array's null bitmap, or NULL if none
  * nitems: number of array elements to advance over (>= 0)
- * typlen, typbyval, typalign: storage parameters of array element datatype
+ * typlen, typbyval, typalign, typstor:
+ * 			storage parameters of array element datatype
  *
  * It is caller's responsibility to ensure that nitems is within range
  */
 static char *
 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
-		   int typlen, bool typbyval, char typalign)
+		   int typlen, bool typbyval, char typalign, char typstor)
 {
 	int			bitmask;
 	int			i;
@@ -4883,7 +4949,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 		{
 			if (*nullbitmap & bitmask)
 			{
-				ptr = att_addlength_pointer(ptr, typlen, ptr);
+				ptr = att_addvarsize_pointer(ptr, typlen, typstor, ptr);
 				ptr = (char *) att_align_nominal(ptr, typalign);
 			}
 			bitmask <<= 1;
@@ -4898,7 +4964,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 = att_addvarsize_pointer(ptr, typlen, typstor, ptr);
 			ptr = (char *) att_align_nominal(ptr, typalign);
 		}
 	}
@@ -4912,10 +4978,10 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
  */
 static int
 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
-				  int typlen, bool typbyval, char typalign)
+				  int typlen, bool typbyval, char typalign, char typstor)
 {
 	return array_seek(ptr, offset, nullbitmap, nitems,
-					  typlen, typbyval, typalign) - ptr;
+					  typlen, typbyval, typalign, typstor) - ptr;
 }
 
 /*
@@ -4926,7 +4992,8 @@ array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
  * srcptr: starting location in source array
  * offset: 0-based linear element number of first element (the one at *srcptr)
  * nullbitmap: start of source array's null bitmap, or NULL if none
- * typlen, typbyval, typalign: storage parameters of array element datatype
+ * typlen, typbyval, typalign, typstor:
+ * 			storage parameters of array element datatype
  *
  * Returns number of bytes copied
  *
@@ -4935,12 +5002,12 @@ array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 static int
 array_copy(char *destptr, int nitems,
 		   char *srcptr, int offset, bits8 *nullbitmap,
-		   int typlen, bool typbyval, char typalign)
+		   int typlen, bool typbyval, char typalign, char typstor)
 {
 	int			numbytes;
 
 	numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
-								 typlen, typbyval, typalign);
+								 typlen, typbyval, typalign, typstor);
 	memcpy(destptr, srcptr, numbytes);
 	return numbytes;
 }
@@ -5037,7 +5104,7 @@ static int
 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 				 int ndim, int *dim, int *lb,
 				 int *st, int *endp,
-				 int typlen, bool typbyval, char typalign)
+				 int typlen, bool typbyval, char typalign, char typstor)
 {
 	int			src_offset,
 				span[MAXDIM],
@@ -5059,7 +5126,7 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 	/* Else gotta do it the hard way */
 	src_offset = ArrayGetOffset(ndim, dim, lb, st);
 	ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
-					 typlen, typbyval, typalign);
+					 typlen, typbyval, typalign, typstor);
 	mda_get_prod(ndim, dim, prod);
 	mda_get_offset_values(ndim, dist, prod, span);
 	for (i = 0; i < ndim; i++)
@@ -5070,12 +5137,12 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 		if (dist[j])
 		{
 			ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
-							 typlen, typbyval, typalign);
+							 typlen, typbyval, typalign, typstor);
 			src_offset += dist[j];
 		}
 		if (!array_get_isnull(arraynullsptr, src_offset))
 		{
-			inc = att_addlength_pointer(0, typlen, ptr);
+			inc = att_addvarsize_pointer(0, typlen, typstor, ptr);
 			inc = att_align_nominal(inc, typalign);
 			ptr += inc;
 			count += inc;
@@ -5104,7 +5171,8 @@ array_extract_slice(ArrayType *newarray,
 					int *endp,
 					int typlen,
 					bool typbyval,
-					char typalign)
+					char typalign,
+					char typstor)
 {
 	char	   *destdataptr = ARR_DATA_PTR(newarray);
 	bits8	   *destnullsptr = ARR_NULLBITMAP(newarray);
@@ -5121,7 +5189,7 @@ array_extract_slice(ArrayType *newarray,
 
 	src_offset = ArrayGetOffset(ndim, dim, lb, st);
 	srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
-							typlen, typbyval, typalign);
+							typlen, typbyval, typalign, typstor);
 	mda_get_prod(ndim, dim, prod);
 	mda_get_range(ndim, span, st, endp);
 	mda_get_offset_values(ndim, dist, prod, span);
@@ -5136,12 +5204,12 @@ array_extract_slice(ArrayType *newarray,
 			/* skip unwanted elements */
 			srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
 									dist[j],
-									typlen, typbyval, typalign);
+									typlen, typbyval, typalign, typstor);
 			src_offset += dist[j];
 		}
 		inc = array_copy(destdataptr, 1,
 						 srcdataptr, src_offset, arraynullsptr,
-						 typlen, typbyval, typalign);
+						 typlen, typbyval, typalign, typstor);
 		if (destnullsptr)
 			array_bitmap_copy(destnullsptr, dest_offset,
 							  arraynullsptr, src_offset,
@@ -5177,7 +5245,8 @@ array_insert_slice(ArrayType *destArray,
 				   int *endp,
 				   int typlen,
 				   bool typbyval,
-				   char typalign)
+				   char typalign,
+				   char typstor)
 {
 	char	   *destPtr = ARR_DATA_PTR(destArray);
 	char	   *origPtr = ARR_DATA_PTR(origArray);
@@ -5202,7 +5271,7 @@ array_insert_slice(ArrayType *destArray,
 	/* copy items before the slice start */
 	inc = array_copy(destPtr, dest_offset,
 					 origPtr, 0, origBitmap,
-					 typlen, typbyval, typalign);
+					 typlen, typbyval, typalign, typstor);
 	destPtr += inc;
 	origPtr += inc;
 	if (destBitmap)
@@ -5222,7 +5291,7 @@ array_insert_slice(ArrayType *destArray,
 		{
 			inc = array_copy(destPtr, dist[j],
 							 origPtr, orig_offset, origBitmap,
-							 typlen, typbyval, typalign);
+							 typlen, typbyval, typalign, typstor);
 			destPtr += inc;
 			origPtr += inc;
 			if (destBitmap)
@@ -5235,7 +5304,7 @@ array_insert_slice(ArrayType *destArray,
 		/* Copy new element at this slice position */
 		inc = array_copy(destPtr, 1,
 						 srcPtr, src_offset, srcBitmap,
-						 typlen, typbyval, typalign);
+						 typlen, typbyval, typalign, typstor);
 		if (destBitmap)
 			array_bitmap_copy(destBitmap, dest_offset,
 							  srcBitmap, src_offset,
@@ -5246,14 +5315,14 @@ array_insert_slice(ArrayType *destArray,
 		src_offset++;
 		/* Advance over old element at this slice position */
 		origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
-							 typlen, typbyval, typalign);
+							 typlen, typbyval, typalign, typstor);
 		orig_offset++;
 	} while ((j = mda_next_tuple(ndim, indx, span)) != -1);
 
 	/* don't miss any data at the end */
 	array_copy(destPtr, orignitems - orig_offset,
 			   origPtr, orig_offset, origBitmap,
-			   typlen, typbyval, typalign);
+			   typlen, typbyval, typalign, typstor);
 	if (destBitmap)
 		array_bitmap_copy(destBitmap, dest_offset,
 						  origBitmap, orig_offset,
@@ -5330,10 +5399,11 @@ initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
 		MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
 	astate->nelems = 0;
 	astate->element_type = element_type;
-	get_typlenbyvalalign(element_type,
-						 &astate->typlen,
-						 &astate->typbyval,
-						 &astate->typalign);
+	get_type_stores(element_type,
+					&astate->typlen,
+					&astate->typbyval,
+					&astate->typalign,
+					&astate->typstorage);
 
 	return astate;
 }
@@ -5470,7 +5540,8 @@ makeMdArrayResult(ArrayBuildState *astate,
 								astate->element_type,
 								astate->typlen,
 								astate->typbyval,
-								astate->typalign);
+								astate->typalign,
+								astate->typstorage);
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -6093,6 +6164,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 	ArrayMetaState *my_extra;
 
 	/*
@@ -6177,16 +6249,18 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
 	if (my_extra->element_type != elmtype)
 	{
 		/* Get info about element type */
-		get_typlenbyvalalign(elmtype,
-							 &my_extra->typlen,
-							 &my_extra->typbyval,
-							 &my_extra->typalign);
+		get_type_stores(elmtype,
+						&my_extra->typlen,
+						&my_extra->typbyval,
+						&my_extra->typalign,
+						&my_extra->typstorage);
 		my_extra->element_type = elmtype;
 	}
 
 	elmlen = my_extra->typlen;
 	elmbyval = my_extra->typbyval;
 	elmalign = my_extra->typalign;
+	elmstor = my_extra->typstorage;
 
 	/* compute required space */
 	if (!isnull)
@@ -6225,7 +6299,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, elmalign, elmstor, p);
 	}
 	else
 	{
@@ -6398,6 +6472,7 @@ array_replace_internal(ArrayType *array,
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typstor;
 	char	   *arraydataptr;
 	bits8	   *bitmap;
 	int			bitmask;
@@ -6442,6 +6517,7 @@ array_replace_internal(ArrayType *array,
 	typlen = typentry->typlen;
 	typbyval = typentry->typbyval;
 	typalign = typentry->typalign;
+	typstor = typentry->typstorage;
 
 	/*
 	 * Detoast values if they are toasted.  The replacement value must be
@@ -6502,7 +6578,7 @@ array_replace_internal(ArrayType *array,
 		{
 			isNull = false;
 			elt = fetch_att(arraydataptr, typbyval, typlen);
-			arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
+			arraydataptr = att_addvarsize_datum(arraydataptr, typlen, typstor, elt);
 			arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
 
 			if (search_isnull)
@@ -6549,7 +6625,7 @@ array_replace_internal(ArrayType *array,
 			else
 			{
 				/* Update total result size */
-				nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
+				nbytes = att_addvarsize_datum(nbytes, typlen, typstor, values[nresult]);
 				nbytes = att_align_nominal(nbytes, typalign);
 				/* check for overflow of total request */
 				if (!AllocSizeIsValid(nbytes))
@@ -6619,7 +6695,7 @@ array_replace_internal(ArrayType *array,
 	/* Insert data into result array */
 	CopyArrayEls(result,
 				 values, nulls, nresult,
-				 typlen, typbyval, typalign,
+				 typlen, typbyval, typalign, typstor,
 				 false);
 
 	pfree(values);
@@ -6925,6 +7001,7 @@ trim_array(PG_FUNCTION_ARGS)
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 	int			lower[MAXDIM];
 	int			upper[MAXDIM];
 	bool		lowerProvided[MAXDIM];
@@ -6948,12 +7025,12 @@ trim_array(PG_FUNCTION_ARGS)
 	}
 
 	/* Fetch the needed information about the element type */
-	get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
+	get_type_stores(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign, &elmstor);
 
 	/* Get the slice */
 	result = array_get_slice(PointerGetDatum(v), 1,
 							 upper, lower, upperProvided, lowerProvided,
-							 -1, elmlen, elmbyval, elmalign);
+							 -1, elmlen, elmbyval, elmalign, elmstor);
 
 	PG_RETURN_DATUM(result);
 }
diff --git a/src/backend/utils/adt/arraysubs.c b/src/backend/utils/adt/arraysubs.c
index 6f68dfa5b23..1af83eff70a 100644
--- a/src/backend/utils/adt/arraysubs.c
+++ b/src/backend/utils/adt/arraysubs.c
@@ -34,6 +34,7 @@ typedef struct ArraySubWorkspace
 	int16		refelemlength;	/* typlen of the array element type */
 	bool		refelembyval;	/* is the element type pass-by-value? */
 	char		refelemalign;	/* typalign of the element type */
+	char		refelemstorage;	/* typstorage of the element type */
 
 	/*
 	 * Subscript values converted to integers.  Note that these arrays must be
@@ -250,6 +251,7 @@ array_subscript_fetch(ExprState *state,
 									  workspace->refelemlength,
 									  workspace->refelembyval,
 									  workspace->refelemalign,
+									  workspace->refelemstorage,
 									  op->resnull);
 }
 
@@ -280,7 +282,8 @@ array_subscript_fetch_slice(ExprState *state,
 									workspace->refattrlength,
 									workspace->refelemlength,
 									workspace->refelembyval,
-									workspace->refelemalign);
+									workspace->refelemalign,
+									workspace->refelemstorage);
 	/* The slice is never NULL, so no need to change *op->resnull */
 }
 
@@ -330,7 +333,8 @@ array_subscript_assign(ExprState *state,
 									  workspace->refattrlength,
 									  workspace->refelemlength,
 									  workspace->refelembyval,
-									  workspace->refelemalign);
+									  workspace->refelemalign,
+									  workspace->refelemstorage);
 	/* The result is never NULL, so no need to change *op->resnull */
 }
 
@@ -383,7 +387,8 @@ array_subscript_assign_slice(ExprState *state,
 									workspace->refattrlength,
 									workspace->refelemlength,
 									workspace->refelembyval,
-									workspace->refelemalign);
+									workspace->refelemalign,
+									workspace->refelemstorage);
 	/* The result is never NULL, so no need to change *op->resnull */
 }
 
@@ -417,6 +422,7 @@ array_subscript_fetch_old(ExprState *state,
 												   workspace->refelemlength,
 												   workspace->refelembyval,
 												   workspace->refelemalign,
+												   workspace->refelemstorage,
 												   &sbsrefstate->prevnull);
 }
 
@@ -460,7 +466,8 @@ array_subscript_fetch_old_slice(ExprState *state,
 												 workspace->refattrlength,
 												 workspace->refelemlength,
 												 workspace->refelembyval,
-												 workspace->refelemalign);
+												 workspace->refelemalign,
+												 workspace->refelemstorage);
 		/* slices of non-null arrays are never null */
 		sbsrefstate->prevnull = false;
 	}
@@ -504,10 +511,11 @@ array_exec_setup(const SubscriptingRef *sbsref,
 	 */
 	workspace->refelemtype = sbsref->refelemtype;
 	workspace->refattrlength = get_typlen(sbsref->refcontainertype);
-	get_typlenbyvalalign(sbsref->refelemtype,
-						 &workspace->refelemlength,
-						 &workspace->refelembyval,
-						 &workspace->refelemalign);
+	get_type_stores(sbsref->refelemtype,
+					&workspace->refelemlength,
+					&workspace->refelembyval,
+					&workspace->refelemalign,
+					&workspace->refelemstorage);
 
 	/*
 	 * Pass back pointers to appropriate step execution functions.
diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c
index d4c2aa0e7e9..4d74ac90117 100644
--- a/src/backend/utils/adt/enum.c
+++ b/src/backend/utils/adt/enum.c
@@ -608,7 +608,7 @@ enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
 	/* and build the result array */
 	/* note this hardwires some details about the representation of Oid */
 	result = construct_array(elems, cnt, enumtypoid,
-							 sizeof(Oid), true, TYPALIGN_INT);
+							 sizeof(Oid), true, TYPALIGN_INT, TYPSTORAGE_PLAIN);
 
 	pfree(elems);
 
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 058aade2af4..ef473cec622 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -484,6 +484,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 	int16		typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typstor;
 	JsonTypeCategory tcategory;
 	Oid			outfuncoid;
 
@@ -497,14 +498,14 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 		return;
 	}
 
-	get_typlenbyvalalign(element_type,
-						 &typlen, &typbyval, &typalign);
+	get_type_stores(element_type,
+						 &typlen, &typbyval, &typalign, &typstor);
 
 	json_categorize_type(element_type, false,
 						 &tcategory, &outfuncoid);
 
 	deconstruct_array(v, element_type, typlen, typbyval,
-					  typalign, &elements, &nulls,
+					  typalign, typstor, &elements, &nulls,
 					  &nitems);
 
 	array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index d602df4eeb5..4ccf4400ea7 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -904,6 +904,7 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
 	int16		typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typstor;
 	JsonTypeCategory tcategory;
 	Oid			outfuncoid;
 
@@ -918,14 +919,14 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
 		return;
 	}
 
-	get_typlenbyvalalign(element_type,
-						 &typlen, &typbyval, &typalign);
+	get_type_stores(element_type,
+						 &typlen, &typbyval, &typalign, &typstor);
 
 	json_categorize_type(element_type, true,
 						 &tcategory, &outfuncoid);
 
 	deconstruct_array(v, element_type, typlen, typbyval,
-					  typalign, &elements, &nulls,
+					  typalign, typstor, &elements, &nulls,
 					  &nitems);
 
 	array_dim_to_jsonb(result, 0, ndim, dim, elements, nulls, &count, tcategory,
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 35fd825babe..a28f9735606 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -996,7 +996,8 @@ multirange_constructor2(PG_FUNCTION_ARGS)
 	else
 	{
 		deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
-						  rangetyp->typalign, &elements, &nulls, &range_count);
+						  rangetyp->typalign, rangetyp->typstorage,
+						  &elements, &nulls, &range_count);
 
 		ranges = palloc0(range_count * sizeof(RangeType *));
 		for (i = 0; i < range_count; i++)
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index b8bdc667dbc..da63a04af31 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -80,6 +80,7 @@ typedef struct OSAPerQueryState
 	int16		typLen;
 	bool		typByVal;
 	char		typAlign;
+	char		typStorage;
 	/* Info about sort ordering: */
 	Oid			sortOperator;
 	Oid			eqOperator;
@@ -264,10 +265,11 @@ ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
 			qstate->sortNullsFirst = sortcl->nulls_first;
 
 			/* Save datatype info */
-			get_typlenbyvalalign(qstate->sortColType,
-								 &qstate->typLen,
-								 &qstate->typByVal,
-								 &qstate->typAlign);
+			get_type_stores(qstate->sortColType,
+							&qstate->typLen,
+							&qstate->typByVal,
+							&qstate->typAlign,
+							&qstate->typStorage);
 		}
 
 		fcinfo->flinfo->fn_extra = qstate;
@@ -838,7 +840,8 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
 										 osastate->qstate->sortColType,
 										 osastate->qstate->typLen,
 										 osastate->qstate->typByVal,
-										 osastate->qstate->typAlign));
+										 osastate->qstate->typAlign,
+										 osastate->qstate->typStorage));
 }
 
 /*
@@ -847,7 +850,8 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
 static Datum
 percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
 								   Oid expect_type,
-								   int16 typLen, bool typByVal, char typAlign,
+								   int16 typLen, bool typByVal,
+								   char typAlign, char typStor,
 								   LerpFunc lerpfunc)
 {
 	OSAPerGroupState *osastate;
@@ -994,7 +998,7 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
 										 expect_type,
 										 typLen,
 										 typByVal,
-										 typAlign));
+										 typAlign, typStor));
 }
 
 /*
@@ -1009,6 +1013,7 @@ percentile_cont_float8_multi_final(PG_FUNCTION_ARGS)
 											  sizeof(float8),
 											  FLOAT8PASSBYVAL,
 											  TYPALIGN_DOUBLE,
+											  TYPSTORAGE_PLAIN,
 											  float8_lerp);
 }
 
@@ -1022,6 +1027,7 @@ percentile_cont_interval_multi_final(PG_FUNCTION_ARGS)
 											  INTERVALOID,
 	/* hard-wired info on type interval */
 											  16, false, TYPALIGN_DOUBLE,
+											  TYPSTORAGE_PLAIN,
 											  interval_lerp);
 }
 
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index dc714345222..80b0a240251 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -30,6 +30,7 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_type.h"
 #include "common/hashfn.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -2671,10 +2672,6 @@ range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum
  * details.
  */
 
-/* Does datatype allow packing into the 1-byte-header varlena format? */
-#define TYPE_IS_PACKABLE(typlen, typstorage) \
-	((typlen) == -1 && (typstorage) != TYPSTORAGE_PLAIN)
-
 /*
  * Increment data_length by the space needed by the datum, including any
  * preceding alignment padding.
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index 42aec95738d..d6589bd24be 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1664,7 +1664,8 @@ build_regexp_match_result(regexp_matches_ctx *matchctx)
 	lbs[0] = 1;
 	/* XXX: this hardcodes assumptions about the text type */
 	return construct_md_array(elems, nulls, 1, dims, lbs,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false,
+							  TYPALIGN_INT, TYPSTORAGE_EXTENDED);
 }
 
 /*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index be1f1f50b78..15d769f55b9 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3055,6 +3055,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 						  -1 /* TEXT's typlen */ ,
 						  false /* TEXT's typbyval */ ,
 						  TYPALIGN_INT /* TEXT's typalign */ ,
+						  TYPSTORAGE_EXTENDED /* TEXT's typalign */ ,
 						  &isnull);
 			if (!isnull)
 			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 08fa6774d9c..48e6212354c 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -1926,6 +1926,7 @@ scalararraysel(PlannerInfo *root,
 		int16		elmlen;
 		bool		elmbyval;
 		char		elmalign;
+		char		elmstor;
 		int			num_elems;
 		Datum	   *elem_values;
 		bool	   *elem_nulls;
@@ -1934,11 +1935,11 @@ scalararraysel(PlannerInfo *root,
 		if (arrayisnull)		/* qual can't succeed if null array */
 			return (Selectivity) 0.0;
 		arrayval = DatumGetArrayTypeP(arraydatum);
-		get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
-							 &elmlen, &elmbyval, &elmalign);
+		get_type_stores(ARR_ELEMTYPE(arrayval),
+							 &elmlen, &elmbyval, &elmalign, &elmstor);
 		deconstruct_array(arrayval,
 						  ARR_ELEMTYPE(arrayval),
-						  elmlen, elmbyval, elmalign,
+						  elmlen, elmbyval, elmalign, elmstor,
 						  &elem_values, &elem_nulls, &num_elems);
 
 		/*
@@ -7488,6 +7489,7 @@ gincost_scalararrayopexpr(PlannerInfo *root,
 	int16		elmlen;
 	bool		elmbyval;
 	char		elmalign;
+	char		elmstor;
 	int			numElems;
 	Datum	   *elemValues;
 	bool	   *elemNulls;
@@ -7523,11 +7525,11 @@ gincost_scalararrayopexpr(PlannerInfo *root,
 
 	/* Otherwise, extract the array elements and iterate over them */
 	arrayval = DatumGetArrayTypeP(((Const *) rightop)->constvalue);
-	get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
-						 &elmlen, &elmbyval, &elmalign);
+	get_type_stores(ARR_ELEMTYPE(arrayval),
+						 &elmlen, &elmbyval, &elmalign, &elmstor);
 	deconstruct_array(arrayval,
 					  ARR_ELEMTYPE(arrayval),
-					  elmlen, elmbyval, elmalign,
+					  elmlen, elmbyval, elmalign, elmstor,
 					  &elemValues, &elemNulls, &numElems);
 
 	memset(&arraycounts, 0, sizeof(arraycounts));
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index f75e25388ca..6c4c6c30163 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -329,8 +329,8 @@ tsvector_setweight_by_filter(PG_FUNCTION_ARGS)
 		if (nulls[i])
 			continue;
 
-		lex = VARDATA(dlexemes[i]);
-		lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		lex = VARDATA_ANY(dlexemes[i]);
+		lex_len = VARSIZE_ANY_EXHDR(dlexemes[i]);
 		lex_pos = tsvector_bsearch(tsout, lex, lex_len);
 
 		if (lex_pos >= 0 && (j = POSDATALEN(tsout, entry + lex_pos)) != 0)
@@ -605,8 +605,8 @@ tsvector_delete_arr(PG_FUNCTION_ARGS)
 		if (nulls[i])
 			continue;
 
-		lex = VARDATA(dlexemes[i]);
-		lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		lex = VARDATA_ANY(dlexemes[i]);
+		lex_len = VARSIZE_ANY_EXHDR(dlexemes[i]);
 		lex_pos = tsvector_bsearch(tsin, lex, lex_len);
 
 		if (lex_pos >= 0)
@@ -770,7 +770,7 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 					 errmsg("lexeme array may not contain nulls")));
 
-		if (VARSIZE(dlexemes[i]) - VARHDRSZ == 0)
+		if (VARSIZE_ANY_EXHDR(dlexemes[i]) == 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING),
 					 errmsg("lexeme array may not contain empty strings")));
@@ -786,7 +786,7 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 
 	/* Calculate space needed for surviving lexemes. */
 	for (i = 0; i < nitems; i++)
-		datalen += VARSIZE(dlexemes[i]) - VARHDRSZ;
+		datalen += VARSIZE_ANY_EXHDR(dlexemes[i]);
 	tslen = CALCDATASIZE(nitems, datalen);
 
 	/* Allocate and fill tsvector. */
@@ -798,8 +798,8 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 	cur = STRPTR(tsout);
 	for (i = 0; i < nitems; i++)
 	{
-		char	   *lex = VARDATA(dlexemes[i]);
-		int			lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		char	   *lex = VARDATA_ANY(dlexemes[i]);
+		int			lex_len = VARSIZE_ANY_EXHDR(dlexemes[i]);
 
 		memcpy(cur, lex, lex_len);
 		arrout[i].haspos = 0;
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 533bebc1c7b..4b43f934c2a 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4867,10 +4867,7 @@ array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
 		/*
 		 * Get info about element type, including its output conversion proc
 		 */
-		get_type_io_data(element_type, IOFunc_output,
-						 &my_extra->typlen, &my_extra->typbyval,
-						 &my_extra->typalign, &my_extra->typdelim,
-						 &my_extra->typioparam, &my_extra->typiofunc);
+		array_type_metadata(element_type, IOFunc_output, my_extra);
 		fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
 					  fcinfo->flinfo->fn_mcxt);
 		my_extra->element_type = element_type;
@@ -5678,6 +5675,7 @@ text_format(PG_FUNCTION_ARGS)
 		int16		elmlen;
 		bool		elmbyval;
 		char		elmalign;
+		char		elmstor;
 		int			nitems;
 
 		/* Should have just the one argument */
@@ -5702,12 +5700,12 @@ text_format(PG_FUNCTION_ARGS)
 
 			/* Get info about array element type */
 			element_type = ARR_ELEMTYPE(arr);
-			get_typlenbyvalalign(element_type,
-								 &elmlen, &elmbyval, &elmalign);
+			get_type_stores(element_type,
+								 &elmlen, &elmbyval, &elmalign, &elmstor);
 
 			/* Extract all array elements */
 			deconstruct_array(arr, element_type, elmlen, elmbyval, elmalign,
-							  &elements, &nulls, &nitems);
+							  elmstor, &elements, &nulls, &nitems);
 		}
 
 		nargs = nitems + 1;
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 0898cb1be4c..fd28af187ad 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -2482,6 +2482,7 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
 		int16		elmlen;
 		bool		elmbyval;
 		char		elmalign;
+		char		elmstor;
 		int			num_elems;
 		Datum	   *elem_values;
 		bool	   *elem_nulls;
@@ -2490,10 +2491,11 @@ map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings)
 
 		array = DatumGetArrayTypeP(value);
 		elmtype = ARR_ELEMTYPE(array);
-		get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+		get_type_stores(elmtype, &elmlen,
+							&elmbyval, &elmalign, &elmstor);
 
 		deconstruct_array(array, elmtype,
-						  elmlen, elmbyval, elmalign,
+						  elmlen, elmbyval, elmalign, elmstor,
 						  &elem_values, &elem_nulls,
 						  &num_elems);
 
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index a85dc0d891f..ec2775918ba 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2284,6 +2284,30 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
 	ReleaseSysCache(tp);
 }
 
+/*
+ * get_type_stores
+ *
+ *  A four-fer: given the type OID, return typlen,
+ *  			typbyval, typalign, typstorage.
+ */
+void
+get_type_stores(Oid typid, int16 *typlen, bool *typbyval,
+						   char *typalign, char *typstor)
+{
+	HeapTuple	tp;
+	Form_pg_type typtup;
+
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+	typtup = (Form_pg_type) GETSTRUCT(tp);
+	*typlen = typtup->typlen;
+	*typbyval = typtup->typbyval;
+	*typalign = typtup->typalign;
+	*typstor = typtup->typstorage;
+	ReleaseSysCache(tp);
+}
+
 /*
  * getTypeIOParam
  *		Given a pg_type row, select the type OID to pass to I/O functions
@@ -2394,6 +2418,63 @@ get_type_io_data(Oid typid,
 	ReleaseSysCache(typeTuple);
 }
 
+/*
+ * array_type_metadata
+ *
+ *		A six-fer:	given the type OID, return typlen, typbyval, typalign,
+ *					typdelim, typioparam, and IO function OID. The IO function
+ *					returned is controlled by IOFuncSelector
+ */
+void
+array_type_metadata(Oid typid,
+				 IOFuncSelector which_func,
+				 ArrayMetaState *metadata)
+{
+	HeapTuple	typeTuple;
+	Form_pg_type typeStruct;
+
+	/*
+	 * In bootstrap mode, pass it off to bootstrap.c.  This hack allows us to
+	 * use array_in and array_out during bootstrap.
+	 */
+	if (unlikely(IsBootstrapProcessingMode()))
+	{
+		boot_array_type_metadata(typid,
+								 which_func,
+								 metadata);
+
+		return;
+	}
+
+	typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+	if (!HeapTupleIsValid(typeTuple))
+		elog(ERROR, "cache lookup failed for type %u", typid);
+	typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+	metadata->typlen = typeStruct->typlen;
+	metadata->typbyval = typeStruct->typbyval;
+	metadata->typalign = typeStruct->typalign;
+	metadata->typstorage = typeStruct->typstorage;
+	metadata->typdelim = typeStruct->typdelim;
+	metadata->typioparam = getTypeIOParam(typeTuple);
+	switch (which_func)
+	{
+		case IOFunc_input:
+			metadata->typiofunc = typeStruct->typinput;
+			break;
+		case IOFunc_output:
+			metadata->typiofunc = typeStruct->typoutput;
+			break;
+		case IOFunc_receive:
+			metadata->typiofunc = typeStruct->typreceive;
+			break;
+		case IOFunc_send:
+			metadata->typiofunc = typeStruct->typsend;
+			break;
+	}
+	ReleaseSysCache(typeTuple);
+}
+
 #ifdef NOT_USED
 char
 get_typalign(Oid typid)
@@ -3287,6 +3368,7 @@ get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
 						  typeForm->typlen,
 						  typeForm->typbyval,
 						  typeForm->typalign,
+						  typeForm->typstorage,
 						  &sslot->values, NULL, &sslot->nvalues);
 
 		/*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1ce7eb9da8f..f6483a6d02e 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -627,6 +627,7 @@ RelationBuildTupleDesc(Relation relation)
 											attp->attlen,
 											attp->attbyval,
 											attp->attalign,
+											attp->attstorage,
 											&is_null);
 				Assert(!is_null);
 				if (attp->attbyval)
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 05d763fa06e..c85f6048029 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -2023,6 +2023,7 @@ extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
 		Oid			element_type;
 		bool		typbyval;
 		char		typalign;
+		char		typstor;
 		int16		typlen;
 
 		Assert(PG_NARGS() == variadic_start + 1);
@@ -2033,10 +2034,10 @@ extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start,
 		array_in = PG_GETARG_ARRAYTYPE_P(variadic_start);
 		element_type = ARR_ELEMTYPE(array_in);
 
-		get_typlenbyvalalign(element_type,
-							 &typlen, &typbyval, &typalign);
+		get_type_stores(element_type,
+						&typlen, &typbyval, &typalign, &typstor);
 		deconstruct_array(array_in, element_type, typlen, typbyval,
-						  typalign, &args_res, &nulls_res,
+						  typalign, typstor, &args_res, &nulls_res,
 						  &nargs);
 
 		/* All the elements of the array have the same type */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c10c0844ab6..c79a04a3472 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -6420,6 +6420,7 @@ TransformGUCArray(ArrayType *array, List **names, List **values)
 					  -1 /* TEXT's typlen */ ,
 					  false /* TEXT's typbyval */ ,
 					  TYPALIGN_INT /* TEXT's typalign */ ,
+					  TYPSTORAGE_EXTENDED /* TEXT's typalign */ ,
 					  &isnull);
 
 		if (isnull)
@@ -6529,6 +6530,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
 						  -1 /* TEXT's typlen */ ,
 						  false /* TEXT's typbyval */ ,
 						  TYPALIGN_INT /* TEXT's typalign */ ,
+						  TYPSTORAGE_EXTENDED /* TEXT's typalign */ ,
 						  &isnull);
 			if (isnull)
 				continue;
@@ -6548,7 +6550,8 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
 					  -1 /* varlena array */ ,
 					  -1 /* TEXT's typlen */ ,
 					  false /* TEXT's typbyval */ ,
-					  TYPALIGN_INT /* TEXT's typalign */ );
+					  TYPALIGN_INT /* TEXT's typalign */ ,
+					  TYPSTORAGE_EXTENDED /* TEXT's typstorage */ );
 	}
 	else
 		a = construct_array_builtin(&datum, 1, TEXTOID);
@@ -6598,6 +6601,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
 					  -1 /* TEXT's typlen */ ,
 					  false /* TEXT's typbyval */ ,
 					  TYPALIGN_INT /* TEXT's typalign */ ,
+					  TYPSTORAGE_EXTENDED /* TEXT's typalign */ ,
 					  &isnull);
 		if (isnull)
 			continue;
@@ -6616,7 +6620,8 @@ GUCArrayDelete(ArrayType *array, const char *name)
 								 -1 /* varlenarray */ ,
 								 -1 /* TEXT's typlen */ ,
 								 false /* TEXT's typbyval */ ,
-								 TYPALIGN_INT /* TEXT's typalign */ );
+								 TYPALIGN_INT /* TEXT's typalign */ ,
+								 TYPSTORAGE_EXTENDED /* TEXT's typstorage */ );
 		else
 			newarray = construct_array_builtin(&d, 1, TEXTOID);
 
@@ -6662,6 +6667,7 @@ GUCArrayReset(ArrayType *array)
 					  -1 /* TEXT's typlen */ ,
 					  false /* TEXT's typbyval */ ,
 					  TYPALIGN_INT /* TEXT's typalign */ ,
+					  TYPSTORAGE_EXTENDED /* TEXT's typalign */ ,
 					  &isnull);
 		if (isnull)
 			continue;
@@ -6682,7 +6688,8 @@ GUCArrayReset(ArrayType *array)
 								 -1 /* varlenarray */ ,
 								 -1 /* TEXT's typlen */ ,
 								 false /* TEXT's typbyval */ ,
-								 TYPALIGN_INT /* TEXT's typalign */ );
+								 TYPALIGN_INT /* TEXT's typalign */ ,
+								 TYPSTORAGE_EXTENDED /* TEXT's typstorage */ );
 		else
 			newarray = construct_array_builtin(&d, 1, TEXTOID);
 
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 0de67e3602a..f2264767fbb 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -199,6 +199,35 @@ fetch_att(const void *T, bool attbyval, int attlen)
 	)) \
 )
 
+/*
+ * att_addvarsize_datum increments the given offset by the space needed for
+ * the given Datum variable to store it.  attdatum is only accessed if we are
+ * dealing with a variable-length attribute.
+ */
+#define att_addvarsize_datum(cur_offset, typlen, typstor, datum) \
+	att_addvarsize_pointer(cur_offset, typlen, typstor, DatumGetPointer(datum))
+
+#define att_addvarsize_pointer(cur_offset, typlen, typstor, ptr) \
+( \
+	((typlen) > 0) ? \
+	( \
+		(cur_offset) + (typlen) \
+	) \
+	: (((typlen) == -1) ? \
+	( \
+		((TYPE_IS_PACKABLE(typlen, typstor) && VARATT_CAN_MAKE_SHORT(ptr)) ? \
+			(cur_offset) + VARATT_CONVERTED_SHORT_SIZE(ptr) \
+		: \
+			(cur_offset) + VARSIZE_ANY(ptr) \
+		) \
+	) \
+	: \
+	( \
+		AssertMacro((typlen) == -2), \
+		(cur_offset) + (strlen((char *) (ptr)) + 1) \
+	)) \
+)
+
 #ifndef FRONTEND
 /*
  * store_att_byval is a partial inverse of fetch_att: store a given Datum
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index 33035d4ed82..9d55dcc419e 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -16,6 +16,8 @@
 
 #include "nodes/execnodes.h"
 #include "nodes/parsenodes.h"
+#include "utils/array.h"
+#include "utils/lsyscache.h"
 
 
 /*
@@ -61,6 +63,10 @@ union YYSTYPE;
 typedef void *yyscan_t;
 #endif
 
+extern void boot_array_type_metadata(Oid typid,
+									 IOFuncSelector which_func,
+									 ArrayMetaState *metadata);
+
 extern int	boot_yyparse(yyscan_t yyscanner);
 extern int	boot_yylex_init(yyscan_t *yyscannerp);
 extern int	boot_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e9259697321..652e9467333 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -345,6 +345,10 @@ MAKE_SYSCACHE(TYPENAMENSP, pg_type_typname_nsp_index, 64);
 #endif							/* EXPOSE_TO_CLIENT_CODE */
 
 
+/* Does datatype allow packing into the 1-byte-header varlena format? */
+#define TYPE_IS_PACKABLE(typlen, typstorage) \
+	((typlen) == -1 && (typstorage) != TYPSTORAGE_PLAIN)
+
 extern ObjectAddress TypeShellMake(const char *typeName,
 								   Oid typeNamespace,
 								   Oid ownerId);
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 759f9a87d38..84e2def31cb 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -163,6 +163,7 @@ typedef struct VacAttrStats
 	int16		statyplen[STATISTIC_NUM_SLOTS];
 	bool		statypbyval[STATISTIC_NUM_SLOTS];
 	char		statypalign[STATISTIC_NUM_SLOTS];
+	char		statypstorage[STATISTIC_NUM_SLOTS];
 
 	/*
 	 * These fields are private to the main ANALYZE code and should not be
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 56fb0d0adbe..f17cedc26b0 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -458,6 +458,7 @@ typedef struct ExprEvalStep
 			int16		elemlength; /* typlen of the array element type */
 			bool		elembyval;	/* is the element type pass-by-value? */
 			char		elemalign;	/* typalign of the element type */
+			char		elemstorage;	/* typstorage of the element type */
 			bool		multidims;	/* is array expression multi-D? */
 		}			arrayexpr;
 
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 157cc0e4c6e..711b37f6714 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -130,6 +130,7 @@ typedef struct ExpandedArrayHeader
 	int16		typlen;			/* needed info about element datatype */
 	bool		typbyval;
 	char		typalign;
+	char		typstorage;
 
 	/*
 	 * If we have a Datum-array representation of the array, it's kept here;
@@ -195,6 +196,7 @@ typedef struct ArrayBuildState
 	int16		typlen;			/* needed info about datatype */
 	bool		typbyval;
 	char		typalign;
+	char		typstorage;
 	bool		private_cxt;	/* use private memory context */
 } ArrayBuildState;
 
@@ -240,6 +242,7 @@ typedef struct ArrayMetaState
 	bool		typbyval;
 	char		typalign;
 	char		typdelim;
+	char		typstorage;
 	Oid			typioparam;
 	Oid			typiofunc;
 	FmgrInfo	proc;
@@ -358,30 +361,34 @@ extern void CopyArrayEls(ArrayType *array,
 						 int typlen,
 						 bool typbyval,
 						 char typalign,
+						 char typstor,
 						 bool freedata);
 
 extern Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx,
-							   int arraytyplen, int elmlen, bool elmbyval, char elmalign,
-							   bool *isNull);
+							   int arraytyplen, int elmlen, bool elmbyval,
+							   char elmalign, char elmstor, bool *isNull);
 extern Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx,
 							   Datum dataValue, bool isNull,
-							   int arraytyplen, int elmlen, bool elmbyval, char elmalign);
+							   int arraytyplen, int elmlen, bool elmbyval,
+							   char elmalign, char elmstor);
 extern Datum array_get_slice(Datum arraydatum, int nSubscripts,
 							 int *upperIndx, int *lowerIndx,
 							 bool *upperProvided, bool *lowerProvided,
-							 int arraytyplen, int elmlen, bool elmbyval, char elmalign);
+							 int arraytyplen, int elmlen, bool elmbyval,
+							 char elmalign, char typstor);
 extern Datum array_set_slice(Datum arraydatum, int nSubscripts,
 							 int *upperIndx, int *lowerIndx,
 							 bool *upperProvided, bool *lowerProvided,
 							 Datum srcArrayDatum, bool isNull,
-							 int arraytyplen, int elmlen, bool elmbyval, char elmalign);
+							 int arraytyplen, int elmlen, bool elmbyval,
+							 char elmalign, char elmstor);
 
 extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
 					   int arraytyplen, int elmlen, bool elmbyval, char elmalign,
-					   bool *isNull);
+					   char elmstor, bool *isNull);
 extern ArrayType *array_set(ArrayType *array, int nSubscripts, int *indx,
-							Datum dataValue, bool isNull,
-							int arraytyplen, int elmlen, bool elmbyval, char elmalign);
+							Datum dataValue, bool isNull, int arraytyplen,
+							int elmlen, bool elmbyval, char elmalign, char elmstor);
 
 extern Datum array_map(Datum arrayd,
 					   struct ExprState *exprstate, struct ExprContext *econtext,
@@ -393,21 +400,23 @@ 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);
+								  int elmlen, bool elmbyval,
+								  char elmalign, char elmstor);
 extern ArrayType *construct_array_builtin(Datum *elems, int nelems, Oid elmtype);
 extern ArrayType *construct_md_array(Datum *elems,
 									 bool *nulls,
 									 int ndims,
 									 int *dims,
 									 int *lbs,
-									 Oid elmtype, int elmlen, bool elmbyval, char elmalign);
+									 Oid elmtype, int elmlen, bool elmbyval,
+									 char elmalign, char elmstor);
 extern ArrayType *construct_empty_array(Oid elmtype);
 extern ExpandedArrayHeader *construct_empty_expanded_array(Oid element_type,
 														   MemoryContext parentcontext,
 														   ArrayMetaState *metacache);
 extern void deconstruct_array(ArrayType *array,
 							  Oid elmtype,
-							  int elmlen, bool elmbyval, char elmalign,
+							  int elmlen, bool elmbyval, char elmalign, char elmstor,
 							  Datum **elemsp, bool **nullsp, int *nelemsp);
 extern void deconstruct_array_builtin(ArrayType *array,
 									  Oid elmtype,
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 20446f6f836..5ee3ab2cdb8 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -16,6 +16,7 @@
 #include "access/attnum.h"
 #include "access/htup.h"
 #include "nodes/pg_list.h"
+#include "utils/array.h"
 
 /* avoid including subscripting.h here */
 struct SubscriptRoutines;
@@ -150,7 +151,12 @@ extern bool get_typbyval(Oid typid);
 extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
 extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
 								 char *typalign);
+extern void get_type_stores(Oid typid, int16 *typlen,
+							bool *typbyval, char *typalign, char *typstor);
 extern Oid	getTypeIOParam(HeapTuple typeTuple);
+extern void array_type_metadata(Oid typid,
+								IOFuncSelector which_func,
+								ArrayMetaState *metadata);
 extern void get_type_io_data(Oid typid,
 							 IOFuncSelector which_func,
 							 int16 *typlen,
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 773a5d2c347..339745f5081 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -85,6 +85,12 @@ extern pg_nodiscard void *repalloc_extended(void *pointer,
 extern pg_nodiscard void *repalloc0(void *pointer, Size oldsize, Size size);
 extern void pfree(void *pointer);
 
+#define PFREE_IF_NEW(newptr, oldptr) \
+	do { \
+		if ((Pointer)(newptr) != (Pointer)(oldptr)) \
+			pfree(newptr); \
+	} while (0)
+
 /*
  * Variants with easier notation and more type safety
  */
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 1b1677e333b..85b5b1be9f4 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1484,6 +1484,7 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
 	int16		typlen;
 	bool		typbyval;
 	char		typalign,
+				typstor,
 				typdelim;
 	Oid			typioparam;
 	Oid			typoutputfunc;
@@ -1506,6 +1507,8 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
 					 &typlen, &typbyval, &typalign,
 					 &typdelim, &typioparam, &typoutputfunc);
 
+	typstor = get_typstorage(elementtype);
+
 	/* Check for a transform function */
 	transform_funcid = get_transform_fromsql(elementtype,
 											 current_call_data->prodesc->lang_oid,
@@ -1531,8 +1534,8 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
 	else
 	{
 		deconstruct_array(ar, elementtype, typlen, typbyval,
-						  typalign, &info->elements, &info->nulls,
-						  &nitems);
+						  typalign, typstor,
+						  &info->elements, &info->nulls, &nitems);
 
 		/* Get total number of elements in each dimension */
 		info->nelems = palloc(sizeof(int) * info->ndims);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index e31206e7f4c..067b82427d6 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -1491,7 +1491,9 @@ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
 								  PointerGetDatum(construct_md_array(elems, NULL,
 																	 1, dims, lbs,
 																	 TEXTOID,
-																	 -1, false, TYPALIGN_INT)),
+																	 -1, false,
+																	 TYPALIGN_INT,
+																	 TYPSTORAGE_EXTENDED)),
 								  false, true);
 			}
 			else
diff --git a/src/test/modules/test_regex/test_regex.c b/src/test/modules/test_regex/test_regex.c
index a780c678fbc..b3e0564f6da 100644
--- a/src/test/modules/test_regex/test_regex.c
+++ b/src/test/modules/test_regex/test_regex.c
@@ -678,7 +678,8 @@ build_test_info_result(regex_t *cpattern, test_re_flags *flags)
 	lbs[0] = 1;
 	/* XXX: this hardcodes assumptions about the text type */
 	return construct_md_array(elems, NULL, 1, dims, lbs,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false,
+							  TYPALIGN_INT, TYPSTORAGE_EXTENDED);
 }
 
 /*
@@ -758,5 +759,6 @@ build_test_match_result(test_regex_ctx *matchctx)
 	lbs[0] = 1;
 	/* XXX: this hardcodes assumptions about the text type */
 	return construct_md_array(elems, nulls, 1, dims, lbs,
-							  TEXTOID, -1, false, TYPALIGN_INT);
+							  TEXTOID, -1, false,
+							  TYPALIGN_INT, TYPSTORAGE_EXTENDED);
 }