mirror of https://github.com/postgres/postgres
Revise TupleTableSlot code to avoid unnecessary construction and disassembly
of tuples when passing data up through multiple plan nodes. A slot can now hold either a normal "physical" HeapTuple, or a "virtual" tuple consisting of Datum/isnull arrays. Upper plan levels can usually just copy the Datum arrays, avoiding heap_formtuple() and possible subsequent nocachegetattr() calls to extract the data again. This work extends Atsushi Ogawa's earlier patch, which provided the key idea of adding Datum arrays to TupleTableSlots. (I believe however that something like this was foreseen way back in Berkeley days --- see the old comment on ExecProject.) A test case involving many levels of join of fairly wide tables (about 80 columns altogether) showed about 3x overall speedup, though simple queries will probably not be helped very much. I have also duplicated some code in heaptuple.c in order to provide versions of heap_formtuple and friends that use "bool" arrays to indicate null attributes, instead of the old convention of "char" arrays containing either 'n' or ' '. This provides a better match to the convention used by ExecEvalExpr. While I have not made a concerted effort to get rid of uses of the old routines, I think they should be deprecated and eventually removed.
This commit is contained in:
parent
712f053587
commit
f97aebd162
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
$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">
|
||||
|
@ -2219,7 +2219,7 @@ CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
|
|||
case, you first need to obtain or construct a <structname>TupleDesc</>
|
||||
descriptor for the tuple structure. When working with Datums, you
|
||||
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
|
||||
<function>TupleDescGetAttInMetadata</>, and then call
|
||||
<function>BuildTupleFromCStrings</> for each row. In the case of a
|
||||
|
@ -2264,7 +2264,7 @@ AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
|
|||
<para>
|
||||
When working with Datums, use
|
||||
<programlisting>
|
||||
HeapTuple heap_formtuple(TupleDesc tupdesc, Datum *values, char *nulls)
|
||||
HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull)
|
||||
</programlisting>
|
||||
to build a <structname>HeapTuple</> given user data in Datum form.
|
||||
</para>
|
||||
|
@ -2383,7 +2383,7 @@ typedef struct
|
|||
*
|
||||
* 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
|
||||
* 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
|
||||
* BlessTupleDesc() first.
|
||||
*/
|
||||
|
|
|
@ -4,16 +4,19 @@
|
|||
* This file contains heap tuple accessor and mutator routines, as well
|
||||
* as various tuple utilities.
|
||||
*
|
||||
* NOTE: there is massive duplication of code in this module to
|
||||
* support both the convention that a null is marked by a bool TRUE,
|
||||
* and the convention that a null is marked by a char 'n'. The latter
|
||||
* convention is deprecated but it'll probably be a long time before
|
||||
* we can get rid of it entirely.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.97 2005/03/14 04:41:12 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The old interface functions have been converted to macros
|
||||
* and moved to heapam.h
|
||||
* $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.98 2005/03/16 21:38:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -31,10 +34,38 @@
|
|||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* heap_compute_data_size
|
||||
* Determine size of the data area of a tuple to be constructed
|
||||
*/
|
||||
Size
|
||||
heap_compute_data_size(TupleDesc tupleDesc,
|
||||
Datum *values,
|
||||
bool *isnull)
|
||||
{
|
||||
Size data_length = 0;
|
||||
int i;
|
||||
int numberOfAttributes = tupleDesc->natts;
|
||||
Form_pg_attribute *att = tupleDesc->attrs;
|
||||
|
||||
for (i = 0; i < numberOfAttributes; i++)
|
||||
{
|
||||
if (isnull[i])
|
||||
continue;
|
||||
|
||||
data_length = att_align(data_length, att[i]->attalign);
|
||||
data_length = att_addlength(data_length, att[i]->attlen, values[i]);
|
||||
}
|
||||
|
||||
return data_length;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* ComputeDataSize
|
||||
*
|
||||
* Determine size of the data area of a tuple to be constructed
|
||||
*
|
||||
* OLD API with char 'n'/' ' convention for indicating nulls
|
||||
* ----------------
|
||||
*/
|
||||
Size
|
||||
|
@ -59,10 +90,107 @@ ComputeDataSize(TupleDesc tupleDesc,
|
|||
return data_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* heap_fill_tuple
|
||||
* Load data portion of a tuple from values/isnull arrays
|
||||
*
|
||||
* We also fill the null bitmap (if any) and set the infomask bits
|
||||
* that reflect the tuple's data contents.
|
||||
*/
|
||||
void
|
||||
heap_fill_tuple(TupleDesc tupleDesc,
|
||||
Datum *values, bool *isnull,
|
||||
char *data, uint16 *infomask, bits8 *bit)
|
||||
{
|
||||
bits8 *bitP;
|
||||
int bitmask;
|
||||
int i;
|
||||
int numberOfAttributes = tupleDesc->natts;
|
||||
Form_pg_attribute *att = tupleDesc->attrs;
|
||||
|
||||
if (bit != NULL)
|
||||
{
|
||||
bitP = &bit[-1];
|
||||
bitmask = CSIGNBIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* just to keep compiler quiet */
|
||||
bitP = NULL;
|
||||
bitmask = 0;
|
||||
}
|
||||
|
||||
*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
|
||||
|
||||
for (i = 0; i < numberOfAttributes; i++)
|
||||
{
|
||||
Size data_length;
|
||||
|
||||
if (bit != NULL)
|
||||
{
|
||||
if (bitmask != CSIGNBIT)
|
||||
bitmask <<= 1;
|
||||
else
|
||||
{
|
||||
bitP += 1;
|
||||
*bitP = 0x0;
|
||||
bitmask = 1;
|
||||
}
|
||||
|
||||
if (isnull[i])
|
||||
{
|
||||
*infomask |= HEAP_HASNULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
*bitP |= bitmask;
|
||||
}
|
||||
|
||||
/* XXX we are aligning the pointer itself, not the offset */
|
||||
data = (char *) att_align((long) data, att[i]->attalign);
|
||||
|
||||
if (att[i]->attbyval)
|
||||
{
|
||||
/* pass-by-value */
|
||||
store_att_byval(data, values[i], att[i]->attlen);
|
||||
data_length = att[i]->attlen;
|
||||
}
|
||||
else if (att[i]->attlen == -1)
|
||||
{
|
||||
/* varlena */
|
||||
*infomask |= HEAP_HASVARWIDTH;
|
||||
if (VARATT_IS_EXTERNAL(values[i]))
|
||||
*infomask |= HEAP_HASEXTERNAL;
|
||||
if (VARATT_IS_COMPRESSED(values[i]))
|
||||
*infomask |= HEAP_HASCOMPRESSED;
|
||||
data_length = VARATT_SIZE(DatumGetPointer(values[i]));
|
||||
memcpy(data, DatumGetPointer(values[i]), data_length);
|
||||
}
|
||||
else if (att[i]->attlen == -2)
|
||||
{
|
||||
/* cstring */
|
||||
*infomask |= HEAP_HASVARWIDTH;
|
||||
data_length = strlen(DatumGetCString(values[i])) + 1;
|
||||
memcpy(data, DatumGetPointer(values[i]), data_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fixed-length pass-by-reference */
|
||||
Assert(att[i]->attlen > 0);
|
||||
data_length = att[i]->attlen;
|
||||
memcpy(data, DatumGetPointer(values[i]), data_length);
|
||||
}
|
||||
|
||||
data += data_length;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* DataFill
|
||||
*
|
||||
* Load data portion of a tuple from values/nulls arrays
|
||||
*
|
||||
* OLD API with char 'n'/' ' convention for indicating nulls
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
|
@ -162,19 +290,19 @@ DataFill(char *data,
|
|||
*/
|
||||
|
||||
/* ----------------
|
||||
* heap_attisnull - returns 1 iff tuple attribute is not present
|
||||
* heap_attisnull - returns TRUE iff tuple attribute is not present
|
||||
* ----------------
|
||||
*/
|
||||
int
|
||||
bool
|
||||
heap_attisnull(HeapTuple tup, int attnum)
|
||||
{
|
||||
if (attnum > (int) tup->t_data->t_natts)
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
if (attnum > 0)
|
||||
{
|
||||
if (HeapTupleNoNulls(tup))
|
||||
return 0;
|
||||
return false;
|
||||
return att_isnull(attnum - 1, tup->t_data->t_bits);
|
||||
}
|
||||
|
||||
|
@ -194,7 +322,7 @@ heap_attisnull(HeapTuple tup, int attnum)
|
|||
elog(ERROR, "invalid attnum: %d", attnum);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
|
@ -215,7 +343,7 @@ heap_attisnull(HeapTuple tup, int attnum)
|
|||
* you cache the offsets once, examining all the other tuples using
|
||||
* the same attribute descriptor will go much quicker. -cim 5/4/91
|
||||
*
|
||||
* NOTE: if you need to change this code, see also heap_deformtuple.
|
||||
* NOTE: if you need to change this code, see also heap_deform_tuple.
|
||||
* ----------------
|
||||
*/
|
||||
Datum
|
||||
|
@ -227,7 +355,7 @@ nocachegetattr(HeapTuple tuple,
|
|||
HeapTupleHeader tup = tuple->t_data;
|
||||
Form_pg_attribute *att = tupleDesc->attrs;
|
||||
char *tp; /* ptr to att in tuple */
|
||||
bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */
|
||||
bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
|
||||
bool slow = false; /* do we have to walk nulls? */
|
||||
|
||||
(void) isnull; /* not used */
|
||||
|
@ -385,11 +513,11 @@ nocachegetattr(HeapTuple tuple,
|
|||
/*
|
||||
* Now we know that we have to walk the tuple CAREFULLY.
|
||||
*
|
||||
* Note - This loop is a little tricky. On iteration i we first set
|
||||
* the offset for attribute i and figure out how much the offset
|
||||
* should be incremented. Finally, we need to align the offset
|
||||
* based on the size of attribute i+1 (for which the offset has
|
||||
* been computed). -mer 12 Dec 1991
|
||||
* Note - This loop is a little tricky. For each non-null attribute,
|
||||
* we have to first account for alignment padding before the attr,
|
||||
* then advance over the attr based on its length. Nulls have no
|
||||
* storage and no alignment padding either. We can use/set attcacheoff
|
||||
* until we pass either a null or a var-width attribute.
|
||||
*/
|
||||
|
||||
for (i = 0; i < attnum; i++)
|
||||
|
@ -400,7 +528,7 @@ nocachegetattr(HeapTuple tuple,
|
|||
continue;
|
||||
}
|
||||
|
||||
/* If we know the next offset, we can skip the rest */
|
||||
/* If we know the next offset, we can skip the alignment calc */
|
||||
if (usecache && att[i]->attcacheoff != -1)
|
||||
off = att[i]->attcacheoff;
|
||||
else
|
||||
|
@ -552,6 +680,110 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
|
|||
memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* heap_form_tuple
|
||||
* construct a tuple from the given values[] and isnull[] arrays,
|
||||
* which are of the length indicated by tupleDescriptor->natts
|
||||
*
|
||||
* The result is allocated in the current memory context.
|
||||
*/
|
||||
HeapTuple
|
||||
heap_form_tuple(TupleDesc tupleDescriptor,
|
||||
Datum *values,
|
||||
bool *isnull)
|
||||
{
|
||||
HeapTuple tuple; /* return tuple */
|
||||
HeapTupleHeader td; /* tuple data */
|
||||
unsigned long len;
|
||||
int hoff;
|
||||
bool hasnull = false;
|
||||
Form_pg_attribute *att = tupleDescriptor->attrs;
|
||||
int numberOfAttributes = tupleDescriptor->natts;
|
||||
int i;
|
||||
|
||||
if (numberOfAttributes > MaxTupleAttributeNumber)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_TOO_MANY_COLUMNS),
|
||||
errmsg("number of columns (%d) exceeds limit (%d)",
|
||||
numberOfAttributes, MaxTupleAttributeNumber)));
|
||||
|
||||
/*
|
||||
* Check for nulls and embedded tuples; expand any toasted attributes
|
||||
* in embedded tuples. This preserves the invariant that toasting can
|
||||
* only go one level deep.
|
||||
*
|
||||
* We can skip calling toast_flatten_tuple_attribute() if the attribute
|
||||
* couldn't possibly be of composite type. All composite datums are
|
||||
* varlena and have alignment 'd'; furthermore they aren't arrays.
|
||||
* Also, if an attribute is already toasted, it must have been sent to
|
||||
* disk already and so cannot contain toasted attributes.
|
||||
*/
|
||||
for (i = 0; i < numberOfAttributes; i++)
|
||||
{
|
||||
if (isnull[i])
|
||||
hasnull = true;
|
||||
else if (att[i]->attlen == -1 &&
|
||||
att[i]->attalign == 'd' &&
|
||||
att[i]->attndims == 0 &&
|
||||
!VARATT_IS_EXTENDED(values[i]))
|
||||
{
|
||||
values[i] = toast_flatten_tuple_attribute(values[i],
|
||||
att[i]->atttypid,
|
||||
att[i]->atttypmod);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine total space needed
|
||||
*/
|
||||
len = offsetof(HeapTupleHeaderData, t_bits);
|
||||
|
||||
if (hasnull)
|
||||
len += BITMAPLEN(numberOfAttributes);
|
||||
|
||||
if (tupleDescriptor->tdhasoid)
|
||||
len += sizeof(Oid);
|
||||
|
||||
hoff = len = MAXALIGN(len); /* align user data safely */
|
||||
|
||||
len += heap_compute_data_size(tupleDescriptor, values, isnull);
|
||||
|
||||
/*
|
||||
* Allocate and zero the space needed. Note that the tuple body and
|
||||
* HeapTupleData management structure are allocated in one chunk.
|
||||
*/
|
||||
tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
|
||||
tuple->t_datamcxt = CurrentMemoryContext;
|
||||
tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
|
||||
|
||||
/*
|
||||
* And fill in the information. Note we fill the Datum fields even
|
||||
* though this tuple may never become a Datum.
|
||||
*/
|
||||
tuple->t_len = len;
|
||||
ItemPointerSetInvalid(&(tuple->t_self));
|
||||
tuple->t_tableOid = InvalidOid;
|
||||
|
||||
HeapTupleHeaderSetDatumLength(td, len);
|
||||
HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
|
||||
HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
|
||||
|
||||
td->t_natts = numberOfAttributes;
|
||||
td->t_hoff = hoff;
|
||||
|
||||
if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */
|
||||
td->t_infomask = HEAP_HASOID;
|
||||
|
||||
heap_fill_tuple(tupleDescriptor,
|
||||
values,
|
||||
isnull,
|
||||
(char *) td + hoff,
|
||||
&td->t_infomask,
|
||||
(hasnull ? td->t_bits : NULL));
|
||||
|
||||
return tuple;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* heap_formtuple
|
||||
*
|
||||
|
@ -559,6 +791,8 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
|
|||
*
|
||||
* Null attributes are indicated by a 'n' in the appropriate byte
|
||||
* of nulls[]. Non-null attributes are indicated by a ' ' (space).
|
||||
*
|
||||
* OLD API with char 'n'/' ' convention for indicating nulls
|
||||
* ----------------
|
||||
*/
|
||||
HeapTuple
|
||||
|
@ -658,11 +892,84 @@ heap_formtuple(TupleDesc tupleDescriptor,
|
|||
return tuple;
|
||||
}
|
||||
|
||||
/*
|
||||
* heap_modify_tuple
|
||||
* form a new tuple from an old tuple and a set of replacement values.
|
||||
*
|
||||
* The replValues, replIsnull, and doReplace arrays must be of the length
|
||||
* indicated by tupleDesc->natts. The new tuple is constructed using the data
|
||||
* from replValues/replIsnull at columns where doReplace is true, and using
|
||||
* the data from the old tuple at columns where doReplace is false.
|
||||
*
|
||||
* The result is allocated in the current memory context.
|
||||
*/
|
||||
HeapTuple
|
||||
heap_modify_tuple(HeapTuple tuple,
|
||||
TupleDesc tupleDesc,
|
||||
Datum *replValues,
|
||||
bool *replIsnull,
|
||||
bool *doReplace)
|
||||
{
|
||||
int numberOfAttributes = tupleDesc->natts;
|
||||
int attoff;
|
||||
Datum *values;
|
||||
bool *isnull;
|
||||
HeapTuple newTuple;
|
||||
|
||||
/*
|
||||
* allocate and fill values and isnull arrays from either the tuple or
|
||||
* the repl information, as appropriate.
|
||||
*
|
||||
* NOTE: it's debatable whether to use heap_deform_tuple() here or just
|
||||
* heap_getattr() only the non-replaced colums. The latter could win
|
||||
* if there are many replaced columns and few non-replaced ones.
|
||||
* However, heap_deform_tuple costs only O(N) while the heap_getattr
|
||||
* way would cost O(N^2) if there are many non-replaced columns, so it
|
||||
* seems better to err on the side of linear cost.
|
||||
*/
|
||||
values = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
|
||||
isnull = (bool *) palloc(numberOfAttributes * sizeof(bool));
|
||||
|
||||
heap_deform_tuple(tuple, tupleDesc, values, isnull);
|
||||
|
||||
for (attoff = 0; attoff < numberOfAttributes; attoff++)
|
||||
{
|
||||
if (doReplace[attoff])
|
||||
{
|
||||
values[attoff] = replValues[attoff];
|
||||
isnull[attoff] = replIsnull[attoff];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* create a new tuple from the values and isnull arrays
|
||||
*/
|
||||
newTuple = heap_form_tuple(tupleDesc, values, isnull);
|
||||
|
||||
pfree(values);
|
||||
pfree(isnull);
|
||||
|
||||
/*
|
||||
* copy the identification info of the old tuple: t_ctid, t_self, and
|
||||
* OID (if any)
|
||||
*/
|
||||
newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
|
||||
newTuple->t_self = tuple->t_self;
|
||||
newTuple->t_tableOid = tuple->t_tableOid;
|
||||
if (tupleDesc->tdhasoid)
|
||||
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
|
||||
|
||||
return newTuple;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* heap_modifytuple
|
||||
*
|
||||
* forms a new tuple from an old tuple and a set of replacement values.
|
||||
* returns a new palloc'ed tuple.
|
||||
*
|
||||
* OLD API with char 'n'/' ' convention for indicating nulls, and
|
||||
* char 'r'/' ' convention for indicating whether to replace columns.
|
||||
* ----------------
|
||||
*/
|
||||
HeapTuple
|
||||
|
@ -727,6 +1034,93 @@ heap_modifytuple(HeapTuple tuple,
|
|||
return newTuple;
|
||||
}
|
||||
|
||||
/*
|
||||
* heap_deform_tuple
|
||||
* Given a tuple, extract data into values/isnull arrays; this is
|
||||
* the inverse of heap_form_tuple.
|
||||
*
|
||||
* Storage for the values/isnull arrays is provided by the caller;
|
||||
* it should be sized according to tupleDesc->natts not tuple->t_natts.
|
||||
*
|
||||
* Note that for pass-by-reference datatypes, the pointer placed
|
||||
* in the Datum will point into the given tuple.
|
||||
*
|
||||
* When all or most of a tuple's fields need to be extracted,
|
||||
* this routine will be significantly quicker than a loop around
|
||||
* heap_getattr; the loop will become O(N^2) as soon as any
|
||||
* noncacheable attribute offsets are involved.
|
||||
*/
|
||||
void
|
||||
heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
|
||||
Datum *values, bool *isnull)
|
||||
{
|
||||
HeapTupleHeader tup = tuple->t_data;
|
||||
bool hasnulls = HeapTupleHasNulls(tuple);
|
||||
Form_pg_attribute *att = tupleDesc->attrs;
|
||||
int tdesc_natts = tupleDesc->natts;
|
||||
int natts; /* number of atts to extract */
|
||||
int attnum;
|
||||
char *tp; /* ptr to tuple data */
|
||||
long off; /* offset in tuple data */
|
||||
bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
|
||||
bool slow = false; /* can we use/set attcacheoff? */
|
||||
|
||||
natts = tup->t_natts;
|
||||
|
||||
/*
|
||||
* In inheritance situations, it is possible that the given tuple
|
||||
* actually has more fields than the caller is expecting. Don't run
|
||||
* off the end of the caller's arrays.
|
||||
*/
|
||||
natts = Min(natts, tdesc_natts);
|
||||
|
||||
tp = (char *) tup + tup->t_hoff;
|
||||
|
||||
off = 0;
|
||||
|
||||
for (attnum = 0; attnum < natts; attnum++)
|
||||
{
|
||||
Form_pg_attribute thisatt = att[attnum];
|
||||
|
||||
if (hasnulls && att_isnull(attnum, bp))
|
||||
{
|
||||
values[attnum] = (Datum) 0;
|
||||
isnull[attnum] = true;
|
||||
slow = true; /* can't use attcacheoff anymore */
|
||||
continue;
|
||||
}
|
||||
|
||||
isnull[attnum] = false;
|
||||
|
||||
if (!slow && thisatt->attcacheoff >= 0)
|
||||
off = thisatt->attcacheoff;
|
||||
else
|
||||
{
|
||||
off = att_align(off, thisatt->attalign);
|
||||
|
||||
if (!slow)
|
||||
thisatt->attcacheoff = off;
|
||||
}
|
||||
|
||||
values[attnum] = fetchatt(thisatt, tp + off);
|
||||
|
||||
off = att_addlength(off, thisatt->attlen, tp + off);
|
||||
|
||||
if (thisatt->attlen <= 0)
|
||||
slow = true; /* can't use attcacheoff anymore */
|
||||
}
|
||||
|
||||
/*
|
||||
* If tuple doesn't have all the atts indicated by tupleDesc, read the
|
||||
* rest as null
|
||||
*/
|
||||
for (; attnum < tdesc_natts; attnum++)
|
||||
{
|
||||
values[attnum] = (Datum) 0;
|
||||
isnull[attnum] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* heap_deformtuple
|
||||
*
|
||||
|
@ -743,6 +1137,8 @@ heap_modifytuple(HeapTuple tuple,
|
|||
* this routine will be significantly quicker than a loop around
|
||||
* heap_getattr; the loop will become O(N^2) as soon as any
|
||||
* noncacheable attribute offsets are involved.
|
||||
*
|
||||
* OLD API with char 'n'/' ' convention for indicating nulls
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
|
@ -759,7 +1155,7 @@ heap_deformtuple(HeapTuple tuple,
|
|||
int attnum;
|
||||
char *tp; /* ptr to tuple data */
|
||||
long off; /* offset in tuple data */
|
||||
bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */
|
||||
bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
|
||||
bool slow = false; /* can we use/set attcacheoff? */
|
||||
|
||||
natts = tup->t_natts;
|
||||
|
@ -818,42 +1214,38 @@ heap_deformtuple(HeapTuple tuple,
|
|||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* slot_deformtuple
|
||||
/*
|
||||
* slot_deform_tuple
|
||||
* Given a TupleTableSlot, extract data from the slot's physical tuple
|
||||
* into its Datum/isnull arrays. Data is extracted up through the
|
||||
* natts'th column (caller must ensure this is a legal column number).
|
||||
*
|
||||
* Given a TupleTableSlot, extract data into cache_values array
|
||||
* from the slot's tuple.
|
||||
*
|
||||
* This is essentially an incremental version of heap_deformtuple:
|
||||
* This is essentially an incremental version of heap_deform_tuple:
|
||||
* on each call we extract attributes up to the one needed, without
|
||||
* re-computing information about previously extracted attributes.
|
||||
* slot->cache_natts is the number of attributes already extracted.
|
||||
*
|
||||
* This only gets called from slot_getattr. Note that slot_getattr
|
||||
* must check for a null attribute since we don't create an array
|
||||
* of null indicators.
|
||||
* ----------------
|
||||
* slot->tts_nvalid is the number of attributes already extracted.
|
||||
*/
|
||||
static void
|
||||
slot_deformtuple(TupleTableSlot *slot, int natts)
|
||||
slot_deform_tuple(TupleTableSlot *slot, int natts)
|
||||
{
|
||||
HeapTuple tuple = slot->val;
|
||||
TupleDesc tupleDesc = slot->ttc_tupleDescriptor;
|
||||
Datum *values = slot->cache_values;
|
||||
HeapTuple tuple = slot->tts_tuple;
|
||||
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
|
||||
Datum *values = slot->tts_values;
|
||||
bool *isnull = slot->tts_isnull;
|
||||
HeapTupleHeader tup = tuple->t_data;
|
||||
bool hasnulls = HeapTupleHasNulls(tuple);
|
||||
Form_pg_attribute *att = tupleDesc->attrs;
|
||||
int attnum;
|
||||
char *tp; /* ptr to tuple data */
|
||||
long off; /* offset in tuple data */
|
||||
bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */
|
||||
bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
|
||||
bool slow; /* can we use/set attcacheoff? */
|
||||
|
||||
/*
|
||||
* Check whether the first call for this tuple, and initialize or
|
||||
* restore loop state.
|
||||
*/
|
||||
attnum = slot->cache_natts;
|
||||
attnum = slot->tts_nvalid;
|
||||
if (attnum == 0)
|
||||
{
|
||||
/* Start from the first attribute */
|
||||
|
@ -863,8 +1255,8 @@ slot_deformtuple(TupleTableSlot *slot, int natts)
|
|||
else
|
||||
{
|
||||
/* Restore state from previous execution */
|
||||
off = slot->cache_off;
|
||||
slow = slot->cache_slow;
|
||||
off = slot->tts_off;
|
||||
slow = slot->tts_slow;
|
||||
}
|
||||
|
||||
tp = (char *) tup + tup->t_hoff;
|
||||
|
@ -876,10 +1268,13 @@ slot_deformtuple(TupleTableSlot *slot, int natts)
|
|||
if (hasnulls && att_isnull(attnum, bp))
|
||||
{
|
||||
values[attnum] = (Datum) 0;
|
||||
isnull[attnum] = true;
|
||||
slow = true; /* can't use attcacheoff anymore */
|
||||
continue;
|
||||
}
|
||||
|
||||
isnull[attnum] = false;
|
||||
|
||||
if (!slow && thisatt->attcacheoff >= 0)
|
||||
off = thisatt->attcacheoff;
|
||||
else
|
||||
|
@ -901,48 +1296,81 @@ slot_deformtuple(TupleTableSlot *slot, int natts)
|
|||
/*
|
||||
* Save state for next execution
|
||||
*/
|
||||
slot->cache_natts = attnum;
|
||||
slot->cache_off = off;
|
||||
slot->cache_slow = slow;
|
||||
slot->tts_nvalid = attnum;
|
||||
slot->tts_off = off;
|
||||
slot->tts_slow = slow;
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* slot_getattr
|
||||
*
|
||||
/*
|
||||
* slot_getattr
|
||||
* This function fetches an attribute of the slot's current tuple.
|
||||
* It is functionally equivalent to heap_getattr, but fetches of
|
||||
* multiple attributes of the same tuple will be optimized better,
|
||||
* because we avoid O(N^2) behavior from multiple calls of
|
||||
* nocachegetattr(), even when attcacheoff isn't usable.
|
||||
* --------------------------------
|
||||
*
|
||||
* A difference from raw heap_getattr is that attnums beyond the
|
||||
* slot's tupdesc's last attribute will be considered NULL even
|
||||
* when the physical tuple is longer than the tupdesc.
|
||||
*/
|
||||
Datum
|
||||
slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
||||
{
|
||||
HeapTuple tuple = slot->val;
|
||||
TupleDesc tupleDesc = slot->ttc_tupleDescriptor;
|
||||
HeapTuple tuple = slot->tts_tuple;
|
||||
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
|
||||
HeapTupleHeader tup;
|
||||
|
||||
/*
|
||||
* system attributes are handled by heap_getsysattr
|
||||
*/
|
||||
if (attnum <= 0)
|
||||
{
|
||||
if (tuple == NULL) /* internal error */
|
||||
elog(ERROR, "cannot extract system attribute from virtual tuple");
|
||||
return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
|
||||
}
|
||||
|
||||
/*
|
||||
* check if attnum is out of range according to either the tupdesc
|
||||
* or the tuple itself; if so return NULL
|
||||
* fast path if desired attribute already cached
|
||||
*/
|
||||
tup = tuple->t_data;
|
||||
if (attnum <= slot->tts_nvalid)
|
||||
{
|
||||
*isnull = slot->tts_isnull[attnum - 1];
|
||||
return slot->tts_values[attnum - 1];
|
||||
}
|
||||
|
||||
if (attnum > tup->t_natts || attnum > tupleDesc->natts)
|
||||
/*
|
||||
* return NULL if attnum is out of range according to the tupdesc
|
||||
*/
|
||||
if (attnum > tupleDesc->natts)
|
||||
{
|
||||
*isnull = true;
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if target attribute is null
|
||||
* otherwise we had better have a physical tuple (tts_nvalid should
|
||||
* equal natts in all virtual-tuple cases)
|
||||
*/
|
||||
if (tuple == NULL) /* internal error */
|
||||
elog(ERROR, "cannot extract attribute from empty tuple slot");
|
||||
|
||||
/*
|
||||
* return NULL if attnum is out of range according to the tuple
|
||||
*
|
||||
* (We have to check this separately because of various inheritance
|
||||
* and table-alteration scenarios: the tuple could be either longer
|
||||
* or shorter than the tupdesc.)
|
||||
*/
|
||||
tup = tuple->t_data;
|
||||
if (attnum > tup->t_natts)
|
||||
{
|
||||
*isnull = true;
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if target attribute is null: no point in groveling through tuple
|
||||
*/
|
||||
if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
|
||||
{
|
||||
|
@ -963,30 +1391,151 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
|
|||
}
|
||||
|
||||
/*
|
||||
* If attribute wasn't already extracted, extract it and preceding
|
||||
* attributes.
|
||||
* Extract the attribute, along with any preceding attributes.
|
||||
*/
|
||||
if (attnum > slot->cache_natts)
|
||||
{
|
||||
/*
|
||||
* If first time for this TupleTableSlot, allocate the cache
|
||||
* workspace. It must have the same lifetime as the slot, so allocate
|
||||
* it in the slot's own context. We size the array according to what
|
||||
* the tupdesc says, NOT the tuple.
|
||||
*/
|
||||
if (slot->cache_values == NULL)
|
||||
slot->cache_values = (Datum *)
|
||||
MemoryContextAlloc(slot->ttc_mcxt,
|
||||
tupleDesc->natts * sizeof(Datum));
|
||||
slot_deform_tuple(slot, attnum);
|
||||
|
||||
slot_deformtuple(slot, attnum);
|
||||
/*
|
||||
* The result is acquired from tts_values array.
|
||||
*/
|
||||
*isnull = slot->tts_isnull[attnum - 1];
|
||||
return slot->tts_values[attnum - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* slot_getallattrs
|
||||
* This function forces all the entries of the slot's Datum/isnull
|
||||
* arrays to be valid. The caller may then extract data directly
|
||||
* from those arrays instead of using slot_getattr.
|
||||
*/
|
||||
void
|
||||
slot_getallattrs(TupleTableSlot *slot)
|
||||
{
|
||||
int tdesc_natts = slot->tts_tupleDescriptor->natts;
|
||||
int attnum;
|
||||
HeapTuple tuple;
|
||||
|
||||
/* Quick out if we have 'em all already */
|
||||
if (slot->tts_nvalid == tdesc_natts)
|
||||
return;
|
||||
|
||||
/*
|
||||
* otherwise we had better have a physical tuple (tts_nvalid should
|
||||
* equal natts in all virtual-tuple cases)
|
||||
*/
|
||||
tuple = slot->tts_tuple;
|
||||
if (tuple == NULL) /* internal error */
|
||||
elog(ERROR, "cannot extract attribute from empty tuple slot");
|
||||
|
||||
/*
|
||||
* load up any slots available from physical tuple
|
||||
*/
|
||||
attnum = tuple->t_data->t_natts;
|
||||
attnum = Min(attnum, tdesc_natts);
|
||||
|
||||
slot_deform_tuple(slot, attnum);
|
||||
|
||||
/*
|
||||
* If tuple doesn't have all the atts indicated by tupleDesc, read the
|
||||
* rest as null
|
||||
*/
|
||||
for (; attnum < tdesc_natts; attnum++)
|
||||
{
|
||||
slot->tts_values[attnum] = (Datum) 0;
|
||||
slot->tts_isnull[attnum] = true;
|
||||
}
|
||||
slot->tts_nvalid = tdesc_natts;
|
||||
}
|
||||
|
||||
/*
|
||||
* slot_getsomeattrs
|
||||
* This function forces the entries of the slot's Datum/isnull
|
||||
* arrays to be valid at least up through the attnum'th entry.
|
||||
*/
|
||||
void
|
||||
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
int attno;
|
||||
|
||||
/* Quick out if we have 'em all already */
|
||||
if (slot->tts_nvalid >= attnum)
|
||||
return;
|
||||
|
||||
/* Check for caller error */
|
||||
if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
|
||||
elog(ERROR, "invalid attribute number %d", attnum);
|
||||
|
||||
/*
|
||||
* otherwise we had better have a physical tuple (tts_nvalid should
|
||||
* equal natts in all virtual-tuple cases)
|
||||
*/
|
||||
tuple = slot->tts_tuple;
|
||||
if (tuple == NULL) /* internal error */
|
||||
elog(ERROR, "cannot extract attribute from empty tuple slot");
|
||||
|
||||
/*
|
||||
* load up any slots available from physical tuple
|
||||
*/
|
||||
attno = tuple->t_data->t_natts;
|
||||
attno = Min(attno, attnum);
|
||||
|
||||
slot_deform_tuple(slot, attno);
|
||||
|
||||
/*
|
||||
* If tuple doesn't have all the atts indicated by tupleDesc, read the
|
||||
* rest as null
|
||||
*/
|
||||
for (; attno < attnum; attno++)
|
||||
{
|
||||
slot->tts_values[attno] = (Datum) 0;
|
||||
slot->tts_isnull[attno] = true;
|
||||
}
|
||||
slot->tts_nvalid = attnum;
|
||||
}
|
||||
|
||||
/*
|
||||
* slot_attisnull
|
||||
* Detect whether an attribute of the slot is null, without
|
||||
* actually fetching it.
|
||||
*/
|
||||
bool
|
||||
slot_attisnull(TupleTableSlot *slot, int attnum)
|
||||
{
|
||||
HeapTuple tuple = slot->tts_tuple;
|
||||
TupleDesc tupleDesc = slot->tts_tupleDescriptor;
|
||||
|
||||
/*
|
||||
* system attributes are handled by heap_attisnull
|
||||
*/
|
||||
if (attnum <= 0)
|
||||
{
|
||||
if (tuple == NULL) /* internal error */
|
||||
elog(ERROR, "cannot extract system attribute from virtual tuple");
|
||||
return heap_attisnull(tuple, attnum);
|
||||
}
|
||||
|
||||
/*
|
||||
* The result is acquired from cache_values array.
|
||||
* fast path if desired attribute already cached
|
||||
*/
|
||||
*isnull = false;
|
||||
return slot->cache_values[attnum - 1];
|
||||
if (attnum <= slot->tts_nvalid)
|
||||
return slot->tts_isnull[attnum - 1];
|
||||
|
||||
/*
|
||||
* return NULL if attnum is out of range according to the tupdesc
|
||||
*/
|
||||
if (attnum > tupleDesc->natts)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* otherwise we had better have a physical tuple (tts_nvalid should
|
||||
* equal natts in all virtual-tuple cases)
|
||||
*/
|
||||
if (tuple == NULL) /* internal error */
|
||||
elog(ERROR, "cannot extract attribute from empty tuple slot");
|
||||
|
||||
/* and let the tuple tell it */
|
||||
return heap_attisnull(tuple, attnum);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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 @@
|
|||
|
||||
static void printtup_startup(DestReceiver *self, int operation,
|
||||
TupleDesc typeinfo);
|
||||
static void printtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver *self);
|
||||
static void printtup_20(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver *self);
|
||||
static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver *self);
|
||||
static void printtup(TupleTableSlot *slot, DestReceiver *self);
|
||||
static void printtup_20(TupleTableSlot *slot, DestReceiver *self);
|
||||
static void printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
|
||||
static void printtup_shutdown(DestReceiver *self);
|
||||
static void printtup_destroy(DestReceiver *self);
|
||||
|
||||
|
@ -65,8 +62,6 @@ typedef struct
|
|||
TupleDesc attrinfo; /* The attr info we are set up for */
|
||||
int nattrs;
|
||||
PrinttupAttrInfo *myinfo; /* Cached info about each attr */
|
||||
Datum *values; /* preallocated space for deformtuple */
|
||||
char *nulls;
|
||||
} DR_printtup;
|
||||
|
||||
/* ----------------
|
||||
|
@ -79,7 +74,7 @@ printtup_create_DR(CommandDest dest, Portal portal)
|
|||
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
|
||||
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||
self->pub.receiveTuple = printtup;
|
||||
self->pub.receiveSlot = printtup;
|
||||
else
|
||||
{
|
||||
/*
|
||||
|
@ -88,9 +83,9 @@ printtup_create_DR(CommandDest dest, Portal portal)
|
|||
* sufficient to look at the first one.
|
||||
*/
|
||||
if (portal->formats && portal->formats[0] != 0)
|
||||
self->pub.receiveTuple = printtup_internal_20;
|
||||
self->pub.receiveSlot = printtup_internal_20;
|
||||
else
|
||||
self->pub.receiveTuple = printtup_20;
|
||||
self->pub.receiveSlot = printtup_20;
|
||||
}
|
||||
self->pub.rStartup = printtup_startup;
|
||||
self->pub.rShutdown = printtup_shutdown;
|
||||
|
@ -105,8 +100,6 @@ printtup_create_DR(CommandDest dest, Portal portal)
|
|||
self->attrinfo = NULL;
|
||||
self->nattrs = 0;
|
||||
self->myinfo = NULL;
|
||||
self->values = NULL;
|
||||
self->nulls = NULL;
|
||||
|
||||
return (DestReceiver *) self;
|
||||
}
|
||||
|
@ -251,12 +244,6 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
|
|||
if (myState->myinfo)
|
||||
pfree(myState->myinfo);
|
||||
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->nattrs = numAttrs;
|
||||
|
@ -265,8 +252,6 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
|
|||
|
||||
myState->myinfo = (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++)
|
||||
{
|
||||
|
@ -302,8 +287,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
|
|||
* ----------------
|
||||
*/
|
||||
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;
|
||||
StringInfoData buf;
|
||||
int natts = typeinfo->natts;
|
||||
|
@ -313,10 +299,8 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
||||
printtup_prepare_info(myState, typeinfo, natts);
|
||||
|
||||
/*
|
||||
* deconstruct the tuple (faster than a heap_getattr loop)
|
||||
*/
|
||||
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
|
||||
/* Make sure the tuple is fully deconstructed */
|
||||
slot_getallattrs(slot);
|
||||
|
||||
/*
|
||||
* Prepare a DataRow message
|
||||
|
@ -331,10 +315,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
for (i = 0; i < natts; ++i)
|
||||
{
|
||||
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
||||
Datum origattr = myState->values[i],
|
||||
Datum origattr = slot->tts_values[i],
|
||||
attr;
|
||||
|
||||
if (myState->nulls[i] == 'n')
|
||||
if (slot->tts_isnull[i])
|
||||
{
|
||||
pq_sendint(&buf, -1, 4);
|
||||
continue;
|
||||
|
@ -389,8 +373,9 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
* ----------------
|
||||
*/
|
||||
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;
|
||||
StringInfoData buf;
|
||||
int natts = typeinfo->natts;
|
||||
|
@ -402,10 +387,8 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
||||
printtup_prepare_info(myState, typeinfo, natts);
|
||||
|
||||
/*
|
||||
* deconstruct the tuple (faster than a heap_getattr loop)
|
||||
*/
|
||||
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
|
||||
/* Make sure the tuple is fully deconstructed */
|
||||
slot_getallattrs(slot);
|
||||
|
||||
/*
|
||||
* tell the frontend to expect new tuple data (in ASCII style)
|
||||
|
@ -419,7 +402,7 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
k = 1 << 7;
|
||||
for (i = 0; i < natts; ++i)
|
||||
{
|
||||
if (myState->nulls[i] != 'n')
|
||||
if (slot->tts_isnull[i])
|
||||
j |= k; /* set bit if not null */
|
||||
k >>= 1;
|
||||
if (k == 0) /* end of byte? */
|
||||
|
@ -438,11 +421,11 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
for (i = 0; i < natts; ++i)
|
||||
{
|
||||
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
||||
Datum origattr = myState->values[i],
|
||||
Datum origattr = slot->tts_values[i],
|
||||
attr;
|
||||
char *outputstr;
|
||||
|
||||
if (myState->nulls[i] == 'n')
|
||||
if (slot->tts_isnull[i])
|
||||
continue;
|
||||
|
||||
Assert(thisState->format == 0);
|
||||
|
@ -483,12 +466,6 @@ printtup_shutdown(DestReceiver *self)
|
|||
if (myState->myinfo)
|
||||
pfree(myState->myinfo);
|
||||
myState->myinfo = NULL;
|
||||
if (myState->values)
|
||||
pfree(myState->values);
|
||||
myState->values = NULL;
|
||||
if (myState->nulls)
|
||||
pfree(myState->nulls);
|
||||
myState->nulls = NULL;
|
||||
|
||||
myState->attrinfo = NULL;
|
||||
}
|
||||
|
@ -548,8 +525,9 @@ debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|||
* ----------------
|
||||
*/
|
||||
void
|
||||
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
||||
debugtup(TupleTableSlot *slot, DestReceiver *self)
|
||||
{
|
||||
TupleDesc typeinfo = slot->tts_tupleDescriptor;
|
||||
int natts = typeinfo->natts;
|
||||
int i;
|
||||
Datum origattr,
|
||||
|
@ -562,7 +540,7 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
|
||||
for (i = 0; i < natts; ++i)
|
||||
{
|
||||
origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
|
||||
origattr = slot_getattr(slot, i + 1, &isnull);
|
||||
if (isnull)
|
||||
continue;
|
||||
getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
|
||||
|
@ -603,8 +581,9 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
* ----------------
|
||||
*/
|
||||
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;
|
||||
StringInfoData buf;
|
||||
int natts = typeinfo->natts;
|
||||
|
@ -616,10 +595,8 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
if (myState->attrinfo != typeinfo || myState->nattrs != natts)
|
||||
printtup_prepare_info(myState, typeinfo, natts);
|
||||
|
||||
/*
|
||||
* deconstruct the tuple (faster than a heap_getattr loop)
|
||||
*/
|
||||
heap_deformtuple(tuple, typeinfo, myState->values, myState->nulls);
|
||||
/* Make sure the tuple is fully deconstructed */
|
||||
slot_getallattrs(slot);
|
||||
|
||||
/*
|
||||
* tell the frontend to expect new tuple data (in binary style)
|
||||
|
@ -633,7 +610,7 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
k = 1 << 7;
|
||||
for (i = 0; i < natts; ++i)
|
||||
{
|
||||
if (myState->nulls[i] != 'n')
|
||||
if (slot->tts_isnull[i])
|
||||
j |= k; /* set bit if not null */
|
||||
k >>= 1;
|
||||
if (k == 0) /* end of byte? */
|
||||
|
@ -652,11 +629,11 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
|||
for (i = 0; i < natts; ++i)
|
||||
{
|
||||
PrinttupAttrInfo *thisState = myState->myinfo + i;
|
||||
Datum origattr = myState->values[i],
|
||||
Datum origattr = slot->tts_values[i],
|
||||
attr;
|
||||
bytea *outputbytes;
|
||||
|
||||
if (myState->nulls[i] == 'n')
|
||||
if (slot->tts_isnull[i])
|
||||
continue;
|
||||
|
||||
Assert(thisState->format == 1);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
||||
|
@ -894,8 +894,7 @@ BuildIndexInfo(Relation index)
|
|||
* Construct Datum[] and nullv[] arrays for a new index tuple.
|
||||
*
|
||||
* indexInfo Info about the index
|
||||
* heapTuple Heap tuple for which we must prepare an index entry
|
||||
* heapDescriptor tupledesc for heap tuple
|
||||
* slot Heap tuple for which we must prepare an index entry
|
||||
* estate executor state for evaluating any index expressions
|
||||
* datum Array of index Datums (output area)
|
||||
* nullv Array of is-null indicators (output area)
|
||||
|
@ -910,8 +909,7 @@ BuildIndexInfo(Relation index)
|
|||
*/
|
||||
void
|
||||
FormIndexDatum(IndexInfo *indexInfo,
|
||||
HeapTuple heapTuple,
|
||||
TupleDesc heapDescriptor,
|
||||
TupleTableSlot *slot,
|
||||
EState *estate,
|
||||
Datum *datum,
|
||||
char *nullv)
|
||||
|
@ -927,7 +925,7 @@ FormIndexDatum(IndexInfo *indexInfo,
|
|||
ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
|
||||
estate);
|
||||
/* 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);
|
||||
|
||||
|
@ -943,7 +941,7 @@ FormIndexDatum(IndexInfo *indexInfo,
|
|||
* Plain index column; get the value we need directly from the
|
||||
* heap tuple.
|
||||
*/
|
||||
iDatum = heap_getattr(heapTuple, keycol, heapDescriptor, &isNull);
|
||||
iDatum = slot_getattr(slot, keycol, &isNull);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1336,12 +1334,10 @@ IndexBuildHeapScan(Relation heapRelation,
|
|||
{
|
||||
HeapScanDesc scan;
|
||||
HeapTuple heapTuple;
|
||||
TupleDesc heapDescriptor;
|
||||
Datum attdata[INDEX_MAX_KEYS];
|
||||
char nulls[INDEX_MAX_KEYS];
|
||||
double reltuples;
|
||||
List *predicate;
|
||||
TupleTable tupleTable;
|
||||
TupleTableSlot *slot;
|
||||
EState *estate;
|
||||
ExprContext *econtext;
|
||||
|
@ -1353,41 +1349,21 @@ IndexBuildHeapScan(Relation heapRelation,
|
|||
*/
|
||||
Assert(OidIsValid(indexRelation->rd_rel->relam));
|
||||
|
||||
heapDescriptor = RelationGetDescr(heapRelation);
|
||||
|
||||
/*
|
||||
* 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();
|
||||
econtext = GetPerTupleExprContext(estate);
|
||||
slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
|
||||
|
||||
/*
|
||||
* If this is a predicate (partial) index, we will need to evaluate
|
||||
* 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;
|
||||
|
||||
/* Arrange for econtext's scan tuple to be the tuple under test */
|
||||
econtext->ecxt_scantuple = slot;
|
||||
|
||||
/* Set up execution state for predicate. */
|
||||
predicate = (List *)
|
||||
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
|
||||
estate);
|
||||
}
|
||||
else
|
||||
{
|
||||
tupleTable = NULL;
|
||||
slot = NULL;
|
||||
predicate = NIL;
|
||||
}
|
||||
/* Set up execution state for predicate, if any. */
|
||||
predicate = (List *)
|
||||
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
|
||||
estate);
|
||||
|
||||
/*
|
||||
* Ok, begin our scan of the base relation. We use SnapshotAny
|
||||
|
@ -1511,8 +1487,7 @@ IndexBuildHeapScan(Relation heapRelation,
|
|||
MemoryContextReset(econtext->ecxt_per_tuple_memory);
|
||||
|
||||
/* 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
|
||||
|
@ -1534,8 +1509,7 @@ IndexBuildHeapScan(Relation heapRelation,
|
|||
* evaluation of any expressions needed.
|
||||
*/
|
||||
FormIndexDatum(indexInfo,
|
||||
heapTuple,
|
||||
heapDescriptor,
|
||||
slot,
|
||||
estate,
|
||||
attdata,
|
||||
nulls);
|
||||
|
@ -1553,8 +1527,7 @@ IndexBuildHeapScan(Relation heapRelation,
|
|||
|
||||
heap_endscan(scan);
|
||||
|
||||
if (tupleTable)
|
||||
ExecDropTupleTable(tupleTable, true);
|
||||
ExecDropSingleTupleTableSlot(slot);
|
||||
|
||||
FreeExecutorState(estate);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
*
|
||||
* 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)
|
|||
int numIndexes;
|
||||
RelationPtr relationDescs;
|
||||
Relation heapRelation;
|
||||
TupleDesc heapDescriptor;
|
||||
TupleTableSlot *slot;
|
||||
IndexInfo **indexInfoArray;
|
||||
Datum datum[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;
|
||||
if (numIndexes == 0)
|
||||
return;
|
||||
relationDescs = indstate->ri_IndexRelationDescs;
|
||||
indexInfoArray = indstate->ri_IndexRelationInfo;
|
||||
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
|
||||
|
@ -106,11 +111,10 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
|
|||
|
||||
/*
|
||||
* 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,
|
||||
heapTuple,
|
||||
heapDescriptor,
|
||||
slot,
|
||||
NULL, /* no expression eval to do */
|
||||
datum,
|
||||
nullv);
|
||||
|
@ -128,6 +132,8 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
|
|||
if (result)
|
||||
pfree(result);
|
||||
}
|
||||
|
||||
ExecDropSingleTupleTableSlot(slot);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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,
|
|||
{
|
||||
MemoryContext ind_context,
|
||||
old_context;
|
||||
TupleDesc heapDescriptor;
|
||||
Datum attdata[INDEX_MAX_KEYS];
|
||||
char nulls[INDEX_MAX_KEYS];
|
||||
int ind,
|
||||
i;
|
||||
|
||||
heapDescriptor = RelationGetDescr(onerel);
|
||||
|
||||
ind_context = AllocSetContextCreate(anl_context,
|
||||
"Analyze Index",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
|
@ -468,7 +465,6 @@ compute_index_stats(Relation onerel, double totalrows,
|
|||
AnlIndexData *thisdata = &indexdata[ind];
|
||||
IndexInfo *indexInfo = thisdata->indexInfo;
|
||||
int attr_cnt = thisdata->attr_cnt;
|
||||
TupleTable tupleTable;
|
||||
TupleTableSlot *slot;
|
||||
EState *estate;
|
||||
ExprContext *econtext;
|
||||
|
@ -492,9 +488,7 @@ compute_index_stats(Relation onerel, double totalrows,
|
|||
estate = CreateExecutorState();
|
||||
econtext = GetPerTupleExprContext(estate);
|
||||
/* Need a slot to hold the current heap tuple, too */
|
||||
tupleTable = ExecCreateTupleTable(1);
|
||||
slot = ExecAllocTableSlot(tupleTable);
|
||||
ExecSetSlotDescriptor(slot, heapDescriptor, false);
|
||||
slot = MakeSingleTupleTableSlot(RelationGetDescr(onerel));
|
||||
|
||||
/* Arrange for econtext's scan tuple to be the tuple under test */
|
||||
econtext->ecxt_scantuple = slot;
|
||||
|
@ -532,8 +526,7 @@ compute_index_stats(Relation onerel, double totalrows,
|
|||
* convenient.
|
||||
*/
|
||||
FormIndexDatum(indexInfo,
|
||||
heapTuple,
|
||||
heapDescriptor,
|
||||
slot,
|
||||
estate,
|
||||
attdata,
|
||||
nulls);
|
||||
|
@ -585,7 +578,7 @@ compute_index_stats(Relation onerel, double totalrows,
|
|||
/* And clean up */
|
||||
MemoryContextSwitchTo(ind_context);
|
||||
|
||||
ExecDropTupleTable(tupleTable, true);
|
||||
ExecDropSingleTupleTableSlot(slot);
|
||||
FreeExecutorState(estate);
|
||||
MemoryContextResetAndDeleteChildren(ind_context);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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,
|
|||
bool isnull;
|
||||
ResultRelInfo *resultRelInfo;
|
||||
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
|
||||
TupleTable tupleTable;
|
||||
TupleTableSlot *slot;
|
||||
bool file_has_oids;
|
||||
int *defmap;
|
||||
|
@ -1518,10 +1517,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||
estate->es_num_result_relations = 1;
|
||||
estate->es_result_relation_info = resultRelInfo;
|
||||
|
||||
/* Set up a dummy tuple table too */
|
||||
tupleTable = ExecCreateTupleTable(1);
|
||||
slot = ExecAllocTableSlot(tupleTable);
|
||||
ExecSetSlotDescriptor(slot, tupDesc, false);
|
||||
/* Set up a tuple slot too */
|
||||
slot = MakeSingleTupleTableSlot(tupDesc);
|
||||
|
||||
econtext = GetPerTupleExprContext(estate);
|
||||
|
||||
|
@ -1989,7 +1986,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
|||
pfree(constraintexprs);
|
||||
pfree(force_notnull);
|
||||
|
||||
ExecDropTupleTable(tupleTable, true);
|
||||
ExecDropSingleTupleTableSlot(slot);
|
||||
|
||||
ExecCloseIndices(resultRelInfo);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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)
|
|||
{
|
||||
ExprContext *econtext;
|
||||
Datum *values;
|
||||
char *nulls;
|
||||
bool *isnull;
|
||||
TupleTableSlot *oldslot;
|
||||
TupleTableSlot *newslot;
|
||||
HeapScanDesc scan;
|
||||
|
@ -2471,17 +2471,15 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|||
* the tuples are the same, the tupDescs might not be (consider
|
||||
* ADD COLUMN without a default).
|
||||
*/
|
||||
oldslot = MakeTupleTableSlot();
|
||||
ExecSetSlotDescriptor(oldslot, oldTupDesc, false);
|
||||
newslot = MakeTupleTableSlot();
|
||||
ExecSetSlotDescriptor(newslot, newTupDesc, false);
|
||||
oldslot = MakeSingleTupleTableSlot(oldTupDesc);
|
||||
newslot = MakeSingleTupleTableSlot(newTupDesc);
|
||||
|
||||
/* Preallocate values/nulls arrays */
|
||||
/* Preallocate values/isnull arrays */
|
||||
i = Max(newTupDesc->natts, oldTupDesc->natts);
|
||||
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(nulls, 'n', i * sizeof(char));
|
||||
memset(isnull, true, i * sizeof(bool));
|
||||
|
||||
/*
|
||||
* Any attributes that are dropped according to the new tuple
|
||||
|
@ -2512,11 +2510,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|||
if (newrel)
|
||||
{
|
||||
/* 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 */
|
||||
foreach (lc, dropped_attrs)
|
||||
nulls[lfirst_int(lc)] = 'n';
|
||||
isnull[lfirst_int(lc)] = true;
|
||||
|
||||
/*
|
||||
* Process supplied expressions to replace selected
|
||||
|
@ -2528,16 +2526,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|||
foreach(l, tab->newvals)
|
||||
{
|
||||
NewColumnValue *ex = lfirst(l);
|
||||
bool isNull;
|
||||
|
||||
values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
|
||||
econtext,
|
||||
&isNull,
|
||||
&isnull[ex->attnum - 1],
|
||||
NULL);
|
||||
if (isNull)
|
||||
nulls[ex->attnum - 1] = 'n';
|
||||
else
|
||||
nulls[ex->attnum - 1] = ' ';
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2545,7 +2538,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|||
* pfree it, since the per-tuple memory context will
|
||||
* 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 */
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
|||
{
|
||||
ResultRelInfo *resultRelInfo;
|
||||
EState *estate;
|
||||
TupleTable tupleTable;
|
||||
TupleTableSlot *slot;
|
||||
} ExecContextData;
|
||||
typedef ExecContextData *ExecContext;
|
||||
|
@ -141,16 +140,14 @@ ExecContext_Init(ExecContext ec, Relation rel)
|
|||
ec->estate->es_num_result_relations = 1;
|
||||
ec->estate->es_result_relation_info = ec->resultRelInfo;
|
||||
|
||||
/* Set up a dummy tuple table too */
|
||||
ec->tupleTable = ExecCreateTupleTable(1);
|
||||
ec->slot = ExecAllocTableSlot(ec->tupleTable);
|
||||
ExecSetSlotDescriptor(ec->slot, tupdesc, false);
|
||||
/* Set up a tuple slot too */
|
||||
ec->slot = MakeSingleTupleTableSlot(tupdesc);
|
||||
}
|
||||
|
||||
static void
|
||||
ExecContext_Finish(ExecContext ec)
|
||||
{
|
||||
ExecDropTupleTable(ec->tupleTable, true);
|
||||
ExecDropSingleTupleTableSlot(ec->slot);
|
||||
ExecCloseIndices(ec->resultRelInfo);
|
||||
FreeExecutorState(ec->estate);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.13 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.14 2005/03/16 21:38:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -41,8 +41,7 @@ static int TupleHashTableMatch(const void *key1, const void *key2,
|
|||
* This actually implements SQL's notion of "not distinct". Two nulls
|
||||
* match, a null and a not-null don't match.
|
||||
*
|
||||
* tuple1, tuple2: the tuples to compare
|
||||
* tupdesc: tuple descriptor applying to both tuples
|
||||
* slot1, slot2: the tuples to compare (must have same columns!)
|
||||
* numCols: the number of attributes to be examined
|
||||
* matchColIdx: array of attribute column numbers
|
||||
* eqFunctions: array of fmgr lookup info for the equality functions to use
|
||||
|
@ -51,9 +50,8 @@ static int TupleHashTableMatch(const void *key1, const void *key2,
|
|||
* NB: evalContext is reset each time!
|
||||
*/
|
||||
bool
|
||||
execTuplesMatch(HeapTuple tuple1,
|
||||
HeapTuple tuple2,
|
||||
TupleDesc tupdesc,
|
||||
execTuplesMatch(TupleTableSlot *slot1,
|
||||
TupleTableSlot *slot2,
|
||||
int numCols,
|
||||
AttrNumber *matchColIdx,
|
||||
FmgrInfo *eqfunctions,
|
||||
|
@ -84,15 +82,9 @@ execTuplesMatch(HeapTuple tuple1,
|
|||
bool isNull1,
|
||||
isNull2;
|
||||
|
||||
attr1 = heap_getattr(tuple1,
|
||||
att,
|
||||
tupdesc,
|
||||
&isNull1);
|
||||
attr1 = slot_getattr(slot1, att, &isNull1);
|
||||
|
||||
attr2 = heap_getattr(tuple2,
|
||||
att,
|
||||
tupdesc,
|
||||
&isNull2);
|
||||
attr2 = slot_getattr(slot2, att, &isNull2);
|
||||
|
||||
if (isNull1 != isNull2)
|
||||
{
|
||||
|
@ -129,9 +121,8 @@ execTuplesMatch(HeapTuple tuple1,
|
|||
* Parameters are identical to execTuplesMatch.
|
||||
*/
|
||||
bool
|
||||
execTuplesUnequal(HeapTuple tuple1,
|
||||
HeapTuple tuple2,
|
||||
TupleDesc tupdesc,
|
||||
execTuplesUnequal(TupleTableSlot *slot1,
|
||||
TupleTableSlot *slot2,
|
||||
int numCols,
|
||||
AttrNumber *matchColIdx,
|
||||
FmgrInfo *eqfunctions,
|
||||
|
@ -162,18 +153,12 @@ execTuplesUnequal(HeapTuple tuple1,
|
|||
bool isNull1,
|
||||
isNull2;
|
||||
|
||||
attr1 = heap_getattr(tuple1,
|
||||
att,
|
||||
tupdesc,
|
||||
&isNull1);
|
||||
attr1 = slot_getattr(slot1, att, &isNull1);
|
||||
|
||||
if (isNull1)
|
||||
continue; /* can't prove anything here */
|
||||
|
||||
attr2 = heap_getattr(tuple2,
|
||||
att,
|
||||
tupdesc,
|
||||
&isNull2);
|
||||
attr2 = slot_getattr(slot2, att, &isNull2);
|
||||
|
||||
if (isNull2)
|
||||
continue; /* can't prove anything here */
|
||||
|
@ -312,6 +297,8 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
|
|||
hashtable->tablecxt = tablecxt;
|
||||
hashtable->tempcxt = tempcxt;
|
||||
hashtable->entrysize = entrysize;
|
||||
hashtable->tableslot = NULL; /* will be made on first lookup */
|
||||
hashtable->inputslot = NULL;
|
||||
|
||||
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
||||
hash_ctl.keysize = sizeof(TupleHashEntryData);
|
||||
|
@ -342,13 +329,27 @@ TupleHashEntry
|
|||
LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
||||
bool *isnew)
|
||||
{
|
||||
HeapTuple tuple = slot->val;
|
||||
TupleDesc tupdesc = slot->ttc_tupleDescriptor;
|
||||
TupleHashEntry entry;
|
||||
MemoryContext oldContext;
|
||||
TupleHashTable saveCurHT;
|
||||
TupleHashEntryData dummy;
|
||||
bool found;
|
||||
|
||||
/* If first time through, clone the input slot to make table slot */
|
||||
if (hashtable->tableslot == NULL)
|
||||
{
|
||||
TupleDesc tupdesc;
|
||||
|
||||
oldContext = MemoryContextSwitchTo(hashtable->tablecxt);
|
||||
/*
|
||||
* We copy the input tuple descriptor just for safety --- we assume
|
||||
* all input tuples will have equivalent descriptors.
|
||||
*/
|
||||
tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
|
||||
hashtable->tableslot = MakeSingleTupleTableSlot(tupdesc);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
}
|
||||
|
||||
/* Need to run the hash functions in short-lived context */
|
||||
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
|
||||
|
||||
|
@ -358,13 +359,14 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
|||
* We save and restore CurTupleHashTable just in case someone manages to
|
||||
* invoke this code re-entrantly.
|
||||
*/
|
||||
hashtable->tupdesc = tupdesc;
|
||||
hashtable->inputslot = slot;
|
||||
saveCurHT = CurTupleHashTable;
|
||||
CurTupleHashTable = hashtable;
|
||||
|
||||
/* Search the hash table */
|
||||
dummy.firstTuple = NULL; /* flag to reference inputslot */
|
||||
entry = (TupleHashEntry) hash_search(hashtable->hashtab,
|
||||
&tuple,
|
||||
&dummy,
|
||||
isnew ? HASH_ENTER : HASH_FIND,
|
||||
&found);
|
||||
|
||||
|
@ -392,7 +394,7 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
|||
|
||||
/* Copy the first tuple into the table context */
|
||||
MemoryContextSwitchTo(hashtable->tablecxt);
|
||||
entry->firstTuple = heap_copytuple(tuple);
|
||||
entry->firstTuple = ExecCopySlotTuple(slot);
|
||||
|
||||
*isnew = true;
|
||||
}
|
||||
|
@ -408,9 +410,12 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
|||
/*
|
||||
* Compute the hash value for a tuple
|
||||
*
|
||||
* The passed-in key is a pointer to a HeapTuple pointer -- this is either
|
||||
* the firstTuple field of a TupleHashEntry struct, or the key value passed
|
||||
* to hash_search. We ignore the keysize.
|
||||
* The passed-in key is a pointer to TupleHashEntryData. In an actual
|
||||
* hash table entry, the firstTuple field therein points to a physical
|
||||
* tuple. LookupTupleHashEntry sets up a dummy TupleHashEntryData with
|
||||
* a NULL firstTuple field --- that cues us to look at the inputslot instead.
|
||||
* This convention avoids the need to materialize virtual input tuples
|
||||
* unless they actually need to get copied into the table.
|
||||
*
|
||||
* CurTupleHashTable must be set before calling this, since dynahash.c
|
||||
* doesn't provide any API that would let us get at the hashtable otherwise.
|
||||
|
@ -421,14 +426,27 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
|||
static uint32
|
||||
TupleHashTableHash(const void *key, Size keysize)
|
||||
{
|
||||
HeapTuple tuple = *(const HeapTuple *) key;
|
||||
HeapTuple tuple = ((const TupleHashEntryData *) key)->firstTuple;
|
||||
TupleTableSlot *slot;
|
||||
TupleHashTable hashtable = CurTupleHashTable;
|
||||
int numCols = hashtable->numCols;
|
||||
AttrNumber *keyColIdx = hashtable->keyColIdx;
|
||||
TupleDesc tupdesc = hashtable->tupdesc;
|
||||
uint32 hashkey = 0;
|
||||
int i;
|
||||
|
||||
if (tuple == NULL)
|
||||
{
|
||||
/* Process the current input tuple for the table */
|
||||
slot = hashtable->inputslot;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Process a tuple already stored in the table */
|
||||
/* (this case never actually occurs in current dynahash.c code) */
|
||||
slot = hashtable->tableslot;
|
||||
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
|
||||
}
|
||||
|
||||
for (i = 0; i < numCols; i++)
|
||||
{
|
||||
AttrNumber att = keyColIdx[i];
|
||||
|
@ -438,7 +456,7 @@ TupleHashTableHash(const void *key, Size keysize)
|
|||
/* rotate hashkey left 1 bit at each step */
|
||||
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
|
||||
|
||||
attr = heap_getattr(tuple, att, tupdesc, &isNull);
|
||||
attr = slot_getattr(slot, att, &isNull);
|
||||
|
||||
if (!isNull) /* treat nulls as having hash key 0 */
|
||||
{
|
||||
|
@ -456,7 +474,7 @@ TupleHashTableHash(const void *key, Size keysize)
|
|||
/*
|
||||
* See whether two tuples (presumably of the same hash value) match
|
||||
*
|
||||
* As above, the passed pointers are pointers to HeapTuple pointers.
|
||||
* As above, the passed pointers are pointers to TupleHashEntryData.
|
||||
*
|
||||
* CurTupleHashTable must be set before calling this, since dynahash.c
|
||||
* doesn't provide any API that would let us get at the hashtable otherwise.
|
||||
|
@ -467,13 +485,28 @@ TupleHashTableHash(const void *key, Size keysize)
|
|||
static int
|
||||
TupleHashTableMatch(const void *key1, const void *key2, Size keysize)
|
||||
{
|
||||
HeapTuple tuple1 = *(const HeapTuple *) key1;
|
||||
HeapTuple tuple2 = *(const HeapTuple *) key2;
|
||||
HeapTuple tuple1 = ((const TupleHashEntryData *) key1)->firstTuple;
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
HeapTuple tuple2 = ((const TupleHashEntryData *) key2)->firstTuple;
|
||||
#endif
|
||||
TupleTableSlot *slot1;
|
||||
TupleTableSlot *slot2;
|
||||
TupleHashTable hashtable = CurTupleHashTable;
|
||||
|
||||
if (execTuplesMatch(tuple1,
|
||||
tuple2,
|
||||
hashtable->tupdesc,
|
||||
/*
|
||||
* We assume that dynahash.c will only ever call us with the first
|
||||
* argument being an actual table entry, and the second argument being
|
||||
* LookupTupleHashEntry's dummy TupleHashEntryData. The other direction
|
||||
* could be supported too, but is not currently used by dynahash.c.
|
||||
*/
|
||||
Assert(tuple1 != NULL);
|
||||
slot1 = hashtable->tableslot;
|
||||
ExecStoreTuple(tuple1, slot1, InvalidBuffer, false);
|
||||
Assert(tuple2 == NULL);
|
||||
slot2 = hashtable->inputslot;
|
||||
|
||||
if (execTuplesMatch(slot1,
|
||||
slot2,
|
||||
hashtable->numCols,
|
||||
hashtable->keyColIdx,
|
||||
hashtable->eqfunctions,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.47 2005/03/14 04:41:12 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.48 2005/03/16 21:38:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -42,10 +42,10 @@
|
|||
* We then execute the plan ignoring the "resjunk" attributes.
|
||||
*
|
||||
* Finally, when at the top level we get back a tuple, we can call
|
||||
* 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we
|
||||
* are interested in, and 'ExecRemoveJunk' to remove all the junk attributes
|
||||
* from a tuple. This new "clean" tuple is then printed, replaced, deleted
|
||||
* or inserted.
|
||||
* ExecGetJunkAttribute to retrieve the value of the junk attributes we
|
||||
* are interested in, and ExecFilterJunk or ExecRemoveJunk to remove all
|
||||
* the junk attributes from a tuple. This new "clean" tuple is then printed,
|
||||
* replaced, deleted or inserted.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -75,6 +75,14 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
|
|||
*/
|
||||
cleanTupType = ExecCleanTypeFromTL(targetList, hasoid);
|
||||
|
||||
/*
|
||||
* Use the given slot, or make a new slot if we weren't given one.
|
||||
*/
|
||||
if (slot)
|
||||
ExecSetSlotDescriptor(slot, cleanTupType, false);
|
||||
else
|
||||
slot = MakeSingleTupleTableSlot(cleanTupType);
|
||||
|
||||
/*
|
||||
* Now calculate the mapping between the original tuple's attributes and
|
||||
* the "clean" tuple's attributes.
|
||||
|
@ -115,9 +123,6 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
|
|||
junkfilter->jf_cleanMap = cleanMap;
|
||||
junkfilter->jf_resultSlot = slot;
|
||||
|
||||
if (slot)
|
||||
ExecSetSlotDescriptor(slot, cleanTupType, false);
|
||||
|
||||
return junkfilter;
|
||||
}
|
||||
|
||||
|
@ -142,6 +147,14 @@ ExecInitJunkFilterConversion(List *targetList,
|
|||
ListCell *t;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Use the given slot, or make a new slot if we weren't given one.
|
||||
*/
|
||||
if (slot)
|
||||
ExecSetSlotDescriptor(slot, cleanTupType, false);
|
||||
else
|
||||
slot = MakeSingleTupleTableSlot(cleanTupType);
|
||||
|
||||
/*
|
||||
* Calculate the mapping between the original tuple's attributes and
|
||||
* the "clean" tuple's attributes.
|
||||
|
@ -188,9 +201,6 @@ ExecInitJunkFilterConversion(List *targetList,
|
|||
junkfilter->jf_cleanMap = cleanMap;
|
||||
junkfilter->jf_resultSlot = slot;
|
||||
|
||||
if (slot)
|
||||
ExecSetSlotDescriptor(slot, cleanTupType, false);
|
||||
|
||||
return junkfilter;
|
||||
}
|
||||
|
||||
|
@ -234,115 +244,78 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
|
|||
}
|
||||
|
||||
/*
|
||||
* ExecRemoveJunk
|
||||
* ExecFilterJunk
|
||||
*
|
||||
* Construct and return a tuple with all the junk attributes removed.
|
||||
*
|
||||
* Note: for historical reasons, this does not store the constructed
|
||||
* tuple into the junkfilter's resultSlot. The caller should do that
|
||||
* if it wants to.
|
||||
* Construct and return a slot with all the junk attributes removed.
|
||||
*/
|
||||
HeapTuple
|
||||
ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
|
||||
TupleTableSlot *
|
||||
ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
|
||||
{
|
||||
#define PREALLOC_SIZE 64
|
||||
HeapTuple tuple;
|
||||
HeapTuple cleanTuple;
|
||||
TupleTableSlot *resultSlot;
|
||||
AttrNumber *cleanMap;
|
||||
TupleDesc cleanTupType;
|
||||
TupleDesc tupType;
|
||||
int cleanLength;
|
||||
int oldLength;
|
||||
int i;
|
||||
Datum *values;
|
||||
char *nulls;
|
||||
bool *isnull;
|
||||
Datum *old_values;
|
||||
char *old_nulls;
|
||||
Datum values_array[PREALLOC_SIZE];
|
||||
Datum old_values_array[PREALLOC_SIZE];
|
||||
char nulls_array[PREALLOC_SIZE];
|
||||
char old_nulls_array[PREALLOC_SIZE];
|
||||
bool *old_isnull;
|
||||
|
||||
/*
|
||||
* get info from the slot and the junk filter
|
||||
* Extract all the values of the old tuple.
|
||||
*/
|
||||
tuple = slot->val;
|
||||
tupType = slot->ttc_tupleDescriptor;
|
||||
oldLength = tupType->natts + 1; /* +1 for NULL */
|
||||
slot_getallattrs(slot);
|
||||
old_values = slot->tts_values;
|
||||
old_isnull = slot->tts_isnull;
|
||||
|
||||
/*
|
||||
* get info from the junk filter
|
||||
*/
|
||||
cleanTupType = junkfilter->jf_cleanTupType;
|
||||
cleanLength = cleanTupType->natts;
|
||||
cleanMap = junkfilter->jf_cleanMap;
|
||||
resultSlot = junkfilter->jf_resultSlot;
|
||||
|
||||
/*
|
||||
* Create the arrays that will hold the attribute values and the null
|
||||
* information for the old tuple and new "clean" tuple.
|
||||
*
|
||||
* Note: we use memory on the stack to optimize things when we are
|
||||
* dealing with a small number of attributes. for large tuples we just
|
||||
* use palloc.
|
||||
* Prepare to build a virtual result tuple.
|
||||
*/
|
||||
if (cleanLength > PREALLOC_SIZE)
|
||||
{
|
||||
values = (Datum *) palloc(cleanLength * sizeof(Datum));
|
||||
nulls = (char *) palloc(cleanLength * sizeof(char));
|
||||
}
|
||||
else
|
||||
{
|
||||
values = values_array;
|
||||
nulls = nulls_array;
|
||||
}
|
||||
if (oldLength > PREALLOC_SIZE)
|
||||
{
|
||||
old_values = (Datum *) palloc(oldLength * sizeof(Datum));
|
||||
old_nulls = (char *) palloc(oldLength * sizeof(char));
|
||||
}
|
||||
else
|
||||
{
|
||||
old_values = old_values_array;
|
||||
old_nulls = old_nulls_array;
|
||||
}
|
||||
ExecClearTuple(resultSlot);
|
||||
values = resultSlot->tts_values;
|
||||
isnull = resultSlot->tts_isnull;
|
||||
|
||||
/*
|
||||
* Extract all the values of the old tuple, offsetting the arrays
|
||||
* so that old_values[0] is NULL and old_values[1] is the first
|
||||
* source attribute; this exactly matches the numbering convention
|
||||
* in cleanMap.
|
||||
*/
|
||||
heap_deformtuple(tuple, tupType, old_values + 1, old_nulls + 1);
|
||||
old_values[0] = (Datum) 0;
|
||||
old_nulls[0] = 'n';
|
||||
|
||||
/*
|
||||
* Transpose into proper fields of the new tuple.
|
||||
* Transpose data into proper fields of the new tuple.
|
||||
*/
|
||||
for (i = 0; i < cleanLength; i++)
|
||||
{
|
||||
int j = cleanMap[i];
|
||||
|
||||
values[i] = old_values[j];
|
||||
nulls[i] = old_nulls[j];
|
||||
if (j == 0)
|
||||
{
|
||||
values[i] = (Datum) 0;
|
||||
isnull[i] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
values[i] = old_values[j - 1];
|
||||
isnull[i] = old_isnull[j - 1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now form the new tuple.
|
||||
* And return the virtual tuple.
|
||||
*/
|
||||
cleanTuple = heap_formtuple(cleanTupType, values, nulls);
|
||||
|
||||
/*
|
||||
* We are done. Free any space allocated for 'values' and 'nulls' and
|
||||
* return the new tuple.
|
||||
*/
|
||||
if (values != values_array)
|
||||
{
|
||||
pfree(values);
|
||||
pfree(nulls);
|
||||
}
|
||||
if (old_values != old_values_array)
|
||||
{
|
||||
pfree(old_values);
|
||||
pfree(old_nulls);
|
||||
}
|
||||
|
||||
return cleanTuple;
|
||||
return ExecStoreVirtualTuple(resultSlot);
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecRemoveJunk
|
||||
*
|
||||
* Convenience routine to generate a physical clean tuple,
|
||||
* rather than just a virtual slot.
|
||||
*/
|
||||
HeapTuple
|
||||
ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
|
||||
{
|
||||
return ExecCopySlotTuple(ExecFilterJunk(junkfilter, slot));
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.241 2005/01/14 17:53:33 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.242 2005/03/16 21:38:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1082,7 +1082,6 @@ lnext: ;
|
|||
if ((junkfilter = estate->es_junkFilter) != NULL)
|
||||
{
|
||||
Datum datum;
|
||||
HeapTuple newTuple;
|
||||
bool isNull;
|
||||
|
||||
/*
|
||||
|
@ -1180,13 +1179,7 @@ lnext: ;
|
|||
* Finally create a new "clean" tuple with all junk attributes
|
||||
* removed
|
||||
*/
|
||||
newTuple = ExecRemoveJunk(junkfilter, slot);
|
||||
|
||||
slot = ExecStoreTuple(newTuple, /* tuple to store */
|
||||
junkfilter->jf_resultSlot, /* dest slot */
|
||||
InvalidBuffer, /* this tuple has no
|
||||
* buffer */
|
||||
true); /* tuple should be pfreed */
|
||||
slot = ExecFilterJunk(junkfilter, slot);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1276,15 +1269,6 @@ ExecSelect(TupleTableSlot *slot,
|
|||
DestReceiver *dest,
|
||||
EState *estate)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
TupleDesc attrtype;
|
||||
|
||||
/*
|
||||
* get the heap tuple out of the tuple table slot
|
||||
*/
|
||||
tuple = slot->val;
|
||||
attrtype = slot->ttc_tupleDescriptor;
|
||||
|
||||
/*
|
||||
* insert the tuple into the "into relation"
|
||||
*
|
||||
|
@ -1292,15 +1276,20 @@ ExecSelect(TupleTableSlot *slot,
|
|||
*/
|
||||
if (estate->es_into_relation_descriptor != NULL)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
|
||||
tuple = ExecCopySlotTuple(slot);
|
||||
heap_insert(estate->es_into_relation_descriptor, tuple,
|
||||
estate->es_snapshot->curcid);
|
||||
/* we know there are no indexes to update */
|
||||
heap_freetuple(tuple);
|
||||
IncrAppended();
|
||||
}
|
||||
|
||||
/*
|
||||
* send the tuple to the destination
|
||||
*/
|
||||
(*dest->receiveTuple) (tuple, attrtype, dest);
|
||||
(*dest->receiveSlot) (slot, dest);
|
||||
IncrRetrieved();
|
||||
(estate->es_processed)++;
|
||||
}
|
||||
|
@ -1325,9 +1314,10 @@ ExecInsert(TupleTableSlot *slot,
|
|||
Oid newId;
|
||||
|
||||
/*
|
||||
* get the heap tuple out of the tuple table slot
|
||||
* get the heap tuple out of the tuple table slot, making sure
|
||||
* we have a writable copy
|
||||
*/
|
||||
tuple = slot->val;
|
||||
tuple = ExecMaterializeSlot(slot);
|
||||
|
||||
/*
|
||||
* get information on the (current) result relation
|
||||
|
@ -1520,9 +1510,10 @@ ExecUpdate(TupleTableSlot *slot,
|
|||
elog(ERROR, "cannot UPDATE during bootstrap");
|
||||
|
||||
/*
|
||||
* get the heap tuple out of the tuple table slot
|
||||
* get the heap tuple out of the tuple table slot, making sure
|
||||
* we have a writable copy
|
||||
*/
|
||||
tuple = slot->val;
|
||||
tuple = ExecMaterializeSlot(slot);
|
||||
|
||||
/*
|
||||
* get information on the (current) result relation
|
||||
|
@ -1604,10 +1595,8 @@ lreplace:;
|
|||
if (!TupIsNull(epqslot))
|
||||
{
|
||||
*tupleid = ctid;
|
||||
tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot);
|
||||
slot = ExecStoreTuple(tuple,
|
||||
estate->es_junkFilter->jf_resultSlot,
|
||||
InvalidBuffer, true);
|
||||
slot = ExecFilterJunk(estate->es_junkFilter, epqslot);
|
||||
tuple = ExecMaterializeSlot(slot);
|
||||
goto lreplace;
|
||||
}
|
||||
}
|
||||
|
@ -1712,7 +1701,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
|
|||
TupleTableSlot *slot, EState *estate)
|
||||
{
|
||||
Relation rel = resultRelInfo->ri_RelationDesc;
|
||||
HeapTuple tuple = slot->val;
|
||||
TupleConstr *constr = rel->rd_att->constr;
|
||||
|
||||
Assert(constr);
|
||||
|
@ -1725,7 +1713,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
|
|||
for (attrChk = 1; attrChk <= natts; attrChk++)
|
||||
{
|
||||
if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
|
||||
heap_attisnull(tuple, attrChk))
|
||||
slot_attisnull(slot, attrChk))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NOT_NULL_VIOLATION),
|
||||
errmsg("null value in column \"%s\" violates not-null constraint",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.172 2005/03/14 04:41:12 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.173 2005/03/16 21:38:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -479,7 +479,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
|
|||
*/
|
||||
if (attnum > 0)
|
||||
{
|
||||
TupleDesc tuple_type = slot->ttc_tupleDescriptor;
|
||||
TupleDesc tuple_type = slot->tts_tupleDescriptor;
|
||||
|
||||
/*
|
||||
* This assert checks that the attnum is valid.
|
||||
|
@ -1333,10 +1333,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
|
|||
}
|
||||
else
|
||||
{
|
||||
char nullflag;
|
||||
|
||||
nullflag = fcinfo.isnull ? 'n' : ' ';
|
||||
tuple = heap_formtuple(tupdesc, &result, &nullflag);
|
||||
tuple = heap_form_tuple(tupdesc, &result, &fcinfo.isnull);
|
||||
}
|
||||
|
||||
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
||||
|
@ -1384,14 +1381,14 @@ no_function_result:
|
|||
{
|
||||
int natts = expectedDesc->natts;
|
||||
Datum *nulldatums;
|
||||
char *nullflags;
|
||||
bool *nullflags;
|
||||
HeapTuple tuple;
|
||||
|
||||
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||
nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
|
||||
nullflags = (char *) palloc(natts * sizeof(char));
|
||||
memset(nullflags, 'n', natts * sizeof(char));
|
||||
tuple = heap_formtuple(expectedDesc, nulldatums, nullflags);
|
||||
nullflags = (bool *) palloc(natts * sizeof(bool));
|
||||
memset(nullflags, true, natts * sizeof(bool));
|
||||
tuple = heap_form_tuple(expectedDesc, nulldatums, nullflags);
|
||||
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
||||
tuplestore_puttuple(tupstore, tuple);
|
||||
}
|
||||
|
@ -1843,9 +1840,9 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
|
|||
HeapTupleData tmptup;
|
||||
AttrNumber *attrMap = cstate->attrMap;
|
||||
Datum *invalues = cstate->invalues;
|
||||
char *innulls = cstate->innulls;
|
||||
bool *inisnull = cstate->inisnull;
|
||||
Datum *outvalues = cstate->outvalues;
|
||||
char *outnulls = cstate->outnulls;
|
||||
bool *outisnull = cstate->outisnull;
|
||||
int i;
|
||||
int outnatts = cstate->outdesc->natts;
|
||||
|
||||
|
@ -1861,7 +1858,7 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
|
|||
Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
|
||||
|
||||
/*
|
||||
* heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
|
||||
* heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader.
|
||||
*/
|
||||
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
|
||||
tmptup.t_data = tuple;
|
||||
|
@ -1872,9 +1869,9 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
|
|||
* source attribute; this exactly matches the numbering convention
|
||||
* in attrMap.
|
||||
*/
|
||||
heap_deformtuple(&tmptup, cstate->indesc, invalues + 1, innulls + 1);
|
||||
heap_deform_tuple(&tmptup, cstate->indesc, invalues + 1, inisnull + 1);
|
||||
invalues[0] = (Datum) 0;
|
||||
innulls[0] = 'n';
|
||||
inisnull[0] = true;
|
||||
|
||||
/*
|
||||
* Transpose into proper fields of the new tuple.
|
||||
|
@ -1884,13 +1881,13 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
|
|||
int j = attrMap[i];
|
||||
|
||||
outvalues[i] = invalues[j];
|
||||
outnulls[i] = innulls[j];
|
||||
outisnull[i] = inisnull[j];
|
||||
}
|
||||
|
||||
/*
|
||||
* Now form the new tuple.
|
||||
*/
|
||||
result = heap_formtuple(cstate->outdesc, outvalues, outnulls);
|
||||
result = heap_form_tuple(cstate->outdesc, outvalues, outisnull);
|
||||
|
||||
return HeapTupleGetDatum(result);
|
||||
}
|
||||
|
@ -2187,7 +2184,7 @@ ExecEvalRow(RowExprState *rstate,
|
|||
{
|
||||
HeapTuple tuple;
|
||||
Datum *values;
|
||||
char *nulls;
|
||||
bool *isnull;
|
||||
int natts;
|
||||
ListCell *arg;
|
||||
int i;
|
||||
|
@ -2200,27 +2197,25 @@ ExecEvalRow(RowExprState *rstate,
|
|||
/* Allocate workspace */
|
||||
natts = rstate->tupdesc->natts;
|
||||
values = (Datum *) palloc0(natts * sizeof(Datum));
|
||||
nulls = (char *) palloc(natts * sizeof(char));
|
||||
isnull = (bool *) palloc(natts * sizeof(bool));
|
||||
|
||||
/* preset to nulls in case rowtype has some later-added columns */
|
||||
memset(nulls, 'n', natts * sizeof(char));
|
||||
memset(isnull, true, natts * sizeof(bool));
|
||||
|
||||
/* Evaluate field values */
|
||||
i = 0;
|
||||
foreach(arg, rstate->args)
|
||||
{
|
||||
ExprState *e = (ExprState *) lfirst(arg);
|
||||
bool eisnull;
|
||||
|
||||
values[i] = ExecEvalExpr(e, econtext, &eisnull, NULL);
|
||||
nulls[i] = eisnull ? 'n' : ' ';
|
||||
values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL);
|
||||
i++;
|
||||
}
|
||||
|
||||
tuple = heap_formtuple(rstate->tupdesc, values, nulls);
|
||||
tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
|
||||
|
||||
pfree(values);
|
||||
pfree(nulls);
|
||||
pfree(isnull);
|
||||
|
||||
return HeapTupleGetDatum(tuple);
|
||||
}
|
||||
|
@ -2628,7 +2623,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
|
|||
Datum tupDatum;
|
||||
TupleDesc tupDesc;
|
||||
Datum *values;
|
||||
char *nulls;
|
||||
bool *isnull;
|
||||
Datum save_datum;
|
||||
bool save_isNull;
|
||||
ListCell *l1,
|
||||
|
@ -2658,12 +2653,12 @@ ExecEvalFieldStore(FieldStoreState *fstate,
|
|||
|
||||
/* Allocate workspace */
|
||||
values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
|
||||
nulls = (char *) palloc(tupDesc->natts * sizeof(char));
|
||||
isnull = (bool *) palloc(tupDesc->natts * sizeof(bool));
|
||||
|
||||
if (!*isNull)
|
||||
{
|
||||
/*
|
||||
* heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
|
||||
* heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader.
|
||||
* We set all the fields in the struct just in case.
|
||||
*/
|
||||
HeapTupleHeader tuphdr;
|
||||
|
@ -2675,12 +2670,12 @@ ExecEvalFieldStore(FieldStoreState *fstate,
|
|||
tmptup.t_tableOid = InvalidOid;
|
||||
tmptup.t_data = tuphdr;
|
||||
|
||||
heap_deformtuple(&tmptup, tupDesc, values, nulls);
|
||||
heap_deform_tuple(&tmptup, tupDesc, values, isnull);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert null input tuple into an all-nulls row */
|
||||
memset(nulls, 'n', tupDesc->natts * sizeof(char));
|
||||
memset(isnull, true, tupDesc->natts * sizeof(bool));
|
||||
}
|
||||
|
||||
/* Result is never null */
|
||||
|
@ -2693,7 +2688,6 @@ ExecEvalFieldStore(FieldStoreState *fstate,
|
|||
{
|
||||
ExprState *newval = (ExprState *) lfirst(l1);
|
||||
AttrNumber fieldnum = lfirst_int(l2);
|
||||
bool eisnull;
|
||||
|
||||
Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
|
||||
|
||||
|
@ -2705,22 +2699,21 @@ ExecEvalFieldStore(FieldStoreState *fstate,
|
|||
* the value would be needed.
|
||||
*/
|
||||
econtext->caseValue_datum = values[fieldnum - 1];
|
||||
econtext->caseValue_isNull = (nulls[fieldnum - 1] == 'n');
|
||||
econtext->caseValue_isNull = isnull[fieldnum - 1];
|
||||
|
||||
values[fieldnum - 1] = ExecEvalExpr(newval,
|
||||
econtext,
|
||||
&eisnull,
|
||||
&isnull[fieldnum - 1],
|
||||
NULL);
|
||||
nulls[fieldnum - 1] = eisnull ? 'n' : ' ';
|
||||
}
|
||||
|
||||
econtext->caseValue_datum = save_datum;
|
||||
econtext->caseValue_isNull = save_isNull;
|
||||
|
||||
tuple = heap_formtuple(tupDesc, values, nulls);
|
||||
tuple = heap_form_tuple(tupDesc, values, isnull);
|
||||
|
||||
pfree(values);
|
||||
pfree(nulls);
|
||||
pfree(isnull);
|
||||
|
||||
return HeapTupleGetDatum(tuple);
|
||||
}
|
||||
|
@ -3074,10 +3067,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
|||
/* preallocate workspace for Datum arrays */
|
||||
n = cstate->indesc->natts + 1; /* +1 for NULL */
|
||||
cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
|
||||
cstate->innulls = (char *) palloc(n * sizeof(char));
|
||||
cstate->inisnull = (bool *) palloc(n * sizeof(bool));
|
||||
n = cstate->outdesc->natts;
|
||||
cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
|
||||
cstate->outnulls = (char *) palloc(n * sizeof(char));
|
||||
cstate->outisnull = (bool *) palloc(n * sizeof(bool));
|
||||
state = (ExprState *) cstate;
|
||||
}
|
||||
break;
|
||||
|
@ -3479,55 +3472,37 @@ ExecCleanTargetListLength(List *targetlist)
|
|||
return len;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecTargetList
|
||||
*
|
||||
/*
|
||||
* ExecTargetList
|
||||
* Evaluates a targetlist with respect to the given
|
||||
* expression context and returns a tuple.
|
||||
* expression context. Returns TRUE if we were able to create
|
||||
* a result, FALSE if we have exhausted a set-valued expression.
|
||||
*
|
||||
* The caller must pass workspace for the values and nulls arrays
|
||||
* as well as the itemIsDone array. This convention saves palloc'ing
|
||||
* workspace on each call, and some callers may find it useful to examine
|
||||
* the values array directly.
|
||||
* Results are stored into the passed values and isnull arrays.
|
||||
* The caller must provide an itemIsDone array that persists across calls.
|
||||
*
|
||||
* As with ExecEvalExpr, the caller should pass isDone = NULL if not
|
||||
* prepared to deal with sets of result tuples. Otherwise, a return
|
||||
* of *isDone = ExprMultipleResult signifies a set element, and a return
|
||||
* of *isDone = ExprEndResult signifies end of the set of tuple.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static HeapTuple
|
||||
static bool
|
||||
ExecTargetList(List *targetlist,
|
||||
TupleDesc targettype,
|
||||
ExprContext *econtext,
|
||||
Datum *values,
|
||||
char *nulls,
|
||||
bool *isnull,
|
||||
ExprDoneCond *itemIsDone,
|
||||
ExprDoneCond *isDone)
|
||||
{
|
||||
MemoryContext oldContext;
|
||||
ListCell *tl;
|
||||
bool isNull;
|
||||
bool haveDoneSets;
|
||||
|
||||
/*
|
||||
* debugging stuff
|
||||
*/
|
||||
EV_printf("ExecTargetList: tl is ");
|
||||
EV_nodeDisplay(targetlist);
|
||||
EV_printf("\n");
|
||||
|
||||
/*
|
||||
* Run in short-lived per-tuple context while computing expressions.
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||
|
||||
/*
|
||||
* There used to be some klugy and demonstrably broken code here that
|
||||
* special-cased the situation where targetlist == NIL. Now we just
|
||||
* fall through and return an empty-but-valid tuple.
|
||||
*/
|
||||
|
||||
/*
|
||||
* evaluate all the expressions in the target list
|
||||
*/
|
||||
|
@ -3544,9 +3519,8 @@ ExecTargetList(List *targetlist,
|
|||
|
||||
values[resind] = ExecEvalExpr(gstate->arg,
|
||||
econtext,
|
||||
&isNull,
|
||||
&isnull[resind],
|
||||
&itemIsDone[resind]);
|
||||
nulls[resind] = isNull ? 'n' : ' ';
|
||||
|
||||
if (itemIsDone[resind] != ExprSingleResult)
|
||||
{
|
||||
|
@ -3581,7 +3555,7 @@ ExecTargetList(List *targetlist,
|
|||
*/
|
||||
*isDone = ExprEndResult;
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3599,9 +3573,8 @@ ExecTargetList(List *targetlist,
|
|||
{
|
||||
values[resind] = ExecEvalExpr(gstate->arg,
|
||||
econtext,
|
||||
&isNull,
|
||||
&isnull[resind],
|
||||
&itemIsDone[resind]);
|
||||
nulls[resind] = isNull ? 'n' : ' ';
|
||||
|
||||
if (itemIsDone[resind] == ExprEndResult)
|
||||
{
|
||||
|
@ -3632,75 +3605,129 @@ ExecTargetList(List *targetlist,
|
|||
|
||||
while (itemIsDone[resind] == ExprMultipleResult)
|
||||
{
|
||||
(void) ExecEvalExpr(gstate->arg,
|
||||
econtext,
|
||||
&isNull,
|
||||
&itemIsDone[resind]);
|
||||
values[resind] = ExecEvalExpr(gstate->arg,
|
||||
econtext,
|
||||
&isnull[resind],
|
||||
&itemIsDone[resind]);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* form the new result tuple (in the caller's memory context!)
|
||||
*/
|
||||
/* Report success */
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
return heap_formtuple(targettype, values, nulls);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecProject
|
||||
/*
|
||||
* ExecVariableList
|
||||
* Evaluates a simple-Variable-list projection.
|
||||
*
|
||||
* Results are stored into the passed values and isnull arrays.
|
||||
*/
|
||||
static void
|
||||
ExecVariableList(ProjectionInfo *projInfo,
|
||||
Datum *values,
|
||||
bool *isnull)
|
||||
{
|
||||
ExprContext *econtext = projInfo->pi_exprContext;
|
||||
int *varSlotOffsets = projInfo->pi_varSlotOffsets;
|
||||
int *varNumbers = projInfo->pi_varNumbers;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Force extraction of all input values that we need.
|
||||
*/
|
||||
if (projInfo->pi_lastInnerVar > 0)
|
||||
slot_getsomeattrs(econtext->ecxt_innertuple,
|
||||
projInfo->pi_lastInnerVar);
|
||||
if (projInfo->pi_lastOuterVar > 0)
|
||||
slot_getsomeattrs(econtext->ecxt_outertuple,
|
||||
projInfo->pi_lastOuterVar);
|
||||
if (projInfo->pi_lastScanVar > 0)
|
||||
slot_getsomeattrs(econtext->ecxt_scantuple,
|
||||
projInfo->pi_lastScanVar);
|
||||
|
||||
/*
|
||||
* Assign to result by direct extraction of fields from source
|
||||
* slots ... a mite ugly, but fast ...
|
||||
*/
|
||||
for (i = list_length(projInfo->pi_targetlist) - 1; i >= 0; i--)
|
||||
{
|
||||
char *slotptr = ((char *) econtext) + varSlotOffsets[i];
|
||||
TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
|
||||
int varNumber = varNumbers[i] - 1;
|
||||
|
||||
values[i] = varSlot->tts_values[varNumber];
|
||||
isnull[i] = varSlot->tts_isnull[varNumber];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecProject
|
||||
*
|
||||
* projects a tuple based on projection info and stores
|
||||
* it in the specified tuple table slot.
|
||||
* it in the previously specified tuple table slot.
|
||||
*
|
||||
* Note: someday soon the executor can be extended to eliminate
|
||||
* redundant projections by storing pointers to datums
|
||||
* in the tuple table and then passing these around when
|
||||
* possible. this should make things much quicker.
|
||||
* -cim 6/3/91
|
||||
* ----------------------------------------------------------------
|
||||
* Note: the result is always a virtual tuple; therefore it
|
||||
* may reference the contents of the exprContext's scan tuples
|
||||
* and/or temporary results constructed in the exprContext.
|
||||
* If the caller wishes the result to be valid longer than that
|
||||
* data will be valid, he must call ExecMaterializeSlot on the
|
||||
* result slot.
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
|
||||
{
|
||||
TupleTableSlot *slot;
|
||||
TupleDesc tupType;
|
||||
HeapTuple newTuple;
|
||||
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
if (projInfo == NULL)
|
||||
return NULL;
|
||||
Assert(projInfo != NULL);
|
||||
|
||||
/*
|
||||
* get the projection info we want
|
||||
*/
|
||||
slot = projInfo->pi_slot;
|
||||
tupType = slot->ttc_tupleDescriptor;
|
||||
|
||||
/*
|
||||
* form a new result tuple (if possible --- result can be NULL)
|
||||
* Clear any former contents of the result slot. This makes it
|
||||
* safe for us to use the slot's Datum/isnull arrays as workspace.
|
||||
* (Also, we can return the slot as-is if we decide no rows can
|
||||
* be projected.)
|
||||
*/
|
||||
newTuple = ExecTargetList(projInfo->pi_targetlist,
|
||||
tupType,
|
||||
projInfo->pi_exprContext,
|
||||
projInfo->pi_tupValues,
|
||||
projInfo->pi_tupNulls,
|
||||
projInfo->pi_itemIsDone,
|
||||
isDone);
|
||||
ExecClearTuple(slot);
|
||||
|
||||
/*
|
||||
* store the tuple in the projection slot and return the slot.
|
||||
* form a new result tuple (if possible); if successful, mark the result
|
||||
* slot as containing a valid virtual tuple
|
||||
*/
|
||||
return ExecStoreTuple(newTuple, /* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
InvalidBuffer, /* tuple has no buffer */
|
||||
true);
|
||||
if (projInfo->pi_isVarList)
|
||||
{
|
||||
/* simple Var list: this always succeeds with one result row */
|
||||
if (isDone)
|
||||
*isDone = ExprSingleResult;
|
||||
ExecVariableList(projInfo,
|
||||
slot->tts_values,
|
||||
slot->tts_isnull);
|
||||
ExecStoreVirtualTuple(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ExecTargetList(projInfo->pi_targetlist,
|
||||
projInfo->pi_exprContext,
|
||||
slot->tts_values,
|
||||
slot->tts_isnull,
|
||||
projInfo->pi_itemIsDone,
|
||||
isDone))
|
||||
ExecStoreVirtualTuple(slot);
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.34 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.35 2005/03/16 21:38:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -106,10 +106,7 @@ ExecScan(ScanState *node,
|
|||
if (TupIsNull(slot))
|
||||
{
|
||||
if (projInfo)
|
||||
return ExecStoreTuple(NULL,
|
||||
projInfo->pi_slot,
|
||||
InvalidBuffer,
|
||||
true);
|
||||
return ExecClearTuple(projInfo->pi_slot);
|
||||
else
|
||||
return slot;
|
||||
}
|
||||
|
@ -183,7 +180,7 @@ ExecAssignScanProjectionInfo(ScanState *node)
|
|||
if (tlist_matches_tupdesc(&node->ps,
|
||||
scan->plan.targetlist,
|
||||
scan->scanrelid,
|
||||
node->ss_ScanTupleSlot->ttc_tupleDescriptor))
|
||||
node->ss_ScanTupleSlot->tts_tupleDescriptor))
|
||||
node->ps.ps_ProjInfo = NULL;
|
||||
else
|
||||
ExecAssignProjectionInfo(&node->ps);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.84 2005/03/14 04:41:12 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.85 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -30,12 +30,13 @@
|
|||
* ExecAllocTableSlot - find an available slot in the table
|
||||
*
|
||||
* SLOT ACCESSORS
|
||||
* ExecStoreTuple - store a tuple in the table
|
||||
* ExecClearTuple - clear contents of a table slot
|
||||
* ExecSetSlotDescriptor - set a slot's tuple descriptor
|
||||
*
|
||||
* SLOT STATUS PREDICATES
|
||||
* TupIsNull - true when slot contains no tuple (macro)
|
||||
* ExecStoreTuple - store a physical tuple in the slot
|
||||
* ExecClearTuple - clear contents of a slot
|
||||
* ExecStoreVirtualTuple - mark slot as containing a virtual tuple
|
||||
* ExecCopySlotTuple - build a physical tuple from a slot
|
||||
* ExecMaterializeSlot - convert virtual to physical storage
|
||||
* ExecCopySlot - copy one slot's contents to another
|
||||
*
|
||||
* CONVENIENCE INITIALIZATION ROUTINES
|
||||
* ExecInitResultTupleSlot \ convenience routines to initialize
|
||||
|
@ -81,10 +82,8 @@
|
|||
* to the slots containing tuples are passed instead of the tuples
|
||||
* themselves. This facilitates the communication of related information
|
||||
* (such as whether or not a tuple should be pfreed, what buffer contains
|
||||
* this tuple, the tuple's tuple descriptor, etc). Note that much of
|
||||
* this information is also kept in the ExprContext of each node.
|
||||
* Soon the executor will be redesigned and ExprContext's will contain
|
||||
* only slot pointers. -cim 3/14/91
|
||||
* this tuple, the tuple's tuple descriptor, etc). It also allows us
|
||||
* to avoid physically constructing projection tuples in many cases.
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
|
@ -142,14 +141,16 @@ ExecCreateTupleTable(int tableSize)
|
|||
TupleTableSlot *slot = &(newtable->array[i]);
|
||||
|
||||
slot->type = T_TupleTableSlot;
|
||||
slot->val = NULL;
|
||||
slot->ttc_tupleDescriptor = NULL;
|
||||
slot->ttc_shouldFree = false;
|
||||
slot->ttc_shouldFreeDesc = false;
|
||||
slot->ttc_buffer = InvalidBuffer;
|
||||
slot->ttc_mcxt = CurrentMemoryContext;
|
||||
slot->cache_values = NULL;
|
||||
slot->cache_natts = 0; /* mark slot_getattr state invalid */
|
||||
slot->tts_isempty = true;
|
||||
slot->tts_shouldFree = false;
|
||||
slot->tts_shouldFreeDesc = false;
|
||||
slot->tts_tuple = NULL;
|
||||
slot->tts_tupleDescriptor = NULL;
|
||||
slot->tts_mcxt = CurrentMemoryContext;
|
||||
slot->tts_buffer = InvalidBuffer;
|
||||
slot->tts_nvalid = 0;
|
||||
slot->tts_values = NULL;
|
||||
slot->tts_isnull = NULL;
|
||||
}
|
||||
|
||||
return newtable;
|
||||
|
@ -189,10 +190,12 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
|
|||
TupleTableSlot *slot = &(table->array[i]);
|
||||
|
||||
ExecClearTuple(slot);
|
||||
if (slot->ttc_shouldFreeDesc)
|
||||
FreeTupleDesc(slot->ttc_tupleDescriptor);
|
||||
if (slot->cache_values)
|
||||
pfree(slot->cache_values);
|
||||
if (slot->tts_shouldFreeDesc)
|
||||
FreeTupleDesc(slot->tts_tupleDescriptor);
|
||||
if (slot->tts_values)
|
||||
pfree(slot->tts_values);
|
||||
if (slot->tts_isnull)
|
||||
pfree(slot->tts_isnull);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,32 +206,61 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
|
|||
}
|
||||
|
||||
/* --------------------------------
|
||||
* MakeTupleTableSlot
|
||||
* MakeSingleTupleTableSlot
|
||||
*
|
||||
* This routine makes an empty standalone TupleTableSlot.
|
||||
* It really shouldn't exist, but there are a few places
|
||||
* that do this, so we may as well centralize the knowledge
|
||||
* of what's in one ...
|
||||
* This is a convenience routine for operations that need a
|
||||
* standalone TupleTableSlot not gotten from the main executor
|
||||
* tuple table. It makes a single slot and initializes it as
|
||||
* though by ExecSetSlotDescriptor(slot, tupdesc, false).
|
||||
* --------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
MakeTupleTableSlot(void)
|
||||
MakeSingleTupleTableSlot(TupleDesc tupdesc)
|
||||
{
|
||||
TupleTableSlot *slot = makeNode(TupleTableSlot);
|
||||
|
||||
/* This should match ExecCreateTupleTable() */
|
||||
slot->val = NULL;
|
||||
slot->ttc_tupleDescriptor = NULL;
|
||||
slot->ttc_shouldFree = false;
|
||||
slot->ttc_shouldFreeDesc = false;
|
||||
slot->ttc_buffer = InvalidBuffer;
|
||||
slot->ttc_mcxt = CurrentMemoryContext;
|
||||
slot->cache_values = NULL;
|
||||
slot->cache_natts = 0; /* mark slot_getattr state invalid */
|
||||
slot->tts_isempty = true;
|
||||
slot->tts_shouldFree = false;
|
||||
slot->tts_shouldFreeDesc = false;
|
||||
slot->tts_tuple = NULL;
|
||||
slot->tts_tupleDescriptor = NULL;
|
||||
slot->tts_mcxt = CurrentMemoryContext;
|
||||
slot->tts_buffer = InvalidBuffer;
|
||||
slot->tts_nvalid = 0;
|
||||
slot->tts_values = NULL;
|
||||
slot->tts_isnull = NULL;
|
||||
|
||||
ExecSetSlotDescriptor(slot, tupdesc, false);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecDropSingleTupleTableSlot
|
||||
*
|
||||
* Release a TupleTableSlot made with MakeSingleTupleTableSlot.
|
||||
* --------------------------------
|
||||
*/
|
||||
void
|
||||
ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
|
||||
{
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(slot != NULL);
|
||||
|
||||
ExecClearTuple(slot);
|
||||
if (slot->tts_shouldFreeDesc)
|
||||
FreeTupleDesc(slot->tts_tupleDescriptor);
|
||||
if (slot->tts_values)
|
||||
pfree(slot->tts_values);
|
||||
if (slot->tts_isnull)
|
||||
pfree(slot->tts_isnull);
|
||||
|
||||
pfree(slot);
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* tuple table slot reservation functions
|
||||
|
@ -274,10 +306,54 @@ ExecAllocTableSlot(TupleTable table)
|
|||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* --------------------------------
|
||||
* ExecSetSlotDescriptor
|
||||
*
|
||||
* This function is used to set the tuple descriptor associated
|
||||
* with the slot's tuple.
|
||||
* --------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
|
||||
TupleDesc tupdesc, /* new tuple descriptor */
|
||||
bool shouldFree) /* is desc owned by slot? */
|
||||
{
|
||||
/* For safety, make sure slot is empty before changing it */
|
||||
ExecClearTuple(slot);
|
||||
|
||||
/*
|
||||
* Release any old descriptor. Also release old Datum/isnull arrays
|
||||
* if present (we don't bother to check if they could be re-used).
|
||||
*/
|
||||
if (slot->tts_shouldFreeDesc)
|
||||
FreeTupleDesc(slot->tts_tupleDescriptor);
|
||||
|
||||
if (slot->tts_values)
|
||||
pfree(slot->tts_values);
|
||||
if (slot->tts_isnull)
|
||||
pfree(slot->tts_isnull);
|
||||
|
||||
/*
|
||||
* Set up the new descriptor
|
||||
*/
|
||||
slot->tts_tupleDescriptor = tupdesc;
|
||||
slot->tts_shouldFreeDesc = shouldFree;
|
||||
|
||||
/*
|
||||
* Allocate Datum/isnull arrays of the appropriate size. These must
|
||||
* have the same lifetime as the slot, so allocate in the slot's own
|
||||
* context.
|
||||
*/
|
||||
slot->tts_values = (Datum *)
|
||||
MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum));
|
||||
slot->tts_isnull = (bool *)
|
||||
MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool));
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecStoreTuple
|
||||
*
|
||||
* This function is used to store a tuple into a specified
|
||||
* This function is used to store a physical tuple into a specified
|
||||
* slot in the tuple table.
|
||||
*
|
||||
* tuple: tuple to store
|
||||
|
@ -304,6 +380,12 @@ ExecAllocTableSlot(TupleTable table)
|
|||
* slot assume ownership of the copy!
|
||||
*
|
||||
* Return value is just the passed-in slot pointer.
|
||||
*
|
||||
* NOTE: before PostgreSQL 8.1, this function would accept a NULL tuple
|
||||
* pointer and effectively behave like ExecClearTuple (though you could
|
||||
* still specify a buffer to pin, which would be an odd combination).
|
||||
* This saved a couple lines of code in a few places, but seemed more likely
|
||||
* to mask logic errors than to be really useful, so it's now disallowed.
|
||||
* --------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
|
@ -315,29 +397,36 @@ ExecStoreTuple(HeapTuple tuple,
|
|||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(tuple != NULL);
|
||||
Assert(slot != NULL);
|
||||
Assert(slot->tts_tupleDescriptor != NULL);
|
||||
/* passing shouldFree=true for a tuple on a disk page is not sane */
|
||||
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
|
||||
|
||||
/*
|
||||
* clear out any old contents of the slot
|
||||
*/
|
||||
ExecClearTuple(slot);
|
||||
if (!slot->tts_isempty)
|
||||
ExecClearTuple(slot);
|
||||
|
||||
/*
|
||||
* store the new tuple into the specified slot.
|
||||
*/
|
||||
slot->val = tuple;
|
||||
slot->ttc_shouldFree = shouldFree;
|
||||
slot->tts_isempty = false;
|
||||
slot->tts_shouldFree = shouldFree;
|
||||
slot->tts_tuple = tuple;
|
||||
|
||||
/*
|
||||
* If tuple is on a disk page, keep the page pinned as long as we hold
|
||||
* a pointer into it. We assume the caller already has such a pin.
|
||||
*/
|
||||
slot->ttc_buffer = buffer;
|
||||
slot->tts_buffer = buffer;
|
||||
if (BufferIsValid(buffer))
|
||||
IncrBufferRefCount(buffer);
|
||||
|
||||
/* Mark extracted state invalid */
|
||||
slot->tts_nvalid = 0;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
|
@ -358,63 +447,231 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
|
|||
Assert(slot != NULL);
|
||||
|
||||
/*
|
||||
* Free the old contents of the specified slot if necessary. (Note:
|
||||
* we allow slot->val to be null even when shouldFree is true, because
|
||||
* there are a few callers of ExecStoreTuple that are too lazy to
|
||||
* distinguish whether they are passing a NULL tuple, and always pass
|
||||
* shouldFree = true.)
|
||||
* Free the old physical tuple if necessary.
|
||||
*/
|
||||
if (slot->ttc_shouldFree && slot->val != NULL)
|
||||
heap_freetuple(slot->val);
|
||||
if (slot->tts_shouldFree)
|
||||
heap_freetuple(slot->tts_tuple);
|
||||
|
||||
slot->val = NULL;
|
||||
slot->ttc_shouldFree = false;
|
||||
slot->tts_tuple = NULL;
|
||||
slot->tts_shouldFree = false;
|
||||
|
||||
/*
|
||||
* Drop the pin on the referenced buffer, if there is one.
|
||||
*/
|
||||
if (BufferIsValid(slot->ttc_buffer))
|
||||
ReleaseBuffer(slot->ttc_buffer);
|
||||
if (BufferIsValid(slot->tts_buffer))
|
||||
ReleaseBuffer(slot->tts_buffer);
|
||||
|
||||
slot->ttc_buffer = InvalidBuffer;
|
||||
slot->tts_buffer = InvalidBuffer;
|
||||
|
||||
/*
|
||||
* mark slot_getattr state invalid
|
||||
* Mark it empty.
|
||||
*/
|
||||
slot->cache_natts = 0;
|
||||
slot->tts_isempty = true;
|
||||
slot->tts_nvalid = 0;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecSetSlotDescriptor
|
||||
* ExecStoreVirtualTuple
|
||||
* Mark a slot as containing a virtual tuple.
|
||||
*
|
||||
* This function is used to set the tuple descriptor associated
|
||||
* with the slot's tuple.
|
||||
* The protocol for loading a slot with virtual tuple data is:
|
||||
* * Call ExecClearTuple to mark the slot empty.
|
||||
* * Store data into the Datum/isnull arrays.
|
||||
* * Call ExecStoreVirtualTuple to mark the slot valid.
|
||||
* This is a bit unclean but it avoids one round of data copying.
|
||||
* --------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
|
||||
TupleDesc tupdesc, /* new tuple descriptor */
|
||||
bool shouldFree) /* is desc owned by slot? */
|
||||
TupleTableSlot *
|
||||
ExecStoreVirtualTuple(TupleTableSlot *slot)
|
||||
{
|
||||
if (slot->ttc_shouldFreeDesc)
|
||||
FreeTupleDesc(slot->ttc_tupleDescriptor);
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(slot != NULL);
|
||||
Assert(slot->tts_tupleDescriptor != NULL);
|
||||
Assert(slot->tts_isempty);
|
||||
|
||||
slot->ttc_tupleDescriptor = tupdesc;
|
||||
slot->ttc_shouldFreeDesc = shouldFree;
|
||||
slot->tts_isempty = false;
|
||||
slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecStoreAllNullTuple
|
||||
* Set up the slot to contain a null in every column.
|
||||
*
|
||||
* At first glance this might sound just like ExecClearTuple, but it's
|
||||
* entirely different: the slot ends up full, not empty.
|
||||
* --------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecStoreAllNullTuple(TupleTableSlot *slot)
|
||||
{
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(slot != NULL);
|
||||
Assert(slot->tts_tupleDescriptor != NULL);
|
||||
|
||||
/* Clear any old contents */
|
||||
ExecClearTuple(slot);
|
||||
|
||||
/*
|
||||
* mark slot_getattr state invalid
|
||||
* Fill all the columns of the virtual tuple with nulls
|
||||
*/
|
||||
slot->cache_natts = 0;
|
||||
MemSet(slot->tts_values, 0,
|
||||
slot->tts_tupleDescriptor->natts * sizeof(Datum));
|
||||
memset(slot->tts_isnull, true,
|
||||
slot->tts_tupleDescriptor->natts * sizeof(bool));
|
||||
|
||||
return ExecStoreVirtualTuple(slot);
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecCopySlotTuple
|
||||
* Obtain a copy of a slot's physical tuple. The copy is
|
||||
* palloc'd in the current memory context.
|
||||
*
|
||||
* This works even if the slot contains a virtual tuple;
|
||||
* however the "system columns" of the result will not be meaningful.
|
||||
* --------------------------------
|
||||
*/
|
||||
HeapTuple
|
||||
ExecCopySlotTuple(TupleTableSlot *slot)
|
||||
{
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(slot != NULL);
|
||||
Assert(!slot->tts_isempty);
|
||||
|
||||
/*
|
||||
* release any old cache array since tupledesc's natts may have changed
|
||||
* If we have a physical tuple then just copy it.
|
||||
*/
|
||||
if (slot->cache_values)
|
||||
pfree(slot->cache_values);
|
||||
slot->cache_values = NULL;
|
||||
if (slot->tts_tuple)
|
||||
return heap_copytuple(slot->tts_tuple);
|
||||
|
||||
/*
|
||||
* Otherwise we need to build a tuple from the Datum array.
|
||||
*/
|
||||
return heap_form_tuple(slot->tts_tupleDescriptor,
|
||||
slot->tts_values,
|
||||
slot->tts_isnull);
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecFetchSlotTuple
|
||||
* Fetch the slot's physical tuple.
|
||||
*
|
||||
* If the slot contains a virtual tuple, we convert it to physical
|
||||
* form. The slot retains ownership of the physical tuple.
|
||||
*
|
||||
* The difference between this and ExecMaterializeSlot() is that this
|
||||
* does not guarantee that the contained tuple is local storage.
|
||||
* Hence, the result must be treated as read-only.
|
||||
* --------------------------------
|
||||
*/
|
||||
HeapTuple
|
||||
ExecFetchSlotTuple(TupleTableSlot *slot)
|
||||
{
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(slot != NULL);
|
||||
Assert(!slot->tts_isempty);
|
||||
|
||||
/*
|
||||
* If we have a physical tuple then just return it.
|
||||
*/
|
||||
if (slot->tts_tuple)
|
||||
return slot->tts_tuple;
|
||||
|
||||
/*
|
||||
* Otherwise materialize the slot...
|
||||
*/
|
||||
return ExecMaterializeSlot(slot);
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecMaterializeSlot
|
||||
* Force a slot into the "materialized" state.
|
||||
*
|
||||
* This causes the slot's tuple to be a local copy not dependent on
|
||||
* any external storage. A pointer to the contained tuple is returned.
|
||||
*
|
||||
* A typical use for this operation is to prepare a computed tuple
|
||||
* for being stored on disk. The original data may or may not be
|
||||
* virtual, but in any case we need a private copy for heap_insert
|
||||
* to scribble on.
|
||||
* --------------------------------
|
||||
*/
|
||||
HeapTuple
|
||||
ExecMaterializeSlot(TupleTableSlot *slot)
|
||||
{
|
||||
HeapTuple newTuple;
|
||||
MemoryContext oldContext;
|
||||
|
||||
/*
|
||||
* sanity checks
|
||||
*/
|
||||
Assert(slot != NULL);
|
||||
Assert(!slot->tts_isempty);
|
||||
|
||||
/*
|
||||
* If we have a physical tuple, and it's locally palloc'd, we have
|
||||
* nothing to do.
|
||||
*/
|
||||
if (slot->tts_tuple && slot->tts_shouldFree)
|
||||
return slot->tts_tuple;
|
||||
|
||||
/*
|
||||
* Otherwise, copy or build a tuple, and then store it as the new slot
|
||||
* value. (Note: tts_nvalid will be reset to zero here. There are
|
||||
* cases in which this could be optimized but it's probably not worth
|
||||
* worrying about.)
|
||||
*
|
||||
* We may be called in a context that is shorter-lived than the
|
||||
* tuple slot, but we have to ensure that the materialized tuple
|
||||
* will survive anyway.
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
|
||||
newTuple = ExecCopySlotTuple(slot);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
ExecStoreTuple(newTuple, slot, InvalidBuffer, true);
|
||||
|
||||
return slot->tts_tuple;
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* ExecCopySlot
|
||||
* Copy the source slot's contents into the destination slot.
|
||||
*
|
||||
* The destination acquires a private copy that will not go away
|
||||
* if the source is cleared.
|
||||
*
|
||||
* The caller must ensure the slots have compatible tupdescs.
|
||||
* --------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
|
||||
{
|
||||
HeapTuple newTuple;
|
||||
MemoryContext oldContext;
|
||||
|
||||
/*
|
||||
* There might be ways to optimize this when the source is virtual,
|
||||
* but for now just always build a physical copy. Make sure it is
|
||||
* in the right context.
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
|
||||
newTuple = ExecCopySlotTuple(srcslot);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
return ExecStoreTuple(newTuple, dstslot, InvalidBuffer, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -474,25 +731,10 @@ TupleTableSlot *
|
|||
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
|
||||
{
|
||||
TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
|
||||
struct tupleDesc nullTupleDesc;
|
||||
HeapTuple nullTuple;
|
||||
Datum values[1];
|
||||
char nulls[1];
|
||||
|
||||
/*
|
||||
* Since heap_getattr() will treat attributes beyond a tuple's t_natts
|
||||
* as being NULL, we can make an all-nulls tuple just by making it be
|
||||
* of zero length. However, the slot descriptor must match the real
|
||||
* tupType.
|
||||
*/
|
||||
nullTupleDesc = *tupType;
|
||||
nullTupleDesc.natts = 0;
|
||||
|
||||
nullTuple = heap_formtuple(&nullTupleDesc, values, nulls);
|
||||
|
||||
ExecSetSlotDescriptor(slot, tupType, false);
|
||||
|
||||
return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
|
||||
return ExecStoreAllNullTuple(slot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
@ -623,10 +865,7 @@ TupleDescGetSlot(TupleDesc tupdesc)
|
|||
BlessTupleDesc(tupdesc);
|
||||
|
||||
/* Make a standalone slot */
|
||||
slot = MakeTupleTableSlot();
|
||||
|
||||
/* Bind the tuple description to the slot */
|
||||
ExecSetSlotDescriptor(slot, tupdesc, true);
|
||||
slot = MakeSingleTupleTableSlot(tupdesc);
|
||||
|
||||
/* Return the slot */
|
||||
return slot;
|
||||
|
@ -759,6 +998,7 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
|
|||
tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
|
||||
|
||||
tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
|
||||
tstate->slot = MakeSingleTupleTableSlot(tupdesc);
|
||||
tstate->dest = dest;
|
||||
|
||||
(*tstate->dest->rStartup) (tstate->dest, (int) CMD_SELECT, tupdesc);
|
||||
|
@ -771,6 +1011,9 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
|
|||
*
|
||||
* values is a list of the external C string representations of the values
|
||||
* to be projected.
|
||||
*
|
||||
* XXX This could be made more efficient, since in reality we probably only
|
||||
* need a virtual tuple.
|
||||
*/
|
||||
void
|
||||
do_tup_output(TupOutputState *tstate, char **values)
|
||||
|
@ -778,12 +1021,14 @@ do_tup_output(TupOutputState *tstate, char **values)
|
|||
/* build a tuple from the input strings using the tupdesc */
|
||||
HeapTuple tuple = BuildTupleFromCStrings(tstate->metadata, values);
|
||||
|
||||
/* put it in a slot */
|
||||
ExecStoreTuple(tuple, tstate->slot, InvalidBuffer, true);
|
||||
|
||||
/* send the tuple to the receiver */
|
||||
(*tstate->dest->receiveTuple) (tuple,
|
||||
tstate->metadata->tupdesc,
|
||||
tstate->dest);
|
||||
(*tstate->dest->receiveSlot) (tstate->slot, tstate->dest);
|
||||
|
||||
/* clean up */
|
||||
heap_freetuple(tuple);
|
||||
ExecClearTuple(tstate->slot);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -816,6 +1061,7 @@ end_tup_output(TupOutputState *tstate)
|
|||
{
|
||||
(*tstate->dest->rShutdown) (tstate->dest);
|
||||
/* note that destroying the dest is not ours to do */
|
||||
ExecDropSingleTupleTableSlot(tstate->slot);
|
||||
/* XXX worth cleaning up the attinmetadata? */
|
||||
pfree(tstate);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.117 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.118 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -485,7 +485,7 @@ ExecGetResultType(PlanState *planstate)
|
|||
{
|
||||
TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
|
||||
|
||||
return slot->ttc_tupleDescriptor;
|
||||
return slot->tts_tupleDescriptor;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
|
@ -504,17 +504,99 @@ ExecBuildProjectionInfo(List *targetList,
|
|||
{
|
||||
ProjectionInfo *projInfo = makeNode(ProjectionInfo);
|
||||
int len;
|
||||
bool isVarList;
|
||||
ListCell *tl;
|
||||
|
||||
len = ExecTargetListLength(targetList);
|
||||
|
||||
projInfo->pi_targetlist = targetList;
|
||||
projInfo->pi_exprContext = econtext;
|
||||
projInfo->pi_slot = slot;
|
||||
if (len > 0)
|
||||
|
||||
/*
|
||||
* Determine whether the target list consists entirely of simple Var
|
||||
* references (ie, references to non-system attributes). If so,
|
||||
* we can use the simpler ExecVariableList instead of ExecTargetList.
|
||||
*/
|
||||
isVarList = true;
|
||||
foreach(tl, targetList)
|
||||
{
|
||||
projInfo->pi_tupValues = (Datum *) palloc(len * sizeof(Datum));
|
||||
projInfo->pi_tupNulls = (char *) palloc(len * sizeof(char));
|
||||
projInfo->pi_itemIsDone = (ExprDoneCond *) palloc(len * sizeof(ExprDoneCond));
|
||||
GenericExprState *gstate = (GenericExprState *) lfirst(tl);
|
||||
Var *variable = (Var *) gstate->arg->expr;
|
||||
|
||||
if (variable == NULL ||
|
||||
!IsA(variable, Var) ||
|
||||
variable->varattno <= 0)
|
||||
{
|
||||
isVarList = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
projInfo->pi_isVarList = isVarList;
|
||||
|
||||
if (isVarList)
|
||||
{
|
||||
int *varSlotOffsets;
|
||||
int *varNumbers;
|
||||
AttrNumber lastInnerVar = 0;
|
||||
AttrNumber lastOuterVar = 0;
|
||||
AttrNumber lastScanVar = 0;
|
||||
|
||||
projInfo->pi_itemIsDone = NULL; /* not needed */
|
||||
projInfo->pi_varSlotOffsets = varSlotOffsets = (int *)
|
||||
palloc0(len * sizeof(int));
|
||||
projInfo->pi_varNumbers = varNumbers = (int *)
|
||||
palloc0(len * sizeof(int));
|
||||
|
||||
/*
|
||||
* Set up the data needed by ExecVariableList. The slots in which
|
||||
* the variables can be found at runtime are denoted by the offsets
|
||||
* of their slot pointers within the econtext. This rather grotty
|
||||
* representation is needed because the caller may not have given
|
||||
* us the real econtext yet (see hacks in nodeSubplan.c).
|
||||
*/
|
||||
foreach(tl, targetList)
|
||||
{
|
||||
GenericExprState *gstate = (GenericExprState *) lfirst(tl);
|
||||
Var *variable = (Var *) gstate->arg->expr;
|
||||
AttrNumber attnum = variable->varattno;
|
||||
TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
|
||||
AttrNumber resind = tle->resdom->resno - 1;
|
||||
|
||||
Assert(resind >= 0 && resind < len);
|
||||
varNumbers[resind] = attnum;
|
||||
|
||||
switch (variable->varno)
|
||||
{
|
||||
case INNER:
|
||||
varSlotOffsets[resind] = offsetof(ExprContext,
|
||||
ecxt_innertuple);
|
||||
lastInnerVar = Max(lastInnerVar, attnum);
|
||||
break;
|
||||
|
||||
case OUTER:
|
||||
varSlotOffsets[resind] = offsetof(ExprContext,
|
||||
ecxt_outertuple);
|
||||
lastOuterVar = Max(lastOuterVar, attnum);
|
||||
break;
|
||||
|
||||
default:
|
||||
varSlotOffsets[resind] = offsetof(ExprContext,
|
||||
ecxt_scantuple);
|
||||
lastScanVar = Max(lastScanVar, attnum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
projInfo->pi_lastInnerVar = lastInnerVar;
|
||||
projInfo->pi_lastOuterVar = lastOuterVar;
|
||||
projInfo->pi_lastScanVar = lastScanVar;
|
||||
}
|
||||
else
|
||||
{
|
||||
projInfo->pi_itemIsDone = (ExprDoneCond *)
|
||||
palloc(len * sizeof(ExprDoneCond));
|
||||
projInfo->pi_varSlotOffsets = NULL;
|
||||
projInfo->pi_varNumbers = NULL;
|
||||
}
|
||||
|
||||
return projInfo;
|
||||
|
@ -582,7 +664,7 @@ ExecGetScanType(ScanState *scanstate)
|
|||
{
|
||||
TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
|
||||
|
||||
return slot->ttc_tupleDescriptor;
|
||||
return slot->tts_tupleDescriptor;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
|
@ -772,20 +854,16 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||
EState *estate,
|
||||
bool is_vacuum)
|
||||
{
|
||||
HeapTuple heapTuple;
|
||||
ResultRelInfo *resultRelInfo;
|
||||
int i;
|
||||
int numIndices;
|
||||
RelationPtr relationDescs;
|
||||
Relation heapRelation;
|
||||
TupleDesc heapDescriptor;
|
||||
IndexInfo **indexInfoArray;
|
||||
ExprContext *econtext;
|
||||
Datum datum[INDEX_MAX_KEYS];
|
||||
char nullv[INDEX_MAX_KEYS];
|
||||
|
||||
heapTuple = slot->val;
|
||||
|
||||
/*
|
||||
* Get information from the result relation info structure.
|
||||
*/
|
||||
|
@ -794,7 +872,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||
relationDescs = resultRelInfo->ri_IndexRelationDescs;
|
||||
indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
|
||||
heapRelation = resultRelInfo->ri_RelationDesc;
|
||||
heapDescriptor = RelationGetDescr(heapRelation);
|
||||
|
||||
/*
|
||||
* We will use the EState's per-tuple context for evaluating
|
||||
|
@ -844,12 +921,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||
|
||||
/*
|
||||
* FormIndexDatum fills in its datum and null parameters with
|
||||
* attribute information taken from the given heap tuple. It also
|
||||
* attribute information taken from the given tuple. It also
|
||||
* computes any expressions needed.
|
||||
*/
|
||||
FormIndexDatum(indexInfo,
|
||||
heapTuple,
|
||||
heapDescriptor,
|
||||
slot,
|
||||
estate,
|
||||
datum,
|
||||
nullv);
|
||||
|
@ -860,9 +936,9 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||
* need to move dead tuples that have the same keys as live ones.
|
||||
*/
|
||||
result = index_insert(relationDescs[i], /* index relation */
|
||||
datum, /* array of heaptuple Datums */
|
||||
datum, /* array of index Datums */
|
||||
nullv, /* info on nulls */
|
||||
&(heapTuple->t_self), /* tid of heap tuple */
|
||||
tupleid, /* tid of heap tuple */
|
||||
heapRelation,
|
||||
relationDescs[i]->rd_index->indisunique && !is_vacuum);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.91 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.92 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -456,8 +456,6 @@ postquel_execute(execution_state *es,
|
|||
SQLFunctionCachePtr fcache)
|
||||
{
|
||||
TupleTableSlot *slot;
|
||||
HeapTuple tup;
|
||||
TupleDesc tupDesc;
|
||||
Datum value;
|
||||
|
||||
if (es->status == F_EXEC_START)
|
||||
|
@ -512,7 +510,7 @@ postquel_execute(execution_state *es,
|
|||
|
||||
/*
|
||||
* Compress out the HeapTuple header data. We assume that
|
||||
* heap_formtuple made the tuple with header and body in one
|
||||
* heap_form_tuple made the tuple with header and body in one
|
||||
* palloc'd chunk. We want to return a pointer to the chunk
|
||||
* start so that it will work if someone tries to free it.
|
||||
*/
|
||||
|
@ -534,7 +532,8 @@ postquel_execute(execution_state *es,
|
|||
else
|
||||
{
|
||||
/* function is declared to return RECORD */
|
||||
tupDesc = fcache->junkFilter->jf_cleanTupType;
|
||||
TupleDesc tupDesc = fcache->junkFilter->jf_cleanTupType;
|
||||
|
||||
if (tupDesc->tdtypeid == RECORDOID &&
|
||||
tupDesc->tdtypmod < 0)
|
||||
assign_record_type_typmod(tupDesc);
|
||||
|
@ -556,10 +555,7 @@ postquel_execute(execution_state *es,
|
|||
* column of the SELECT result, and then copy into current
|
||||
* execution context if needed.
|
||||
*/
|
||||
tup = slot->val;
|
||||
tupDesc = slot->ttc_tupleDescriptor;
|
||||
|
||||
value = heap_getattr(tup, 1, tupDesc, &(fcinfo->isnull));
|
||||
value = slot_getattr(slot, 1, &(fcinfo->isnull));
|
||||
|
||||
if (!fcinfo->isnull)
|
||||
value = datumCopy(value, fcache->typbyval, fcache->typlen);
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.129 2005/03/12 20:25:06 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.130 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -748,7 +748,7 @@ agg_retrieve_direct(AggState *aggstate)
|
|||
* Make a copy of the first input tuple; we will use this
|
||||
* for comparisons (in group mode) and for projection.
|
||||
*/
|
||||
aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
|
||||
aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -813,9 +813,8 @@ agg_retrieve_direct(AggState *aggstate)
|
|||
*/
|
||||
if (node->aggstrategy == AGG_SORTED)
|
||||
{
|
||||
if (!execTuplesMatch(firstSlot->val,
|
||||
outerslot->val,
|
||||
firstSlot->ttc_tupleDescriptor,
|
||||
if (!execTuplesMatch(firstSlot,
|
||||
outerslot,
|
||||
node->numCols, node->grpColIdx,
|
||||
aggstate->eqfunctions,
|
||||
tmpcontext->ecxt_per_tuple_memory))
|
||||
|
@ -823,7 +822,7 @@ agg_retrieve_direct(AggState *aggstate)
|
|||
/*
|
||||
* Save the first input tuple of the next group.
|
||||
*/
|
||||
aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
|
||||
aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -863,31 +862,11 @@ agg_retrieve_direct(AggState *aggstate)
|
|||
*/
|
||||
if (TupIsNull(firstSlot))
|
||||
{
|
||||
TupleDesc tupType;
|
||||
|
||||
/* Should only happen in non-grouped mode */
|
||||
Assert(node->aggstrategy == AGG_PLAIN);
|
||||
Assert(aggstate->agg_done);
|
||||
|
||||
tupType = firstSlot->ttc_tupleDescriptor;
|
||||
/* watch out for zero-column input tuples, though... */
|
||||
if (tupType && tupType->natts > 0)
|
||||
{
|
||||
HeapTuple nullsTuple;
|
||||
Datum *dvalues;
|
||||
char *dnulls;
|
||||
|
||||
dvalues = (Datum *) palloc0(sizeof(Datum) * tupType->natts);
|
||||
dnulls = (char *) palloc(sizeof(char) * tupType->natts);
|
||||
MemSet(dnulls, 'n', sizeof(char) * tupType->natts);
|
||||
nullsTuple = heap_formtuple(tupType, dvalues, dnulls);
|
||||
ExecStoreTuple(nullsTuple,
|
||||
firstSlot,
|
||||
InvalidBuffer,
|
||||
true);
|
||||
pfree(dvalues);
|
||||
pfree(dnulls);
|
||||
}
|
||||
ExecStoreAllNullTuple(firstSlot);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.30 2005/01/27 06:36:42 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.31 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -99,7 +99,10 @@ FunctionNext(FunctionScanState *node)
|
|||
ScanDirectionIsForward(direction),
|
||||
&should_free);
|
||||
slot = node->ss.ss_ScanTupleSlot;
|
||||
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
||||
if (heapTuple)
|
||||
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
||||
else
|
||||
return ExecClearTuple(slot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* locate group boundaries.
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.60 2005/03/10 23:21:21 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.61 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -36,11 +36,9 @@ TupleTableSlot *
|
|||
ExecGroup(GroupState *node)
|
||||
{
|
||||
ExprContext *econtext;
|
||||
TupleDesc tupdesc;
|
||||
int numCols;
|
||||
AttrNumber *grpColIdx;
|
||||
HeapTuple outerTuple;
|
||||
HeapTuple firsttuple;
|
||||
TupleTableSlot *firsttupleslot;
|
||||
TupleTableSlot *outerslot;
|
||||
|
||||
/*
|
||||
|
@ -49,10 +47,14 @@ ExecGroup(GroupState *node)
|
|||
if (node->grp_done)
|
||||
return NULL;
|
||||
econtext = node->ss.ps.ps_ExprContext;
|
||||
tupdesc = ExecGetScanType(&node->ss);
|
||||
numCols = ((Group *) node->ss.ps.plan)->numCols;
|
||||
grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
|
||||
|
||||
/*
|
||||
* The ScanTupleSlot holds the (copied) first tuple of each group.
|
||||
*/
|
||||
firsttupleslot = node->ss.ss_ScanTupleSlot;
|
||||
|
||||
/*
|
||||
* We need not call ResetExprContext here because execTuplesMatch will
|
||||
* reset the per-tuple memory context once per input tuple.
|
||||
|
@ -62,8 +64,7 @@ ExecGroup(GroupState *node)
|
|||
* If first time through, acquire first input tuple and determine
|
||||
* whether to return it or not.
|
||||
*/
|
||||
firsttuple = node->grp_firstTuple;
|
||||
if (firsttuple == NULL)
|
||||
if (TupIsNull(firsttupleslot))
|
||||
{
|
||||
outerslot = ExecProcNode(outerPlanState(node));
|
||||
if (TupIsNull(outerslot))
|
||||
|
@ -72,13 +73,9 @@ ExecGroup(GroupState *node)
|
|||
node->grp_done = TRUE;
|
||||
return NULL;
|
||||
}
|
||||
node->grp_firstTuple = firsttuple = heap_copytuple(outerslot->val);
|
||||
/* Set up tuple as input for qual test and projection */
|
||||
ExecStoreTuple(firsttuple,
|
||||
node->ss.ss_ScanTupleSlot,
|
||||
InvalidBuffer,
|
||||
false);
|
||||
econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
|
||||
/* Copy tuple, set up as input for qual test and projection */
|
||||
ExecCopySlot(firsttupleslot, outerslot);
|
||||
econtext->ecxt_scantuple = firsttupleslot;
|
||||
/*
|
||||
* Check the qual (HAVING clause); if the group does not match,
|
||||
* ignore it and fall into scan loop.
|
||||
|
@ -112,14 +109,12 @@ ExecGroup(GroupState *node)
|
|||
node->grp_done = TRUE;
|
||||
return NULL;
|
||||
}
|
||||
outerTuple = outerslot->val;
|
||||
|
||||
/*
|
||||
* Compare with first tuple and see if this tuple is of the same
|
||||
* group. If so, ignore it and keep scanning.
|
||||
*/
|
||||
if (!execTuplesMatch(firsttuple, outerTuple,
|
||||
tupdesc,
|
||||
if (!execTuplesMatch(firsttupleslot, outerslot,
|
||||
numCols, grpColIdx,
|
||||
node->eqfunctions,
|
||||
econtext->ecxt_per_tuple_memory))
|
||||
|
@ -129,14 +124,9 @@ ExecGroup(GroupState *node)
|
|||
* We have the first tuple of the next input group. See if we
|
||||
* want to return it.
|
||||
*/
|
||||
heap_freetuple(firsttuple);
|
||||
node->grp_firstTuple = firsttuple = heap_copytuple(outerTuple);
|
||||
/* Set up tuple as input for qual test and projection */
|
||||
ExecStoreTuple(firsttuple,
|
||||
node->ss.ss_ScanTupleSlot,
|
||||
InvalidBuffer,
|
||||
false);
|
||||
econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
|
||||
/* Copy tuple, set up as input for qual test and projection */
|
||||
ExecCopySlot(firsttupleslot, outerslot);
|
||||
econtext->ecxt_scantuple = firsttupleslot;
|
||||
/*
|
||||
* Check the qual (HAVING clause); if the group does not match,
|
||||
* ignore it and loop back to scan the rest of the group.
|
||||
|
@ -173,7 +163,6 @@ ExecInitGroup(Group *node, EState *estate)
|
|||
grpstate = makeNode(GroupState);
|
||||
grpstate->ss.ps.plan = (Plan *) node;
|
||||
grpstate->ss.ps.state = estate;
|
||||
grpstate->grp_firstTuple = NULL;
|
||||
grpstate->grp_done = FALSE;
|
||||
|
||||
/*
|
||||
|
@ -255,11 +244,8 @@ void
|
|||
ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
|
||||
{
|
||||
node->grp_done = FALSE;
|
||||
if (node->grp_firstTuple != NULL)
|
||||
{
|
||||
heap_freetuple(node->grp_firstTuple);
|
||||
node->grp_firstTuple = NULL;
|
||||
}
|
||||
/* must clear first tuple */
|
||||
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
||||
|
||||
if (((PlanState *) node)->lefttree &&
|
||||
((PlanState *) node)->lefttree->chgParam == NULL)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.90 2005/03/13 19:59:40 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.91 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -75,7 +75,7 @@ ExecHash(HashState *node)
|
|||
/* We have to compute the hash value */
|
||||
econtext->ecxt_innertuple = slot;
|
||||
hashvalue = ExecHashGetHashValue(hashtable, econtext, hashkeys);
|
||||
ExecHashTableInsert(hashtable, slot->val, hashvalue);
|
||||
ExecHashTableInsert(hashtable, ExecFetchSlotTuple(slot), hashvalue);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.68 2005/03/06 22:15:04 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.69 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -188,7 +188,8 @@ ExecHashJoin(HashJoinState *node)
|
|||
* Save it in the corresponding outer-batch file.
|
||||
*/
|
||||
Assert(batchno > hashtable->curbatch);
|
||||
ExecHashJoinSaveTuple(outerTupleSlot->val, hashvalue,
|
||||
ExecHashJoinSaveTuple(ExecFetchSlotTuple(outerTupleSlot),
|
||||
hashvalue,
|
||||
&hashtable->outerBatchFile[batchno]);
|
||||
node->hj_NeedNewOuter = true;
|
||||
continue; /* loop around for a new outer tuple */
|
||||
|
@ -652,7 +653,9 @@ start_over:
|
|||
* NOTE: some tuples may be sent to future batches. Also,
|
||||
* it is possible for hashtable->nbatch to be increased here!
|
||||
*/
|
||||
ExecHashTableInsert(hashtable, slot->val, hashvalue);
|
||||
ExecHashTableInsert(hashtable,
|
||||
ExecFetchSlotTuple(slot),
|
||||
hashvalue);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.99 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.100 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -165,7 +165,7 @@ IndexNext(IndexScanState *node)
|
|||
break;
|
||||
}
|
||||
if (qual == NULL) /* would not be returned by indices */
|
||||
slot->val = NULL;
|
||||
ExecClearTuple(slot);
|
||||
|
||||
/* Flag for the next call that no more tuples */
|
||||
estate->es_evTupleNull[scanrelid - 1] = true;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.20 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.21 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -38,7 +38,6 @@ TupleTableSlot * /* return: a tuple or NULL */
|
|||
ExecLimit(LimitState *node)
|
||||
{
|
||||
ScanDirection direction;
|
||||
TupleTableSlot *resultTupleSlot;
|
||||
TupleTableSlot *slot;
|
||||
PlanState *outerPlan;
|
||||
|
||||
|
@ -47,7 +46,6 @@ ExecLimit(LimitState *node)
|
|||
*/
|
||||
direction = node->ps.state->es_direction;
|
||||
outerPlan = outerPlanState(node);
|
||||
resultTupleSlot = node->ps.ps_ResultTupleSlot;
|
||||
|
||||
/*
|
||||
* The main logic is a simple state machine.
|
||||
|
@ -219,12 +217,7 @@ ExecLimit(LimitState *node)
|
|||
/* Return the current tuple */
|
||||
Assert(!TupIsNull(slot));
|
||||
|
||||
ExecStoreTuple(slot->val,
|
||||
resultTupleSlot,
|
||||
InvalidBuffer,
|
||||
false); /* tuple does not belong to slot */
|
||||
|
||||
return resultTupleSlot;
|
||||
return slot;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -324,7 +317,7 @@ ExecInitLimit(Limit *node, EState *estate)
|
|||
#define LIMIT_NSLOTS 1
|
||||
|
||||
/*
|
||||
* Tuple table initialization
|
||||
* Tuple table initialization (XXX not actually used...)
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &limitstate->ps);
|
||||
|
||||
|
@ -363,10 +356,6 @@ void
|
|||
ExecEndLimit(LimitState *node)
|
||||
{
|
||||
ExecFreeExprContext(&node->ps);
|
||||
|
||||
/* clean up tuple table */
|
||||
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
||||
|
||||
ExecEndNode(outerPlanState(node));
|
||||
}
|
||||
|
||||
|
@ -377,8 +366,6 @@ ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
|
|||
/* resetting lstate will force offset/limit recalculation */
|
||||
node->lstate = LIMIT_INITIAL;
|
||||
|
||||
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
||||
|
||||
/*
|
||||
* if chgParam of subnode is not null then plan will be re-scanned by
|
||||
* first ExecProcNode.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.48 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.49 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -127,7 +127,7 @@ ExecMaterial(MaterialState *node)
|
|||
node->eof_underlying = true;
|
||||
return NULL;
|
||||
}
|
||||
heapTuple = outerslot->val;
|
||||
heapTuple = ExecFetchSlotTuple(outerslot);
|
||||
should_free = false;
|
||||
|
||||
/*
|
||||
|
@ -139,10 +139,13 @@ ExecMaterial(MaterialState *node)
|
|||
}
|
||||
|
||||
/*
|
||||
* Return the obtained tuple.
|
||||
* Return the obtained tuple, if any.
|
||||
*/
|
||||
slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot;
|
||||
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
||||
if (heapTuple)
|
||||
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
||||
else
|
||||
return ExecClearTuple(slot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.69 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.70 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -80,10 +80,8 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
|
|||
|
||||
#define MarkInnerTuple(innerTupleSlot, mergestate) \
|
||||
( \
|
||||
ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \
|
||||
(mergestate)->mj_MarkedTupleSlot, \
|
||||
InvalidBuffer, \
|
||||
true) \
|
||||
ExecCopySlot((mergestate)->mj_MarkedTupleSlot, \
|
||||
(innerTupleSlot)) \
|
||||
)
|
||||
|
||||
|
||||
|
@ -246,8 +244,7 @@ ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
|
|||
if (TupIsNull(outerSlot))
|
||||
printf("(nil)\n");
|
||||
else
|
||||
MJ_debugtup(outerSlot->val,
|
||||
outerSlot->ttc_tupleDescriptor);
|
||||
MJ_debugtup(outerSlot);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -259,8 +256,7 @@ ExecMergeTupleDumpInner(MergeJoinState *mergestate)
|
|||
if (TupIsNull(innerSlot))
|
||||
printf("(nil)\n");
|
||||
else
|
||||
MJ_debugtup(innerSlot->val,
|
||||
innerSlot->ttc_tupleDescriptor);
|
||||
MJ_debugtup(innerSlot);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -272,8 +268,7 @@ ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
|
|||
if (TupIsNull(markedSlot))
|
||||
printf("(nil)\n");
|
||||
else
|
||||
MJ_debugtup(markedSlot->val,
|
||||
markedSlot->ttc_tupleDescriptor);
|
||||
MJ_debugtup(markedSlot);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSeqscan.c,v 1.51 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSeqscan.c,v 1.52 2005/03/16 21:38:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -110,12 +110,12 @@ SeqNext(SeqScanState *node)
|
|||
* refcount of the buffer; the refcount will not be dropped until the
|
||||
* tuple table slot is cleared.
|
||||
*/
|
||||
|
||||
slot = ExecStoreTuple(tuple, /* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
scandesc->rs_cbuf, /* buffer associated with
|
||||
if (tuple)
|
||||
ExecStoreTuple(tuple, /* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
scandesc->rs_cbuf, /* buffer associated with
|
||||
* this tuple */
|
||||
false); /* don't pfree this pointer */
|
||||
false); /* don't pfree this pointer */
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.15 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.16 2005/03/16 21:38:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -49,14 +49,12 @@ ExecSetOp(SetOpState *node)
|
|||
SetOp *plannode = (SetOp *) node->ps.plan;
|
||||
TupleTableSlot *resultTupleSlot;
|
||||
PlanState *outerPlan;
|
||||
TupleDesc tupDesc;
|
||||
|
||||
/*
|
||||
* get information from the node
|
||||
*/
|
||||
outerPlan = outerPlanState(node);
|
||||
resultTupleSlot = node->ps.ps_ResultTupleSlot;
|
||||
tupDesc = ExecGetResultType(&node->ps);
|
||||
|
||||
/*
|
||||
* If the previously-returned tuple needs to be returned more than
|
||||
|
@ -105,11 +103,7 @@ ExecSetOp(SetOpState *node)
|
|||
*/
|
||||
if (node->subplan_done)
|
||||
return NULL; /* no more tuples */
|
||||
ExecStoreTuple(heap_copytuple(inputTupleSlot->val),
|
||||
resultTupleSlot,
|
||||
InvalidBuffer,
|
||||
true); /* free copied tuple at
|
||||
* ExecClearTuple */
|
||||
ExecCopySlot(resultTupleSlot, inputTupleSlot);
|
||||
node->numLeft = 0;
|
||||
node->numRight = 0;
|
||||
endOfGroup = false;
|
||||
|
@ -127,9 +121,8 @@ ExecSetOp(SetOpState *node)
|
|||
* Else test if the new tuple and the previously saved tuple
|
||||
* match.
|
||||
*/
|
||||
if (execTuplesMatch(inputTupleSlot->val,
|
||||
resultTupleSlot->val,
|
||||
tupDesc,
|
||||
if (execTuplesMatch(inputTupleSlot,
|
||||
resultTupleSlot,
|
||||
plannode->numCols, plannode->dupColIdx,
|
||||
node->eqfunctions,
|
||||
node->tempContext))
|
||||
|
@ -189,9 +182,8 @@ ExecSetOp(SetOpState *node)
|
|||
int flag;
|
||||
bool isNull;
|
||||
|
||||
flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
|
||||
flag = DatumGetInt32(slot_getattr(inputTupleSlot,
|
||||
plannode->flagColIdx,
|
||||
tupDesc,
|
||||
&isNull));
|
||||
Assert(!isNull);
|
||||
if (flag)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSort.c,v 1.49 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSort.c,v 1.50 2005/03/16 21:38:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -104,7 +104,8 @@ ExecSort(SortState *node)
|
|||
if (TupIsNull(slot))
|
||||
break;
|
||||
|
||||
tuplesort_puttuple(tuplesortstate, (void *) slot->val);
|
||||
tuplesort_puttuple(tuplesortstate,
|
||||
(void *) ExecFetchSlotTuple(slot));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -136,7 +137,10 @@ ExecSort(SortState *node)
|
|||
&should_free);
|
||||
|
||||
slot = node->ss.ps.ps_ResultTupleSlot;
|
||||
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
||||
if (heapTuple)
|
||||
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
||||
else
|
||||
return ExecClearTuple(slot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.66 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.67 2005/03/16 21:38:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -37,7 +37,8 @@ static Datum ExecScanSubPlan(SubPlanState *node,
|
|||
bool *isNull);
|
||||
static void buildSubPlanHash(SubPlanState *node);
|
||||
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
|
||||
static bool tupleAllNulls(HeapTuple tuple);
|
||||
static bool slotAllNulls(TupleTableSlot *slot);
|
||||
static bool slotNoNulls(TupleTableSlot *slot);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
@ -78,7 +79,6 @@ ExecHashSubPlan(SubPlanState *node,
|
|||
PlanState *planstate = node->planstate;
|
||||
ExprContext *innerecontext = node->innerecontext;
|
||||
TupleTableSlot *slot;
|
||||
HeapTuple tup;
|
||||
|
||||
/* Shouldn't have any direct correlation Vars */
|
||||
if (subplan->parParam != NIL || node->args != NIL)
|
||||
|
@ -105,7 +105,6 @@ ExecHashSubPlan(SubPlanState *node,
|
|||
*/
|
||||
node->projLeft->pi_exprContext = econtext;
|
||||
slot = ExecProject(node->projLeft, NULL);
|
||||
tup = slot->val;
|
||||
|
||||
/*
|
||||
* Note: because we are typically called in a per-tuple context, we
|
||||
|
@ -137,7 +136,7 @@ ExecHashSubPlan(SubPlanState *node,
|
|||
* comparison we will not even make, unless there's a chance match of
|
||||
* hash keys.
|
||||
*/
|
||||
if (HeapTupleNoNulls(tup))
|
||||
if (slotNoNulls(slot))
|
||||
{
|
||||
if (node->havehashrows &&
|
||||
LookupTupleHashEntry(node->hashtable, slot, NULL) != NULL)
|
||||
|
@ -171,7 +170,7 @@ ExecHashSubPlan(SubPlanState *node,
|
|||
ExecClearTuple(slot);
|
||||
return BoolGetDatum(false);
|
||||
}
|
||||
if (tupleAllNulls(tup))
|
||||
if (slotAllNulls(slot))
|
||||
{
|
||||
ExecClearTuple(slot);
|
||||
*isNull = true;
|
||||
|
@ -271,8 +270,7 @@ ExecScanSubPlan(SubPlanState *node,
|
|||
!TupIsNull(slot);
|
||||
slot = ExecProcNode(planstate))
|
||||
{
|
||||
HeapTuple tup = slot->val;
|
||||
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
||||
TupleDesc tdesc = slot->tts_tupleDescriptor;
|
||||
Datum rowresult = BoolGetDatum(!useOr);
|
||||
bool rownull = false;
|
||||
int col = 1;
|
||||
|
@ -303,13 +301,12 @@ ExecScanSubPlan(SubPlanState *node,
|
|||
* copied tuple for eventual freeing.
|
||||
*/
|
||||
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
||||
tup = heap_copytuple(tup);
|
||||
if (node->curTuple)
|
||||
heap_freetuple(node->curTuple);
|
||||
node->curTuple = tup;
|
||||
node->curTuple = ExecCopySlotTuple(slot);
|
||||
MemoryContextSwitchTo(node->sub_estate->es_query_cxt);
|
||||
|
||||
result = heap_getattr(tup, col, tdesc, isNull);
|
||||
result = heap_getattr(node->curTuple, col, tdesc, isNull);
|
||||
/* keep scanning subplan to make sure there's only one tuple */
|
||||
continue;
|
||||
}
|
||||
|
@ -321,7 +318,7 @@ ExecScanSubPlan(SubPlanState *node,
|
|||
|
||||
found = true;
|
||||
/* stash away current value */
|
||||
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
|
||||
dvalue = slot_getattr(slot, 1, &disnull);
|
||||
astate = accumArrayResult(astate, dvalue, disnull,
|
||||
tdesc->attrs[0]->atttypid,
|
||||
oldcontext);
|
||||
|
@ -357,7 +354,7 @@ ExecScanSubPlan(SubPlanState *node,
|
|||
*/
|
||||
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
|
||||
Assert(prmdata->execPlan == NULL);
|
||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||
prmdata->value = slot_getattr(slot, col,
|
||||
&(prmdata->isnull));
|
||||
|
||||
/*
|
||||
|
@ -554,8 +551,6 @@ buildSubPlanHash(SubPlanState *node)
|
|||
!TupIsNull(slot);
|
||||
slot = ExecProcNode(planstate))
|
||||
{
|
||||
HeapTuple tup = slot->val;
|
||||
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
||||
int col = 1;
|
||||
ListCell *plst;
|
||||
bool isnew;
|
||||
|
@ -571,20 +566,16 @@ buildSubPlanHash(SubPlanState *node)
|
|||
|
||||
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
|
||||
Assert(prmdata->execPlan == NULL);
|
||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||
prmdata->value = slot_getattr(slot, col,
|
||||
&(prmdata->isnull));
|
||||
col++;
|
||||
}
|
||||
slot = ExecProject(node->projRight, NULL);
|
||||
tup = slot->val;
|
||||
|
||||
/*
|
||||
* If result contains any nulls, store separately or not at all.
|
||||
* (Since we know the projection tuple has no junk columns, we can
|
||||
* just look at the overall hasnull info bit, instead of groveling
|
||||
* through the columns.)
|
||||
*/
|
||||
if (HeapTupleNoNulls(tup))
|
||||
if (slotNoNulls(slot))
|
||||
{
|
||||
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
||||
node->havehashrows = true;
|
||||
|
@ -606,7 +597,8 @@ buildSubPlanHash(SubPlanState *node)
|
|||
* Since the projected tuples are in the sub-query's context and not
|
||||
* the main context, we'd better clear the tuple slot before there's
|
||||
* any chance of a reset of the sub-query's context. Else we will
|
||||
* have the potential for a double free attempt.
|
||||
* have the potential for a double free attempt. (XXX possibly
|
||||
* no longer needed, but can't hurt.)
|
||||
*/
|
||||
ExecClearTuple(node->projRight->pi_slot);
|
||||
|
||||
|
@ -626,17 +618,15 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
|
|||
{
|
||||
int numCols = hashtable->numCols;
|
||||
AttrNumber *keyColIdx = hashtable->keyColIdx;
|
||||
HeapTuple tuple = slot->val;
|
||||
TupleDesc tupdesc = slot->ttc_tupleDescriptor;
|
||||
TupleHashIterator hashiter;
|
||||
TupleHashEntry entry;
|
||||
|
||||
ResetTupleHashIterator(hashtable, &hashiter);
|
||||
while ((entry = ScanTupleHashTable(&hashiter)) != NULL)
|
||||
{
|
||||
if (!execTuplesUnequal(entry->firstTuple,
|
||||
tuple,
|
||||
tupdesc,
|
||||
ExecStoreTuple(entry->firstTuple, hashtable->tableslot,
|
||||
InvalidBuffer, false);
|
||||
if (!execTuplesUnequal(hashtable->tableslot, slot,
|
||||
numCols, keyColIdx,
|
||||
hashtable->eqfunctions,
|
||||
hashtable->tempcxt))
|
||||
|
@ -646,17 +636,40 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
|
|||
}
|
||||
|
||||
/*
|
||||
* tupleAllNulls: is the tuple completely NULL?
|
||||
* slotAllNulls: is the slot completely NULL?
|
||||
*
|
||||
* This does not test for dropped columns, which is OK because we only
|
||||
* use it on projected tuples.
|
||||
*/
|
||||
static bool
|
||||
tupleAllNulls(HeapTuple tuple)
|
||||
slotAllNulls(TupleTableSlot *slot)
|
||||
{
|
||||
int ncols = tuple->t_data->t_natts;
|
||||
int ncols = slot->tts_tupleDescriptor->natts;
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= ncols; i++)
|
||||
{
|
||||
if (!heap_attisnull(tuple, i))
|
||||
if (!slot_attisnull(slot, i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* slotNoNulls: is the slot entirely not NULL?
|
||||
*
|
||||
* This does not test for dropped columns, which is OK because we only
|
||||
* use it on projected tuples.
|
||||
*/
|
||||
static bool
|
||||
slotNoNulls(TupleTableSlot *slot)
|
||||
{
|
||||
int ncols = slot->tts_tupleDescriptor->natts;
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= ncols; i++)
|
||||
{
|
||||
if (slot_attisnull(slot, i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -932,8 +945,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
|||
!TupIsNull(slot);
|
||||
slot = ExecProcNode(planstate))
|
||||
{
|
||||
HeapTuple tup = slot->val;
|
||||
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
||||
TupleDesc tdesc = slot->tts_tupleDescriptor;
|
||||
int i = 1;
|
||||
|
||||
if (subLinkType == EXISTS_SUBLINK)
|
||||
|
@ -956,7 +968,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
|||
|
||||
found = true;
|
||||
/* stash away current value */
|
||||
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
|
||||
dvalue = slot_getattr(slot, 1, &disnull);
|
||||
astate = accumArrayResult(astate, dvalue, disnull,
|
||||
tdesc->attrs[0]->atttypid,
|
||||
oldcontext);
|
||||
|
@ -981,10 +993,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
|||
* freeing.
|
||||
*/
|
||||
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
||||
tup = heap_copytuple(tup);
|
||||
if (node->curTuple)
|
||||
heap_freetuple(node->curTuple);
|
||||
node->curTuple = tup;
|
||||
node->curTuple = ExecCopySlotTuple(slot);
|
||||
MemoryContextSwitchTo(node->sub_estate->es_query_cxt);
|
||||
|
||||
/*
|
||||
|
@ -996,7 +1007,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
|||
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
||||
|
||||
prm->execPlan = NULL;
|
||||
prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull));
|
||||
prm->value = heap_getattr(node->curTuple, i, tdesc,
|
||||
&(prm->isnull));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeUnique.c,v 1.45 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeUnique.c,v 1.46 2005/03/16 21:38:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -44,14 +44,12 @@ ExecUnique(UniqueState *node)
|
|||
TupleTableSlot *resultTupleSlot;
|
||||
TupleTableSlot *slot;
|
||||
PlanState *outerPlan;
|
||||
TupleDesc tupDesc;
|
||||
|
||||
/*
|
||||
* get information from the node
|
||||
*/
|
||||
outerPlan = outerPlanState(node);
|
||||
resultTupleSlot = node->ps.ps_ResultTupleSlot;
|
||||
tupDesc = ExecGetResultType(&node->ps);
|
||||
|
||||
/*
|
||||
* now loop, returning only non-duplicate tuples. We assume that the
|
||||
|
@ -59,7 +57,7 @@ ExecUnique(UniqueState *node)
|
|||
*
|
||||
* We return the first tuple from each group of duplicates (or the last
|
||||
* tuple of each group, when moving backwards). At either end of the
|
||||
* subplan, clear priorTuple so that we correctly return the
|
||||
* subplan, clear the result slot so that we correctly return the
|
||||
* first/last tuple when reversing direction.
|
||||
*/
|
||||
for (;;)
|
||||
|
@ -71,16 +69,14 @@ ExecUnique(UniqueState *node)
|
|||
if (TupIsNull(slot))
|
||||
{
|
||||
/* end of subplan; reset in case we change direction */
|
||||
if (node->priorTuple != NULL)
|
||||
heap_freetuple(node->priorTuple);
|
||||
node->priorTuple = NULL;
|
||||
ExecClearTuple(resultTupleSlot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Always return the first/last tuple from the subplan.
|
||||
*/
|
||||
if (node->priorTuple == NULL)
|
||||
if (TupIsNull(resultTupleSlot))
|
||||
break;
|
||||
|
||||
/*
|
||||
|
@ -88,8 +84,7 @@ ExecUnique(UniqueState *node)
|
|||
* match. If so then we loop back and fetch another new tuple
|
||||
* from the subplan.
|
||||
*/
|
||||
if (!execTuplesMatch(slot->val, node->priorTuple,
|
||||
tupDesc,
|
||||
if (!execTuplesMatch(slot, resultTupleSlot,
|
||||
plannode->numCols, plannode->uniqColIdx,
|
||||
node->eqfunctions,
|
||||
node->tempContext))
|
||||
|
@ -101,28 +96,8 @@ ExecUnique(UniqueState *node)
|
|||
* any). Save it and return it. We must copy it because the source
|
||||
* subplan won't guarantee that this source tuple is still accessible
|
||||
* after fetching the next source tuple.
|
||||
*
|
||||
* Note that we manage the copy ourselves. We can't rely on the result
|
||||
* tuple slot to maintain the tuple reference because our caller may
|
||||
* replace the slot contents with a different tuple. We assume that
|
||||
* the caller will no longer be interested in the current tuple after
|
||||
* he next calls us.
|
||||
*
|
||||
* tgl 3/2004: the above concern is no longer valid; junkfilters used to
|
||||
* modify their input's return slot but don't anymore, and I don't
|
||||
* think anyplace else does either. Not worth changing this code
|
||||
* though.
|
||||
*/
|
||||
if (node->priorTuple != NULL)
|
||||
heap_freetuple(node->priorTuple);
|
||||
node->priorTuple = heap_copytuple(slot->val);
|
||||
|
||||
ExecStoreTuple(node->priorTuple,
|
||||
resultTupleSlot,
|
||||
InvalidBuffer,
|
||||
false); /* tuple does not belong to slot */
|
||||
|
||||
return resultTupleSlot;
|
||||
return ExecCopySlot(resultTupleSlot, slot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
@ -144,8 +119,6 @@ ExecInitUnique(Unique *node, EState *estate)
|
|||
uniquestate->ps.plan = (Plan *) node;
|
||||
uniquestate->ps.state = estate;
|
||||
|
||||
uniquestate->priorTuple = NULL;
|
||||
|
||||
/*
|
||||
* Miscellaneous initialization
|
||||
*
|
||||
|
@ -220,12 +193,8 @@ ExecEndUnique(UniqueState *node)
|
|||
void
|
||||
ExecReScanUnique(UniqueState *node, ExprContext *exprCtxt)
|
||||
{
|
||||
/* must clear result tuple so first input tuple is returned */
|
||||
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
||||
if (node->priorTuple != NULL)
|
||||
{
|
||||
heap_freetuple(node->priorTuple);
|
||||
node->priorTuple = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* if chgParam of subnode is not null then plan will be re-scanned by
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.134 2005/02/10 20:36:27 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.135 2005/03/16 21:38:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -1191,7 +1191,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|||
* of current SPI procedure
|
||||
*/
|
||||
void
|
||||
spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
|
||||
spi_printtup(TupleTableSlot *slot, DestReceiver *self)
|
||||
{
|
||||
SPITupleTable *tuptable;
|
||||
MemoryContext oldcxt;
|
||||
|
@ -1219,7 +1219,8 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
|
|||
tuptable->alloced * sizeof(HeapTuple));
|
||||
}
|
||||
|
||||
tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
|
||||
tuptable->vals[tuptable->alloced - tuptable->free] =
|
||||
ExecCopySlotTuple(slot);
|
||||
(tuptable->free)--;
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.13 2004/12/31 21:59:45 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.14 2005/03/16 21:38:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -40,12 +40,12 @@ tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|||
* Receive a tuple from the executor and store it in the tuplestore.
|
||||
*/
|
||||
static void
|
||||
tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
||||
tstoreReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
|
||||
{
|
||||
TStoreState *myState = (TStoreState *) self;
|
||||
MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt);
|
||||
|
||||
tuplestore_puttuple(myState->tstore, tuple);
|
||||
tuplestore_puttuple(myState->tstore, ExecFetchSlotTuple(slot));
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ CreateTuplestoreDestReceiver(Tuplestorestate *tStore,
|
|||
{
|
||||
TStoreState *self = (TStoreState *) palloc(sizeof(TStoreState));
|
||||
|
||||
self->pub.receiveTuple = tstoreReceiveTuple;
|
||||
self->pub.receiveSlot = tstoreReceiveSlot;
|
||||
self->pub.rStartup = tstoreStartupReceiver;
|
||||
self->pub.rShutdown = tstoreShutdownReceiver;
|
||||
self->pub.rDestroy = tstoreDestroyReceiver;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
|
@ -468,18 +468,18 @@ print_tl(List *tlist, List *rtable)
|
|||
void
|
||||
print_slot(TupleTableSlot *slot)
|
||||
{
|
||||
if (!slot->val)
|
||||
if (TupIsNull(slot))
|
||||
{
|
||||
printf("tuple is null.\n");
|
||||
return;
|
||||
}
|
||||
if (!slot->ttc_tupleDescriptor)
|
||||
if (!slot->tts_tupleDescriptor)
|
||||
{
|
||||
printf("no tuple descriptor.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
debugtup(slot->val, slot->ttc_tupleDescriptor, NULL);
|
||||
debugtup(slot, NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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 @@
|
|||
* ----------------
|
||||
*/
|
||||
static void
|
||||
donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
|
||||
donothingReceive(TupleTableSlot *slot, DestReceiver *self)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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,
|
|||
DestReceiver *dest)
|
||||
{
|
||||
long current_tuple_count = 0;
|
||||
TupleTableSlot *slot;
|
||||
|
||||
slot = MakeSingleTupleTableSlot(portal->tupDesc);
|
||||
|
||||
(*dest->rStartup) (dest, CMD_SELECT, portal->tupDesc);
|
||||
|
||||
|
@ -863,10 +866,11 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
|
|||
if (tup == NULL)
|
||||
break;
|
||||
|
||||
(*dest->receiveTuple) (tup, portal->tupDesc, dest);
|
||||
ExecStoreTuple(tup, slot, InvalidBuffer, should_free);
|
||||
|
||||
if (should_free)
|
||||
pfree(tup);
|
||||
(*dest->receiveSlot) (slot, dest);
|
||||
|
||||
ExecClearTuple(slot);
|
||||
|
||||
/*
|
||||
* check our tuple count.. if we've processed the proper
|
||||
|
@ -881,6 +885,8 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
|
|||
|
||||
(*dest->rShutdown) (dest);
|
||||
|
||||
ExecDropSingleTupleTableSlot(slot);
|
||||
|
||||
return (uint32) current_tuple_count;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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,
|
|||
Buffer newbuf, HeapTuple newtup);
|
||||
|
||||
/* 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 void heap_fill_tuple(TupleDesc tupleDesc,
|
||||
Datum *values, bool *isnull,
|
||||
char *data, uint16 *infomask, bits8 *bit);
|
||||
extern void DataFill(char *data, TupleDesc tupleDesc,
|
||||
Datum *values, char *nulls, uint16 *infomask,
|
||||
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,
|
||||
TupleDesc att, bool *isnull);
|
||||
extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
|
||||
bool *isnull);
|
||||
extern HeapTuple heap_copytuple(HeapTuple tuple);
|
||||
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,
|
||||
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,
|
||||
TupleDesc tupleDesc,
|
||||
Datum *replValues,
|
||||
char *replNulls,
|
||||
char *replActions);
|
||||
extern void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
|
||||
Datum *values, bool *isnull);
|
||||
extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc,
|
||||
Datum *values, char *nulls);
|
||||
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 */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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,
|
|||
|
||||
extern void debugStartup(DestReceiver *self, int operation,
|
||||
TupleDesc typeinfo);
|
||||
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver *self);
|
||||
extern void debugtup(TupleTableSlot *slot, DestReceiver *self);
|
||||
|
||||
/* XXX these are really in executor/spi.c */
|
||||
extern void spi_dest_startup(DestReceiver *self, int operation,
|
||||
TupleDesc typeinfo);
|
||||
extern void spi_printtup(HeapTuple tuple, TupleDesc typeinfo,
|
||||
DestReceiver *self);
|
||||
extern void spi_printtup(TupleTableSlot *slot, DestReceiver *self);
|
||||
|
||||
#endif /* PRINTTUP_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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);
|
|||
extern IndexInfo *BuildIndexInfo(Relation index);
|
||||
|
||||
extern void FormIndexDatum(IndexInfo *indexInfo,
|
||||
HeapTuple heapTuple,
|
||||
TupleDesc heapDescriptor,
|
||||
TupleTableSlot *slot,
|
||||
EState *estate,
|
||||
Datum *datum,
|
||||
char *nullv);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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;
|
|||
#define MJ_printf(s) printf(s)
|
||||
#define MJ1_printf(s, p) printf(s, p)
|
||||
#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_DEBUG_QUAL(clause, res) \
|
||||
MJ2_printf(" ExecQual(%s, econtext) returns %s\n", \
|
||||
|
@ -276,7 +276,7 @@ extern int NIndexTupleInserted;
|
|||
#define MJ_printf(s)
|
||||
#define MJ1_printf(s, p)
|
||||
#define MJ2_printf(s, p1, p2)
|
||||
#define MJ_debugtup(tuple, type)
|
||||
#define MJ_debugtup(slot)
|
||||
#define MJ_dump(state)
|
||||
#define MJ_DEBUG_QUAL(clause, res)
|
||||
#define MJ_DEBUG_MERGE_COMPARE(qual, res)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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 @@
|
|||
#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;
|
||||
* now it's just a macro invoking the function pointed to by an ExprState
|
||||
|
@ -50,20 +39,18 @@ extern bool ExecMayReturnRawTuples(PlanState *node);
|
|||
/*
|
||||
* prototypes from functions in execGrouping.c
|
||||
*/
|
||||
extern bool execTuplesMatch(HeapTuple tuple1,
|
||||
HeapTuple tuple2,
|
||||
TupleDesc tupdesc,
|
||||
int numCols,
|
||||
AttrNumber *matchColIdx,
|
||||
FmgrInfo *eqfunctions,
|
||||
MemoryContext evalContext);
|
||||
extern bool execTuplesUnequal(HeapTuple tuple1,
|
||||
HeapTuple tuple2,
|
||||
TupleDesc tupdesc,
|
||||
int numCols,
|
||||
AttrNumber *matchColIdx,
|
||||
FmgrInfo *eqfunctions,
|
||||
MemoryContext evalContext);
|
||||
extern bool execTuplesMatch(TupleTableSlot *slot1,
|
||||
TupleTableSlot *slot2,
|
||||
int numCols,
|
||||
AttrNumber *matchColIdx,
|
||||
FmgrInfo *eqfunctions,
|
||||
MemoryContext evalContext);
|
||||
extern bool execTuplesUnequal(TupleTableSlot *slot1,
|
||||
TupleTableSlot *slot2,
|
||||
int numCols,
|
||||
AttrNumber *matchColIdx,
|
||||
FmgrInfo *eqfunctions,
|
||||
MemoryContext evalContext);
|
||||
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
|
||||
int numCols,
|
||||
AttrNumber *matchColIdx);
|
||||
|
@ -92,6 +79,8 @@ extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
|
|||
TupleTableSlot *slot);
|
||||
extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
|
||||
char *attrName, Datum *value, bool *isNull);
|
||||
extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
|
||||
TupleTableSlot *slot);
|
||||
extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
|
||||
|
||||
|
||||
|
@ -158,17 +147,6 @@ extern void ExecAssignScanProjectionInfo(ScanState *node);
|
|||
/*
|
||||
* 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 ExecInitScanTupleSlot(EState *estate, ScanState *scanstate);
|
||||
extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
|
||||
|
@ -183,6 +161,7 @@ typedef struct TupOutputState
|
|||
{
|
||||
/* use "struct" here to allow forward reference */
|
||||
struct AttInMetadata *metadata;
|
||||
TupleTableSlot *slot;
|
||||
DestReceiver *dest;
|
||||
} TupOutputState;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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 @@
|
|||
#include "access/htup.h"
|
||||
|
||||
|
||||
/*
|
||||
* The executor stores pointers to tuples in a "tuple table"
|
||||
* which is composed of TupleTableSlots. Sometimes the tuples
|
||||
* are pointers to buffer pages, while others are pointers to
|
||||
* palloc'ed memory; the shouldFree variable tells us whether
|
||||
* we may call pfree() on a tuple. When shouldFree is true,
|
||||
* the tuple is "owned" by the TupleTableSlot and should be
|
||||
* freed when the slot's reference to the tuple is dropped.
|
||||
/*----------
|
||||
* The executor stores tuples in a "tuple table" which is composed of
|
||||
* independent TupleTableSlots. There are several cases we need to handle:
|
||||
* 1. physical tuple in a disk buffer page
|
||||
* 2. physical tuple constructed in palloc'ed memory
|
||||
* 3. "virtual" tuple consisting of Datum/isnull arrays
|
||||
*
|
||||
* shouldFreeDesc is similar to shouldFree: if it's true, then the
|
||||
* 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.
|
||||
*
|
||||
* tts_shouldFreeDesc is similar to tts_shouldFree: if it's true, then the
|
||||
* tupleDescriptor is "owned" by the TupleTableSlot and should be
|
||||
* 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
|
||||
* slot's reference to that buffer. (shouldFree should always be
|
||||
* false in such a case, since presumably val is pointing at the
|
||||
* slot's reference to that buffer. (tts_shouldFree should always be
|
||||
* false in such a case, since presumably tts_tuple is pointing at the
|
||||
* buffer page.)
|
||||
*
|
||||
* The slot_getattr() routine allows extraction of attribute values from
|
||||
* a TupleTableSlot's current tuple. It is equivalent to heap_getattr()
|
||||
* except that it can optimize fetching of multiple values more efficiently.
|
||||
* The cache_xxx fields of TupleTableSlot are support for slot_getattr().
|
||||
* tts_nvalid indicates the number of valid columns in the tts_values/isnull
|
||||
* arrays. When the slot is holding a "virtual" tuple this must be equal
|
||||
* to the descriptor's natts. When the slot is holding a physical tuple
|
||||
* 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
|
||||
{
|
||||
NodeTag type; /* vestigial ... allows IsA tests */
|
||||
HeapTuple val; /* current tuple, or NULL if none */
|
||||
TupleDesc ttc_tupleDescriptor; /* tuple's descriptor */
|
||||
bool ttc_shouldFree; /* should pfree tuple? */
|
||||
bool ttc_shouldFreeDesc; /* should pfree descriptor? */
|
||||
Buffer ttc_buffer; /* tuple's buffer, or InvalidBuffer */
|
||||
MemoryContext ttc_mcxt; /* slot itself is in this context */
|
||||
Datum *cache_values; /* currently extracted values */
|
||||
int cache_natts; /* # of valid values in cache_values */
|
||||
bool cache_slow; /* saved state for slot_getattr */
|
||||
long cache_off; /* saved state for slot_getattr */
|
||||
bool tts_isempty; /* true = slot is empty */
|
||||
bool tts_shouldFree; /* should pfree tuple? */
|
||||
bool tts_shouldFreeDesc; /* should pfree descriptor? */
|
||||
bool tts_slow; /* saved state for slot_deform_tuple */
|
||||
HeapTuple tts_tuple; /* physical tuple, or NULL if none */
|
||||
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
|
||||
MemoryContext tts_mcxt; /* slot itself is in this context */
|
||||
Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
|
||||
int tts_nvalid; /* # of valid values in tts_values */
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -69,7 +112,36 @@ typedef struct TupleTableData
|
|||
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 */
|
||||
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 */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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
|
|||
* This is all the information needed to perform projections ---
|
||||
* that is, form new tuples by evaluation of targetlist expressions.
|
||||
* 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
|
||||
* in the given slot. As a side-effect, the actual datum values and
|
||||
* null indicators are placed in the work arrays tupValues/tupNulls.
|
||||
* in the given slot. Note that the result will be a "virtual" tuple
|
||||
* 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
|
||||
* exprContext expression context in which to evaluate targetlist
|
||||
* slot slot to place projection result in
|
||||
* tupValues array of computed values
|
||||
* tupNull array of null indicators
|
||||
* 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
|
||||
|
@ -197,9 +205,13 @@ typedef struct ProjectionInfo
|
|||
List *pi_targetlist;
|
||||
ExprContext *pi_exprContext;
|
||||
TupleTableSlot *pi_slot;
|
||||
Datum *pi_tupValues;
|
||||
char *pi_tupNulls;
|
||||
ExprDoneCond *pi_itemIsDone;
|
||||
bool pi_isVarList;
|
||||
int *pi_varSlotOffsets;
|
||||
int *pi_varNumbers;
|
||||
int pi_lastInnerVar;
|
||||
int pi_lastOuterVar;
|
||||
int pi_lastScanVar;
|
||||
} ProjectionInfo;
|
||||
|
||||
/* ----------------
|
||||
|
@ -222,7 +234,7 @@ typedef struct ProjectionInfo
|
|||
* cleanMap: A map with the correspondence between the non-junk
|
||||
* attribute numbers of the "original" tuple and the
|
||||
* 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
|
||||
|
@ -354,7 +366,8 @@ typedef struct TupleHashTableData
|
|||
MemoryContext tablecxt; /* memory context containing table */
|
||||
MemoryContext tempcxt; /* context for function evaluations */
|
||||
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;
|
||||
|
||||
typedef HASH_SEQ_STATUS TupleHashIterator;
|
||||
|
@ -589,9 +602,9 @@ typedef struct ConvertRowtypeExprState
|
|||
TupleDesc outdesc; /* tupdesc for result rowtype */
|
||||
AttrNumber *attrMap; /* indexes of input fields, or 0 for null */
|
||||
Datum *invalues; /* workspace for deconstructing source */
|
||||
char *innulls;
|
||||
bool *inisnull;
|
||||
Datum *outvalues; /* workspace for constructing result */
|
||||
char *outnulls;
|
||||
bool *outisnull;
|
||||
} ConvertRowtypeExprState;
|
||||
|
||||
/* ----------------
|
||||
|
@ -1065,7 +1078,6 @@ typedef struct GroupState
|
|||
{
|
||||
ScanState ss; /* its first field is NodeTag */
|
||||
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 */
|
||||
} GroupState;
|
||||
|
||||
|
@ -1111,7 +1123,7 @@ typedef struct AggState
|
|||
* Unique nodes are used "on top of" sort nodes to discard
|
||||
* duplicate tuples returned from the sort phase. Basically
|
||||
* 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
|
||||
* we just fetch another tuple from the sort and try again.
|
||||
* ----------------
|
||||
|
@ -1120,7 +1132,6 @@ typedef struct UniqueState
|
|||
{
|
||||
PlanState ps; /* its first field is NodeTag */
|
||||
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
|
||||
HeapTuple priorTuple; /* most recently returned tuple, or NULL */
|
||||
MemoryContext tempContext; /* short-term context for comparisons */
|
||||
} UniqueState;
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
* destination. The executor, as well as utility statements that can return
|
||||
* tuples, are passed the resulting DestReceiver* pointer. Each executor run
|
||||
* 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
|
||||
* destroyed by calling its rDestroy method.
|
||||
*
|
||||
|
@ -54,14 +54,14 @@
|
|||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* 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
|
||||
#define DEST_H
|
||||
|
||||
#include "access/htup.h"
|
||||
#include "executor/tuptable.h"
|
||||
|
||||
|
||||
/* buffer size to use for command completion tags */
|
||||
|
@ -92,10 +92,8 @@ typedef enum
|
|||
* In the simplest cases, there is no state info, just the function
|
||||
* pointers that the executor must call.
|
||||
*
|
||||
* Note: the receiveTuple routine must be passed a TupleDesc identical to the
|
||||
* one given to the rStartup routine. The reason for passing it again is just
|
||||
* that some destinations would otherwise need dynamic state merely to
|
||||
* remember the tupledesc pointer.
|
||||
* Note: the receiveSlot routine must be passed a slot containing a TupleDesc
|
||||
* identical to the one given to the rStartup routine.
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct _DestReceiver DestReceiver;
|
||||
|
@ -103,9 +101,8 @@ typedef struct _DestReceiver DestReceiver;
|
|||
struct _DestReceiver
|
||||
{
|
||||
/* Called for each tuple to be output: */
|
||||
void (*receiveTuple) (HeapTuple tuple,
|
||||
TupleDesc typeinfo,
|
||||
DestReceiver *self);
|
||||
void (*receiveSlot) (TupleTableSlot *slot,
|
||||
DestReceiver *self);
|
||||
/* Per-executor-run initialization and shutdown: */
|
||||
void (*rStartup) (DestReceiver *self,
|
||||
int operation,
|
||||
|
|
Loading…
Reference in New Issue