aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execTuples.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execTuples.c')
-rw-r--r--src/backend/executor/execTuples.c438
1 files changed, 342 insertions, 96 deletions
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 8574efc95d8..22af86b27f5 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.84 2005/03/14 04:41:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.85 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -30,12 +30,13 @@
* ExecAllocTableSlot - find an available slot in the table
*
* SLOT ACCESSORS
- * ExecStoreTuple - store a tuple in the table
- * ExecClearTuple - clear contents of a table slot
* ExecSetSlotDescriptor - set a slot's tuple descriptor
- *
- * SLOT STATUS PREDICATES
- * TupIsNull - true when slot contains no tuple (macro)
+ * ExecStoreTuple - store a physical tuple in the slot
+ * ExecClearTuple - clear contents of a slot
+ * ExecStoreVirtualTuple - mark slot as containing a virtual tuple
+ * ExecCopySlotTuple - build a physical tuple from a slot
+ * ExecMaterializeSlot - convert virtual to physical storage
+ * ExecCopySlot - copy one slot's contents to another
*
* CONVENIENCE INITIALIZATION ROUTINES
* ExecInitResultTupleSlot \ convenience routines to initialize
@@ -81,10 +82,8 @@
* to the slots containing tuples are passed instead of the tuples
* themselves. This facilitates the communication of related information
* (such as whether or not a tuple should be pfreed, what buffer contains
- * this tuple, the tuple's tuple descriptor, etc). Note that much of
- * this information is also kept in the ExprContext of each node.
- * Soon the executor will be redesigned and ExprContext's will contain
- * only slot pointers. -cim 3/14/91
+ * this tuple, the tuple's tuple descriptor, etc). It also allows us
+ * to avoid physically constructing projection tuples in many cases.
*/
#include "postgres.h"
@@ -142,14 +141,16 @@ ExecCreateTupleTable(int tableSize)
TupleTableSlot *slot = &(newtable->array[i]);
slot->type = T_TupleTableSlot;
- slot->val = NULL;
- slot->ttc_tupleDescriptor = NULL;
- slot->ttc_shouldFree = false;
- slot->ttc_shouldFreeDesc = false;
- slot->ttc_buffer = InvalidBuffer;
- slot->ttc_mcxt = CurrentMemoryContext;
- slot->cache_values = NULL;
- slot->cache_natts = 0; /* mark slot_getattr state invalid */
+ slot->tts_isempty = true;
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeDesc = 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;
}
return newtable;
@@ -189,10 +190,12 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
TupleTableSlot *slot = &(table->array[i]);
ExecClearTuple(slot);
- if (slot->ttc_shouldFreeDesc)
- FreeTupleDesc(slot->ttc_tupleDescriptor);
- if (slot->cache_values)
- pfree(slot->cache_values);
+ if (slot->tts_shouldFreeDesc)
+ FreeTupleDesc(slot->tts_tupleDescriptor);
+ if (slot->tts_values)
+ pfree(slot->tts_values);
+ if (slot->tts_isnull)
+ pfree(slot->tts_isnull);
}
}
@@ -203,32 +206,61 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
}
/* --------------------------------
- * MakeTupleTableSlot
+ * MakeSingleTupleTableSlot
*
- * This routine makes an empty standalone TupleTableSlot.
- * It really shouldn't exist, but there are a few places
- * that do this, so we may as well centralize the knowledge
- * of what's in one ...
+ * This is a convenience routine for operations that need a
+ * standalone TupleTableSlot not gotten from the main executor
+ * tuple table. It makes a single slot and initializes it as
+ * though by ExecSetSlotDescriptor(slot, tupdesc, false).
* --------------------------------
*/
TupleTableSlot *
-MakeTupleTableSlot(void)
+MakeSingleTupleTableSlot(TupleDesc tupdesc)
{
TupleTableSlot *slot = makeNode(TupleTableSlot);
/* This should match ExecCreateTupleTable() */
- slot->val = NULL;
- slot->ttc_tupleDescriptor = NULL;
- slot->ttc_shouldFree = false;
- slot->ttc_shouldFreeDesc = false;
- slot->ttc_buffer = InvalidBuffer;
- slot->ttc_mcxt = CurrentMemoryContext;
- slot->cache_values = NULL;
- slot->cache_natts = 0; /* mark slot_getattr state invalid */
+ slot->tts_isempty = true;
+ slot->tts_shouldFree = false;
+ slot->tts_shouldFreeDesc = 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;
+
+ ExecSetSlotDescriptor(slot, tupdesc, false);
return slot;
}
+/* --------------------------------
+ * ExecDropSingleTupleTableSlot
+ *
+ * Release a TupleTableSlot made with MakeSingleTupleTableSlot.
+ * --------------------------------
+ */
+void
+ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+
+ ExecClearTuple(slot);
+ if (slot->tts_shouldFreeDesc)
+ FreeTupleDesc(slot->tts_tupleDescriptor);
+ if (slot->tts_values)
+ pfree(slot->tts_values);
+ if (slot->tts_isnull)
+ pfree(slot->tts_isnull);
+
+ pfree(slot);
+}
+
/* ----------------------------------------------------------------
* tuple table slot reservation functions
@@ -275,9 +307,53 @@ ExecAllocTableSlot(TupleTable table)
*/
/* --------------------------------
+ * ExecSetSlotDescriptor
+ *
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple.
+ * --------------------------------
+ */
+void
+ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
+ TupleDesc tupdesc, /* new tuple descriptor */
+ bool shouldFree) /* is desc owned by slot? */
+{
+ /* For safety, make sure slot is empty before changing it */
+ ExecClearTuple(slot);
+
+ /*
+ * Release any old descriptor. Also release old Datum/isnull arrays
+ * if present (we don't bother to check if they could be re-used).
+ */
+ if (slot->tts_shouldFreeDesc)
+ FreeTupleDesc(slot->tts_tupleDescriptor);
+
+ if (slot->tts_values)
+ pfree(slot->tts_values);
+ if (slot->tts_isnull)
+ pfree(slot->tts_isnull);
+
+ /*
+ * Set up the new descriptor
+ */
+ slot->tts_tupleDescriptor = tupdesc;
+ slot->tts_shouldFreeDesc = shouldFree;
+
+ /*
+ * Allocate Datum/isnull arrays of the appropriate size. These must
+ * have the same lifetime as the slot, so allocate in the slot's own
+ * context.
+ */
+ slot->tts_values = (Datum *)
+ MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum));
+ slot->tts_isnull = (bool *)
+ MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool));
+}
+
+/* --------------------------------
* ExecStoreTuple
*
- * This function is used to store a tuple into a specified
+ * This function is used to store a physical tuple into a specified
* slot in the tuple table.
*
* tuple: tuple to store
@@ -304,6 +380,12 @@ ExecAllocTableSlot(TupleTable table)
* slot assume ownership of the copy!
*
* Return value is just the passed-in slot pointer.
+ *
+ * NOTE: before PostgreSQL 8.1, this function would accept a NULL tuple
+ * pointer and effectively behave like ExecClearTuple (though you could
+ * still specify a buffer to pin, which would be an odd combination).
+ * This saved a couple lines of code in a few places, but seemed more likely
+ * to mask logic errors than to be really useful, so it's now disallowed.
* --------------------------------
*/
TupleTableSlot *
@@ -315,29 +397,36 @@ ExecStoreTuple(HeapTuple tuple,
/*
* sanity checks
*/
+ Assert(tuple != NULL);
Assert(slot != NULL);
+ Assert(slot->tts_tupleDescriptor != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
/*
* clear out any old contents of the slot
*/
- ExecClearTuple(slot);
+ if (!slot->tts_isempty)
+ ExecClearTuple(slot);
/*
* store the new tuple into the specified slot.
*/
- slot->val = tuple;
- slot->ttc_shouldFree = shouldFree;
+ slot->tts_isempty = false;
+ slot->tts_shouldFree = shouldFree;
+ slot->tts_tuple = tuple;
/*
* If tuple is on a disk page, keep the page pinned as long as we hold
* a pointer into it. We assume the caller already has such a pin.
*/
- slot->ttc_buffer = buffer;
+ slot->tts_buffer = buffer;
if (BufferIsValid(buffer))
IncrBufferRefCount(buffer);
+ /* Mark extracted state invalid */
+ slot->tts_nvalid = 0;
+
return slot;
}
@@ -358,63 +447,231 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
Assert(slot != NULL);
/*
- * Free the old contents of the specified slot if necessary. (Note:
- * we allow slot->val to be null even when shouldFree is true, because
- * there are a few callers of ExecStoreTuple that are too lazy to
- * distinguish whether they are passing a NULL tuple, and always pass
- * shouldFree = true.)
+ * Free the old physical tuple if necessary.
*/
- if (slot->ttc_shouldFree && slot->val != NULL)
- heap_freetuple(slot->val);
+ if (slot->tts_shouldFree)
+ heap_freetuple(slot->tts_tuple);
- slot->val = NULL;
- slot->ttc_shouldFree = false;
+ slot->tts_tuple = NULL;
+ slot->tts_shouldFree = false;
/*
* Drop the pin on the referenced buffer, if there is one.
*/
- if (BufferIsValid(slot->ttc_buffer))
- ReleaseBuffer(slot->ttc_buffer);
+ if (BufferIsValid(slot->tts_buffer))
+ ReleaseBuffer(slot->tts_buffer);
- slot->ttc_buffer = InvalidBuffer;
+ slot->tts_buffer = InvalidBuffer;
/*
- * mark slot_getattr state invalid
+ * Mark it empty.
*/
- slot->cache_natts = 0;
+ slot->tts_isempty = true;
+ slot->tts_nvalid = 0;
return slot;
}
/* --------------------------------
- * ExecSetSlotDescriptor
+ * ExecStoreVirtualTuple
+ * Mark a slot as containing a virtual tuple.
*
- * This function is used to set the tuple descriptor associated
- * with the slot's tuple.
+ * The protocol for loading a slot with virtual tuple data is:
+ * * Call ExecClearTuple to mark the slot empty.
+ * * Store data into the Datum/isnull arrays.
+ * * Call ExecStoreVirtualTuple to mark the slot valid.
+ * This is a bit unclean but it avoids one round of data copying.
* --------------------------------
*/
-void
-ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
- TupleDesc tupdesc, /* new tuple descriptor */
- bool shouldFree) /* is desc owned by slot? */
+TupleTableSlot *
+ExecStoreVirtualTuple(TupleTableSlot *slot)
{
- if (slot->ttc_shouldFreeDesc)
- FreeTupleDesc(slot->ttc_tupleDescriptor);
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_isempty);
- slot->ttc_tupleDescriptor = tupdesc;
- slot->ttc_shouldFreeDesc = shouldFree;
+ slot->tts_isempty = false;
+ slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
+ return slot;
+}
+
+/* --------------------------------
+ * ExecStoreAllNullTuple
+ * Set up the slot to contain a null in every column.
+ *
+ * At first glance this might sound just like ExecClearTuple, but it's
+ * entirely different: the slot ends up full, not empty.
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecStoreAllNullTuple(TupleTableSlot *slot)
+{
/*
- * mark slot_getattr state invalid
+ * sanity checks
*/
- slot->cache_natts = 0;
+ Assert(slot != NULL);
+ Assert(slot->tts_tupleDescriptor != NULL);
+
+ /* Clear any old contents */
+ ExecClearTuple(slot);
/*
- * release any old cache array since tupledesc's natts may have changed
+ * Fill all the columns of the virtual tuple with nulls
*/
- if (slot->cache_values)
- pfree(slot->cache_values);
- slot->cache_values = NULL;
+ MemSet(slot->tts_values, 0,
+ slot->tts_tupleDescriptor->natts * sizeof(Datum));
+ memset(slot->tts_isnull, true,
+ slot->tts_tupleDescriptor->natts * sizeof(bool));
+
+ return ExecStoreVirtualTuple(slot);
+}
+
+/* --------------------------------
+ * ExecCopySlotTuple
+ * Obtain a copy of a slot's physical tuple. The copy is
+ * palloc'd in the current memory context.
+ *
+ * This works even if the slot contains a virtual tuple;
+ * however the "system columns" of the result will not be meaningful.
+ * --------------------------------
+ */
+HeapTuple
+ExecCopySlotTuple(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ /*
+ * If we have a physical tuple then just copy it.
+ */
+ if (slot->tts_tuple)
+ return heap_copytuple(slot->tts_tuple);
+
+ /*
+ * Otherwise we need to build a tuple from the Datum array.
+ */
+ return heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+}
+
+/* --------------------------------
+ * ExecFetchSlotTuple
+ * Fetch the slot's physical tuple.
+ *
+ * If the slot contains a virtual tuple, we convert it to physical
+ * form. The slot retains ownership of the physical tuple.
+ *
+ * The difference between this and ExecMaterializeSlot() is that this
+ * does not guarantee that the contained tuple is local storage.
+ * Hence, the result must be treated as read-only.
+ * --------------------------------
+ */
+HeapTuple
+ExecFetchSlotTuple(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ /*
+ * If we have a physical tuple then just return it.
+ */
+ if (slot->tts_tuple)
+ return slot->tts_tuple;
+
+ /*
+ * Otherwise materialize the slot...
+ */
+ return ExecMaterializeSlot(slot);
+}
+
+/* --------------------------------
+ * ExecMaterializeSlot
+ * Force a slot into the "materialized" state.
+ *
+ * This causes the slot's tuple to be a local copy not dependent on
+ * any external storage. A pointer to the contained tuple is returned.
+ *
+ * A typical use for this operation is to prepare a computed tuple
+ * for being stored on disk. The original data may or may not be
+ * virtual, but in any case we need a private copy for heap_insert
+ * to scribble on.
+ * --------------------------------
+ */
+HeapTuple
+ExecMaterializeSlot(TupleTableSlot *slot)
+{
+ HeapTuple newTuple;
+ MemoryContext oldContext;
+
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ /*
+ * If we have a physical tuple, and it's locally palloc'd, we have
+ * nothing to do.
+ */
+ if (slot->tts_tuple && slot->tts_shouldFree)
+ return slot->tts_tuple;
+
+ /*
+ * Otherwise, copy or build a tuple, and then store it as the new slot
+ * value. (Note: tts_nvalid will be reset to zero here. There are
+ * cases in which this could be optimized but it's probably not worth
+ * worrying about.)
+ *
+ * We may be called in a context that is shorter-lived than the
+ * tuple slot, but we have to ensure that the materialized tuple
+ * will survive anyway.
+ */
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ newTuple = ExecCopySlotTuple(slot);
+ MemoryContextSwitchTo(oldContext);
+
+ ExecStoreTuple(newTuple, slot, InvalidBuffer, true);
+
+ return slot->tts_tuple;
+}
+
+/* --------------------------------
+ * ExecCopySlot
+ * Copy the source slot's contents into the destination slot.
+ *
+ * The destination acquires a private copy that will not go away
+ * if the source is cleared.
+ *
+ * The caller must ensure the slots have compatible tupdescs.
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ HeapTuple newTuple;
+ MemoryContext oldContext;
+
+ /*
+ * There might be ways to optimize this when the source is virtual,
+ * but for now just always build a physical copy. Make sure it is
+ * in the right context.
+ */
+ oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
+ newTuple = ExecCopySlotTuple(srcslot);
+ MemoryContextSwitchTo(oldContext);
+
+ return ExecStoreTuple(newTuple, dstslot, InvalidBuffer, true);
}
@@ -474,25 +731,10 @@ TupleTableSlot *
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{
TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
- struct tupleDesc nullTupleDesc;
- HeapTuple nullTuple;
- Datum values[1];
- char nulls[1];
-
- /*
- * Since heap_getattr() will treat attributes beyond a tuple's t_natts
- * as being NULL, we can make an all-nulls tuple just by making it be
- * of zero length. However, the slot descriptor must match the real
- * tupType.
- */
- nullTupleDesc = *tupType;
- nullTupleDesc.natts = 0;
-
- nullTuple = heap_formtuple(&nullTupleDesc, values, nulls);
ExecSetSlotDescriptor(slot, tupType, false);
- return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
+ return ExecStoreAllNullTuple(slot);
}
/* ----------------------------------------------------------------
@@ -623,10 +865,7 @@ TupleDescGetSlot(TupleDesc tupdesc)
BlessTupleDesc(tupdesc);
/* Make a standalone slot */
- slot = MakeTupleTableSlot();
-
- /* Bind the tuple description to the slot */
- ExecSetSlotDescriptor(slot, tupdesc, true);
+ slot = MakeSingleTupleTableSlot(tupdesc);
/* Return the slot */
return slot;
@@ -759,6 +998,7 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
+ tstate->slot = MakeSingleTupleTableSlot(tupdesc);
tstate->dest = dest;
(*tstate->dest->rStartup) (tstate->dest, (int) CMD_SELECT, tupdesc);
@@ -771,6 +1011,9 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
*
* values is a list of the external C string representations of the values
* to be projected.
+ *
+ * XXX This could be made more efficient, since in reality we probably only
+ * need a virtual tuple.
*/
void
do_tup_output(TupOutputState *tstate, char **values)
@@ -778,12 +1021,14 @@ do_tup_output(TupOutputState *tstate, char **values)
/* build a tuple from the input strings using the tupdesc */
HeapTuple tuple = BuildTupleFromCStrings(tstate->metadata, values);
+ /* put it in a slot */
+ ExecStoreTuple(tuple, tstate->slot, InvalidBuffer, true);
+
/* send the tuple to the receiver */
- (*tstate->dest->receiveTuple) (tuple,
- tstate->metadata->tupdesc,
- tstate->dest);
+ (*tstate->dest->receiveSlot) (tstate->slot, tstate->dest);
+
/* clean up */
- heap_freetuple(tuple);
+ ExecClearTuple(tstate->slot);
}
/*
@@ -816,6 +1061,7 @@ end_tup_output(TupOutputState *tstate)
{
(*tstate->dest->rShutdown) (tstate->dest);
/* note that destroying the dest is not ours to do */
+ ExecDropSingleTupleTableSlot(tstate->slot);
/* XXX worth cleaning up the attinmetadata? */
pfree(tstate);
}