stored short varlena in array

Started by Quan Zongliangabout 1 year ago3 messages
#1Quan Zongliang
quanzongliang@yeah.net

Hi

Now, the varlena type is stored directly in the array. Did not consider
short varlena. If it's like fill_val(), using short varlena saves memory
footprint and disk space.
In TODO, there is a requirement to be implemented:
Allow single-byte header storage for array elements

This patch modifies many files.
Based on 38da053463bef32adf563ddee5277d16d2b6c5af
Has passed the regression test.
But it can affect many contribs. The code needs to be adjusted. Like
hstore and ltree.

Disk space usage test

create table t1 (c1 varchar[]);
insert into t1
select '{a,b,c,d,e,f,g,h,i,j,k,l,m,n}'
from generate_series(1,100000);
select pg_relation_size('t1')/8192;

before
postgres=# select pg_relation_size('t1')/8192;
?column?
----------
2041
(1 row)

after
postgres=# select pg_relation_size('t1')/8192;
?column?
----------
1334
(1 row)

Memory usage
'{a,b,c,d,e,f,g,h,i,j,k,l,m,n}'::varchar[]

before
136 bytes
after
80 bytes

--
Zongliang Quan

#2Quan Zongliang
quanzongliang@yeah.net
In reply to: Quan Zongliang (#1)
1 attachment(s)
Re: stored short varlena in array

On 2024/12/25 14:29, Quan Zongliang wrote:

Hi

Now, the varlena type is stored directly in the array. Did not consider
short varlena. If it's like fill_val(), using short varlena saves memory
footprint and disk space.
In TODO, there is a requirement to be implemented:
 Allow single-byte header storage for array elements

This patch modifies many files.
Based on 38da053463bef32adf563ddee5277d16d2b6c5af
Has passed the regression test.
But it can affect many contribs. The code needs to be adjusted. Like
hstore and ltree.

Sorry I forgot the patch attachment.

Attachments:

arrayelemhdr.patchtext/plain; charset=UTF-8; name=arrayelemhdr.patchDownload
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);
 }
#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Quan Zongliang (#1)
Re: stored short varlena in array

Quan Zongliang <quanzongliang@yeah.net> writes:

Now, the varlena type is stored directly in the array. Did not consider
short varlena. If it's like fill_val(), using short varlena saves memory
footprint and disk space.

TBH, I think this is a bad idea and we should reject it. As you have
already discovered, the code footprint of such a change is enormous
(and I have little confidence that you found all the places to fix).
The consequences would be equally dire in extensions, which'd likely
be dealing with ensuing bugs for years to come.

The reason we didn't do this when we originally invented short varlena
headers is that we presumed that array-level compression would remove
most of the benefit. Of course that only happens if the array is big
enough to get the attention of the tuple toaster, which is why your
example with very small arrays shows a benefit. But I'm doubtful that
such use-cases justify the pain we'd endure getting to the point where
this'd work reliably. The percentage savings drops off drastically as
the length of the individual strings grows, so this example with
one-byte strings is very much a best-case scenario.

In short, I'm afraid this ship sailed a long time ago. Perhaps it
was a poor decision but I think we're stuck with it.

regards, tom lane