Commit 06e10abc authored by Tom Lane's avatar Tom Lane

Fix problems with cached tuple descriptors disappearing while still in use

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
parent b49ce32d
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be * some of the executor utility code such as "ExecTypeFromTL" should be
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/resowner.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -84,6 +85,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) ...@@ -84,6 +85,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
desc->tdtypeid = RECORDOID; desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1; desc->tdtypmod = -1;
desc->tdhasoid = hasoid; desc->tdhasoid = hasoid;
desc->tdrefcount = -1; /* assume not reference-counted */
return desc; return desc;
} }
...@@ -116,6 +118,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) ...@@ -116,6 +118,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
desc->tdtypeid = RECORDOID; desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1; desc->tdtypmod = -1;
desc->tdhasoid = hasoid; desc->tdhasoid = hasoid;
desc->tdrefcount = -1; /* assume not reference-counted */
return desc; return desc;
} }
...@@ -214,6 +217,12 @@ FreeTupleDesc(TupleDesc tupdesc) ...@@ -214,6 +217,12 @@ FreeTupleDesc(TupleDesc tupdesc)
{ {
int i; 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)
{ {
if (tupdesc->constr->num_defval > 0) if (tupdesc->constr->num_defval > 0)
...@@ -246,12 +255,48 @@ FreeTupleDesc(TupleDesc tupdesc) ...@@ -246,12 +255,48 @@ FreeTupleDesc(TupleDesc tupdesc)
pfree(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 * Compare two TupleDesc structures for logical equality
* *
* Note: we deliberately do not check the attrelid and tdtypmod fields. * 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 * 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. * matches a requested type, and is harmless for relcache.c's uses.
* We don't compare tdrefcount, either.
*/ */
bool bool
equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * INTERFACE ROUTINES
...@@ -892,7 +892,10 @@ toast_flatten_tuple_attribute(Datum value, ...@@ -892,7 +892,10 @@ toast_flatten_tuple_attribute(Datum value,
* If nothing to untoast, just return the original tuple. * If nothing to untoast, just return the original tuple.
*/ */
if (!need_change) if (!need_change)
{
ReleaseTupleDesc(tupleDesc);
return value; return value;
}
/* /*
* Calculate the new size of the tuple. Header size should not change, * Calculate the new size of the tuple. Header size should not change,
...@@ -929,6 +932,7 @@ toast_flatten_tuple_attribute(Datum value, ...@@ -929,6 +932,7 @@ toast_flatten_tuple_attribute(Datum value,
for (i = 0; i < numAttrs; i++) for (i = 0; i < numAttrs; i++)
if (toast_free[i]) if (toast_free[i])
pfree(DatumGetPointer(toast_values[i])); pfree(DatumGetPointer(toast_values[i]));
ReleaseTupleDesc(tupleDesc);
return PointerGetDatum(new_data); return PointerGetDatum(new_data);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -2637,6 +2637,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
MemoryContextSwitchTo(oldCxt); MemoryContextSwitchTo(oldCxt);
heap_endscan(scan); heap_endscan(scan);
ExecDropSingleTupleTableSlot(oldslot);
ExecDropSingleTupleTableSlot(newslot);
} }
FreeExecutorState(estate); FreeExecutorState(estate);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -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. * Use the given slot, or make a new slot if we weren't given one.
*/ */
if (slot) if (slot)
ExecSetSlotDescriptor(slot, cleanTupType, false); ExecSetSlotDescriptor(slot, cleanTupType);
else else
slot = MakeSingleTupleTableSlot(cleanTupType); slot = MakeSingleTupleTableSlot(cleanTupType);
...@@ -150,7 +150,7 @@ ExecInitJunkFilterConversion(List *targetList, ...@@ -150,7 +150,7 @@ ExecInitJunkFilterConversion(List *targetList,
* Use the given slot, or make a new slot if we weren't given one. * Use the given slot, or make a new slot if we weren't given one.
*/ */
if (slot) if (slot)
ExecSetSlotDescriptor(slot, cleanTupType, false); ExecSetSlotDescriptor(slot, cleanTupType);
else else
slot = MakeSingleTupleTableSlot(cleanTupType); slot = MakeSingleTupleTableSlot(cleanTupType);
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -1445,9 +1445,7 @@ ExecInsert(TupleTableSlot *slot,
TupleTableSlot *newslot = estate->es_trig_tuple_slot; TupleTableSlot *newslot = estate->es_trig_tuple_slot;
if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor) if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
ExecSetSlotDescriptor(newslot, ExecSetSlotDescriptor(newslot, slot->tts_tupleDescriptor);
slot->tts_tupleDescriptor,
false);
ExecStoreTuple(newtuple, newslot, InvalidBuffer, false); ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
slot = newslot; slot = newslot;
tuple = newtuple; tuple = newtuple;
...@@ -1654,9 +1652,7 @@ ExecUpdate(TupleTableSlot *slot, ...@@ -1654,9 +1652,7 @@ ExecUpdate(TupleTableSlot *slot,
TupleTableSlot *newslot = estate->es_trig_tuple_slot; TupleTableSlot *newslot = estate->es_trig_tuple_slot;
if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor) if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
ExecSetSlotDescriptor(newslot, ExecSetSlotDescriptor(newslot, slot->tts_tupleDescriptor);
slot->tts_tupleDescriptor,
false);
ExecStoreTuple(newtuple, newslot, InvalidBuffer, false); ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
slot = newslot; slot = newslot;
tuple = newtuple; tuple = newtuple;
......
This diff is collapsed.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -143,7 +143,6 @@ ExecCreateTupleTable(int tableSize)
slot->type = T_TupleTableSlot; slot->type = T_TupleTableSlot;
slot->tts_isempty = true; slot->tts_isempty = true;
slot->tts_shouldFree = false; slot->tts_shouldFree = false;
slot->tts_shouldFreeDesc = false;
slot->tts_tuple = NULL; slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL; slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext; slot->tts_mcxt = CurrentMemoryContext;
...@@ -189,8 +188,8 @@ ExecDropTupleTable(TupleTable table, /* tuple table */ ...@@ -189,8 +188,8 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
TupleTableSlot *slot = &(table->array[i]); TupleTableSlot *slot = &(table->array[i]);
ExecClearTuple(slot); ExecClearTuple(slot);
if (slot->tts_shouldFreeDesc) if (slot->tts_tupleDescriptor)
FreeTupleDesc(slot->tts_tupleDescriptor); ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (slot->tts_values) if (slot->tts_values)
pfree(slot->tts_values); pfree(slot->tts_values);
if (slot->tts_isnull) if (slot->tts_isnull)
...@@ -210,7 +209,7 @@ ExecDropTupleTable(TupleTable table, /* tuple table */ ...@@ -210,7 +209,7 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
* This is a convenience routine for operations that need a * This is a convenience routine for operations that need a
* standalone TupleTableSlot not gotten from the main executor * standalone TupleTableSlot not gotten from the main executor
* tuple table. It makes a single slot and initializes it as * tuple table. It makes a single slot and initializes it as
* though by ExecSetSlotDescriptor(slot, tupdesc, false). * though by ExecSetSlotDescriptor(slot, tupdesc).
* -------------------------------- * --------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
...@@ -221,7 +220,6 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc) ...@@ -221,7 +220,6 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
/* This should match ExecCreateTupleTable() */ /* This should match ExecCreateTupleTable() */
slot->tts_isempty = true; slot->tts_isempty = true;
slot->tts_shouldFree = false; slot->tts_shouldFree = false;
slot->tts_shouldFreeDesc = false;
slot->tts_tuple = NULL; slot->tts_tuple = NULL;
slot->tts_tupleDescriptor = NULL; slot->tts_tupleDescriptor = NULL;
slot->tts_mcxt = CurrentMemoryContext; slot->tts_mcxt = CurrentMemoryContext;
...@@ -230,7 +228,7 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc) ...@@ -230,7 +228,7 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
slot->tts_values = NULL; slot->tts_values = NULL;
slot->tts_isnull = NULL; slot->tts_isnull = NULL;
ExecSetSlotDescriptor(slot, tupdesc, false); ExecSetSlotDescriptor(slot, tupdesc);
return slot; return slot;
} }
...@@ -250,8 +248,8 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot) ...@@ -250,8 +248,8 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Assert(slot != NULL); Assert(slot != NULL);
ExecClearTuple(slot); ExecClearTuple(slot);
if (slot->tts_shouldFreeDesc) if (slot->tts_tupleDescriptor)
FreeTupleDesc(slot->tts_tupleDescriptor); ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (slot->tts_values) if (slot->tts_values)
pfree(slot->tts_values); pfree(slot->tts_values);
if (slot->tts_isnull) if (slot->tts_isnull)
...@@ -309,13 +307,15 @@ ExecAllocTableSlot(TupleTable table) ...@@ -309,13 +307,15 @@ ExecAllocTableSlot(TupleTable table)
* ExecSetSlotDescriptor * ExecSetSlotDescriptor
* *
* This function is used to set the tuple descriptor associated * 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 void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
TupleDesc tupdesc, /* new tuple descriptor */ TupleDesc tupdesc) /* new tuple descriptor */
bool shouldFree) /* is desc owned by slot? */
{ {
/* For safety, make sure slot is empty before changing it */ /* For safety, make sure slot is empty before changing it */
ExecClearTuple(slot); ExecClearTuple(slot);
...@@ -324,8 +324,8 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ ...@@ -324,8 +324,8 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* Release any old descriptor. Also release old Datum/isnull arrays if * Release any old descriptor. Also release old Datum/isnull arrays if
* present (we don't bother to check if they could be re-used). * present (we don't bother to check if they could be re-used).
*/ */
if (slot->tts_shouldFreeDesc) if (slot->tts_tupleDescriptor)
FreeTupleDesc(slot->tts_tupleDescriptor); ReleaseTupleDesc(slot->tts_tupleDescriptor);
if (slot->tts_values) if (slot->tts_values)
pfree(slot->tts_values); pfree(slot->tts_values);
...@@ -333,10 +333,10 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ ...@@ -333,10 +333,10 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
pfree(slot->tts_isnull); 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_tupleDescriptor = tupdesc;
slot->tts_shouldFreeDesc = shouldFree; PinTupleDesc(tupdesc);
/* /*
* Allocate Datum/isnull arrays of the appropriate size. These must have * Allocate Datum/isnull arrays of the appropriate size. These must have
...@@ -740,7 +740,7 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) ...@@ -740,7 +740,7 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{ {
TupleTableSlot *slot = ExecInitExtraTupleSlot(estate); TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupType, false); ExecSetSlotDescriptor(slot, tupType);
return ExecStoreAllNullTuple(slot); return ExecStoreAllNullTuple(slot);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -427,12 +427,11 @@ ExecAssignExprContext(EState *estate, PlanState *planstate)
* ---------------- * ----------------
*/ */
void void
ExecAssignResultType(PlanState *planstate, ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc)
TupleDesc tupDesc, bool shouldFree)
{ {
TupleTableSlot *slot = planstate->ps_ResultTupleSlot; TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
ExecSetSlotDescriptor(slot, tupDesc, shouldFree); ExecSetSlotDescriptor(slot, tupDesc);
} }
/* ---------------- /* ----------------
...@@ -461,7 +460,7 @@ ExecAssignResultTypeFromTL(PlanState *planstate) ...@@ -461,7 +460,7 @@ ExecAssignResultTypeFromTL(PlanState *planstate)
* to set up planstate->targetlist ... * to set up planstate->targetlist ...
*/ */
tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
ExecAssignResultType(planstate, tupDesc, true); ExecAssignResultType(planstate, tupDesc);
} }
/* ---------------- /* ----------------
...@@ -659,12 +658,11 @@ ExecGetScanType(ScanState *scanstate) ...@@ -659,12 +658,11 @@ ExecGetScanType(ScanState *scanstate)
* ---------------- * ----------------
*/ */
void void
ExecAssignScanType(ScanState *scanstate, ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc)
TupleDesc tupDesc, bool shouldFree)
{ {
TupleTableSlot *slot = scanstate->ss_ScanTupleSlot; TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
ExecSetSlotDescriptor(slot, tupDesc, shouldFree); ExecSetSlotDescriptor(slot, tupDesc);
} }
/* ---------------- /* ----------------
...@@ -680,7 +678,7 @@ ExecAssignScanTypeFromOuterPlan(ScanState *scanstate) ...@@ -680,7 +678,7 @@ ExecAssignScanTypeFromOuterPlan(ScanState *scanstate)
outerPlan = outerPlanState(scanstate); outerPlan = outerPlanState(scanstate);
tupDesc = ExecGetResultType(outerPlan); tupDesc = ExecGetResultType(outerPlan);
ExecAssignScanType(scanstate, tupDesc, false); ExecAssignScanType(scanstate, tupDesc);
} }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -538,7 +538,7 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
/* /*
* get the scan type from the relation descriptor. * 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. * Initialize result tuple type and projection info.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -220,7 +220,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
BlessTupleDesc(tupdesc); BlessTupleDesc(tupdesc);
scanstate->tupdesc = tupdesc; scanstate->tupdesc = tupdesc;
ExecAssignScanType(&scanstate->ss, tupdesc, false); ExecAssignScanType(&scanstate->ss, tupdesc);
/* /*
* Other node-specific setup * Other node-specific setup
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -436,8 +436,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
ExecAssignProjectionInfo(&hjstate->js.ps); ExecAssignProjectionInfo(&hjstate->js.ps);
ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot, ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
ExecGetResultType(outerPlanState(hjstate)), ExecGetResultType(outerPlanState(hjstate)));
false);
/* /*
* initialize hash-specific info * initialize hash-specific info
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -516,7 +516,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
/* /*
* get the scan type from the relation descriptor. * get the scan type from the relation descriptor.
*/ */
ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false); ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation));
/* /*
* Open the index relation. * Open the index relation.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1552,8 +1552,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate); mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot, ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
ExecGetResultType(innerPlanState(mergestate)), ExecGetResultType(innerPlanState(mergestate)));
false);
switch (node->join.jointype) switch (node->join.jointype)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -159,7 +159,7 @@ InitScanRelation(SeqScanState *node, EState *estate)
node->ss_currentRelation = currentRelation; node->ss_currentRelation = currentRelation;
node->ss_currentScanDesc = currentScanDesc; node->ss_currentScanDesc = currentScanDesc;
ExecAssignScanType(node, RelationGetDescr(currentRelation), false); ExecAssignScanType(node, RelationGetDescr(currentRelation));
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -865,14 +865,14 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
*/ */
tupDesc = ExecTypeFromTL(leftptlist, false); tupDesc = ExecTypeFromTL(leftptlist, false);
slot = ExecAllocTableSlot(tupTable); slot = ExecAllocTableSlot(tupTable);
ExecSetSlotDescriptor(slot, tupDesc, true); ExecSetSlotDescriptor(slot, tupDesc);
node->projLeft = ExecBuildProjectionInfo(lefttlist, node->projLeft = ExecBuildProjectionInfo(lefttlist,
NULL, NULL,
slot); slot);
tupDesc = ExecTypeFromTL(rightptlist, false); tupDesc = ExecTypeFromTL(rightptlist, false);
slot = ExecAllocTableSlot(tupTable); slot = ExecAllocTableSlot(tupTable);
ExecSetSlotDescriptor(slot, tupDesc, true); ExecSetSlotDescriptor(slot, tupDesc);
node->projRight = ExecBuildProjectionInfo(righttlist, node->projRight = ExecBuildProjectionInfo(righttlist,
node->innerecontext, node->innerecontext,
slot); slot);
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -202,11 +202,13 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
subquerystate->ss.ps.ps_TupFromTlist = false; 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, ExecAssignScanType(&subquerystate->ss,
ExecGetResultType(subquerystate->subplan), CreateTupleDescCopy(ExecGetResultType(subquerystate->subplan)));
false);
/* /*
* Initialize result tuple type and projection info. * Initialize result tuple type and projection info.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -524,7 +524,7 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
/* /*
* get the scan type from the relation descriptor. * 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. * Initialize result tuple type and projection info.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -1418,12 +1418,19 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum, ...@@ -1418,12 +1418,19 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
return true; return true;
tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1); tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1);
if (fieldnum <= 0 || fieldnum > tupdesc->natts) if (fieldnum <= 0 || fieldnum > tupdesc->natts)
{
ReleaseTupleDesc(tupdesc);
return false; return false;
}
attr = tupdesc->attrs[fieldnum - 1]; attr = tupdesc->attrs[fieldnum - 1];
if (attr->attisdropped || if (attr->attisdropped ||
attr->atttypid != expectedtype || attr->atttypid != expectedtype ||
attr->atttypmod != expectedtypmod) attr->atttypmod != expectedtypmod)
{
ReleaseTupleDesc(tupdesc);
return false; return false;
}
ReleaseTupleDesc(tupdesc);
return true; return true;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -772,6 +772,8 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
format_type_be(targetTypeId)), format_type_be(targetTypeId)),
errdetail("Input has too many columns."))); errdetail("Input has too many columns.")));
ReleaseTupleDesc(tupdesc);
rowexpr = makeNode(RowExpr); rowexpr = makeNode(RowExpr);
rowexpr->args = newargs; rowexpr->args = newargs;
rowexpr->row_typeid = targetTypeId; rowexpr->row_typeid = targetTypeId;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -850,7 +850,8 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
((Var *) expr)->vartype == RECORDOID) ((Var *) expr)->vartype == RECORDOID)
tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0); tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0);
else if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) 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); Assert(tupleDesc);
/* Generate a list of references to the individual fields */ /* Generate a list of references to the individual fields */
...@@ -1027,7 +1028,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) ...@@ -1027,7 +1028,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
* appropriate error message while failing. * appropriate error message while failing.
*/ */
if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) 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; return tupleDesc;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -264,6 +264,7 @@ record_in(PG_FUNCTION_ARGS)
pfree(buf.data); pfree(buf.data);
pfree(values); pfree(values);
pfree(nulls); pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_HEAPTUPLEHEADER(result); PG_RETURN_HEAPTUPLEHEADER(result);
} }
...@@ -411,6 +412,7 @@ record_out(PG_FUNCTION_ARGS) ...@@ -411,6 +412,7 @@ record_out(PG_FUNCTION_ARGS)
pfree(values); pfree(values);
pfree(nulls); pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_CSTRING(buf.data); PG_RETURN_CSTRING(buf.data);
} }
...@@ -605,6 +607,7 @@ record_recv(PG_FUNCTION_ARGS) ...@@ -605,6 +607,7 @@ record_recv(PG_FUNCTION_ARGS)
heap_freetuple(tuple); heap_freetuple(tuple);
pfree(values); pfree(values);
pfree(nulls); pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_HEAPTUPLEHEADER(result); PG_RETURN_HEAPTUPLEHEADER(result);
} }
...@@ -731,6 +734,7 @@ record_send(PG_FUNCTION_ARGS) ...@@ -731,6 +734,7 @@ record_send(PG_FUNCTION_ARGS)
pfree(values); pfree(values);
pfree(nulls); pfree(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees * ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text * 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" #include "postgres.h"
...@@ -2697,7 +2697,8 @@ get_name_for_var_field(Var *var, int fieldno, ...@@ -2697,7 +2697,8 @@ get_name_for_var_field(Var *var, int fieldno,
* appropriate error message while failing. * appropriate error message while failing.
*/ */
if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) 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 */ /* Got the tupdesc, so we can extract the field name */
Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
...@@ -3312,8 +3313,8 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -3312,8 +3313,8 @@ get_rule_expr(Node *node, deparse_context *context,
TupleDesc tupdesc; TupleDesc tupdesc;
if (get_expr_result_type(arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) if (get_expr_result_type(arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
tupdesc = lookup_rowtype_tupdesc(exprType(arg), tupdesc = lookup_rowtype_tupdesc_copy(exprType(arg),
exprTypmod(arg)); exprTypmod(arg));
Assert(tupdesc); Assert(tupdesc);
/* Got the tupdesc, so we can extract the field name */ /* Got the tupdesc, so we can extract the field name */
Assert(fno >= 1 && fno <= tupdesc->natts); Assert(fno >= 1 && fno <= tupdesc->natts);
...@@ -3514,6 +3515,8 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -3514,6 +3515,8 @@ get_rule_expr(Node *node, deparse_context *context,
} }
i++; i++;
} }
ReleaseTupleDesc(tupdesc);
} }
appendStringInfo(buf, ")"); appendStringInfo(buf, ")");
if (rowexpr->row_format == COERCE_EXPLICIT_CAST) if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -313,6 +313,8 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
/* and allocate attribute tuple form storage */ /* and allocate attribute tuple form storage */
relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts,
relationForm->relhasoids); relationForm->relhasoids);
/* which we mark as a reference-counted tupdesc */
relation->rd_att->tdrefcount = 1;
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
...@@ -1234,6 +1236,8 @@ formrdesc(const char *relationName, Oid relationReltype, ...@@ -1234,6 +1236,8 @@ formrdesc(const char *relationName, Oid relationReltype,
* defined by macros in src/include/catalog/ headers. * defined by macros in src/include/catalog/ headers.
*/ */
relation->rd_att = CreateTemplateTupleDesc(natts, hasoids); relation->rd_att = CreateTemplateTupleDesc(natts, hasoids);
relation->rd_att->tdrefcount = 1; /* mark as refcounted */
relation->rd_att->tdtypeid = relationReltype; relation->rd_att->tdtypeid = relationReltype;
relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
...@@ -1591,7 +1595,10 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1591,7 +1595,10 @@ RelationClearRelation(Relation relation, bool rebuild)
{ {
/* ok to zap remaining substructure */ /* ok to zap remaining substructure */
flush_rowtype_cache(old_reltype); 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) if (relation->rd_rulescxt)
MemoryContextDelete(relation->rd_rulescxt); MemoryContextDelete(relation->rd_rulescxt);
pfree(relation); pfree(relation);
...@@ -1601,7 +1608,10 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1601,7 +1608,10 @@ RelationClearRelation(Relation relation, bool rebuild)
/* /*
* When rebuilding an open relcache entry, must preserve ref count and * When rebuilding an open relcache entry, must preserve ref count and
* rd_createSubid state. Also attempt to preserve the tupledesc 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 * Note that this process does not touch CurrentResourceOwner; which
* is good because whatever ref counts the entry may have do not * is good because whatever ref counts the entry may have do not
...@@ -1618,7 +1628,9 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1618,7 +1628,9 @@ RelationClearRelation(Relation relation, bool rebuild)
{ {
/* Should only get here if relation was deleted */ /* Should only get here if relation was deleted */
flush_rowtype_cache(old_reltype); flush_rowtype_cache(old_reltype);
FreeTupleDesc(old_att); Assert(old_att->tdrefcount > 0);
if (--old_att->tdrefcount == 0)
FreeTupleDesc(old_att);
if (old_rulescxt) if (old_rulescxt)
MemoryContextDelete(old_rulescxt); MemoryContextDelete(old_rulescxt);
pfree(relation); pfree(relation);
...@@ -1629,13 +1641,17 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1629,13 +1641,17 @@ RelationClearRelation(Relation relation, bool rebuild)
if (equalTupleDescs(old_att, relation->rd_att)) if (equalTupleDescs(old_att, relation->rd_att))
{ {
/* needn't flush typcache here */ /* 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; relation->rd_att = old_att;
} }
else else
{ {
flush_rowtype_cache(old_reltype); 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)) if (equalRuleLocks(old_rules, relation->rd_rules))
{ {
...@@ -2075,6 +2091,7 @@ RelationBuildLocalRelation(const char *relname, ...@@ -2075,6 +2091,7 @@ RelationBuildLocalRelation(const char *relname,
* catalogs. We can copy attnotnull constraints here, however. * catalogs. We can copy attnotnull constraints here, however.
*/ */
rel->rd_att = CreateTupleDescCopy(tupDesc); rel->rd_att = CreateTupleDescCopy(tupDesc);
rel->rd_att->tdrefcount = 1; /* mark as refcounted */
has_not_null = false; has_not_null = false;
for (i = 0; i < natts; i++) for (i = 0; i < natts; i++)
{ {
...@@ -2996,6 +3013,8 @@ load_relcache_init_file(void) ...@@ -2996,6 +3013,8 @@ load_relcache_init_file(void)
/* initialize attribute tuple forms */ /* initialize attribute tuple forms */
rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
relform->relhasoids); relform->relhasoids);
rel->rd_att->tdrefcount = 1; /* mark as refcounted */
rel->rd_att->tdtypeid = relform->reltype; rel->rd_att->tdtypeid = relform->reltype;
rel->rd_att->tdtypmod = -1; /* unnecessary, but... */ rel->rd_att->tdtypmod = -1; /* unnecessary, but... */
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -270,12 +270,16 @@ lookup_type_cache(Oid type_id, int flags)
Assert(rel->rd_rel->reltype == typentry->type_id); Assert(rel->rd_rel->reltype == typentry->type_id);
/* /*
* Notice that we simply store a link to the relcache's tupdesc. Since * Link to the tupdesc and increment its refcount (we assert it's
* we are relying on relcache to detect cache flush events, there's * a refcounted descriptor). We don't use IncrTupleDescRefCount()
* not a lot of point to maintaining an independent copy. * for this, because the reference mustn't be entered in the current
* resource owner; it can outlive the current query.
*/ */
typentry->tupDesc = RelationGetDescr(rel); typentry->tupDesc = RelationGetDescr(rel);
Assert(typentry->tupDesc->tdrefcount > 0);
typentry->tupDesc->tdrefcount++;
relation_close(rel, AccessShareLock); relation_close(rel, AccessShareLock);
} }
...@@ -283,29 +287,13 @@ lookup_type_cache(Oid type_id, int flags) ...@@ -283,29 +287,13 @@ lookup_type_cache(Oid type_id, int flags)
} }
/* /*
* lookup_rowtype_tupdesc * lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
*
* 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
* *
* As above, but if the type is not a known composite type and noError * Same API as lookup_rowtype_tupdesc_noerror, but the returned tupdesc
* is true, returns NULL instead of ereport'ing. (Note that if a bogus * hasn't had its refcount bumped.
* type_id is passed, you'll get an ereport anyway.)
*/ */
TupleDesc static TupleDesc
lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError) lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
{ {
if (type_id != RECORDOID) if (type_id != RECORDOID)
{ {
...@@ -339,6 +327,59 @@ lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError) ...@@ -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 * assign_record_type_typmod
...@@ -425,6 +466,8 @@ assign_record_type_typmod(TupleDesc tupDesc) ...@@ -425,6 +466,8 @@ assign_record_type_typmod(TupleDesc tupDesc)
/* if fail in subrs, no damage except possibly some wasted memory... */ /* if fail in subrs, no damage except possibly some wasted memory... */
entDesc = CreateTupleDescCopy(tupDesc); entDesc = CreateTupleDescCopy(tupDesc);
recentry->tupdescs = lcons(entDesc, recentry->tupdescs); recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
/* mark it as a reference-counted tupdesc */
entDesc->tdrefcount = 1;
/* now it's safe to advance NextRecordTypmod */ /* now it's safe to advance NextRecordTypmod */
newtypmod = NextRecordTypmod++; newtypmod = NextRecordTypmod++;
entDesc->tdtypmod = newtypmod; entDesc->tdtypmod = newtypmod;
...@@ -456,6 +499,17 @@ flush_rowtype_cache(Oid type_id) ...@@ -456,6 +499,17 @@ flush_rowtype_cache(Oid type_id)
HASH_FIND, NULL); HASH_FIND, NULL);
if (typentry == NULL) if (typentry == NULL)
return; /* no matching entry */ 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; typentry->tupDesc = NULL;
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Copyright (c) 2002-2006, PostgreSQL Global Development Group * Copyright (c) 2002-2006, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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) ...@@ -185,8 +185,7 @@ shutdown_MultiFuncCall(Datum arg)
* receives the actual datatype OID (this is mainly useful for scalar * receives the actual datatype OID (this is mainly useful for scalar
* result types). If resultTupleDesc isn't NULL, *resultTupleDesc * result types). If resultTupleDesc isn't NULL, *resultTupleDesc
* receives a pointer to a TupleDesc when the result is of a composite * 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 * type, or NULL when it's a scalar result.
* be copied if it is to be accessed over a long period.
* *
* One hard case that this handles is resolution of actual rowtypes for * One hard case that this handles is resolution of actual rowtypes for
* functions returning RECORD (from either the function's OUT parameter * functions returning RECORD (from either the function's OUT parameter
...@@ -246,7 +245,7 @@ get_expr_result_type(Node *expr, ...@@ -246,7 +245,7 @@ get_expr_result_type(Node *expr,
*resultTupleDesc = NULL; *resultTupleDesc = NULL;
result = get_type_func_class(typid); result = get_type_func_class(typid);
if (result == TYPEFUNC_COMPOSITE && resultTupleDesc) if (result == TYPEFUNC_COMPOSITE && resultTupleDesc)
*resultTupleDesc = lookup_rowtype_tupdesc(typid, -1); *resultTupleDesc = lookup_rowtype_tupdesc_copy(typid, -1);
} }
return result; return result;
...@@ -363,7 +362,7 @@ internal_get_result_type(Oid funcid, ...@@ -363,7 +362,7 @@ internal_get_result_type(Oid funcid,
{ {
case TYPEFUNC_COMPOSITE: case TYPEFUNC_COMPOSITE:
if (resultTupleDesc) if (resultTupleDesc)
*resultTupleDesc = lookup_rowtype_tupdesc(rettype, -1); *resultTupleDesc = lookup_rowtype_tupdesc_copy(rettype, -1);
/* Named composite types can't have any polymorphic columns */ /* Named composite types can't have any polymorphic columns */
break; break;
case TYPEFUNC_SCALAR: case TYPEFUNC_SCALAR:
...@@ -1053,7 +1052,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) ...@@ -1053,7 +1052,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
if (functypclass == TYPEFUNC_COMPOSITE) if (functypclass == TYPEFUNC_COMPOSITE)
{ {
/* Composite data type, e.g. a table's row type */ /* 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) if (colaliases != NIL)
{ {
......
$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 Notes about resource owners
--------------------------- ---------------------------
...@@ -61,9 +61,9 @@ ResourceOwner transfers lock ownership to the parent instead of actually ...@@ -61,9 +61,9 @@ ResourceOwner transfers lock ownership to the parent instead of actually
releasing the lock, if isCommit is true. releasing the lock, if isCommit is true.
Currently, ResourceOwners contain direct support for recording ownership Currently, ResourceOwners contain direct support for recording ownership
of buffer pins, lmgr locks, and catcache and relcache references. Other of buffer pins, lmgr locks, and catcache, relcache, and tupdesc references.
objects can be associated with a ResourceOwner by recording the address of Other objects can be associated with a ResourceOwner by recording the address
the owning ResourceOwner in such an object. There is an API for other 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 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. their own data structures to find the objects that need to be deleted.
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * 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 ...@@ -57,6 +57,11 @@ typedef struct ResourceOwnerData
int nrelrefs; /* number of owned relcache pins */ int nrelrefs; /* number of owned relcache pins */
Relation *relrefs; /* dynamically allocated array */ Relation *relrefs; /* dynamically allocated array */
int maxrelrefs; /* currently allocated array size */ 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; } ResourceOwnerData;
...@@ -87,6 +92,7 @@ static void ResourceOwnerReleaseInternal(ResourceOwner owner, ...@@ -87,6 +92,7 @@ static void ResourceOwnerReleaseInternal(ResourceOwner owner,
bool isCommit, bool isCommit,
bool isTopLevel); bool isTopLevel);
static void PrintRelCacheLeakWarning(Relation rel); static void PrintRelCacheLeakWarning(Relation rel);
static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
/***************************************************************************** /*****************************************************************************
...@@ -258,7 +264,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, ...@@ -258,7 +264,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* /*
* Release catcache references. Note that ReleaseCatCache will remove * Release catcache references. Note that ReleaseCatCache will remove
* the catref entry from my list, so I just have to iterate till there * 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 * As with buffer pins, warn if any are left at commit time, and
* release back-to-front for speed. * release back-to-front for speed.
...@@ -269,12 +275,20 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, ...@@ -269,12 +275,20 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]); PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]); ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
} }
/* Ditto for catcache lists */
while (owner->ncatlistrefs > 0) while (owner->ncatlistrefs > 0)
{ {
if (isCommit) if (isCommit)
PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]); PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
ReleaseCatCacheList(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 */ /* Clean up index scans too */
ReleaseResources_hash(); ReleaseResources_hash();
...@@ -304,6 +318,7 @@ ResourceOwnerDelete(ResourceOwner owner) ...@@ -304,6 +318,7 @@ ResourceOwnerDelete(ResourceOwner owner)
Assert(owner->ncatrefs == 0); Assert(owner->ncatrefs == 0);
Assert(owner->ncatlistrefs == 0); Assert(owner->ncatlistrefs == 0);
Assert(owner->nrelrefs == 0); Assert(owner->nrelrefs == 0);
Assert(owner->ntupdescs == 0);
/* /*
* Delete children. The recursive call will delink the child from me, so * Delete children. The recursive call will delink the child from me, so
...@@ -328,6 +343,8 @@ ResourceOwnerDelete(ResourceOwner owner) ...@@ -328,6 +343,8 @@ ResourceOwnerDelete(ResourceOwner owner)
pfree(owner->catlistrefs); pfree(owner->catlistrefs);
if (owner->relrefs) if (owner->relrefs)
pfree(owner->relrefs); pfree(owner->relrefs);
if (owner->tupdescs)
pfree(owner->tupdescs);
pfree(owner); pfree(owner);
} }
...@@ -742,3 +759,85 @@ PrintRelCacheLeakWarning(Relation rel) ...@@ -742,3 +759,85 @@ PrintRelCacheLeakWarning(Relation rel)
elog(WARNING, "relcache reference leak: relation \"%s\" not closed", elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
RelationGetRelationName(rel)); 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);
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -57,6 +57,14 @@ typedef struct tupleConstr
* tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous * 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 * row type, or a value >= 0 to allow the rowtype to be looked up in the
* typcache.c type cache. * 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 typedef struct tupleDesc
{ {
...@@ -67,6 +75,7 @@ typedef struct tupleDesc ...@@ -67,6 +75,7 @@ typedef struct tupleDesc
Oid tdtypeid; /* composite type ID for tuple type */ Oid tdtypeid; /* composite type ID for tuple type */
int32 tdtypmod; /* typmod for tuple type */ int32 tdtypmod; /* typmod for tuple type */
bool tdhasoid; /* tuple has oid attribute in its header */ bool tdhasoid; /* tuple has oid attribute in its header */
int tdrefcount; /* reference count, or -1 if not counting */
} *TupleDesc; } *TupleDesc;
...@@ -81,6 +90,21 @@ extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc); ...@@ -81,6 +90,21 @@ extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
extern void FreeTupleDesc(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 bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
extern void TupleDescInitEntry(TupleDesc desc, extern void TupleDescInitEntry(TupleDesc desc,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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); ...@@ -249,8 +249,7 @@ extern ExprContext *MakePerTupleExprContext(EState *estate);
} while (0) } while (0)
extern void ExecAssignExprContext(EState *estate, PlanState *planstate); extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
extern void ExecAssignResultType(PlanState *planstate, extern void ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc);
TupleDesc tupDesc, bool shouldFree);
extern void ExecAssignResultTypeFromTL(PlanState *planstate); extern void ExecAssignResultTypeFromTL(PlanState *planstate);
extern TupleDesc ExecGetResultType(PlanState *planstate); extern TupleDesc ExecGetResultType(PlanState *planstate);
extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
...@@ -259,8 +258,7 @@ extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ...@@ -259,8 +258,7 @@ extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
extern void ExecAssignProjectionInfo(PlanState *planstate); extern void ExecAssignProjectionInfo(PlanState *planstate);
extern void ExecFreeExprContext(PlanState *planstate); extern void ExecFreeExprContext(PlanState *planstate);
extern TupleDesc ExecGetScanType(ScanState *scanstate); extern TupleDesc ExecGetScanType(ScanState *scanstate);
extern void ExecAssignScanType(ScanState *scanstate, extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc);
TupleDesc tupDesc, bool shouldFree);
extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate); extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate);
extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid); extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -15,6 +15,7 @@
#define TUPTABLE_H #define TUPTABLE_H
#include "access/htup.h" #include "access/htup.h"
#include "access/tupdesc.h"
/*---------- /*----------
...@@ -56,13 +57,17 @@ ...@@ -56,13 +57,17 @@
* TRUE, tts_shouldFree FALSE, tts_tuple NULL, tts_buffer InvalidBuffer, * TRUE, tts_shouldFree FALSE, tts_tuple NULL, tts_buffer InvalidBuffer,
* and tts_nvalid zero. * 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 * 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. * 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 * 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 * on the indicated buffer page; drop the pin when we release the
* slot's reference to that buffer. (tts_shouldFree should always be * slot's reference to that buffer. (tts_shouldFree should always be
...@@ -87,7 +92,6 @@ typedef struct TupleTableSlot ...@@ -87,7 +92,6 @@ typedef struct TupleTableSlot
NodeTag type; /* vestigial ... allows IsA tests */ NodeTag type; /* vestigial ... allows IsA tests */
bool tts_isempty; /* true = slot is empty */ bool tts_isempty; /* true = slot is empty */
bool tts_shouldFree; /* should pfree tuple? */ bool tts_shouldFree; /* should pfree tuple? */
bool tts_shouldFreeDesc; /* should pfree descriptor? */
bool tts_slow; /* saved state for slot_deform_tuple */ bool tts_slow; /* saved state for slot_deform_tuple */
HeapTuple tts_tuple; /* physical tuple, or NULL if none */ HeapTuple tts_tuple; /* physical tuple, or NULL if none */
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
...@@ -124,8 +128,7 @@ extern void ExecDropTupleTable(TupleTable table, bool shouldFree); ...@@ -124,8 +128,7 @@ extern void ExecDropTupleTable(TupleTable table, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc); extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot); extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecAllocTableSlot(TupleTable table); extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
TupleDesc tupdesc, bool shouldFree);
extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple, extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
TupleTableSlot *slot, TupleTableSlot *slot,
Buffer buffer, Buffer buffer,
......
...@@ -12,13 +12,14 @@ ...@@ -12,13 +12,14 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 #ifndef RESOWNER_H
#define RESOWNER_H #define RESOWNER_H
#include "access/tupdesc.h"
#include "storage/buf.h" #include "storage/buf.h"
#include "utils/catcache.h" #include "utils/catcache.h"
#include "utils/rel.h" #include "utils/rel.h"
...@@ -107,4 +108,11 @@ extern void ResourceOwnerRememberRelationRef(ResourceOwner owner, ...@@ -107,4 +108,11 @@ extern void ResourceOwnerRememberRelationRef(ResourceOwner owner,
extern void ResourceOwnerForgetRelationRef(ResourceOwner owner, extern void ResourceOwnerForgetRelationRef(ResourceOwner owner,
Relation rel); 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 */ #endif /* RESOWNER_H */
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -58,7 +58,7 @@ typedef struct TypeCacheEntry
/* /*
* Tuple descriptor if it's a composite type (row type). NULL if not * Tuple descriptor if it's a composite type (row type). NULL if not
* composite or information hasn't yet been requested. (NOTE: this is * 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; TupleDesc tupDesc;
} TypeCacheEntry; } TypeCacheEntry;
...@@ -79,6 +79,8 @@ extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod); ...@@ -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, extern TupleDesc lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod,
bool noError); bool noError);
extern TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod);
extern void assign_record_type_typmod(TupleDesc tupDesc); extern void assign_record_type_typmod(TupleDesc tupDesc);
extern void flush_rowtype_cache(Oid type_id); extern void flush_rowtype_cache(Oid type_id);
......
/********************************************************************** /**********************************************************************
* plperl.c - perl as a procedural language for PostgreSQL * 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) ...@@ -904,6 +904,7 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
hashref = plperl_hash_from_tuple(&tmptup, tupdesc); hashref = plperl_hash_from_tuple(&tmptup, tupdesc);
XPUSHs(sv_2mortal(hashref)); XPUSHs(sv_2mortal(hashref));
ReleaseTupleDesc(tupdesc);
} }
else else
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -233,6 +233,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
tmptup.t_tableOid = InvalidOid; tmptup.t_tableOid = InvalidOid;
tmptup.t_data = td; tmptup.t_data = td;
exec_move_row(&estate, NULL, row, &tmptup, tupdesc); exec_move_row(&estate, NULL, row, &tmptup, tupdesc);
ReleaseTupleDesc(tupdesc);
} }
else else
{ {
...@@ -1701,10 +1702,11 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt) ...@@ -1701,10 +1702,11 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
/* /*
* Run the query * Run the query
*
* Retrieving two rows can be slower than a single row, e.g. * Retrieving two rows can be slower than a single row, e.g.
* a sequential scan where the scan has to be completed to * a sequential scan where the scan has to be completed to
* check for a second row. For this reason, we only do the * check for a second row. For this reason, we only retrieve
* second-line check for STRICT. * the second row if checking STRICT.
*/ */
exec_run_select(estate, stmt->query, stmt->strict ? 2 : 1, NULL); exec_run_select(estate, stmt->query, stmt->strict ? 2 : 1, NULL);
tuptab = estate->eval_tuptable; tuptab = estate->eval_tuptable;
...@@ -1717,22 +1719,21 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt) ...@@ -1717,22 +1719,21 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
*/ */
if (n == 0) if (n == 0)
{ {
if (!stmt->strict) if (stmt->strict)
{
/* null the target */
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
exec_eval_cleanup(estate);
return PLPGSQL_RC_OK;
}
else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NO_DATA), (errcode(ERRCODE_NO_DATA),
errmsg("query returned no rows"))); 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, ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION), (errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("query more than one row"))); errmsg("query returned more than one row")));
/* /*
* Put the first result into the target and set found to true * Put the first result into the target and set found to true
...@@ -3138,6 +3139,7 @@ exec_assign_value(PLpgSQL_execstate *estate, ...@@ -3138,6 +3139,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
tmptup.t_tableOid = InvalidOid; tmptup.t_tableOid = InvalidOid;
tmptup.t_data = td; tmptup.t_data = td;
exec_move_row(estate, NULL, row, &tmptup, tupdesc); exec_move_row(estate, NULL, row, &tmptup, tupdesc);
ReleaseTupleDesc(tupdesc);
} }
break; break;
} }
...@@ -3180,6 +3182,7 @@ exec_assign_value(PLpgSQL_execstate *estate, ...@@ -3180,6 +3182,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
tmptup.t_tableOid = InvalidOid; tmptup.t_tableOid = InvalidOid;
tmptup.t_data = td; tmptup.t_data = td;
exec_move_row(estate, rec, NULL, &tmptup, tupdesc); exec_move_row(estate, rec, NULL, &tmptup, tupdesc);
ReleaseTupleDesc(tupdesc);
} }
break; break;
} }
......
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * 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) ...@@ -883,6 +883,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
tmptup.t_data = td; tmptup.t_data = td;
arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc); arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
ReleaseTupleDesc(tupdesc);
} }
} }
else else
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* pltcl.c - PostgreSQL support for Tcl as * pltcl.c - PostgreSQL support for Tcl as
* procedural language (PL) * 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) ...@@ -511,6 +511,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp); pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringAppendElement(&tcl_cmd,
Tcl_DStringValue(&list_tmp)); Tcl_DStringValue(&list_tmp));
ReleaseTupleDesc(tupdesc);
} }
} }
else else
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment