aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/common/heaptuple.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-03-16 21:38:10 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-03-16 21:38:10 +0000
commitf97aebd162987d00bd9b9f592ff54e9e90f11843 (patch)
tree78c6ff493070f92e7fda262a2c25e2545f0d2b21 /src/backend/access/common/heaptuple.c
parent712f053587b552769d1d3f6fe0ec03ab79c05d26 (diff)
downloadpostgresql-f97aebd162987d00bd9b9f592ff54e9e90f11843.tar.gz
postgresql-f97aebd162987d00bd9b9f592ff54e9e90f11843.zip
Revise TupleTableSlot code to avoid unnecessary construction and disassembly
of tuples when passing data up through multiple plan nodes. A slot can now hold either a normal "physical" HeapTuple, or a "virtual" tuple consisting of Datum/isnull arrays. Upper plan levels can usually just copy the Datum arrays, avoiding heap_formtuple() and possible subsequent nocachegetattr() calls to extract the data again. This work extends Atsushi Ogawa's earlier patch, which provided the key idea of adding Datum arrays to TupleTableSlots. (I believe however that something like this was foreseen way back in Berkeley days --- see the old comment on ExecProject.) A test case involving many levels of join of fairly wide tables (about 80 columns altogether) showed about 3x overall speedup, though simple queries will probably not be helped very much. I have also duplicated some code in heaptuple.c in order to provide versions of heap_formtuple and friends that use "bool" arrays to indicate null attributes, instead of the old convention of "char" arrays containing either 'n' or ' '. This provides a better match to the convention used by ExecEvalExpr. While I have not made a concerted effort to get rid of uses of the old routines, I think they should be deprecated and eventually removed.
Diffstat (limited to 'src/backend/access/common/heaptuple.c')
-rw-r--r--src/backend/access/common/heaptuple.c689
1 files changed, 619 insertions, 70 deletions
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 22d5e44dc3f..88df2231795 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];
+ }
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ {
+ *isnull = true;
+ return (Datum) 0;
+ }
- if (attnum > tup->t_natts || attnum > tupleDesc->natts)
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should
+ * equal natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * return NULL if attnum is out of range according to the tuple
+ *
+ * (We have to check this separately because of various inheritance
+ * and table-alteration scenarios: the tuple could be either longer
+ * or shorter than the tupdesc.)
+ */
+ tup = tuple->t_data;
+ if (attnum > tup->t_natts)
{
*isnull = true;
return (Datum) 0;
}
/*
- * check if target attribute is null
+ * check if target attribute is null: no point in groveling through tuple
*/
if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
{
@@ -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.
+ */
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * The result is acquired from tts_values array.
+ */
+ *isnull = slot->tts_isnull[attnum - 1];
+ return slot->tts_values[attnum - 1];
+}
+
+/*
+ * slot_getallattrs
+ * This function forces all the entries of the slot's Datum/isnull
+ * arrays to be valid. The caller may then extract data directly
+ * from those arrays instead of using slot_getattr.
+ */
+void
+slot_getallattrs(TupleTableSlot *slot)
+{
+ int tdesc_natts = slot->tts_tupleDescriptor->natts;
+ int attnum;
+ HeapTuple tuple;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid == tdesc_natts)
+ return;
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should
+ * equal natts in all virtual-tuple cases)
+ */
+ tuple = slot->tts_tuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
+ */
+ attnum = tuple->t_data->t_natts;
+ attnum = Min(attnum, tdesc_natts);
+
+ slot_deform_tuple(slot, attnum);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attnum < tdesc_natts; attnum++)
+ {
+ slot->tts_values[attnum] = (Datum) 0;
+ slot->tts_isnull[attnum] = true;
+ }
+ slot->tts_nvalid = tdesc_natts;
+}
+
+/*
+ * slot_getsomeattrs
+ * This function forces the entries of the slot's Datum/isnull
+ * arrays to be valid at least up through the attnum'th entry.
+ */
+void
+slot_getsomeattrs(TupleTableSlot *slot, int attnum)
+{
+ HeapTuple tuple;
+ int attno;
+
+ /* Quick out if we have 'em all already */
+ if (slot->tts_nvalid >= attnum)
+ return;
+
+ /* Check for caller error */
+ if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
+ elog(ERROR, "invalid attribute number %d", attnum);
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should
+ * equal natts in all virtual-tuple cases)
+ */
+ tuple = slot->tts_tuple;
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /*
+ * load up any slots available from physical tuple
*/
- if (attnum > slot->cache_natts)
+ attno = tuple->t_data->t_natts;
+ attno = Min(attno, attnum);
+
+ slot_deform_tuple(slot, attno);
+
+ /*
+ * If tuple doesn't have all the atts indicated by tupleDesc, read the
+ * rest as null
+ */
+ for (; attno < attnum; attno++)
{
- /*
- * If first time for this TupleTableSlot, allocate the cache
- * workspace. It must have the same lifetime as the slot, so allocate
- * it in the slot's own context. We size the array according to what
- * the tupdesc says, NOT the tuple.
- */
- if (slot->cache_values == NULL)
- slot->cache_values = (Datum *)
- MemoryContextAlloc(slot->ttc_mcxt,
- tupleDesc->natts * sizeof(Datum));
+ slot->tts_values[attno] = (Datum) 0;
+ slot->tts_isnull[attno] = true;
+ }
+ slot->tts_nvalid = attnum;
+}
+
+/*
+ * slot_attisnull
+ * Detect whether an attribute of the slot is null, without
+ * actually fetching it.
+ */
+bool
+slot_attisnull(TupleTableSlot *slot, int attnum)
+{
+ HeapTuple tuple = slot->tts_tuple;
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- slot_deformtuple(slot, attnum);
+ /*
+ * system attributes are handled by heap_attisnull
+ */
+ if (attnum <= 0)
+ {
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract system attribute from virtual tuple");
+ return heap_attisnull(tuple, attnum);
}
/*
- * The result is acquired from cache_values array.
+ * fast path if desired attribute already cached
*/
- *isnull = false;
- return slot->cache_values[attnum - 1];
+ if (attnum <= slot->tts_nvalid)
+ return slot->tts_isnull[attnum - 1];
+
+ /*
+ * return NULL if attnum is out of range according to the tupdesc
+ */
+ if (attnum > tupleDesc->natts)
+ return true;
+
+ /*
+ * otherwise we had better have a physical tuple (tts_nvalid should
+ * equal natts in all virtual-tuple cases)
+ */
+ if (tuple == NULL) /* internal error */
+ elog(ERROR, "cannot extract attribute from empty tuple slot");
+
+ /* and let the tuple tell it */
+ return heap_attisnull(tuple, attnum);
}
/* ----------------