diff options
Diffstat (limited to 'src/backend/access')
-rw-r--r-- | src/backend/access/common/heaptuple.c | 689 | ||||
-rw-r--r-- | src/backend/access/common/printtup.c | 83 |
2 files changed, 649 insertions, 123 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); } /* ---------------- diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 3ce44009b91..6f44533822b 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); |