]> granicus.if.org Git - postgresql/commitdiff
Fix problems with cached tuple descriptors disappearing while still in use
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 16 Jun 2006 18:42:24 +0000 (18:42 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 16 Jun 2006 18:42:24 +0000 (18:42 +0000)
by creating a reference-count mechanism, similar to what we did a long time
ago for catcache entries.  The back branches have an ugly solution involving
lots of extra copies, but this way is more efficient.  Reference counting is
only applied to tupdescs that are actually in caches --- there seems no need
to use it for tupdescs that are generated in the executor, since they'll go
away during plan shutdown by virtue of being in the per-query memory context.
Neil Conway and Tom Lane

36 files changed:
src/backend/access/common/tupdesc.c
src/backend/access/heap/tuptoaster.c
src/backend/commands/tablecmds.c
src/backend/executor/execJunk.c
src/backend/executor/execMain.c
src/backend/executor/execQual.c
src/backend/executor/execTuples.c
src/backend/executor/execUtils.c
src/backend/executor/nodeBitmapHeapscan.c
src/backend/executor/nodeFunctionscan.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeSeqscan.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeSubqueryscan.c
src/backend/executor/nodeTidscan.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_target.c
src/backend/utils/adt/rowtypes.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/cache/relcache.c
src/backend/utils/cache/typcache.c
src/backend/utils/fmgr/funcapi.c
src/backend/utils/resowner/README
src/backend/utils/resowner/resowner.c
src/include/access/tupdesc.h
src/include/executor/executor.h
src/include/executor/tuptable.h
src/include/utils/resowner.h
src/include/utils/typcache.h
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpython/plpython.c
src/pl/tcl/pltcl.c

index 020011966f010a69f5002928c4661b87ddc0f6e0..a70259fc3d739a03fce6da50603ee72c60ed78e3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.116 2006/03/16 00:31:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.117 2006/06/16 18:42:21 tgl Exp $
  *
  * NOTES
  *       some of the executor utility code such as "ExecTypeFromTL" should be
@@ -23,6 +23,7 @@
 #include "catalog/pg_type.h"
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
+#include "utils/resowner.h"
 #include "utils/syscache.h"
 
 
@@ -84,6 +85,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
        desc->tdtypeid = RECORDOID;
        desc->tdtypmod = -1;
        desc->tdhasoid = hasoid;
+       desc->tdrefcount = -1;          /* assume not reference-counted */
 
        return desc;
 }
@@ -116,6 +118,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
        desc->tdtypeid = RECORDOID;
        desc->tdtypmod = -1;
        desc->tdhasoid = hasoid;
+       desc->tdrefcount = -1;          /* assume not reference-counted */
 
        return desc;
 }
@@ -214,6 +217,12 @@ FreeTupleDesc(TupleDesc tupdesc)
 {
        int                     i;
 
+       /*
+        * Possibly this should assert tdrefcount == 0, to disallow explicit
+        * freeing of un-refcounted tupdescs?
+        */
+       Assert(tupdesc->tdrefcount <= 0);
+
        if (tupdesc->constr)
        {
                if (tupdesc->constr->num_defval > 0)
@@ -246,12 +255,48 @@ FreeTupleDesc(TupleDesc tupdesc)
        pfree(tupdesc);
 }
 
+/*
+ * Increment the reference count of a tupdesc, and log the reference in
+ * CurrentResourceOwner.
+ *
+ * Do not apply this to tupdescs that are not being refcounted.  (Use the
+ * macro PinTupleDesc for tupdescs of uncertain status.)
+ */
+void
+IncrTupleDescRefCount(TupleDesc tupdesc)
+{
+       Assert(tupdesc->tdrefcount >= 0);
+
+       ResourceOwnerEnlargeTupleDescs(CurrentResourceOwner);
+       tupdesc->tdrefcount++;
+       ResourceOwnerRememberTupleDesc(CurrentResourceOwner, tupdesc);
+}
+
+/*
+ * Decrement the reference count of a tupdesc, remove the corresponding
+ * reference from CurrentResourceOwner, and free the tupdesc if no more
+ * references remain.
+ *
+ * Do not apply this to tupdescs that are not being refcounted.  (Use the
+ * macro ReleaseTupleDesc for tupdescs of uncertain status.)
+ */
+void
+DecrTupleDescRefCount(TupleDesc tupdesc)
+{
+       Assert(tupdesc->tdrefcount > 0);
+
+       ResourceOwnerForgetTupleDesc(CurrentResourceOwner, tupdesc);
+       if (--tupdesc->tdrefcount == 0)
+               FreeTupleDesc(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 don't compare tdrefcount, either.
  */
 bool
 equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
index bc3236c2a63704f6c47efcae47a7445186572110..c7cbe240362c35aca85e2e6992c4c0792a1cfb06 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.59 2006/03/05 15:58:21 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.60 2006/06/16 18:42:21 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -892,7 +892,10 @@ toast_flatten_tuple_attribute(Datum value,
         * If nothing to untoast, just return the original tuple.
         */
        if (!need_change)
+       {
+               ReleaseTupleDesc(tupleDesc);
                return value;
+       }
 
        /*
         * Calculate the new size of the tuple.  Header size should not change,
@@ -929,6 +932,7 @@ toast_flatten_tuple_attribute(Datum value,
        for (i = 0; i < numAttrs; i++)
                if (toast_free[i])
                        pfree(DatumGetPointer(toast_values[i]));
+       ReleaseTupleDesc(tupleDesc);
 
        return PointerGetDatum(new_data);
 }
index 9da1d86c42c8d8b2f8d86cb2f7e7dd5a9479e0ab..2d80f6c536b8dd7f82cb4564fd3f71f681c33db0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.184 2006/05/10 23:18:39 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.185 2006/06/16 18:42:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2637,6 +2637,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
 
                MemoryContextSwitchTo(oldCxt);
                heap_endscan(scan);
+
+               ExecDropSingleTupleTableSlot(oldslot);
+               ExecDropSingleTupleTableSlot(newslot);
        }
 
        FreeExecutorState(estate);
index 787a569b8f89c8c86bed018dffb7c74938b4cb93..459e5db7aaa69928797df3d1848bcc9bdfa91dd6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.52 2006/03/05 15:58:25 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.53 2006/06/16 18:42:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,7 +79,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
         * Use the given slot, or make a new slot if we weren't given one.
         */
        if (slot)
-               ExecSetSlotDescriptor(slot, cleanTupType, false);
+               ExecSetSlotDescriptor(slot, cleanTupType);
        else
                slot = MakeSingleTupleTableSlot(cleanTupType);
 
@@ -150,7 +150,7 @@ ExecInitJunkFilterConversion(List *targetList,
         * Use the given slot, or make a new slot if we weren't given one.
         */
        if (slot)
-               ExecSetSlotDescriptor(slot, cleanTupType, false);
+               ExecSetSlotDescriptor(slot, cleanTupType);
        else
                slot = MakeSingleTupleTableSlot(cleanTupType);
 
index e8e0c8bd56d19fbe71e6bee1df214ec6fce29712..b39e7a587b18159aae255bb7b7a459b6cc222229 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.270 2006/04/30 18:30:38 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.271 2006/06/16 18:42:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1445,9 +1445,7 @@ ExecInsert(TupleTableSlot *slot,
                        TupleTableSlot *newslot = estate->es_trig_tuple_slot;
 
                        if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
-                               ExecSetSlotDescriptor(newslot,
-                                                                         slot->tts_tupleDescriptor,
-                                                                         false);
+                               ExecSetSlotDescriptor(newslot, slot->tts_tupleDescriptor);
                        ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
                        slot = newslot;
                        tuple = newtuple;
@@ -1654,9 +1652,7 @@ ExecUpdate(TupleTableSlot *slot,
                        TupleTableSlot *newslot = estate->es_trig_tuple_slot;
 
                        if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
-                               ExecSetSlotDescriptor(newslot,
-                                                                         slot->tts_tupleDescriptor,
-                                                                         false);
+                               ExecSetSlotDescriptor(newslot, slot->tts_tupleDescriptor);
                        ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
                        slot = newslot;
                        tuple = newtuple;
index 41ea452df5859c19a99fc29abc3291fba730ccd7..30ba2de5b43b3355072cc436d39499897b65faa7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.190 2006/04/22 01:25:58 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.191 2006/06/16 18:42:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -71,6 +71,10 @@ static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
                          bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
                          bool *isNull, ExprDoneCond *isDone);
+static void ShutdownFuncExpr(Datum arg);
+static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
+                                  TupleDesc *cache_field, ExprContext *econtext);
+static void ShutdownTupleDescRef(Datum arg);
 static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
                                 List *argList, ExprContext *econtext);
 static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
@@ -715,6 +719,9 @@ GetAttributeByNum(HeapTupleHeader tuple,
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
@@ -773,6 +780,9 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
                                                  attrno,
                                                  tupDesc,
                                                  isNull);
+
+       ReleaseTupleDesc(tupDesc);
+
        return result;
 }
 
@@ -826,6 +836,61 @@ ShutdownFuncExpr(Datum arg)
        fcache->shutdown_reg = false;
 }
 
+/*
+ * get_cached_rowtype: utility function to lookup a rowtype tupdesc
+ *
+ * type_id, typmod: identity of the rowtype
+ * cache_field: where to cache the TupleDesc pointer in expression state node
+ *             (field must be initialized to NULL)
+ * econtext: expression context we are executing in
+ *
+ * NOTE: because the shutdown callback will be called during plan rescan,
+ * must be prepared to re-do this during any node execution; cannot call
+ * just once during expression initialization
+ */
+static TupleDesc
+get_cached_rowtype(Oid type_id, int32 typmod,
+                                  TupleDesc *cache_field, ExprContext *econtext)
+{
+       TupleDesc       tupDesc = *cache_field;
+
+       /* Do lookup if no cached value or if requested type changed */
+       if (tupDesc == NULL ||
+               type_id != tupDesc->tdtypeid ||
+               typmod != tupDesc->tdtypmod)
+       {
+               tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
+
+               if (*cache_field)
+               {
+                       /* Release old tupdesc; but callback is already registered */
+                       ReleaseTupleDesc(*cache_field);
+               }
+               else
+               {
+                       /* Need to register shutdown callback to release tupdesc */
+                       RegisterExprContextCallback(econtext,
+                                                                               ShutdownTupleDescRef,
+                                                                               PointerGetDatum(cache_field));
+               }
+               *cache_field = tupDesc;
+       }
+       return tupDesc;
+}
+
+/*
+ * Callback function to release a tupdesc refcount at expression tree shutdown
+ */
+static void
+ShutdownTupleDescRef(Datum arg)
+{
+       TupleDesc *cache_field = (TupleDesc *) DatumGetPointer(arg);
+
+       if (*cache_field)
+               ReleaseTupleDesc(*cache_field);
+       *cache_field = NULL;
+}
+
 /*
  * Evaluate arguments for a function.
  */
@@ -1351,9 +1416,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
                                        HeapTupleHeader td;
 
                                        td = DatumGetHeapTupleHeader(result);
-                                       tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td),
+                                       tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
                                                                                           HeapTupleHeaderGetTypMod(td));
-                                       tupdesc = CreateTupleDescCopy(tupdesc);
                                }
                                else
                                {
@@ -1919,17 +1983,18 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
                                           ExprContext *econtext,
                                           bool *isNull, ExprDoneCond *isDone)
 {
+       ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
        HeapTuple       result;
        Datum           tupDatum;
        HeapTupleHeader tuple;
        HeapTupleData tmptup;
-       AttrNumber *attrMap = cstate->attrMap;
-       Datum      *invalues = cstate->invalues;
-       bool       *inisnull = cstate->inisnull;
-       Datum      *outvalues = cstate->outvalues;
-       bool       *outisnull = cstate->outisnull;
+       AttrNumber *attrMap;
+       Datum      *invalues;
+       bool       *inisnull;
+       Datum      *outvalues;
+       bool       *outisnull;
        int                     i;
-       int                     outnatts = cstate->outdesc->natts;
+       int                     outnatts;
 
        tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
 
@@ -1939,9 +2004,82 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
 
        tuple = DatumGetHeapTupleHeader(tupDatum);
 
+       /* Lookup tupdescs if first time through or after rescan */
+       if (cstate->indesc == NULL)
+               get_cached_rowtype(exprType((Node *) convert->arg), -1,
+                                                  &cstate->indesc, econtext);
+       if (cstate->outdesc == NULL)
+               get_cached_rowtype(convert->resulttype, -1,
+                                                  &cstate->outdesc, econtext);
+
        Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid);
        Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
 
+       /* if first time through, initialize */
+       if (cstate->attrMap == NULL)
+       {
+               MemoryContext   old_cxt;
+               int             n;
+
+               /* allocate state in long-lived memory context */
+               old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
+               /* prepare map from old to new attribute numbers */
+               n = cstate->outdesc->natts;
+               cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
+               for (i = 0; i < n; i++)
+               {
+                       Form_pg_attribute att = cstate->outdesc->attrs[i];
+                       char       *attname;
+                       Oid                     atttypid;
+                       int32           atttypmod;
+                       int                     j;
+
+                       if (att->attisdropped)
+                               continue;               /* attrMap[i] is already 0 */
+                       attname = NameStr(att->attname);
+                       atttypid = att->atttypid;
+                       atttypmod = att->atttypmod;
+                       for (j = 0; j < cstate->indesc->natts; j++)
+                       {
+                               att = cstate->indesc->attrs[j];
+                               if (att->attisdropped)
+                                       continue;
+                               if (strcmp(attname, NameStr(att->attname)) == 0)
+                               {
+                                       /* Found it, check type */
+                                       if (atttypid != att->atttypid || atttypmod != att->atttypmod)
+                                               elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
+                                                        attname,
+                                                        format_type_be(cstate->indesc->tdtypeid),
+                                                        format_type_be(cstate->outdesc->tdtypeid));
+                                       cstate->attrMap[i] = (AttrNumber) (j + 1);
+                                       break;
+                               }
+                       }
+                       if (cstate->attrMap[i] == 0)
+                               elog(ERROR, "attribute \"%s\" of type %s does not exist",
+                                        attname,
+                                        format_type_be(cstate->indesc->tdtypeid));
+               }
+               /* preallocate workspace for Datum arrays */
+               n = cstate->indesc->natts + 1;  /* +1 for NULL */
+               cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
+               cstate->inisnull = (bool *) palloc(n * sizeof(bool));
+               n = cstate->outdesc->natts;
+               cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
+               cstate->outisnull = (bool *) palloc(n * sizeof(bool));
+
+               MemoryContextSwitchTo(old_cxt);
+       }
+
+       attrMap = cstate->attrMap;
+       invalues = cstate->invalues;
+       inisnull = cstate->inisnull;
+       outvalues = cstate->outvalues;
+       outisnull = cstate->outisnull;
+       outnatts = cstate->outdesc->natts;
+
        /*
         * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader.
         */
@@ -2797,22 +2935,8 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
        tupTypmod = HeapTupleHeaderGetTypMod(tuple);
 
        /* Lookup tupdesc if first time through or if type changes */
-       tupDesc = fstate->argdesc;
-       if (tupDesc == NULL ||
-               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);
-       }
+       tupDesc = get_cached_rowtype(tupType, tupTypmod,
+                                                                &fstate->argdesc, econtext);
 
        /*
         * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set all
@@ -2859,22 +2983,9 @@ ExecEvalFieldStore(FieldStoreState *fstate,
        if (isDone && *isDone == ExprEndResult)
                return tupDatum;
 
-       /* Lookup tupdesc if first time through or if type changes */
-       tupDesc = fstate->argdesc;
-       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);
-       }
+       /* Lookup tupdesc if first time through or after rescan */
+       tupDesc = get_cached_rowtype(fstore->resulttype, -1,
+                                                                &fstate->argdesc, econtext);
 
        /* Allocate workspace */
        values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
@@ -3247,61 +3358,9 @@ ExecInitExpr(Expr *node, PlanState *parent)
                        {
                                ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
                                ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
-                               int                     i;
-                               int                     n;
 
                                cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
                                cstate->arg = ExecInitExpr(convert->arg, parent);
-                               /* save copies of needed tuple descriptors */
-                               cstate->indesc = lookup_rowtype_tupdesc(exprType((Node *) convert->arg), -1);
-                               cstate->indesc = CreateTupleDescCopy(cstate->indesc);
-                               cstate->outdesc = lookup_rowtype_tupdesc(convert->resulttype, -1);
-                               cstate->outdesc = CreateTupleDescCopy(cstate->outdesc);
-                               /* prepare map from old to new attribute numbers */
-                               n = cstate->outdesc->natts;
-                               cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
-                               for (i = 0; i < n; i++)
-                               {
-                                       Form_pg_attribute att = cstate->outdesc->attrs[i];
-                                       char       *attname;
-                                       Oid                     atttypid;
-                                       int32           atttypmod;
-                                       int                     j;
-
-                                       if (att->attisdropped)
-                                               continue;               /* attrMap[i] is already 0 */
-                                       attname = NameStr(att->attname);
-                                       atttypid = att->atttypid;
-                                       atttypmod = att->atttypmod;
-                                       for (j = 0; j < cstate->indesc->natts; j++)
-                                       {
-                                               att = cstate->indesc->attrs[j];
-                                               if (att->attisdropped)
-                                                       continue;
-                                               if (strcmp(attname, NameStr(att->attname)) == 0)
-                                               {
-                                                       /* Found it, check type */
-                                                       if (atttypid != att->atttypid || atttypmod != att->atttypmod)
-                                                               elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
-                                                                        attname,
-                                                                        format_type_be(cstate->indesc->tdtypeid),
-                                                                 format_type_be(cstate->outdesc->tdtypeid));
-                                                       cstate->attrMap[i] = (AttrNumber) (j + 1);
-                                                       break;
-                                               }
-                                       }
-                                       if (cstate->attrMap[i] == 0)
-                                               elog(ERROR, "attribute \"%s\" of type %s does not exist",
-                                                        attname,
-                                                        format_type_be(cstate->indesc->tdtypeid));
-                               }
-                               /* preallocate workspace for Datum arrays */
-                               n = cstate->indesc->natts + 1;  /* +1 for NULL */
-                               cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
-                               cstate->inisnull = (bool *) palloc(n * sizeof(bool));
-                               n = cstate->outdesc->natts;
-                               cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
-                               cstate->outisnull = (bool *) palloc(n * sizeof(bool));
                                state = (ExprState *) cstate;
                        }
                        break;
@@ -3372,12 +3431,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                        /* generic record, use runtime type assignment */
                                        rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
                                        BlessTupleDesc(rstate->tupdesc);
+                                       /* we won't need to redo this at runtime */
                                }
                                else
                                {
                                        /* it's been cast to a named type, use that */
-                                       rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
-                                       rstate->tupdesc = CreateTupleDescCopy(rstate->tupdesc);
+                                       rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
                                }
                                /* Set up evaluation, skipping any deleted columns */
                                Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
index 81f589fd44b4a81bbbe28d9a9b4cbac4886b765b..971822525f0c54ead64ed13ad04594c5b5cd334b 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.93 2006/04/04 19:35:34 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.94 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -143,7 +143,6 @@ ExecCreateTupleTable(int tableSize)
                slot->type = T_TupleTableSlot;
                slot->tts_isempty = true;
                slot->tts_shouldFree = false;
-               slot->tts_shouldFreeDesc = false;
                slot->tts_tuple = NULL;
                slot->tts_tupleDescriptor = NULL;
                slot->tts_mcxt = CurrentMemoryContext;
@@ -189,8 +188,8 @@ ExecDropTupleTable(TupleTable table,        /* tuple table */
                        TupleTableSlot *slot = &(table->array[i]);
 
                        ExecClearTuple(slot);
-                       if (slot->tts_shouldFreeDesc)
-                               FreeTupleDesc(slot->tts_tupleDescriptor);
+                       if (slot->tts_tupleDescriptor)
+                               ReleaseTupleDesc(slot->tts_tupleDescriptor);
                        if (slot->tts_values)
                                pfree(slot->tts_values);
                        if (slot->tts_isnull)
@@ -210,7 +209,7 @@ ExecDropTupleTable(TupleTable table,        /* tuple table */
  *             This is a convenience routine for operations that need a
  *             standalone TupleTableSlot not gotten from the main executor
  *             tuple table.  It makes a single slot and initializes it as
- *             though by ExecSetSlotDescriptor(slot, tupdesc, false).
+ *             though by ExecSetSlotDescriptor(slot, tupdesc).
  * --------------------------------
  */
 TupleTableSlot *
@@ -221,7 +220,6 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
        /* This should match ExecCreateTupleTable() */
        slot->tts_isempty = true;
        slot->tts_shouldFree = false;
-       slot->tts_shouldFreeDesc = false;
        slot->tts_tuple = NULL;
        slot->tts_tupleDescriptor = NULL;
        slot->tts_mcxt = CurrentMemoryContext;
@@ -230,7 +228,7 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
        slot->tts_values = NULL;
        slot->tts_isnull = NULL;
 
-       ExecSetSlotDescriptor(slot, tupdesc, false);
+       ExecSetSlotDescriptor(slot, tupdesc);
 
        return slot;
 }
@@ -250,8 +248,8 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
        Assert(slot != NULL);
 
        ExecClearTuple(slot);
-       if (slot->tts_shouldFreeDesc)
-               FreeTupleDesc(slot->tts_tupleDescriptor);
+       if (slot->tts_tupleDescriptor)
+               ReleaseTupleDesc(slot->tts_tupleDescriptor);
        if (slot->tts_values)
                pfree(slot->tts_values);
        if (slot->tts_isnull)
@@ -309,13 +307,15 @@ ExecAllocTableSlot(TupleTable table)
  *             ExecSetSlotDescriptor
  *
  *             This function is used to set the tuple descriptor associated
- *             with the slot's tuple.
+ *             with the slot's tuple.  The passed descriptor must have lifespan
+ *             at least equal to the slot's.  If it is a reference-counted descriptor
+ *             then the reference count is incremented for as long as the slot holds
+ *             a reference.
  * --------------------------------
  */
 void
 ExecSetSlotDescriptor(TupleTableSlot *slot,            /* slot to change */
-                                         TupleDesc tupdesc,            /* new tuple descriptor */
-                                         bool shouldFree)      /* is desc owned by slot? */
+                                         TupleDesc tupdesc)            /* new tuple descriptor */
 {
        /* For safety, make sure slot is empty before changing it */
        ExecClearTuple(slot);
@@ -324,8 +324,8 @@ ExecSetSlotDescriptor(TupleTableSlot *slot,         /* slot to change */
         * Release any old descriptor.  Also release old Datum/isnull arrays if
         * present (we don't bother to check if they could be re-used).
         */
-       if (slot->tts_shouldFreeDesc)
-               FreeTupleDesc(slot->tts_tupleDescriptor);
+       if (slot->tts_tupleDescriptor)
+               ReleaseTupleDesc(slot->tts_tupleDescriptor);
 
        if (slot->tts_values)
                pfree(slot->tts_values);
@@ -333,10 +333,10 @@ ExecSetSlotDescriptor(TupleTableSlot *slot,               /* slot to change */
                pfree(slot->tts_isnull);
 
        /*
-        * Set up the new descriptor
+        * Install the new descriptor; if it's refcounted, bump its refcount.
         */
        slot->tts_tupleDescriptor = tupdesc;
-       slot->tts_shouldFreeDesc = shouldFree;
+       PinTupleDesc(tupdesc);
 
        /*
         * Allocate Datum/isnull arrays of the appropriate size.  These must have
@@ -740,7 +740,7 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 {
        TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
 
-       ExecSetSlotDescriptor(slot, tupType, false);
+       ExecSetSlotDescriptor(slot, tupType);
 
        return ExecStoreAllNullTuple(slot);
 }
index d1a294f9bb8b2dd0f8d282714315ee0ed42fc230..c30c8f2badf97d2033fe8354973747792f22d2ca 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.134 2006/04/30 18:30:38 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.135 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -427,12 +427,11 @@ ExecAssignExprContext(EState *estate, PlanState *planstate)
  * ----------------
  */
 void
-ExecAssignResultType(PlanState *planstate,
-                                        TupleDesc tupDesc, bool shouldFree)
+ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc)
 {
        TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
 
-       ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
+       ExecSetSlotDescriptor(slot, tupDesc);
 }
 
 /* ----------------
@@ -461,7 +460,7 @@ ExecAssignResultTypeFromTL(PlanState *planstate)
         * to set up planstate->targetlist ...
         */
        tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
-       ExecAssignResultType(planstate, tupDesc, true);
+       ExecAssignResultType(planstate, tupDesc);
 }
 
 /* ----------------
@@ -659,12 +658,11 @@ ExecGetScanType(ScanState *scanstate)
  * ----------------
  */
 void
-ExecAssignScanType(ScanState *scanstate,
-                                  TupleDesc tupDesc, bool shouldFree)
+ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc)
 {
        TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
 
-       ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
+       ExecSetSlotDescriptor(slot, tupDesc);
 }
 
 /* ----------------
@@ -680,7 +678,7 @@ ExecAssignScanTypeFromOuterPlan(ScanState *scanstate)
        outerPlan = outerPlanState(scanstate);
        tupDesc = ExecGetResultType(outerPlan);
 
-       ExecAssignScanType(scanstate, tupDesc, false);
+       ExecAssignScanType(scanstate, tupDesc);
 }
 
 
index 5ecded32ed91980c7b326b3bbe2801b4a5bcf146..6f8a7a66558f52831f1478bc7531d70571ab61c0 100644 (file)
@@ -21,7 +21,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeBitmapHeapscan.c,v 1.11 2006/05/23 15:21:52 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeBitmapHeapscan.c,v 1.12 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -538,7 +538,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
        /*
         * get the scan type from the relation descriptor.
         */
-       ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation), false);
+       ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
 
        /*
         * Initialize result tuple type and projection info.
index 494ac209a926492dbdb202bfe7afa5da60b8b6dc..0c77e8216909d887dfba8bd0df076a43deb5e55b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.38 2006/03/16 00:31:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.39 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -220,7 +220,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
        BlessTupleDesc(tupdesc);
 
        scanstate->tupdesc = tupdesc;
-       ExecAssignScanType(&scanstate->ss, tupdesc, false);
+       ExecAssignScanType(&scanstate->ss, tupdesc);
 
        /*
         * Other node-specific setup
index 0aabe49e9327f7d169e2632b17bdbe31605b15cf..097343fd88cdfc2bbf1c04142500edac291dcc5d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.81 2006/03/05 15:58:26 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.82 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -436,8 +436,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
        ExecAssignProjectionInfo(&hjstate->js.ps);
 
        ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
-                                                 ExecGetResultType(outerPlanState(hjstate)),
-                                                 false);
+                                                 ExecGetResultType(outerPlanState(hjstate)));
 
        /*
         * initialize hash-specific info
index 41b4dea980c3739209daa2ea245a57b8f87f57b5..433151a8a760e692b3f3ccfa0b7bf42eee219b74 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.113 2006/05/23 15:21:52 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.114 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -516,7 +516,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
        /*
         * get the scan type from the relation descriptor.
         */
-       ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false);
+       ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation));
 
        /*
         * Open the index relation.
index 0c59e11be60b3c035e1518c6a24e86dc202b8760..dbf056e25ef3d573f94c925c32d2b3bb5e7eb2d9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.79 2006/03/17 19:38:12 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.80 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1552,8 +1552,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 
        mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
        ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
-                                                 ExecGetResultType(innerPlanState(mergestate)),
-                                                 false);
+                                                 ExecGetResultType(innerPlanState(mergestate)));
 
        switch (node->join.jointype)
        {
index ce42ffbd6b7ac385f4cd13714e70f76db58505a6..c6e61d04d93cae67cc11d4996affec5b5be504ec 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeSeqscan.c,v 1.58 2006/03/05 15:58:26 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeSeqscan.c,v 1.59 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -159,7 +159,7 @@ InitScanRelation(SeqScanState *node, EState *estate)
        node->ss_currentRelation = currentRelation;
        node->ss_currentScanDesc = currentScanDesc;
 
-       ExecAssignScanType(node, RelationGetDescr(currentRelation), false);
+       ExecAssignScanType(node, RelationGetDescr(currentRelation));
 }
 
 
index 181f86ed03ef8a975518aa60db32df037df230f4..c6663c7eac2827d7f30d70d599433e14e4ac3c4f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.74 2006/03/05 15:58:26 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.75 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -865,14 +865,14 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
                 */
                tupDesc = ExecTypeFromTL(leftptlist, false);
                slot = ExecAllocTableSlot(tupTable);
-               ExecSetSlotDescriptor(slot, tupDesc, true);
+               ExecSetSlotDescriptor(slot, tupDesc);
                node->projLeft = ExecBuildProjectionInfo(lefttlist,
                                                                                                 NULL,
                                                                                                 slot);
 
                tupDesc = ExecTypeFromTL(rightptlist, false);
                slot = ExecAllocTableSlot(tupTable);
-               ExecSetSlotDescriptor(slot, tupDesc, true);
+               ExecSetSlotDescriptor(slot, tupDesc);
                node->projRight = ExecBuildProjectionInfo(righttlist,
                                                                                                  node->innerecontext,
                                                                                                  slot);
index 02b072893a51e444945daabc2d65fedbe238b643..e16bf7fe3c5033f8ed564276fc4507e7674e392a 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.29 2006/03/05 15:58:26 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.30 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -202,11 +202,13 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
        subquerystate->ss.ps.ps_TupFromTlist = false;
 
        /*
-        * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo)
+        * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo).
+        * Because the subplan is in its own memory context, we need to copy its
+        * result tuple type not just link to it; else the tupdesc will disappear
+        * too soon during shutdown.
         */
        ExecAssignScanType(&subquerystate->ss,
-                                          ExecGetResultType(subquerystate->subplan),
-                                          false);
+                       CreateTupleDescCopy(ExecGetResultType(subquerystate->subplan)));
 
        /*
         * Initialize result tuple type and projection info.
index 75862f08ac43bc93366c2c72353b9550b294ef02..78fd702e70c46e2484c35b02c9cebdccb419683c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.48 2006/03/05 15:58:26 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.49 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -524,7 +524,7 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
        /*
         * get the scan type from the relation descriptor.
         */
-       ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation), false);
+       ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation));
 
        /*
         * Initialize result tuple type and projection info.
index 10a994bcb3fd1bef9daa64211c2c9d2bb1e08d61..4da9f47dec5ba6e2872fe317771d010b525128bc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.211 2006/04/22 01:25:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.212 2006/06/16 18:42:22 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1418,12 +1418,19 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
                return true;
        tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1);
        if (fieldnum <= 0 || fieldnum > tupdesc->natts)
+       {
+               ReleaseTupleDesc(tupdesc);
                return false;
+       }
        attr = tupdesc->attrs[fieldnum - 1];
        if (attr->attisdropped ||
                attr->atttypid != expectedtype ||
                attr->atttypmod != expectedtypmod)
+       {
+               ReleaseTupleDesc(tupdesc);
                return false;
+       }
+       ReleaseTupleDesc(tupdesc);
        return true;
 }
 
index 3f5d8563f60d0e88692cb0df160d42ba27e415af..bbf3c8aab7f66f15761efeae36ef0ff3e7b66789 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.138 2006/04/22 01:25:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.139 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -772,6 +772,8 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
                                                format_type_be(targetTypeId)),
                                 errdetail("Input has too many columns.")));
 
+       ReleaseTupleDesc(tupdesc);
+
        rowexpr = makeNode(RowExpr);
        rowexpr->args = newargs;
        rowexpr->row_typeid = targetTypeId;
index 4e3c47eb208f2227105b793f756ce4928c7b0738..86670d2679410811a5c891d003bd664926c2c262 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.142 2006/03/23 00:19:30 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.143 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -850,7 +850,8 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
                ((Var *) expr)->vartype == RECORDOID)
                tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0);
        else if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
-               tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+               tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
+                                                                                               exprTypmod(expr));
        Assert(tupleDesc);
 
        /* Generate a list of references to the individual fields */
@@ -1027,7 +1028,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
         * appropriate error message while failing.
         */
        if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
-               tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+               tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
+                                                                                               exprTypmod(expr));
 
        return tupleDesc;
 }
index bb61dc2956840de9073f278a593d31697e4deae5..f701da8582b41cb19c891df871b7394553ef1628 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.15 2006/04/04 19:35:36 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.16 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -264,6 +264,7 @@ record_in(PG_FUNCTION_ARGS)
        pfree(buf.data);
        pfree(values);
        pfree(nulls);
+       ReleaseTupleDesc(tupdesc);
 
        PG_RETURN_HEAPTUPLEHEADER(result);
 }
@@ -411,6 +412,7 @@ record_out(PG_FUNCTION_ARGS)
 
        pfree(values);
        pfree(nulls);
+       ReleaseTupleDesc(tupdesc);
 
        PG_RETURN_CSTRING(buf.data);
 }
@@ -605,6 +607,7 @@ record_recv(PG_FUNCTION_ARGS)
        heap_freetuple(tuple);
        pfree(values);
        pfree(nulls);
+       ReleaseTupleDesc(tupdesc);
 
        PG_RETURN_HEAPTUPLEHEADER(result);
 }
@@ -731,6 +734,7 @@ record_send(PG_FUNCTION_ARGS)
 
        pfree(values);
        pfree(nulls);
+       ReleaseTupleDesc(tupdesc);
 
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
index 83697436c0e0422c5f95e7b26fddae624648b8c8..182d3cf1072536a5c7bf988a56a8ebd2fd09149d 100644 (file)
@@ -2,7 +2,7 @@
  * ruleutils.c - Functions to convert stored expressions/querytrees
  *                             back to source text
  *
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.223 2006/05/28 21:13:53 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.224 2006/06/16 18:42:22 tgl Exp $
  **********************************************************************/
 
 #include "postgres.h"
@@ -2697,7 +2697,8 @@ get_name_for_var_field(Var *var, int fieldno,
         * appropriate error message while failing.
         */
        if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
-               tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+               tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
+                                                                                               exprTypmod(expr));
 
        /* Got the tupdesc, so we can extract the field name */
        Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
@@ -3312,8 +3313,8 @@ get_rule_expr(Node *node, deparse_context *context,
                                        TupleDesc       tupdesc;
 
                                        if (get_expr_result_type(arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-                                               tupdesc = lookup_rowtype_tupdesc(exprType(arg),
-                                                                                                                exprTypmod(arg));
+                                               tupdesc = lookup_rowtype_tupdesc_copy(exprType(arg),
+                                                                                                                         exprTypmod(arg));
                                        Assert(tupdesc);
                                        /* Got the tupdesc, so we can extract the field name */
                                        Assert(fno >= 1 && fno <= tupdesc->natts);
@@ -3514,6 +3515,8 @@ get_rule_expr(Node *node, deparse_context *context,
                                                }
                                                i++;
                                        }
+
+                                       ReleaseTupleDesc(tupdesc);
                                }
                                appendStringInfo(buf, ")");
                                if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
index 9d4ffa4b148c3aa96276dddac3e86675fb9350fe..82de42020e89ec1a2e9c0fdadc9633d91e4d9086 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.241 2006/05/06 15:51:07 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.242 2006/06/16 18:42:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -313,6 +313,8 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
        /* and allocate attribute tuple form storage */
        relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts,
                                                                                           relationForm->relhasoids);
+       /* which we mark as a reference-counted tupdesc */
+       relation->rd_att->tdrefcount = 1;
 
        MemoryContextSwitchTo(oldcxt);
 
@@ -1234,6 +1236,8 @@ formrdesc(const char *relationName, Oid relationReltype,
         * defined by macros in src/include/catalog/ headers.
         */
        relation->rd_att = CreateTemplateTupleDesc(natts, hasoids);
+       relation->rd_att->tdrefcount = 1;       /* mark as refcounted */
+
        relation->rd_att->tdtypeid = relationReltype;
        relation->rd_att->tdtypmod = -1;        /* unnecessary, but... */
 
@@ -1591,7 +1595,10 @@ RelationClearRelation(Relation relation, bool rebuild)
        {
                /* ok to zap remaining substructure */
                flush_rowtype_cache(old_reltype);
-               FreeTupleDesc(relation->rd_att);
+               /* can't use DecrTupleDescRefCount here */
+               Assert(relation->rd_att->tdrefcount > 0);
+               if (--relation->rd_att->tdrefcount == 0)
+                       FreeTupleDesc(relation->rd_att);
                if (relation->rd_rulescxt)
                        MemoryContextDelete(relation->rd_rulescxt);
                pfree(relation);
@@ -1601,7 +1608,10 @@ RelationClearRelation(Relation relation, bool rebuild)
                /*
                 * When rebuilding an open relcache entry, must preserve ref count and
                 * rd_createSubid state.  Also attempt to preserve the tupledesc and
-                * rewrite-rule substructures in place.
+                * rewrite-rule substructures in place.  (Note: the refcount mechanism
+                * for tupledescs may eventually ensure that we don't really need to
+                * preserve the tupledesc in-place, but for now there are still a lot
+                * of places that assume an open rel's tupledesc won't move.)
                 *
                 * Note that this process does not touch CurrentResourceOwner; which
                 * is good because whatever ref counts the entry may have do not
@@ -1618,7 +1628,9 @@ RelationClearRelation(Relation relation, bool rebuild)
                {
                        /* Should only get here if relation was deleted */
                        flush_rowtype_cache(old_reltype);
-                       FreeTupleDesc(old_att);
+                       Assert(old_att->tdrefcount > 0);
+                       if (--old_att->tdrefcount == 0)
+                               FreeTupleDesc(old_att);
                        if (old_rulescxt)
                                MemoryContextDelete(old_rulescxt);
                        pfree(relation);
@@ -1629,13 +1641,17 @@ RelationClearRelation(Relation relation, bool rebuild)
                if (equalTupleDescs(old_att, relation->rd_att))
                {
                        /* needn't flush typcache here */
-                       FreeTupleDesc(relation->rd_att);
+                       Assert(relation->rd_att->tdrefcount == 1);
+                       if (--relation->rd_att->tdrefcount == 0)
+                               FreeTupleDesc(relation->rd_att);
                        relation->rd_att = old_att;
                }
                else
                {
                        flush_rowtype_cache(old_reltype);
-                       FreeTupleDesc(old_att);
+                       Assert(old_att->tdrefcount > 0);
+                       if (--old_att->tdrefcount == 0)
+                               FreeTupleDesc(old_att);
                }
                if (equalRuleLocks(old_rules, relation->rd_rules))
                {
@@ -2075,6 +2091,7 @@ RelationBuildLocalRelation(const char *relname,
         * catalogs.  We can copy attnotnull constraints here, however.
         */
        rel->rd_att = CreateTupleDescCopy(tupDesc);
+       rel->rd_att->tdrefcount = 1;    /* mark as refcounted */
        has_not_null = false;
        for (i = 0; i < natts; i++)
        {
@@ -2996,6 +3013,8 @@ load_relcache_init_file(void)
                /* initialize attribute tuple forms */
                rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
                                                                                          relform->relhasoids);
+               rel->rd_att->tdrefcount = 1;    /* mark as refcounted */
+
                rel->rd_att->tdtypeid = relform->reltype;
                rel->rd_att->tdtypmod = -1;             /* unnecessary, but... */
 
index c3f0afa6853ace8a720836e5e8b85e4f71cb1cd4..fbeefad74574b7f0f2b6d71cbde5fe44adea4aff 100644 (file)
@@ -36,7 +36,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.18 2006/03/05 15:58:45 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.19 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -270,12 +270,16 @@ lookup_type_cache(Oid type_id, int flags)
                Assert(rel->rd_rel->reltype == typentry->type_id);
 
                /*
-                * Notice that we simply store a link to the relcache's tupdesc. Since
-                * we are relying on relcache to detect cache flush events, there's
-                * not a lot of point to maintaining an independent copy.
+                * Link to the tupdesc and increment its refcount (we assert it's
+                * a refcounted descriptor).  We don't use IncrTupleDescRefCount()
+                * for this, because the reference mustn't be entered in the current
+                * resource owner; it can outlive the current query.
                 */
                typentry->tupDesc = RelationGetDescr(rel);
 
+               Assert(typentry->tupDesc->tdrefcount > 0);
+               typentry->tupDesc->tdrefcount++;
+
                relation_close(rel, AccessShareLock);
        }
 
@@ -283,29 +287,13 @@ lookup_type_cache(Oid type_id, int flags)
 }
 
 /*
- * lookup_rowtype_tupdesc
- *
- * Given a typeid/typmod that should describe a known composite type,
- * return the tuple descriptor for the type.  Will ereport on failure.
- *
- * Note: returned TupleDesc points to cached copy; caller must copy it
- * if intending to scribble on it or keep a reference for a long time.
- */
-TupleDesc
-lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
-{
-       return lookup_rowtype_tupdesc_noerror(type_id, typmod, false);
-}
-
-/*
- * lookup_rowtype_tupdesc_noerror
+ * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
  *
- * As above, but if the type is not a known composite type and noError
- * is true, returns NULL instead of ereport'ing.  (Note that if a bogus
- * type_id is passed, you'll get an ereport anyway.)
+ * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
+ * hasn't had its refcount bumped.
  */
-TupleDesc
-lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
+static TupleDesc
+lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
 {
        if (type_id != RECORDOID)
        {
@@ -339,6 +327,59 @@ lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
        }
 }
 
+/*
+ * lookup_rowtype_tupdesc
+ *
+ * Given a typeid/typmod that should describe a known composite type,
+ * return the tuple descriptor for the type.  Will ereport on failure.
+ *
+ * Note: on success, we increment the refcount of the returned TupleDesc,
+ * and log the reference in CurrentResourceOwner.  Caller should call
+ * ReleaseTupleDesc or DecrTupleDescRefCount when done using the tupdesc.
+ */
+TupleDesc
+lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
+{
+       TupleDesc       tupDesc;
+
+       tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
+       IncrTupleDescRefCount(tupDesc);
+       return tupDesc;
+}
+
+/*
+ * lookup_rowtype_tupdesc_noerror
+ *
+ * As above, but if the type is not a known composite type and noError
+ * is true, returns NULL instead of ereport'ing.  (Note that if a bogus
+ * type_id is passed, you'll get an ereport anyway.)
+ */
+TupleDesc
+lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
+{
+       TupleDesc       tupDesc;
+
+       tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
+       if (tupDesc != NULL)
+               IncrTupleDescRefCount(tupDesc);
+       return tupDesc;
+}
+
+/*
+ * lookup_rowtype_tupdesc_copy
+ *
+ * Like lookup_rowtype_tupdesc(), but the returned TupleDesc has been
+ * copied into the CurrentMemoryContext and is not reference-counted.
+ */
+TupleDesc
+lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
+{
+       TupleDesc tmp;
+
+       tmp = lookup_rowtype_tupdesc_internal(type_id, typmod, false);
+       return CreateTupleDescCopyConstr(tmp);
+}
+
 
 /*
  * assign_record_type_typmod
@@ -425,6 +466,8 @@ assign_record_type_typmod(TupleDesc tupDesc)
        /* if fail in subrs, no damage except possibly some wasted memory... */
        entDesc = CreateTupleDescCopy(tupDesc);
        recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
+       /* mark it as a reference-counted tupdesc */
+       entDesc->tdrefcount = 1;
        /* now it's safe to advance NextRecordTypmod */
        newtypmod = NextRecordTypmod++;
        entDesc->tdtypmod = newtypmod;
@@ -456,6 +499,17 @@ flush_rowtype_cache(Oid type_id)
                                                                                          HASH_FIND, NULL);
        if (typentry == NULL)
                return;                                 /* no matching entry */
+       if (typentry->tupDesc == NULL)
+               return;                                 /* tupdesc hasn't been requested */
+
+       /*
+        * Release our refcount and free the tupdesc if none remain.
+        * (Can't use DecrTupleDescRefCount because this reference is not
+        * logged in current resource owner.)
+        */
+       Assert(typentry->tupDesc->tdrefcount > 0);
+       if (--typentry->tupDesc->tdrefcount == 0)
+               FreeTupleDesc(typentry->tupDesc);
 
        typentry->tupDesc = NULL;
 }
index 6508d275bbee5d267b7ba7dedf223fc18510edf2..851ef2917cededad8602a397d2e73a8cd604dd61 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2002-2006, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.29 2006/03/05 15:58:46 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.30 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -185,8 +185,7 @@ shutdown_MultiFuncCall(Datum arg)
  *             receives the actual datatype OID (this is mainly useful for scalar
  *             result types).  If resultTupleDesc isn't NULL, *resultTupleDesc
  *             receives a pointer to a TupleDesc when the result is of a composite
- *             type, or NULL when it's a scalar result.  NB: the tupledesc should
- *             be copied if it is to be accessed over a long period.
+ *             type, or NULL when it's a scalar result.
  *
  * One hard case that this handles is resolution of actual rowtypes for
  * functions returning RECORD (from either the function's OUT parameter
@@ -246,7 +245,7 @@ get_expr_result_type(Node *expr,
                        *resultTupleDesc = NULL;
                result = get_type_func_class(typid);
                if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
-                       *resultTupleDesc = lookup_rowtype_tupdesc(typid, -1);
+                       *resultTupleDesc = lookup_rowtype_tupdesc_copy(typid, -1);
        }
 
        return result;
@@ -363,7 +362,7 @@ internal_get_result_type(Oid funcid,
        {
                case TYPEFUNC_COMPOSITE:
                        if (resultTupleDesc)
-                               *resultTupleDesc = lookup_rowtype_tupdesc(rettype, -1);
+                               *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1);
                        /* Named composite types can't have any polymorphic columns */
                        break;
                case TYPEFUNC_SCALAR:
@@ -1053,7 +1052,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
        if (functypclass == TYPEFUNC_COMPOSITE)
        {
                /* Composite data type, e.g. a table's row type */
-               tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
+               tupdesc = lookup_rowtype_tupdesc_copy(typeoid, -1);
 
                if (colaliases != NIL)
                {
index 474dc76c77584f7cf171683759b9398ff83173a8..56629d5089d0e72ea625bf8dd49c785b6db8ec2e 100644 (file)
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.3 2004/08/25 18:43:43 tgl Exp $
+$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.4 2006/06/16 18:42:23 tgl Exp $
 
 Notes about resource owners
 ---------------------------
@@ -61,9 +61,9 @@ ResourceOwner transfers lock ownership to the parent instead of actually
 releasing the lock, if isCommit is true.
 
 Currently, ResourceOwners contain direct support for recording ownership
-of buffer pins, lmgr locks, and catcache and relcache references.  Other
-objects can be associated with a ResourceOwner by recording the address of
-the owning ResourceOwner in such an object.  There is an API for other
+of buffer pins, lmgr locks, and catcache, relcache, and tupdesc references.
+Other objects can be associated with a ResourceOwner by recording the address
+of the owning ResourceOwner in such an object.  There is an API for other
 modules to get control during ResourceOwner release, so that they can scan
 their own data structures to find the objects that need to be deleted.
 
index 97ce5f12100030f6ebe2af3c52e69a9930cfde9a..af379803754270e643c43f53619c637a68bc692f 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.19 2006/04/03 13:44:33 teodor Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.20 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,6 +57,11 @@ typedef struct ResourceOwnerData
        int                     nrelrefs;               /* number of owned relcache pins */
        Relation   *relrefs;            /* dynamically allocated array */
        int                     maxrelrefs;             /* currently allocated array size */
+
+       /* We have built-in support for remembering tupdesc references */
+       int                     ntupdescs;              /* number of owned tupdesc references */
+       TupleDesc  *tupdescs;           /* dynamically allocated array */
+       int                     maxtupdescs;    /* currently allocated array size */
 } ResourceOwnerData;
 
 
@@ -87,6 +92,7 @@ static void ResourceOwnerReleaseInternal(ResourceOwner owner,
                                                         bool isCommit,
                                                         bool isTopLevel);
 static void PrintRelCacheLeakWarning(Relation rel);
+static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
 
 
 /*****************************************************************************
@@ -258,7 +264,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
                /*
                 * Release catcache references.  Note that ReleaseCatCache will remove
                 * the catref entry from my list, so I just have to iterate till there
-                * are none.  Ditto for catcache lists.
+                * are none.
                 *
                 * As with buffer pins, warn if any are left at commit time, and
                 * release back-to-front for speed.
@@ -269,12 +275,20 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
                                PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
                        ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
                }
+               /* Ditto for catcache lists */
                while (owner->ncatlistrefs > 0)
                {
                        if (isCommit)
                                PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
                        ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
                }
+               /* Ditto for tupdesc references */
+               while (owner->ntupdescs > 0)
+               {
+                       if (isCommit)
+                               PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
+                       DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
+               }
 
                /* Clean up index scans too */
                ReleaseResources_hash();
@@ -304,6 +318,7 @@ ResourceOwnerDelete(ResourceOwner owner)
        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
@@ -328,6 +343,8 @@ ResourceOwnerDelete(ResourceOwner owner)
                pfree(owner->catlistrefs);
        if (owner->relrefs)
                pfree(owner->relrefs);
+       if (owner->tupdescs)
+               pfree(owner->tupdescs);
 
        pfree(owner);
 }
@@ -742,3 +759,85 @@ PrintRelCacheLeakWarning(Relation rel)
        elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
                 RelationGetRelationName(rel));
 }
+
+/*
+ * Make sure there is room for at least one more entry in a ResourceOwner's
+ * tupdesc reference 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 tupdesc reference 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 tupdesc reference 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, "tupdesc reference %p is not owned by resource owner %s",
+                tupdesc, owner->name);
+}
+
+/*
+ * Debugging subroutine
+ */
+static void
+PrintTupleDescLeakWarning(TupleDesc tupdesc)
+{
+       elog(WARNING,
+                "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
+                tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
+}
index 670ef5c92e0e7434d522816f401ea386d5f01416..035a0a85e528daf80743a6116ecc30e29ac7c648 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.49 2006/03/16 00:31:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.50 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,6 +57,14 @@ typedef struct tupleConstr
  * 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
  * typcache.c type cache.
+ *
+ * Tuple descriptors that live in caches (relcache or typcache, at present)
+ * are reference-counted: they can be deleted when their reference count goes
+ * to zero.  Tuple descriptors created by the executor need no reference
+ * counting, however: they are simply created in the appropriate memory
+ * context and go away when the context is freed.  We set the tdrefcount
+ * field of such a descriptor to -1, while reference-counted descriptors
+ * always have tdrefcount >= 0.
  */
 typedef struct tupleDesc
 {
@@ -67,6 +75,7 @@ typedef struct tupleDesc
        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 */
 }      *TupleDesc;
 
 
@@ -81,6 +90,21 @@ extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
 
 extern void FreeTupleDesc(TupleDesc tupdesc);
 
+extern void IncrTupleDescRefCount(TupleDesc tupdesc);
+extern void DecrTupleDescRefCount(TupleDesc tupdesc);
+
+#define PinTupleDesc(tupdesc) \
+       do { \
+               if ((tupdesc)->tdrefcount >= 0) \
+                       IncrTupleDescRefCount(tupdesc); \
+       } while (0)
+
+#define ReleaseTupleDesc(tupdesc) \
+       do { \
+               if ((tupdesc)->tdrefcount >= 0) \
+                       DecrTupleDescRefCount(tupdesc); \
+       } while (0)
+  
 extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
 
 extern void TupleDescInitEntry(TupleDesc desc,
index a29e13949747d0727413cd9aefabe8976674da72..d926d8b5c5c5249f0a863da90ee98bf171111cef 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.126 2006/03/05 15:58:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.127 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -249,8 +249,7 @@ extern ExprContext *MakePerTupleExprContext(EState *estate);
        } while (0)
 
 extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
-extern void ExecAssignResultType(PlanState *planstate,
-                                        TupleDesc tupDesc, bool shouldFree);
+extern void ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc);
 extern void ExecAssignResultTypeFromTL(PlanState *planstate);
 extern TupleDesc ExecGetResultType(PlanState *planstate);
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
@@ -259,8 +258,7 @@ extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 extern void ExecAssignProjectionInfo(PlanState *planstate);
 extern void ExecFreeExprContext(PlanState *planstate);
 extern TupleDesc ExecGetScanType(ScanState *scanstate);
-extern void ExecAssignScanType(ScanState *scanstate,
-                                  TupleDesc tupDesc, bool shouldFree);
+extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc);
 extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate);
 
 extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
index 1b2eb883ecff2a2c9a2ca12e5f7a0ba0a46609ba..b506651fbbd9248be13c67ac65be9afa039ec4fd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.30 2006/03/05 15:58:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.31 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #define TUPTABLE_H
 
 #include "access/htup.h"
+#include "access/tupdesc.h"
 
 
 /*----------
  * TRUE, tts_shouldFree FALSE, tts_tuple NULL, tts_buffer InvalidBuffer,
  * and tts_nvalid zero.
  *
+ * The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot
+ * code.  The caller of ExecSetSlotDescriptor() is responsible for providing
+ * a descriptor that will live as long as the slot does.  (Typically, both
+ * slots and descriptors are in per-query memory and are freed by memory
+ * context deallocation at query end; so it's not worth providing any extra
+ * mechanism to do more.  However, the slot will increment the tupdesc
+ * reference count if a reference-counted tupdesc is supplied.)
+ *
  * When tts_shouldFree is true, the physical tuple is "owned" by the slot
  * and should be freed when the slot's reference to the tuple is dropped.
  *
- * tts_shouldFreeDesc is similar to tts_shouldFree: if it's true, then the
- * tupleDescriptor is "owned" by the TupleTableSlot and should be
- * freed when the slot's reference to the descriptor is dropped.
- *
  * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
  * on the indicated buffer page; drop the pin when we release the
  * slot's reference to that buffer.  (tts_shouldFree should always be
@@ -87,7 +92,6 @@ typedef struct TupleTableSlot
        NodeTag         type;                   /* vestigial ... allows IsA tests */
        bool            tts_isempty;    /* true = slot is empty */
        bool            tts_shouldFree; /* should pfree tuple? */
-       bool            tts_shouldFreeDesc;             /* should pfree descriptor? */
        bool            tts_slow;               /* saved state for slot_deform_tuple */
        HeapTuple       tts_tuple;              /* physical tuple, or NULL if none */
        TupleDesc       tts_tupleDescriptor;    /* slot's tuple descriptor */
@@ -124,8 +128,7 @@ extern void ExecDropTupleTable(TupleTable table, bool shouldFree);
 extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
 extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
 extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
-extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
-                                         TupleDesc tupdesc, bool shouldFree);
+extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
 extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
                           TupleTableSlot *slot,
                           Buffer buffer,
index 4e69f844a7e4cb38d3744aa5b22955196950e7b8..9c8b91670b1fe341b7176507e50a16d30dec90a0 100644 (file)
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.6 2006/03/05 15:59:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.7 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef RESOWNER_H
 #define RESOWNER_H
 
+#include "access/tupdesc.h"
 #include "storage/buf.h"
 #include "utils/catcache.h"
 #include "utils/rel.h"
@@ -107,4 +108,11 @@ extern void ResourceOwnerRememberRelationRef(ResourceOwner owner,
 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 */
index cbb13be3255439e4ac7dd2d9495eaf96c9f09122..7d3e284871c7e837fd6cc5b8aaa1a3c038e976c7 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.10 2006/03/05 15:59:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.11 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,7 +58,7 @@ typedef struct TypeCacheEntry
        /*
         * Tuple descriptor if it's a composite type (row type).  NULL if not
         * composite or information hasn't yet been requested.  (NOTE: this is
-        * actually just a link to information maintained by relcache.c.)
+        * a reference-counted tupledesc.)
         */
        TupleDesc       tupDesc;
 } TypeCacheEntry;
@@ -79,6 +79,8 @@ extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod);
 extern TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod,
                                                           bool noError);
 
+extern TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod);
+
 extern void assign_record_type_typmod(TupleDesc tupDesc);
 
 extern void flush_rowtype_cache(Oid type_id);
index 612d4261f693064e4aa1bee01bc3640652c678d2..2ed16b12cbeb1f7b7522d3f2a15351536046a911 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plperl.c - perl as a procedural language for PostgreSQL
  *
- *       $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.111 2006/05/30 22:12:15 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.112 2006/06/16 18:42:23 tgl Exp $
  *
  **********************************************************************/
 
@@ -904,6 +904,7 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 
                        hashref = plperl_hash_from_tuple(&tmptup, tupdesc);
                        XPUSHs(sv_2mortal(hashref));
+                       ReleaseTupleDesc(tupdesc);
                }
                else
                {
index 893ad1dfdc8effcc86d2959622799aa4cb2d091d..e74dcac23111cbd16784901b0845b57cd2ac4261 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.171 2006/06/15 18:02:22 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.172 2006/06/16 18:42:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -233,6 +233,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
                                                tmptup.t_tableOid = InvalidOid;
                                                tmptup.t_data = td;
                                                exec_move_row(&estate, NULL, row, &tmptup, tupdesc);
+                                               ReleaseTupleDesc(tupdesc);
                                        }
                                        else
                                        {
@@ -1701,10 +1702,11 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
 
        /*
         * Run the query
+        *
         * Retrieving two rows can be slower than a single row, e.g. 
         * a sequential scan where the scan has to be completed to
-        * check for a second row.  For this reason, we only do the
-        * second-line check for STRICT.
+        * check for a second row.  For this reason, we only retrieve
+        * the second row if checking STRICT.
         */
        exec_run_select(estate, stmt->query, stmt->strict ? 2 : 1, NULL);
        tuptab = estate->eval_tuptable;
@@ -1717,22 +1719,21 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
         */
        if (n == 0)
        {
-               if (!stmt->strict)
-               {
-                       /* null the target */
-                       exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
-                       exec_eval_cleanup(estate);
-                       return PLPGSQL_RC_OK;
-               }
-               else
+               if (stmt->strict)
                        ereport(ERROR,
-                               (errcode(ERRCODE_NO_DATA),
-                                errmsg("query returned no rows")));
+                                       (errcode(ERRCODE_NO_DATA),
+                                        errmsg("query returned no rows")));
+
+               /* set the target to NULL(s) */
+               exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
+               exec_eval_cleanup(estate);
+               return PLPGSQL_RC_OK;
        }
-       else if (n > 1 && stmt->strict)
+
+       if (n > 1 && stmt->strict)
                ereport(ERROR,
-                       (errcode(ERRCODE_CARDINALITY_VIOLATION),
-                        errmsg("query more than one row")));
+                               (errcode(ERRCODE_CARDINALITY_VIOLATION),
+                                errmsg("query returned more than one row")));
 
        /*
         * Put the first result into the target and set found to true
@@ -3138,6 +3139,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                        tmptup.t_tableOid = InvalidOid;
                                        tmptup.t_data = td;
                                        exec_move_row(estate, NULL, row, &tmptup, tupdesc);
+                                       ReleaseTupleDesc(tupdesc);
                                }
                                break;
                        }
@@ -3180,6 +3182,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                        tmptup.t_tableOid = InvalidOid;
                                        tmptup.t_data = td;
                                        exec_move_row(estate, rec, NULL, &tmptup, tupdesc);
+                                       ReleaseTupleDesc(tupdesc);
                                }
                                break;
                        }
index 1aedc0c76256aeeb38b98d83c2ed8763b13d868a..bc8310a045b42c94204c007c7a61e843c8cfe5a9 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.81 2006/05/30 22:12:16 tgl Exp $
+ *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.82 2006/06/16 18:42:23 tgl Exp $
  *
  *********************************************************************
  */
@@ -883,6 +883,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                        tmptup.t_data = td;
 
                                        arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
+                                       ReleaseTupleDesc(tupdesc);
                                }
                        }
                        else
index 198b11c77e9bb86d0e439595f6d5354fb85ba6fb..75964b2568424ce8e46c0e7a574f4626fd98e4e8 100644 (file)
@@ -2,7 +2,7 @@
  * pltcl.c             - PostgreSQL support for Tcl as
  *                               procedural language (PL)
  *
- *       $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.104 2006/05/30 22:12:16 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.105 2006/06/16 18:42:24 tgl Exp $
  *
  **********************************************************************/
 
@@ -511,6 +511,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
                                        pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
                                        Tcl_DStringAppendElement(&tcl_cmd,
                                                                                         Tcl_DStringValue(&list_tmp));
+                                       ReleaseTupleDesc(tupdesc);
                                }
                        }
                        else