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