diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 1fcf2afdd9..d18d66c64a 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.328 2009/09/26 22:42:01 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.329 2009/09/27 20:09:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -755,40 +755,12 @@ InitPlan(QueryDesc *queryDesc, int eflags) } /* - * Initialize the executor "tuple" table. We need slots for all the plan - * nodes, plus possibly output slots for the junkfilter(s). At this point - * we aren't sure if we need junkfilters, so just add slots for them - * unconditionally. Also, if it's not a SELECT, set up a slot for use for - * trigger output tuples. Also, one for RETURNING-list evaluation. + * Initialize the executor's tuple table. Also, if it's not a SELECT, + * set up a tuple table slot for use for trigger output tuples. */ - { - int nSlots; - - /* Slots for the main plan tree */ - nSlots = ExecCountSlotsNode(plan); - /* Add slots for subplans and initplans */ - foreach(l, plannedstmt->subplans) - { - Plan *subplan = (Plan *) lfirst(l); - - nSlots += ExecCountSlotsNode(subplan); - } - /* Add slots for junkfilter(s) */ - if (plannedstmt->resultRelations != NIL) - nSlots += list_length(plannedstmt->resultRelations); - else - nSlots += 1; - if (operation != CMD_SELECT) - nSlots++; /* for es_trig_tuple_slot */ - if (plannedstmt->returningLists) - nSlots++; /* for RETURNING projection */ - - estate->es_tupleTable = ExecCreateTupleTable(nSlots); - - if (operation != CMD_SELECT) - estate->es_trig_tuple_slot = - ExecAllocTableSlot(estate->es_tupleTable); - } + estate->es_tupleTable = NIL; + if (operation != CMD_SELECT) + estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate); /* mark EvalPlanQual not active */ estate->es_plannedstmt = plannedstmt; @@ -909,7 +881,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) j = ExecInitJunkFilter(subplan->plan->targetlist, resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, - ExecAllocTableSlot(estate->es_tupleTable)); + ExecInitExtraTupleSlot(estate)); /* * Since it must be UPDATE/DELETE, there had better be a @@ -953,7 +925,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, - ExecAllocTableSlot(estate->es_tupleTable)); + ExecInitExtraTupleSlot(estate)); estate->es_junkFilter = j; if (estate->es_result_relation_info) estate->es_result_relation_info->ri_junkFilter = j; @@ -1026,7 +998,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) false); /* Set up a slot for the output of the RETURNING projection(s) */ - slot = ExecAllocTableSlot(estate->es_tupleTable); + slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupType); /* Need an econtext too */ econtext = CreateExprContext(estate); @@ -1387,10 +1359,12 @@ ExecEndPlan(PlanState *planstate, EState *estate) } /* - * destroy the executor "tuple" table. + * destroy the executor's tuple table. Actually we only care about + * releasing buffer pins and tupdesc refcounts; there's no need to + * pfree the TupleTableSlots, since the containing memory context + * is about to go away anyway. */ - ExecDropTupleTable(estate->es_tupleTable, true); - estate->es_tupleTable = NULL; + ExecResetTupleTable(estate->es_tupleTable, false); /* * close the result relation(s) if any, but hold locks until xact commit. @@ -2712,10 +2686,9 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq) epqstate->es_evTuple = priorepq->estate->es_evTuple; /* - * Create sub-tuple-table; we needn't redo the CountSlots work though. + * Each epqstate also has its own tuple table. */ - epqstate->es_tupleTable = - ExecCreateTupleTable(estate->es_tupleTable->size); + epqstate->es_tupleTable = NIL; /* * Initialize private state information for each SubPlan. We must do this @@ -2770,8 +2743,9 @@ EvalPlanQualStop(evalPlanQual *epq) ExecEndNode(subplanstate); } - ExecDropTupleTable(epqstate->es_tupleTable, true); - epqstate->es_tupleTable = NULL; + /* throw away the per-epqstate tuple table completely */ + ExecResetTupleTable(epqstate->es_tupleTable, true); + epqstate->es_tupleTable = NIL; if (epqstate->es_evTuple[epq->rti - 1] != NULL) { diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index d92ba062d4..73a882c6a1 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1,9 +1,11 @@ /*------------------------------------------------------------------------- * * execTuples.c - * Routines dealing with the executor tuple tables. These are used to - * ensure that the executor frees copies of tuples (made by - * ExecTargetList) properly. + * Routines dealing with TupleTableSlots. These are used for resource + * management associated with tuples (eg, releasing buffer pins for + * tuples in disk buffers, or freeing the memory occupied by transient + * tuples). Slots also provide access abstraction that lets us implement + * "virtual" tuples to reduce data-copying overhead. * * Routines dealing with the type information for tuples. Currently, * the type information for a tuple is an array of FormData_pg_attribute. @@ -15,21 +17,19 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.109 2009/07/23 21:27:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.110 2009/09/27 20:09:57 tgl Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * - * TABLE CREATE/DELETE - * ExecCreateTupleTable - create a new tuple table - * ExecDropTupleTable - destroy a table - * MakeSingleTupleTableSlot - make a single-slot table - * ExecDropSingleTupleTableSlot - destroy same - * - * SLOT RESERVATION - * ExecAllocTableSlot - find an available slot in the table + * SLOT CREATION/DESTRUCTION + * MakeTupleTableSlot - create an empty slot + * ExecAllocTableSlot - create a slot within a tuple table + * ExecResetTupleTable - clear and optionally delete a tuple table + * MakeSingleTupleTableSlot - make a standalone slot, set its descriptor + * ExecDropSingleTupleTableSlot - destroy a standalone slot * * SLOT ACCESSORS * ExecSetSlotDescriptor - set a slot's tuple descriptor @@ -57,12 +57,9 @@ * * At ExecutorStart() * ---------------- - * - InitPlan() calls ExecCreateTupleTable() to create the tuple - * table which will hold tuples processed by the executor. - * * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and - * ExecInitResultTupleSlot() to reserve places in the tuple - * table for the tuples returned by the access methods and the + * ExecInitResultTupleSlot() to construct TupleTableSlots + * for the tuples returned by the access methods and the * tuples resulting from performing target list projections. * * During ExecutorRun() @@ -79,7 +76,7 @@ * * At ExecutorEnd() * ---------------- - * - EndPlan() calls ExecDropTupleTable() to clean up any remaining + * - EndPlan() calls ExecResetTupleTable() to clean up any remaining * tuples left over from executing the query. * * The important thing to watch in the executor code is how pointers @@ -110,120 +107,16 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList, */ /* -------------------------------- - * ExecCreateTupleTable + * MakeTupleTableSlot * - * This creates a new tuple table of the specified size. - * - * This should be used by InitPlan() to allocate the table. - * The table's address will be stored in the EState structure. - * -------------------------------- - */ -TupleTable -ExecCreateTupleTable(int tableSize) -{ - TupleTable newtable; - int i; - - /* - * sanity checks - */ - Assert(tableSize >= 1); - - /* - * allocate the table itself - */ - newtable = (TupleTable) palloc(sizeof(TupleTableData) + - (tableSize - 1) *sizeof(TupleTableSlot)); - newtable->size = tableSize; - newtable->next = 0; - - /* - * initialize all the slots to empty states - */ - for (i = 0; i < tableSize; i++) - { - TupleTableSlot *slot = &(newtable->array[i]); - - slot->type = T_TupleTableSlot; - slot->tts_isempty = true; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = false; - slot->tts_tuple = NULL; - slot->tts_tupleDescriptor = NULL; - slot->tts_mcxt = CurrentMemoryContext; - slot->tts_buffer = InvalidBuffer; - slot->tts_nvalid = 0; - slot->tts_values = NULL; - slot->tts_isnull = NULL; - slot->tts_mintuple = NULL; - } - - return newtable; -} - -/* -------------------------------- - * ExecDropTupleTable - * - * This frees the storage used by the tuple table itself - * and optionally frees the contents of the table also. - * It is expected that this routine be called by EndPlan(). - * -------------------------------- - */ -void -ExecDropTupleTable(TupleTable table, /* tuple table */ - bool shouldFree) /* true if we should free slot - * contents */ -{ - /* - * sanity checks - */ - Assert(table != NULL); - - /* - * first free all the valid pointers in the tuple array and drop refcounts - * of any referenced buffers, if that's what the caller wants. (There is - * probably no good reason for the caller ever not to want it!) - */ - if (shouldFree) - { - int next = table->next; - int i; - - for (i = 0; i < next; i++) - { - TupleTableSlot *slot = &(table->array[i]); - - ExecClearTuple(slot); - if (slot->tts_tupleDescriptor) - ReleaseTupleDesc(slot->tts_tupleDescriptor); - if (slot->tts_values) - pfree(slot->tts_values); - if (slot->tts_isnull) - pfree(slot->tts_isnull); - } - } - - /* - * finally free the tuple table itself. - */ - pfree(table); -} - -/* -------------------------------- - * MakeSingleTupleTableSlot - * - * 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). + * Basic routine to make an empty TupleTableSlot. * -------------------------------- */ TupleTableSlot * -MakeSingleTupleTableSlot(TupleDesc tupdesc) +MakeTupleTableSlot(void) { TupleTableSlot *slot = makeNode(TupleTableSlot); - /* This should match ExecCreateTupleTable() */ slot->tts_isempty = true; slot->tts_shouldFree = false; slot->tts_shouldFreeMin = false; @@ -236,6 +129,85 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc) slot->tts_isnull = NULL; slot->tts_mintuple = NULL; + return slot; +} + +/* -------------------------------- + * ExecAllocTableSlot + * + * Create a tuple table slot within a tuple table (which is just a List). + * -------------------------------- + */ +TupleTableSlot * +ExecAllocTableSlot(List **tupleTable) +{ + TupleTableSlot *slot = MakeTupleTableSlot(); + + *tupleTable = lappend(*tupleTable, slot); + + return slot; +} + +/* -------------------------------- + * ExecResetTupleTable + * + * This releases any resources (buffer pins, tupdesc refcounts) + * held by the tuple table, and optionally releases the memory + * occupied by the tuple table data structure. + * It is expected that this routine be called by EndPlan(). + * -------------------------------- + */ +void +ExecResetTupleTable(List *tupleTable, /* tuple table */ + bool shouldFree) /* true if we should free memory */ +{ + ListCell *lc; + + foreach(lc, tupleTable) + { + TupleTableSlot *slot = (TupleTableSlot *) lfirst(lc); + + /* Sanity checks */ + Assert(IsA(slot, TupleTableSlot)); + + /* Always release resources and reset the slot to empty */ + ExecClearTuple(slot); + if (slot->tts_tupleDescriptor) + { + ReleaseTupleDesc(slot->tts_tupleDescriptor); + slot->tts_tupleDescriptor = NULL; + } + + /* If shouldFree, release memory occupied by the slot itself */ + if (shouldFree) + { + if (slot->tts_values) + pfree(slot->tts_values); + if (slot->tts_isnull) + pfree(slot->tts_isnull); + pfree(slot); + } + } + + /* If shouldFree, release the list structure */ + if (shouldFree) + list_free(tupleTable); +} + +/* -------------------------------- + * MakeSingleTupleTableSlot + * + * 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 + * to use the given tuple descriptor. + * -------------------------------- + */ +TupleTableSlot * +MakeSingleTupleTableSlot(TupleDesc tupdesc) +{ + TupleTableSlot *slot = MakeTupleTableSlot(); + ExecSetSlotDescriptor(slot, tupdesc); return slot; @@ -245,16 +217,14 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc) * ExecDropSingleTupleTableSlot * * Release a TupleTableSlot made with MakeSingleTupleTableSlot. + * DON'T use this on a slot that's part of a tuple table list! * -------------------------------- */ void ExecDropSingleTupleTableSlot(TupleTableSlot *slot) { - /* - * sanity checks - */ - Assert(slot != NULL); - + /* This should match ExecResetTupleTable's processing of one slot */ + Assert(IsA(slot, TupleTableSlot)); ExecClearTuple(slot); if (slot->tts_tupleDescriptor) ReleaseTupleDesc(slot->tts_tupleDescriptor); @@ -262,50 +232,10 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot) pfree(slot->tts_values); if (slot->tts_isnull) pfree(slot->tts_isnull); - pfree(slot); } -/* ---------------------------------------------------------------- - * tuple table slot reservation functions - * ---------------------------------------------------------------- - */ - -/* -------------------------------- - * ExecAllocTableSlot - * - * This routine is used to reserve slots in the table for - * use by the various plan nodes. It is expected to be - * called by the node init routines (ex: ExecInitNestLoop) - * once per slot needed by the node. Not all nodes need - * slots (some just pass tuples around). - * -------------------------------- - */ -TupleTableSlot * -ExecAllocTableSlot(TupleTable table) -{ - int slotnum; /* new slot number */ - - /* - * sanity checks - */ - Assert(table != NULL); - - /* - * We expect that the table was made big enough to begin with. We cannot - * reallocate it on the fly since previous plan nodes have already got - * pointers to individual entries. - */ - if (table->next >= table->size) - elog(ERROR, "plan requires more slots than are available"); - - slotnum = table->next; - table->next++; - - return &(table->array[slotnum]); -} - /* ---------------------------------------------------------------- * tuple table slot accessor functions * ---------------------------------------------------------------- @@ -915,7 +845,7 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot) void ExecInitResultTupleSlot(EState *estate, PlanState *planstate) { - planstate->ps_ResultTupleSlot = ExecAllocTableSlot(estate->es_tupleTable); + planstate->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable); } /* ---------------- @@ -925,7 +855,7 @@ ExecInitResultTupleSlot(EState *estate, PlanState *planstate) void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate) { - scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(estate->es_tupleTable); + scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable); } /* ---------------- @@ -935,7 +865,7 @@ ExecInitScanTupleSlot(EState *estate, ScanState *scanstate) TupleTableSlot * ExecInitExtraTupleSlot(EState *estate) { - return ExecAllocTableSlot(estate->es_tupleTable); + return ExecAllocTableSlot(&estate->es_tupleTable); } /* ---------------- diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 43c0e54065..c20c76cbae 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.161 2009/07/29 20:56:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.162 2009/09/27 20:09:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -197,7 +197,7 @@ CreateExecutorState(void) estate->es_query_cxt = qcontext; - estate->es_tupleTable = NULL; + estate->es_tupleTable = NIL; estate->es_processed = 0; estate->es_lastoid = InvalidOid; diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 016dbc378b..753655e9a4 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.99 2009/06/11 14:48:57 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.100 2009/09/27 20:09:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -721,7 +721,6 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) int ncols, i; TupleDesc tupDesc; - TupleTable tupTable; TupleTableSlot *slot; List *oplist, *lefttlist, @@ -852,15 +851,6 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) i++; } - /* - * Create a tupletable to hold these tuples. (Note: we never bother - * to free the tupletable explicitly; that's okay because it will - * never store raw disk tuples that might have associated buffer pins. - * The only resource involved is memory, which will be cleaned up by - * freeing the query context.) - */ - tupTable = ExecCreateTupleTable(2); - /* * Construct tupdescs, slots and projection nodes for left and right * sides. The lefthand expressions will be evaluated in the parent @@ -870,7 +860,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * own innerecontext. */ tupDesc = ExecTypeFromTL(leftptlist, false); - slot = ExecAllocTableSlot(tupTable); + slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, NULL, @@ -878,7 +868,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) NULL); tupDesc = ExecTypeFromTL(rightptlist, false); - slot = ExecAllocTableSlot(tupTable); + slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, sstate->innerecontext, diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 879a310c68..ffac7e0162 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.42 2009/06/11 14:49:11 momjian Exp $ + * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.43 2009/09/27 20:09:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,7 @@ #include "storage/buf.h" /*---------- - * The executor stores tuples in a "tuple table" which is composed of + * The executor stores tuples in a "tuple table" which is a List of * independent TupleTableSlots. There are several cases we need to handle: * 1. physical tuple in a disk buffer page * 2. physical tuple constructed in palloc'ed memory @@ -112,7 +112,7 @@ */ typedef struct TupleTableSlot { - NodeTag type; /* vestigial ... allows IsA tests */ + NodeTag type; bool tts_isempty; /* true = slot is empty */ bool tts_shouldFree; /* should pfree tts_tuple? */ bool tts_shouldFreeMin; /* should pfree tts_mintuple? */ @@ -132,19 +132,6 @@ typedef struct TupleTableSlot #define TTS_HAS_PHYSICAL_TUPLE(slot) \ ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr)) -/* - * Tuple table data structure: an array of TupleTableSlots. - */ -typedef struct TupleTableData -{ - int size; /* size of the table (number of slots) */ - int next; /* next available slot number */ - TupleTableSlot array[1]; /* VARIABLE LENGTH ARRAY - must be last */ -} TupleTableData; /* VARIABLE LENGTH STRUCT */ - -typedef TupleTableData *TupleTable; - - /* * TupIsNull -- is a TupleTableSlot empty? */ @@ -152,11 +139,11 @@ typedef TupleTableData *TupleTable; ((slot) == NULL || (slot)->tts_isempty) /* in executor/execTuples.c */ -extern TupleTable ExecCreateTupleTable(int tableSize); -extern void ExecDropTupleTable(TupleTable table, bool shouldFree); +extern TupleTableSlot *MakeTupleTableSlot(void); +extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable); +extern void ExecResetTupleTable(List *tupleTable, bool shouldFree); extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc); extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot); -extern TupleTableSlot *ExecAllocTableSlot(TupleTable table); extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc); extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 742ff32575..ea66e109c1 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.207 2009/08/23 18:26:08 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.208 2009/09/27 20:09:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -352,7 +352,7 @@ typedef struct EState /* Other working state: */ MemoryContext es_query_cxt; /* per-query context in which EState lives */ - TupleTable es_tupleTable; /* Array of TupleTableSlots */ + List *es_tupleTable; /* List of TupleTableSlots */ uint32 es_processed; /* # of tuples processed */ Oid es_lastoid; /* last oid processed (by INSERT) */