diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index a5df2d64e2880d61d5f83627b5627335b4b2d1e1..75b191ba2aa96bbe87eb51bf3ba41ddf1cee2427 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -41,8 +41,6 @@ TupleDesc
 CreateTemplateTupleDesc(int natts, bool hasoid)
 {
 	TupleDesc	desc;
-	char	   *stg;
-	int			attroffset;
 
 	/*
 	 * sanity checks
@@ -51,38 +49,10 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
 
 	/*
 	 * Allocate enough memory for the tuple descriptor, including the
-	 * attribute rows, and set up the attribute row pointers.
-	 *
-	 * Note: we assume that sizeof(struct tupleDesc) is a multiple of the
-	 * struct pointer alignment requirement, and hence we don't need to insert
-	 * alignment padding between the struct and the array of attribute row
-	 * pointers.
-	 *
-	 * Note: Only the fixed part of pg_attribute rows is included in tuple
-	 * descriptors, so we only need ATTRIBUTE_FIXED_PART_SIZE space per attr.
-	 * That might need alignment padding, however.
+	 * attribute rows.
 	 */
-	attroffset = sizeof(struct tupleDesc) + natts * sizeof(Form_pg_attribute);
-	attroffset = MAXALIGN(attroffset);
-	stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_FIXED_PART_SIZE));
-	desc = (TupleDesc) stg;
-
-	if (natts > 0)
-	{
-		Form_pg_attribute *attrs;
-		int			i;
-
-		attrs = (Form_pg_attribute *) (stg + sizeof(struct tupleDesc));
-		desc->attrs = attrs;
-		stg += attroffset;
-		for (i = 0; i < natts; i++)
-		{
-			attrs[i] = (Form_pg_attribute) stg;
-			stg += MAXALIGN(ATTRIBUTE_FIXED_PART_SIZE);
-		}
-	}
-	else
-		desc->attrs = NULL;
+	desc = (TupleDesc) palloc(offsetof(struct tupleDesc, attrs) +
+							  natts * sizeof(FormData_pg_attribute));
 
 	/*
 	 * Initialize other fields of the tupdesc.
@@ -99,12 +69,9 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
 
 /*
  * CreateTupleDesc
- *		This function allocates a new TupleDesc pointing to a given
+ *		This function allocates a new TupleDesc by copying a given
  *		Form_pg_attribute array.
  *
- * Note: if the TupleDesc is ever freed, the Form_pg_attribute array
- * will not be freed thereby.
- *
  * Tuple type ID information is initially set for an anonymous record type;
  * caller can overwrite this if needed.
  */
@@ -112,20 +79,12 @@ TupleDesc
 CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
 {
 	TupleDesc	desc;
+	int			i;
 
-	/*
-	 * sanity checks
-	 */
-	AssertArg(natts >= 0);
+	desc = CreateTemplateTupleDesc(natts, hasoid);
 
-	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
-	desc->attrs = attrs;
-	desc->natts = natts;
-	desc->constr = NULL;
-	desc->tdtypeid = RECORDOID;
-	desc->tdtypmod = -1;
-	desc->tdhasoid = hasoid;
-	desc->tdrefcount = -1;		/* assume not reference-counted */
+	for (i = 0; i < natts; ++i)
+		memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
 
 	return desc;
 }
@@ -147,10 +106,12 @@ CreateTupleDescCopy(TupleDesc tupdesc)
 
 	for (i = 0; i < desc->natts; i++)
 	{
-		memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
-		desc->attrs[i]->attnotnull = false;
-		desc->attrs[i]->atthasdef = false;
-		desc->attrs[i]->attidentity = '\0';
+		Form_pg_attribute att = TupleDescAttr(desc, i);
+
+		memcpy(att, &tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
+		att->attnotnull = false;
+		att->atthasdef = false;
+		att->attidentity = '\0';
 	}
 
 	desc->tdtypeid = tupdesc->tdtypeid;
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 31b77a08fae87ba5376e2647fd29c53bfa3ed789..39fd59686a7145d28c614c83c9857610862cd9ec 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -71,17 +71,17 @@ typedef struct tupleConstr
 typedef struct tupleDesc
 {
 	int			natts;			/* number of attributes in the tuple */
-	Form_pg_attribute *attrs;
-	/* attrs[N] is a pointer to the description of Attribute Number N+1 */
-	TupleConstr *constr;		/* constraints, or NULL if none */
 	Oid			tdtypeid;		/* composite type ID for tuple type */
 	int32		tdtypmod;		/* typmod for tuple type */
 	bool		tdhasoid;		/* tuple has oid attribute in its header */
 	int			tdrefcount;		/* reference count, or -1 if not counting */
+	TupleConstr *constr;		/* constraints, or NULL if none */
+	/* attrs[N] is the description of Attribute Number N+1 */
+	FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER];
 }		   *TupleDesc;
 
 /* Accessor for the i'th attribute of tupdesc. */
-#define TupleDescAttr(tupdesc, i) ((tupdesc)->attrs[(i)])
+#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)])
 
 extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);