diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index bc79ec29ffcd978bb6a668ba72898dead8a14307..0d1d4022106ab07b569fcbfca3cf1ead765934d7 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.108 2004/12/31 21:59:07 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.109 2005/03/07 04:42:16 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -31,19 +31,19 @@
 #include "utils/typcache.h"
 
 
-/* ----------------------------------------------------------------
- *		CreateTemplateTupleDesc
- *
- *		This function allocates and zeros a tuple descriptor structure.
+/*
+ * CreateTemplateTupleDesc
+ *		This function allocates an empty tuple descriptor structure.
  *
  * Tuple type ID information is initially set for an anonymous record type;
  * caller can overwrite this if needed.
- * ----------------------------------------------------------------
  */
 TupleDesc
 CreateTemplateTupleDesc(int natts, bool hasoid)
 {
 	TupleDesc	desc;
+	char	   *stg;
+	int			attroffset;
 
 	/*
 	 * sanity checks
@@ -51,15 +51,33 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
 	AssertArg(natts >= 0);
 
 	/*
-	 * Allocate enough memory for the tuple descriptor, and zero the
-	 * attrs[] array since TupleDescInitEntry assumes that the array is
-	 * filled with NULL pointers.
+	 * 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.
 	 */
-	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
+	attroffset = sizeof(struct tupleDesc) + natts * sizeof(Form_pg_attribute);
+	attroffset = MAXALIGN(attroffset);
+	stg = palloc(attroffset + natts * MAXALIGN(ATTRIBUTE_TUPLE_SIZE));
+	desc = (TupleDesc) stg;
 
 	if (natts > 0)
-		desc->attrs = (Form_pg_attribute *)
-			palloc0(natts * sizeof(Form_pg_attribute));
+	{
+		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_TUPLE_SIZE);
+		}
+	}
 	else
 		desc->attrs = NULL;
 
@@ -75,15 +93,16 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
 	return desc;
 }
 
-/* ----------------------------------------------------------------
- *		CreateTupleDesc
- *
+/*
+ * CreateTupleDesc
  *		This function allocates a new TupleDesc pointing to a given
- *		Form_pg_attribute array
+ *		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.
- * ----------------------------------------------------------------
  */
 TupleDesc
 CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
@@ -106,14 +125,12 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
 	return desc;
 }
 
-/* ----------------------------------------------------------------
- *		CreateTupleDescCopy
- *
+/*
+ * CreateTupleDescCopy
  *		This function creates a new TupleDesc by copying from an existing
- *		TupleDesc
+ *		TupleDesc.
  *
- *		!!! Constraints and defaults are not copied !!!
- * ----------------------------------------------------------------
+ * !!! Constraints and defaults are not copied !!!
  */
 TupleDesc
 CreateTupleDescCopy(TupleDesc tupdesc)
@@ -121,38 +138,25 @@ CreateTupleDescCopy(TupleDesc tupdesc)
 	TupleDesc	desc;
 	int			i;
 
-	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
-	desc->natts = tupdesc->natts;
-	if (desc->natts > 0)
+	desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
+
+	for (i = 0; i < desc->natts; i++)
 	{
-		desc->attrs = (Form_pg_attribute *)
-			palloc(desc->natts * sizeof(Form_pg_attribute));
-		for (i = 0; i < desc->natts; i++)
-		{
-			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
-			memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
-			desc->attrs[i]->attnotnull = false;
-			desc->attrs[i]->atthasdef = false;
-		}
+		memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
+		desc->attrs[i]->attnotnull = false;
+		desc->attrs[i]->atthasdef = false;
 	}
-	else
-		desc->attrs = NULL;
-
-	desc->constr = NULL;
 
 	desc->tdtypeid = tupdesc->tdtypeid;
 	desc->tdtypmod = tupdesc->tdtypmod;
-	desc->tdhasoid = tupdesc->tdhasoid;
 
 	return desc;
 }
 
-/* ----------------------------------------------------------------
- *		CreateTupleDescCopyConstr
- *
+/*
+ * CreateTupleDescCopyConstr
  *		This function creates a new TupleDesc by copying from an existing
- *		TupleDesc (including its constraints and defaults)
- * ----------------------------------------------------------------
+ *		TupleDesc (including its constraints and defaults).
  */
 TupleDesc
 CreateTupleDescCopyConstr(TupleDesc tupdesc)
@@ -161,20 +165,12 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
 	TupleConstr *constr = tupdesc->constr;
 	int			i;
 
-	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
-	desc->natts = tupdesc->natts;
-	if (desc->natts > 0)
+	desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
+
+	for (i = 0; i < desc->natts; i++)
 	{
-		desc->attrs = (Form_pg_attribute *)
-			palloc(desc->natts * sizeof(Form_pg_attribute));
-		for (i = 0; i < desc->natts; i++)
-		{
-			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
-			memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
-		}
+		memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
 	}
-	else
-		desc->attrs = NULL;
 
 	if (constr)
 	{
@@ -208,12 +204,9 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
 
 		desc->constr = cpy;
 	}
-	else
-		desc->constr = NULL;
 
 	desc->tdtypeid = tupdesc->tdtypeid;
 	desc->tdtypmod = tupdesc->tdtypmod;
-	desc->tdhasoid = tupdesc->tdhasoid;
 
 	return desc;
 }
@@ -226,10 +219,6 @@ FreeTupleDesc(TupleDesc tupdesc)
 {
 	int			i;
 
-	for (i = 0; i < tupdesc->natts; i++)
-		pfree(tupdesc->attrs[i]);
-	if (tupdesc->attrs)
-		pfree(tupdesc->attrs);
 	if (tupdesc->constr)
 	{
 		if (tupdesc->constr->num_defval > 0)
@@ -379,12 +368,10 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 	return true;
 }
 
-/* ----------------------------------------------------------------
- *		TupleDescInitEntry
- *
+/*
+ * TupleDescInitEntry
  *		This function initializes a single attribute structure in
- *		a preallocated tuple descriptor.
- * ----------------------------------------------------------------
+ *		a previously allocated tuple descriptor.
  */
 void
 TupleDescInitEntry(TupleDesc desc,
@@ -404,18 +391,12 @@ TupleDescInitEntry(TupleDesc desc,
 	AssertArg(PointerIsValid(desc));
 	AssertArg(attributeNumber >= 1);
 	AssertArg(attributeNumber <= desc->natts);
-	AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
-
-	/*
-	 * allocate storage for this attribute
-	 */
-
-	att = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
-	desc->attrs[attributeNumber - 1] = att;
 
 	/*
 	 * initialize the attribute fields
 	 */
+	att = desc->attrs[attributeNumber - 1];
+
 	att->attrelid = 0;			/* dummy value */
 
 	/*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0d389c408422f4662548e14ab31446602cffa4fd..a3bcca272e373f4f7740ec9813b45fd72a2ab1d5 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.245 2005/03/04 20:21:05 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.246 2005/03/07 04:42:16 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -97,14 +97,11 @@ ConstructTupleDescriptor(Relation heapRelation,
 	for (i = 0; i < numatts; i++)
 	{
 		AttrNumber	atnum = indexInfo->ii_KeyAttrNumbers[i];
-		Form_pg_attribute to;
+		Form_pg_attribute to = indexTupDesc->attrs[i];
 		HeapTuple	tuple;
 		Form_pg_type typeTup;
 		Oid			keyType;
 
-		indexTupDesc->attrs[i] = to =
-			(Form_pg_attribute) palloc0(ATTRIBUTE_TUPLE_SIZE);
-
 		if (atnum != 0)
 		{
 			/* Simple index column */
@@ -152,6 +149,8 @@ ConstructTupleDescriptor(Relation heapRelation,
 			/* Expressional index */
 			Node	   *indexkey;
 
+			MemSet(to, 0, ATTRIBUTE_TUPLE_SIZE);
+
 			if (indexpr_item == NULL)	/* shouldn't happen */
 				elog(ERROR, "too few entries in indexprs list");
 			indexkey = (Node *) lfirst(indexpr_item);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 50ffb0f1b0e8d8e1d90310227f967f1416ac12b3..d1143381d2d7ccddb9b4299ed47d6c973206d84f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.215 2005/01/10 20:02:23 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.216 2005/03/07 04:42:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -491,12 +491,8 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
 			elog(ERROR, "invalid attribute number %d for %s",
 				 attp->attnum, RelationGetRelationName(relation));
 
-		relation->rd_att->attrs[attp->attnum - 1] =
-			(Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
-												   ATTRIBUTE_TUPLE_SIZE);
-
-		memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]),
-			   (char *) attp,
+		memcpy(relation->rd_att->attrs[attp->attnum - 1],
+			   attp,
 			   ATTRIBUTE_TUPLE_SIZE);
 
 		/* Update constraint/default info */
@@ -1338,9 +1334,8 @@ formrdesc(const char *relationName, Oid relationReltype,
 	has_not_null = false;
 	for (i = 0; i < natts; i++)
 	{
-		relation->rd_att->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
-		memcpy((char *) relation->rd_att->attrs[i],
-			   (char *) &att[i],
+		memcpy(relation->rd_att->attrs[i],
+			   &att[i],
 			   ATTRIBUTE_TUPLE_SIZE);
 		has_not_null |= att[i].attnotnull;
 		/* make sure attcacheoff is valid */
@@ -3044,9 +3039,8 @@ load_relcache_init_file(void)
 		{
 			if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
 				goto read_failed;
-
-			rel->rd_att->attrs[i] = (Form_pg_attribute) palloc(len);
-
+			if (len != ATTRIBUTE_TUPLE_SIZE)
+				goto read_failed;
 			if ((nread = fread(rel->rd_att->attrs[i], 1, len, fp)) != len)
 				goto read_failed;
 
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 8ffb2e70c854a32306bac3d6b93f77000fddc48b..f9b89ed253c3e13a923b6f7c10e5f0ee692de767 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.46 2004/12/31 22:03:21 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.47 2005/03/07 04:42:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,13 +42,17 @@ typedef struct tupleConstr
 } TupleConstr;
 
 /*
- * This structure contains all information (i.e. from Classes
- * pg_attribute, pg_attrdef, pg_constraint) for the structure of a tuple.
+ * This struct is passed around within the backend to describe the structure
+ * of tuples.  For tuples coming from on-disk relations, the information is
+ * collected from the pg_attribute, pg_attrdef, and pg_constraint catalogs.
+ * Transient row types (such as the result of a join query) have anonymous
+ * TupleDesc structs that generally omit any constraint info; therefore the
+ * structure is designed to let the constraints be omitted efficiently.
  *
  * Note that only user attributes, not system attributes, are mentioned in
  * TupleDesc; with the exception that tdhasoid indicates if OID is present.
  *
- * If the tuple is known to correspond to a named rowtype (such as a table's
+ * If the tupdesc is known to correspond to a named rowtype (such as a table's
  * rowtype) then tdtypeid identifies that type and tdtypmod is -1.	Otherwise
  * tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous
  * row type, or a value >= 0 to allow the rowtype to be looked up in the
@@ -56,13 +60,13 @@ typedef struct tupleConstr
  */
 typedef struct tupleDesc
 {
-	int			natts;			/* Number of attributes in the tuple */
+	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;
+	/* 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 */
+	bool		tdhasoid;		/* tuple has oid attribute in its header */
 }	*TupleDesc;