*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
/*
- * 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;
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
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;
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);
}
/*
- * 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.
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
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)
{
/*-------------------------------------------------------------------------
*
* 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.
*
*
* 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
*
* 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()
*
* 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
*/
/* --------------------------------
- * ExecCreateTupleTable
- *
- * This creates a new tuple table of the specified size.
+ * MakeTupleTableSlot
*
- * This should be used by InitPlan() to allocate the table.
- * The table's address will be stored in the EState structure.
+ * Basic routine to make an empty TupleTableSlot.
* --------------------------------
*/
-TupleTable
-ExecCreateTupleTable(int tableSize)
+TupleTableSlot *
+MakeTupleTableSlot(void)
{
- TupleTable newtable;
- int i;
+ TupleTableSlot *slot = makeNode(TupleTableSlot);
- /*
- * sanity checks
- */
- Assert(tableSize >= 1);
+ 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;
- /*
- * allocate the table itself
- */
- newtable = (TupleTable) palloc(sizeof(TupleTableData) +
- (tableSize - 1) *sizeof(TupleTableSlot));
- newtable->size = tableSize;
- newtable->next = 0;
+ return slot;
+}
- /*
- * 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;
- }
+/* --------------------------------
+ * 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 newtable;
+ return slot;
}
/* --------------------------------
- * ExecDropTupleTable
+ * ExecResetTupleTable
*
- * This frees the storage used by the tuple table itself
- * and optionally frees the contents of the table also.
+ * 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
-ExecDropTupleTable(TupleTable table, /* tuple table */
- bool shouldFree) /* true if we should free slot
- * contents */
+ExecResetTupleTable(List *tupleTable, /* tuple table */
+ bool shouldFree) /* true if we should free memory */
{
- /*
- * sanity checks
- */
- Assert(table != NULL);
+ ListCell *lc;
- /*
- * 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)
+ foreach(lc, tupleTable)
{
- int next = table->next;
- int i;
+ TupleTableSlot *slot = (TupleTableSlot *) lfirst(lc);
- for (i = 0; i < next; i++)
+ /* Sanity checks */
+ Assert(IsA(slot, TupleTableSlot));
+
+ /* Always release resources and reset the slot to empty */
+ ExecClearTuple(slot);
+ if (slot->tts_tupleDescriptor)
{
- TupleTableSlot *slot = &(table->array[i]);
+ ReleaseTupleDesc(slot->tts_tupleDescriptor);
+ slot->tts_tupleDescriptor = NULL;
+ }
- ExecClearTuple(slot);
- if (slot->tts_tupleDescriptor)
- ReleaseTupleDesc(slot->tts_tupleDescriptor);
+ /* 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);
}
}
- /*
- * finally free the tuple table itself.
- */
- pfree(table);
+ /* If shouldFree, release the list structure */
+ if (shouldFree)
+ list_free(tupleTable);
}
/* --------------------------------
*
* 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).
+ * tuple table. It makes a single slot and initializes it
+ * to use the given tuple descriptor.
* --------------------------------
*/
TupleTableSlot *
MakeSingleTupleTableSlot(TupleDesc tupdesc)
{
- TupleTableSlot *slot = makeNode(TupleTableSlot);
-
- /* This should match ExecCreateTupleTable() */
- 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;
+ TupleTableSlot *slot = MakeTupleTableSlot();
ExecSetSlotDescriptor(slot, 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);
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
* ----------------------------------------------------------------
*/
void
ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
{
- planstate->ps_ResultTupleSlot = ExecAllocTableSlot(estate->es_tupleTable);
+ planstate->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable);
}
/* ----------------
void
ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
{
- scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(estate->es_tupleTable);
+ scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable);
}
/* ----------------
TupleTableSlot *
ExecInitExtraTupleSlot(EState *estate)
{
- return ExecAllocTableSlot(estate->es_tupleTable);
+ return ExecAllocTableSlot(&estate->es_tupleTable);
}
/* ----------------
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
*/
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? */
((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?
*/
#define TupIsNull(slot) \
((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,