<!--
-$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">
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
<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>
*
* 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.
*/
* 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 $
*
*-------------------------------------------------------------------------
*/
* ----------------------------------------------------------------
*/
+/*
+ * 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
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
*/
/* ----------------
- * 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);
}
elog(ERROR, "invalid attnum: %d", attnum);
}
- return 0;
+ return false;
}
/* ----------------
* 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
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 */
/*
* 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++)
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
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
*
*
* 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
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
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
*
* 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
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;
}
}
-/* ----------------
- * 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 */
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;
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
/*
* 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];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
- if (attnum > tup->t_natts || attnum > tupleDesc->natts)
+ /*
+ * 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
+ * check if target attribute is null: no point in groveling through tuple
*/
if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
{
}
/*
- * If attribute wasn't already extracted, extract it and preceding
- * attributes.
+ * Extract the attribute, along with any preceding attributes.
+ */
+ slot_deform_tuple(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
*/
- if (attnum > slot->cache_natts)
+ 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++)
{
- /*
- * 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->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;
- slot_deformtuple(slot, attnum);
+ /*
+ * 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);
}
/* ----------------
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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;
/* ----------------
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
- self->pub.receiveTuple = printtup;
+ self->pub.receiveSlot = printtup;
else
{
/*
* 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;
self->attrinfo = NULL;
self->nattrs = 0;
self->myinfo = NULL;
- self->values = NULL;
- self->nulls = NULL;
return (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 = typeinfo;
myState->nattrs = 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++)
{
* ----------------
*/
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;
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
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;
* ----------------
*/
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;
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)
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? */
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);
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;
}
* ----------------
*/
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,
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,
* ----------------
*/
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;
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)
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? */
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);
*
*
* 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
* 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)
*/
void
FormIndexDatum(IndexInfo *indexInfo,
- HeapTuple heapTuple,
- TupleDesc heapDescriptor,
+ TupleTableSlot *slot,
EState *estate,
Datum *datum,
char *nullv)
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);
* 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
{
{
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;
*/
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
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
* evaluation of any expressions needed.
*/
FormIndexDatum(indexInfo,
- heapTuple,
- heapDescriptor,
+ slot,
estate,
attdata,
nulls);
heap_endscan(scan);
- if (tupleTable)
- ExecDropTupleTable(tupleTable, true);
+ ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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
/*
* 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);
if (result)
pfree(result);
}
+
+ ExecDropSingleTupleTableSlot(slot);
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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,
AnlIndexData *thisdata = &indexdata[ind];
IndexInfo *indexInfo = thisdata->indexInfo;
int attr_cnt = thisdata->attr_cnt;
- TupleTable tupleTable;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
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;
* convenient.
*/
FormIndexDatum(indexInfo,
- heapTuple,
- heapDescriptor,
+ slot,
estate,
attdata,
nulls);
/* And clean up */
MemoryContextSwitchTo(ind_context);
- ExecDropTupleTable(tupleTable, true);
+ ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
MemoryContextResetAndDeleteChildren(ind_context);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
bool isnull;
ResultRelInfo *resultRelInfo;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
- TupleTable tupleTable;
TupleTableSlot *slot;
bool file_has_oids;
int *defmap;
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);
pfree(constraintexprs);
pfree(force_notnull);
- ExecDropTupleTable(tupleTable, true);
+ ExecDropSingleTupleTableSlot(slot);
ExecCloseIndices(resultRelInfo);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
ExprContext *econtext;
Datum *values;
- char *nulls;
+ bool *isnull;
TupleTableSlot *oldslot;
TupleTableSlot *newslot;
HeapScanDesc scan;
* 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
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
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] = ' ';
}
/*
* 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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
ResultRelInfo *resultRelInfo;
EState *estate;
- TupleTable tupleTable;
TupleTableSlot *slot;
} ExecContextData;
typedef ExecContextData *ExecContext;
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);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
* 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,
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)
{
* Parameters are identical to execTuplesMatch.
*/
bool
-execTuplesUnequal(HeapTuple tuple1,
- HeapTuple tuple2,
- TupleDesc tupdesc,
+execTuplesUnequal(TupleTableSlot *slot1,
+ TupleTableSlot *slot2,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo *eqfunctions,
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 */
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);
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);
* 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);
/* Copy the first tuple into the table context */
MemoryContextSwitchTo(hashtable->tablecxt);
- entry->firstTuple = heap_copytuple(tuple);
+ entry->firstTuple = ExecCopySlotTuple(slot);
*isnew = true;
}
/*
* 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.
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];
/* 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 */
{
/*
* 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.
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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
*
*-------------------------------------------------------------------------
*/
*/
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.
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
- if (slot)
- ExecSetSlotDescriptor(slot, cleanTupType, false);
-
return junkfilter;
}
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.
junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_resultSlot = slot;
- if (slot)
- ExecSetSlotDescriptor(slot, cleanTupType, false);
-
return junkfilter;
}
}
/*
- * ExecRemoveJunk
+ * ExecFilterJunk
*
- * Construct and return a tuple with all the junk attributes removed.
- *
- * Note: for historical reasons, this does not store the constructed
- * tuple into the junkfilter's resultSlot. The caller should do that
- * if it wants to.
+ * Construct and return a slot with all the junk attributes removed.
*/
-HeapTuple
-ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
+TupleTableSlot *
+ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
{
-#define PREALLOC_SIZE 64
- HeapTuple tuple;
- HeapTuple cleanTuple;
+ TupleTableSlot *resultSlot;
AttrNumber *cleanMap;
TupleDesc cleanTupType;
- TupleDesc tupType;
int cleanLength;
- int oldLength;
int i;
Datum *values;
- char *nulls;
+ bool *isnull;
Datum *old_values;
- char *old_nulls;
- Datum values_array[PREALLOC_SIZE];
- Datum old_values_array[PREALLOC_SIZE];
- char nulls_array[PREALLOC_SIZE];
- char old_nulls_array[PREALLOC_SIZE];
+ bool *old_isnull;
/*
- * get info from the slot and the junk filter
+ * Extract all the values of the old tuple.
*/
- tuple = slot->val;
- tupType = slot->ttc_tupleDescriptor;
- oldLength = tupType->natts + 1; /* +1 for NULL */
+ slot_getallattrs(slot);
+ old_values = slot->tts_values;
+ old_isnull = slot->tts_isnull;
+ /*
+ * get info from the junk filter
+ */
cleanTupType = junkfilter->jf_cleanTupType;
cleanLength = cleanTupType->natts;
cleanMap = junkfilter->jf_cleanMap;
+ resultSlot = junkfilter->jf_resultSlot;
/*
- * Create the arrays that will hold the attribute values and the null
- * information for the old tuple and new "clean" tuple.
- *
- * Note: we use memory on the stack to optimize things when we are
- * dealing with a small number of attributes. for large tuples we just
- * use palloc.
+ * Prepare to build a virtual result tuple.
*/
- if (cleanLength > PREALLOC_SIZE)
- {
- values = (Datum *) palloc(cleanLength * sizeof(Datum));
- nulls = (char *) palloc(cleanLength * sizeof(char));
- }
- else
- {
- values = values_array;
- nulls = nulls_array;
- }
- if (oldLength > PREALLOC_SIZE)
- {
- old_values = (Datum *) palloc(oldLength * sizeof(Datum));
- old_nulls = (char *) palloc(oldLength * sizeof(char));
- }
- else
- {
- old_values = old_values_array;
- old_nulls = old_nulls_array;
- }
-
- /*
- * Extract all the values of the old tuple, offsetting the arrays
- * so that old_values[0] is NULL and old_values[1] is the first
- * source attribute; this exactly matches the numbering convention
- * in cleanMap.
- */
- heap_deformtuple(tuple, tupType, old_values + 1, old_nulls + 1);
- old_values[0] = (Datum) 0;
- old_nulls[0] = 'n';
+ ExecClearTuple(resultSlot);
+ values = resultSlot->tts_values;
+ isnull = resultSlot->tts_isnull;
/*
- * Transpose into proper fields of the new tuple.
+ * Transpose data into proper fields of the new tuple.
*/
for (i = 0; i < cleanLength; i++)
{
int j = cleanMap[i];
- values[i] = old_values[j];
- nulls[i] = old_nulls[j];
+ if (j == 0)
+ {
+ values[i] = (Datum) 0;
+ isnull[i] = true;
+ }
+ else
+ {
+ values[i] = old_values[j - 1];
+ isnull[i] = old_isnull[j - 1];
+ }
}
/*
- * Now form the new tuple.
- */
- cleanTuple = heap_formtuple(cleanTupType, values, nulls);
-
- /*
- * We are done. Free any space allocated for 'values' and 'nulls' and
- * return the new tuple.
+ * And return the virtual tuple.
*/
- if (values != values_array)
- {
- pfree(values);
- pfree(nulls);
- }
- if (old_values != old_values_array)
- {
- pfree(old_values);
- pfree(old_nulls);
- }
+ return ExecStoreVirtualTuple(resultSlot);
+}
- return cleanTuple;
+/*
+ * ExecRemoveJunk
+ *
+ * Convenience routine to generate a physical clean tuple,
+ * rather than just a virtual slot.
+ */
+HeapTuple
+ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
+{
+ return ExecCopySlotTuple(ExecFilterJunk(junkfilter, slot));
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
if ((junkfilter = estate->es_junkFilter) != NULL)
{
Datum datum;
- HeapTuple newTuple;
bool isNull;
/*
* 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);
}
/*
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"
*
*/
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)++;
}
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
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
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;
}
}
TupleTableSlot *slot, EState *estate)
{
Relation rel = resultRelInfo->ri_RelationDesc;
- HeapTuple tuple = slot->val;
TupleConstr *constr = rel->rd_att->constr;
Assert(constr);
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",
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
if (attnum > 0)
{
- TupleDesc tuple_type = slot->ttc_tupleDescriptor;
+ TupleDesc tuple_type = slot->tts_tupleDescriptor;
/*
* This assert checks that the attnum is valid.
}
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);
{
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);
}
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;
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;
* 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.
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);
}
{
HeapTuple tuple;
Datum *values;
- char *nulls;
+ bool *isnull;
int natts;
ListCell *arg;
int i;
/* 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);
}
Datum tupDatum;
TupleDesc tupDesc;
Datum *values;
- char *nulls;
+ bool *isnull;
Datum save_datum;
bool save_isNull;
ListCell *l1,
/* 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;
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 */
{
ExprState *newval = (ExprState *) lfirst(l1);
AttrNumber fieldnum = lfirst_int(l2);
- bool eisnull;
Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
* 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);
}
/* 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;
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
*/
values[resind] = ExecEvalExpr(gstate->arg,
econtext,
- &isNull,
+ &isnull[resind],
&itemIsDone[resind]);
- nulls[resind] = isNull ? 'n' : ' ';
if (itemIsDone[resind] != ExprSingleResult)
{
*/
*isDone = ExprEndResult;
MemoryContextSwitchTo(oldContext);
- return NULL;
+ return false;
}
else
{
{
values[resind] = ExecEvalExpr(gstate->arg,
econtext,
- &isNull,
+ &isnull[resind],
&itemIsDone[resind]);
- nulls[resind] = isNull ? 'n' : ' ';
if (itemIsDone[resind] == ExprEndResult)
{
while (itemIsDone[resind] == ExprMultipleResult)
{
- (void) ExecEvalExpr(gstate->arg,
- econtext,
- &isNull,
- &itemIsDone[resind]);
+ values[resind] = ExecEvalExpr(gstate->arg,
+ econtext,
+ &isnull[resind],
+ &itemIsDone[resind]);
}
}
MemoryContextSwitchTo(oldContext);
- return NULL;
+ return false;
}
}
}
+ /* Report success */
+ MemoryContextSwitchTo(oldContext);
+
+ return true;
+}
+
+/*
+ * ExecVariableList
+ * Evaluates a simple-Variable-list projection.
+ *
+ * Results are stored into the passed values and isnull arrays.
+ */
+static void
+ExecVariableList(ProjectionInfo *projInfo,
+ Datum *values,
+ bool *isnull)
+{
+ ExprContext *econtext = projInfo->pi_exprContext;
+ int *varSlotOffsets = projInfo->pi_varSlotOffsets;
+ int *varNumbers = projInfo->pi_varNumbers;
+ int i;
+
/*
- * form the new result tuple (in the caller's memory context!)
+ * Force extraction of all input values that we need.
*/
- MemoryContextSwitchTo(oldContext);
+ if (projInfo->pi_lastInnerVar > 0)
+ slot_getsomeattrs(econtext->ecxt_innertuple,
+ projInfo->pi_lastInnerVar);
+ if (projInfo->pi_lastOuterVar > 0)
+ slot_getsomeattrs(econtext->ecxt_outertuple,
+ projInfo->pi_lastOuterVar);
+ if (projInfo->pi_lastScanVar > 0)
+ slot_getsomeattrs(econtext->ecxt_scantuple,
+ projInfo->pi_lastScanVar);
- return heap_formtuple(targettype, values, nulls);
+ /*
+ * Assign to result by direct extraction of fields from source
+ * slots ... a mite ugly, but fast ...
+ */
+ for (i = list_length(projInfo->pi_targetlist) - 1; i >= 0; i--)
+ {
+ char *slotptr = ((char *) econtext) + varSlotOffsets[i];
+ TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
+ int varNumber = varNumbers[i] - 1;
+
+ values[i] = varSlot->tts_values[varNumber];
+ isnull[i] = varSlot->tts_isnull[varNumber];
+ }
}
-/* ----------------------------------------------------------------
- * ExecProject
+/*
+ * ExecProject
*
* projects a tuple based on projection info and stores
- * it in the specified tuple table slot.
- *
- * Note: someday soon the executor can be extended to eliminate
- * redundant projections by storing pointers to datums
- * in the tuple table and then passing these around when
- * possible. this should make things much quicker.
- * -cim 6/3/91
- * ----------------------------------------------------------------
+ * it in the previously specified tuple table slot.
+ *
+ * Note: the result is always a virtual tuple; therefore it
+ * may reference the contents of the exprContext's scan tuples
+ * and/or temporary results constructed in the exprContext.
+ * If the caller wishes the result to be valid longer than that
+ * data will be valid, he must call ExecMaterializeSlot on the
+ * result slot.
*/
TupleTableSlot *
ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
{
TupleTableSlot *slot;
- TupleDesc tupType;
- HeapTuple newTuple;
/*
* sanity checks
*/
- if (projInfo == NULL)
- return NULL;
+ Assert(projInfo != NULL);
/*
* get the projection info we want
*/
slot = projInfo->pi_slot;
- tupType = slot->ttc_tupleDescriptor;
/*
- * form a new result tuple (if possible --- result can be NULL)
+ * Clear any former contents of the result slot. This makes it
+ * safe for us to use the slot's Datum/isnull arrays as workspace.
+ * (Also, we can return the slot as-is if we decide no rows can
+ * be projected.)
*/
- newTuple = ExecTargetList(projInfo->pi_targetlist,
- tupType,
- projInfo->pi_exprContext,
- projInfo->pi_tupValues,
- projInfo->pi_tupNulls,
- projInfo->pi_itemIsDone,
- isDone);
+ ExecClearTuple(slot);
/*
- * store the tuple in the projection slot and return the slot.
+ * form a new result tuple (if possible); if successful, mark the result
+ * slot as containing a valid virtual tuple
*/
- return ExecStoreTuple(newTuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* tuple has no buffer */
- true);
+ if (projInfo->pi_isVarList)
+ {
+ /* simple Var list: this always succeeds with one result row */
+ if (isDone)
+ *isDone = ExprSingleResult;
+ ExecVariableList(projInfo,
+ slot->tts_values,
+ slot->tts_isnull);
+ ExecStoreVirtualTuple(slot);
+ }
+ else
+ {
+ if (ExecTargetList(projInfo->pi_targetlist,
+ projInfo->pi_exprContext,
+ slot->tts_values,
+ slot->tts_isnull,
+ projInfo->pi_itemIsDone,
+ isDone))
+ ExecStoreVirtualTuple(slot);
+ }
+
+ return slot;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
if (TupIsNull(slot))
{
if (projInfo)
- return ExecStoreTuple(NULL,
- projInfo->pi_slot,
- InvalidBuffer,
- true);
+ return ExecClearTuple(projInfo->pi_slot);
else
return slot;
}
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
* 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"
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;
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);
}
}
}
/* --------------------------------
- * 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
* ----------------------------------------------------------------
*/
+/* --------------------------------
+ * 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
* 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 *
/*
* 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;
}
Assert(slot != NULL);
/*
- * Free the old contents of the specified slot if necessary. (Note:
- * we allow slot->val to be null even when shouldFree is true, because
- * there are a few callers of ExecStoreTuple that are too lazy to
- * distinguish whether they are passing a NULL tuple, and always pass
- * shouldFree = true.)
+ * Free the old physical tuple if necessary.
*/
- if (slot->ttc_shouldFree && slot->val != NULL)
- heap_freetuple(slot->val);
+ if (slot->tts_shouldFree)
+ heap_freetuple(slot->tts_tuple);
- slot->val = NULL;
- slot->ttc_shouldFree = false;
+ slot->tts_tuple = NULL;
+ slot->tts_shouldFree = false;
/*
* Drop the pin on the referenced buffer, if there is one.
*/
- if (BufferIsValid(slot->ttc_buffer))
- ReleaseBuffer(slot->ttc_buffer);
+ if (BufferIsValid(slot->tts_buffer))
+ ReleaseBuffer(slot->tts_buffer);
- slot->ttc_buffer = InvalidBuffer;
+ slot->tts_buffer = InvalidBuffer;
/*
- * mark slot_getattr state invalid
+ * Mark it empty.
*/
- slot->cache_natts = 0;
+ slot->tts_isempty = true;
+ slot->tts_nvalid = 0;
return slot;
}
/* --------------------------------
- * ExecSetSlotDescriptor
+ * ExecStoreVirtualTuple
+ * Mark a slot as containing a virtual tuple.
*
- * This function is used to set the tuple descriptor associated
- * with the slot's tuple.
+ * The protocol for loading a slot with virtual tuple data is:
+ * * Call ExecClearTuple to mark the slot empty.
+ * * Store data into the Datum/isnull arrays.
+ * * Call ExecStoreVirtualTuple to mark the slot valid.
+ * This is a bit unclean but it avoids one round of data copying.
* --------------------------------
*/
-void
-ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
- TupleDesc tupdesc, /* new tuple descriptor */
- bool shouldFree) /* is desc owned by slot? */
+TupleTableSlot *
+ExecStoreVirtualTuple(TupleTableSlot *slot)
{
- if (slot->ttc_shouldFreeDesc)
- FreeTupleDesc(slot->ttc_tupleDescriptor);
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tts_isempty);
- slot->ttc_tupleDescriptor = tupdesc;
- slot->ttc_shouldFreeDesc = shouldFree;
+ slot->tts_isempty = false;
+ slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
+ return slot;
+}
+
+/* --------------------------------
+ * ExecStoreAllNullTuple
+ * Set up the slot to contain a null in every column.
+ *
+ * At first glance this might sound just like ExecClearTuple, but it's
+ * entirely different: the slot ends up full, not empty.
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecStoreAllNullTuple(TupleTableSlot *slot)
+{
/*
- * mark slot_getattr state invalid
+ * sanity checks
*/
- slot->cache_natts = 0;
+ Assert(slot != NULL);
+ Assert(slot->tts_tupleDescriptor != NULL);
+
+ /* Clear any old contents */
+ ExecClearTuple(slot);
/*
- * release any old cache array since tupledesc's natts may have changed
+ * Fill all the columns of the virtual tuple with nulls
*/
- if (slot->cache_values)
- pfree(slot->cache_values);
- slot->cache_values = NULL;
+ MemSet(slot->tts_values, 0,
+ slot->tts_tupleDescriptor->natts * sizeof(Datum));
+ memset(slot->tts_isnull, true,
+ slot->tts_tupleDescriptor->natts * sizeof(bool));
+
+ return ExecStoreVirtualTuple(slot);
+}
+
+/* --------------------------------
+ * ExecCopySlotTuple
+ * Obtain a copy of a slot's physical tuple. The copy is
+ * palloc'd in the current memory context.
+ *
+ * This works even if the slot contains a virtual tuple;
+ * however the "system columns" of the result will not be meaningful.
+ * --------------------------------
+ */
+HeapTuple
+ExecCopySlotTuple(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ /*
+ * If we have a physical tuple then just copy it.
+ */
+ if (slot->tts_tuple)
+ return heap_copytuple(slot->tts_tuple);
+
+ /*
+ * Otherwise we need to build a tuple from the Datum array.
+ */
+ return heap_form_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+}
+
+/* --------------------------------
+ * ExecFetchSlotTuple
+ * Fetch the slot's physical tuple.
+ *
+ * If the slot contains a virtual tuple, we convert it to physical
+ * form. The slot retains ownership of the physical tuple.
+ *
+ * The difference between this and ExecMaterializeSlot() is that this
+ * does not guarantee that the contained tuple is local storage.
+ * Hence, the result must be treated as read-only.
+ * --------------------------------
+ */
+HeapTuple
+ExecFetchSlotTuple(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ /*
+ * If we have a physical tuple then just return it.
+ */
+ if (slot->tts_tuple)
+ return slot->tts_tuple;
+
+ /*
+ * Otherwise materialize the slot...
+ */
+ return ExecMaterializeSlot(slot);
+}
+
+/* --------------------------------
+ * ExecMaterializeSlot
+ * Force a slot into the "materialized" state.
+ *
+ * This causes the slot's tuple to be a local copy not dependent on
+ * any external storage. A pointer to the contained tuple is returned.
+ *
+ * A typical use for this operation is to prepare a computed tuple
+ * for being stored on disk. The original data may or may not be
+ * virtual, but in any case we need a private copy for heap_insert
+ * to scribble on.
+ * --------------------------------
+ */
+HeapTuple
+ExecMaterializeSlot(TupleTableSlot *slot)
+{
+ HeapTuple newTuple;
+ MemoryContext oldContext;
+
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ /*
+ * If we have a physical tuple, and it's locally palloc'd, we have
+ * nothing to do.
+ */
+ if (slot->tts_tuple && slot->tts_shouldFree)
+ return slot->tts_tuple;
+
+ /*
+ * Otherwise, copy or build a tuple, and then store it as the new slot
+ * value. (Note: tts_nvalid will be reset to zero here. There are
+ * cases in which this could be optimized but it's probably not worth
+ * worrying about.)
+ *
+ * We may be called in a context that is shorter-lived than the
+ * tuple slot, but we have to ensure that the materialized tuple
+ * will survive anyway.
+ */
+ oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ newTuple = ExecCopySlotTuple(slot);
+ MemoryContextSwitchTo(oldContext);
+
+ ExecStoreTuple(newTuple, slot, InvalidBuffer, true);
+
+ return slot->tts_tuple;
+}
+
+/* --------------------------------
+ * ExecCopySlot
+ * Copy the source slot's contents into the destination slot.
+ *
+ * The destination acquires a private copy that will not go away
+ * if the source is cleared.
+ *
+ * The caller must ensure the slots have compatible tupdescs.
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
+{
+ HeapTuple newTuple;
+ MemoryContext oldContext;
+
+ /*
+ * There might be ways to optimize this when the source is virtual,
+ * but for now just always build a physical copy. Make sure it is
+ * in the right context.
+ */
+ oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
+ newTuple = ExecCopySlotTuple(srcslot);
+ MemoryContextSwitchTo(oldContext);
+
+ return ExecStoreTuple(newTuple, dstslot, InvalidBuffer, true);
}
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);
}
/* ----------------------------------------------------------------
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;
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);
*
* 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)
/* 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);
}
/*
{
(*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);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
- return slot->ttc_tupleDescriptor;
+ return slot->tts_tupleDescriptor;
}
/* ----------------
{
ProjectionInfo *projInfo = makeNode(ProjectionInfo);
int len;
+ bool isVarList;
+ ListCell *tl;
len = ExecTargetListLength(targetList);
projInfo->pi_targetlist = targetList;
projInfo->pi_exprContext = econtext;
projInfo->pi_slot = slot;
- if (len > 0)
+
+ /*
+ * Determine whether the target list consists entirely of simple Var
+ * references (ie, references to non-system attributes). If so,
+ * we can use the simpler ExecVariableList instead of ExecTargetList.
+ */
+ isVarList = true;
+ foreach(tl, targetList)
+ {
+ GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+ Var *variable = (Var *) gstate->arg->expr;
+
+ if (variable == NULL ||
+ !IsA(variable, Var) ||
+ variable->varattno <= 0)
+ {
+ isVarList = false;
+ break;
+ }
+ }
+ projInfo->pi_isVarList = isVarList;
+
+ if (isVarList)
+ {
+ int *varSlotOffsets;
+ int *varNumbers;
+ AttrNumber lastInnerVar = 0;
+ AttrNumber lastOuterVar = 0;
+ AttrNumber lastScanVar = 0;
+
+ projInfo->pi_itemIsDone = NULL; /* not needed */
+ projInfo->pi_varSlotOffsets = varSlotOffsets = (int *)
+ palloc0(len * sizeof(int));
+ projInfo->pi_varNumbers = varNumbers = (int *)
+ palloc0(len * sizeof(int));
+
+ /*
+ * Set up the data needed by ExecVariableList. The slots in which
+ * the variables can be found at runtime are denoted by the offsets
+ * of their slot pointers within the econtext. This rather grotty
+ * representation is needed because the caller may not have given
+ * us the real econtext yet (see hacks in nodeSubplan.c).
+ */
+ foreach(tl, targetList)
+ {
+ GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+ Var *variable = (Var *) gstate->arg->expr;
+ AttrNumber attnum = variable->varattno;
+ TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+ AttrNumber resind = tle->resdom->resno - 1;
+
+ Assert(resind >= 0 && resind < len);
+ varNumbers[resind] = attnum;
+
+ switch (variable->varno)
+ {
+ case INNER:
+ varSlotOffsets[resind] = offsetof(ExprContext,
+ ecxt_innertuple);
+ lastInnerVar = Max(lastInnerVar, attnum);
+ break;
+
+ case OUTER:
+ varSlotOffsets[resind] = offsetof(ExprContext,
+ ecxt_outertuple);
+ lastOuterVar = Max(lastOuterVar, attnum);
+ break;
+
+ default:
+ varSlotOffsets[resind] = offsetof(ExprContext,
+ ecxt_scantuple);
+ lastScanVar = Max(lastScanVar, attnum);
+ break;
+ }
+ }
+ projInfo->pi_lastInnerVar = lastInnerVar;
+ projInfo->pi_lastOuterVar = lastOuterVar;
+ projInfo->pi_lastScanVar = lastScanVar;
+ }
+ else
{
- projInfo->pi_tupValues = (Datum *) palloc(len * sizeof(Datum));
- projInfo->pi_tupNulls = (char *) palloc(len * sizeof(char));
- projInfo->pi_itemIsDone = (ExprDoneCond *) palloc(len * sizeof(ExprDoneCond));
+ projInfo->pi_itemIsDone = (ExprDoneCond *)
+ palloc(len * sizeof(ExprDoneCond));
+ projInfo->pi_varSlotOffsets = NULL;
+ projInfo->pi_varNumbers = NULL;
}
return projInfo;
{
TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
- return slot->ttc_tupleDescriptor;
+ return slot->tts_tupleDescriptor;
}
/* ----------------
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.
*/
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
/*
* 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);
* 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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
SQLFunctionCachePtr fcache)
{
TupleTableSlot *slot;
- HeapTuple tup;
- TupleDesc tupDesc;
Datum value;
if (es->status == F_EXEC_START)
/*
* 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.
*/
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);
* 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);
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
{
*/
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))
/*
* Save the first input tuple of the next group.
*/
- aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
+ aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
break;
}
}
*/
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);
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
}
/* ----------------------------------------------------------------
* 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 $
*
*-------------------------------------------------------------------------
*/
ExecGroup(GroupState *node)
{
ExprContext *econtext;
- TupleDesc tupdesc;
int numCols;
AttrNumber *grpColIdx;
- HeapTuple outerTuple;
- HeapTuple firsttuple;
+ TupleTableSlot *firsttupleslot;
TupleTableSlot *outerslot;
/*
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.
* 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))
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.
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))
* 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.
grpstate = makeNode(GroupState);
grpstate->ss.ps.plan = (Plan *) node;
grpstate->ss.ps.state = estate;
- grpstate->grp_firstTuple = NULL;
grpstate->grp_done = FALSE;
/*
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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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);
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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 */
* 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);
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
ExecLimit(LimitState *node)
{
ScanDirection direction;
- TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
PlanState *outerPlan;
*/
direction = node->ps.state->es_direction;
outerPlan = outerPlanState(node);
- resultTupleSlot = node->ps.ps_ResultTupleSlot;
/*
* The main logic is a simple state machine.
/* Return the current tuple */
Assert(!TupIsNull(slot));
- ExecStoreTuple(slot->val,
- resultTupleSlot,
- InvalidBuffer,
- false); /* tuple does not belong to slot */
-
- return resultTupleSlot;
+ return slot;
}
/*
#define LIMIT_NSLOTS 1
/*
- * Tuple table initialization
+ * Tuple table initialization (XXX not actually used...)
*/
ExecInitResultTupleSlot(estate, &limitstate->ps);
ExecEndLimit(LimitState *node)
{
ExecFreeExprContext(&node->ps);
-
- /* clean up tuple table */
- ExecClearTuple(node->ps.ps_ResultTupleSlot);
-
ExecEndNode(outerPlanState(node));
}
/* 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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
node->eof_underlying = true;
return NULL;
}
- heapTuple = outerslot->val;
+ heapTuple = ExecFetchSlotTuple(outerslot);
should_free = false;
/*
}
/*
- * 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);
}
/* ----------------------------------------------------------------
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#define MarkInnerTuple(innerTupleSlot, mergestate) \
( \
- ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \
- (mergestate)->mj_MarkedTupleSlot, \
- InvalidBuffer, \
- true) \
+ ExecCopySlot((mergestate)->mj_MarkedTupleSlot, \
+ (innerTupleSlot)) \
)
if (TupIsNull(outerSlot))
printf("(nil)\n");
else
- MJ_debugtup(outerSlot->val,
- outerSlot->ttc_tupleDescriptor);
+ MJ_debugtup(outerSlot);
}
static void
if (TupIsNull(innerSlot))
printf("(nil)\n");
else
- MJ_debugtup(innerSlot->val,
- innerSlot->ttc_tupleDescriptor);
+ MJ_debugtup(innerSlot);
}
static void
if (TupIsNull(markedSlot))
printf("(nil)\n");
else
- MJ_debugtup(markedSlot->val,
- markedSlot->ttc_tupleDescriptor);
+ MJ_debugtup(markedSlot);
}
static void
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*/
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;
* 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))
int flag;
bool isNull;
- flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
+ flag = DatumGetInt32(slot_getattr(inputTupleSlot,
plannode->flagColIdx,
- tupDesc,
&isNull));
Assert(!isNull);
if (flag)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
if (TupIsNull(slot))
break;
- tuplesort_puttuple(tuplesortstate, (void *) slot->val);
+ tuplesort_puttuple(tuplesortstate,
+ (void *) ExecFetchSlotTuple(slot));
}
/*
&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);
}
/* ----------------------------------------------------------------
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
/* ----------------------------------------------------------------
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)
*/
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
* 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)
ExecClearTuple(slot);
return BoolGetDatum(false);
}
- if (tupleAllNulls(tup))
+ if (slotAllNulls(slot))
{
ExecClearTuple(slot);
*isNull = true;
!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;
* 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;
}
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);
*/
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));
/*
!TupIsNull(slot);
slot = ExecProcNode(planstate))
{
- HeapTuple tup = slot->val;
- TupleDesc tdesc = slot->ttc_tupleDescriptor;
int col = 1;
ListCell *plst;
bool isnew;
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;
* 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);
{
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))
}
/*
- * tupleAllNulls: is the tuple completely NULL?
+ * slotAllNulls: is the slot completely NULL?
+ *
+ * This does not test for dropped columns, which is OK because we only
+ * use it on projected tuples.
+ */
+static bool
+slotAllNulls(TupleTableSlot *slot)
+{
+ int ncols = slot->tts_tupleDescriptor->natts;
+ int i;
+
+ for (i = 1; i <= ncols; i++)
+ {
+ if (!slot_attisnull(slot, i))
+ return false;
+ }
+ return true;
+}
+
+/*
+ * slotNoNulls: is the slot entirely not NULL?
+ *
+ * This does not test for dropped columns, which is OK because we only
+ * use it on projected tuples.
*/
static bool
-tupleAllNulls(HeapTuple tuple)
+slotNoNulls(TupleTableSlot *slot)
{
- int ncols = tuple->t_data->t_natts;
+ int ncols = slot->tts_tupleDescriptor->natts;
int i;
for (i = 1; i <= ncols; i++)
{
- if (!heap_attisnull(tuple, i))
+ if (slot_attisnull(slot, i))
return false;
}
return true;
!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)
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);
* 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);
/*
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++;
}
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*
* 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 (;;)
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;
/*
* 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))
* 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);
}
/* ----------------------------------------------------------------
uniquestate->ps.plan = (Plan *) node;
uniquestate->ps.state = estate;
- uniquestate->priorTuple = NULL;
-
/*
* Miscellaneous initialization
*
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* of current SPI procedure
*/
void
-spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
+spi_printtup(TupleTableSlot *slot, DestReceiver *self)
{
SPITupleTable *tuptable;
MemoryContext oldcxt;
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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);
}
{
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;
*
*
* 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
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 *
* 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 $
*
*-------------------------------------------------------------------------
*/
* ----------------
*/
static void
-donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
+donothingReceive(TupleTableSlot *slot, DestReceiver *self)
{
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
DestReceiver *dest)
{
long current_tuple_count = 0;
+ TupleTableSlot *slot;
+
+ slot = MakeSingleTupleTableSlot(portal->tupDesc);
(*dest->rStartup) (dest, CMD_SELECT, portal->tupDesc);
if (tup == NULL)
break;
- (*dest->receiveTuple) (tup, portal->tupDesc, dest);
+ ExecStoreTuple(tup, slot, InvalidBuffer, should_free);
+
+ (*dest->receiveSlot) (slot, dest);
- if (should_free)
- pfree(tup);
+ ExecClearTuple(slot);
/*
* check our tuple count.. if we've processed the proper
(*dest->rShutdown) (dest);
+ ExecDropSingleTupleTableSlot(slot);
+
return (uint32) current_tuple_count;
}
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
extern IndexInfo *BuildIndexInfo(Relation index);
extern void FormIndexDatum(IndexInfo *indexInfo,
- HeapTuple heapTuple,
- TupleDesc heapDescriptor,
+ TupleTableSlot *slot,
EState *estate,
Datum *datum,
char *nullv);
* 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 $
*
*-------------------------------------------------------------------------
*/
#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", \
#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)
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
/*
* 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);
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);
/*
* 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);
{
/* use "struct" here to allow forward reference */
struct AttInMetadata *metadata;
+ TupleTableSlot *slot;
DestReceiver *dest;
} TupOutputState;
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
+ *
+ * The first two cases are similar in that they both deal with "materialized"
+ * tuples, but resource management is different. For a tuple in a disk page
+ * we need to hold a pin on the buffer until the TupleTableSlot's reference
+ * to the tuple is dropped; while for a palloc'd tuple we usually want the
+ * tuple pfree'd when the TupleTableSlot's reference is dropped.
+ *
+ * A "virtual" tuple is an optimization used to minimize physical data
+ * copying in a nest of plan nodes. Any pass-by-reference Datums in the
+ * tuple point to storage that is not directly associated with the
+ * TupleTableSlot; generally they will point to part of a tuple stored in
+ * a lower plan node's output TupleTableSlot, or to a function result
+ * constructed in a plan node's per-tuple econtext. It is the responsibility
+ * of the generating plan node to be sure these resources are not released
+ * for as long as the virtual tuple needs to be valid. We only use virtual
+ * tuples in the result slots of plan nodes --- tuples to be copied anywhere
+ * else need to be "materialized" into physical tuples. Note also that a
+ * virtual tuple does not have any "system columns".
+ *
+ * The Datum/isnull arrays of a TupleTableSlot serve double duty. When the
+ * slot contains a virtual tuple, they are the authoritative data. When the
+ * slot contains a physical tuple, the arrays contain data extracted from
+ * the tuple. (In this state, any pass-by-reference Datums point into
+ * the physical tuple.) The extracted information is built "lazily",
+ * ie, only as needed. This serves to avoid repeated extraction of data
+ * from the physical tuple.
+ *
+ * A TupleTableSlot can also be "empty", holding no valid data. This is
+ * the only valid state for a freshly-created slot that has not yet had a
+ * tuple descriptor assigned to it. In this state, tts_isempty must be
+ * TRUE, tts_shouldFree FALSE, tts_tuple NULL, tts_buffer InvalidBuffer,
+ * and tts_nvalid zero.
+ *
+ * When tts_shouldFree is true, the physical tuple is "owned" by the slot
+ * and should be freed when the slot's reference to the tuple is dropped.
*
- * shouldFreeDesc is similar to shouldFree: if it's true, then the
+ * tts_shouldFreeDesc is similar to tts_shouldFree: if it's true, then the
* tupleDescriptor is "owned" by the TupleTableSlot and should be
* 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;
/*
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
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;
/* ----------------
* 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
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;
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;
/* ----------------
{
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;
* 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.
* ----------------
{
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;
* 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.
*
* 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 */
* 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;
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,