============================================================
*** src/backend/access/common/tupdesc.c	83ca807d4fdd572c409bc9214922b6ba9da7ce18
--- src/backend/access/common/tupdesc.c	7b97197c862755fecba8d06a9acacae5204336ec
***************
*** 23,48 ****
  #include "catalog/pg_type.h"
  #include "parser/parse_type.h"
  #include "utils/builtins.h"
  #include "utils/syscache.h"
  
  
  /*
   * 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
! 	 */
  	AssertArg(natts >= 0);
  
  	/*
--- 23,78 ----
  #include "catalog/pg_type.h"
  #include "parser/parse_type.h"
  #include "utils/builtins.h"
+ #include "utils/memutils.h"
+ #include "utils/resowner.h"
  #include "utils/syscache.h"
  
+ static TupleDesc CreateTemplateTupleDescInternal(int natts, bool hasoid,
+ 												 bool persistent);
+ static TupleDesc CreateTupleDescCopyInternal(TupleDesc tupdesc,
+ 											 bool persistent);
  
  /*
   * 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.
!  *
!  * The refcount on the returned TupleDesc is initially 1: the caller
!  * should decrement the reference count via DecrTupleDescRefCount()
!  * when finished. The TupleDesc will automatically be released (with a
!  * warning) when the CurrentResourceOwner is released.
   */
  TupleDesc
  CreateTemplateTupleDesc(int natts, bool hasoid)
  {
+ 	return CreateTemplateTupleDescInternal(natts, hasoid, false);
+ }
+ 
+ /*
+  * CreateTemplateTupleDescPersistent
+  *		Like CreateTemplateTupleDesc, but creates a "persistent"
+  *		reference to the new TupleDesc: that is, the reference will
+  *		not be released when the CurrentResourceOwner is released.
+  *
+  * This is needed when a TupleDesc is being created, but
+  * CurrentResourceOwner is not yet defined.
+  */
+ TupleDesc
+ CreateTemplateTupleDescPersistent(int natts, bool hasoid)
+ {
+ 	return CreateTemplateTupleDescInternal(natts, hasoid, true);
+ }
+ 
+ static TupleDesc
+ CreateTemplateTupleDescInternal(int natts, bool hasoid, bool persistent)
+ {
  	TupleDesc	desc;
  	char	   *stg;
  	int			attroffset;
  
! 	/* sanity checks */
  	AssertArg(natts >= 0);
  
  	/*
***************
*** 56,62 ****
  	 */
  	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)
--- 86,93 ----
  	 */
  	attroffset = sizeof(struct tupleDesc) + natts * sizeof(Form_pg_attribute);
  	attroffset = MAXALIGN(attroffset);
! 	stg = MemoryContextAlloc(TopMemoryContext,
! 							 attroffset + natts * MAXALIGN(ATTRIBUTE_TUPLE_SIZE));
  	desc = (TupleDesc) stg;
  
  	if (natts > 0)
***************
*** 76,84 ****
  	else
  		desc->attrs = NULL;
  
! 	/*
! 	 * Initialize other fields of the tupdesc.
! 	 */
  	desc->natts = natts;
  	desc->constr = NULL;
  	desc->tdtypeid = RECORDOID;
--- 107,113 ----
  	else
  		desc->attrs = NULL;
  
! 	/* Initialize the other fields of the tupdesc. */
  	desc->natts = natts;
  	desc->constr = NULL;
  	desc->tdtypeid = RECORDOID;
***************
*** 85,90 ****
--- 114,123 ----
  	desc->tdtypmod = -1;
  	desc->tdhasoid = hasoid;
  
+ 	desc->refcount = 0;
+ 	/* Set the refcount to 1; if !persistent, inform resowner */
+ 	IncrTupleDescRefCount(desc, persistent);
+ 
  	return desc;
  }
  
***************
*** 93,103 ****
   *		This function allocates a new TupleDesc pointing to 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.
   */
  TupleDesc
  CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
--- 126,140 ----
   *		This function allocates a new TupleDesc pointing to a given
   *		Form_pg_attribute array.
   *
!  * Note: when the TupleDesc is destroyed, 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.
+  *
+  * The refcount on the returned TupleDesc is initially 1: the caller
+  * should decrement the reference count via DecrTupleDescRefCount()
+  * when finished.
   */
  TupleDesc
  CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
***************
*** 109,115 ****
  	 */
  	AssertArg(natts >= 0);
  
! 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
  	desc->attrs = attrs;
  	desc->natts = natts;
  	desc->constr = NULL;
--- 146,153 ----
  	 */
  	AssertArg(natts >= 0);
  
! 	desc = (TupleDesc) MemoryContextAlloc(TopMemoryContext,
! 										  sizeof(struct tupleDesc));
  	desc->attrs = attrs;
  	desc->natts = natts;
  	desc->constr = NULL;
***************
*** 117,122 ****
--- 155,164 ----
  	desc->tdtypmod = -1;
  	desc->tdhasoid = hasoid;
  
+ 	desc->refcount = 0;
+ 	/* Set the refcount to 1 and inform the CurrentResourceOwner */
+ 	IncrTupleDescRefCount(desc, false);
+ 
  	return desc;
  }
  
***************
*** 125,139 ****
   *		This function creates a new TupleDesc by copying from an existing
   *		TupleDesc.
   *
   * !!! Constraints and defaults are not copied !!!
   */
  TupleDesc
  CreateTupleDescCopy(TupleDesc tupdesc)
  {
  	TupleDesc	desc;
  	int			i;
  
! 	desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
  
  	for (i = 0; i < desc->natts; i++)
  	{
--- 167,205 ----
   *		This function creates a new TupleDesc by copying from an existing
   *		TupleDesc.
   *
+  * The refcount on the returned TupleDesc is initially 1: the caller
+  * should decrement the reference count via DecrTupleDescRefCount()
+  * when finished.
+  *
   * !!! Constraints and defaults are not copied !!!
   */
  TupleDesc
  CreateTupleDescCopy(TupleDesc tupdesc)
  {
+ 	return CreateTupleDescCopyInternal(tupdesc, false);
+ }
+ 
+ /*
+  * CreateTupleDescCopyPersistent
+  *		Like CreateTupleDescCopy, but creates a "persistent" reference
+  *		to the copy of the TupleDesc: that is, the reference will not
+  *		be released when the CurrentResourceOwner is released.
+  */
+ TupleDesc
+ CreateTupleDescCopyPersistent(TupleDesc tupdesc)
+ {
+ 	return CreateTupleDescCopyInternal(tupdesc, true);
+ }
+ 
+ static TupleDesc
+ CreateTupleDescCopyInternal(TupleDesc tupdesc, bool persistent)
+ {
  	TupleDesc	desc;
  	int			i;
  
! 	desc = CreateTemplateTupleDescInternal(tupdesc->natts,
! 										   tupdesc->tdhasoid,
! 										   persistent);
  
  	for (i = 0; i < desc->natts; i++)
  	{
***************
*** 152,157 ****
--- 218,227 ----
   * CreateTupleDescCopyConstr
   *		This function creates a new TupleDesc by copying from an existing
   *		TupleDesc (including its constraints and defaults).
+  *
+  * The refcount on the returned TupleDesc is initially 1: the caller
+  * should decrement the reference count via DecrTupleDescRefCount()
+  * when finished.
   */
  TupleDesc
  CreateTupleDescCopyConstr(TupleDesc tupdesc)
***************
*** 163,176 ****
  	desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
  
  	for (i = 0; i < desc->natts; i++)
- 	{
  		memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
- 	}
  
  	if (constr)
  	{
! 		TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
  
  		cpy->has_not_null = constr->has_not_null;
  
  		if ((cpy->num_defval = constr->num_defval) > 0)
--- 233,256 ----
  	desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
  
  	for (i = 0; i < desc->natts; i++)
  		memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
  
  	if (constr)
  	{
! 		TupleConstr *cpy;
! 		MemoryContext old_cxt;
  
+ 		/*
+ 		 * allocate constraints in TopMemoryContext, like the TupleDesc itself.
+ 		 *
+ 		 * XXX: if we elog error within this block, we'll leak any
+ 		 * allocations we've done here, as well as the allocation of
+ 		 * "desc" itself at the beginning of the routine. Is there a
+ 		 * reasonable way to avoid this?
+ 		 */
+ 		old_cxt = MemoryContextSwitchTo(TopMemoryContext);
+ 
+ 		cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
  		cpy->has_not_null = constr->has_not_null;
  
  		if ((cpy->num_defval = constr->num_defval) > 0)
***************
*** 198,203 ****
--- 278,284 ----
  		}
  
  		desc->constr = cpy;
+ 		MemoryContextSwitchTo(old_cxt);
  	}
  
  	desc->tdtypeid = tupdesc->tdtypeid;
***************
*** 207,233 ****
  }
  
  /*
!  * Free a TupleDesc including all substructure
   */
  void
! FreeTupleDesc(TupleDesc tupdesc)
  {
! 	int			i;
  
! 	if (tupdesc->constr)
  	{
! 		if (tupdesc->constr->num_defval > 0)
! 		{
! 			AttrDefault *attrdef = tupdesc->constr->defval;
  
! 			for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
! 			{
! 				if (attrdef[i].adbin)
! 					pfree(attrdef[i].adbin);
! 			}
! 			pfree(attrdef);
! 		}
! 		if (tupdesc->constr->num_check > 0)
  		{
  			ConstrCheck *check = tupdesc->constr->check;
  
--- 288,335 ----
  }
  
  /*
!  * Increment the reference count on a TupleDesc. If "persistent" is
!  * false, the CurrentResourceOwner will be informed about the new
!  * reference (so it will automatically be released, with a warning, at
!  * the end of the current query). Otherwise, the reference is
!  * persistent: it will only be released when DecrTupleDescRefCount is
!  * invoked.
   */
  void
! IncrTupleDescRefCount(TupleDesc tupdesc, bool persistent)
  {
! 	Assert(tupdesc->refcount >= 0);
  
! 	if (!persistent)
! 		ResourceOwnerEnlargeTupleDescs(CurrentResourceOwner);
! 
! 	tupdesc->refcount++;
! 
! 	if (!persistent)
! 		ResourceOwnerRememberTupleDesc(CurrentResourceOwner, tupdesc);
! }
! 
! /*
!  * Decrement the reference count on a TupleDesc. When the refcount
!  * reaches zero, the TupleDesc is destroyed. If "persistent" is true,
!  * we are releasing a persistent reference -- see
!  * IncrTupleDescRefCount.
!  */
! void
! DecrTupleDescRefCount(TupleDesc tupdesc, bool persistent)
! {
! 	Assert(tupdesc->refcount > 0);
! 
! 	tupdesc->refcount--;
! 	if (!persistent)
! 		ResourceOwnerForgetTupleDesc(CurrentResourceOwner, tupdesc);
! 
! 	/* If this was the last reference, we can free the tupdesc */
! 	if (tupdesc->refcount == 0)
  	{
! 		int			i;
  
! 		if (tupdesc->constr)
  		{
  			if (tupdesc->constr->num_defval > 0)
  			{
***************
*** 229,257 ****
  		}
  		if (tupdesc->constr->num_check > 0)
  		{
! 			ConstrCheck *check = tupdesc->constr->check;
  
! 			for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
  			{
! 				if (check[i].ccname)
! 					pfree(check[i].ccname);
! 				if (check[i].ccbin)
! 					pfree(check[i].ccbin);
  			}
! 			pfree(check);
  		}
- 		pfree(tupdesc->constr);
- 	}
  
! 	pfree(tupdesc);
  }
  
  /*
   * Compare two TupleDesc structures for logical equality
   *
!  * Note: we deliberately do not check the attrelid and tdtypmod fields.
!  * This allows typcache.c to use this routine to see if a cached record type
!  * matches a requested type, and is harmless for relcache.c's uses.
   */
  bool
  equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
--- 331,375 ----
  		}
  		if (tupdesc->constr->num_check > 0)
  		{
! 			if (tupdesc->constr->num_defval > 0)
! 			{
! 				AttrDefault *attrdef = tupdesc->constr->defval;
  
! 				for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
! 				{
! 					if (attrdef[i].adbin)
! 						pfree(attrdef[i].adbin);
! 				}
! 				pfree(attrdef);
! 			}
! 			if (tupdesc->constr->num_check > 0)
  			{
! 				ConstrCheck *check = tupdesc->constr->check;
! 
! 				for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
! 				{
! 					if (check[i].ccname)
! 						pfree(check[i].ccname);
! 					if (check[i].ccbin)
! 						pfree(check[i].ccbin);
! 				}
! 				pfree(check);
  			}
! 			pfree(tupdesc->constr);
  		}
  
! 		pfree(tupdesc);
! 	}
  }
  
  /*
   * Compare two TupleDesc structures for logical equality
   *
!  * Note: we deliberately do not check the attrelid and tdtypmod
!  * fields.  This allows typcache.c to use this routine to see if a
!  * cached record type matches a requested type, and is harmless for
!  * relcache.c's uses. We also don't compare the refcount field, as it
!  * does not influence logical equality.
   */
  bool
  equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
***************
*** 256,264 ****
  bool
  equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
  {
! 	int			i,
! 				j,
! 				n;
  
  	if (tupdesc1->natts != tupdesc2->natts)
  		return false;
--- 374,380 ----
  bool
  equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
  {
! 	int			i;
  
  	if (tupdesc1->natts != tupdesc2->natts)
  		return false;
***************
*** 318,323 ****
--- 434,441 ----
  	{
  		TupleConstr *constr1 = tupdesc1->constr;
  		TupleConstr *constr2 = tupdesc2->constr;
+ 		int			 j,
+ 					 n;
  
  		if (constr2 == NULL)
  			return false;
***************
*** 453,458 ****
--- 571,582 ----
   * Note: the default assumption is no OIDs; caller may modify the returned
   * TupleDesc if it wants OIDs.	Also, tdtypeid will need to be filled in
   * later on.
+  *
+  * The refcount on the returned TupleDesc is initially 1: the caller
+  * should decrement the reference count via DecrTupleDescRefCount()
+  * when finished. The reference will be released automatically (and a
+  * warning emitted) if it has not been released manually by the end of
+  * the current transaction.
   */
  TupleDesc
  BuildDescForRelation(List *schema)
***************
*** 462,482 ****
  	ListCell   *l;
  	TupleDesc	desc;
  	AttrDefault *attrdef = NULL;
! 	TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
  	char	   *attname;
  	int32		atttypmod;
  	int			attdim;
  	int			ndef = 0;
  
  	/*
! 	 * allocate a new tuple descriptor
  	 */
! 	natts = list_length(schema);
! 	desc = CreateTemplateTupleDesc(natts, false);
  	constr->has_not_null = false;
  
  	attnum = 0;
- 
  	foreach(l, schema)
  	{
  		ColumnDef  *entry = lfirst(l);
--- 586,610 ----
  	ListCell   *l;
  	TupleDesc	desc;
  	AttrDefault *attrdef = NULL;
! 	TupleConstr *constr;
  	char	   *attname;
  	int32		atttypmod;
  	int			attdim;
  	int			ndef = 0;
  
+ 	natts = list_length(schema);
+ 	/* allocate a new tuple descriptor: no OIDs */
+ 	desc = CreateTemplateTupleDesc(natts, false);
+ 
  	/*
! 	 * The TupleDesc is allocated in TopMemoryContext, so be sure to
! 	 * allocate the constraints and defaults there as well.
  	 */
! 	constr = (TupleConstr *) MemoryContextAllocZero(TopMemoryContext,
! 													sizeof(TupleConstr));
  	constr->has_not_null = false;
  
  	attnum = 0;
  	foreach(l, schema)
  	{
  		ColumnDef  *entry = lfirst(l);
***************
*** 508,522 ****
  		desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
  
  		/*
! 		 * Note we copy only pre-cooked default expressions. Digestion of raw
! 		 * ones is someone else's problem.
  		 */
  		if (entry->cooked_default != NULL)
  		{
  			if (attrdef == NULL)
! 				attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault));
  			attrdef[ndef].adnum = attnum;
! 			attrdef[ndef].adbin = pstrdup(entry->cooked_default);
  			ndef++;
  			desc->attrs[attnum - 1]->atthasdef = true;
  		}
--- 636,652 ----
  		desc->attrs[attnum - 1]->attnotnull = entry->is_not_null;
  
  		/*
! 		 * Note we copy only pre-cooked default expressions. Digestion
! 		 * of raw ones is someone else's problem.
  		 */
  		if (entry->cooked_default != NULL)
  		{
  			if (attrdef == NULL)
! 				attrdef = MemoryContextAlloc(TopMemoryContext,
! 											 natts * sizeof(AttrDefault));
  			attrdef[ndef].adnum = attnum;
! 			attrdef[ndef].adbin = MemoryContextStrdup(TopMemoryContext,
! 													  entry->cooked_default);
  			ndef++;
  			desc->attrs[attnum - 1]->atthasdef = true;
  		}
============================================================
*** src/backend/catalog/heap.c	061fd2d464a71b7ca9e041867cf745946cc4106d
--- src/backend/catalog/heap.c	042085c2af04356985c7e8e470151b806c02c7d4
***************
*** 661,666 ****
--- 661,669 ----
   *
   *		creates a new cataloged relation.  see comments above.
   * --------------------------------
+  *
+  * XXX: from a quick glance, it seems this doesn't modify `tupdesc',
+  * right?
   */
  Oid
  heap_create_with_catalog(const char *relname,
============================================================
*** src/backend/catalog/pg_proc.c	c2650f60bc4ec98b5805a2ef47a36a189dfa1913
--- src/backend/catalog/pg_proc.c	5177eccc191f474a302bbbfab0337731d6a8ccda
***************
*** 306,311 ****
--- 306,316 ----
  					errmsg("cannot change return type of existing function"),
  				errdetail("Row type defined by OUT parameters is different."),
  						 errhint("Use DROP FUNCTION first.")));
+ 
+ 			if (olddesc)
+ 				DecrTupleDescRefCount(olddesc, false);
+ 			if (newdesc)
+ 				DecrTupleDescRefCount(newdesc, false);
  		}
  
  		/* Can't change aggregate status, either */
============================================================
*** src/backend/commands/cluster.c	b67817d34860ebefcd16d1b7d39946564cd7c75d
--- src/backend/commands/cluster.c	72fd6a4fb2363081cd463871d719f2d28386de19
***************
*** 556,574 ****
  Oid
  make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
  {
! 	TupleDesc	OldHeapDesc,
! 				tupdesc;
  	Oid			OIDNewHeap;
  	Relation	OldHeap;
  
  	OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
- 	OldHeapDesc = RelationGetDescr(OldHeap);
  
  	/*
  	 * Need to make a copy of the tuple descriptor, since
  	 * heap_create_with_catalog modifies it.
  	 */
! 	tupdesc = CreateTupleDescCopyConstr(OldHeapDesc);
  
  	OIDNewHeap = heap_create_with_catalog(NewName,
  										  RelationGetNamespace(OldHeap),
--- 556,572 ----
  Oid
  make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
  {
! 	TupleDesc	tupdesc;
  	Oid			OIDNewHeap;
  	Relation	OldHeap;
  
  	OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
  
  	/*
  	 * Need to make a copy of the tuple descriptor, since
  	 * heap_create_with_catalog modifies it.
  	 */
! 	tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(OldHeap));
  
  	OIDNewHeap = heap_create_with_catalog(NewName,
  										  RelationGetNamespace(OldHeap),
***************
*** 597,602 ****
--- 595,601 ----
  	AlterTableCreateToastTable(OIDNewHeap, true);
  
  	heap_close(OldHeap, NoLock);
+ 	DecrTupleDescRefCount(tupdesc, false);
  
  	return OIDNewHeap;
  }
============================================================
*** src/backend/commands/portalcmds.c	ba8e42896ec9179f64b4b1841199ca5b087456a9
--- src/backend/commands/portalcmds.c	02d88e1b1deab547f8cd25045da12d4f305de0a9
***************
*** 326,340 ****
  	Assert(portal->holdStore != NULL);
  
  	/*
! 	 * Before closing down the executor, we must copy the tupdesc into
! 	 * long-term memory, since it was created in executor memory.
  	 */
! 	oldcxt = MemoryContextSwitchTo(portal->holdContext);
  
- 	portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
- 
- 	MemoryContextSwitchTo(oldcxt);
- 
  	/*
  	 * Check for improper portal use, and mark portal active.
  	 */
--- 326,338 ----
  	Assert(portal->holdStore != NULL);
  
  	/*
! 	 * Obtain a persistent reference to the TupleDesc, so that it will
! 	 * remain valid beyond the end of the current transaction.
! 	 *
! 	 * XXX: need to release this reference -- how?
  	 */
! 	IncrTupleDescRefCount(portal->tupDesc, true);
  
  	/*
  	 * Check for improper portal use, and mark portal active.
  	 */
***************
*** 344,349 ****
--- 342,350 ----
  				 errmsg("portal \"%s\" cannot be run", portal->name)));
  	portal->status = PORTAL_ACTIVE;
  
+ 	/* Remember the cxt to switch back to */
+ 	oldcxt = CurrentMemoryContext;
+ 
  	/*
  	 * Set up global portal context pointers.
  	 */
============================================================
*** src/backend/commands/tablecmds.c	76d1d7a4c6fa64a4be960e16ee7c44f84939522b
--- src/backend/commands/tablecmds.c	bec332001a3ba70e9a68e80527aaead4b89a4367
***************
*** 500,505 ****
--- 500,506 ----
  	 * visible to anyone else anyway, until commit).
  	 */
  	relation_close(rel, NoLock);
+ 	DecrTupleDescRefCount(descriptor, false);
  
  	return relationId;
  }
============================================================
*** src/backend/executor/execGrouping.c	35e3b92b41d4303b613be0e7d7c6299156e56cf9
--- src/backend/executor/execGrouping.c	d0b46dae076e749337615e74c0b911b874131513
***************
*** 343,348 ****
--- 343,350 ----
  		/*
  		 * We copy the input tuple descriptor just for safety --- we assume
  		 * all input tuples will have equivalent descriptors.
+ 		 *
+ 		 * XXX: no need to do the copy, I believe
  		 */
  		tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
  		hashtable->tableslot = MakeSingleTupleTableSlot(tupdesc);
============================================================
*** src/backend/executor/execMain.c	e27d63160872f2c9d9821285d3108be557e83d3f
--- src/backend/executor/execMain.c	b357f62800b759fc82d1df1f52c4ea652e811457
***************
*** 764,770 ****
  												  ONCOMMIT_NOOP,
  												  allowSystemTableMods);
  
! 		FreeTupleDesc(tupdesc);
  
  		/*
  		 * Advance command counter so that the newly-created relation's
--- 764,770 ----
  												  ONCOMMIT_NOOP,
  												  allowSystemTableMods);
  
! 		DecrTupleDescRefCount(tupdesc, false);
  
  		/*
  		 * Advance command counter so that the newly-created relation's
============================================================
*** src/backend/executor/execQual.c	eb1daef85341439578a3f6bb73549c4420292bc3
--- src/backend/executor/execQual.c	4f908b1d8fdd8721da6ecc9e6c730c1debce5660
***************
*** 2792,2807 ****
  		tupType != tupDesc->tdtypeid ||
  		tupTypmod != tupDesc->tdtypmod)
  	{
- 		MemoryContext oldcontext;
- 
  		tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
- 		/* Copy the tupdesc into query storage for safety */
- 		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- 		tupDesc = CreateTupleDescCopy(tupDesc);
  		if (fstate->argdesc)
! 			FreeTupleDesc(fstate->argdesc);
  		fstate->argdesc = tupDesc;
- 		MemoryContextSwitchTo(oldcontext);
  	}
  
  	/*
--- 2792,2801 ----
  		tupType != tupDesc->tdtypeid ||
  		tupTypmod != tupDesc->tdtypmod)
  	{
  		tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
  		if (fstate->argdesc)
! 			DecrTupleDescRefCount(fstate->argdesc, false);
  		fstate->argdesc = tupDesc;
  	}
  
  	/*
***************
*** 2854,2869 ****
  	if (tupDesc == NULL ||
  		fstore->resulttype != tupDesc->tdtypeid)
  	{
- 		MemoryContext oldcontext;
- 
  		tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
- 		/* Copy the tupdesc into query storage for safety */
- 		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- 		tupDesc = CreateTupleDescCopy(tupDesc);
  		if (fstate->argdesc)
! 			FreeTupleDesc(fstate->argdesc);
  		fstate->argdesc = tupDesc;
- 		MemoryContextSwitchTo(oldcontext);
  	}
  
  	/* Allocate workspace */
--- 2848,2857 ----
  	if (tupDesc == NULL ||
  		fstore->resulttype != tupDesc->tdtypeid)
  	{
  		tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
  		if (fstate->argdesc)
! 			DecrTupleDescRefCount(fstate->argdesc, false);
  		fstate->argdesc = tupDesc;
  	}
  
  	/* Allocate workspace */
============================================================
*** src/backend/executor/execTuples.c	beb35fc6de01cf74389db494cfb73dc6bb1824b0
--- src/backend/executor/execTuples.c	f95fb47f1d8e827d18b3965e4c34d6162e749413
***************
*** 95,100 ****
--- 95,101 ----
  #include "utils/lsyscache.h"
  #include "utils/typcache.h"
  
+ /* XXX: TupleDesc refcounting needs changes here */
  
  static TupleDesc ExecTypeFromTLInternal(List *targetList,
  					   bool hasoid, bool skipjunk);
***************
*** 175,183 ****
  	Assert(table != NULL);
  
  	/*
! 	 * first free all the valid pointers in the tuple array and drop refcounts
! 	 * of any referenced buffers, if that's what the caller wants.  (There is
! 	 * probably no good reason for the caller ever not to want it!)
  	 */
  	if (shouldFree)
  	{
--- 176,185 ----
  	Assert(table != NULL);
  
  	/*
! 	 * first free all the valid pointers in the tuple array and drop
! 	 * refcounts of any referenced buffers and TupleDescs, if that's
! 	 * what the caller wants.  (There is probably no good reason for
! 	 * the caller ever not to want it!)
  	 */
  	if (shouldFree)
  	{
***************
*** 190,196 ****
  
  			ExecClearTuple(slot);
  			if (slot->tts_shouldFreeDesc)
! 				FreeTupleDesc(slot->tts_tupleDescriptor);
  			if (slot->tts_values)
  				pfree(slot->tts_values);
  			if (slot->tts_isnull)
--- 192,198 ----
  
  			ExecClearTuple(slot);
  			if (slot->tts_shouldFreeDesc)
! 				DecrTupleDescRefCount(slot->tts_tupleDescriptor, false);
  			if (slot->tts_values)
  				pfree(slot->tts_values);
  			if (slot->tts_isnull)
***************
*** 251,257 ****
  
  	ExecClearTuple(slot);
  	if (slot->tts_shouldFreeDesc)
! 		FreeTupleDesc(slot->tts_tupleDescriptor);
  	if (slot->tts_values)
  		pfree(slot->tts_values);
  	if (slot->tts_isnull)
--- 253,259 ----
  
  	ExecClearTuple(slot);
  	if (slot->tts_shouldFreeDesc)
! 		DecrTupleDescRefCount(slot->tts_tupleDescriptor, false);
  	if (slot->tts_values)
  		pfree(slot->tts_values);
  	if (slot->tts_isnull)
***************
*** 325,331 ****
  	 * present (we don't bother to check if they could be re-used).
  	 */
  	if (slot->tts_shouldFreeDesc)
! 		FreeTupleDesc(slot->tts_tupleDescriptor);
  
  	if (slot->tts_values)
  		pfree(slot->tts_values);
--- 327,333 ----
  	 * present (we don't bother to check if they could be re-used).
  	 */
  	if (slot->tts_shouldFreeDesc)
! 		DecrTupleDescRefCount(slot->tts_tupleDescriptor, false);
  
  	if (slot->tts_values)
  		pfree(slot->tts_values);
============================================================
*** src/backend/utils/cache/catcache.c	f0ffeb297db27dc31d43b2116d7d1a204c809f2e
--- src/backend/utils/cache/catcache.c	2fb41a653030fbbd745cabe8640abcc026e139a5
***************
*** 844,850 ****
  	cp->cc_reloid = reloid;
  	cp->cc_indexoid = indexoid;
  	cp->cc_relisshared = false; /* temporary */
! 	cp->cc_tupdesc = (TupleDesc) NULL;
  	cp->cc_reloidattr = reloidattr;
  	cp->cc_ntup = 0;
  	cp->cc_nbuckets = NCCBUCKETS;
--- 844,850 ----
  	cp->cc_reloid = reloid;
  	cp->cc_indexoid = indexoid;
  	cp->cc_relisshared = false; /* temporary */
! 	cp->cc_tupdesc = NULL;
  	cp->cc_reloidattr = reloidattr;
  	cp->cc_ntup = 0;
  	cp->cc_nbuckets = NCCBUCKETS;
***************
*** 925,934 ****
  
  	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
  
! 	/*
! 	 * copy the relcache's tuple descriptor to permanent cache storage
! 	 */
! 	tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
  
  	/*
  	 * save the relation's name and relisshared flag, too (cc_relname is used
--- 925,933 ----
  
  	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
  
! 	/* obtain our own persistent reference to the relation's tupdesc */
! 	tupdesc = RelationGetDescr(relation);
! 	IncrTupleDescRefCount(tupdesc, true);
  
  	/*
  	 * save the relation's name and relisshared flag, too (cc_relname is used
============================================================
*** src/backend/utils/cache/relcache.c	33edc76756ebe142a44c81e0785cafe4d50de293
--- src/backend/utils/cache/relcache.c	53ab6898d25a7d9a0805de605f7c189637fd8753
***************
*** 316,324 ****
  	/* initialize relation tuple form */
  	relation->rd_rel = relationForm;
  
! 	/* and allocate attribute tuple form storage */
! 	relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts,
! 											   relationForm->relhasoids);
  
  	MemoryContextSwitchTo(oldcxt);
  
--- 316,324 ----
  	/* initialize relation tuple form */
  	relation->rd_rel = relationForm;
  
! 	/* allocate tuple descriptor */
! 	relation->rd_att = CreateTemplateTupleDescPersistent(relationForm->relnatts,
! 														 relationForm->relhasoids);
  
  	MemoryContextSwitchTo(oldcxt);
  
***************
*** 348,354 ****
  	relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
  	relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;
  
! 	constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
  												sizeof(TupleConstr));
  	constr->has_not_null = false;
  
--- 348,354 ----
  	relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
  	relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;
  
! 	constr = (TupleConstr *) MemoryContextAlloc(TopMemoryContext,
  												sizeof(TupleConstr));
  	constr->has_not_null = false;
  
***************
*** 406,412 ****
  		{
  			if (attrdef == NULL)
  				attrdef = (AttrDefault *)
! 					MemoryContextAllocZero(CacheMemoryContext,
  										   relation->rd_rel->relnatts *
  										   sizeof(AttrDefault));
  			attrdef[ndef].adnum = attp->attnum;
--- 406,412 ----
  		{
  			if (attrdef == NULL)
  				attrdef = (AttrDefault *)
! 					MemoryContextAllocZero(TopMemoryContext,
  										   relation->rd_rel->relnatts *
  										   sizeof(AttrDefault));
  			attrdef[ndef].adnum = attp->attnum;
***************
*** 474,480 ****
  		{
  			constr->num_check = relation->rd_rel->relchecks;
  			constr->check = (ConstrCheck *)
! 				MemoryContextAllocZero(CacheMemoryContext,
  									constr->num_check * sizeof(ConstrCheck));
  			CheckConstraintFetch(relation);
  		}
--- 474,480 ----
  		{
  			constr->num_check = relation->rd_rel->relchecks;
  			constr->check = (ConstrCheck *)
! 				MemoryContextAllocZero(TopMemoryContext,
  									constr->num_check * sizeof(ConstrCheck));
  			CheckConstraintFetch(relation);
  		}
***************
*** 1238,1244 ****
  	 * because it will never be replaced.  The input values must be correctly
  	 * defined by macros in src/include/catalog/ headers.
  	 */
! 	relation->rd_att = CreateTemplateTupleDesc(natts, hasoids);
  	relation->rd_att->tdtypeid = relationReltype;
  	relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
  
--- 1238,1244 ----
  	 * because it will never be replaced.  The input values must be correctly
  	 * defined by macros in src/include/catalog/ headers.
  	 */
! 	relation->rd_att = CreateTemplateTupleDescPersistent(natts, hasoids);
  	relation->rd_att->tdtypeid = relationReltype;
  	relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
  
***************
*** 1262,1268 ****
  	/* mark not-null status */
  	if (has_not_null)
  	{
! 		TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
  
  		constr->has_not_null = true;
  		relation->rd_att->constr = constr;
--- 1262,1273 ----
  	/* mark not-null status */
  	if (has_not_null)
  	{
! 		/*
! 		 * The TupleDesc is allocated in TopMemoryContext, so be
! 		 * sure to allocate the TupleConstr there as well.
! 		 */
! 		TupleConstr *constr = (TupleConstr *)
! 			MemoryContextAlloc(TopMemoryContext, sizeof(TupleConstr));
  
  		constr->has_not_null = true;
  		relation->rd_att->constr = constr;
***************
*** 1571,1577 ****
  	{
  		/* ok to zap remaining substructure */
  		flush_rowtype_cache(old_reltype);
! 		FreeTupleDesc(relation->rd_att);
  		if (relation->rd_rulescxt)
  			MemoryContextDelete(relation->rd_rulescxt);
  		pfree(relation);
--- 1576,1582 ----
  	{
  		/* ok to zap remaining substructure */
  		flush_rowtype_cache(old_reltype);
! 		DecrTupleDescRefCount(relation->rd_att, true);
  		if (relation->rd_rulescxt)
  			MemoryContextDelete(relation->rd_rulescxt);
  		pfree(relation);
***************
*** 1598,1604 ****
  		{
  			/* Should only get here if relation was deleted */
  			flush_rowtype_cache(old_reltype);
! 			FreeTupleDesc(old_att);
  			if (old_rulescxt)
  				MemoryContextDelete(old_rulescxt);
  			pfree(relation);
--- 1603,1609 ----
  		{
  			/* Should only get here if relation was deleted */
  			flush_rowtype_cache(old_reltype);
! 			DecrTupleDescRefCount(old_att, true);
  			if (old_rulescxt)
  				MemoryContextDelete(old_rulescxt);
  			pfree(relation);
***************
*** 1609,1621 ****
  		if (equalTupleDescs(old_att, relation->rd_att))
  		{
  			/* needn't flush typcache here */
! 			FreeTupleDesc(relation->rd_att);
  			relation->rd_att = old_att;
  		}
  		else
  		{
  			flush_rowtype_cache(old_reltype);
! 			FreeTupleDesc(old_att);
  		}
  		if (equalRuleLocks(old_rules, relation->rd_rules))
  		{
--- 1614,1626 ----
  		if (equalTupleDescs(old_att, relation->rd_att))
  		{
  			/* needn't flush typcache here */
! 			DecrTupleDescRefCount(relation->rd_att, true);
  			relation->rd_att = old_att;
  		}
  		else
  		{
  			flush_rowtype_cache(old_reltype);
! 			DecrTupleDescRefCount(old_att, true);
  		}
  		if (equalRuleLocks(old_rules, relation->rd_rules))
  		{
***************
*** 2044,2056 ****
  	rel->rd_istemp = isTempNamespace(relnamespace);
  
  	/*
! 	 * create a new tuple descriptor from the one passed in.  We do this
! 	 * partly to copy it into the cache context, and partly because the new
! 	 * relation can't have any defaults or constraints yet; they have to be
! 	 * added in later steps, because they require additions to multiple system
! 	 * catalogs.  We can copy attnotnull constraints here, however.
  	 */
! 	rel->rd_att = CreateTupleDescCopy(tupDesc);
  	has_not_null = false;
  	for (i = 0; i < natts; i++)
  	{
--- 2049,2062 ----
  	rel->rd_istemp = isTempNamespace(relnamespace);
  
  	/*
! 	 * create a new tuple descriptor from the one passed in.  We do
! 	 * because the new relation can't have any defaults or constraints
! 	 * yet; they have to be added in later steps, because they require
! 	 * additions to multiple system catalogs.  We can copy attnotnull
! 	 * constraints here, however. Note that we make sure we hold a
! 	 * persistent reference to the TupleDesc.
  	 */
! 	rel->rd_att = CreateTupleDescCopyPersistent(tupDesc);
  	has_not_null = false;
  	for (i = 0; i < natts; i++)
  	{
***************
*** 2060,2066 ****
  
  	if (has_not_null)
  	{
! 		TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
  
  		constr->has_not_null = true;
  		rel->rd_att->constr = constr;
--- 2066,2077 ----
  
  	if (has_not_null)
  	{
! 		/*
! 		 * The TupleDesc is allocated in TopMemoryContext, so be
! 		 * sure to allocate the TupleConstr there as well.
! 		 */
! 		TupleConstr *constr = (TupleConstr *)
! 			MemoryContextAlloc(TopMemoryContext, sizeof(TupleConstr));
  
  		constr->has_not_null = true;
  		rel->rd_att->constr = constr;
***************
*** 2097,2103 ****
  	rel->rd_rel->reltablespace = reltablespace;
  
  	RelationInitLockInfo(rel);	/* see lmgr.c */
- 
  	RelationInitPhysicalAddr(rel);
  
  	/*
--- 2108,2113 ----
***************
*** 2163,2170 ****
  	 * now.  Otherwise, initialize the cache with pre-made descriptors for the
  	 * critical "nailed-in" system catalogs.
  	 */
! 	if (IsBootstrapProcessingMode() ||
! 		!load_relcache_init_file())
  	{
  		formrdesc("pg_class", PG_CLASS_RELTYPE_OID,
  				  true, Natts_pg_class, Desc_pg_class);
--- 2173,2179 ----
  	 * now.  Otherwise, initialize the cache with pre-made descriptors for the
  	 * critical "nailed-in" system catalogs.
  	 */
! 	if (IsBootstrapProcessingMode() || !load_relcache_init_file())
  	{
  		formrdesc("pg_class", PG_CLASS_RELTYPE_OID,
  				  true, Natts_pg_class, Desc_pg_class);
***************
*** 2177,2182 ****
--- 2186,2192 ----
  
  #define NUM_CRITICAL_RELS	4	/* fix if you change list above */
  	}
+ 	CurrentResourceOwner = NULL;
  
  	MemoryContextSwitchTo(oldcxt);
  }
***************
*** 2348,2363 ****
  GetPgIndexDescriptor(void)
  {
  	static TupleDesc pgindexdesc = NULL;
- 	MemoryContext oldcxt;
  	int			i;
  
  	/* Already done? */
  	if (pgindexdesc)
! 		return pgindexdesc;
  
! 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
! 
! 	pgindexdesc = CreateTemplateTupleDesc(Natts_pg_index, false);
  	pgindexdesc->tdtypeid = RECORDOID;	/* not right, but we don't care */
  	pgindexdesc->tdtypmod = -1;
  
--- 2358,2370 ----
  GetPgIndexDescriptor(void)
  {
  	static TupleDesc pgindexdesc = NULL;
  	int			i;
  
  	/* Already done? */
  	if (pgindexdesc)
! 		return pgindexdesc; /* XXX: increment ref count? */
  
! 	pgindexdesc = CreateTemplateTupleDescPersistent(Natts_pg_index, false);
  	pgindexdesc->tdtypeid = RECORDOID;	/* not right, but we don't care */
  	pgindexdesc->tdtypmod = -1;
  
***************
*** 2375,2382 ****
  
  	/* Note: we don't bother to set up a TupleConstr entry */
  
- 	MemoryContextSwitchTo(oldcxt);
- 
  	return pgindexdesc;
  }
  
--- 2382,2387 ----
***************
*** 2427,2433 ****
  				NameStr(relation->rd_att->attrs[adform->adnum - 1]->attname),
  					 RelationGetRelationName(relation));
  			else
! 				attrdef[i].adbin = MemoryContextStrdup(CacheMemoryContext,
  								 DatumGetCString(DirectFunctionCall1(textout,
  																	 val)));
  			break;
--- 2432,2438 ----
  				NameStr(relation->rd_att->attrs[adform->adnum - 1]->attname),
  					 RelationGetRelationName(relation));
  			else
! 				attrdef[i].adbin = MemoryContextStrdup(TopMemoryContext,
  								 DatumGetCString(DirectFunctionCall1(textout,
  																	 val)));
  			break;
***************
*** 2480,2486 ****
  			elog(ERROR, "unexpected constraint record found for rel %s",
  				 RelationGetRelationName(relation));
  
! 		check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
  												  NameStr(conform->conname));
  
  		/* Grab and test conbin is actually set */
--- 2485,2491 ----
  			elog(ERROR, "unexpected constraint record found for rel %s",
  				 RelationGetRelationName(relation));
  
! 		check[found].ccname = MemoryContextStrdup(TopMemoryContext,
  												  NameStr(conform->conname));
  
  		/* Grab and test conbin is actually set */
***************
*** 2491,2497 ****
  			elog(ERROR, "null conbin for rel %s",
  				 RelationGetRelationName(relation));
  
! 		check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
  								 DatumGetCString(DirectFunctionCall1(textout,
  																	 val)));
  		found++;
--- 2496,2502 ----
  			elog(ERROR, "null conbin for rel %s",
  				 RelationGetRelationName(relation));
  
! 		check[found].ccbin = MemoryContextStrdup(TopMemoryContext,
  								 DatumGetCString(DirectFunctionCall1(textout,
  																	 val)));
  		found++;
***************
*** 2966,2973 ****
  		rel->rd_rel = relform;
  
  		/* initialize attribute tuple forms */
! 		rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
! 											  relform->relhasoids);
  		rel->rd_att->tdtypeid = relform->reltype;
  		rel->rd_att->tdtypmod = -1;		/* unnecessary, but... */
  
--- 2971,2978 ----
  		rel->rd_rel = relform;
  
  		/* initialize attribute tuple forms */
! 		rel->rd_att = CreateTemplateTupleDescPersistent(relform->relnatts,
! 														relform->relhasoids);
  		rel->rd_att->tdtypeid = relform->reltype;
  		rel->rd_att->tdtypmod = -1;		/* unnecessary, but... */
  
***************
*** 2988,2994 ****
  		/* mark not-null status */
  		if (has_not_null)
  		{
! 			TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
  
  			constr->has_not_null = true;
  			rel->rd_att->constr = constr;
--- 2993,3004 ----
  		/* mark not-null status */
  		if (has_not_null)
  		{
! 			/*
! 			 * The TupleDesc is allocated in TopMemoryContext, so be
! 			 * sure to allocate the TupleConstr there as well.
! 			 */
! 			TupleConstr *constr = (TupleConstr *)
! 				MemoryContextAllocZero(TopMemoryContext, sizeof(TupleConstr));
  
  			constr->has_not_null = true;
  			rel->rd_att->constr = constr;
============================================================
*** src/backend/utils/cache/typcache.c	85eb6ae0e0048cd300ef5142aa3926b2f5907b2f
--- src/backend/utils/cache/typcache.c	a26c74ff19052d8e7dfbcb0d9535b56dfc90b605
***************
*** 394,399 ****
--- 394,401 ----
  TupleDesc
  lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
  {
+ 	TupleDesc result;
+ 
  	if (type_id != RECORDOID)
  	{
  		/*
***************
*** 407,413 ****
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("type %s is not composite",
  							format_type_be(type_id))));
! 		return typentry->tupDesc;
  	}
  	else
  	{
--- 409,415 ----
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("type %s is not composite",
  							format_type_be(type_id))));
! 		result = typentry->tupDesc;
  	}
  	else
  	{
***************
*** 422,429 ****
  						 errmsg("record type has not been registered")));
  			return NULL;
  		}
! 		return RecordCacheArray[typmod];
  	}
  }
  
  
--- 424,440 ----
  						 errmsg("record type has not been registered")));
  			return NULL;
  		}
! 		result = RecordCacheArray[typmod];
  	}
+ 
+ 	/*
+ 	 * increment the refcount and return the TupleDesc, so that the
+ 	 * returned TupleDesc survives as long as is needed by the caller
+ 	 * (until end-of-transaction).
+ 	 */
+ 	if (result)
+ 		IncrTupleDescRefCount(result, false);
+ 	return result;
  }
  
  
***************
*** 509,514 ****
--- 520,527 ----
  		RecordCacheArrayLen = newlen;
  	}
  
+ 	/* XXX: we don't want to copy the TupleDesc here, right? */
+ 
  	/* if fail in subrs, no damage except possibly some wasted memory... */
  	entDesc = CreateTupleDescCopy(tupDesc);
  	recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
============================================================
*** src/backend/utils/fmgr/funcapi.c	1e22765271d66af893b77287b1907c075fe5a866
--- src/backend/utils/fmgr/funcapi.c	3172c3172c339a5b78847182d8a372bf820d8d9f
***************
*** 321,327 ****
--- 321,330 ----
  				tupdesc->tdtypmod < 0)
  				assign_record_type_typmod(tupdesc);
  			if (resultTupleDesc)
+ 			{
  				*resultTupleDesc = tupdesc;
+ 				IncrTupleDescRefCount(*resultTupleDesc, false);
+ 			}
  			result = TYPEFUNC_COMPOSITE;
  		}
  		else
***************
*** 332,338 ****
  		}
  
  		ReleaseSysCache(tp);
! 
  		return result;
  	}
  
--- 335,341 ----
  		}
  
  		ReleaseSysCache(tp);
! 		DecrTupleDescRefCount(tupdesc, false);
  		return result;
  	}
  
***************
*** 375,381 ****
--- 378,387 ----
  			{
  				result = TYPEFUNC_COMPOSITE;
  				if (resultTupleDesc)
+ 				{
  					*resultTupleDesc = rsinfo->expectedDesc;
+ 					IncrTupleDescRefCount(*resultTupleDesc, false);
+ 				}
  				/* Assume no polymorphic columns here, either */
  			}
  			break;
***************
*** 1053,1058 ****
--- 1059,1065 ----
  	if (functypclass == TYPEFUNC_COMPOSITE)
  	{
  		/* Composite data type, e.g. a table's row type */
+ 		/* XXX: copy not needed, right? */
  		tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
  
  		if (colaliases != NIL)
============================================================
*** src/backend/utils/resowner/resowner.c	8e55e3ae813bd6f162ef5de540740cedbe7d56a3
--- src/backend/utils/resowner/resowner.c	e19f0d21cade09dd9f23d0a43f94db2538121bee
***************
*** 39,50 ****
  	ResourceOwner nextchild;	/* next child of same parent */
  	const char *name;			/* name (just for debugging) */
  
! 	/* We have built-in support for remembering owned buffers */
  	int			nbuffers;		/* number of owned buffer pins */
  	Buffer	   *buffers;		/* dynamically allocated array */
  	int			maxbuffers;		/* currently allocated array size */
  
- 	/* We have built-in support for remembering catcache references */
  	int			ncatrefs;		/* number of owned catcache pins */
  	HeapTuple  *catrefs;		/* dynamically allocated array */
  	int			maxcatrefs;		/* currently allocated array size */
--- 39,54 ----
  	ResourceOwner nextchild;	/* next child of same parent */
  	const char *name;			/* name (just for debugging) */
  
! 	/*
! 	 * We have built-in supportfor remembering buffer pins, catcache
! 	 * references, catcache-list pins, relcache references, and
! 	 * tupledescs.
! 	 */
! 
  	int			nbuffers;		/* number of owned buffer pins */
  	Buffer	   *buffers;		/* dynamically allocated array */
  	int			maxbuffers;		/* currently allocated array size */
  
  	int			ncatrefs;		/* number of owned catcache pins */
  	HeapTuple  *catrefs;		/* dynamically allocated array */
  	int			maxcatrefs;		/* currently allocated array size */
***************
*** 53,62 ****
  	CatCList  **catlistrefs;	/* dynamically allocated array */
  	int			maxcatlistrefs; /* currently allocated array size */
  
- 	/* We have built-in support for remembering relcache references */
  	int			nrelrefs;		/* number of owned relcache pins */
  	Relation   *relrefs;		/* dynamically allocated array */
  	int			maxrelrefs;		/* currently allocated array size */
  } ResourceOwnerData;
  
  
--- 57,69 ----
  	CatCList  **catlistrefs;	/* dynamically allocated array */
  	int			maxcatlistrefs; /* currently allocated array size */
  
  	int			nrelrefs;		/* number of owned relcache pins */
  	Relation   *relrefs;		/* dynamically allocated array */
  	int			maxrelrefs;		/* currently allocated array size */
+ 
+ 	int			ntupdescs;		/* number of owned tupledescs */
+ 	TupleDesc  *tupdescs;		/* dynamically allocated array */
+ 	int			maxtupdescs;	/* currently allocated array size */
  } ResourceOwnerData;
  
  
***************
*** 87,92 ****
--- 94,100 ----
  							 bool isCommit,
  							 bool isTopLevel);
  static void PrintRelCacheLeakWarning(Relation rel);
+ static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
  
  
  /*****************************************************************************
***************
*** 279,284 ****
--- 287,300 ----
  		/* Clean up index scans too */
  		ReleaseResources_gist();
  		ReleaseResources_hash();
+ 
+ 		/* Clean up tupledescs */
+ 		while (owner->ntupdescs > 0)
+ 		{
+ 			if (isCommit)
+ 				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
+ 			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1], false);
+ 		}
  	}
  
  	/* Let add-on modules get a chance too */
***************
*** 305,310 ****
--- 321,327 ----
  	Assert(owner->ncatrefs == 0);
  	Assert(owner->ncatlistrefs == 0);
  	Assert(owner->nrelrefs == 0);
+ 	Assert(owner->ntupdescs == 0);
  
  	/*
  	 * Delete children.  The recursive call will delink the child from me, so
***************
*** 329,334 ****
--- 346,353 ----
  		pfree(owner->catlistrefs);
  	if (owner->relrefs)
  		pfree(owner->relrefs);
+ 	if (owner->tupdescs)
+ 		pfree(owner->tupdescs);
  
  	pfree(owner);
  }
***************
*** 735,745 ****
  }
  
  /*
!  * Debugging subroutine
   */
  static void
  PrintRelCacheLeakWarning(Relation rel)
  {
  	elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
  		 RelationGetRelationName(rel));
  }
--- 754,844 ----
  }
  
  /*
!  * Make sure there is room for at least one more entry in a ResourceOwner's
!  * TupleDesc array.
!  *
!  * This is separate from actually inserting an entry because if we run out
!  * of memory, it's critical to do so *before* acquiring the resource.
   */
+ void
+ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
+ {
+ 	int			newmax;
+ 
+ 	if (owner->ntupdescs < owner->maxtupdescs)
+ 		return;					/* nothing to do */
+ 
+ 	if (owner->tupdescs == NULL)
+ 	{
+ 		newmax = 16;
+ 		owner->tupdescs = (TupleDesc *)
+ 			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
+ 		owner->maxtupdescs = newmax;
+ 	}
+ 	else
+ 	{
+ 		newmax = owner->maxtupdescs * 2;
+ 		owner->tupdescs = (TupleDesc *)
+ 			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
+ 		owner->maxtupdescs = newmax;
+ 	}
+ }
+ 
+ /*
+  * Remember that a TupleDesc is owned by a ResourceOwner
+  *
+  * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
+  */
+ void
+ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
+ {
+ 	Assert(owner->ntupdescs < owner->maxtupdescs);
+ 	owner->tupdescs[owner->ntupdescs] = tupdesc;
+ 	owner->ntupdescs++;
+ }
+ 
+ /*
+  * Forget that a TupleDesc is owned by a ResourceOwner
+  */
+ void
+ ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
+ {
+ 	TupleDesc  *tupdescs = owner->tupdescs;
+ 	int			nt1 = owner->ntupdescs - 1;
+ 	int			i;
+ 
+ 	for (i = nt1; i >= 0; i--)
+ 	{
+ 		if (tupdescs[i] == tupdesc)
+ 		{
+ 			while (i < nt1)
+ 			{
+ 				tupdescs[i] = tupdescs[i + 1];
+ 				i++;
+ 			}
+ 			owner->ntupdescs = nt1;
+ 			return;
+ 		}
+ 	}
+ 	elog(ERROR, "TupleDesc %p is not owned by resource owner %s",
+ 		 tupdesc, owner->name);
+ }
+ 
+ 
+ /*
+  * Debugging subroutines
+  */
  static void
  PrintRelCacheLeakWarning(Relation rel)
  {
  	elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
  		 RelationGetRelationName(rel));
  }
+ 
+ static void
+ PrintTupleDescLeakWarning(TupleDesc tupdesc)
+ {
+ 	elog(WARNING, "TupleDesc reference leak: TupleDesc %p is still referenced",
+ 		 tupdesc);
+ }
+ 		 
============================================================
*** src/include/access/tupdesc.h	838f14152479f66e6f98d9055886ab655da40f70
--- src/include/access/tupdesc.h	fa7d91d453252532dcde494e502cbf474f2d1197
***************
*** 67,85 ****
  	Oid			tdtypeid;		/* composite type ID for tuple type */
  	int32		tdtypmod;		/* typmod for tuple type */
  	bool		tdhasoid;		/* tuple has oid attribute in its header */
  }	*TupleDesc;
  
  
  extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
- 
  extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
! 				Form_pg_attribute *attrs);
! 
  extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
  
! extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
  
! extern void FreeTupleDesc(TupleDesc tupdesc);
  
  extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
  
--- 67,87 ----
  	Oid			tdtypeid;		/* composite type ID for tuple type */
  	int32		tdtypmod;		/* typmod for tuple type */
  	bool		tdhasoid;		/* tuple has oid attribute in its header */
+ 	int			refcount;		/* number of active references */
  }	*TupleDesc;
  
  
  extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
  extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
! 								 Form_pg_attribute *attrs);
  extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
+ extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
  
! extern TupleDesc CreateTemplateTupleDescPersistent(int natts, bool hasoid);
! extern TupleDesc CreateTupleDescCopyPersistent(TupleDesc tupdesc);
  
! extern void IncrTupleDescRefCount(TupleDesc tupdesc, bool persistent);
! extern void DecrTupleDescRefCount(TupleDesc tupdesc, bool persistent);
  
  extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
  
============================================================
*** src/include/utils/resowner.h	ba8a6dae7162d1f5026daf7a9552db29f9615e91
--- src/include/utils/resowner.h	7322c8b8cd6206c6090099eb837516a866049d50
***************
*** 19,24 ****
--- 19,25 ----
  #ifndef RESOWNER_H
  #define RESOWNER_H
  
+ #include "access/tupdesc.h"
  #include "storage/buf.h"
  #include "utils/catcache.h"
  #include "utils/rel.h"
***************
*** 107,110 ****
--- 108,118 ----
  extern void ResourceOwnerForgetRelationRef(ResourceOwner owner,
  							   Relation rel);
  
+ /* support for tupledesc refcount management */
+ extern void ResourceOwnerEnlargeTupleDescs(ResourceOwner owner);
+ extern void ResourceOwnerRememberTupleDesc(ResourceOwner owner,
+ 										   TupleDesc tupdesc);
+ extern void ResourceOwnerForgetTupleDesc(ResourceOwner owner,
+ 										 TupleDesc tupdesc);
+ 
  #endif   /* RESOWNER_H */
============================================================
*** src/pl/plpgsql/src/pl_exec.c	0f3dff3cfb05fda66b508752d7583ca187aa567a
--- src/pl/plpgsql/src/pl_exec.c	757aaa625fd2801edd25ef4b0ab174e1c1007b74
***************
*** 259,264 ****
--- 259,265 ----
  						tmptup.t_tableOid = InvalidOid;
  						tmptup.t_data = td;
  						exec_move_row(&estate, NULL, row, &tmptup, tupdesc);
+ 						DecrTupleDescRefCount(tupdesc, false);
  					}
  					else
  					{
***************
*** 329,339 ****
  			rsi->setResult = estate.tuple_store;
  			if (estate.rettupdesc)
  			{
! 				MemoryContext oldcxt;
! 
! 				oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt);
! 				rsi->setDesc = CreateTupleDescCopy(estate.rettupdesc);
! 				MemoryContextSwitchTo(oldcxt);
  			}
  		}
  		estate.retval = (Datum) 0;
--- 330,337 ----
  			rsi->setResult = estate.tuple_store;
  			if (estate.rettupdesc)
  			{
! 				rsi->setDesc = estate.rettupdesc;
! 				IncrTupleDescRefCount(rsi->setDesc, false);
  			}
  		}
  		estate.retval = (Datum) 0;
***************
*** 368,376 ****
--- 366,376 ----
  					 * rowtype, so we don't really need to be restrictive.
  					 * Pass back the generated result type, instead.
  					 */
+ 					Assert(tupdesc == NULL);
  					tupdesc = estate.rettupdesc;
  					if (tupdesc == NULL)		/* shouldn't happen */
  						elog(ERROR, "return type must be a row type");
+ 					IncrTupleDescRefCount(tupdesc, false);
  					break;
  				default:
  					/* shouldn't get here if retistuple is true ... */
***************
*** 385,390 ****
--- 385,393 ----
  			estate.retval =
  				PointerGetDatum(SPI_returntuple((HeapTuple) (estate.retval),
  												tupdesc));
+ 
+ 			/* We're done with the TupleDesc */
+ 			DecrTupleDescRefCount(tupdesc, false);
  		}
  		else
  		{
***************
*** 473,482 ****
  	 */
  	rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]);
  	rec_new->freetup = false;
- 	rec_new->freetupdesc = false;
  	rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]);
  	rec_old->freetup = false;
- 	rec_old->freetupdesc = false;
  
  	if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
  	{
--- 476,483 ----
***************
*** 512,521 ****
  	else
  		elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
  
  	/*
  	 * Assign the special tg_ variables
  	 */
- 
  	var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]);
  	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
  		var->value = DirectFunctionCall1(textin, CStringGetDatum("INSERT"));
--- 513,526 ----
  	else
  		elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
  
+ 	if (rec_new->tupdesc)
+ 		IncrTupleDescRefCount(rec_new->tupdesc, false);
+ 	if (rec_old->tupdesc)
+ 		IncrTupleDescRefCount(rec_old->tupdesc, false);
+ 
  	/*
  	 * Assign the special tg_ variables
  	 */
  	var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]);
  	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
  		var->value = DirectFunctionCall1(textin, CStringGetDatum("INSERT"));
***************
*** 742,748 ****
  				new->tup = NULL;
  				new->tupdesc = NULL;
  				new->freetup = false;
- 				new->freetupdesc = false;
  
  				result = (PLpgSQL_datum *) new;
  			}
--- 747,752 ----
***************
*** 851,857 ****
  					if (rec->freetup)
  					{
  						heap_freetuple(rec->tup);
! 						FreeTupleDesc(rec->tupdesc);
  						rec->freetup = false;
  					}
  
--- 855,861 ----
  					if (rec->freetup)
  					{
  						heap_freetuple(rec->tup);
! 						DecrTupleDescRefCount(rec->tupdesc, false);
  						rec->freetup = false;
  					}
  
***************
*** 1798,1803 ****
--- 1802,1808 ----
  					{
  						estate->retval = (Datum) rec->tup;
  						estate->rettupdesc = rec->tupdesc;
+ 						IncrTupleDescRefCount(estate->rettupdesc, false);
  						estate->retisnull = false;
  					}
  				}
***************
*** 1813,1818 ****
--- 1818,1824 ----
  					if (estate->retval == (Datum) NULL) /* should not happen */
  						elog(ERROR, "row not compatible with its own tupdesc");
  					estate->rettupdesc = row->rowtupdesc;
+ 					IncrTupleDescRefCount(estate->rettupdesc, false);
  					estate->retisnull = false;
  				}
  				break;
***************
*** 1833,1838 ****
--- 1839,1845 ----
  			{
  				estate->retval = (Datum) estate->eval_tuptable->vals[0];
  				estate->rettupdesc = estate->eval_tuptable->tupdesc;
+ 				IncrTupleDescRefCount(estate->rettupdesc, false);
  				estate->retisnull = false;
  			}
  		}
***************
*** 2032,2037 ****
--- 2039,2045 ----
  	MemoryContextSwitchTo(oldcxt);
  
  	estate->rettupdesc = rsi->expectedDesc;
+ 	IncrTupleDescRefCount(estate->rettupdesc, false);
  }
  
  /* ----------
***************
*** 3114,3119 ****
--- 3122,3128 ----
  					tmptup.t_tableOid = InvalidOid;
  					tmptup.t_data = td;
  					exec_move_row(estate, NULL, row, &tmptup, tupdesc);
+ 					DecrTupleDescRefCount(tupdesc, false);
  				}
  				break;
  			}
***************
*** 3156,3161 ****
--- 3165,3171 ----
  					tmptup.t_tableOid = InvalidOid;
  					tmptup.t_data = td;
  					exec_move_row(estate, rec, NULL, &tmptup, tupdesc);
+ 					DecrTupleDescRefCount(tupdesc, false);
  				}
  				break;
  			}
***************
*** 3905,3912 ****
  		 */
  		if (HeapTupleIsValid(tup))
  			tup = heap_copytuple(tup);
- 		if (tupdesc)
- 			tupdesc = CreateTupleDescCopy(tupdesc);
  
  		if (rec->freetup)
  		{
--- 3915,3920 ----
***************
*** 3913,3923 ****
  			heap_freetuple(rec->tup);
  			rec->freetup = false;
  		}
- 		if (rec->freetupdesc)
- 		{
- 			FreeTupleDesc(rec->tupdesc);
- 			rec->freetupdesc = false;
- 		}
  
  		if (HeapTupleIsValid(tup))
  		{
--- 3921,3926 ----
***************
*** 3940,3952 ****
  		else
  			rec->tup = NULL;
  
! 		if (tupdesc)
! 		{
! 			rec->tupdesc = tupdesc;
! 			rec->freetupdesc = true;
! 		}
! 		else
! 			rec->tupdesc = NULL;
  
  		return;
  	}
--- 3943,3957 ----
  		else
  			rec->tup = NULL;
  
! 		/*
! 		 * Replace our old TupleDesc, if any, with the TupleDesc
! 		 * passed by the caller, if any
! 		 */
! 		if (rec->tupdesc)
! 			DecrTupleDescRefCount(rec->tupdesc, false);
! 		rec->tupdesc = tupdesc;
! 		if (rec->tupdesc)
! 			IncrTupleDescRefCount(rec->tupdesc, false);
  
  		return;
  	}
============================================================
*** src/pl/plpgsql/src/plpgsql.h	40743f1288e49eb6a6aedf3ddb0846b598d7319b
--- src/pl/plpgsql/src/plpgsql.h	1cca54371d032d003e86ed6c4c0403eaebc8a5c8
***************
*** 261,267 ****
  	HeapTuple	tup;
  	TupleDesc	tupdesc;
  	bool		freetup;
- 	bool		freetupdesc;
  } PLpgSQL_rec;
  
  
--- 261,266 ----
