aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execGrouping.c117
-rw-r--r--src/backend/executor/execJunk.c155
-rw-r--r--src/backend/executor/execMain.c46
-rw-r--r--src/backend/executor/execQual.c237
-rw-r--r--src/backend/executor/execScan.c9
-rw-r--r--src/backend/executor/execTuples.c438
-rw-r--r--src/backend/executor/execUtils.c110
-rw-r--r--src/backend/executor/functions.c14
-rw-r--r--src/backend/executor/nodeAgg.c33
-rw-r--r--src/backend/executor/nodeFunctionscan.c7
-rw-r--r--src/backend/executor/nodeGroup.c48
-rw-r--r--src/backend/executor/nodeHash.c4
-rw-r--r--src/backend/executor/nodeHashjoin.c9
-rw-r--r--src/backend/executor/nodeIndexscan.c4
-rw-r--r--src/backend/executor/nodeLimit.c19
-rw-r--r--src/backend/executor/nodeMaterial.c11
-rw-r--r--src/backend/executor/nodeMergejoin.c17
-rw-r--r--src/backend/executor/nodeSeqscan.c12
-rw-r--r--src/backend/executor/nodeSetOp.c18
-rw-r--r--src/backend/executor/nodeSort.c10
-rw-r--r--src/backend/executor/nodeSubplan.c86
-rw-r--r--src/backend/executor/nodeUnique.c45
-rw-r--r--src/backend/executor/spi.c7
-rw-r--r--src/backend/executor/tstoreReceiver.c8
24 files changed, 867 insertions, 597 deletions
diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c
index 1d6364a4152..37db2bcd2f6 100644
--- a/src/backend/executor/execGrouping.c
+++ b/src/backend/executor/execGrouping.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.13 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.14 2005/03/16 21:38:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -41,8 +41,7 @@ static int TupleHashTableMatch(const void *key1, const void *key2,
* This actually implements SQL's notion of "not distinct". Two nulls
* match, a null and a not-null don't match.
*
- * tuple1, tuple2: the tuples to compare
- * tupdesc: tuple descriptor applying to both tuples
+ * slot1, slot2: the tuples to compare (must have same columns!)
* numCols: the number of attributes to be examined
* matchColIdx: array of attribute column numbers
* eqFunctions: array of fmgr lookup info for the equality functions to use
@@ -51,9 +50,8 @@ static int TupleHashTableMatch(const void *key1, const void *key2,
* NB: evalContext is reset each time!
*/
bool
-execTuplesMatch(HeapTuple tuple1,
- HeapTuple tuple2,
- TupleDesc tupdesc,
+execTuplesMatch(TupleTableSlot *slot1,
+ TupleTableSlot *slot2,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo *eqfunctions,
@@ -84,15 +82,9 @@ execTuplesMatch(HeapTuple tuple1,
bool isNull1,
isNull2;
- attr1 = heap_getattr(tuple1,
- att,
- tupdesc,
- &isNull1);
+ attr1 = slot_getattr(slot1, att, &isNull1);
- attr2 = heap_getattr(tuple2,
- att,
- tupdesc,
- &isNull2);
+ attr2 = slot_getattr(slot2, att, &isNull2);
if (isNull1 != isNull2)
{
@@ -129,9 +121,8 @@ execTuplesMatch(HeapTuple tuple1,
* Parameters are identical to execTuplesMatch.
*/
bool
-execTuplesUnequal(HeapTuple tuple1,
- HeapTuple tuple2,
- TupleDesc tupdesc,
+execTuplesUnequal(TupleTableSlot *slot1,
+ TupleTableSlot *slot2,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo *eqfunctions,
@@ -162,18 +153,12 @@ execTuplesUnequal(HeapTuple tuple1,
bool isNull1,
isNull2;
- attr1 = heap_getattr(tuple1,
- att,
- tupdesc,
- &isNull1);
+ attr1 = slot_getattr(slot1, att, &isNull1);
if (isNull1)
continue; /* can't prove anything here */
- attr2 = heap_getattr(tuple2,
- att,
- tupdesc,
- &isNull2);
+ attr2 = slot_getattr(slot2, att, &isNull2);
if (isNull2)
continue; /* can't prove anything here */
@@ -312,6 +297,8 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
hashtable->tablecxt = tablecxt;
hashtable->tempcxt = tempcxt;
hashtable->entrysize = entrysize;
+ hashtable->tableslot = NULL; /* will be made on first lookup */
+ hashtable->inputslot = NULL;
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(TupleHashEntryData);
@@ -342,13 +329,27 @@ TupleHashEntry
LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
bool *isnew)
{
- HeapTuple tuple = slot->val;
- TupleDesc tupdesc = slot->ttc_tupleDescriptor;
TupleHashEntry entry;
MemoryContext oldContext;
TupleHashTable saveCurHT;
+ TupleHashEntryData dummy;
bool found;
+ /* If first time through, clone the input slot to make table slot */
+ if (hashtable->tableslot == NULL)
+ {
+ TupleDesc tupdesc;
+
+ oldContext = MemoryContextSwitchTo(hashtable->tablecxt);
+ /*
+ * We copy the input tuple descriptor just for safety --- we assume
+ * all input tuples will have equivalent descriptors.
+ */
+ tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+ hashtable->tableslot = MakeSingleTupleTableSlot(tupdesc);
+ MemoryContextSwitchTo(oldContext);
+ }
+
/* Need to run the hash functions in short-lived context */
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
@@ -358,13 +359,14 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
* We save and restore CurTupleHashTable just in case someone manages to
* invoke this code re-entrantly.
*/
- hashtable->tupdesc = tupdesc;
+ hashtable->inputslot = slot;
saveCurHT = CurTupleHashTable;
CurTupleHashTable = hashtable;
/* Search the hash table */
+ dummy.firstTuple = NULL; /* flag to reference inputslot */
entry = (TupleHashEntry) hash_search(hashtable->hashtab,
- &tuple,
+ &dummy,
isnew ? HASH_ENTER : HASH_FIND,
&found);
@@ -392,7 +394,7 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
/* Copy the first tuple into the table context */
MemoryContextSwitchTo(hashtable->tablecxt);
- entry->firstTuple = heap_copytuple(tuple);
+ entry->firstTuple = ExecCopySlotTuple(slot);
*isnew = true;
}
@@ -408,9 +410,12 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
/*
* Compute the hash value for a tuple
*
- * The passed-in key is a pointer to a HeapTuple pointer -- this is either
- * the firstTuple field of a TupleHashEntry struct, or the key value passed
- * to hash_search. We ignore the keysize.
+ * The passed-in key is a pointer to TupleHashEntryData. In an actual
+ * hash table entry, the firstTuple field therein points to a physical
+ * tuple. LookupTupleHashEntry sets up a dummy TupleHashEntryData with
+ * a NULL firstTuple field --- that cues us to look at the inputslot instead.
+ * This convention avoids the need to materialize virtual input tuples
+ * unless they actually need to get copied into the table.
*
* CurTupleHashTable must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise.
@@ -421,14 +426,27 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
static uint32
TupleHashTableHash(const void *key, Size keysize)
{
- HeapTuple tuple = *(const HeapTuple *) key;
+ HeapTuple tuple = ((const TupleHashEntryData *) key)->firstTuple;
+ TupleTableSlot *slot;
TupleHashTable hashtable = CurTupleHashTable;
int numCols = hashtable->numCols;
AttrNumber *keyColIdx = hashtable->keyColIdx;
- TupleDesc tupdesc = hashtable->tupdesc;
uint32 hashkey = 0;
int i;
+ if (tuple == NULL)
+ {
+ /* Process the current input tuple for the table */
+ slot = hashtable->inputslot;
+ }
+ else
+ {
+ /* Process a tuple already stored in the table */
+ /* (this case never actually occurs in current dynahash.c code) */
+ slot = hashtable->tableslot;
+ ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+ }
+
for (i = 0; i < numCols; i++)
{
AttrNumber att = keyColIdx[i];
@@ -438,7 +456,7 @@ TupleHashTableHash(const void *key, Size keysize)
/* rotate hashkey left 1 bit at each step */
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
- attr = heap_getattr(tuple, att, tupdesc, &isNull);
+ attr = slot_getattr(slot, att, &isNull);
if (!isNull) /* treat nulls as having hash key 0 */
{
@@ -456,7 +474,7 @@ TupleHashTableHash(const void *key, Size keysize)
/*
* See whether two tuples (presumably of the same hash value) match
*
- * As above, the passed pointers are pointers to HeapTuple pointers.
+ * As above, the passed pointers are pointers to TupleHashEntryData.
*
* CurTupleHashTable must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise.
@@ -467,13 +485,28 @@ TupleHashTableHash(const void *key, Size keysize)
static int
TupleHashTableMatch(const void *key1, const void *key2, Size keysize)
{
- HeapTuple tuple1 = *(const HeapTuple *) key1;
- HeapTuple tuple2 = *(const HeapTuple *) key2;
+ HeapTuple tuple1 = ((const TupleHashEntryData *) key1)->firstTuple;
+#ifdef USE_ASSERT_CHECKING
+ HeapTuple tuple2 = ((const TupleHashEntryData *) key2)->firstTuple;
+#endif
+ TupleTableSlot *slot1;
+ TupleTableSlot *slot2;
TupleHashTable hashtable = CurTupleHashTable;
- if (execTuplesMatch(tuple1,
- tuple2,
- hashtable->tupdesc,
+ /*
+ * We assume that dynahash.c will only ever call us with the first
+ * argument being an actual table entry, and the second argument being
+ * LookupTupleHashEntry's dummy TupleHashEntryData. The other direction
+ * could be supported too, but is not currently used by dynahash.c.
+ */
+ Assert(tuple1 != NULL);
+ slot1 = hashtable->tableslot;
+ ExecStoreTuple(tuple1, slot1, InvalidBuffer, false);
+ Assert(tuple2 == NULL);
+ slot2 = hashtable->inputslot;
+
+ if (execTuplesMatch(slot1,
+ slot2,
hashtable->numCols,
hashtable->keyColIdx,
hashtable->eqfunctions,
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index f747976acfa..2dfd90b51fa 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.47 2005/03/14 04:41:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.48 2005/03/16 21:38:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,10 +42,10 @@
* We then execute the plan ignoring the "resjunk" attributes.
*
* Finally, when at the top level we get back a tuple, we can call
- * 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we
- * are interested in, and 'ExecRemoveJunk' to remove all the junk attributes
- * from a tuple. This new "clean" tuple is then printed, replaced, deleted
- * or inserted.
+ * ExecGetJunkAttribute to retrieve the value of the junk attributes we
+ * are interested in, and ExecFilterJunk or ExecRemoveJunk to remove all
+ * the junk attributes from a tuple. This new "clean" tuple is then printed,
+ * replaced, deleted or inserted.
*
*-------------------------------------------------------------------------
*/
@@ -76,6 +76,14 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
cleanTupType = ExecCleanTypeFromTL(targetList, hasoid);
/*
+ * Use the given slot, or make a new slot if we weren't given one.
+ */
+ if (slot)
+ ExecSetSlotDescriptor(slot, cleanTupType, false);
+ else
+ slot = MakeSingleTupleTableSlot(cleanTupType);
+
+ /*
* Now calculate the mapping between the original tuple's attributes and
* the "clean" tuple's attributes.
*
@@ -115,9 +123,6 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
- if (slot)
- ExecSetSlotDescriptor(slot, cleanTupType, false);
-
return junkfilter;
}
@@ -143,6 +148,14 @@ ExecInitJunkFilterConversion(List *targetList,
int i;
/*
+ * Use the given slot, or make a new slot if we weren't given one.
+ */
+ if (slot)
+ ExecSetSlotDescriptor(slot, cleanTupType, false);
+ else
+ slot = MakeSingleTupleTableSlot(cleanTupType);
+
+ /*
* Calculate the mapping between the original tuple's attributes and
* the "clean" tuple's attributes.
*
@@ -188,9 +201,6 @@ ExecInitJunkFilterConversion(List *targetList,
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
- if (slot)
- ExecSetSlotDescriptor(slot, cleanTupType, false);
-
return junkfilter;
}
@@ -234,115 +244,78 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
}
/*
- * ExecRemoveJunk
+ * ExecFilterJunk
*
- * Construct and return a tuple with all the junk attributes removed.
- *
- * Note: for historical reasons, this does not store the constructed
- * tuple into the junkfilter's resultSlot. The caller should do that
- * if it wants to.
+ * Construct and return a slot with all the junk attributes removed.
*/
-HeapTuple
-ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
+TupleTableSlot *
+ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
{
-#define PREALLOC_SIZE 64
- HeapTuple tuple;
- HeapTuple cleanTuple;
+ TupleTableSlot *resultSlot;
AttrNumber *cleanMap;
TupleDesc cleanTupType;
- TupleDesc tupType;
int cleanLength;
- int oldLength;
int i;
Datum *values;
- char *nulls;
+ bool *isnull;
Datum *old_values;
- char *old_nulls;
- Datum values_array[PREALLOC_SIZE];
- Datum old_values_array[PREALLOC_SIZE];
- char nulls_array[PREALLOC_SIZE];
- char old_nulls_array[PREALLOC_SIZE];
+ bool *old_isnull;
/*
- * get info from the slot and the junk filter
+ * Extract all the values of the old tuple.
*/
- tuple = slot->val;
- tupType = slot->ttc_tupleDescriptor;
- oldLength = tupType->natts + 1; /* +1 for NULL */
+ slot_getallattrs(slot);
+ old_values = slot->tts_values;
+ old_isnull = slot->tts_isnull;
+ /*
+ * get info from the junk filter
+ */
cleanTupType = junkfilter->jf_cleanTupType;
cleanLength = cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
+ resultSlot = junkfilter->jf_resultSlot;
/*
- * Create the arrays that will hold the attribute values and the null
- * information for the old tuple and new "clean" tuple.
- *
- * Note: we use memory on the stack to optimize things when we are
- * dealing with a small number of attributes. for large tuples we just
- * use palloc.
+ * Prepare to build a virtual result tuple.
*/
- if (cleanLength > PREALLOC_SIZE)
- {
- values = (Datum *) palloc(cleanLength * sizeof(Datum));
- nulls = (char *) palloc(cleanLength * sizeof(char));
- }
- else
- {
- values = values_array;
- nulls = nulls_array;
- }
- if (oldLength > PREALLOC_SIZE)
- {
- old_values = (Datum *) palloc(oldLength * sizeof(Datum));
- old_nulls = (char *) palloc(oldLength * sizeof(char));
- }
- else
- {
- old_values = old_values_array;
- old_nulls = old_nulls_array;
- }
-
- /*
- * Extract all the values of the old tuple, offsetting the arrays
- * so that old_values[0] is NULL and old_values[1] is the first
- * source attribute; this exactly matches the numbering convention
- * in cleanMap.
- */
- heap_deformtuple(tuple, tupType, old_values + 1, old_nulls + 1);
- old_values[0] = (Datum) 0;
- old_nulls[0] = 'n';
+ ExecClearTuple(resultSlot);
+ values = resultSlot->tts_values;
+ isnull = resultSlot->tts_isnull;
/*
- * Transpose into proper fields of the new tuple.
+ * Transpose data into proper fields of the new tuple.
*/
for (i = 0; i < cleanLength; i++)
{
int j = cleanMap[i];
- values[i] = old_values[j];
- nulls[i] = old_nulls[j];
+ if (j == 0)
+ {
+ values[i] = (Datum) 0;
+ isnull[i] = true;
+ }
+ else
+ {
+ values[i] = old_values[j - 1];
+ isnull[i] = old_isnull[j - 1];
+ }
}
/*
- * Now form the new tuple.
- */
- cleanTuple = heap_formtuple(cleanTupType, values, nulls);
-
- /*
- * We are done. Free any space allocated for 'values' and 'nulls' and
- * return the new tuple.
+ * And return the virtual tuple.
*/
- if (values != values_array)
- {
- pfree(values);
- pfree(nulls);
- }
- if (old_values != old_values_array)
- {
- pfree(old_values);
- pfree(old_nulls);
- }
+ return ExecStoreVirtualTuple(resultSlot);
+}
- return cleanTuple;
+/*
+ * ExecRemoveJunk
+ *
+ * Convenience routine to generate a physical clean tuple,
+ * rather than just a virtual slot.
+ */
+HeapTuple
+ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
+{
+ return ExecCopySlotTuple(ExecFilterJunk(junkfilter, slot));
}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 1e7a1b5440f..0294063d3f5 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.241 2005/01/14 17:53:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.242 2005/03/16 21:38:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1082,7 +1082,6 @@ lnext: ;
if ((junkfilter = estate->es_junkFilter) != NULL)
{
Datum datum;
- HeapTuple newTuple;
bool isNull;
/*
@@ -1180,13 +1179,7 @@ lnext: ;
* Finally create a new "clean" tuple with all junk attributes
* removed
*/
- newTuple = ExecRemoveJunk(junkfilter, slot);
-
- slot = ExecStoreTuple(newTuple, /* tuple to store */
- junkfilter->jf_resultSlot, /* dest slot */
- InvalidBuffer, /* this tuple has no
- * buffer */
- true); /* tuple should be pfreed */
+ slot = ExecFilterJunk(junkfilter, slot);
}
/*
@@ -1276,15 +1269,6 @@ ExecSelect(TupleTableSlot *slot,
DestReceiver *dest,
EState *estate)
{
- HeapTuple tuple;
- TupleDesc attrtype;
-
- /*
- * get the heap tuple out of the tuple table slot
- */
- tuple = slot->val;
- attrtype = slot->ttc_tupleDescriptor;
-
/*
* insert the tuple into the "into relation"
*
@@ -1292,15 +1276,20 @@ ExecSelect(TupleTableSlot *slot,
*/
if (estate->es_into_relation_descriptor != NULL)
{
+ HeapTuple tuple;
+
+ tuple = ExecCopySlotTuple(slot);
heap_insert(estate->es_into_relation_descriptor, tuple,
estate->es_snapshot->curcid);
+ /* we know there are no indexes to update */
+ heap_freetuple(tuple);
IncrAppended();
}
/*
* send the tuple to the destination
*/
- (*dest->receiveTuple) (tuple, attrtype, dest);
+ (*dest->receiveSlot) (slot, dest);
IncrRetrieved();
(estate->es_processed)++;
}
@@ -1325,9 +1314,10 @@ ExecInsert(TupleTableSlot *slot,
Oid newId;
/*
- * get the heap tuple out of the tuple table slot
+ * get the heap tuple out of the tuple table slot, making sure
+ * we have a writable copy
*/
- tuple = slot->val;
+ tuple = ExecMaterializeSlot(slot);
/*
* get information on the (current) result relation
@@ -1520,9 +1510,10 @@ ExecUpdate(TupleTableSlot *slot,
elog(ERROR, "cannot UPDATE during bootstrap");
/*
- * get the heap tuple out of the tuple table slot
+ * get the heap tuple out of the tuple table slot, making sure
+ * we have a writable copy
*/
- tuple = slot->val;
+ tuple = ExecMaterializeSlot(slot);
/*
* get information on the (current) result relation
@@ -1604,10 +1595,8 @@ lreplace:;
if (!TupIsNull(epqslot))
{
*tupleid = ctid;
- tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot);
- slot = ExecStoreTuple(tuple,
- estate->es_junkFilter->jf_resultSlot,
- InvalidBuffer, true);
+ slot = ExecFilterJunk(estate->es_junkFilter, epqslot);
+ tuple = ExecMaterializeSlot(slot);
goto lreplace;
}
}
@@ -1712,7 +1701,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate)
{
Relation rel = resultRelInfo->ri_RelationDesc;
- HeapTuple tuple = slot->val;
TupleConstr *constr = rel->rd_att->constr;
Assert(constr);
@@ -1725,7 +1713,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
for (attrChk = 1; attrChk <= natts; attrChk++)
{
if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
- heap_attisnull(tuple, attrChk))
+ slot_attisnull(slot, attrChk))
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("null value in column \"%s\" violates not-null constraint",
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 87cc201a3e3..3613d31a056 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.172 2005/03/14 04:41:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.173 2005/03/16 21:38:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -479,7 +479,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
*/
if (attnum > 0)
{
- TupleDesc tuple_type = slot->ttc_tupleDescriptor;
+ TupleDesc tuple_type = slot->tts_tupleDescriptor;
/*
* This assert checks that the attnum is valid.
@@ -1333,10 +1333,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
}
else
{
- char nullflag;
-
- nullflag = fcinfo.isnull ? 'n' : ' ';
- tuple = heap_formtuple(tupdesc, &result, &nullflag);
+ tuple = heap_form_tuple(tupdesc, &result, &fcinfo.isnull);
}
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
@@ -1384,14 +1381,14 @@ no_function_result:
{
int natts = expectedDesc->natts;
Datum *nulldatums;
- char *nullflags;
+ bool *nullflags;
HeapTuple tuple;
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
- nullflags = (char *) palloc(natts * sizeof(char));
- memset(nullflags, 'n', natts * sizeof(char));
- tuple = heap_formtuple(expectedDesc, nulldatums, nullflags);
+ nullflags = (bool *) palloc(natts * sizeof(bool));
+ memset(nullflags, true, natts * sizeof(bool));
+ tuple = heap_form_tuple(expectedDesc, nulldatums, nullflags);
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tuplestore_puttuple(tupstore, tuple);
}
@@ -1843,9 +1840,9 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
HeapTupleData tmptup;
AttrNumber *attrMap = cstate->attrMap;
Datum *invalues = cstate->invalues;
- char *innulls = cstate->innulls;
+ bool *inisnull = cstate->inisnull;
Datum *outvalues = cstate->outvalues;
- char *outnulls = cstate->outnulls;
+ bool *outisnull = cstate->outisnull;
int i;
int outnatts = cstate->outdesc->natts;
@@ -1861,7 +1858,7 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
/*
- * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
+ * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
tmptup.t_data = tuple;
@@ -1872,9 +1869,9 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
* source attribute; this exactly matches the numbering convention
* in attrMap.
*/
- heap_deformtuple(&tmptup, cstate->indesc, invalues + 1, innulls + 1);
+ heap_deform_tuple(&tmptup, cstate->indesc, invalues + 1, inisnull + 1);
invalues[0] = (Datum) 0;
- innulls[0] = 'n';
+ inisnull[0] = true;
/*
* Transpose into proper fields of the new tuple.
@@ -1884,13 +1881,13 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
int j = attrMap[i];
outvalues[i] = invalues[j];
- outnulls[i] = innulls[j];
+ outisnull[i] = inisnull[j];
}
/*
* Now form the new tuple.
*/
- result = heap_formtuple(cstate->outdesc, outvalues, outnulls);
+ result = heap_form_tuple(cstate->outdesc, outvalues, outisnull);
return HeapTupleGetDatum(result);
}
@@ -2187,7 +2184,7 @@ ExecEvalRow(RowExprState *rstate,
{
HeapTuple tuple;
Datum *values;
- char *nulls;
+ bool *isnull;
int natts;
ListCell *arg;
int i;
@@ -2200,27 +2197,25 @@ ExecEvalRow(RowExprState *rstate,
/* Allocate workspace */
natts = rstate->tupdesc->natts;
values = (Datum *) palloc0(natts * sizeof(Datum));
- nulls = (char *) palloc(natts * sizeof(char));
+ isnull = (bool *) palloc(natts * sizeof(bool));
/* preset to nulls in case rowtype has some later-added columns */
- memset(nulls, 'n', natts * sizeof(char));
+ memset(isnull, true, natts * sizeof(bool));
/* Evaluate field values */
i = 0;
foreach(arg, rstate->args)
{
ExprState *e = (ExprState *) lfirst(arg);
- bool eisnull;
- values[i] = ExecEvalExpr(e, econtext, &eisnull, NULL);
- nulls[i] = eisnull ? 'n' : ' ';
+ values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL);
i++;
}
- tuple = heap_formtuple(rstate->tupdesc, values, nulls);
+ tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
pfree(values);
- pfree(nulls);
+ pfree(isnull);
return HeapTupleGetDatum(tuple);
}
@@ -2628,7 +2623,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
Datum tupDatum;
TupleDesc tupDesc;
Datum *values;
- char *nulls;
+ bool *isnull;
Datum save_datum;
bool save_isNull;
ListCell *l1,
@@ -2658,12 +2653,12 @@ ExecEvalFieldStore(FieldStoreState *fstate,
/* Allocate workspace */
values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
- nulls = (char *) palloc(tupDesc->natts * sizeof(char));
+ isnull = (bool *) palloc(tupDesc->natts * sizeof(bool));
if (!*isNull)
{
/*
- * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
+ * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader.
* We set all the fields in the struct just in case.
*/
HeapTupleHeader tuphdr;
@@ -2675,12 +2670,12 @@ ExecEvalFieldStore(FieldStoreState *fstate,
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = tuphdr;
- heap_deformtuple(&tmptup, tupDesc, values, nulls);
+ heap_deform_tuple(&tmptup, tupDesc, values, isnull);
}
else
{
/* Convert null input tuple into an all-nulls row */
- memset(nulls, 'n', tupDesc->natts * sizeof(char));
+ memset(isnull, true, tupDesc->natts * sizeof(bool));
}
/* Result is never null */
@@ -2693,7 +2688,6 @@ ExecEvalFieldStore(FieldStoreState *fstate,
{
ExprState *newval = (ExprState *) lfirst(l1);
AttrNumber fieldnum = lfirst_int(l2);
- bool eisnull;
Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
@@ -2705,22 +2699,21 @@ ExecEvalFieldStore(FieldStoreState *fstate,
* the value would be needed.
*/
econtext->caseValue_datum = values[fieldnum - 1];
- econtext->caseValue_isNull = (nulls[fieldnum - 1] == 'n');
+ econtext->caseValue_isNull = isnull[fieldnum - 1];
values[fieldnum - 1] = ExecEvalExpr(newval,
econtext,
- &eisnull,
+ &isnull[fieldnum - 1],
NULL);
- nulls[fieldnum - 1] = eisnull ? 'n' : ' ';
}
econtext->caseValue_datum = save_datum;
econtext->caseValue_isNull = save_isNull;
- tuple = heap_formtuple(tupDesc, values, nulls);
+ tuple = heap_form_tuple(tupDesc, values, isnull);
pfree(values);
- pfree(nulls);
+ pfree(isnull);
return HeapTupleGetDatum(tuple);
}
@@ -3074,10 +3067,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
/* preallocate workspace for Datum arrays */
n = cstate->indesc->natts + 1; /* +1 for NULL */
cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
- cstate->innulls = (char *) palloc(n * sizeof(char));
+ cstate->inisnull = (bool *) palloc(n * sizeof(bool));
n = cstate->outdesc->natts;
cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
- cstate->outnulls = (char *) palloc(n * sizeof(char));
+ cstate->outisnull = (bool *) palloc(n * sizeof(bool));
state = (ExprState *) cstate;
}
break;
@@ -3479,56 +3472,38 @@ ExecCleanTargetListLength(List *targetlist)
return len;
}
-/* ----------------------------------------------------------------
- * ExecTargetList
- *
+/*
+ * ExecTargetList
* Evaluates a targetlist with respect to the given
- * expression context and returns a tuple.
+ * expression context. Returns TRUE if we were able to create
+ * a result, FALSE if we have exhausted a set-valued expression.
*
- * The caller must pass workspace for the values and nulls arrays
- * as well as the itemIsDone array. This convention saves palloc'ing
- * workspace on each call, and some callers may find it useful to examine
- * the values array directly.
+ * Results are stored into the passed values and isnull arrays.
+ * The caller must provide an itemIsDone array that persists across calls.
*
* As with ExecEvalExpr, the caller should pass isDone = NULL if not
* prepared to deal with sets of result tuples. Otherwise, a return
* of *isDone = ExprMultipleResult signifies a set element, and a return
* of *isDone = ExprEndResult signifies end of the set of tuple.
- * ----------------------------------------------------------------
*/
-static HeapTuple
+static bool
ExecTargetList(List *targetlist,
- TupleDesc targettype,
ExprContext *econtext,
Datum *values,
- char *nulls,
+ bool *isnull,
ExprDoneCond *itemIsDone,
ExprDoneCond *isDone)
{
MemoryContext oldContext;
ListCell *tl;
- bool isNull;
bool haveDoneSets;
/*
- * debugging stuff
- */
- EV_printf("ExecTargetList: tl is ");
- EV_nodeDisplay(targetlist);
- EV_printf("\n");
-
- /*
* Run in short-lived per-tuple context while computing expressions.
*/
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
/*
- * There used to be some klugy and demonstrably broken code here that
- * special-cased the situation where targetlist == NIL. Now we just
- * fall through and return an empty-but-valid tuple.
- */
-
- /*
* evaluate all the expressions in the target list
*/
if (isDone)
@@ -3544,9 +3519,8 @@ ExecTargetList(List *targetlist,
values[resind] = ExecEvalExpr(gstate->arg,
econtext,
- &isNull,
+ &isnull[resind],
&itemIsDone[resind]);
- nulls[resind] = isNull ? 'n' : ' ';
if (itemIsDone[resind] != ExprSingleResult)
{
@@ -3581,7 +3555,7 @@ ExecTargetList(List *targetlist,
*/
*isDone = ExprEndResult;
MemoryContextSwitchTo(oldContext);
- return NULL;
+ return false;
}
else
{
@@ -3599,9 +3573,8 @@ ExecTargetList(List *targetlist,
{
values[resind] = ExecEvalExpr(gstate->arg,
econtext,
- &isNull,
+ &isnull[resind],
&itemIsDone[resind]);
- nulls[resind] = isNull ? 'n' : ' ';
if (itemIsDone[resind] == ExprEndResult)
{
@@ -3632,75 +3605,129 @@ ExecTargetList(List *targetlist,
while (itemIsDone[resind] == ExprMultipleResult)
{
- (void) ExecEvalExpr(gstate->arg,
- econtext,
- &isNull,
- &itemIsDone[resind]);
+ values[resind] = ExecEvalExpr(gstate->arg,
+ econtext,
+ &isnull[resind],
+ &itemIsDone[resind]);
}
}
MemoryContextSwitchTo(oldContext);
- return NULL;
+ return false;
}
}
}
+ /* Report success */
+ MemoryContextSwitchTo(oldContext);
+
+ return true;
+}
+
+/*
+ * ExecVariableList
+ * Evaluates a simple-Variable-list projection.
+ *
+ * Results are stored into the passed values and isnull arrays.
+ */
+static void
+ExecVariableList(ProjectionInfo *projInfo,
+ Datum *values,
+ bool *isnull)
+{
+ ExprContext *econtext = projInfo->pi_exprContext;
+ int *varSlotOffsets = projInfo->pi_varSlotOffsets;
+ int *varNumbers = projInfo->pi_varNumbers;
+ int i;
+
/*
- * form the new result tuple (in the caller's memory context!)
+ * Force extraction of all input values that we need.
*/
- MemoryContextSwitchTo(oldContext);
+ if (projInfo->pi_lastInnerVar > 0)
+ slot_getsomeattrs(econtext->ecxt_innertuple,
+ projInfo->pi_lastInnerVar);
+ if (projInfo->pi_lastOuterVar > 0)
+ slot_getsomeattrs(econtext->ecxt_outertuple,
+ projInfo->pi_lastOuterVar);
+ if (projInfo->pi_lastScanVar > 0)
+ slot_getsomeattrs(econtext->ecxt_scantuple,
+ projInfo->pi_lastScanVar);
- return heap_formtuple(targettype, values, nulls);
+ /*
+ * Assign to result by direct extraction of fields from source
+ * slots ... a mite ugly, but fast ...
+ */
+ for (i = list_length(projInfo->pi_targetlist) - 1; i >= 0; i--)
+ {
+ char *slotptr = ((char *) econtext) + varSlotOffsets[i];
+ TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
+ int varNumber = varNumbers[i] - 1;
+
+ values[i] = varSlot->tts_values[varNumber];
+ isnull[i] = varSlot->tts_isnull[varNumber];
+ }
}
-/* ----------------------------------------------------------------
- * ExecProject
+/*
+ * ExecProject
*
* projects a tuple based on projection info and stores
- * it in the specified tuple table slot.
- *
- * Note: someday soon the executor can be extended to eliminate
- * redundant projections by storing pointers to datums
- * in the tuple table and then passing these around when
- * possible. this should make things much quicker.
- * -cim 6/3/91
- * ----------------------------------------------------------------
+ * it in the previously specified tuple table slot.
+ *
+ * Note: the result is always a virtual tuple; therefore it
+ * may reference the contents of the exprContext's scan tuples
+ * and/or temporary results constructed in the exprContext.
+ * If the caller wishes the result to be valid longer than that
+ * data will be valid, he must call ExecMaterializeSlot on the
+ * result slot.
*/
TupleTableSlot *
ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
{
TupleTableSlot *slot;
- TupleDesc tupType;
- HeapTuple newTuple;
/*
* sanity checks
*/
- if (projInfo == NULL)
- return NULL;
+ Assert(projInfo != NULL);
/*
* get the projection info we want
*/
slot = projInfo->pi_slot;
- tupType = slot->ttc_tupleDescriptor;
/*
- * form a new result tuple (if possible --- result can be NULL)
+ * Clear any former contents of the result slot. This makes it
+ * safe for us to use the slot's Datum/isnull arrays as workspace.
+ * (Also, we can return the slot as-is if we decide no rows can
+ * be projected.)
*/
- newTuple = ExecTargetList(projInfo->pi_targetlist,
- tupType,
- projInfo->pi_exprContext,
- projInfo->pi_tupValues,
- projInfo->pi_tupNulls,
- projInfo->pi_itemIsDone,
- isDone);
+ ExecClearTuple(slot);
/*
- * store the tuple in the projection slot and return the slot.
+ * form a new result tuple (if possible); if successful, mark the result
+ * slot as containing a valid virtual tuple
*/
- return ExecStoreTuple(newTuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* tuple has no buffer */
- true);
+ if (projInfo->pi_isVarList)
+ {
+ /* simple Var list: this always succeeds with one result row */
+ if (isDone)
+ *isDone = ExprSingleResult;
+ ExecVariableList(projInfo,
+ slot->tts_values,
+ slot->tts_isnull);
+ ExecStoreVirtualTuple(slot);
+ }
+ else
+ {
+ if (ExecTargetList(projInfo->pi_targetlist,
+ projInfo->pi_exprContext,
+ slot->tts_values,
+ slot->tts_isnull,
+ projInfo->pi_itemIsDone,
+ isDone))
+ ExecStoreVirtualTuple(slot);
+ }
+
+ return slot;
}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index f4d8eba354d..1e80fa7be06 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.34 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.35 2005/03/16 21:38:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -106,10 +106,7 @@ ExecScan(ScanState *node,
if (TupIsNull(slot))
{
if (projInfo)
- return ExecStoreTuple(NULL,
- projInfo->pi_slot,
- InvalidBuffer,
- true);
+ return ExecClearTuple(projInfo->pi_slot);
else
return slot;
}
@@ -183,7 +180,7 @@ ExecAssignScanProjectionInfo(ScanState *node)
if (tlist_matches_tupdesc(&node->ps,
scan->plan.targetlist,
scan->scanrelid,
- node->ss_ScanTupleSlot->ttc_tupleDescriptor))
+ node->ss_ScanTupleSlot->tts_tupleDescriptor))
node->ps.ps_ProjInfo = NULL;
else
ExecAssignProjectionInfo(&node->ps);
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);
}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 1718739fd6c..74567b04417 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.117 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.118 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -485,7 +485,7 @@ ExecGetResultType(PlanState *planstate)
{
TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
- return slot->ttc_tupleDescriptor;
+ return slot->tts_tupleDescriptor;
}
/* ----------------
@@ -504,17 +504,99 @@ ExecBuildProjectionInfo(List *targetList,
{
ProjectionInfo *projInfo = makeNode(ProjectionInfo);
int len;
+ bool isVarList;
+ ListCell *tl;
len = ExecTargetListLength(targetList);
projInfo->pi_targetlist = targetList;
projInfo->pi_exprContext = econtext;
projInfo->pi_slot = slot;
- if (len > 0)
+
+ /*
+ * Determine whether the target list consists entirely of simple Var
+ * references (ie, references to non-system attributes). If so,
+ * we can use the simpler ExecVariableList instead of ExecTargetList.
+ */
+ isVarList = true;
+ foreach(tl, targetList)
+ {
+ GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+ Var *variable = (Var *) gstate->arg->expr;
+
+ if (variable == NULL ||
+ !IsA(variable, Var) ||
+ variable->varattno <= 0)
+ {
+ isVarList = false;
+ break;
+ }
+ }
+ projInfo->pi_isVarList = isVarList;
+
+ if (isVarList)
+ {
+ int *varSlotOffsets;
+ int *varNumbers;
+ AttrNumber lastInnerVar = 0;
+ AttrNumber lastOuterVar = 0;
+ AttrNumber lastScanVar = 0;
+
+ projInfo->pi_itemIsDone = NULL; /* not needed */
+ projInfo->pi_varSlotOffsets = varSlotOffsets = (int *)
+ palloc0(len * sizeof(int));
+ projInfo->pi_varNumbers = varNumbers = (int *)
+ palloc0(len * sizeof(int));
+
+ /*
+ * Set up the data needed by ExecVariableList. The slots in which
+ * the variables can be found at runtime are denoted by the offsets
+ * of their slot pointers within the econtext. This rather grotty
+ * representation is needed because the caller may not have given
+ * us the real econtext yet (see hacks in nodeSubplan.c).
+ */
+ foreach(tl, targetList)
+ {
+ GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+ Var *variable = (Var *) gstate->arg->expr;
+ AttrNumber attnum = variable->varattno;
+ TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+ AttrNumber resind = tle->resdom->resno - 1;
+
+ Assert(resind >= 0 && resind < len);
+ varNumbers[resind] = attnum;
+
+ switch (variable->varno)
+ {
+ case INNER:
+ varSlotOffsets[resind] = offsetof(ExprContext,
+ ecxt_innertuple);
+ lastInnerVar = Max(lastInnerVar, attnum);
+ break;
+
+ case OUTER:
+ varSlotOffsets[resind] = offsetof(ExprContext,
+ ecxt_outertuple);
+ lastOuterVar = Max(lastOuterVar, attnum);
+ break;
+
+ default:
+ varSlotOffsets[resind] = offsetof(ExprContext,
+ ecxt_scantuple);
+ lastScanVar = Max(lastScanVar, attnum);
+ break;
+ }
+ }
+ projInfo->pi_lastInnerVar = lastInnerVar;
+ projInfo->pi_lastOuterVar = lastOuterVar;
+ projInfo->pi_lastScanVar = lastScanVar;
+ }
+ else
{
- projInfo->pi_tupValues = (Datum *) palloc(len * sizeof(Datum));
- projInfo->pi_tupNulls = (char *) palloc(len * sizeof(char));
- projInfo->pi_itemIsDone = (ExprDoneCond *) palloc(len * sizeof(ExprDoneCond));
+ projInfo->pi_itemIsDone = (ExprDoneCond *)
+ palloc(len * sizeof(ExprDoneCond));
+ projInfo->pi_varSlotOffsets = NULL;
+ projInfo->pi_varNumbers = NULL;
}
return projInfo;
@@ -582,7 +664,7 @@ ExecGetScanType(ScanState *scanstate)
{
TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
- return slot->ttc_tupleDescriptor;
+ return slot->tts_tupleDescriptor;
}
/* ----------------
@@ -772,20 +854,16 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
EState *estate,
bool is_vacuum)
{
- HeapTuple heapTuple;
ResultRelInfo *resultRelInfo;
int i;
int numIndices;
RelationPtr relationDescs;
Relation heapRelation;
- TupleDesc heapDescriptor;
IndexInfo **indexInfoArray;
ExprContext *econtext;
Datum datum[INDEX_MAX_KEYS];
char nullv[INDEX_MAX_KEYS];
- heapTuple = slot->val;
-
/*
* Get information from the result relation info structure.
*/
@@ -794,7 +872,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
relationDescs = resultRelInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
heapRelation = resultRelInfo->ri_RelationDesc;
- heapDescriptor = RelationGetDescr(heapRelation);
/*
* We will use the EState's per-tuple context for evaluating
@@ -844,12 +921,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
/*
* FormIndexDatum fills in its datum and null parameters with
- * attribute information taken from the given heap tuple. It also
+ * attribute information taken from the given tuple. It also
* computes any expressions needed.
*/
FormIndexDatum(indexInfo,
- heapTuple,
- heapDescriptor,
+ slot,
estate,
datum,
nullv);
@@ -860,9 +936,9 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
* need to move dead tuples that have the same keys as live ones.
*/
result = index_insert(relationDescs[i], /* index relation */
- datum, /* array of heaptuple Datums */
+ datum, /* array of index Datums */
nullv, /* info on nulls */
- &(heapTuple->t_self), /* tid of heap tuple */
+ tupleid, /* tid of heap tuple */
heapRelation,
relationDescs[i]->rd_index->indisunique && !is_vacuum);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 9ce1c985038..df54b56ff57 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.91 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.92 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -456,8 +456,6 @@ postquel_execute(execution_state *es,
SQLFunctionCachePtr fcache)
{
TupleTableSlot *slot;
- HeapTuple tup;
- TupleDesc tupDesc;
Datum value;
if (es->status == F_EXEC_START)
@@ -512,7 +510,7 @@ postquel_execute(execution_state *es,
/*
* Compress out the HeapTuple header data. We assume that
- * heap_formtuple made the tuple with header and body in one
+ * heap_form_tuple made the tuple with header and body in one
* palloc'd chunk. We want to return a pointer to the chunk
* start so that it will work if someone tries to free it.
*/
@@ -534,7 +532,8 @@ postquel_execute(execution_state *es,
else
{
/* function is declared to return RECORD */
- tupDesc = fcache->junkFilter->jf_cleanTupType;
+ TupleDesc tupDesc = fcache->junkFilter->jf_cleanTupType;
+
if (tupDesc->tdtypeid == RECORDOID &&
tupDesc->tdtypmod < 0)
assign_record_type_typmod(tupDesc);
@@ -556,10 +555,7 @@ postquel_execute(execution_state *es,
* column of the SELECT result, and then copy into current
* execution context if needed.
*/
- tup = slot->val;
- tupDesc = slot->ttc_tupleDescriptor;
-
- value = heap_getattr(tup, 1, tupDesc, &(fcinfo->isnull));
+ value = slot_getattr(slot, 1, &(fcinfo->isnull));
if (!fcinfo->isnull)
value = datumCopy(value, fcache->typbyval, fcache->typlen);
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 8f641cc843b..1e211803df1 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -61,7 +61,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.129 2005/03/12 20:25:06 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.130 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -748,7 +748,7 @@ agg_retrieve_direct(AggState *aggstate)
* Make a copy of the first input tuple; we will use this
* for comparisons (in group mode) and for projection.
*/
- aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
+ aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
}
else
{
@@ -813,9 +813,8 @@ agg_retrieve_direct(AggState *aggstate)
*/
if (node->aggstrategy == AGG_SORTED)
{
- if (!execTuplesMatch(firstSlot->val,
- outerslot->val,
- firstSlot->ttc_tupleDescriptor,
+ if (!execTuplesMatch(firstSlot,
+ outerslot,
node->numCols, node->grpColIdx,
aggstate->eqfunctions,
tmpcontext->ecxt_per_tuple_memory))
@@ -823,7 +822,7 @@ agg_retrieve_direct(AggState *aggstate)
/*
* Save the first input tuple of the next group.
*/
- aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
+ aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
break;
}
}
@@ -863,31 +862,11 @@ agg_retrieve_direct(AggState *aggstate)
*/
if (TupIsNull(firstSlot))
{
- TupleDesc tupType;
-
/* Should only happen in non-grouped mode */
Assert(node->aggstrategy == AGG_PLAIN);
Assert(aggstate->agg_done);
- tupType = firstSlot->ttc_tupleDescriptor;
- /* watch out for zero-column input tuples, though... */
- if (tupType && tupType->natts > 0)
- {
- HeapTuple nullsTuple;
- Datum *dvalues;
- char *dnulls;
-
- dvalues = (Datum *) palloc0(sizeof(Datum) * tupType->natts);
- dnulls = (char *) palloc(sizeof(char) * tupType->natts);
- MemSet(dnulls, 'n', sizeof(char) * tupType->natts);
- nullsTuple = heap_formtuple(tupType, dvalues, dnulls);
- ExecStoreTuple(nullsTuple,
- firstSlot,
- InvalidBuffer,
- true);
- pfree(dvalues);
- pfree(dnulls);
- }
+ ExecStoreAllNullTuple(firstSlot);
}
/*
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index be1fbb05155..edf5c676352 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.30 2005/01/27 06:36:42 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.31 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -99,7 +99,10 @@ FunctionNext(FunctionScanState *node)
ScanDirectionIsForward(direction),
&should_free);
slot = node->ss.ss_ScanTupleSlot;
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
+ if (heapTuple)
+ return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
+ else
+ return ExecClearTuple(slot);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index f8bd18f3499..e16a228fa15 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -15,7 +15,7 @@
* locate group boundaries.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.60 2005/03/10 23:21:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.61 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,11 +36,9 @@ TupleTableSlot *
ExecGroup(GroupState *node)
{
ExprContext *econtext;
- TupleDesc tupdesc;
int numCols;
AttrNumber *grpColIdx;
- HeapTuple outerTuple;
- HeapTuple firsttuple;
+ TupleTableSlot *firsttupleslot;
TupleTableSlot *outerslot;
/*
@@ -49,11 +47,15 @@ ExecGroup(GroupState *node)
if (node->grp_done)
return NULL;
econtext = node->ss.ps.ps_ExprContext;
- tupdesc = ExecGetScanType(&node->ss);
numCols = ((Group *) node->ss.ps.plan)->numCols;
grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
/*
+ * The ScanTupleSlot holds the (copied) first tuple of each group.
+ */
+ firsttupleslot = node->ss.ss_ScanTupleSlot;
+
+ /*
* We need not call ResetExprContext here because execTuplesMatch will
* reset the per-tuple memory context once per input tuple.
*/
@@ -62,8 +64,7 @@ ExecGroup(GroupState *node)
* If first time through, acquire first input tuple and determine
* whether to return it or not.
*/
- firsttuple = node->grp_firstTuple;
- if (firsttuple == NULL)
+ if (TupIsNull(firsttupleslot))
{
outerslot = ExecProcNode(outerPlanState(node));
if (TupIsNull(outerslot))
@@ -72,13 +73,9 @@ ExecGroup(GroupState *node)
node->grp_done = TRUE;
return NULL;
}
- node->grp_firstTuple = firsttuple = heap_copytuple(outerslot->val);
- /* Set up tuple as input for qual test and projection */
- ExecStoreTuple(firsttuple,
- node->ss.ss_ScanTupleSlot,
- InvalidBuffer,
- false);
- econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
+ /* Copy tuple, set up as input for qual test and projection */
+ ExecCopySlot(firsttupleslot, outerslot);
+ econtext->ecxt_scantuple = firsttupleslot;
/*
* Check the qual (HAVING clause); if the group does not match,
* ignore it and fall into scan loop.
@@ -112,14 +109,12 @@ ExecGroup(GroupState *node)
node->grp_done = TRUE;
return NULL;
}
- outerTuple = outerslot->val;
/*
* Compare with first tuple and see if this tuple is of the same
* group. If so, ignore it and keep scanning.
*/
- if (!execTuplesMatch(firsttuple, outerTuple,
- tupdesc,
+ if (!execTuplesMatch(firsttupleslot, outerslot,
numCols, grpColIdx,
node->eqfunctions,
econtext->ecxt_per_tuple_memory))
@@ -129,14 +124,9 @@ ExecGroup(GroupState *node)
* We have the first tuple of the next input group. See if we
* want to return it.
*/
- heap_freetuple(firsttuple);
- node->grp_firstTuple = firsttuple = heap_copytuple(outerTuple);
- /* Set up tuple as input for qual test and projection */
- ExecStoreTuple(firsttuple,
- node->ss.ss_ScanTupleSlot,
- InvalidBuffer,
- false);
- econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
+ /* Copy tuple, set up as input for qual test and projection */
+ ExecCopySlot(firsttupleslot, outerslot);
+ econtext->ecxt_scantuple = firsttupleslot;
/*
* Check the qual (HAVING clause); if the group does not match,
* ignore it and loop back to scan the rest of the group.
@@ -173,7 +163,6 @@ ExecInitGroup(Group *node, EState *estate)
grpstate = makeNode(GroupState);
grpstate->ss.ps.plan = (Plan *) node;
grpstate->ss.ps.state = estate;
- grpstate->grp_firstTuple = NULL;
grpstate->grp_done = FALSE;
/*
@@ -255,11 +244,8 @@ void
ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
{
node->grp_done = FALSE;
- if (node->grp_firstTuple != NULL)
- {
- heap_freetuple(node->grp_firstTuple);
- node->grp_firstTuple = NULL;
- }
+ /* must clear first tuple */
+ ExecClearTuple(node->ss.ss_ScanTupleSlot);
if (((PlanState *) node)->lefttree &&
((PlanState *) node)->lefttree->chgParam == NULL)
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index ded952a43e5..daf24c3d5ab 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.90 2005/03/13 19:59:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.91 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -75,7 +75,7 @@ ExecHash(HashState *node)
/* We have to compute the hash value */
econtext->ecxt_innertuple = slot;
hashvalue = ExecHashGetHashValue(hashtable, econtext, hashkeys);
- ExecHashTableInsert(hashtable, slot->val, hashvalue);
+ ExecHashTableInsert(hashtable, ExecFetchSlotTuple(slot), hashvalue);
}
/*
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 4f4eb701d3c..26d7bde3353 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.68 2005/03/06 22:15:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.69 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -188,7 +188,8 @@ ExecHashJoin(HashJoinState *node)
* Save it in the corresponding outer-batch file.
*/
Assert(batchno > hashtable->curbatch);
- ExecHashJoinSaveTuple(outerTupleSlot->val, hashvalue,
+ ExecHashJoinSaveTuple(ExecFetchSlotTuple(outerTupleSlot),
+ hashvalue,
&hashtable->outerBatchFile[batchno]);
node->hj_NeedNewOuter = true;
continue; /* loop around for a new outer tuple */
@@ -652,7 +653,9 @@ start_over:
* NOTE: some tuples may be sent to future batches. Also,
* it is possible for hashtable->nbatch to be increased here!
*/
- ExecHashTableInsert(hashtable, slot->val, hashvalue);
+ ExecHashTableInsert(hashtable,
+ ExecFetchSlotTuple(slot),
+ hashvalue);
}
/*
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index a7c91093adb..6546f479708 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.99 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.100 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -165,7 +165,7 @@ IndexNext(IndexScanState *node)
break;
}
if (qual == NULL) /* would not be returned by indices */
- slot->val = NULL;
+ ExecClearTuple(slot);
/* Flag for the next call that no more tuples */
estate->es_evTupleNull[scanrelid - 1] = true;
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index b4942d62907..40e0283e86f 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.20 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.21 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,7 +38,6 @@ TupleTableSlot * /* return: a tuple or NULL */
ExecLimit(LimitState *node)
{
ScanDirection direction;
- TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
PlanState *outerPlan;
@@ -47,7 +46,6 @@ ExecLimit(LimitState *node)
*/
direction = node->ps.state->es_direction;
outerPlan = outerPlanState(node);
- resultTupleSlot = node->ps.ps_ResultTupleSlot;
/*
* The main logic is a simple state machine.
@@ -219,12 +217,7 @@ ExecLimit(LimitState *node)
/* Return the current tuple */
Assert(!TupIsNull(slot));
- ExecStoreTuple(slot->val,
- resultTupleSlot,
- InvalidBuffer,
- false); /* tuple does not belong to slot */
-
- return resultTupleSlot;
+ return slot;
}
/*
@@ -324,7 +317,7 @@ ExecInitLimit(Limit *node, EState *estate)
#define LIMIT_NSLOTS 1
/*
- * Tuple table initialization
+ * Tuple table initialization (XXX not actually used...)
*/
ExecInitResultTupleSlot(estate, &limitstate->ps);
@@ -363,10 +356,6 @@ void
ExecEndLimit(LimitState *node)
{
ExecFreeExprContext(&node->ps);
-
- /* clean up tuple table */
- ExecClearTuple(node->ps.ps_ResultTupleSlot);
-
ExecEndNode(outerPlanState(node));
}
@@ -377,8 +366,6 @@ ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
/* resetting lstate will force offset/limit recalculation */
node->lstate = LIMIT_INITIAL;
- ExecClearTuple(node->ps.ps_ResultTupleSlot);
-
/*
* if chgParam of subnode is not null then plan will be re-scanned by
* first ExecProcNode.
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 08b58494639..fe128595576 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.48 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.49 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -127,7 +127,7 @@ ExecMaterial(MaterialState *node)
node->eof_underlying = true;
return NULL;
}
- heapTuple = outerslot->val;
+ heapTuple = ExecFetchSlotTuple(outerslot);
should_free = false;
/*
@@ -139,10 +139,13 @@ ExecMaterial(MaterialState *node)
}
/*
- * Return the obtained tuple.
+ * Return the obtained tuple, if any.
*/
slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot;
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
+ if (heapTuple)
+ return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
+ else
+ return ExecClearTuple(slot);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index ade0d9c69ed..bb93e2367d0 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.69 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.70 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -80,10 +80,8 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
#define MarkInnerTuple(innerTupleSlot, mergestate) \
( \
- ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \
- (mergestate)->mj_MarkedTupleSlot, \
- InvalidBuffer, \
- true) \
+ ExecCopySlot((mergestate)->mj_MarkedTupleSlot, \
+ (innerTupleSlot)) \
)
@@ -246,8 +244,7 @@ ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
if (TupIsNull(outerSlot))
printf("(nil)\n");
else
- MJ_debugtup(outerSlot->val,
- outerSlot->ttc_tupleDescriptor);
+ MJ_debugtup(outerSlot);
}
static void
@@ -259,8 +256,7 @@ ExecMergeTupleDumpInner(MergeJoinState *mergestate)
if (TupIsNull(innerSlot))
printf("(nil)\n");
else
- MJ_debugtup(innerSlot->val,
- innerSlot->ttc_tupleDescriptor);
+ MJ_debugtup(innerSlot);
}
static void
@@ -272,8 +268,7 @@ ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
if (TupIsNull(markedSlot))
printf("(nil)\n");
else
- MJ_debugtup(markedSlot->val,
- markedSlot->ttc_tupleDescriptor);
+ MJ_debugtup(markedSlot);
}
static void
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 621911652a9..f5a284a26bf 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeSeqscan.c,v 1.51 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeSeqscan.c,v 1.52 2005/03/16 21:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -110,12 +110,12 @@ SeqNext(SeqScanState *node)
* refcount of the buffer; the refcount will not be dropped until the
* tuple table slot is cleared.
*/
-
- slot = ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with
+ if (tuple)
+ ExecStoreTuple(tuple, /* tuple to store */
+ slot, /* slot to store in */
+ scandesc->rs_cbuf, /* buffer associated with
* this tuple */
- false); /* don't pfree this pointer */
+ false); /* don't pfree this pointer */
return slot;
}
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 2ecd2fda9f8..a0de4db74d4 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -21,7 +21,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.15 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.16 2005/03/16 21:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,14 +49,12 @@ ExecSetOp(SetOpState *node)
SetOp *plannode = (SetOp *) node->ps.plan;
TupleTableSlot *resultTupleSlot;
PlanState *outerPlan;
- TupleDesc tupDesc;
/*
* get information from the node
*/
outerPlan = outerPlanState(node);
resultTupleSlot = node->ps.ps_ResultTupleSlot;
- tupDesc = ExecGetResultType(&node->ps);
/*
* If the previously-returned tuple needs to be returned more than
@@ -105,11 +103,7 @@ ExecSetOp(SetOpState *node)
*/
if (node->subplan_done)
return NULL; /* no more tuples */
- ExecStoreTuple(heap_copytuple(inputTupleSlot->val),
- resultTupleSlot,
- InvalidBuffer,
- true); /* free copied tuple at
- * ExecClearTuple */
+ ExecCopySlot(resultTupleSlot, inputTupleSlot);
node->numLeft = 0;
node->numRight = 0;
endOfGroup = false;
@@ -127,9 +121,8 @@ ExecSetOp(SetOpState *node)
* Else test if the new tuple and the previously saved tuple
* match.
*/
- if (execTuplesMatch(inputTupleSlot->val,
- resultTupleSlot->val,
- tupDesc,
+ if (execTuplesMatch(inputTupleSlot,
+ resultTupleSlot,
plannode->numCols, plannode->dupColIdx,
node->eqfunctions,
node->tempContext))
@@ -189,9 +182,8 @@ ExecSetOp(SetOpState *node)
int flag;
bool isNull;
- flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
+ flag = DatumGetInt32(slot_getattr(inputTupleSlot,
plannode->flagColIdx,
- tupDesc,
&isNull));
Assert(!isNull);
if (flag)
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 2823ba7fcd3..ef025374149 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeSort.c,v 1.49 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeSort.c,v 1.50 2005/03/16 21:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -104,7 +104,8 @@ ExecSort(SortState *node)
if (TupIsNull(slot))
break;
- tuplesort_puttuple(tuplesortstate, (void *) slot->val);
+ tuplesort_puttuple(tuplesortstate,
+ (void *) ExecFetchSlotTuple(slot));
}
/*
@@ -136,7 +137,10 @@ ExecSort(SortState *node)
&should_free);
slot = node->ss.ps.ps_ResultTupleSlot;
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
+ if (heapTuple)
+ return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
+ else
+ return ExecClearTuple(slot);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 3dcd03db072..7d40fe8b1ed 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.66 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.67 2005/03/16 21:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,7 +37,8 @@ static Datum ExecScanSubPlan(SubPlanState *node,
bool *isNull);
static void buildSubPlanHash(SubPlanState *node);
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
-static bool tupleAllNulls(HeapTuple tuple);
+static bool slotAllNulls(TupleTableSlot *slot);
+static bool slotNoNulls(TupleTableSlot *slot);
/* ----------------------------------------------------------------
@@ -78,7 +79,6 @@ ExecHashSubPlan(SubPlanState *node,
PlanState *planstate = node->planstate;
ExprContext *innerecontext = node->innerecontext;
TupleTableSlot *slot;
- HeapTuple tup;
/* Shouldn't have any direct correlation Vars */
if (subplan->parParam != NIL || node->args != NIL)
@@ -105,7 +105,6 @@ ExecHashSubPlan(SubPlanState *node,
*/
node->projLeft->pi_exprContext = econtext;
slot = ExecProject(node->projLeft, NULL);
- tup = slot->val;
/*
* Note: because we are typically called in a per-tuple context, we
@@ -137,7 +136,7 @@ ExecHashSubPlan(SubPlanState *node,
* comparison we will not even make, unless there's a chance match of
* hash keys.
*/
- if (HeapTupleNoNulls(tup))
+ if (slotNoNulls(slot))
{
if (node->havehashrows &&
LookupTupleHashEntry(node->hashtable, slot, NULL) != NULL)
@@ -171,7 +170,7 @@ ExecHashSubPlan(SubPlanState *node,
ExecClearTuple(slot);
return BoolGetDatum(false);
}
- if (tupleAllNulls(tup))
+ if (slotAllNulls(slot))
{
ExecClearTuple(slot);
*isNull = true;
@@ -271,8 +270,7 @@ ExecScanSubPlan(SubPlanState *node,
!TupIsNull(slot);
slot = ExecProcNode(planstate))
{
- HeapTuple tup = slot->val;
- TupleDesc tdesc = slot->ttc_tupleDescriptor;
+ TupleDesc tdesc = slot->tts_tupleDescriptor;
Datum rowresult = BoolGetDatum(!useOr);
bool rownull = false;
int col = 1;
@@ -303,13 +301,12 @@ ExecScanSubPlan(SubPlanState *node,
* copied tuple for eventual freeing.
*/
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- tup = heap_copytuple(tup);
if (node->curTuple)
heap_freetuple(node->curTuple);
- node->curTuple = tup;
+ node->curTuple = ExecCopySlotTuple(slot);
MemoryContextSwitchTo(node->sub_estate->es_query_cxt);
- result = heap_getattr(tup, col, tdesc, isNull);
+ result = heap_getattr(node->curTuple, col, tdesc, isNull);
/* keep scanning subplan to make sure there's only one tuple */
continue;
}
@@ -321,7 +318,7 @@ ExecScanSubPlan(SubPlanState *node,
found = true;
/* stash away current value */
- dvalue = heap_getattr(tup, 1, tdesc, &disnull);
+ dvalue = slot_getattr(slot, 1, &disnull);
astate = accumArrayResult(astate, dvalue, disnull,
tdesc->attrs[0]->atttypid,
oldcontext);
@@ -357,7 +354,7 @@ ExecScanSubPlan(SubPlanState *node,
*/
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
Assert(prmdata->execPlan == NULL);
- prmdata->value = heap_getattr(tup, col, tdesc,
+ prmdata->value = slot_getattr(slot, col,
&(prmdata->isnull));
/*
@@ -554,8 +551,6 @@ buildSubPlanHash(SubPlanState *node)
!TupIsNull(slot);
slot = ExecProcNode(planstate))
{
- HeapTuple tup = slot->val;
- TupleDesc tdesc = slot->ttc_tupleDescriptor;
int col = 1;
ListCell *plst;
bool isnew;
@@ -571,20 +566,16 @@ buildSubPlanHash(SubPlanState *node)
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
Assert(prmdata->execPlan == NULL);
- prmdata->value = heap_getattr(tup, col, tdesc,
+ prmdata->value = slot_getattr(slot, col,
&(prmdata->isnull));
col++;
}
slot = ExecProject(node->projRight, NULL);
- tup = slot->val;
/*
* If result contains any nulls, store separately or not at all.
- * (Since we know the projection tuple has no junk columns, we can
- * just look at the overall hasnull info bit, instead of groveling
- * through the columns.)
*/
- if (HeapTupleNoNulls(tup))
+ if (slotNoNulls(slot))
{
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
node->havehashrows = true;
@@ -606,7 +597,8 @@ buildSubPlanHash(SubPlanState *node)
* Since the projected tuples are in the sub-query's context and not
* the main context, we'd better clear the tuple slot before there's
* any chance of a reset of the sub-query's context. Else we will
- * have the potential for a double free attempt.
+ * have the potential for a double free attempt. (XXX possibly
+ * no longer needed, but can't hurt.)
*/
ExecClearTuple(node->projRight->pi_slot);
@@ -626,17 +618,15 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
{
int numCols = hashtable->numCols;
AttrNumber *keyColIdx = hashtable->keyColIdx;
- HeapTuple tuple = slot->val;
- TupleDesc tupdesc = slot->ttc_tupleDescriptor;
TupleHashIterator hashiter;
TupleHashEntry entry;
ResetTupleHashIterator(hashtable, &hashiter);
while ((entry = ScanTupleHashTable(&hashiter)) != NULL)
{
- if (!execTuplesUnequal(entry->firstTuple,
- tuple,
- tupdesc,
+ ExecStoreTuple(entry->firstTuple, hashtable->tableslot,
+ InvalidBuffer, false);
+ if (!execTuplesUnequal(hashtable->tableslot, slot,
numCols, keyColIdx,
hashtable->eqfunctions,
hashtable->tempcxt))
@@ -646,17 +636,40 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
}
/*
- * tupleAllNulls: is the tuple completely NULL?
+ * slotAllNulls: is the slot completely NULL?
+ *
+ * This does not test for dropped columns, which is OK because we only
+ * use it on projected tuples.
+ */
+static bool
+slotAllNulls(TupleTableSlot *slot)
+{
+ int ncols = slot->tts_tupleDescriptor->natts;
+ int i;
+
+ for (i = 1; i <= ncols; i++)
+ {
+ if (!slot_attisnull(slot, i))
+ return false;
+ }
+ return true;
+}
+
+/*
+ * slotNoNulls: is the slot entirely not NULL?
+ *
+ * This does not test for dropped columns, which is OK because we only
+ * use it on projected tuples.
*/
static bool
-tupleAllNulls(HeapTuple tuple)
+slotNoNulls(TupleTableSlot *slot)
{
- int ncols = tuple->t_data->t_natts;
+ int ncols = slot->tts_tupleDescriptor->natts;
int i;
for (i = 1; i <= ncols; i++)
{
- if (!heap_attisnull(tuple, i))
+ if (slot_attisnull(slot, i))
return false;
}
return true;
@@ -932,8 +945,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
!TupIsNull(slot);
slot = ExecProcNode(planstate))
{
- HeapTuple tup = slot->val;
- TupleDesc tdesc = slot->ttc_tupleDescriptor;
+ TupleDesc tdesc = slot->tts_tupleDescriptor;
int i = 1;
if (subLinkType == EXISTS_SUBLINK)
@@ -956,7 +968,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
found = true;
/* stash away current value */
- dvalue = heap_getattr(tup, 1, tdesc, &disnull);
+ dvalue = slot_getattr(slot, 1, &disnull);
astate = accumArrayResult(astate, dvalue, disnull,
tdesc->attrs[0]->atttypid,
oldcontext);
@@ -981,10 +993,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
* freeing.
*/
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- tup = heap_copytuple(tup);
if (node->curTuple)
heap_freetuple(node->curTuple);
- node->curTuple = tup;
+ node->curTuple = ExecCopySlotTuple(slot);
MemoryContextSwitchTo(node->sub_estate->es_query_cxt);
/*
@@ -996,7 +1007,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
prm->execPlan = NULL;
- prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull));
+ prm->value = heap_getattr(node->curTuple, i, tdesc,
+ &(prm->isnull));
i++;
}
}
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index dbd628ba878..10ffddd5cdf 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeUnique.c,v 1.45 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeUnique.c,v 1.46 2005/03/16 21:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -44,14 +44,12 @@ ExecUnique(UniqueState *node)
TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
PlanState *outerPlan;
- TupleDesc tupDesc;
/*
* get information from the node
*/
outerPlan = outerPlanState(node);
resultTupleSlot = node->ps.ps_ResultTupleSlot;
- tupDesc = ExecGetResultType(&node->ps);
/*
* now loop, returning only non-duplicate tuples. We assume that the
@@ -59,7 +57,7 @@ ExecUnique(UniqueState *node)
*
* We return the first tuple from each group of duplicates (or the last
* tuple of each group, when moving backwards). At either end of the
- * subplan, clear priorTuple so that we correctly return the
+ * subplan, clear the result slot so that we correctly return the
* first/last tuple when reversing direction.
*/
for (;;)
@@ -71,16 +69,14 @@ ExecUnique(UniqueState *node)
if (TupIsNull(slot))
{
/* end of subplan; reset in case we change direction */
- if (node->priorTuple != NULL)
- heap_freetuple(node->priorTuple);
- node->priorTuple = NULL;
+ ExecClearTuple(resultTupleSlot);
return NULL;
}
/*
* Always return the first/last tuple from the subplan.
*/
- if (node->priorTuple == NULL)
+ if (TupIsNull(resultTupleSlot))
break;
/*
@@ -88,8 +84,7 @@ ExecUnique(UniqueState *node)
* match. If so then we loop back and fetch another new tuple
* from the subplan.
*/
- if (!execTuplesMatch(slot->val, node->priorTuple,
- tupDesc,
+ if (!execTuplesMatch(slot, resultTupleSlot,
plannode->numCols, plannode->uniqColIdx,
node->eqfunctions,
node->tempContext))
@@ -101,28 +96,8 @@ ExecUnique(UniqueState *node)
* any). Save it and return it. We must copy it because the source
* subplan won't guarantee that this source tuple is still accessible
* after fetching the next source tuple.
- *
- * Note that we manage the copy ourselves. We can't rely on the result
- * tuple slot to maintain the tuple reference because our caller may
- * replace the slot contents with a different tuple. We assume that
- * the caller will no longer be interested in the current tuple after
- * he next calls us.
- *
- * tgl 3/2004: the above concern is no longer valid; junkfilters used to
- * modify their input's return slot but don't anymore, and I don't
- * think anyplace else does either. Not worth changing this code
- * though.
*/
- if (node->priorTuple != NULL)
- heap_freetuple(node->priorTuple);
- node->priorTuple = heap_copytuple(slot->val);
-
- ExecStoreTuple(node->priorTuple,
- resultTupleSlot,
- InvalidBuffer,
- false); /* tuple does not belong to slot */
-
- return resultTupleSlot;
+ return ExecCopySlot(resultTupleSlot, slot);
}
/* ----------------------------------------------------------------
@@ -144,8 +119,6 @@ ExecInitUnique(Unique *node, EState *estate)
uniquestate->ps.plan = (Plan *) node;
uniquestate->ps.state = estate;
- uniquestate->priorTuple = NULL;
-
/*
* Miscellaneous initialization
*
@@ -220,12 +193,8 @@ ExecEndUnique(UniqueState *node)
void
ExecReScanUnique(UniqueState *node, ExprContext *exprCtxt)
{
+ /* must clear result tuple so first input tuple is returned */
ExecClearTuple(node->ps.ps_ResultTupleSlot);
- if (node->priorTuple != NULL)
- {
- heap_freetuple(node->priorTuple);
- node->priorTuple = NULL;
- }
/*
* if chgParam of subnode is not null then plan will be re-scanned by
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index fd860fcb551..d26e3068509 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.134 2005/02/10 20:36:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.135 2005/03/16 21:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1191,7 +1191,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
* of current SPI procedure
*/
void
-spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
+spi_printtup(TupleTableSlot *slot, DestReceiver *self)
{
SPITupleTable *tuptable;
MemoryContext oldcxt;
@@ -1219,7 +1219,8 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
tuptable->alloced * sizeof(HeapTuple));
}
- tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
+ tuptable->vals[tuptable->alloced - tuptable->free] =
+ ExecCopySlotTuple(slot);
(tuptable->free)--;
MemoryContextSwitchTo(oldcxt);
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index 5cf9d015448..819fa962b04 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.13 2004/12/31 21:59:45 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.14 2005/03/16 21:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,12 +40,12 @@ tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
* Receive a tuple from the executor and store it in the tuplestore.
*/
static void
-tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
+tstoreReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
{
TStoreState *myState = (TStoreState *) self;
MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt);
- tuplestore_puttuple(myState->tstore, tuple);
+ tuplestore_puttuple(myState->tstore, ExecFetchSlotTuple(slot));
MemoryContextSwitchTo(oldcxt);
}
@@ -77,7 +77,7 @@ CreateTuplestoreDestReceiver(Tuplestorestate *tStore,
{
TStoreState *self = (TStoreState *) palloc(sizeof(TStoreState));
- self->pub.receiveTuple = tstoreReceiveTuple;
+ self->pub.receiveSlot = tstoreReceiveSlot;
self->pub.rStartup = tstoreStartupReceiver;
self->pub.rShutdown = tstoreShutdownReceiver;
self->pub.rDestroy = tstoreDestroyReceiver;