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