Commit f97aebd1 authored by Tom Lane's avatar Tom Lane

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.
parent 712f0535
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.100 2005/03/12 20:25:06 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.101 2005/03/16 21:38:04 tgl Exp $
--> -->
<sect1 id="xfunc"> <sect1 id="xfunc">
...@@ -2219,7 +2219,7 @@ CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean ...@@ -2219,7 +2219,7 @@ CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
case, you first need to obtain or construct a <structname>TupleDesc</> case, you first need to obtain or construct a <structname>TupleDesc</>
descriptor for the tuple structure. When working with Datums, you descriptor for the tuple structure. When working with Datums, you
pass the <structname>TupleDesc</> to <function>BlessTupleDesc</>, pass the <structname>TupleDesc</> to <function>BlessTupleDesc</>,
and then call <function>heap_formtuple</> for each row. When working and then call <function>heap_form_tuple</> for each row. When working
with C strings, you pass the <structname>TupleDesc</> to with C strings, you pass the <structname>TupleDesc</> to
<function>TupleDescGetAttInMetadata</>, and then call <function>TupleDescGetAttInMetadata</>, and then call
<function>BuildTupleFromCStrings</> for each row. In the case of a <function>BuildTupleFromCStrings</> for each row. In the case of a
...@@ -2264,7 +2264,7 @@ AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) ...@@ -2264,7 +2264,7 @@ AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
<para> <para>
When working with Datums, use When working with Datums, use
<programlisting> <programlisting>
HeapTuple heap_formtuple(TupleDesc tupdesc, Datum *values, char *nulls) HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull)
</programlisting> </programlisting>
to build a <structname>HeapTuple</> given user data in Datum form. to build a <structname>HeapTuple</> given user data in Datum form.
</para> </para>
...@@ -2383,7 +2383,7 @@ typedef struct ...@@ -2383,7 +2383,7 @@ typedef struct
* *
* tuple_desc is for use when returning tuples (i.e. composite data types) * tuple_desc is for use when returning tuples (i.e. composite data types)
* and is only needed if you are going to build the tuples with * and is only needed if you are going to build the tuples with
* heap_formtuple() rather than with BuildTupleFromCStrings(). Note that * heap_form_tuple() rather than with BuildTupleFromCStrings(). Note that
* the TupleDesc pointer stored here should usually have been run through * the TupleDesc pointer stored here should usually have been run through
* BlessTupleDesc() first. * BlessTupleDesc() first.
*/ */
......
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.86 2004/12/31 21:59:07 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.87 2005/03/16 21:38:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -25,12 +25,9 @@ ...@@ -25,12 +25,9 @@
static void printtup_startup(DestReceiver *self, int operation, static void printtup_startup(DestReceiver *self, int operation,
TupleDesc typeinfo); TupleDesc typeinfo);
static void printtup(HeapTuple tuple, TupleDesc typeinfo, static void printtup(TupleTableSlot *slot, DestReceiver *self);
DestReceiver *self); static void printtup_20(TupleTableSlot *slot, DestReceiver *self);
static void printtup_20(HeapTuple tuple, TupleDesc typeinfo, static void printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
DestReceiver *self);
static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self);
static void printtup_shutdown(DestReceiver *self); static void printtup_shutdown(DestReceiver *self);
static void printtup_destroy(DestReceiver *self); static void printtup_destroy(DestReceiver *self);
...@@ -65,8 +62,6 @@ typedef struct ...@@ -65,8 +62,6 @@ typedef struct
TupleDesc attrinfo; /* The attr info we are set up for */ TupleDesc attrinfo; /* The attr info we are set up for */
int nattrs; int nattrs;
PrinttupAttrInfo *myinfo; /* Cached info about each attr */ PrinttupAttrInfo *myinfo; /* Cached info about each attr */
Datum *values; /* preallocated space for deformtuple */
char *nulls;
} DR_printtup; } DR_printtup;
/* ---------------- /* ----------------
...@@ -79,7 +74,7 @@ printtup_create_DR(CommandDest dest, Portal portal) ...@@ -79,7 +74,7 @@ printtup_create_DR(CommandDest dest, Portal portal)
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup)); DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
self->pub.receiveTuple = printtup; self->pub.receiveSlot = printtup;
else else
{ {
/* /*
...@@ -88,9 +83,9 @@ printtup_create_DR(CommandDest dest, Portal portal) ...@@ -88,9 +83,9 @@ printtup_create_DR(CommandDest dest, Portal portal)
* sufficient to look at the first one. * sufficient to look at the first one.
*/ */
if (portal->formats && portal->formats[0] != 0) if (portal->formats && portal->formats[0] != 0)
self->pub.receiveTuple = printtup_internal_20; self->pub.receiveSlot = printtup_internal_20;
else else
self->pub.receiveTuple = printtup_20; self->pub.receiveSlot = printtup_20;
} }
self->pub.rStartup = printtup_startup; self->pub.rStartup = printtup_startup;
self->pub.rShutdown = printtup_shutdown; self->pub.rShutdown = printtup_shutdown;
...@@ -105,8 +100,6 @@ printtup_create_DR(CommandDest dest, Portal portal) ...@@ -105,8 +100,6 @@ printtup_create_DR(CommandDest dest, Portal portal)
self->attrinfo = NULL; self->attrinfo = NULL;
self->nattrs = 0; self->nattrs = 0;
self->myinfo = NULL; self->myinfo = NULL;
self->values = NULL;
self->nulls = NULL;
return (DestReceiver *) self; return (DestReceiver *) self;
} }
...@@ -251,12 +244,6 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) ...@@ -251,12 +244,6 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
if (myState->myinfo) if (myState->myinfo)
pfree(myState->myinfo); pfree(myState->myinfo);
myState->myinfo = NULL; myState->myinfo = NULL;
if (myState->values)
pfree(myState->values);
myState->values = NULL;
if (myState->nulls)
pfree(myState->nulls);
myState->nulls = NULL;
myState->attrinfo = typeinfo; myState->attrinfo = typeinfo;
myState->nattrs = numAttrs; myState->nattrs = numAttrs;
...@@ -265,8 +252,6 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) ...@@ -265,8 +252,6 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
myState->myinfo = (PrinttupAttrInfo *) myState->myinfo = (PrinttupAttrInfo *)
palloc0(numAttrs * sizeof(PrinttupAttrInfo)); palloc0(numAttrs * sizeof(PrinttupAttrInfo));
myState->values = (Datum *) palloc(numAttrs * sizeof(Datum));
myState->nulls = (char *) palloc(numAttrs * sizeof(char));
for (i = 0; i < numAttrs; i++) for (i = 0; i < numAttrs; i++)
{ {
...@@ -302,8 +287,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) ...@@ -302,8 +287,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
* ---------------- * ----------------
*/ */
static void static void
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) printtup(TupleTableSlot *slot, DestReceiver *self)
{ {
TupleDesc typeinfo = slot->tts_tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self; DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf; StringInfoData buf;
int natts = typeinfo->natts; int natts = typeinfo->natts;
...@@ -313,10 +299,8 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -313,10 +299,8 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
if (myState->attrinfo != typeinfo || myState->nattrs != natts) if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts); printtup_prepare_info(myState, typeinfo, natts);
/* /* Make sure the tuple is fully deconstructed */
* deconstruct the tuple (faster than a heap_getattr loop) slot_getallattrs(slot);
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/* /*
* Prepare a DataRow message * Prepare a DataRow message
...@@ -331,10 +315,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -331,10 +315,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = myState->values[i], Datum origattr = slot->tts_values[i],
attr; attr;
if (myState->nulls[i] == 'n') if (slot->tts_isnull[i])
{ {
pq_sendint(&buf, -1, 4); pq_sendint(&buf, -1, 4);
continue; continue;
...@@ -389,8 +373,9 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -389,8 +373,9 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
* ---------------- * ----------------
*/ */
static void static void
printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) printtup_20(TupleTableSlot *slot, DestReceiver *self)
{ {
TupleDesc typeinfo = slot->tts_tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self; DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf; StringInfoData buf;
int natts = typeinfo->natts; int natts = typeinfo->natts;
...@@ -402,10 +387,8 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -402,10 +387,8 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
if (myState->attrinfo != typeinfo || myState->nattrs != natts) if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts); printtup_prepare_info(myState, typeinfo, natts);
/* /* Make sure the tuple is fully deconstructed */
* deconstruct the tuple (faster than a heap_getattr loop) slot_getallattrs(slot);
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/* /*
* tell the frontend to expect new tuple data (in ASCII style) * tell the frontend to expect new tuple data (in ASCII style)
...@@ -419,7 +402,7 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -419,7 +402,7 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
k = 1 << 7; k = 1 << 7;
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
if (myState->nulls[i] != 'n') if (slot->tts_isnull[i])
j |= k; /* set bit if not null */ j |= k; /* set bit if not null */
k >>= 1; k >>= 1;
if (k == 0) /* end of byte? */ if (k == 0) /* end of byte? */
...@@ -438,11 +421,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -438,11 +421,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = myState->values[i], Datum origattr = slot->tts_values[i],
attr; attr;
char *outputstr; char *outputstr;
if (myState->nulls[i] == 'n') if (slot->tts_isnull[i])
continue; continue;
Assert(thisState->format == 0); Assert(thisState->format == 0);
...@@ -483,12 +466,6 @@ printtup_shutdown(DestReceiver *self) ...@@ -483,12 +466,6 @@ printtup_shutdown(DestReceiver *self)
if (myState->myinfo) if (myState->myinfo)
pfree(myState->myinfo); pfree(myState->myinfo);
myState->myinfo = NULL; myState->myinfo = NULL;
if (myState->values)
pfree(myState->values);
myState->values = NULL;
if (myState->nulls)
pfree(myState->nulls);
myState->nulls = NULL;
myState->attrinfo = NULL; myState->attrinfo = NULL;
} }
...@@ -548,8 +525,9 @@ debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo) ...@@ -548,8 +525,9 @@ debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
* ---------------- * ----------------
*/ */
void void
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) debugtup(TupleTableSlot *slot, DestReceiver *self)
{ {
TupleDesc typeinfo = slot->tts_tupleDescriptor;
int natts = typeinfo->natts; int natts = typeinfo->natts;
int i; int i;
Datum origattr, Datum origattr,
...@@ -562,7 +540,7 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -562,7 +540,7 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull); origattr = slot_getattr(slot, i + 1, &isnull);
if (isnull) if (isnull)
continue; continue;
getTypeOutputInfo(typeinfo->attrs[i]->atttypid, getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
...@@ -603,8 +581,9 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -603,8 +581,9 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
* ---------------- * ----------------
*/ */
static void static void
printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
{ {
TupleDesc typeinfo = slot->tts_tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self; DR_printtup *myState = (DR_printtup *) self;
StringInfoData buf; StringInfoData buf;
int natts = typeinfo->natts; int natts = typeinfo->natts;
...@@ -616,10 +595,8 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -616,10 +595,8 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
if (myState->attrinfo != typeinfo || myState->nattrs != natts) if (myState->attrinfo != typeinfo || myState->nattrs != natts)
printtup_prepare_info(myState, typeinfo, natts); printtup_prepare_info(myState, typeinfo, natts);
/* /* Make sure the tuple is fully deconstructed */
* deconstruct the tuple (faster than a heap_getattr loop) slot_getallattrs(slot);
*/
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
/* /*
* tell the frontend to expect new tuple data (in binary style) * tell the frontend to expect new tuple data (in binary style)
...@@ -633,7 +610,7 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -633,7 +610,7 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
k = 1 << 7; k = 1 << 7;
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
if (myState->nulls[i] != 'n') if (slot->tts_isnull[i])
j |= k; /* set bit if not null */ j |= k; /* set bit if not null */
k >>= 1; k >>= 1;
if (k == 0) /* end of byte? */ if (k == 0) /* end of byte? */
...@@ -652,11 +629,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) ...@@ -652,11 +629,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
for (i = 0; i < natts; ++i) for (i = 0; i < natts; ++i)
{ {
PrinttupAttrInfo *thisState = myState->myinfo + i; PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = myState->values[i], Datum origattr = slot->tts_values[i],
attr; attr;
bytea *outputbytes; bytea *outputbytes;
if (myState->nulls[i] == 'n') if (slot->tts_isnull[i])
continue; continue;
Assert(thisState->format == 1); Assert(thisState->format == 1);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.246 2005/03/07 04:42:16 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.247 2005/03/16 21:38:04 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -894,8 +894,7 @@ BuildIndexInfo(Relation index) ...@@ -894,8 +894,7 @@ BuildIndexInfo(Relation index)
* Construct Datum[] and nullv[] arrays for a new index tuple. * Construct Datum[] and nullv[] arrays for a new index tuple.
* *
* indexInfo Info about the index * indexInfo Info about the index
* heapTuple Heap tuple for which we must prepare an index entry * slot Heap tuple for which we must prepare an index entry
* heapDescriptor tupledesc for heap tuple
* estate executor state for evaluating any index expressions * estate executor state for evaluating any index expressions
* datum Array of index Datums (output area) * datum Array of index Datums (output area)
* nullv Array of is-null indicators (output area) * nullv Array of is-null indicators (output area)
...@@ -910,8 +909,7 @@ BuildIndexInfo(Relation index) ...@@ -910,8 +909,7 @@ BuildIndexInfo(Relation index)
*/ */
void void
FormIndexDatum(IndexInfo *indexInfo, FormIndexDatum(IndexInfo *indexInfo,
HeapTuple heapTuple, TupleTableSlot *slot,
TupleDesc heapDescriptor,
EState *estate, EState *estate,
Datum *datum, Datum *datum,
char *nullv) char *nullv)
...@@ -927,7 +925,7 @@ FormIndexDatum(IndexInfo *indexInfo, ...@@ -927,7 +925,7 @@ FormIndexDatum(IndexInfo *indexInfo,
ExecPrepareExpr((Expr *) indexInfo->ii_Expressions, ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
estate); estate);
/* Check caller has set up context correctly */ /* Check caller has set up context correctly */
Assert(GetPerTupleExprContext(estate)->ecxt_scantuple->val == heapTuple); Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
} }
indexpr_item = list_head(indexInfo->ii_ExpressionsState); indexpr_item = list_head(indexInfo->ii_ExpressionsState);
...@@ -943,7 +941,7 @@ FormIndexDatum(IndexInfo *indexInfo, ...@@ -943,7 +941,7 @@ FormIndexDatum(IndexInfo *indexInfo,
* Plain index column; get the value we need directly from the * Plain index column; get the value we need directly from the
* heap tuple. * heap tuple.
*/ */
iDatum = heap_getattr(heapTuple, keycol, heapDescriptor, &isNull); iDatum = slot_getattr(slot, keycol, &isNull);
} }
else else
{ {
...@@ -1336,12 +1334,10 @@ IndexBuildHeapScan(Relation heapRelation, ...@@ -1336,12 +1334,10 @@ IndexBuildHeapScan(Relation heapRelation,
{ {
HeapScanDesc scan; HeapScanDesc scan;
HeapTuple heapTuple; HeapTuple heapTuple;
TupleDesc heapDescriptor;
Datum attdata[INDEX_MAX_KEYS]; Datum attdata[INDEX_MAX_KEYS];
char nulls[INDEX_MAX_KEYS]; char nulls[INDEX_MAX_KEYS];
double reltuples; double reltuples;
List *predicate; List *predicate;
TupleTable tupleTable;
TupleTableSlot *slot; TupleTableSlot *slot;
EState *estate; EState *estate;
ExprContext *econtext; ExprContext *econtext;
...@@ -1353,41 +1349,21 @@ IndexBuildHeapScan(Relation heapRelation, ...@@ -1353,41 +1349,21 @@ IndexBuildHeapScan(Relation heapRelation,
*/ */
Assert(OidIsValid(indexRelation->rd_rel->relam)); Assert(OidIsValid(indexRelation->rd_rel->relam));
heapDescriptor = RelationGetDescr(heapRelation);
/* /*
* Need an EState for evaluation of index expressions and * Need an EState for evaluation of index expressions and
* partial-index predicates. * partial-index predicates. Also a slot to hold the current tuple.
*/ */
estate = CreateExecutorState(); estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate); econtext = GetPerTupleExprContext(estate);
slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
/* /* Arrange for econtext's scan tuple to be the tuple under test */
* If this is a predicate (partial) index, we will need to evaluate econtext->ecxt_scantuple = slot;
* the predicate using ExecQual, which requires the current tuple to
* be in a slot of a TupleTable. Likewise if there are any
* expressions.
*/
if (indexInfo->ii_Predicate != NIL || indexInfo->ii_Expressions != NIL)
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
ExecSetSlotDescriptor(slot, heapDescriptor, false);
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate. */ /* Set up execution state for predicate, if any. */
predicate = (List *) predicate = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate); estate);
}
else
{
tupleTable = NULL;
slot = NULL;
predicate = NIL;
}
/* /*
* Ok, begin our scan of the base relation. We use SnapshotAny * Ok, begin our scan of the base relation. We use SnapshotAny
...@@ -1511,8 +1487,7 @@ IndexBuildHeapScan(Relation heapRelation, ...@@ -1511,8 +1487,7 @@ IndexBuildHeapScan(Relation heapRelation,
MemoryContextReset(econtext->ecxt_per_tuple_memory); MemoryContextReset(econtext->ecxt_per_tuple_memory);
/* Set up for predicate or expression evaluation */ /* Set up for predicate or expression evaluation */
if (slot) ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
/* /*
* In a partial index, discard tuples that don't satisfy the * In a partial index, discard tuples that don't satisfy the
...@@ -1534,8 +1509,7 @@ IndexBuildHeapScan(Relation heapRelation, ...@@ -1534,8 +1509,7 @@ IndexBuildHeapScan(Relation heapRelation,
* evaluation of any expressions needed. * evaluation of any expressions needed.
*/ */
FormIndexDatum(indexInfo, FormIndexDatum(indexInfo,
heapTuple, slot,
heapDescriptor,
estate, estate,
attdata, attdata,
nulls); nulls);
...@@ -1553,8 +1527,7 @@ IndexBuildHeapScan(Relation heapRelation, ...@@ -1553,8 +1527,7 @@ IndexBuildHeapScan(Relation heapRelation,
heap_endscan(scan); heap_endscan(scan);
if (tupleTable) ExecDropSingleTupleTableSlot(slot);
ExecDropTupleTable(tupleTable, true);
FreeExecutorState(estate); FreeExecutorState(estate);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.107 2004/12/31 21:59:38 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.108 2005/03/16 21:38:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -73,19 +73,24 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ...@@ -73,19 +73,24 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
int numIndexes; int numIndexes;
RelationPtr relationDescs; RelationPtr relationDescs;
Relation heapRelation; Relation heapRelation;
TupleDesc heapDescriptor; TupleTableSlot *slot;
IndexInfo **indexInfoArray; IndexInfo **indexInfoArray;
Datum datum[INDEX_MAX_KEYS]; Datum datum[INDEX_MAX_KEYS];
char nullv[INDEX_MAX_KEYS]; char nullv[INDEX_MAX_KEYS];
/* /*
* Get information from the state structure. * Get information from the state structure. Fall out if nothing to do.
*/ */
numIndexes = indstate->ri_NumIndices; numIndexes = indstate->ri_NumIndices;
if (numIndexes == 0)
return;
relationDescs = indstate->ri_IndexRelationDescs; relationDescs = indstate->ri_IndexRelationDescs;
indexInfoArray = indstate->ri_IndexRelationInfo; indexInfoArray = indstate->ri_IndexRelationInfo;
heapRelation = indstate->ri_RelationDesc; heapRelation = indstate->ri_RelationDesc;
heapDescriptor = RelationGetDescr(heapRelation);
/* Need a slot to hold the tuple being examined */
slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
/* /*
* for each index, form and insert the index tuple * for each index, form and insert the index tuple
...@@ -106,11 +111,10 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ...@@ -106,11 +111,10 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
/* /*
* FormIndexDatum fills in its datum and null parameters with * FormIndexDatum fills in its datum and null parameters with
* attribute information taken from the given heap tuple. * attribute information taken from the given tuple.
*/ */
FormIndexDatum(indexInfo, FormIndexDatum(indexInfo,
heapTuple, slot,
heapDescriptor,
NULL, /* no expression eval to do */ NULL, /* no expression eval to do */
datum, datum,
nullv); nullv);
...@@ -128,6 +132,8 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ...@@ -128,6 +132,8 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
if (result) if (result)
pfree(result); pfree(result);
} }
ExecDropSingleTupleTableSlot(slot);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.82 2005/02/11 00:41:12 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.83 2005/03/16 21:38:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -448,14 +448,11 @@ compute_index_stats(Relation onerel, double totalrows, ...@@ -448,14 +448,11 @@ compute_index_stats(Relation onerel, double totalrows,
{ {
MemoryContext ind_context, MemoryContext ind_context,
old_context; old_context;
TupleDesc heapDescriptor;
Datum attdata[INDEX_MAX_KEYS]; Datum attdata[INDEX_MAX_KEYS];
char nulls[INDEX_MAX_KEYS]; char nulls[INDEX_MAX_KEYS];
int ind, int ind,
i; i;
heapDescriptor = RelationGetDescr(onerel);
ind_context = AllocSetContextCreate(anl_context, ind_context = AllocSetContextCreate(anl_context,
"Analyze Index", "Analyze Index",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_MINSIZE,
...@@ -468,7 +465,6 @@ compute_index_stats(Relation onerel, double totalrows, ...@@ -468,7 +465,6 @@ compute_index_stats(Relation onerel, double totalrows,
AnlIndexData *thisdata = &indexdata[ind]; AnlIndexData *thisdata = &indexdata[ind];
IndexInfo *indexInfo = thisdata->indexInfo; IndexInfo *indexInfo = thisdata->indexInfo;
int attr_cnt = thisdata->attr_cnt; int attr_cnt = thisdata->attr_cnt;
TupleTable tupleTable;
TupleTableSlot *slot; TupleTableSlot *slot;
EState *estate; EState *estate;
ExprContext *econtext; ExprContext *econtext;
...@@ -492,9 +488,7 @@ compute_index_stats(Relation onerel, double totalrows, ...@@ -492,9 +488,7 @@ compute_index_stats(Relation onerel, double totalrows,
estate = CreateExecutorState(); estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate); econtext = GetPerTupleExprContext(estate);
/* Need a slot to hold the current heap tuple, too */ /* Need a slot to hold the current heap tuple, too */
tupleTable = ExecCreateTupleTable(1); slot = MakeSingleTupleTableSlot(RelationGetDescr(onerel));
slot = ExecAllocTableSlot(tupleTable);
ExecSetSlotDescriptor(slot, heapDescriptor, false);
/* Arrange for econtext's scan tuple to be the tuple under test */ /* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot; econtext->ecxt_scantuple = slot;
...@@ -532,8 +526,7 @@ compute_index_stats(Relation onerel, double totalrows, ...@@ -532,8 +526,7 @@ compute_index_stats(Relation onerel, double totalrows,
* convenient. * convenient.
*/ */
FormIndexDatum(indexInfo, FormIndexDatum(indexInfo,
heapTuple, slot,
heapDescriptor,
estate, estate,
attdata, attdata,
nulls); nulls);
...@@ -585,7 +578,7 @@ compute_index_stats(Relation onerel, double totalrows, ...@@ -585,7 +578,7 @@ compute_index_stats(Relation onerel, double totalrows,
/* And clean up */ /* And clean up */
MemoryContextSwitchTo(ind_context); MemoryContextSwitchTo(ind_context);
ExecDropTupleTable(tupleTable, true); ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate); FreeExecutorState(estate);
MemoryContextResetAndDeleteChildren(ind_context); MemoryContextResetAndDeleteChildren(ind_context);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.237 2005/03/12 05:41:34 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.238 2005/03/16 21:38:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1487,7 +1487,6 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -1487,7 +1487,6 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
bool isnull; bool isnull;
ResultRelInfo *resultRelInfo; ResultRelInfo *resultRelInfo;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
TupleTable tupleTable;
TupleTableSlot *slot; TupleTableSlot *slot;
bool file_has_oids; bool file_has_oids;
int *defmap; int *defmap;
...@@ -1518,10 +1517,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -1518,10 +1517,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
estate->es_num_result_relations = 1; estate->es_num_result_relations = 1;
estate->es_result_relation_info = resultRelInfo; estate->es_result_relation_info = resultRelInfo;
/* Set up a dummy tuple table too */ /* Set up a tuple slot too */
tupleTable = ExecCreateTupleTable(1); slot = MakeSingleTupleTableSlot(tupDesc);
slot = ExecAllocTableSlot(tupleTable);
ExecSetSlotDescriptor(slot, tupDesc, false);
econtext = GetPerTupleExprContext(estate); econtext = GetPerTupleExprContext(estate);
...@@ -1989,7 +1986,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, ...@@ -1989,7 +1986,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
pfree(constraintexprs); pfree(constraintexprs);
pfree(force_notnull); pfree(force_notnull);
ExecDropTupleTable(tupleTable, true); ExecDropSingleTupleTableSlot(slot);
ExecCloseIndices(resultRelInfo); ExecCloseIndices(resultRelInfo);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.146 2005/02/09 23:17:26 neilc Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.147 2005/03/16 21:38:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2455,7 +2455,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2455,7 +2455,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
{ {
ExprContext *econtext; ExprContext *econtext;
Datum *values; Datum *values;
char *nulls; bool *isnull;
TupleTableSlot *oldslot; TupleTableSlot *oldslot;
TupleTableSlot *newslot; TupleTableSlot *newslot;
HeapScanDesc scan; HeapScanDesc scan;
...@@ -2471,17 +2471,15 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2471,17 +2471,15 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
* the tuples are the same, the tupDescs might not be (consider * the tuples are the same, the tupDescs might not be (consider
* ADD COLUMN without a default). * ADD COLUMN without a default).
*/ */
oldslot = MakeTupleTableSlot(); oldslot = MakeSingleTupleTableSlot(oldTupDesc);
ExecSetSlotDescriptor(oldslot, oldTupDesc, false); newslot = MakeSingleTupleTableSlot(newTupDesc);
newslot = MakeTupleTableSlot();
ExecSetSlotDescriptor(newslot, newTupDesc, false);
/* Preallocate values/nulls arrays */ /* Preallocate values/isnull arrays */
i = Max(newTupDesc->natts, oldTupDesc->natts); i = Max(newTupDesc->natts, oldTupDesc->natts);
values = (Datum *) palloc(i * sizeof(Datum)); values = (Datum *) palloc(i * sizeof(Datum));
nulls = (char *) palloc(i * sizeof(char)); isnull = (bool *) palloc(i * sizeof(bool));
memset(values, 0, i * sizeof(Datum)); memset(values, 0, i * sizeof(Datum));
memset(nulls, 'n', i * sizeof(char)); memset(isnull, true, i * sizeof(bool));
/* /*
* Any attributes that are dropped according to the new tuple * Any attributes that are dropped according to the new tuple
...@@ -2512,11 +2510,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2512,11 +2510,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
if (newrel) if (newrel)
{ {
/* Extract data from old tuple */ /* Extract data from old tuple */
heap_deformtuple(tuple, oldTupDesc, values, nulls); heap_deform_tuple(tuple, oldTupDesc, values, isnull);
/* Set dropped attributes to null in new tuple */ /* Set dropped attributes to null in new tuple */
foreach (lc, dropped_attrs) foreach (lc, dropped_attrs)
nulls[lfirst_int(lc)] = 'n'; isnull[lfirst_int(lc)] = true;
/* /*
* Process supplied expressions to replace selected * Process supplied expressions to replace selected
...@@ -2528,16 +2526,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2528,16 +2526,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
foreach(l, tab->newvals) foreach(l, tab->newvals)
{ {
NewColumnValue *ex = lfirst(l); NewColumnValue *ex = lfirst(l);
bool isNull;
values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate, values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
econtext, econtext,
&isNull, &isnull[ex->attnum - 1],
NULL); NULL);
if (isNull)
nulls[ex->attnum - 1] = 'n';
else
nulls[ex->attnum - 1] = ' ';
} }
/* /*
...@@ -2545,7 +2538,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2545,7 +2538,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
* pfree it, since the per-tuple memory context will * pfree it, since the per-tuple memory context will
* be reset shortly. * be reset shortly.
*/ */
tuple = heap_formtuple(newTupDesc, values, nulls); tuple = heap_form_tuple(newTupDesc, values, isnull);
} }
/* Now check any constraints on the possibly-changed tuple */ /* Now check any constraints on the possibly-changed tuple */
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.303 2005/03/04 20:21:06 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.304 2005/03/16 21:38:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -114,7 +114,6 @@ typedef struct ExecContextData ...@@ -114,7 +114,6 @@ typedef struct ExecContextData
{ {
ResultRelInfo *resultRelInfo; ResultRelInfo *resultRelInfo;
EState *estate; EState *estate;
TupleTable tupleTable;
TupleTableSlot *slot; TupleTableSlot *slot;
} ExecContextData; } ExecContextData;
typedef ExecContextData *ExecContext; typedef ExecContextData *ExecContext;
...@@ -141,16 +140,14 @@ ExecContext_Init(ExecContext ec, Relation rel) ...@@ -141,16 +140,14 @@ ExecContext_Init(ExecContext ec, Relation rel)
ec->estate->es_num_result_relations = 1; ec->estate->es_num_result_relations = 1;
ec->estate->es_result_relation_info = ec->resultRelInfo; ec->estate->es_result_relation_info = ec->resultRelInfo;
/* Set up a dummy tuple table too */ /* Set up a tuple slot too */
ec->tupleTable = ExecCreateTupleTable(1); ec->slot = MakeSingleTupleTableSlot(tupdesc);
ec->slot = ExecAllocTableSlot(ec->tupleTable);
ExecSetSlotDescriptor(ec->slot, tupdesc, false);
} }
static void static void
ExecContext_Finish(ExecContext ec) ExecContext_Finish(ExecContext ec)
{ {
ExecDropTupleTable(ec->tupleTable, true); ExecDropSingleTupleTableSlot(ec->slot);
ExecCloseIndices(ec->resultRelInfo); ExecCloseIndices(ec->resultRelInfo);
FreeExecutorState(ec->estate); FreeExecutorState(ec->estate);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -41,8 +41,7 @@ static int TupleHashTableMatch(const void *key1, const void *key2,
* This actually implements SQL's notion of "not distinct". Two nulls * This actually implements SQL's notion of "not distinct". Two nulls
* match, a null and a not-null don't match. * match, a null and a not-null don't match.
* *
* tuple1, tuple2: the tuples to compare * slot1, slot2: the tuples to compare (must have same columns!)
* tupdesc: tuple descriptor applying to both tuples
* numCols: the number of attributes to be examined * numCols: the number of attributes to be examined
* matchColIdx: array of attribute column numbers * matchColIdx: array of attribute column numbers
* eqFunctions: array of fmgr lookup info for the equality functions to use * 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, ...@@ -51,9 +50,8 @@ static int TupleHashTableMatch(const void *key1, const void *key2,
* NB: evalContext is reset each time! * NB: evalContext is reset each time!
*/ */
bool bool
execTuplesMatch(HeapTuple tuple1, execTuplesMatch(TupleTableSlot *slot1,
HeapTuple tuple2, TupleTableSlot *slot2,
TupleDesc tupdesc,
int numCols, int numCols,
AttrNumber *matchColIdx, AttrNumber *matchColIdx,
FmgrInfo *eqfunctions, FmgrInfo *eqfunctions,
...@@ -84,15 +82,9 @@ execTuplesMatch(HeapTuple tuple1, ...@@ -84,15 +82,9 @@ execTuplesMatch(HeapTuple tuple1,
bool isNull1, bool isNull1,
isNull2; isNull2;
attr1 = heap_getattr(tuple1, attr1 = slot_getattr(slot1, att, &isNull1);
att,
tupdesc,
&isNull1);
attr2 = heap_getattr(tuple2, attr2 = slot_getattr(slot2, att, &isNull2);
att,
tupdesc,
&isNull2);
if (isNull1 != isNull2) if (isNull1 != isNull2)
{ {
...@@ -129,9 +121,8 @@ execTuplesMatch(HeapTuple tuple1, ...@@ -129,9 +121,8 @@ execTuplesMatch(HeapTuple tuple1,
* Parameters are identical to execTuplesMatch. * Parameters are identical to execTuplesMatch.
*/ */
bool bool
execTuplesUnequal(HeapTuple tuple1, execTuplesUnequal(TupleTableSlot *slot1,
HeapTuple tuple2, TupleTableSlot *slot2,
TupleDesc tupdesc,
int numCols, int numCols,
AttrNumber *matchColIdx, AttrNumber *matchColIdx,
FmgrInfo *eqfunctions, FmgrInfo *eqfunctions,
...@@ -162,18 +153,12 @@ execTuplesUnequal(HeapTuple tuple1, ...@@ -162,18 +153,12 @@ execTuplesUnequal(HeapTuple tuple1,
bool isNull1, bool isNull1,
isNull2; isNull2;
attr1 = heap_getattr(tuple1, attr1 = slot_getattr(slot1, att, &isNull1);
att,
tupdesc,
&isNull1);
if (isNull1) if (isNull1)
continue; /* can't prove anything here */ continue; /* can't prove anything here */
attr2 = heap_getattr(tuple2, attr2 = slot_getattr(slot2, att, &isNull2);
att,
tupdesc,
&isNull2);
if (isNull2) if (isNull2)
continue; /* can't prove anything here */ continue; /* can't prove anything here */
...@@ -312,6 +297,8 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, ...@@ -312,6 +297,8 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
hashtable->tablecxt = tablecxt; hashtable->tablecxt = tablecxt;
hashtable->tempcxt = tempcxt; hashtable->tempcxt = tempcxt;
hashtable->entrysize = entrysize; hashtable->entrysize = entrysize;
hashtable->tableslot = NULL; /* will be made on first lookup */
hashtable->inputslot = NULL;
MemSet(&hash_ctl, 0, sizeof(hash_ctl)); MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(TupleHashEntryData); hash_ctl.keysize = sizeof(TupleHashEntryData);
...@@ -342,13 +329,27 @@ TupleHashEntry ...@@ -342,13 +329,27 @@ TupleHashEntry
LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
bool *isnew) bool *isnew)
{ {
HeapTuple tuple = slot->val;
TupleDesc tupdesc = slot->ttc_tupleDescriptor;
TupleHashEntry entry; TupleHashEntry entry;
MemoryContext oldContext; MemoryContext oldContext;
TupleHashTable saveCurHT; TupleHashTable saveCurHT;
TupleHashEntryData dummy;
bool found; 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 */ /* Need to run the hash functions in short-lived context */
oldContext = MemoryContextSwitchTo(hashtable->tempcxt); oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
...@@ -358,13 +359,14 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, ...@@ -358,13 +359,14 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
* We save and restore CurTupleHashTable just in case someone manages to * We save and restore CurTupleHashTable just in case someone manages to
* invoke this code re-entrantly. * invoke this code re-entrantly.
*/ */
hashtable->tupdesc = tupdesc; hashtable->inputslot = slot;
saveCurHT = CurTupleHashTable; saveCurHT = CurTupleHashTable;
CurTupleHashTable = hashtable; CurTupleHashTable = hashtable;
/* Search the hash table */ /* Search the hash table */
dummy.firstTuple = NULL; /* flag to reference inputslot */
entry = (TupleHashEntry) hash_search(hashtable->hashtab, entry = (TupleHashEntry) hash_search(hashtable->hashtab,
&tuple, &dummy,
isnew ? HASH_ENTER : HASH_FIND, isnew ? HASH_ENTER : HASH_FIND,
&found); &found);
...@@ -392,7 +394,7 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, ...@@ -392,7 +394,7 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
/* Copy the first tuple into the table context */ /* Copy the first tuple into the table context */
MemoryContextSwitchTo(hashtable->tablecxt); MemoryContextSwitchTo(hashtable->tablecxt);
entry->firstTuple = heap_copytuple(tuple); entry->firstTuple = ExecCopySlotTuple(slot);
*isnew = true; *isnew = true;
} }
...@@ -408,9 +410,12 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, ...@@ -408,9 +410,12 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
/* /*
* Compute the hash value for a tuple * Compute the hash value for a tuple
* *
* The passed-in key is a pointer to a HeapTuple pointer -- this is either * The passed-in key is a pointer to TupleHashEntryData. In an actual
* the firstTuple field of a TupleHashEntry struct, or the key value passed * hash table entry, the firstTuple field therein points to a physical
* to hash_search. We ignore the keysize. * 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 * CurTupleHashTable must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise. * doesn't provide any API that would let us get at the hashtable otherwise.
...@@ -421,14 +426,27 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, ...@@ -421,14 +426,27 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
static uint32 static uint32
TupleHashTableHash(const void *key, Size keysize) TupleHashTableHash(const void *key, Size keysize)
{ {
HeapTuple tuple = *(const HeapTuple *) key; HeapTuple tuple = ((const TupleHashEntryData *) key)->firstTuple;
TupleTableSlot *slot;
TupleHashTable hashtable = CurTupleHashTable; TupleHashTable hashtable = CurTupleHashTable;
int numCols = hashtable->numCols; int numCols = hashtable->numCols;
AttrNumber *keyColIdx = hashtable->keyColIdx; AttrNumber *keyColIdx = hashtable->keyColIdx;
TupleDesc tupdesc = hashtable->tupdesc;
uint32 hashkey = 0; uint32 hashkey = 0;
int i; 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++) for (i = 0; i < numCols; i++)
{ {
AttrNumber att = keyColIdx[i]; AttrNumber att = keyColIdx[i];
...@@ -438,7 +456,7 @@ TupleHashTableHash(const void *key, Size keysize) ...@@ -438,7 +456,7 @@ TupleHashTableHash(const void *key, Size keysize)
/* rotate hashkey left 1 bit at each step */ /* rotate hashkey left 1 bit at each step */
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0); 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 */ if (!isNull) /* treat nulls as having hash key 0 */
{ {
...@@ -456,7 +474,7 @@ TupleHashTableHash(const void *key, Size keysize) ...@@ -456,7 +474,7 @@ TupleHashTableHash(const void *key, Size keysize)
/* /*
* See whether two tuples (presumably of the same hash value) match * 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 * CurTupleHashTable must be set before calling this, since dynahash.c
* doesn't provide any API that would let us get at the hashtable otherwise. * doesn't provide any API that would let us get at the hashtable otherwise.
...@@ -467,13 +485,28 @@ TupleHashTableHash(const void *key, Size keysize) ...@@ -467,13 +485,28 @@ TupleHashTableHash(const void *key, Size keysize)
static int static int
TupleHashTableMatch(const void *key1, const void *key2, Size keysize) TupleHashTableMatch(const void *key1, const void *key2, Size keysize)
{ {
HeapTuple tuple1 = *(const HeapTuple *) key1; HeapTuple tuple1 = ((const TupleHashEntryData *) key1)->firstTuple;
HeapTuple tuple2 = *(const HeapTuple *) key2; #ifdef USE_ASSERT_CHECKING
HeapTuple tuple2 = ((const TupleHashEntryData *) key2)->firstTuple;
#endif
TupleTableSlot *slot1;
TupleTableSlot *slot2;
TupleHashTable hashtable = CurTupleHashTable; TupleHashTable hashtable = CurTupleHashTable;
if (execTuplesMatch(tuple1, /*
tuple2, * We assume that dynahash.c will only ever call us with the first
hashtable->tupdesc, * 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->numCols,
hashtable->keyColIdx, hashtable->keyColIdx,
hashtable->eqfunctions, hashtable->eqfunctions,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -42,10 +42,10 @@
* We then execute the plan ignoring the "resjunk" attributes. * We then execute the plan ignoring the "resjunk" attributes.
* *
* Finally, when at the top level we get back a tuple, we can call * Finally, when at the top level we get back a tuple, we can call
* 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we * ExecGetJunkAttribute to retrieve the value of the junk attributes we
* are interested in, and 'ExecRemoveJunk' to remove all the junk attributes * are interested in, and ExecFilterJunk or ExecRemoveJunk to remove all
* from a tuple. This new "clean" tuple is then printed, replaced, deleted * the junk attributes from a tuple. This new "clean" tuple is then printed,
* or inserted. * replaced, deleted or inserted.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -75,6 +75,14 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) ...@@ -75,6 +75,14 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
*/ */
cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); 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 * Now calculate the mapping between the original tuple's attributes and
* the "clean" tuple's attributes. * the "clean" tuple's attributes.
...@@ -115,9 +123,6 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) ...@@ -115,9 +123,6 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
junkfilter->jf_cleanMap = cleanMap; junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot; junkfilter->jf_resultSlot = slot;
if (slot)
ExecSetSlotDescriptor(slot, cleanTupType, false);
return junkfilter; return junkfilter;
} }
...@@ -142,6 +147,14 @@ ExecInitJunkFilterConversion(List *targetList, ...@@ -142,6 +147,14 @@ ExecInitJunkFilterConversion(List *targetList,
ListCell *t; ListCell *t;
int i; 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 * Calculate the mapping between the original tuple's attributes and
* the "clean" tuple's attributes. * the "clean" tuple's attributes.
...@@ -188,9 +201,6 @@ ExecInitJunkFilterConversion(List *targetList, ...@@ -188,9 +201,6 @@ ExecInitJunkFilterConversion(List *targetList,
junkfilter->jf_cleanMap = cleanMap; junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot; junkfilter->jf_resultSlot = slot;
if (slot)
ExecSetSlotDescriptor(slot, cleanTupType, false);
return junkfilter; return junkfilter;
} }
...@@ -234,115 +244,78 @@ ExecGetJunkAttribute(JunkFilter *junkfilter, ...@@ -234,115 +244,78 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
} }
/* /*
* ExecRemoveJunk * ExecFilterJunk
* *
* Construct and return a tuple with all the junk attributes removed. * Construct and return a slot 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.
*/ */
HeapTuple TupleTableSlot *
ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
{ {
#define PREALLOC_SIZE 64 TupleTableSlot *resultSlot;
HeapTuple tuple;
HeapTuple cleanTuple;
AttrNumber *cleanMap; AttrNumber *cleanMap;
TupleDesc cleanTupType; TupleDesc cleanTupType;
TupleDesc tupType;
int cleanLength; int cleanLength;
int oldLength;
int i; int i;
Datum *values; Datum *values;
char *nulls; bool *isnull;
Datum *old_values; Datum *old_values;
char *old_nulls; bool *old_isnull;
Datum values_array[PREALLOC_SIZE];
Datum old_values_array[PREALLOC_SIZE];
char nulls_array[PREALLOC_SIZE];
char old_nulls_array[PREALLOC_SIZE];
/* /*
* get info from the slot and the junk filter * Extract all the values of the old tuple.
*/ */
tuple = slot->val; slot_getallattrs(slot);
tupType = slot->ttc_tupleDescriptor; old_values = slot->tts_values;
oldLength = tupType->natts + 1; /* +1 for NULL */ old_isnull = slot->tts_isnull;
/*
* get info from the junk filter
*/
cleanTupType = junkfilter->jf_cleanTupType; cleanTupType = junkfilter->jf_cleanTupType;
cleanLength = cleanTupType->natts; cleanLength = cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap; cleanMap = junkfilter->jf_cleanMap;
resultSlot = junkfilter->jf_resultSlot;
/* /*
* Create the arrays that will hold the attribute values and the null * Prepare to build a virtual result tuple.
* 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.
*/ */
if (cleanLength > PREALLOC_SIZE) ExecClearTuple(resultSlot);
{ values = resultSlot->tts_values;
values = (Datum *) palloc(cleanLength * sizeof(Datum)); isnull = resultSlot->tts_isnull;
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';
/* /*
* Transpose into proper fields of the new tuple. * Transpose data into proper fields of the new tuple.
*/ */
for (i = 0; i < cleanLength; i++) for (i = 0; i < cleanLength; i++)
{ {
int j = cleanMap[i]; int j = cleanMap[i];
values[i] = old_values[j]; if (j == 0)
nulls[i] = old_nulls[j]; {
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) return ExecStoreVirtualTuple(resultSlot);
{ }
pfree(values);
pfree(nulls);
}
if (old_values != old_values_array)
{
pfree(old_values);
pfree(old_nulls);
}
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));
} }
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * 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: ; ...@@ -1082,7 +1082,6 @@ lnext: ;
if ((junkfilter = estate->es_junkFilter) != NULL) if ((junkfilter = estate->es_junkFilter) != NULL)
{ {
Datum datum; Datum datum;
HeapTuple newTuple;
bool isNull; bool isNull;
/* /*
...@@ -1180,13 +1179,7 @@ lnext: ; ...@@ -1180,13 +1179,7 @@ lnext: ;
* Finally create a new "clean" tuple with all junk attributes * Finally create a new "clean" tuple with all junk attributes
* removed * removed
*/ */
newTuple = ExecRemoveJunk(junkfilter, slot); slot = ExecFilterJunk(junkfilter, slot);
slot = ExecStoreTuple(newTuple, /* tuple to store */
junkfilter->jf_resultSlot, /* dest slot */
InvalidBuffer, /* this tuple has no
* buffer */
true); /* tuple should be pfreed */
} }
/* /*
...@@ -1276,15 +1269,6 @@ ExecSelect(TupleTableSlot *slot, ...@@ -1276,15 +1269,6 @@ ExecSelect(TupleTableSlot *slot,
DestReceiver *dest, DestReceiver *dest,
EState *estate) 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" * insert the tuple into the "into relation"
* *
...@@ -1292,15 +1276,20 @@ ExecSelect(TupleTableSlot *slot, ...@@ -1292,15 +1276,20 @@ ExecSelect(TupleTableSlot *slot,
*/ */
if (estate->es_into_relation_descriptor != NULL) if (estate->es_into_relation_descriptor != NULL)
{ {
HeapTuple tuple;
tuple = ExecCopySlotTuple(slot);
heap_insert(estate->es_into_relation_descriptor, tuple, heap_insert(estate->es_into_relation_descriptor, tuple,
estate->es_snapshot->curcid); estate->es_snapshot->curcid);
/* we know there are no indexes to update */
heap_freetuple(tuple);
IncrAppended(); IncrAppended();
} }
/* /*
* send the tuple to the destination * send the tuple to the destination
*/ */
(*dest->receiveTuple) (tuple, attrtype, dest); (*dest->receiveSlot) (slot, dest);
IncrRetrieved(); IncrRetrieved();
(estate->es_processed)++; (estate->es_processed)++;
} }
...@@ -1325,9 +1314,10 @@ ExecInsert(TupleTableSlot *slot, ...@@ -1325,9 +1314,10 @@ ExecInsert(TupleTableSlot *slot,
Oid newId; 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 * get information on the (current) result relation
...@@ -1520,9 +1510,10 @@ ExecUpdate(TupleTableSlot *slot, ...@@ -1520,9 +1510,10 @@ ExecUpdate(TupleTableSlot *slot,
elog(ERROR, "cannot UPDATE during bootstrap"); 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 * get information on the (current) result relation
...@@ -1604,10 +1595,8 @@ lreplace:; ...@@ -1604,10 +1595,8 @@ lreplace:;
if (!TupIsNull(epqslot)) if (!TupIsNull(epqslot))
{ {
*tupleid = ctid; *tupleid = ctid;
tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot); slot = ExecFilterJunk(estate->es_junkFilter, epqslot);
slot = ExecStoreTuple(tuple, tuple = ExecMaterializeSlot(slot);
estate->es_junkFilter->jf_resultSlot,
InvalidBuffer, true);
goto lreplace; goto lreplace;
} }
} }
...@@ -1712,7 +1701,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1712,7 +1701,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate) TupleTableSlot *slot, EState *estate)
{ {
Relation rel = resultRelInfo->ri_RelationDesc; Relation rel = resultRelInfo->ri_RelationDesc;
HeapTuple tuple = slot->val;
TupleConstr *constr = rel->rd_att->constr; TupleConstr *constr = rel->rd_att->constr;
Assert(constr); Assert(constr);
...@@ -1725,7 +1713,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1725,7 +1713,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
for (attrChk = 1; attrChk <= natts; attrChk++) for (attrChk = 1; attrChk <= natts; attrChk++)
{ {
if (rel->rd_att->attrs[attrChk - 1]->attnotnull && if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
heap_attisnull(tuple, attrChk)) slot_attisnull(slot, attrChk))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION), (errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("null value in column \"%s\" violates not-null constraint", errmsg("null value in column \"%s\" violates not-null constraint",
......
This diff is collapsed.
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -106,10 +106,7 @@ ExecScan(ScanState *node,
if (TupIsNull(slot)) if (TupIsNull(slot))
{ {
if (projInfo) if (projInfo)
return ExecStoreTuple(NULL, return ExecClearTuple(projInfo->pi_slot);
projInfo->pi_slot,
InvalidBuffer,
true);
else else
return slot; return slot;
} }
...@@ -183,7 +180,7 @@ ExecAssignScanProjectionInfo(ScanState *node) ...@@ -183,7 +180,7 @@ ExecAssignScanProjectionInfo(ScanState *node)
if (tlist_matches_tupdesc(&node->ps, if (tlist_matches_tupdesc(&node->ps,
scan->plan.targetlist, scan->plan.targetlist,
scan->scanrelid, scan->scanrelid,
node->ss_ScanTupleSlot->ttc_tupleDescriptor)) node->ss_ScanTupleSlot->tts_tupleDescriptor))
node->ps.ps_ProjInfo = NULL; node->ps.ps_ProjInfo = NULL;
else else
ExecAssignProjectionInfo(&node->ps); ExecAssignProjectionInfo(&node->ps);
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -485,7 +485,7 @@ ExecGetResultType(PlanState *planstate)
{ {
TupleTableSlot *slot = planstate->ps_ResultTupleSlot; TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
return slot->ttc_tupleDescriptor; return slot->tts_tupleDescriptor;
} }
/* ---------------- /* ----------------
...@@ -504,17 +504,99 @@ ExecBuildProjectionInfo(List *targetList, ...@@ -504,17 +504,99 @@ ExecBuildProjectionInfo(List *targetList,
{ {
ProjectionInfo *projInfo = makeNode(ProjectionInfo); ProjectionInfo *projInfo = makeNode(ProjectionInfo);
int len; int len;
bool isVarList;
ListCell *tl;
len = ExecTargetListLength(targetList); len = ExecTargetListLength(targetList);
projInfo->pi_targetlist = targetList; projInfo->pi_targetlist = targetList;
projInfo->pi_exprContext = econtext; projInfo->pi_exprContext = econtext;
projInfo->pi_slot = slot; 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_itemIsDone = (ExprDoneCond *)
projInfo->pi_tupNulls = (char *) palloc(len * sizeof(char)); palloc(len * sizeof(ExprDoneCond));
projInfo->pi_itemIsDone = (ExprDoneCond *) palloc(len * sizeof(ExprDoneCond)); projInfo->pi_varSlotOffsets = NULL;
projInfo->pi_varNumbers = NULL;
} }
return projInfo; return projInfo;
...@@ -582,7 +664,7 @@ ExecGetScanType(ScanState *scanstate) ...@@ -582,7 +664,7 @@ ExecGetScanType(ScanState *scanstate)
{ {
TupleTableSlot *slot = scanstate->ss_ScanTupleSlot; TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
return slot->ttc_tupleDescriptor; return slot->tts_tupleDescriptor;
} }
/* ---------------- /* ----------------
...@@ -772,20 +854,16 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -772,20 +854,16 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
EState *estate, EState *estate,
bool is_vacuum) bool is_vacuum)
{ {
HeapTuple heapTuple;
ResultRelInfo *resultRelInfo; ResultRelInfo *resultRelInfo;
int i; int i;
int numIndices; int numIndices;
RelationPtr relationDescs; RelationPtr relationDescs;
Relation heapRelation; Relation heapRelation;
TupleDesc heapDescriptor;
IndexInfo **indexInfoArray; IndexInfo **indexInfoArray;
ExprContext *econtext; ExprContext *econtext;
Datum datum[INDEX_MAX_KEYS]; Datum datum[INDEX_MAX_KEYS];
char nullv[INDEX_MAX_KEYS]; char nullv[INDEX_MAX_KEYS];
heapTuple = slot->val;
/* /*
* Get information from the result relation info structure. * Get information from the result relation info structure.
*/ */
...@@ -794,7 +872,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -794,7 +872,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
relationDescs = resultRelInfo->ri_IndexRelationDescs; relationDescs = resultRelInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelInfo->ri_IndexRelationInfo; indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
heapRelation = resultRelInfo->ri_RelationDesc; heapRelation = resultRelInfo->ri_RelationDesc;
heapDescriptor = RelationGetDescr(heapRelation);
/* /*
* We will use the EState's per-tuple context for evaluating * We will use the EState's per-tuple context for evaluating
...@@ -844,12 +921,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -844,12 +921,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
/* /*
* FormIndexDatum fills in its datum and null parameters with * 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. * computes any expressions needed.
*/ */
FormIndexDatum(indexInfo, FormIndexDatum(indexInfo,
heapTuple, slot,
heapDescriptor,
estate, estate,
datum, datum,
nullv); nullv);
...@@ -860,9 +936,9 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -860,9 +936,9 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
* need to move dead tuples that have the same keys as live ones. * need to move dead tuples that have the same keys as live ones.
*/ */
result = index_insert(relationDescs[i], /* index relation */ result = index_insert(relationDescs[i], /* index relation */
datum, /* array of heaptuple Datums */ datum, /* array of index Datums */
nullv, /* info on nulls */ nullv, /* info on nulls */
&(heapTuple->t_self), /* tid of heap tuple */ tupleid, /* tid of heap tuple */
heapRelation, heapRelation,
relationDescs[i]->rd_index->indisunique && !is_vacuum); relationDescs[i]->rd_index->indisunique && !is_vacuum);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -456,8 +456,6 @@ postquel_execute(execution_state *es,
SQLFunctionCachePtr fcache) SQLFunctionCachePtr fcache)
{ {
TupleTableSlot *slot; TupleTableSlot *slot;
HeapTuple tup;
TupleDesc tupDesc;
Datum value; Datum value;
if (es->status == F_EXEC_START) if (es->status == F_EXEC_START)
...@@ -512,7 +510,7 @@ postquel_execute(execution_state *es, ...@@ -512,7 +510,7 @@ postquel_execute(execution_state *es,
/* /*
* Compress out the HeapTuple header data. We assume that * 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 * palloc'd chunk. We want to return a pointer to the chunk
* start so that it will work if someone tries to free it. * start so that it will work if someone tries to free it.
*/ */
...@@ -534,7 +532,8 @@ postquel_execute(execution_state *es, ...@@ -534,7 +532,8 @@ postquel_execute(execution_state *es,
else else
{ {
/* function is declared to return RECORD */ /* function is declared to return RECORD */
tupDesc = fcache->junkFilter->jf_cleanTupType; TupleDesc tupDesc = fcache->junkFilter->jf_cleanTupType;
if (tupDesc->tdtypeid == RECORDOID && if (tupDesc->tdtypeid == RECORDOID &&
tupDesc->tdtypmod < 0) tupDesc->tdtypmod < 0)
assign_record_type_typmod(tupDesc); assign_record_type_typmod(tupDesc);
...@@ -556,10 +555,7 @@ postquel_execute(execution_state *es, ...@@ -556,10 +555,7 @@ postquel_execute(execution_state *es,
* column of the SELECT result, and then copy into current * column of the SELECT result, and then copy into current
* execution context if needed. * execution context if needed.
*/ */
tup = slot->val; value = slot_getattr(slot, 1, &(fcinfo->isnull));
tupDesc = slot->ttc_tupleDescriptor;
value = heap_getattr(tup, 1, tupDesc, &(fcinfo->isnull));
if (!fcinfo->isnull) if (!fcinfo->isnull)
value = datumCopy(value, fcache->typbyval, fcache->typlen); value = datumCopy(value, fcache->typbyval, fcache->typlen);
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -748,7 +748,7 @@ agg_retrieve_direct(AggState *aggstate)
* Make a copy of the first input tuple; we will use this * Make a copy of the first input tuple; we will use this
* for comparisons (in group mode) and for projection. * for comparisons (in group mode) and for projection.
*/ */
aggstate->grp_firstTuple = heap_copytuple(outerslot->val); aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
} }
else else
{ {
...@@ -813,9 +813,8 @@ agg_retrieve_direct(AggState *aggstate) ...@@ -813,9 +813,8 @@ agg_retrieve_direct(AggState *aggstate)
*/ */
if (node->aggstrategy == AGG_SORTED) if (node->aggstrategy == AGG_SORTED)
{ {
if (!execTuplesMatch(firstSlot->val, if (!execTuplesMatch(firstSlot,
outerslot->val, outerslot,
firstSlot->ttc_tupleDescriptor,
node->numCols, node->grpColIdx, node->numCols, node->grpColIdx,
aggstate->eqfunctions, aggstate->eqfunctions,
tmpcontext->ecxt_per_tuple_memory)) tmpcontext->ecxt_per_tuple_memory))
...@@ -823,7 +822,7 @@ agg_retrieve_direct(AggState *aggstate) ...@@ -823,7 +822,7 @@ agg_retrieve_direct(AggState *aggstate)
/* /*
* Save the first input tuple of the next group. * Save the first input tuple of the next group.
*/ */
aggstate->grp_firstTuple = heap_copytuple(outerslot->val); aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
break; break;
} }
} }
...@@ -863,31 +862,11 @@ agg_retrieve_direct(AggState *aggstate) ...@@ -863,31 +862,11 @@ agg_retrieve_direct(AggState *aggstate)
*/ */
if (TupIsNull(firstSlot)) if (TupIsNull(firstSlot))
{ {
TupleDesc tupType;
/* Should only happen in non-grouped mode */ /* Should only happen in non-grouped mode */
Assert(node->aggstrategy == AGG_PLAIN); Assert(node->aggstrategy == AGG_PLAIN);
Assert(aggstate->agg_done); Assert(aggstate->agg_done);
tupType = firstSlot->ttc_tupleDescriptor; ExecStoreAllNullTuple(firstSlot);
/* 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);
}
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -99,7 +99,10 @@ FunctionNext(FunctionScanState *node)
ScanDirectionIsForward(direction), ScanDirectionIsForward(direction),
&should_free); &should_free);
slot = node->ss.ss_ScanTupleSlot; 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 @@ ...@@ -15,7 +15,7 @@
* locate group boundaries. * locate group boundaries.
* *
* IDENTIFICATION * 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 * ...@@ -36,11 +36,9 @@ TupleTableSlot *
ExecGroup(GroupState *node) ExecGroup(GroupState *node)
{ {
ExprContext *econtext; ExprContext *econtext;
TupleDesc tupdesc;
int numCols; int numCols;
AttrNumber *grpColIdx; AttrNumber *grpColIdx;
HeapTuple outerTuple; TupleTableSlot *firsttupleslot;
HeapTuple firsttuple;
TupleTableSlot *outerslot; TupleTableSlot *outerslot;
/* /*
...@@ -49,10 +47,14 @@ ExecGroup(GroupState *node) ...@@ -49,10 +47,14 @@ ExecGroup(GroupState *node)
if (node->grp_done) if (node->grp_done)
return NULL; return NULL;
econtext = node->ss.ps.ps_ExprContext; econtext = node->ss.ps.ps_ExprContext;
tupdesc = ExecGetScanType(&node->ss);
numCols = ((Group *) node->ss.ps.plan)->numCols; numCols = ((Group *) node->ss.ps.plan)->numCols;
grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx; 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 * We need not call ResetExprContext here because execTuplesMatch will
* reset the per-tuple memory context once per input tuple. * reset the per-tuple memory context once per input tuple.
...@@ -62,8 +64,7 @@ ExecGroup(GroupState *node) ...@@ -62,8 +64,7 @@ ExecGroup(GroupState *node)
* If first time through, acquire first input tuple and determine * If first time through, acquire first input tuple and determine
* whether to return it or not. * whether to return it or not.
*/ */
firsttuple = node->grp_firstTuple; if (TupIsNull(firsttupleslot))
if (firsttuple == NULL)
{ {
outerslot = ExecProcNode(outerPlanState(node)); outerslot = ExecProcNode(outerPlanState(node));
if (TupIsNull(outerslot)) if (TupIsNull(outerslot))
...@@ -72,13 +73,9 @@ ExecGroup(GroupState *node) ...@@ -72,13 +73,9 @@ ExecGroup(GroupState *node)
node->grp_done = TRUE; node->grp_done = TRUE;
return NULL; return NULL;
} }
node->grp_firstTuple = firsttuple = heap_copytuple(outerslot->val); /* Copy tuple, set up as input for qual test and projection */
/* Set up tuple as input for qual test and projection */ ExecCopySlot(firsttupleslot, outerslot);
ExecStoreTuple(firsttuple, econtext->ecxt_scantuple = firsttupleslot;
node->ss.ss_ScanTupleSlot,
InvalidBuffer,
false);
econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
/* /*
* Check the qual (HAVING clause); if the group does not match, * Check the qual (HAVING clause); if the group does not match,
* ignore it and fall into scan loop. * ignore it and fall into scan loop.
...@@ -112,14 +109,12 @@ ExecGroup(GroupState *node) ...@@ -112,14 +109,12 @@ ExecGroup(GroupState *node)
node->grp_done = TRUE; node->grp_done = TRUE;
return NULL; return NULL;
} }
outerTuple = outerslot->val;
/* /*
* Compare with first tuple and see if this tuple is of the same * Compare with first tuple and see if this tuple is of the same
* group. If so, ignore it and keep scanning. * group. If so, ignore it and keep scanning.
*/ */
if (!execTuplesMatch(firsttuple, outerTuple, if (!execTuplesMatch(firsttupleslot, outerslot,
tupdesc,
numCols, grpColIdx, numCols, grpColIdx,
node->eqfunctions, node->eqfunctions,
econtext->ecxt_per_tuple_memory)) econtext->ecxt_per_tuple_memory))
...@@ -129,14 +124,9 @@ ExecGroup(GroupState *node) ...@@ -129,14 +124,9 @@ ExecGroup(GroupState *node)
* We have the first tuple of the next input group. See if we * We have the first tuple of the next input group. See if we
* want to return it. * want to return it.
*/ */
heap_freetuple(firsttuple); /* Copy tuple, set up as input for qual test and projection */
node->grp_firstTuple = firsttuple = heap_copytuple(outerTuple); ExecCopySlot(firsttupleslot, outerslot);
/* Set up tuple as input for qual test and projection */ econtext->ecxt_scantuple = firsttupleslot;
ExecStoreTuple(firsttuple,
node->ss.ss_ScanTupleSlot,
InvalidBuffer,
false);
econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
/* /*
* Check the qual (HAVING clause); if the group does not match, * Check the qual (HAVING clause); if the group does not match,
* ignore it and loop back to scan the rest of the group. * ignore it and loop back to scan the rest of the group.
...@@ -173,7 +163,6 @@ ExecInitGroup(Group *node, EState *estate) ...@@ -173,7 +163,6 @@ ExecInitGroup(Group *node, EState *estate)
grpstate = makeNode(GroupState); grpstate = makeNode(GroupState);
grpstate->ss.ps.plan = (Plan *) node; grpstate->ss.ps.plan = (Plan *) node;
grpstate->ss.ps.state = estate; grpstate->ss.ps.state = estate;
grpstate->grp_firstTuple = NULL;
grpstate->grp_done = FALSE; grpstate->grp_done = FALSE;
/* /*
...@@ -255,11 +244,8 @@ void ...@@ -255,11 +244,8 @@ void
ExecReScanGroup(GroupState *node, ExprContext *exprCtxt) ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
{ {
node->grp_done = FALSE; node->grp_done = FALSE;
if (node->grp_firstTuple != NULL) /* must clear first tuple */
{ ExecClearTuple(node->ss.ss_ScanTupleSlot);
heap_freetuple(node->grp_firstTuple);
node->grp_firstTuple = NULL;
}
if (((PlanState *) node)->lefttree && if (((PlanState *) node)->lefttree &&
((PlanState *) node)->lefttree->chgParam == NULL) ((PlanState *) node)->lefttree->chgParam == NULL)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -75,7 +75,7 @@ ExecHash(HashState *node)
/* We have to compute the hash value */ /* We have to compute the hash value */
econtext->ecxt_innertuple = slot; econtext->ecxt_innertuple = slot;
hashvalue = ExecHashGetHashValue(hashtable, econtext, hashkeys); hashvalue = ExecHashGetHashValue(hashtable, econtext, hashkeys);
ExecHashTableInsert(hashtable, slot->val, hashvalue); ExecHashTableInsert(hashtable, ExecFetchSlotTuple(slot), hashvalue);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -188,7 +188,8 @@ ExecHashJoin(HashJoinState *node)
* Save it in the corresponding outer-batch file. * Save it in the corresponding outer-batch file.
*/ */
Assert(batchno > hashtable->curbatch); Assert(batchno > hashtable->curbatch);
ExecHashJoinSaveTuple(outerTupleSlot->val, hashvalue, ExecHashJoinSaveTuple(ExecFetchSlotTuple(outerTupleSlot),
hashvalue,
&hashtable->outerBatchFile[batchno]); &hashtable->outerBatchFile[batchno]);
node->hj_NeedNewOuter = true; node->hj_NeedNewOuter = true;
continue; /* loop around for a new outer tuple */ continue; /* loop around for a new outer tuple */
...@@ -652,7 +653,9 @@ start_over: ...@@ -652,7 +653,9 @@ start_over:
* NOTE: some tuples may be sent to future batches. Also, * NOTE: some tuples may be sent to future batches. Also,
* it is possible for hashtable->nbatch to be increased here! * it is possible for hashtable->nbatch to be increased here!
*/ */
ExecHashTableInsert(hashtable, slot->val, hashvalue); ExecHashTableInsert(hashtable,
ExecFetchSlotTuple(slot),
hashvalue);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -165,7 +165,7 @@ IndexNext(IndexScanState *node)
break; break;
} }
if (qual == NULL) /* would not be returned by indices */ if (qual == NULL) /* would not be returned by indices */
slot->val = NULL; ExecClearTuple(slot);
/* Flag for the next call that no more tuples */ /* Flag for the next call that no more tuples */
estate->es_evTupleNull[scanrelid - 1] = true; estate->es_evTupleNull[scanrelid - 1] = true;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 */ ...@@ -38,7 +38,6 @@ TupleTableSlot * /* return: a tuple or NULL */
ExecLimit(LimitState *node) ExecLimit(LimitState *node)
{ {
ScanDirection direction; ScanDirection direction;
TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot; TupleTableSlot *slot;
PlanState *outerPlan; PlanState *outerPlan;
...@@ -47,7 +46,6 @@ ExecLimit(LimitState *node) ...@@ -47,7 +46,6 @@ ExecLimit(LimitState *node)
*/ */
direction = node->ps.state->es_direction; direction = node->ps.state->es_direction;
outerPlan = outerPlanState(node); outerPlan = outerPlanState(node);
resultTupleSlot = node->ps.ps_ResultTupleSlot;
/* /*
* The main logic is a simple state machine. * The main logic is a simple state machine.
...@@ -219,12 +217,7 @@ ExecLimit(LimitState *node) ...@@ -219,12 +217,7 @@ ExecLimit(LimitState *node)
/* Return the current tuple */ /* Return the current tuple */
Assert(!TupIsNull(slot)); Assert(!TupIsNull(slot));
ExecStoreTuple(slot->val, return slot;
resultTupleSlot,
InvalidBuffer,
false); /* tuple does not belong to slot */
return resultTupleSlot;
} }
/* /*
...@@ -324,7 +317,7 @@ ExecInitLimit(Limit *node, EState *estate) ...@@ -324,7 +317,7 @@ ExecInitLimit(Limit *node, EState *estate)
#define LIMIT_NSLOTS 1 #define LIMIT_NSLOTS 1
/* /*
* Tuple table initialization * Tuple table initialization (XXX not actually used...)
*/ */
ExecInitResultTupleSlot(estate, &limitstate->ps); ExecInitResultTupleSlot(estate, &limitstate->ps);
...@@ -363,10 +356,6 @@ void ...@@ -363,10 +356,6 @@ void
ExecEndLimit(LimitState *node) ExecEndLimit(LimitState *node)
{ {
ExecFreeExprContext(&node->ps); ExecFreeExprContext(&node->ps);
/* clean up tuple table */
ExecClearTuple(node->ps.ps_ResultTupleSlot);
ExecEndNode(outerPlanState(node)); ExecEndNode(outerPlanState(node));
} }
...@@ -377,8 +366,6 @@ ExecReScanLimit(LimitState *node, ExprContext *exprCtxt) ...@@ -377,8 +366,6 @@ ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
/* resetting lstate will force offset/limit recalculation */ /* resetting lstate will force offset/limit recalculation */
node->lstate = LIMIT_INITIAL; node->lstate = LIMIT_INITIAL;
ExecClearTuple(node->ps.ps_ResultTupleSlot);
/* /*
* if chgParam of subnode is not null then plan will be re-scanned by * if chgParam of subnode is not null then plan will be re-scanned by
* first ExecProcNode. * first ExecProcNode.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -127,7 +127,7 @@ ExecMaterial(MaterialState *node)
node->eof_underlying = true; node->eof_underlying = true;
return NULL; return NULL;
} }
heapTuple = outerslot->val; heapTuple = ExecFetchSlotTuple(outerslot);
should_free = false; should_free = false;
/* /*
...@@ -139,10 +139,13 @@ ExecMaterial(MaterialState *node) ...@@ -139,10 +139,13 @@ ExecMaterial(MaterialState *node)
} }
/* /*
* Return the obtained tuple. * Return the obtained tuple, if any.
*/ */
slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot; 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 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -80,10 +80,8 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
#define MarkInnerTuple(innerTupleSlot, mergestate) \ #define MarkInnerTuple(innerTupleSlot, mergestate) \
( \ ( \
ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \ ExecCopySlot((mergestate)->mj_MarkedTupleSlot, \
(mergestate)->mj_MarkedTupleSlot, \ (innerTupleSlot)) \
InvalidBuffer, \
true) \
) )
...@@ -246,8 +244,7 @@ ExecMergeTupleDumpOuter(MergeJoinState *mergestate) ...@@ -246,8 +244,7 @@ ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
if (TupIsNull(outerSlot)) if (TupIsNull(outerSlot))
printf("(nil)\n"); printf("(nil)\n");
else else
MJ_debugtup(outerSlot->val, MJ_debugtup(outerSlot);
outerSlot->ttc_tupleDescriptor);
} }
static void static void
...@@ -259,8 +256,7 @@ ExecMergeTupleDumpInner(MergeJoinState *mergestate) ...@@ -259,8 +256,7 @@ ExecMergeTupleDumpInner(MergeJoinState *mergestate)
if (TupIsNull(innerSlot)) if (TupIsNull(innerSlot))
printf("(nil)\n"); printf("(nil)\n");
else else
MJ_debugtup(innerSlot->val, MJ_debugtup(innerSlot);
innerSlot->ttc_tupleDescriptor);
} }
static void static void
...@@ -272,8 +268,7 @@ ExecMergeTupleDumpMarked(MergeJoinState *mergestate) ...@@ -272,8 +268,7 @@ ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
if (TupIsNull(markedSlot)) if (TupIsNull(markedSlot))
printf("(nil)\n"); printf("(nil)\n");
else else
MJ_debugtup(markedSlot->val, MJ_debugtup(markedSlot);
markedSlot->ttc_tupleDescriptor);
} }
static void static void
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -110,12 +110,12 @@ SeqNext(SeqScanState *node)
* refcount of the buffer; the refcount will not be dropped until the * refcount of the buffer; the refcount will not be dropped until the
* tuple table slot is cleared. * tuple table slot is cleared.
*/ */
if (tuple)
slot = ExecStoreTuple(tuple, /* tuple to store */ ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */ slot, /* slot to store in */
scandesc->rs_cbuf, /* buffer associated with scandesc->rs_cbuf, /* buffer associated with
* this tuple */ * this tuple */
false); /* don't pfree this pointer */ false); /* don't pfree this pointer */
return slot; return slot;
} }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -49,14 +49,12 @@ ExecSetOp(SetOpState *node)
SetOp *plannode = (SetOp *) node->ps.plan; SetOp *plannode = (SetOp *) node->ps.plan;
TupleTableSlot *resultTupleSlot; TupleTableSlot *resultTupleSlot;
PlanState *outerPlan; PlanState *outerPlan;
TupleDesc tupDesc;
/* /*
* get information from the node * get information from the node
*/ */
outerPlan = outerPlanState(node); outerPlan = outerPlanState(node);
resultTupleSlot = node->ps.ps_ResultTupleSlot; resultTupleSlot = node->ps.ps_ResultTupleSlot;
tupDesc = ExecGetResultType(&node->ps);
/* /*
* If the previously-returned tuple needs to be returned more than * If the previously-returned tuple needs to be returned more than
...@@ -105,11 +103,7 @@ ExecSetOp(SetOpState *node) ...@@ -105,11 +103,7 @@ ExecSetOp(SetOpState *node)
*/ */
if (node->subplan_done) if (node->subplan_done)
return NULL; /* no more tuples */ return NULL; /* no more tuples */
ExecStoreTuple(heap_copytuple(inputTupleSlot->val), ExecCopySlot(resultTupleSlot, inputTupleSlot);
resultTupleSlot,
InvalidBuffer,
true); /* free copied tuple at
* ExecClearTuple */
node->numLeft = 0; node->numLeft = 0;
node->numRight = 0; node->numRight = 0;
endOfGroup = false; endOfGroup = false;
...@@ -127,9 +121,8 @@ ExecSetOp(SetOpState *node) ...@@ -127,9 +121,8 @@ ExecSetOp(SetOpState *node)
* Else test if the new tuple and the previously saved tuple * Else test if the new tuple and the previously saved tuple
* match. * match.
*/ */
if (execTuplesMatch(inputTupleSlot->val, if (execTuplesMatch(inputTupleSlot,
resultTupleSlot->val, resultTupleSlot,
tupDesc,
plannode->numCols, plannode->dupColIdx, plannode->numCols, plannode->dupColIdx,
node->eqfunctions, node->eqfunctions,
node->tempContext)) node->tempContext))
...@@ -189,9 +182,8 @@ ExecSetOp(SetOpState *node) ...@@ -189,9 +182,8 @@ ExecSetOp(SetOpState *node)
int flag; int flag;
bool isNull; bool isNull;
flag = DatumGetInt32(heap_getattr(inputTupleSlot->val, flag = DatumGetInt32(slot_getattr(inputTupleSlot,
plannode->flagColIdx, plannode->flagColIdx,
tupDesc,
&isNull)); &isNull));
Assert(!isNull); Assert(!isNull);
if (flag) if (flag)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -104,7 +104,8 @@ ExecSort(SortState *node)
if (TupIsNull(slot)) if (TupIsNull(slot))
break; break;
tuplesort_puttuple(tuplesortstate, (void *) slot->val); tuplesort_puttuple(tuplesortstate,
(void *) ExecFetchSlotTuple(slot));
} }
/* /*
...@@ -136,7 +137,10 @@ ExecSort(SortState *node) ...@@ -136,7 +137,10 @@ ExecSort(SortState *node)
&should_free); &should_free);
slot = node->ss.ps.ps_ResultTupleSlot; 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 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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, ...@@ -37,7 +37,8 @@ static Datum ExecScanSubPlan(SubPlanState *node,
bool *isNull); bool *isNull);
static void buildSubPlanHash(SubPlanState *node); static void buildSubPlanHash(SubPlanState *node);
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot); 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, ...@@ -78,7 +79,6 @@ ExecHashSubPlan(SubPlanState *node,
PlanState *planstate = node->planstate; PlanState *planstate = node->planstate;
ExprContext *innerecontext = node->innerecontext; ExprContext *innerecontext = node->innerecontext;
TupleTableSlot *slot; TupleTableSlot *slot;
HeapTuple tup;
/* Shouldn't have any direct correlation Vars */ /* Shouldn't have any direct correlation Vars */
if (subplan->parParam != NIL || node->args != NIL) if (subplan->parParam != NIL || node->args != NIL)
...@@ -105,7 +105,6 @@ ExecHashSubPlan(SubPlanState *node, ...@@ -105,7 +105,6 @@ ExecHashSubPlan(SubPlanState *node,
*/ */
node->projLeft->pi_exprContext = econtext; node->projLeft->pi_exprContext = econtext;
slot = ExecProject(node->projLeft, NULL); slot = ExecProject(node->projLeft, NULL);
tup = slot->val;
/* /*
* Note: because we are typically called in a per-tuple context, we * Note: because we are typically called in a per-tuple context, we
...@@ -137,7 +136,7 @@ ExecHashSubPlan(SubPlanState *node, ...@@ -137,7 +136,7 @@ ExecHashSubPlan(SubPlanState *node,
* comparison we will not even make, unless there's a chance match of * comparison we will not even make, unless there's a chance match of
* hash keys. * hash keys.
*/ */
if (HeapTupleNoNulls(tup)) if (slotNoNulls(slot))
{ {
if (node->havehashrows && if (node->havehashrows &&
LookupTupleHashEntry(node->hashtable, slot, NULL) != NULL) LookupTupleHashEntry(node->hashtable, slot, NULL) != NULL)
...@@ -171,7 +170,7 @@ ExecHashSubPlan(SubPlanState *node, ...@@ -171,7 +170,7 @@ ExecHashSubPlan(SubPlanState *node,
ExecClearTuple(slot); ExecClearTuple(slot);
return BoolGetDatum(false); return BoolGetDatum(false);
} }
if (tupleAllNulls(tup)) if (slotAllNulls(slot))
{ {
ExecClearTuple(slot); ExecClearTuple(slot);
*isNull = true; *isNull = true;
...@@ -271,8 +270,7 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -271,8 +270,7 @@ ExecScanSubPlan(SubPlanState *node,
!TupIsNull(slot); !TupIsNull(slot);
slot = ExecProcNode(planstate)) slot = ExecProcNode(planstate))
{ {
HeapTuple tup = slot->val; TupleDesc tdesc = slot->tts_tupleDescriptor;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
Datum rowresult = BoolGetDatum(!useOr); Datum rowresult = BoolGetDatum(!useOr);
bool rownull = false; bool rownull = false;
int col = 1; int col = 1;
...@@ -303,13 +301,12 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -303,13 +301,12 @@ ExecScanSubPlan(SubPlanState *node,
* copied tuple for eventual freeing. * copied tuple for eventual freeing.
*/ */
MemoryContextSwitchTo(econtext->ecxt_per_query_memory); MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tup = heap_copytuple(tup);
if (node->curTuple) if (node->curTuple)
heap_freetuple(node->curTuple); heap_freetuple(node->curTuple);
node->curTuple = tup; node->curTuple = ExecCopySlotTuple(slot);
MemoryContextSwitchTo(node->sub_estate->es_query_cxt); 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 */ /* keep scanning subplan to make sure there's only one tuple */
continue; continue;
} }
...@@ -321,7 +318,7 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -321,7 +318,7 @@ ExecScanSubPlan(SubPlanState *node,
found = true; found = true;
/* stash away current value */ /* stash away current value */
dvalue = heap_getattr(tup, 1, tdesc, &disnull); dvalue = slot_getattr(slot, 1, &disnull);
astate = accumArrayResult(astate, dvalue, disnull, astate = accumArrayResult(astate, dvalue, disnull,
tdesc->attrs[0]->atttypid, tdesc->attrs[0]->atttypid,
oldcontext); oldcontext);
...@@ -357,7 +354,7 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -357,7 +354,7 @@ ExecScanSubPlan(SubPlanState *node,
*/ */
prmdata = &(econtext->ecxt_param_exec_vals[paramid]); prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
Assert(prmdata->execPlan == NULL); Assert(prmdata->execPlan == NULL);
prmdata->value = heap_getattr(tup, col, tdesc, prmdata->value = slot_getattr(slot, col,
&(prmdata->isnull)); &(prmdata->isnull));
/* /*
...@@ -554,8 +551,6 @@ buildSubPlanHash(SubPlanState *node) ...@@ -554,8 +551,6 @@ buildSubPlanHash(SubPlanState *node)
!TupIsNull(slot); !TupIsNull(slot);
slot = ExecProcNode(planstate)) slot = ExecProcNode(planstate))
{ {
HeapTuple tup = slot->val;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
int col = 1; int col = 1;
ListCell *plst; ListCell *plst;
bool isnew; bool isnew;
...@@ -571,20 +566,16 @@ buildSubPlanHash(SubPlanState *node) ...@@ -571,20 +566,16 @@ buildSubPlanHash(SubPlanState *node)
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]); prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
Assert(prmdata->execPlan == NULL); Assert(prmdata->execPlan == NULL);
prmdata->value = heap_getattr(tup, col, tdesc, prmdata->value = slot_getattr(slot, col,
&(prmdata->isnull)); &(prmdata->isnull));
col++; col++;
} }
slot = ExecProject(node->projRight, NULL); slot = ExecProject(node->projRight, NULL);
tup = slot->val;
/* /*
* If result contains any nulls, store separately or not at all. * 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); (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
node->havehashrows = true; node->havehashrows = true;
...@@ -606,7 +597,8 @@ buildSubPlanHash(SubPlanState *node) ...@@ -606,7 +597,8 @@ buildSubPlanHash(SubPlanState *node)
* Since the projected tuples are in the sub-query's context and not * 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 * 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 * 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); ExecClearTuple(node->projRight->pi_slot);
...@@ -626,17 +618,15 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot) ...@@ -626,17 +618,15 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
{ {
int numCols = hashtable->numCols; int numCols = hashtable->numCols;
AttrNumber *keyColIdx = hashtable->keyColIdx; AttrNumber *keyColIdx = hashtable->keyColIdx;
HeapTuple tuple = slot->val;
TupleDesc tupdesc = slot->ttc_tupleDescriptor;
TupleHashIterator hashiter; TupleHashIterator hashiter;
TupleHashEntry entry; TupleHashEntry entry;
ResetTupleHashIterator(hashtable, &hashiter); ResetTupleHashIterator(hashtable, &hashiter);
while ((entry = ScanTupleHashTable(&hashiter)) != NULL) while ((entry = ScanTupleHashTable(&hashiter)) != NULL)
{ {
if (!execTuplesUnequal(entry->firstTuple, ExecStoreTuple(entry->firstTuple, hashtable->tableslot,
tuple, InvalidBuffer, false);
tupdesc, if (!execTuplesUnequal(hashtable->tableslot, slot,
numCols, keyColIdx, numCols, keyColIdx,
hashtable->eqfunctions, hashtable->eqfunctions,
hashtable->tempcxt)) hashtable->tempcxt))
...@@ -646,17 +636,40 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot) ...@@ -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 static bool
tupleAllNulls(HeapTuple tuple) slotNoNulls(TupleTableSlot *slot)
{ {
int ncols = tuple->t_data->t_natts; int ncols = slot->tts_tupleDescriptor->natts;
int i; int i;
for (i = 1; i <= ncols; i++) for (i = 1; i <= ncols; i++)
{ {
if (!heap_attisnull(tuple, i)) if (slot_attisnull(slot, i))
return false; return false;
} }
return true; return true;
...@@ -932,8 +945,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -932,8 +945,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
!TupIsNull(slot); !TupIsNull(slot);
slot = ExecProcNode(planstate)) slot = ExecProcNode(planstate))
{ {
HeapTuple tup = slot->val; TupleDesc tdesc = slot->tts_tupleDescriptor;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
int i = 1; int i = 1;
if (subLinkType == EXISTS_SUBLINK) if (subLinkType == EXISTS_SUBLINK)
...@@ -956,7 +968,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -956,7 +968,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
found = true; found = true;
/* stash away current value */ /* stash away current value */
dvalue = heap_getattr(tup, 1, tdesc, &disnull); dvalue = slot_getattr(slot, 1, &disnull);
astate = accumArrayResult(astate, dvalue, disnull, astate = accumArrayResult(astate, dvalue, disnull,
tdesc->attrs[0]->atttypid, tdesc->attrs[0]->atttypid,
oldcontext); oldcontext);
...@@ -981,10 +993,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -981,10 +993,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
* freeing. * freeing.
*/ */
MemoryContextSwitchTo(econtext->ecxt_per_query_memory); MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tup = heap_copytuple(tup);
if (node->curTuple) if (node->curTuple)
heap_freetuple(node->curTuple); heap_freetuple(node->curTuple);
node->curTuple = tup; node->curTuple = ExecCopySlotTuple(slot);
MemoryContextSwitchTo(node->sub_estate->es_query_cxt); MemoryContextSwitchTo(node->sub_estate->es_query_cxt);
/* /*
...@@ -996,7 +1007,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -996,7 +1007,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]); ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
prm->execPlan = NULL; prm->execPlan = NULL;
prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull)); prm->value = heap_getattr(node->curTuple, i, tdesc,
&(prm->isnull));
i++; i++;
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -44,14 +44,12 @@ ExecUnique(UniqueState *node)
TupleTableSlot *resultTupleSlot; TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot; TupleTableSlot *slot;
PlanState *outerPlan; PlanState *outerPlan;
TupleDesc tupDesc;
/* /*
* get information from the node * get information from the node
*/ */
outerPlan = outerPlanState(node); outerPlan = outerPlanState(node);
resultTupleSlot = node->ps.ps_ResultTupleSlot; resultTupleSlot = node->ps.ps_ResultTupleSlot;
tupDesc = ExecGetResultType(&node->ps);
/* /*
* now loop, returning only non-duplicate tuples. We assume that the * now loop, returning only non-duplicate tuples. We assume that the
...@@ -59,7 +57,7 @@ ExecUnique(UniqueState *node) ...@@ -59,7 +57,7 @@ ExecUnique(UniqueState *node)
* *
* We return the first tuple from each group of duplicates (or the last * 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 * 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. * first/last tuple when reversing direction.
*/ */
for (;;) for (;;)
...@@ -71,16 +69,14 @@ ExecUnique(UniqueState *node) ...@@ -71,16 +69,14 @@ ExecUnique(UniqueState *node)
if (TupIsNull(slot)) if (TupIsNull(slot))
{ {
/* end of subplan; reset in case we change direction */ /* end of subplan; reset in case we change direction */
if (node->priorTuple != NULL) ExecClearTuple(resultTupleSlot);
heap_freetuple(node->priorTuple);
node->priorTuple = NULL;
return NULL; return NULL;
} }
/* /*
* Always return the first/last tuple from the subplan. * Always return the first/last tuple from the subplan.
*/ */
if (node->priorTuple == NULL) if (TupIsNull(resultTupleSlot))
break; break;
/* /*
...@@ -88,8 +84,7 @@ ExecUnique(UniqueState *node) ...@@ -88,8 +84,7 @@ ExecUnique(UniqueState *node)
* match. If so then we loop back and fetch another new tuple * match. If so then we loop back and fetch another new tuple
* from the subplan. * from the subplan.
*/ */
if (!execTuplesMatch(slot->val, node->priorTuple, if (!execTuplesMatch(slot, resultTupleSlot,
tupDesc,
plannode->numCols, plannode->uniqColIdx, plannode->numCols, plannode->uniqColIdx,
node->eqfunctions, node->eqfunctions,
node->tempContext)) node->tempContext))
...@@ -101,28 +96,8 @@ ExecUnique(UniqueState *node) ...@@ -101,28 +96,8 @@ ExecUnique(UniqueState *node)
* any). Save it and return it. We must copy it because the source * any). Save it and return it. We must copy it because the source
* subplan won't guarantee that this source tuple is still accessible * subplan won't guarantee that this source tuple is still accessible
* after fetching the next source tuple. * 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) return ExecCopySlot(resultTupleSlot, slot);
heap_freetuple(node->priorTuple);
node->priorTuple = heap_copytuple(slot->val);
ExecStoreTuple(node->priorTuple,
resultTupleSlot,
InvalidBuffer,
false); /* tuple does not belong to slot */
return resultTupleSlot;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
...@@ -144,8 +119,6 @@ ExecInitUnique(Unique *node, EState *estate) ...@@ -144,8 +119,6 @@ ExecInitUnique(Unique *node, EState *estate)
uniquestate->ps.plan = (Plan *) node; uniquestate->ps.plan = (Plan *) node;
uniquestate->ps.state = estate; uniquestate->ps.state = estate;
uniquestate->priorTuple = NULL;
/* /*
* Miscellaneous initialization * Miscellaneous initialization
* *
...@@ -220,12 +193,8 @@ ExecEndUnique(UniqueState *node) ...@@ -220,12 +193,8 @@ ExecEndUnique(UniqueState *node)
void void
ExecReScanUnique(UniqueState *node, ExprContext *exprCtxt) ExecReScanUnique(UniqueState *node, ExprContext *exprCtxt)
{ {
/* must clear result tuple so first input tuple is returned */
ExecClearTuple(node->ps.ps_ResultTupleSlot); 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 * if chgParam of subnode is not null then plan will be re-scanned by
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1191,7 +1191,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
* of current SPI procedure * of current SPI procedure
*/ */
void void
spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) spi_printtup(TupleTableSlot *slot, DestReceiver *self)
{ {
SPITupleTable *tuptable; SPITupleTable *tuptable;
MemoryContext oldcxt; MemoryContext oldcxt;
...@@ -1219,7 +1219,8 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) ...@@ -1219,7 +1219,8 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
tuptable->alloced * sizeof(HeapTuple)); tuptable->alloced * sizeof(HeapTuple));
} }
tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple); tuptable->vals[tuptable->alloced - tuptable->free] =
ExecCopySlotTuple(slot);
(tuptable->free)--; (tuptable->free)--;
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -40,12 +40,12 @@ tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
* Receive a tuple from the executor and store it in the tuplestore. * Receive a tuple from the executor and store it in the tuplestore.
*/ */
static void static void
tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) tstoreReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
{ {
TStoreState *myState = (TStoreState *) self; TStoreState *myState = (TStoreState *) self;
MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt); MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt);
tuplestore_puttuple(myState->tstore, tuple); tuplestore_puttuple(myState->tstore, ExecFetchSlotTuple(slot));
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
} }
...@@ -77,7 +77,7 @@ CreateTuplestoreDestReceiver(Tuplestorestate *tStore, ...@@ -77,7 +77,7 @@ CreateTuplestoreDestReceiver(Tuplestorestate *tStore,
{ {
TStoreState *self = (TStoreState *) palloc(sizeof(TStoreState)); TStoreState *self = (TStoreState *) palloc(sizeof(TStoreState));
self->pub.receiveTuple = tstoreReceiveTuple; self->pub.receiveSlot = tstoreReceiveSlot;
self->pub.rStartup = tstoreStartupReceiver; self->pub.rStartup = tstoreStartupReceiver;
self->pub.rShutdown = tstoreShutdownReceiver; self->pub.rShutdown = tstoreShutdownReceiver;
self->pub.rDestroy = tstoreDestroyReceiver; self->pub.rDestroy = tstoreDestroyReceiver;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.72 2004/12/31 21:59:55 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.73 2005/03/16 21:38:08 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -468,18 +468,18 @@ print_tl(List *tlist, List *rtable) ...@@ -468,18 +468,18 @@ print_tl(List *tlist, List *rtable)
void void
print_slot(TupleTableSlot *slot) print_slot(TupleTableSlot *slot)
{ {
if (!slot->val) if (TupIsNull(slot))
{ {
printf("tuple is null.\n"); printf("tuple is null.\n");
return; return;
} }
if (!slot->ttc_tupleDescriptor) if (!slot->tts_tupleDescriptor)
{ {
printf("no tuple descriptor.\n"); printf("no tuple descriptor.\n");
return; return;
} }
debugtup(slot->val, slot->ttc_tupleDescriptor, NULL); debugtup(slot, NULL);
} }
static char * static char *
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/dest.c,v 1.64 2004/12/31 22:01:16 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/tcop/dest.c,v 1.65 2005/03/16 21:38:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
* ---------------- * ----------------
*/ */
static void static void
donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self) donothingReceive(TupleTableSlot *slot, DestReceiver *self)
{ {
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.91 2005/02/10 20:36:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.92 2005/03/16 21:38:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -836,6 +836,9 @@ RunFromStore(Portal portal, ScanDirection direction, long count, ...@@ -836,6 +836,9 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
DestReceiver *dest) DestReceiver *dest)
{ {
long current_tuple_count = 0; long current_tuple_count = 0;
TupleTableSlot *slot;
slot = MakeSingleTupleTableSlot(portal->tupDesc);
(*dest->rStartup) (dest, CMD_SELECT, portal->tupDesc); (*dest->rStartup) (dest, CMD_SELECT, portal->tupDesc);
...@@ -863,10 +866,11 @@ RunFromStore(Portal portal, ScanDirection direction, long count, ...@@ -863,10 +866,11 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
if (tup == NULL) if (tup == NULL)
break; break;
(*dest->receiveTuple) (tup, portal->tupDesc, dest); ExecStoreTuple(tup, slot, InvalidBuffer, should_free);
(*dest->receiveSlot) (slot, dest);
if (should_free) ExecClearTuple(slot);
pfree(tup);
/* /*
* check our tuple count.. if we've processed the proper * check our tuple count.. if we've processed the proper
...@@ -881,6 +885,8 @@ RunFromStore(Portal portal, ScanDirection direction, long count, ...@@ -881,6 +885,8 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
(*dest->rShutdown) (dest); (*dest->rShutdown) (dest);
ExecDropSingleTupleTableSlot(slot);
return (uint32) current_tuple_count; return (uint32) current_tuple_count;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.95 2005/03/14 04:41:13 tgl Exp $ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.96 2005/03/16 21:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -178,27 +178,42 @@ extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf, ...@@ -178,27 +178,42 @@ extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple newtup); Buffer newbuf, HeapTuple newtup);
/* in common/heaptuple.c */ /* in common/heaptuple.c */
extern Size heap_compute_data_size(TupleDesc tupleDesc,
Datum *values, bool *isnull);
extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *values, char *nulls); extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *values, char *nulls);
extern void heap_fill_tuple(TupleDesc tupleDesc,
Datum *values, bool *isnull,
char *data, uint16 *infomask, bits8 *bit);
extern void DataFill(char *data, TupleDesc tupleDesc, extern void DataFill(char *data, TupleDesc tupleDesc,
Datum *values, char *nulls, uint16 *infomask, Datum *values, char *nulls, uint16 *infomask,
bits8 *bit); bits8 *bit);
extern int heap_attisnull(HeapTuple tup, int attnum); extern bool heap_attisnull(HeapTuple tup, int attnum);
extern Datum nocachegetattr(HeapTuple tup, int attnum, extern Datum nocachegetattr(HeapTuple tup, int attnum,
TupleDesc att, bool *isnull); TupleDesc att, bool *isnull);
extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
bool *isnull); bool *isnull);
extern HeapTuple heap_copytuple(HeapTuple tuple); extern HeapTuple heap_copytuple(HeapTuple tuple);
extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest); extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest);
extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor,
Datum *values, bool *isnull);
extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor, extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor,
Datum *values, char *nulls); Datum *values, char *nulls);
extern HeapTuple heap_modify_tuple(HeapTuple tuple,
TupleDesc tupleDesc,
Datum *replValues,
bool *replIsnull,
bool *doReplace);
extern HeapTuple heap_modifytuple(HeapTuple tuple, extern HeapTuple heap_modifytuple(HeapTuple tuple,
TupleDesc tupleDesc, TupleDesc tupleDesc,
Datum *replValues, Datum *replValues,
char *replNulls, char *replNulls,
char *replActions); char *replActions);
extern void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
Datum *values, bool *isnull);
extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc, extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc,
Datum *values, char *nulls); Datum *values, char *nulls);
extern void heap_freetuple(HeapTuple tuple); extern void heap_freetuple(HeapTuple tuple);
extern HeapTuple heap_addheader(int natts, bool withoid, Size structlen, void *structure); extern HeapTuple heap_addheader(int natts, bool withoid,
Size structlen, void *structure);
#endif /* HEAPAM_H */ #endif /* HEAPAM_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/printtup.h,v 1.32 2004/12/31 22:03:21 pgsql Exp $ * $PostgreSQL: pgsql/src/include/access/printtup.h,v 1.33 2005/03/16 21:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,13 +23,11 @@ extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, ...@@ -23,13 +23,11 @@ extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
extern void debugStartup(DestReceiver *self, int operation, extern void debugStartup(DestReceiver *self, int operation,
TupleDesc typeinfo); TupleDesc typeinfo);
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo, extern void debugtup(TupleTableSlot *slot, DestReceiver *self);
DestReceiver *self);
/* XXX these are really in executor/spi.c */ /* XXX these are really in executor/spi.c */
extern void spi_dest_startup(DestReceiver *self, int operation, extern void spi_dest_startup(DestReceiver *self, int operation,
TupleDesc typeinfo); TupleDesc typeinfo);
extern void spi_printtup(HeapTuple tuple, TupleDesc typeinfo, extern void spi_printtup(TupleTableSlot *slot, DestReceiver *self);
DestReceiver *self);
#endif /* PRINTTUP_H */ #endif /* PRINTTUP_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.59 2004/12/31 22:03:24 pgsql Exp $ * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.60 2005/03/16 21:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -46,8 +46,7 @@ extern void index_drop(Oid indexId); ...@@ -46,8 +46,7 @@ extern void index_drop(Oid indexId);
extern IndexInfo *BuildIndexInfo(Relation index); extern IndexInfo *BuildIndexInfo(Relation index);
extern void FormIndexDatum(IndexInfo *indexInfo, extern void FormIndexDatum(IndexInfo *indexInfo,
HeapTuple heapTuple, TupleTableSlot *slot,
TupleDesc heapDescriptor,
EState *estate, EState *estate,
Datum *datum, Datum *datum,
char *nullv); char *nullv);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/execdebug.h,v 1.25 2004/12/31 22:03:29 pgsql Exp $ * $PostgreSQL: pgsql/src/include/executor/execdebug.h,v 1.26 2005/03/16 21:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -256,7 +256,7 @@ extern int NIndexTupleInserted; ...@@ -256,7 +256,7 @@ extern int NIndexTupleInserted;
#define MJ_printf(s) printf(s) #define MJ_printf(s) printf(s)
#define MJ1_printf(s, p) printf(s, p) #define MJ1_printf(s, p) printf(s, p)
#define MJ2_printf(s, p1, p2) printf(s, p1, p2) #define MJ2_printf(s, p1, p2) printf(s, p1, p2)
#define MJ_debugtup(tuple, type) debugtup(tuple, type, NULL) #define MJ_debugtup(slot) debugtup(slot, NULL)
#define MJ_dump(state) ExecMergeTupleDump(state) #define MJ_dump(state) ExecMergeTupleDump(state)
#define MJ_DEBUG_QUAL(clause, res) \ #define MJ_DEBUG_QUAL(clause, res) \
MJ2_printf(" ExecQual(%s, econtext) returns %s\n", \ MJ2_printf(" ExecQual(%s, econtext) returns %s\n", \
...@@ -276,7 +276,7 @@ extern int NIndexTupleInserted; ...@@ -276,7 +276,7 @@ extern int NIndexTupleInserted;
#define MJ_printf(s) #define MJ_printf(s)
#define MJ1_printf(s, p) #define MJ1_printf(s, p)
#define MJ2_printf(s, p1, p2) #define MJ2_printf(s, p1, p2)
#define MJ_debugtup(tuple, type) #define MJ_debugtup(slot)
#define MJ_dump(state) #define MJ_dump(state)
#define MJ_DEBUG_QUAL(clause, res) #define MJ_DEBUG_QUAL(clause, res)
#define MJ_DEBUG_MERGE_COMPARE(qual, res) #define MJ_DEBUG_MERGE_COMPARE(qual, res)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.116 2005/03/14 04:41:13 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.117 2005/03/16 21:38:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,17 +17,6 @@ ...@@ -17,17 +17,6 @@
#include "executor/execdesc.h" #include "executor/execdesc.h"
/*
* TupIsNull
*
* This is used mainly to detect when there are no more
* tuples to process.
*/
/* return: true if tuple in slot is NULL, slot is slot to test */
#define TupIsNull(slot) \
((slot) == NULL || (slot)->val == NULL)
/* /*
* ExecEvalExpr was formerly a function containing a switch statement; * ExecEvalExpr was formerly a function containing a switch statement;
* now it's just a macro invoking the function pointed to by an ExprState * now it's just a macro invoking the function pointed to by an ExprState
...@@ -50,20 +39,18 @@ extern bool ExecMayReturnRawTuples(PlanState *node); ...@@ -50,20 +39,18 @@ extern bool ExecMayReturnRawTuples(PlanState *node);
/* /*
* prototypes from functions in execGrouping.c * prototypes from functions in execGrouping.c
*/ */
extern bool execTuplesMatch(HeapTuple tuple1, extern bool execTuplesMatch(TupleTableSlot *slot1,
HeapTuple tuple2, TupleTableSlot *slot2,
TupleDesc tupdesc, int numCols,
int numCols, AttrNumber *matchColIdx,
AttrNumber *matchColIdx, FmgrInfo *eqfunctions,
FmgrInfo *eqfunctions, MemoryContext evalContext);
MemoryContext evalContext); extern bool execTuplesUnequal(TupleTableSlot *slot1,
extern bool execTuplesUnequal(HeapTuple tuple1, TupleTableSlot *slot2,
HeapTuple tuple2, int numCols,
TupleDesc tupdesc, AttrNumber *matchColIdx,
int numCols, FmgrInfo *eqfunctions,
AttrNumber *matchColIdx, MemoryContext evalContext);
FmgrInfo *eqfunctions,
MemoryContext evalContext);
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc, extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
int numCols, int numCols,
AttrNumber *matchColIdx); AttrNumber *matchColIdx);
...@@ -92,6 +79,8 @@ extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, ...@@ -92,6 +79,8 @@ extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
TupleTableSlot *slot); TupleTableSlot *slot);
extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
char *attrName, Datum *value, bool *isNull); char *attrName, Datum *value, bool *isNull);
extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
TupleTableSlot *slot);
extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot); extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
...@@ -158,17 +147,6 @@ extern void ExecAssignScanProjectionInfo(ScanState *node); ...@@ -158,17 +147,6 @@ extern void ExecAssignScanProjectionInfo(ScanState *node);
/* /*
* prototypes from functions in execTuples.c * prototypes from functions in execTuples.c
*/ */
extern TupleTable ExecCreateTupleTable(int tableSize);
extern void ExecDropTupleTable(TupleTable table, bool shouldFree);
extern TupleTableSlot *MakeTupleTableSlot(void);
extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
TupleDesc tupdesc, bool shouldFree);
extern void ExecInitResultTupleSlot(EState *estate, PlanState *planstate); extern void ExecInitResultTupleSlot(EState *estate, PlanState *planstate);
extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate);
extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
...@@ -183,6 +161,7 @@ typedef struct TupOutputState ...@@ -183,6 +161,7 @@ typedef struct TupOutputState
{ {
/* use "struct" here to allow forward reference */ /* use "struct" here to allow forward reference */
struct AttInMetadata *metadata; struct AttInMetadata *metadata;
TupleTableSlot *slot;
DestReceiver *dest; DestReceiver *dest;
} TupOutputState; } TupOutputState;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.27 2005/03/14 04:41:13 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.28 2005/03/16 21:38:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,43 +17,86 @@ ...@@ -17,43 +17,86 @@
#include "access/htup.h" #include "access/htup.h"
/* /*----------
* The executor stores pointers to tuples in a "tuple table" * The executor stores tuples in a "tuple table" which is composed of
* which is composed of TupleTableSlots. Sometimes the tuples * independent TupleTableSlots. There are several cases we need to handle:
* are pointers to buffer pages, while others are pointers to * 1. physical tuple in a disk buffer page
* palloc'ed memory; the shouldFree variable tells us whether * 2. physical tuple constructed in palloc'ed memory
* we may call pfree() on a tuple. When shouldFree is true, * 3. "virtual" tuple consisting of Datum/isnull arrays
* the tuple is "owned" by the TupleTableSlot and should be *
* freed when the slot's reference to the tuple is dropped. * The first two cases are similar in that they both deal with "materialized"
* tuples, but resource management is different. For a tuple in a disk page
* we need to hold a pin on the buffer until the TupleTableSlot's reference
* to the tuple is dropped; while for a palloc'd tuple we usually want the
* tuple pfree'd when the TupleTableSlot's reference is dropped.
*
* A "virtual" tuple is an optimization used to minimize physical data
* copying in a nest of plan nodes. Any pass-by-reference Datums in the
* tuple point to storage that is not directly associated with the
* TupleTableSlot; generally they will point to part of a tuple stored in
* a lower plan node's output TupleTableSlot, or to a function result
* constructed in a plan node's per-tuple econtext. It is the responsibility
* of the generating plan node to be sure these resources are not released
* for as long as the virtual tuple needs to be valid. We only use virtual
* tuples in the result slots of plan nodes --- tuples to be copied anywhere
* else need to be "materialized" into physical tuples. Note also that a
* virtual tuple does not have any "system columns".
*
* The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
* slot contains a virtual tuple, they are the authoritative data. When the
* slot contains a physical tuple, the arrays contain data extracted from
* the tuple. (In this state, any pass-by-reference Datums point into
* the physical tuple.) The extracted information is built "lazily",
* ie, only as needed. This serves to avoid repeated extraction of data
* from the physical tuple.
*
* A TupleTableSlot can also be "empty", holding no valid data. This is
* the only valid state for a freshly-created slot that has not yet had a
* tuple descriptor assigned to it. In this state, tts_isempty must be
* TRUE, tts_shouldFree FALSE, tts_tuple NULL, tts_buffer InvalidBuffer,
* and tts_nvalid zero.
*
* When tts_shouldFree is true, the physical tuple is "owned" by the slot
* and should be freed when the slot's reference to the tuple is dropped.
* *
* shouldFreeDesc is similar to shouldFree: if it's true, then the * tts_shouldFreeDesc is similar to tts_shouldFree: if it's true, then the
* tupleDescriptor is "owned" by the TupleTableSlot and should be * tupleDescriptor is "owned" by the TupleTableSlot and should be
* freed when the slot's reference to the descriptor is dropped. * freed when the slot's reference to the descriptor is dropped.
* *
* If buffer is not InvalidBuffer, then the slot is holding a pin * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
* on the indicated buffer page; drop the pin when we release the * on the indicated buffer page; drop the pin when we release the
* slot's reference to that buffer. (shouldFree should always be * slot's reference to that buffer. (tts_shouldFree should always be
* false in such a case, since presumably val is pointing at the * false in such a case, since presumably tts_tuple is pointing at the
* buffer page.) * buffer page.)
* *
* The slot_getattr() routine allows extraction of attribute values from * tts_nvalid indicates the number of valid columns in the tts_values/isnull
* a TupleTableSlot's current tuple. It is equivalent to heap_getattr() * arrays. When the slot is holding a "virtual" tuple this must be equal
* except that it can optimize fetching of multiple values more efficiently. * to the descriptor's natts. When the slot is holding a physical tuple
* The cache_xxx fields of TupleTableSlot are support for slot_getattr(). * this is equal to the number of columns we have extracted (we always
* extract columns from left to right, so there are no holes).
*
* tts_values/tts_isnull are allocated when a descriptor is assigned to the
* slot; they are of length equal to the descriptor's natts.
*
* tts_slow/tts_off are saved state for slot_deform_tuple, and should not
* be touched by any other code.
*----------
*/ */
typedef struct TupleTableSlot typedef struct TupleTableSlot
{ {
NodeTag type; /* vestigial ... allows IsA tests */ NodeTag type; /* vestigial ... allows IsA tests */
HeapTuple val; /* current tuple, or NULL if none */ bool tts_isempty; /* true = slot is empty */
TupleDesc ttc_tupleDescriptor; /* tuple's descriptor */ bool tts_shouldFree; /* should pfree tuple? */
bool ttc_shouldFree; /* should pfree tuple? */ bool tts_shouldFreeDesc; /* should pfree descriptor? */
bool ttc_shouldFreeDesc; /* should pfree descriptor? */ bool tts_slow; /* saved state for slot_deform_tuple */
Buffer ttc_buffer; /* tuple's buffer, or InvalidBuffer */ HeapTuple tts_tuple; /* physical tuple, or NULL if none */
MemoryContext ttc_mcxt; /* slot itself is in this context */ TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
Datum *cache_values; /* currently extracted values */ MemoryContext tts_mcxt; /* slot itself is in this context */
int cache_natts; /* # of valid values in cache_values */ Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
bool cache_slow; /* saved state for slot_getattr */ int tts_nvalid; /* # of valid values in tts_values */
long cache_off; /* saved state for slot_getattr */ Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
long tts_off; /* saved state for slot_deform_tuple */
} TupleTableSlot; } TupleTableSlot;
/* /*
...@@ -69,7 +112,36 @@ typedef struct TupleTableData ...@@ -69,7 +112,36 @@ typedef struct TupleTableData
typedef TupleTableData *TupleTable; typedef TupleTableData *TupleTable;
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
#define TupIsNull(slot) \
((slot) == NULL || (slot)->tts_isempty)
/* in executor/execTuples.c */
extern TupleTable ExecCreateTupleTable(int tableSize);
extern void ExecDropTupleTable(TupleTable table, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecAllocTableSlot(TupleTable table);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
TupleDesc tupdesc, bool shouldFree);
extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
/* in access/common/heaptuple.c */ /* in access/common/heaptuple.c */
extern Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull); extern Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull);
extern void slot_getallattrs(TupleTableSlot *slot);
extern void slot_getsomeattrs(TupleTableSlot *slot, int attnum);
extern bool slot_attisnull(TupleTableSlot *slot, int attnum);
#endif /* TUPTABLE_H */ #endif /* TUPTABLE_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.123 2005/03/06 22:15:05 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.124 2005/03/16 21:38:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -175,20 +175,28 @@ typedef struct ReturnSetInfo ...@@ -175,20 +175,28 @@ typedef struct ReturnSetInfo
* This is all the information needed to perform projections --- * This is all the information needed to perform projections ---
* that is, form new tuples by evaluation of targetlist expressions. * that is, form new tuples by evaluation of targetlist expressions.
* Nodes which need to do projections create one of these. * Nodes which need to do projections create one of these.
* In theory, when a node wants to perform a projection
* it should just update this information as necessary and then
* call ExecProject(). -cim 6/3/91
* *
* ExecProject() evaluates the tlist, forms a tuple, and stores it * ExecProject() evaluates the tlist, forms a tuple, and stores it
* in the given slot. As a side-effect, the actual datum values and * in the given slot. Note that the result will be a "virtual" tuple
* null indicators are placed in the work arrays tupValues/tupNulls. * unless ExecMaterializeSlot() is then called to force it to be
* converted to a physical tuple. The slot must have a tupledesc
* that matches the output of the tlist!
*
* The planner very often produces tlists that consist entirely of
* simple Var references (lower levels of a plan tree almost always
* look like that). So we have an optimization to handle that case
* with minimum overhead.
* *
* targetlist target list for projection * targetlist target list for projection
* exprContext expression context in which to evaluate targetlist * exprContext expression context in which to evaluate targetlist
* slot slot to place projection result in * slot slot to place projection result in
* tupValues array of computed values
* tupNull array of null indicators
* itemIsDone workspace for ExecProject * itemIsDone workspace for ExecProject
* isVarList TRUE if simple-Var-list optimization applies
* varSlotOffsets array indicating which slot each simple Var is from
* varNumbers array indicating attr numbers of simple Vars
* lastInnerVar highest attnum from inner tuple slot (0 if none)
* lastOuterVar highest attnum from outer tuple slot (0 if none)
* lastScanVar highest attnum from scan tuple slot (0 if none)
* ---------------- * ----------------
*/ */
typedef struct ProjectionInfo typedef struct ProjectionInfo
...@@ -197,9 +205,13 @@ typedef struct ProjectionInfo ...@@ -197,9 +205,13 @@ typedef struct ProjectionInfo
List *pi_targetlist; List *pi_targetlist;
ExprContext *pi_exprContext; ExprContext *pi_exprContext;
TupleTableSlot *pi_slot; TupleTableSlot *pi_slot;
Datum *pi_tupValues;
char *pi_tupNulls;
ExprDoneCond *pi_itemIsDone; ExprDoneCond *pi_itemIsDone;
bool pi_isVarList;
int *pi_varSlotOffsets;
int *pi_varNumbers;
int pi_lastInnerVar;
int pi_lastOuterVar;
int pi_lastScanVar;
} ProjectionInfo; } ProjectionInfo;
/* ---------------- /* ----------------
...@@ -222,7 +234,7 @@ typedef struct ProjectionInfo ...@@ -222,7 +234,7 @@ typedef struct ProjectionInfo
* cleanMap: A map with the correspondence between the non-junk * cleanMap: A map with the correspondence between the non-junk
* attribute numbers of the "original" tuple and the * attribute numbers of the "original" tuple and the
* attribute numbers of the "clean" tuple. * attribute numbers of the "clean" tuple.
* resultSlot: tuple slot that can be used to hold cleaned tuple. * resultSlot: tuple slot used to hold cleaned tuple.
* ---------------- * ----------------
*/ */
typedef struct JunkFilter typedef struct JunkFilter
...@@ -354,7 +366,8 @@ typedef struct TupleHashTableData ...@@ -354,7 +366,8 @@ typedef struct TupleHashTableData
MemoryContext tablecxt; /* memory context containing table */ MemoryContext tablecxt; /* memory context containing table */
MemoryContext tempcxt; /* context for function evaluations */ MemoryContext tempcxt; /* context for function evaluations */
Size entrysize; /* actual size to make each hash entry */ Size entrysize; /* actual size to make each hash entry */
TupleDesc tupdesc; /* tuple descriptor */ TupleTableSlot *tableslot; /* slot for referencing table entries */
TupleTableSlot *inputslot; /* current input tuple's slot */
} TupleHashTableData; } TupleHashTableData;
typedef HASH_SEQ_STATUS TupleHashIterator; typedef HASH_SEQ_STATUS TupleHashIterator;
...@@ -589,9 +602,9 @@ typedef struct ConvertRowtypeExprState ...@@ -589,9 +602,9 @@ typedef struct ConvertRowtypeExprState
TupleDesc outdesc; /* tupdesc for result rowtype */ TupleDesc outdesc; /* tupdesc for result rowtype */
AttrNumber *attrMap; /* indexes of input fields, or 0 for null */ AttrNumber *attrMap; /* indexes of input fields, or 0 for null */
Datum *invalues; /* workspace for deconstructing source */ Datum *invalues; /* workspace for deconstructing source */
char *innulls; bool *inisnull;
Datum *outvalues; /* workspace for constructing result */ Datum *outvalues; /* workspace for constructing result */
char *outnulls; bool *outisnull;
} ConvertRowtypeExprState; } ConvertRowtypeExprState;
/* ---------------- /* ----------------
...@@ -1065,7 +1078,6 @@ typedef struct GroupState ...@@ -1065,7 +1078,6 @@ typedef struct GroupState
{ {
ScanState ss; /* its first field is NodeTag */ ScanState ss; /* its first field is NodeTag */
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */ FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
HeapTuple grp_firstTuple; /* copy of first tuple of current group */
bool grp_done; /* indicates completion of Group scan */ bool grp_done; /* indicates completion of Group scan */
} GroupState; } GroupState;
...@@ -1111,7 +1123,7 @@ typedef struct AggState ...@@ -1111,7 +1123,7 @@ typedef struct AggState
* Unique nodes are used "on top of" sort nodes to discard * Unique nodes are used "on top of" sort nodes to discard
* duplicate tuples returned from the sort phase. Basically * duplicate tuples returned from the sort phase. Basically
* all it does is compare the current tuple from the subplan * all it does is compare the current tuple from the subplan
* with the previously fetched tuple stored in priorTuple. * with the previously fetched tuple (stored in its result slot).
* If the two are identical in all interesting fields, then * If the two are identical in all interesting fields, then
* we just fetch another tuple from the sort and try again. * we just fetch another tuple from the sort and try again.
* ---------------- * ----------------
...@@ -1120,7 +1132,6 @@ typedef struct UniqueState ...@@ -1120,7 +1132,6 @@ typedef struct UniqueState
{ {
PlanState ps; /* its first field is NodeTag */ PlanState ps; /* its first field is NodeTag */
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */ FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
HeapTuple priorTuple; /* most recently returned tuple, or NULL */
MemoryContext tempContext; /* short-term context for comparisons */ MemoryContext tempContext; /* short-term context for comparisons */
} UniqueState; } UniqueState;
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
* destination. The executor, as well as utility statements that can return * destination. The executor, as well as utility statements that can return
* tuples, are passed the resulting DestReceiver* pointer. Each executor run * tuples, are passed the resulting DestReceiver* pointer. Each executor run
* or utility execution calls the receiver's rStartup method, then the * or utility execution calls the receiver's rStartup method, then the
* receiveTuple method (zero or more times), then the rShutdown method. * receiveSlot method (zero or more times), then the rShutdown method.
* The same receiver object may be re-used multiple times; eventually it is * The same receiver object may be re-used multiple times; eventually it is
* destroyed by calling its rDestroy method. * destroyed by calling its rDestroy method.
* *
...@@ -54,14 +54,14 @@ ...@@ -54,14 +54,14 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/tcop/dest.h,v 1.45 2004/12/31 22:03:44 pgsql Exp $ * $PostgreSQL: pgsql/src/include/tcop/dest.h,v 1.46 2005/03/16 21:38:10 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef DEST_H #ifndef DEST_H
#define DEST_H #define DEST_H
#include "access/htup.h" #include "executor/tuptable.h"
/* buffer size to use for command completion tags */ /* buffer size to use for command completion tags */
...@@ -92,10 +92,8 @@ typedef enum ...@@ -92,10 +92,8 @@ typedef enum
* In the simplest cases, there is no state info, just the function * In the simplest cases, there is no state info, just the function
* pointers that the executor must call. * pointers that the executor must call.
* *
* Note: the receiveTuple routine must be passed a TupleDesc identical to the * Note: the receiveSlot routine must be passed a slot containing a TupleDesc
* one given to the rStartup routine. The reason for passing it again is just * identical to the one given to the rStartup routine.
* that some destinations would otherwise need dynamic state merely to
* remember the tupledesc pointer.
* ---------------- * ----------------
*/ */
typedef struct _DestReceiver DestReceiver; typedef struct _DestReceiver DestReceiver;
...@@ -103,9 +101,8 @@ typedef struct _DestReceiver DestReceiver; ...@@ -103,9 +101,8 @@ typedef struct _DestReceiver DestReceiver;
struct _DestReceiver struct _DestReceiver
{ {
/* Called for each tuple to be output: */ /* Called for each tuple to be output: */
void (*receiveTuple) (HeapTuple tuple, void (*receiveSlot) (TupleTableSlot *slot,
TupleDesc typeinfo, DestReceiver *self);
DestReceiver *self);
/* Per-executor-run initialization and shutdown: */ /* Per-executor-run initialization and shutdown: */
void (*rStartup) (DestReceiver *self, void (*rStartup) (DestReceiver *self,
int operation, int operation,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment