mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Revise TupleTableSlot code to avoid unnecessary construction and disassembly
of tuples when passing data up through multiple plan nodes. A slot can now hold either a normal "physical" HeapTuple, or a "virtual" tuple consisting of Datum/isnull arrays. Upper plan levels can usually just copy the Datum arrays, avoiding heap_formtuple() and possible subsequent nocachegetattr() calls to extract the data again. This work extends Atsushi Ogawa's earlier patch, which provided the key idea of adding Datum arrays to TupleTableSlots. (I believe however that something like this was foreseen way back in Berkeley days --- see the old comment on ExecProject.) A test case involving many levels of join of fairly wide tables (about 80 columns altogether) showed about 3x overall speedup, though simple queries will probably not be helped very much. I have also duplicated some code in heaptuple.c in order to provide versions of heap_formtuple and friends that use "bool" arrays to indicate null attributes, instead of the old convention of "char" arrays containing either 'n' or ' '. This provides a better match to the convention used by ExecEvalExpr. While I have not made a concerted effort to get rid of uses of the old routines, I think they should be deprecated and eventually removed.
This commit is contained in:
@ -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,
|
||||
|
@ -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.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -75,6 +75,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;
|
||||
}
|
||||
|
||||
@ -142,6 +147,14 @@ ExecInitJunkFilterConversion(List *targetList,
|
||||
ListCell *t;
|
||||
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;
|
||||
}
|
||||
ExecClearTuple(resultSlot);
|
||||
values = resultSlot->tts_values;
|
||||
isnull = resultSlot->tts_isnull;
|
||||
|
||||
/*
|
||||
* 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';
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* And return the virtual tuple.
|
||||
*/
|
||||
cleanTuple = heap_formtuple(cleanTupType, values, nulls);
|
||||
|
||||
/*
|
||||
* We are done. Free any space allocated for 'values' and 'nulls' and
|
||||
* return the new tuple.
|
||||
*/
|
||||
if (values != values_array)
|
||||
{
|
||||
pfree(values);
|
||||
pfree(nulls);
|
||||
}
|
||||
if (old_values != old_values_array)
|
||||
{
|
||||
pfree(old_values);
|
||||
pfree(old_nulls);
|
||||
}
|
||||
|
||||
return cleanTuple;
|
||||
return ExecStoreVirtualTuple(resultSlot);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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,55 +3472,37 @@ 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
|
||||
*/
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* form the new result tuple (in the caller's memory context!)
|
||||
*/
|
||||
/* Report success */
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
return heap_formtuple(targettype, values, nulls);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecProject
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Force extraction of all input values that we need.
|
||||
*/
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* projects a tuple based on projection info and stores
|
||||
* it in the specified tuple table slot.
|
||||
* it in the previously 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
|
||||
* ----------------------------------------------------------------
|
||||
* 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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
@ -274,10 +306,54 @@ 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)
|
||||
{
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(slot != NULL);
|
||||
Assert(slot->tts_tupleDescriptor != NULL);
|
||||
|
||||
/* Clear any old contents */
|
||||
ExecClearTuple(slot);
|
||||
|
||||
/*
|
||||
* mark slot_getattr state invalid
|
||||
* Fill all the columns of the virtual tuple with nulls
|
||||
*/
|
||||
slot->cache_natts = 0;
|
||||
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);
|
||||
|
||||
/*
|
||||
* release any old cache array since tupledesc's natts may have changed
|
||||
* If we have a physical tuple then just copy it.
|
||||
*/
|
||||
if (slot->cache_values)
|
||||
pfree(slot->cache_values);
|
||||
slot->cache_values = NULL;
|
||||
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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
projInfo->pi_tupValues = (Datum *) palloc(len * sizeof(Datum));
|
||||
projInfo->pi_tupNulls = (char *) palloc(len * sizeof(char));
|
||||
projInfo->pi_itemIsDone = (ExprDoneCond *) palloc(len * sizeof(ExprDoneCond));
|
||||
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_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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
@ -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,10 +47,14 @@ 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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
@ -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
|
||||
tupleAllNulls(HeapTuple tuple)
|
||||
slotAllNulls(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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
slotNoNulls(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;
|
||||
@ -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++;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user