/*------------------------------------------------------------------------- * * heaptuple.c * This file contains heap tuple accessor and mutator routines, as well * as various tuple utilities. * * Some notes about varlenas and this code: * * Before Postgres 8.3 varlenas always had a 4-byte length header, and * therefore always needed 4-byte alignment (at least). This wasted space * for short varlenas, for example CHAR(1) took 5 bytes and could need up to * 3 additional padding bytes for alignment. * * Now, a short varlena (up to 126 data bytes) is reduced to a 1-byte header * and we don't align it. To hide this from datatype-specific functions that * don't want to deal with it, such a datum is considered "toasted" and will * be expanded back to the normal 4-byte-header format by pg_detoast_datum. * (In performance-critical code paths we can use pg_detoast_datum_packed * and the appropriate access macros to avoid that overhead.) Note that this * conversion is performed directly in heap_form_tuple, without invoking * heaptoast.c. * * This change will break any code that assumes it needn't detoast values * that have been put into a tuple but never sent to disk. Hopefully there * are few such places. * * Varlenas still have alignment INT (or DOUBLE) in pg_type/pg_attribute, since * that's the normal requirement for the untoasted format. But we ignore that * for the 1-byte-header format. This means that the actual start position * of a varlena datum may vary depending on which format it has. To determine * what is stored, we have to require that alignment padding bytes be zero. * (Postgres actually has always zeroed them, but now it's required!) Since * the first byte of a 1-byte-header varlena can never be zero, we can examine * the first byte after the previous datum to tell if it's a pad byte or the * start of a 1-byte-header varlena. * * Note that while formerly we could rely on the first varlena column of a * system catalog to be at the offset suggested by the C struct for the * catalog, this is now risky: it's only safe if the preceding field is * word-aligned, so that there will never be any padding. * * We don't pack varlenas whose attstorage is PLAIN, since the data type * isn't expecting to have to detoast values. This is used in particular * by oidvector and int2vector, which are used in the system catalogs * and we'd like to still refer to them via C struct offsets. * * * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/access/common/heaptuple.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heaptoast.h" #include "access/sysattr.h" #include "access/tupdesc_details.h" #include "common/hashfn.h" #include "executor/tuptable.h" #include "utils/datum.h" #include "utils/expandeddatum.h" #include "utils/hsearch.h" #include "utils/memutils.h" /* * Does att's datatype allow packing into the 1-byte-header varlena format? * While functions that use TupleDescAttr() and assign attstorage = * TYPSTORAGE_PLAIN cannot use packed varlena headers, functions that call * TupleDescInitEntry() use typeForm->typstorage (TYPSTORAGE_EXTENDED) and * can use packed varlena headers, e.g.: * CREATE TABLE test(a VARCHAR(10000) STORAGE PLAIN); * INSERT INTO test VALUES (repeat('A',10)); * This can be verified with pageinspect. */ #define ATT_IS_PACKABLE(att) \ ((att)->attlen == -1 && (att)->attstorage != TYPSTORAGE_PLAIN) /* Use this if it's already known varlena */ #define VARLENA_ATT_IS_PACKABLE(att) \ ((att)->attstorage != TYPSTORAGE_PLAIN) /* * Setup for cacheing pass-by-ref missing attributes in a way that survives * tupleDesc destruction. */ typedef struct { int len; Datum value; } missing_cache_key; static HTAB *missing_cache = NULL; static uint32 missing_hash(const void *key, Size keysize) { const missing_cache_key *entry = (missing_cache_key *) key; return hash_bytes((const unsigned char *) entry->value, entry->len); } static int missing_match(const void *key1, const void *key2, Size keysize) { const missing_cache_key *entry1 = (missing_cache_key *) key1; const missing_cache_key *entry2 = (missing_cache_key *) key2; if (entry1->len != entry2->len) return entry1->len > entry2->len ? 1 : -1; return memcmp(DatumGetPointer(entry1->value), DatumGetPointer(entry2->value), entry1->len); } static void init_missing_cache() { HASHCTL hash_ctl; hash_ctl.keysize = sizeof(missing_cache_key); hash_ctl.entrysize = sizeof(missing_cache_key); hash_ctl.hcxt = TopMemoryContext; hash_ctl.hash = missing_hash; hash_ctl.match = missing_match; missing_cache = hash_create("Missing Values Cache", 32, &hash_ctl, HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE); } /* ---------------------------------------------------------------- * misc support routines * ---------------------------------------------------------------- */ /* * Return the missing value of an attribute, or NULL if there isn't one. */ Datum getmissingattr(TupleDesc tupleDesc, int attnum, bool *isnull) { Form_pg_attribute att; Assert(attnum <= tupleDesc->natts); Assert(attnum > 0); att = TupleDescAttr(tupleDesc, attnum - 1); if (att->atthasmissing) { AttrMissing *attrmiss; Assert(tupleDesc->constr); Assert(tupleDesc->constr->missing); attrmiss = tupleDesc->constr->missing + (attnum - 1); if (attrmiss->am_present) { missing_cache_key key; missing_cache_key *entry; bool found; MemoryContext oldctx; *isnull = false; /* no need to cache by-value attributes */ if (att->attbyval) return attrmiss->am_value; /* set up cache if required */ if (missing_cache == NULL) init_missing_cache(); /* check if there's a cache entry */ Assert(att->attlen > 0 || att->attlen == -1); if (att->attlen > 0) key.len = att->attlen; else key.len = VARSIZE_ANY(attrmiss->am_value); key.value = attrmiss->am_value; entry = hash_search(missing_cache, &key, HASH_ENTER, &found); if (!found) { /* cache miss, so we need a non-transient copy of the datum */ oldctx = MemoryContextSwitchTo(TopMemoryContext); entry->value = datumCopy(attrmiss->am_value, false, att->attlen); MemoryContextSwitchTo(oldctx); } return entry->value; } } *isnull = true; return PointerGetDatum(NULL); } /* * 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; for (i = 0; i < numberOfAttributes; i++) { Datum val; Form_pg_attribute atti; if (isnull[i]) continue; val = values[i]; atti = TupleDescAttr(tupleDesc, i); if (ATT_IS_PACKABLE(atti) && VARATT_CAN_MAKE_SHORT(DatumGetPointer(val))) { /* * we're anticipating converting to a short varlena header, so * adjust length and don't count any alignment */ data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val)); } else if (atti->attlen == -1 && VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val))) { /* * we want to flatten the expanded value so that the constructed * tuple doesn't depend on it */ data_length = att_align_nominal(data_length, atti->attalign); data_length += EOH_get_flat_size(DatumGetEOHP(val)); } else { data_length = att_align_datum(data_length, atti->attalign, atti->attlen, val); data_length = att_addlength_datum(data_length, atti->attlen, val); } } return data_length; } /* * Per-attribute helper for heap_fill_tuple and other routines building tuples. * * Fill in either a data value or a bit in the null bitmask */ static inline void fill_val(Form_pg_attribute att, bits8 **bit, int *bitmask, char **dataP, uint16 *infomask, Datum datum, bool isnull) { Size data_length; char *data = *dataP; /* * If we're building a null bitmap, set the appropriate bit for the * current column value here. */ if (bit != NULL) { if (*bitmask != HIGHBIT) *bitmask <<= 1; else { *bit += 1; **bit = 0x0; *bitmask = 1; } if (isnull) { *infomask |= HEAP_HASNULL; return; } **bit |= *bitmask; } /* * XXX we use the att_align macros on the pointer value itself, not on an * offset. This is a bit of a hack. */ if (att->attbyval) { /* pass-by-value */ data = (char *) att_align_nominal(data, att->attalign); store_att_byval(data, datum, att->attlen); data_length = att->attlen; } else if (att->attlen == -1) { /* varlena */ Pointer val = DatumGetPointer(datum); *infomask |= HEAP_HASVARWIDTH; if (VARATT_IS_EXTERNAL(val)) { if (VARATT_IS_EXTERNAL_EXPANDED(val)) { /* * we want to flatten the expanded value so that the * constructed tuple doesn't depend on it */ ExpandedObjectHeader *eoh = DatumGetEOHP(datum); data = (char *) att_align_nominal(data, att->attalign); data_length = EOH_get_flat_size(eoh); EOH_flatten_into(eoh, data, data_length); } else { *infomask |= HEAP_HASEXTERNAL; /* no alignment, since it's short by definition */ data_length = VARSIZE_EXTERNAL(val); memcpy(data, val, data_length); } } else if (VARATT_IS_SHORT(val)) { /* no alignment for short varlenas */ data_length = VARSIZE_SHORT(val); memcpy(data, val, data_length); } else if (VARLENA_ATT_IS_PACKABLE(att) && VARATT_CAN_MAKE_SHORT(val)) { /* convert to short varlena -- no alignment */ data_length = VARATT_CONVERTED_SHORT_SIZE(val); SET_VARSIZE_SHORT(data, data_length); memcpy(data + 1, VARDATA(val), data_length - 1); } else { /* full 4-byte header varlena */ data = (char *) att_align_nominal(data, att->attalign); data_length = VARSIZE(val); memcpy(data, val, data_length); } } else if (att->attlen == -2) { /* cstring ... never needs alignment */ *infomask |= HEAP_HASVARWIDTH; Assert(att->attalign == TYPALIGN_CHAR); data_length = strlen(DatumGetCString(datum)) + 1; memcpy(data, DatumGetPointer(datum), data_length); } else { /* fixed-length pass-by-reference */ data = (char *) att_align_nominal(data, att->attalign); Assert(att->attlen > 0); data_length = att->attlen; memcpy(data, DatumGetPointer(datum), data_length); } data += data_length; *dataP = data; } /* * 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. * * NOTE: it is now REQUIRED that the caller have pre-zeroed the data area. */ void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit) { bits8 *bitP; int bitmask; int i; int numberOfAttributes = tupleDesc->natts; #ifdef USE_ASSERT_CHECKING char *start = data; #endif if (bit != NULL) { bitP = &bit[-1]; bitmask = HIGHBIT; } else { /* just to keep compiler quiet */ bitP = NULL; bitmask = 0; } *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL); for (i = 0; i < numberOfAttributes; i++) { Form_pg_attribute attr = TupleDescAttr(tupleDesc, i); fill_val(attr, bitP ? &bitP : NULL, &bitmask, &data, infomask, values ? values[i] : PointerGetDatum(NULL), isnull ? isnull[i] : true); } Assert((data - start) == data_size); } /* ---------------------------------------------------------------- * heap tuple interface * ---------------------------------------------------------------- */ /* ---------------- * heap_attisnull - returns true iff tuple attribute is not present * ---------------- */ bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc) { /* * We allow a NULL tupledesc for relations not expected to have missing * values, such as catalog relations and indexes. */ Assert(!tupleDesc || attnum <= tupleDesc->natts); if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data)) { if (tupleDesc && TupleDescAttr(tupleDesc, attnum - 1)->atthasmissing) return false; else return true; } if (attnum > 0) { if (HeapTupleNoNulls(tup)) return false; return att_isnull(attnum - 1, tup->t_data->t_bits); } switch (attnum) { case TableOidAttributeNumber: case SelfItemPointerAttributeNumber: case MinTransactionIdAttributeNumber: case MinCommandIdAttributeNumber: case MaxTransactionIdAttributeNumber: case MaxCommandIdAttributeNumber: /* these are never null */ break; default: elog(ERROR, "invalid attnum: %d", attnum); } return false; } /* ---------------- * nocachegetattr * * This only gets called from fastgetattr() macro, in cases where * we can't use a cacheoffset and the value is not null. * * This caches attribute offsets in the attribute descriptor. * * An alternative way to speed things up would be to cache offsets * with the tuple, but that seems more difficult unless you take * the storage hit of actually putting those offsets into the * tuple you send to disk. Yuck. * * This scheme will be slightly slower than that, but should * perform well for queries which hit large #'s of tuples. After * 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_deform_tuple. * Also see nocache_index_getattr, which is the same code for index * tuples. * ---------------- */ Datum nocachegetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc) { HeapTupleHeader td = tup->t_data; char *tp; /* ptr to data part of tuple */ bits8 *bp = td->t_bits; /* ptr to null bitmap in tuple */ bool slow = false; /* do we have to walk attrs? */ int off; /* current offset within data */ /* ---------------- * Three cases: * * 1: No nulls and no variable-width attributes. * 2: Has a null or a var-width AFTER att. * 3: Has nulls or var-widths BEFORE att. * ---------------- */ attnum--; if (!HeapTupleNoNulls(tup)) { /* * there's a null somewhere in the tuple * * check to see if any preceding bits are null... */ int byte = attnum >> 3; int finalbit = attnum & 0x07; /* check for nulls "before" final bit of last byte */ if ((~bp[byte]) & ((1 << finalbit) - 1)) slow = true; else { /* check for nulls in any "earlier" bytes */ int i; for (i = 0; i < byte; i++) { if (bp[i] != 0xFF) { slow = true; break; } } } } tp = (char *) td + td->t_hoff; if (!slow) { Form_pg_attribute att; /* * If we get here, there are no nulls up to and including the target * attribute. If we have a cached offset, we can use it. */ att = TupleDescAttr(tupleDesc, attnum); if (att->attcacheoff >= 0) return fetchatt(att, tp + att->attcacheoff); /* * Otherwise, check for non-fixed-length attrs up to and including * target. If there aren't any, it's safe to cheaply initialize the * cached offsets for these attrs. */ if (HeapTupleHasVarWidth(tup)) { int j; for (j = 0; j <= attnum; j++) { if (TupleDescAttr(tupleDesc, j)->attlen <= 0) { slow = true; break; } } } } if (!slow) { int natts = tupleDesc->natts; int j = 1; /* * If we get here, we have a tuple with no nulls or var-widths up to * and including the target attribute, so we can use the cached offset * ... only we don't have it yet, or we'd not have got here. Since * it's cheap to compute offsets for fixed-width columns, we take the * opportunity to initialize the cached offsets for *all* the leading * fixed-width columns, in hope of avoiding future visits to this * routine. */ TupleDescAttr(tupleDesc, 0)->attcacheoff = 0; /* we might have set some offsets in the slow path previously */ while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0) j++; off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff + TupleDescAttr(tupleDesc, j - 1)->attlen; for (; j < natts; j++) { Form_pg_attribute att = TupleDescAttr(tupleDesc, j); if (att->attlen <= 0) break; off = att_align_nominal(off, att->attalign); att->attcacheoff = off; off += att->attlen; } Assert(j > attnum); off = TupleDescAttr(tupleDesc, attnum)->attcacheoff; } else { bool usecache = true; int i; /* * Now we know that we have to walk the tuple CAREFULLY. But we still * might be able to cache some offsets for next time. * * 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 reach either a null or a var-width attribute. */ off = 0; for (i = 0;; i++) /* loop exit is at "break" */ { Form_pg_attribute att = TupleDescAttr(tupleDesc, i); if (HeapTupleHasNulls(tup) && att_isnull(i, bp)) { usecache = false; continue; /* this cannot be the target att */ } /* If we know the next offset, we can skip the rest */ if (usecache && att->attcacheoff >= 0) off = att->attcacheoff; else if (att->attlen == -1) { /* * We can only cache the offset for a varlena attribute if the * offset is already suitably aligned, so that there would be * no pad bytes in any case: then the offset will be valid for * either an aligned or unaligned value. */ if (usecache && off == att_align_nominal(off, att->attalign)) att->attcacheoff = off; else { off = att_align_pointer(off, att->attalign, -1, tp + off); usecache = false; } } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, att->attalign); if (usecache) att->attcacheoff = off; } if (i == attnum) break; off = att_addlength_pointer(off, att->attlen, tp + off); if (usecache && att->attlen <= 0) usecache = false; } } return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off); } /* ---------------- * heap_getsysattr * * Fetch the value of a system attribute for a tuple. * * This is a support routine for the heap_getattr macro. The macro * has already determined that the attnum refers to a system attribute. * ---------------- */ Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) { Datum result; Assert(tup); /* Currently, no sys attribute ever reads as NULL. */ *isnull = false; switch (attnum) { case SelfItemPointerAttributeNumber: /* pass-by-reference datatype */ result = PointerGetDatum(&(tup->t_self)); break; case MinTransactionIdAttributeNumber: result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmin(tup->t_data)); break; case MaxTransactionIdAttributeNumber: result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmax(tup->t_data)); break; case MinCommandIdAttributeNumber: case MaxCommandIdAttributeNumber: /* * cmin and cmax are now both aliases for the same field, which * can in fact also be a combo command id. XXX perhaps we should * return the "real" cmin or cmax if possible, that is if we are * inside the originating transaction? */ result = CommandIdGetDatum(HeapTupleHeaderGetRawCommandId(tup->t_data)); break; case TableOidAttributeNumber: result = ObjectIdGetDatum(tup->t_tableOid); break; default: elog(ERROR, "invalid attnum: %d", attnum); result = 0; /* keep compiler quiet */ break; } return result; } /* ---------------- * heap_copytuple * * returns a copy of an entire tuple * * The HeapTuple struct, tuple header, and tuple data are all allocated * as a single palloc() block. * ---------------- */ HeapTuple heap_copytuple(HeapTuple tuple) { HeapTuple newTuple; if (!HeapTupleIsValid(tuple) || tuple->t_data == NULL) return NULL; newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple->t_len); newTuple->t_len = tuple->t_len; newTuple->t_self = tuple->t_self; newTuple->t_tableOid = tuple->t_tableOid; newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE); memcpy((char *) newTuple->t_data, (char *) tuple->t_data, tuple->t_len); return newTuple; } /* ---------------- * heap_copytuple_with_tuple * * copy a tuple into a caller-supplied HeapTuple management struct * * Note that after calling this function, the "dest" HeapTuple will not be * allocated as a single palloc() block (unlike with heap_copytuple()). * ---------------- */ void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest) { if (!HeapTupleIsValid(src) || src->t_data == NULL) { dest->t_data = NULL; return; } dest->t_len = src->t_len; dest->t_self = src->t_self; dest->t_tableOid = src->t_tableOid; dest->t_data = (HeapTupleHeader) palloc(src->t_len); memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len); } /* * Expand a tuple which has fewer attributes than required. For each attribute * not present in the sourceTuple, if there is a missing value that will be * used. Otherwise the attribute will be set to NULL. * * The source tuple must have fewer attributes than the required number. * * Only one of targetHeapTuple and targetMinimalTuple may be supplied. The * other argument must be NULL. */ static void expand_tuple(HeapTuple *targetHeapTuple, MinimalTuple *targetMinimalTuple, HeapTuple sourceTuple, TupleDesc tupleDesc) { AttrMissing *attrmiss = NULL; int attnum; int firstmissingnum; bool hasNulls = HeapTupleHasNulls(sourceTuple); HeapTupleHeader targetTHeader; HeapTupleHeader sourceTHeader = sourceTuple->t_data; int sourceNatts = HeapTupleHeaderGetNatts(sourceTHeader); int natts = tupleDesc->natts; int sourceNullLen; int targetNullLen; Size sourceDataLen = sourceTuple->t_len - sourceTHeader->t_hoff; Size targetDataLen; Size len; int hoff; bits8 *nullBits = NULL; int bitMask = 0; char *targetData; uint16 *infoMask; Assert((targetHeapTuple && !targetMinimalTuple) || (!targetHeapTuple && targetMinimalTuple)); Assert(sourceNatts < natts); sourceNullLen = (hasNulls ? BITMAPLEN(sourceNatts) : 0); targetDataLen = sourceDataLen; if (tupleDesc->constr && tupleDesc->constr->missing) { /* * If there are missing values we want to put them into the tuple. * Before that we have to compute the extra length for the values * array and the variable length data. */ attrmiss = tupleDesc->constr->missing; /* * Find the first item in attrmiss for which we don't have a value in * the source. We can ignore all the missing entries before that. */ for (firstmissingnum = sourceNatts; firstmissingnum < natts; firstmissingnum++) { if (attrmiss[firstmissingnum].am_present) break; else hasNulls = true; } /* * Now walk the missing attributes. If there is a missing value make * space for it. Otherwise, it's going to be NULL. */ for (attnum = firstmissingnum; attnum < natts; attnum++) { if (attrmiss[attnum].am_present) { Form_pg_attribute att = TupleDescAttr(tupleDesc, attnum); targetDataLen = att_align_datum(targetDataLen, att->attalign, att->attlen, attrmiss[attnum].am_value); targetDataLen = att_addlength_pointer(targetDataLen, att->attlen, attrmiss[attnum].am_value); } else { /* no missing value, so it must be null */ hasNulls = true; } } } /* end if have missing values */ else { /* * If there are no missing values at all then NULLS must be allowed, * since some of the attributes are known to be absent. */ hasNulls = true; } len = 0; if (hasNulls) { targetNullLen = BITMAPLEN(natts); len += targetNullLen; } else targetNullLen = 0; /* * Allocate and zero the space needed. Note that the tuple body and * HeapTupleData management structure are allocated in one chunk. */ if (targetHeapTuple) { len += offsetof(HeapTupleHeaderData, t_bits); hoff = len = MAXALIGN(len); /* align user data safely */ len += targetDataLen; *targetHeapTuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len); (*targetHeapTuple)->t_data = targetTHeader = (HeapTupleHeader) ((char *) *targetHeapTuple + HEAPTUPLESIZE); (*targetHeapTuple)->t_len = len; (*targetHeapTuple)->t_tableOid = sourceTuple->t_tableOid; (*targetHeapTuple)->t_self = sourceTuple->t_self; targetTHeader->t_infomask = sourceTHeader->t_infomask; targetTHeader->t_hoff = hoff; HeapTupleHeaderSetNatts(targetTHeader, natts); HeapTupleHeaderSetDatumLength(targetTHeader, len); HeapTupleHeaderSetTypeId(targetTHeader, tupleDesc->tdtypeid); HeapTupleHeaderSetTypMod(targetTHeader, tupleDesc->tdtypmod); /* We also make sure that t_ctid is invalid unless explicitly set */ ItemPointerSetInvalid(&(targetTHeader->t_ctid)); if (targetNullLen > 0) nullBits = (bits8 *) ((char *) (*targetHeapTuple)->t_data + offsetof(HeapTupleHeaderData, t_bits)); targetData = (char *) (*targetHeapTuple)->t_data + hoff; infoMask = &(targetTHeader->t_infomask); } else { len += SizeofMinimalTupleHeader; hoff = len = MAXALIGN(len); /* align user data safely */ len += targetDataLen; *targetMinimalTuple = (MinimalTuple) palloc0(len); (*targetMinimalTuple)->t_len = len; (*targetMinimalTuple)->t_hoff = hoff + MINIMAL_TUPLE_OFFSET; (*targetMinimalTuple)->t_infomask = sourceTHeader->t_infomask; /* Same macro works for MinimalTuples */ HeapTupleHeaderSetNatts(*targetMinimalTuple, natts); if (targetNullLen > 0) nullBits = (bits8 *) ((char *) *targetMinimalTuple + offsetof(MinimalTupleData, t_bits)); targetData = (char *) *targetMinimalTuple + hoff; infoMask = &((*targetMinimalTuple)->t_infomask); } if (targetNullLen > 0) { if (sourceNullLen > 0) { /* if bitmap pre-existed copy in - all is set */ memcpy(nullBits, ((char *) sourceTHeader) + offsetof(HeapTupleHeaderData, t_bits), sourceNullLen); nullBits += sourceNullLen - 1; } else { sourceNullLen = BITMAPLEN(sourceNatts); /* Set NOT NULL for all existing attributes */ memset(nullBits, 0xff, sourceNullLen); nullBits += sourceNullLen - 1; if (sourceNatts & 0x07) { /* build the mask (inverted!) */ bitMask = 0xff << (sourceNatts & 0x07); /* Voila */ *nullBits = ~bitMask; } } bitMask = (1 << ((sourceNatts - 1) & 0x07)); } /* End if have null bitmap */ memcpy(targetData, ((char *) sourceTuple->t_data) + sourceTHeader->t_hoff, sourceDataLen); targetData += sourceDataLen; /* Now fill in the missing values */ for (attnum = sourceNatts; attnum < natts; attnum++) { Form_pg_attribute attr = TupleDescAttr(tupleDesc, attnum); if (attrmiss && attrmiss[attnum].am_present) { fill_val(attr, nullBits ? &nullBits : NULL, &bitMask, &targetData, infoMask, attrmiss[attnum].am_value, false); } else { fill_val(attr, &nullBits, &bitMask, &targetData, infoMask, (Datum) 0, true); } } /* end loop over missing attributes */ } /* * Fill in the missing values for a minimal HeapTuple */ MinimalTuple minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc) { MinimalTuple minimalTuple; expand_tuple(NULL, &minimalTuple, sourceTuple, tupleDesc); return minimalTuple; } /* * Fill in the missing values for an ordinary HeapTuple */ HeapTuple heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc) { HeapTuple heapTuple; expand_tuple(&heapTuple, NULL, sourceTuple, tupleDesc); return heapTuple; } /* ---------------- * heap_copy_tuple_as_datum * * copy a tuple as a composite-type Datum * ---------------- */ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { HeapTupleHeader td; /* * If the tuple contains any external TOAST pointers, we have to inline * those fields to meet the conventions for composite-type Datums. */ if (HeapTupleHasExternal(tuple)) return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); /* * Fast path for easy case: just make a palloc'd copy and insert the * correct composite-Datum header fields (since those may not be set if * the given tuple came from disk, rather than from heap_form_tuple). */ td = (HeapTupleHeader) palloc(tuple->t_len); memcpy((char *) td, (char *) tuple->t_data, tuple->t_len); HeapTupleHeaderSetDatumLength(td, tuple->t_len); HeapTupleHeaderSetTypeId(td, tupleDesc->tdtypeid); HeapTupleHeaderSetTypMod(td, tupleDesc->tdtypmod); return PointerGetDatum(td); } /* * 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 */ Size len, data_len; int hoff; bool hasnull = false; 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 */ for (i = 0; i < numberOfAttributes; i++) { if (isnull[i]) { hasnull = true; break; } } /* * Determine total space needed */ len = offsetof(HeapTupleHeaderData, t_bits); if (hasnull) len += BITMAPLEN(numberOfAttributes); hoff = len = MAXALIGN(len); /* align user data safely */ data_len = heap_compute_data_size(tupleDescriptor, values, isnull); len += data_len; /* * 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_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. This lets HeapTupleHeaderGetDatum * identify the tuple type if needed. */ tuple->t_len = len; ItemPointerSetInvalid(&(tuple->t_self)); tuple->t_tableOid = InvalidOid; HeapTupleHeaderSetDatumLength(td, len); HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid); HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod); /* We also make sure that t_ctid is invalid unless explicitly set */ ItemPointerSetInvalid(&(td->t_ctid)); HeapTupleHeaderSetNatts(td, numberOfAttributes); td->t_hoff = hoff; heap_fill_tuple(tupleDescriptor, values, isnull, (char *) td + hoff, data_len, &td->t_infomask, (hasnull ? td->t_bits : NULL)); 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 columns. 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 */ newTuple->t_data->t_ctid = tuple->t_data->t_ctid; newTuple->t_self = tuple->t_self; newTuple->t_tableOid = tuple->t_tableOid; return newTuple; } /* * heap_modify_tuple_by_cols * form a new tuple from an old tuple and a set of replacement values. * * This is like heap_modify_tuple, except that instead of specifying which * column(s) to replace by a boolean map, an array of target column numbers * is used. This is often more convenient when a fixed number of columns * are to be replaced. The replCols, replValues, and replIsnull arrays must * be of length nCols. Target column numbers are indexed from 1. * * The result is allocated in the current memory context. */ HeapTuple heap_modify_tuple_by_cols(HeapTuple tuple, TupleDesc tupleDesc, int nCols, int *replCols, Datum *replValues, bool *replIsnull) { int numberOfAttributes = tupleDesc->natts; Datum *values; bool *isnull; HeapTuple newTuple; int i; /* * allocate and fill values and isnull arrays from the tuple, then replace * selected columns from the input arrays. */ values = (Datum *) palloc(numberOfAttributes * sizeof(Datum)); isnull = (bool *) palloc(numberOfAttributes * sizeof(bool)); heap_deform_tuple(tuple, tupleDesc, values, isnull); for (i = 0; i < nCols; i++) { int attnum = replCols[i]; if (attnum <= 0 || attnum > numberOfAttributes) elog(ERROR, "invalid column number %d", attnum); values[attnum - 1] = replValues[i]; isnull[attnum - 1] = replIsnull[i]; } /* * 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 */ newTuple->t_data->t_ctid = tuple->t_data->t_ctid; newTuple->t_self = tuple->t_self; newTuple->t_tableOid = tuple->t_tableOid; 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 * HeapTupleHeaderGetNatts(tuple->t_data). * * 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); int tdesc_natts = tupleDesc->natts; int natts; /* number of atts to extract */ int attnum; char *tp; /* ptr to tuple data */ uint32 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 = HeapTupleHeaderGetNatts(tup); /* * 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 = TupleDescAttr(tupleDesc, 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 if (thisatt->attlen == -1) { /* * We can only cache the offset for a varlena attribute if the * offset is already suitably aligned, so that there would be no * pad bytes in any case: then the offset will be valid for either * an aligned or unaligned value. */ if (!slow && off == att_align_nominal(off, thisatt->attalign)) thisatt->attcacheoff = off; else { off = att_align_pointer(off, thisatt->attalign, -1, tp + off); slow = true; } } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, thisatt->attalign); if (!slow) thisatt->attcacheoff = off; } values[attnum] = fetchatt(thisatt, tp + off); off = att_addlength_pointer(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 nulls or missing values as appropriate. */ for (; attnum < tdesc_natts; attnum++) values[attnum] = getmissingattr(tupleDesc, attnum + 1, &isnull[attnum]); } /* * heap_freetuple */ void heap_freetuple(HeapTuple htup) { pfree(htup); } /* * heap_form_minimal_tuple * construct a MinimalTuple from the given values[] and isnull[] arrays, * which are of the length indicated by tupleDescriptor->natts * * This is exactly like heap_form_tuple() except that the result is a * "minimal" tuple lacking a HeapTupleData header as well as room for system * columns. * * The result is allocated in the current memory context. */ MinimalTuple heap_form_minimal_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull) { MinimalTuple tuple; /* return tuple */ Size len, data_len; int hoff; bool hasnull = false; 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 */ for (i = 0; i < numberOfAttributes; i++) { if (isnull[i]) { hasnull = true; break; } } /* * Determine total space needed */ len = SizeofMinimalTupleHeader; if (hasnull) len += BITMAPLEN(numberOfAttributes); hoff = len = MAXALIGN(len); /* align user data safely */ data_len = heap_compute_data_size(tupleDescriptor, values, isnull); len += data_len; /* * Allocate and zero the space needed. */ tuple = (MinimalTuple) palloc0(len); /* * And fill in the information. */ tuple->t_len = len; HeapTupleHeaderSetNatts(tuple, numberOfAttributes); tuple->t_hoff = hoff + MINIMAL_TUPLE_OFFSET; heap_fill_tuple(tupleDescriptor, values, isnull, (char *) tuple + hoff, data_len, &tuple->t_infomask, (hasnull ? tuple->t_bits : NULL)); return tuple; } /* * heap_free_minimal_tuple */ void heap_free_minimal_tuple(MinimalTuple mtup) { pfree(mtup); } /* * heap_copy_minimal_tuple * copy a MinimalTuple * * The result is allocated in the current memory context. */ MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup) { MinimalTuple result; result = (MinimalTuple) palloc(mtup->t_len); memcpy(result, mtup, mtup->t_len); return result; } /* * heap_tuple_from_minimal_tuple * create a HeapTuple by copying from a MinimalTuple; * system columns are filled with zeroes * * The result is allocated in the current memory context. * The HeapTuple struct, tuple header, and tuple data are all allocated * as a single palloc() block. */ HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup) { HeapTuple result; uint32 len = mtup->t_len + MINIMAL_TUPLE_OFFSET; result = (HeapTuple) palloc(HEAPTUPLESIZE + len); result->t_len = len; ItemPointerSetInvalid(&(result->t_self)); result->t_tableOid = InvalidOid; result->t_data = (HeapTupleHeader) ((char *) result + HEAPTUPLESIZE); memcpy((char *) result->t_data + MINIMAL_TUPLE_OFFSET, mtup, mtup->t_len); memset(result->t_data, 0, offsetof(HeapTupleHeaderData, t_infomask2)); return result; } /* * minimal_tuple_from_heap_tuple * create a MinimalTuple by copying from a HeapTuple * * The result is allocated in the current memory context. */ MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup) { MinimalTuple result; uint32 len; Assert(htup->t_len > MINIMAL_TUPLE_OFFSET); len = htup->t_len - MINIMAL_TUPLE_OFFSET; result = (MinimalTuple) palloc(len); memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len); result->t_len = len; return result; } /* * This mainly exists so JIT can inline the definition, but it's also * sometimes useful in debugging sessions. */ size_t varsize_any(void *p) { return VARSIZE_ANY(p); }