/* ---------------- * heap_attisnull - returns TRUE iff tuple attribute is not present * ---------------- */ bool heap_attisnull(HeapTuple tup, int attnum) { if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data)) 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 ObjectIdAttributeNumber: case MinTransactionIdAttributeNumber: case MinCommandIdAttributeNumber: case MaxTransactionIdAttributeNumber: case MaxCommandIdAttributeNumber: /* these are never null */ break; default: elog(ERROR, "invalid attnum: %d", attnum); } return false; }
bool heap_attisnull_normalattr(HeapTuple tup, int attnum) { Assert(attnum > 0); if (HeapTupleNoNulls(tup)) return false; return att_isnull(attnum - 1, tup->t_data->t_bits); }
/* * Try to decode a tuple using a types string provided previously. * * Arguments: * tupleData - pointer to the tuple data * tupleSize - tuple size in bytes */ void FormatDecode(const char *tupleData, unsigned int tupleSize) { HeapTupleHeader header = (HeapTupleHeader) tupleData; const char *data = tupleData + header->t_hoff; unsigned int size = tupleSize - header->t_hoff; int curr_attr; CopyClear(); for (curr_attr = 0; curr_attr < ncallbacks; curr_attr++) { int ret; unsigned int processed_size = 0; if ((header->t_infomask & HEAP_HASNULL) && att_isnull(curr_attr, header->t_bits)) { CopyAppend("\\N"); continue; } if (size <= 0) { printf("Error: unable to decode a tuple, no more bytes left. Partial data: %s\n", copyString.data); return; } ret = callbacks[curr_attr] (data, size, &processed_size); if (ret < 0) { printf("Error: unable to decode a tuple, callback #%d returned %d. Partial data: %s\n", curr_attr + 1, ret, copyString.data); return; } size -= processed_size; data += processed_size; } if (size != 0) { printf("Error: unable to decode a tuple, %d bytes left, 0 expected. Partial data: %s\n", size, copyString.data); return; } CopyFlush(); }
/* ---------------- * 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; }
HeapTuple CheckerConstraints(Checker *checker, HeapTuple tuple, int *parsing_field) { if (checker->has_constraints) { *parsing_field = 0; /* Place tuple in tuple slot */ ExecStoreTuple(tuple, checker->slot, InvalidBuffer, false); /* Check the constraints of the tuple */ ExecConstraints(checker->resultRelInfo, checker->slot, checker->estate); } else if (checker->has_not_null && HeapTupleHasNulls(tuple)) { /* * Even if CHECK_CONSTRAINTS is not specified, check NOT NULL constraint */ TupleDesc desc = checker->desc; int i; for (i = 0; i < desc->natts; i++) { if (desc->attrs[i]->attnotnull && att_isnull(i, tuple->t_data->t_bits)) { *parsing_field = i + 1; /* 1 origin */ ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("null value in column \"%s\" violates not-null constraint", NameStr(desc->attrs[i]->attname)))); } } } return tuple; }
/* * 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). * * 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->tts_nvalid is the number of attributes already extracted. */ static void slot_deform_tuple(TupleTableSlot *slot, int natts) { HeapTuple tuple = TupGetHeapTuple(slot); TupleDesc tupleDesc = slot->tts_tupleDescriptor; Datum *values = slot->PRIVATE_tts_values; bool *isnull = slot->PRIVATE_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 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->PRIVATE_tts_nvalid; if (attnum == 0) { /* Start from the first attribute */ off = 0; slow = false; } else { /* Restore state from previous execution */ off = slot->PRIVATE_tts_off; slow = slot->PRIVATE_tts_slow; } tp = (char *) tup + tup->t_hoff; for (; 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 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; } if (!slow && thisatt->attlen < 0) slow = true; 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 */ } /* * Save state for next execution */ slot->PRIVATE_tts_nvalid = attnum; slot->PRIVATE_tts_off = off; slot->PRIVATE_tts_slow = slow; }
/* * tuple_data_split_internal * * Split raw tuple data taken directly from a page into an array of bytea * elements. This routine does a lookup on NULL values and creates array * elements accordingly. This is a reimplementation of nocachegetattr() * in heaptuple.c simplified for educational purposes. */ static Datum tuple_data_split_internal(Oid relid, char *tupdata, uint16 tupdata_len, uint16 t_infomask, uint16 t_infomask2, bits8 *t_bits, bool do_detoast) { ArrayBuildState *raw_attrs; int nattrs; int i; int off = 0; Relation rel; TupleDesc tupdesc; /* Get tuple descriptor from relation OID */ rel = relation_open(relid, AccessShareLock); tupdesc = RelationGetDescr(rel); raw_attrs = initArrayResult(BYTEAOID, CurrentMemoryContext, false); nattrs = tupdesc->natts; if (nattrs < (t_infomask2 & HEAP_NATTS_MASK)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("number of attributes in tuple header is greater than number of attributes in tuple descriptor"))); for (i = 0; i < nattrs; i++) { Form_pg_attribute attr; bool is_null; bytea *attr_data = NULL; attr = TupleDescAttr(tupdesc, i); /* * Tuple header can specify less attributes than tuple descriptor as * ALTER TABLE ADD COLUMN without DEFAULT keyword does not actually * change tuples in pages, so attributes with numbers greater than * (t_infomask2 & HEAP_NATTS_MASK) should be treated as NULL. */ if (i >= (t_infomask2 & HEAP_NATTS_MASK)) is_null = true; else is_null = (t_infomask & HEAP_HASNULL) && att_isnull(i, t_bits); if (!is_null) { int len; if (attr->attlen == -1) { off = att_align_pointer(off, attr->attalign, -1, tupdata + off); /* * As VARSIZE_ANY throws an exception if it can't properly * detect the type of external storage in macros VARTAG_SIZE, * this check is repeated to have a nicer error handling. */ if (VARATT_IS_EXTERNAL(tupdata + off) && !VARATT_IS_EXTERNAL_ONDISK(tupdata + off) && !VARATT_IS_EXTERNAL_INDIRECT(tupdata + off)) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("first byte of varlena attribute is incorrect for attribute %d", i))); len = VARSIZE_ANY(tupdata + off); } else { off = att_align_nominal(off, attr->attalign); len = attr->attlen; } if (tupdata_len < off + len) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("unexpected end of tuple data"))); if (attr->attlen == -1 && do_detoast) attr_data = DatumGetByteaPCopy(tupdata + off); else { attr_data = (bytea *) palloc(len + VARHDRSZ); SET_VARSIZE(attr_data, len + VARHDRSZ); memcpy(VARDATA(attr_data), tupdata + off, len); } off = att_addlength_pointer(off, attr->attlen, tupdata + off); } raw_attrs = accumArrayResult(raw_attrs, PointerGetDatum(attr_data), is_null, BYTEAOID, CurrentMemoryContext); if (attr_data) pfree(attr_data); } if (tupdata_len != off) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("end of tuple reached without looking at all its data"))); relation_close(rel, AccessShareLock); return makeArrayResult(raw_attrs, CurrentMemoryContext); }
/* * Convert an index tuple into Datum/isnull arrays. * * The caller must allocate sufficient storage for the output arrays. * (INDEX_MAX_KEYS entries should be enough.) * * This is nearly the same as heap_deform_tuple(), but for IndexTuples. * One difference is that the tuple should never have any missing columns. */ void index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor, Datum *values, bool *isnull) { int hasnulls = IndexTupleHasNulls(tup); int natts = tupleDescriptor->natts; /* number of atts to extract */ int attnum; char *tp; /* ptr to tuple data */ int off; /* offset in tuple data */ bits8 *bp; /* ptr to null bitmap in tuple */ bool slow = false; /* can we use/set attcacheoff? */ /* Assert to protect callers who allocate fixed-size arrays */ Assert(natts <= INDEX_MAX_KEYS); /* XXX "knows" t_bits are just after fixed tuple header! */ bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData)); tp = (char *) tup + IndexInfoFindDataOffset(tup->t_info); off = 0; for (attnum = 0; attnum < natts; attnum++) { Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, 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 */ } }
static void convert_heaptuple(HeapTupleHeader htup) { /* HEAP_HASEXTENDED and HEAP_HASCOMPRESSED flags were removed. Clear them out */ htup->t_infomask &= ~(VERSION4_HEAP_HASEXTENDED | VERSION4_HEAP_HASCOMPRESSED); /* HEAP_HASOID flag was moved */ if (htup->t_infomask & VERSION4_HEAP_HASOID) { htup->t_infomask &= ~(VERSION4_HEAP_HASOID); htup->t_infomask |= HEAP_HASOID; } /* Any numeric columns? */ if (curr_hasnumerics) { /* This is like heap_deform_tuple() */ bool hasnulls = (htup->t_infomask & HEAP_HASNULL) != 0; int attnum; char *tp; long off; bits8 *bp = htup->t_bits; /* ptr to null bitmap in tuple */ tp = (char *) htup + htup->t_hoff; off = 0; for (attnum = 0; attnum < curr_natts; attnum++) { AttInfo *thisatt = &curr_atts[attnum]; if (hasnulls && att_isnull(attnum, bp)) continue; if (thisatt->attlen == -1) { off = att_align_pointer(off, thisatt->attalign, -1, tp + off); } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, thisatt->attalign); } if (thisatt->is_numeric) { /* * Before PostgreSQL 8.3, the n_weight and n_sign_dscale fields * were the other way 'round. Swap them. * * FIXME: need to handle toasted datums here. */ Datum datum = PointerGetDatum(tp + off); if (VARATT_IS_COMPRESSED(datum)) pg_log(PG_FATAL, "converting compressed numeric datums is not implemented\n"); else if (VARATT_IS_EXTERNAL(datum)) pg_log(PG_FATAL, "converting toasted numeric datums is not implemented\n"); else { char *numericdata = VARDATA_ANY(DatumGetPointer(datum)); int sz = VARSIZE_ANY_EXHDR(DatumGetPointer(datum)); uint16 tmp; if (sz < 4) pg_log(PG_FATAL, "unexpected size for numeric datum: %d\n", sz); memcpy(&tmp, &numericdata[0], 2); memcpy(&numericdata[0], &numericdata[2], 2); memcpy(&numericdata[2], &tmp, 2); } } off = att_addlength_pointer(off, thisatt->attlen, tp + off); } } }
/* * 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? */ Assert(!is_heaptuple_memtuple(tuple)); 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 = 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 { /* if it's a varlena it may or may not be aligned, so check for * something that looks like a padding byte before aligning. If * we're already aligned it may be the leading byte of a 4-byte * header but then the att_align is harmless. Don't bother looking * if it's not a varlena though.*/ if (thisatt->attlen != -1 || !tp[off]) off = att_align(off, thisatt->attalign); if (!slow && thisatt->attlen != -1) thisatt->attcacheoff = off; } if (!slow && thisatt->attlen < 0) slow = true; values[attnum] = fetchatt(thisatt, tp + off); #ifdef USE_ASSERT_CHECKING /* Ignore attributes with dropped types */ if (thisatt->attlen == -1 && !thisatt->attisdropped) { Assert(VARATT_IS_SHORT_D(values[attnum]) || !VARATT_COULD_SHORT_D(values[attnum]) || thisatt->atttypid == OIDVECTOROID || thisatt->atttypid == INT2VECTOROID || thisatt->atttypid >= FirstNormalObjectId); } #endif off = att_addlength(off, thisatt->attlen, PointerGetDatum(tp + off)); } /* * 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; } }
/* checks the individual attributes of the tuple */ uint32 check_index_tuple_attributes(Relation rel, PageHeader header, int block, int i, char *buffer) { IndexTuple tuple; uint32 nerrs = 0; int j, off; bits8 * bitmap; BTPageOpaque opaque; ereport(DEBUG2,(errmsg("[%d:%d] checking attributes for the tuple", block, i))); /* get the index tuple and info about the page */ tuple = (IndexTuple)(buffer + header->pd_linp[i].lp_off); opaque = (BTPageOpaque)(buffer + header->pd_special); /* current attribute offset - always starts at (buffer + off) */ off = header->pd_linp[i].lp_off + IndexInfoFindDataOffset(tuple->t_info); ereport(DEBUG3,(errmsg("[%d:%d] tuple has %d attributes", block, (i+1), RelationGetNumberOfAttributes(rel)))); bitmap = (bits8*)(buffer + header->pd_linp[i].lp_off + sizeof(IndexTupleData)); /* TODO This is mostly copy'n'paste from check_heap_tuple_attributes, so maybe it could be refactored to share the code. */ /* For left-most tuples on non-leaf pages, there are no data actually (see src/backend/access/nbtree/README, last paragraph in section "Notes About Data Representation") Use P_LEFTMOST/P_ISLEAF to identify such cases (for the leftmost item only) and set len = 0. */ if (P_LEFTMOST(opaque) && (! P_ISLEAF(opaque)) && (i == 0)) { ereport(DEBUG3, (errmsg("[%d:%d] leftmost tuple on non-leaf block => no data, skipping", block, i))); return nerrs; } /* check all the index attributes */ for (j = 0; j < rel->rd_att->natts; j++) { /* default length of the attribute */ int len = rel->rd_att->attrs[j]->attlen; /* copy from src/backend/commands/analyze.c */ bool is_varlena = (!rel->rd_att->attrs[j]->attbyval && len == -1); bool is_varwidth = (!rel->rd_att->attrs[j]->attbyval && len < 0); /* thus it's "len = -2" */ /* if the attribute is marked as NULL (in the tuple header), skip to the next attribute */ if (IndexTupleHasNulls(tuple) && att_isnull(j, bitmap)) { ereport(DEBUG3, (errmsg("[%d:%d] attribute '%s' is NULL (skipping)", block, (i+1), rel->rd_att->attrs[j]->attname.data))); continue; } /* fix the alignment (see src/include/access/tupmacs.h) */ off = att_align_pointer(off, rel->rd_att->attrs[j]->attalign, rel->rd_att->attrs[j]->attlen, buffer+off); if (is_varlena) { /* other interesting macros (see postgres.h) - should do something about those ... VARATT_IS_COMPRESSED(PTR) VARATT_IS_4B_C(PTR) VARATT_IS_EXTERNAL(PTR) VARATT_IS_1B_E(PTR) VARATT_IS_SHORT(PTR) VARATT_IS_1B(PTR) VARATT_IS_EXTENDED(PTR) (!VARATT_IS_4B_U(PTR)) */ len = VARSIZE_ANY(buffer + off); if (len < 0) { ereport(WARNING, (errmsg("[%d:%d] attribute '%s' has negative length < 0 (%d)", block, (i+1), rel->rd_att->attrs[j]->attname.data, len))); ++nerrs; break; } if (VARATT_IS_COMPRESSED(buffer + off)) { /* the raw length should be less than 1G (and positive) */ if ((VARRAWSIZE_4B_C(buffer + off) < 0) || (VARRAWSIZE_4B_C(buffer + off) > 1024*1024)) { ereport(WARNING, (errmsg("[%d:%d] attribute '%s' has invalid length %d (should be between 0 and 1G)", block, (i+1), rel->rd_att->attrs[j]->attname.data, VARRAWSIZE_4B_C(buffer + off)))); ++nerrs; /* no break here, this does not break the page structure - we may check the other attributes */ } } /* FIXME Check if the varlena value may be detoasted. */ } else if (is_varwidth) { /* get the C-string length (at most to the end of tuple), +1 as it does not include '\0' at the end */ /* if the string is not properly terminated, then this returns 'remaining space + 1' so it's detected */ len = strnlen(buffer + off, header->pd_linp[i].lp_off + len + header->pd_linp[i].lp_len - off) + 1; } /* Check if the length makes sense (is not negative and does not overflow * the tuple end, stop validating the other rows (we don't know where to * continue anyway). */ if (off + len > (header->pd_linp[i].lp_off + header->pd_linp[i].lp_len)) { ereport(WARNING, (errmsg("[%d:%d] attribute '%s' (off=%d len=%d) overflows tuple end (off=%d, len=%d)", block, (i+1), rel->rd_att->attrs[j]->attname.data, off, len, header->pd_linp[i].lp_off, header->pd_linp[i].lp_len))); ++nerrs; break; } /* skip to the next attribute */ off += len; ereport(DEBUG3,(errmsg("[%d:%d] attribute '%s' len=%d", block, (i+1), rel->rd_att->attrs[j]->attname.data, len))); } ereport(DEBUG3,(errmsg("[%d:%d] last attribute ends at %d, tuple ends at %d", block, (i+1), off, header->pd_linp[i].lp_off + header->pd_linp[i].lp_len))); /* after the last attribute, the offset should be exactly the same as the end of the tuple */ if (MAXALIGN(off) != header->pd_linp[i].lp_off + header->pd_linp[i].lp_len) { ereport(WARNING, (errmsg("[%d:%d] the last attribute ends at %d but the tuple ends at %d", block, (i+1), off, header->pd_linp[i].lp_off + header->pd_linp[i].lp_len))); ++nerrs; } return nerrs; }
/* ---------------- * nocache_index_getattr * * This gets called from index_getattr() macro, and only in cases * where we can't use cacheoffset and the value is not null. * * This caches attribute offsets in the attribute descriptor. * * An alternate 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 * ---------------- */ Datum nocache_index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) { Form_pg_attribute *att = tupleDesc->attrs; char *tp; /* ptr to att in tuple */ bits8 *bp = NULL; /* ptr to null bitmask in tuple */ bool slow = false; /* do we have to walk nulls? */ int data_off; /* tuple data offset */ (void) isnull; /* not used */ /* * sanity checks */ /* ---------------- * 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. * ---------------- */ #ifdef IN_MACRO /* This is handled in the macro */ Assert(PointerIsValid(isnull)); Assert(attnum > 0); *isnull = false; #endif data_off = IndexInfoFindDataOffset(tup->t_info); attnum--; if (!IndexTupleHasNulls(tup)) { #ifdef IN_MACRO /* This is handled in the macro */ if (att[attnum]->attcacheoff != -1) { return fetchatt(att[attnum], (char *) tup + data_off + att[attnum]->attcacheoff); } #endif } else { /* * there's a null somewhere in the tuple * * check to see if desired att is null */ /* XXX "knows" t_bits are just after fixed tuple header! */ bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData)); #ifdef IN_MACRO /* This is handled in the macro */ if (att_isnull(attnum, bp)) { *isnull = true; return (Datum) NULL; } #endif /* * Now 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 *) tup + data_off; /* * now check for any non-fixed length attrs before our attribute */ if (!slow) { if (att[attnum]->attcacheoff != -1) { return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } else if (IndexTupleHasVarwidths(tup)) { int j; for (j = 0; j < attnum; j++) { if (att[j]->attlen <= 0) { slow = true; break; } } } } /* * If slow is false, and we got here, we know that we have a tuple with no * nulls or var-widths before the target attribute. If possible, we also * want to initialize the remainder of the attribute cached offset values. */ if (!slow) { int j = 1; long off; /* * need to set cache for some atts */ att[0]->attcacheoff = 0; while (j < attnum && att[j]->attcacheoff > 0) j++; off = att[j - 1]->attcacheoff + att[j - 1]->attlen; for (; j <= attnum; j++) { off = att_align(off, att[j]->attalign); att[j]->attcacheoff = off; off += att[j]->attlen; } return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } else { bool usecache = true; int off = 0; int i; /* * Now we know that we have to walk the tuple CAREFULLY. */ for (i = 0; i < attnum; i++) { if (IndexTupleHasNulls(tup)) { if (att_isnull(i, bp)) { usecache = false; continue; } } /* If we know the next offset, we can skip the rest */ if (usecache && att[i]->attcacheoff != -1) off = att[i]->attcacheoff; else { off = att_align(off, att[i]->attalign); if (usecache) att[i]->attcacheoff = off; } off = att_addlength(off, att[i]->attlen, tp + off); if (usecache && att[i]->attlen <= 0) usecache = false; } off = att_align(off, att[attnum]->attalign); return fetchatt(att[attnum], tp + off); } }
/* ---------------- * 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 tuple, int attnum, TupleDesc tupleDesc) { 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 bitmap in tuple */ bool slow = false; /* do we have to walk nulls? */ Assert(!is_heaptuple_memtuple(tuple)); /* If any cached offsets are there we can check that they make sense, but * there may not be any at all, so pass -1 for the attnum we know is valid */ #ifdef IN_MACRO /* This is handled in the macro */ Assert(attnum > 0); if (isnull) *isnull = false; #endif attnum--; /* ---------------- * 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. * ---------------- */ if (HeapTupleNoNulls(tuple)) { #ifdef IN_MACRO /* This is handled in the macro */ if (att[attnum]->attcacheoff != -1) { return fetchatt(att[attnum], (char *) tup + tup->t_hoff + att[attnum]->attcacheoff); } #endif } else { /* * there's a null somewhere in the tuple * * check to see if desired att is null */ #ifdef IN_MACRO /* This is handled in the macro */ if (att_isnull(attnum, bp)) { if (isnull) *isnull = true; return (Datum) NULL; } #endif /* * Now 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 *) tup + tup->t_hoff; /* * now check for any non-fixed length attrs before our attribute */ if (!slow) { /* * 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. */ if (att[attnum]->attcacheoff >= 0) { return fetchatt(att[attnum], tp + att[attnum]->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(tuple)) { int j; /* * In for(), we test <= and not < because we want to see if we can * go past it in initializing offsets. */ for (j = 0; j <= attnum; j++) { if (att[j]->attlen <= 0) { slow = true; break; } } } } /* * If slow is false, and we got here, we know that we have a tuple with no * nulls or var-widths before the target attribute. If possible, we also * want to initialize the remainder of the attribute cached offset values. */ if (!slow) { int j = 1; long off; int natts = HeapTupleHeaderGetNatts(tup); /* * 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. */ /* this is always true */ att[0]->attcacheoff = 0; while (j < attnum && att[j]->attcacheoff > 0) j++; off = att[j - 1]->attcacheoff + att[j - 1]->attlen; for (; j <= attnum || /* Can we compute more? We will probably need them */ (j < natts && att[j]->attcacheoff == -1 && (HeapTupleNoNulls(tuple) || !att_isnull(j, bp)) && (HeapTupleAllFixed(tuple) || att[j]->attlen > 0)); j++) { /* don't need to worry about shortvarlenas here since we're only * looking at non-varlenas. Note that it's important that we check * that the target attribute itself is a nonvarlena too since we * can't use cached offsets for even the first varlena any more. */ off = att_align(off, att[j]->attalign); att[j]->attcacheoff = off; off = att_addlength(off, att[j]->attlen, PointerGetDatum(tp + off)); } return fetchatt(att[attnum], tp + att[attnum]->attcacheoff); } else { bool usecache = true; int off = 0; int i; /* this is always true */ att[0]->attcacheoff = 0; /* * Now we know that we have to walk the tuple CAREFULLY. * * 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. */ for (i = 0; i < attnum; i++) { if (HeapTupleHasNulls(tuple) && att_isnull(i, bp)) { usecache = false; continue; } /* If we know the next offset, we can skip the alignment calc */ if (usecache && att[i]->attcacheoff != -1) off = att[i]->attcacheoff; else { /* if it's a varlena it may or may not be aligned, so check for * something that looks like a padding byte before aligning. If * we're already aligned it may be the leading byte of a 4-byte * header but then the att_align is harmless. Don't bother * looking if it's not a varlena though.*/ if (att[i]->attlen != -1 || !tp[off]) off = att_align(off, att[i]->attalign); if (usecache && att[i]->attlen != -1) att[i]->attcacheoff = off; } if (att[i]->attlen < 0) usecache = false; off = att_addlength(off, att[i]->attlen, PointerGetDatum(tp + off)); } if (att[attnum]->attlen != -1 || !tp[off]) off = att_align(off, att[attnum]->attalign); return fetchatt(att[attnum], tp + off); } }
/* ---------------- * nocache_index_getattr * * This gets called from index_getattr() macro, and only in cases * where we can't use 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 * ---------------- */ Datum nocache_index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc) { Form_pg_attribute *att = tupleDesc->attrs; char *tp; /* ptr to data part of tuple */ bits8 *bp = NULL; /* ptr to null bitmap in tuple */ bool slow = false; /* do we have to walk attrs? */ int data_off; /* tuple data offset */ 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. * ---------------- */ data_off = IndexInfoFindDataOffset(tup->t_info); attnum--; if (IndexTupleHasNulls(tup)) { /* * there's a null somewhere in the tuple * * check to see if desired att is null */ /* XXX "knows" t_bits are just after fixed tuple header! */ bp = (bits8 *) ((char *) tup + sizeof(IndexTupleData)); /* * Now 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 *) tup + data_off; if (!slow) { /* * 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. */ if (att[attnum]->attcacheoff >= 0) { return fetchatt(att[attnum], tp + att[attnum]->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 (IndexTupleHasVarwidths(tup)) { int j; for (j = 0; j <= attnum; j++) { if (att[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. */ att[0]->attcacheoff = 0; /* we might have set some offsets in the slow path previously */ while (j < natts && att[j]->attcacheoff > 0) j++; off = att[j - 1]->attcacheoff + att[j - 1]->attlen; for (; j < natts; j++) { if (att[j]->attlen <= 0) break; off = att_align_nominal(off, att[j]->attalign); att[j]->attcacheoff = off; off += att[j]->attlen; } Assert(j > attnum); off = att[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" */ { if (IndexTupleHasNulls(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[i]->attcacheoff >= 0) off = att[i]->attcacheoff; else if (att[i]->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[i]->attalign)) att[i]->attcacheoff = off; else { off = att_align_pointer(off, att[i]->attalign, -1, tp + off); usecache = false; } } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, att[i]->attalign); if (usecache) att[i]->attcacheoff = off; } if (i == attnum) break; off = att_addlength_pointer(off, att[i]->attlen, tp + off); if (usecache && att[i]->attlen <= 0) usecache = false; } } return fetchatt(att[attnum], tp + off); }
/* * heap_deformtuple * * Given a tuple, extract data into values/nulls arrays; this is * the inverse of heap_formtuple. * * Storage for the values/nulls 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. * * OLD API with char 'n'/' ' convention for indicating nulls. * This is deprecated and should not be used in new code, but we keep it * around for use by old add-on modules. */ void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, char *nulls) { 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 = 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 = att[attnum]; if (hasnulls && att_isnull(attnum, bp)) { values[attnum] = (Datum) 0; nulls[attnum] = 'n'; slow = true; /* can't use attcacheoff anymore */ continue; } nulls[attnum] = ' '; 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 null */ for (; attnum < tdesc_natts; attnum++) { values[attnum] = (Datum) 0; nulls[attnum] = 'n'; } }
/* * 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? */ Assert(!is_heaptuple_memtuple(tuple)); 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 = 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 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; } if (!slow && thisatt->attlen < 0) slow = true; values[attnum] = fetchatt(thisatt, tp + off); #ifdef USE_ASSERT_CHECKING /* Ignore attributes with dropped types */ if (thisatt->attlen == -1 && !thisatt->attisdropped) { Assert(VARATT_IS_SHORT(DatumGetPointer(values[attnum])) || !VARATT_CAN_MAKE_SHORT(DatumGetPointer(values[attnum])) || thisatt->atttypid == OIDVECTOROID || thisatt->atttypid == INT2VECTOROID || thisatt->atttypid >= FirstNormalObjectId); } #endif off = att_addlength_pointer(off, thisatt->attlen, tp + off); } /* * 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; } }
/* * load_auth_entries: read pg_authid into auth_entry[] * * auth_info_out: pointer to auth_entry * where address to auth_entry[] should be stored * total_roles_out: pointer to int where num of total roles should be stored */ static void load_auth_entries(Relation rel_authid, auth_entry **auth_info_out, int *total_roles_out) { BlockNumber totalblocks; HeapScanDesc scan; HeapTuple tuple; int curr_role = 0; int total_roles = 0; int est_rows; auth_entry *auth_info; /* * Read pg_authid and fill temporary data structures. Note we must read * all roles, even those without rolcanlogin. */ totalblocks = RelationGetNumberOfBlocks(rel_authid); totalblocks = totalblocks ? totalblocks : 1; est_rows = totalblocks * (BLCKSZ / (sizeof(HeapTupleHeaderData) + sizeof(FormData_pg_authid))); auth_info = (auth_entry *) palloc(est_rows * sizeof(auth_entry)); scan = heap_beginscan(rel_authid, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_authid aform = (Form_pg_authid) GETSTRUCT(tuple); HeapTupleHeader tup = tuple->t_data; char *tp; /* ptr to tuple data */ long off; /* offset in tuple data */ bits8 *bp = tup->t_bits; /* ptr to null bitmask in tuple */ Datum datum; if (curr_role >= est_rows) { est_rows *= 2; auth_info = (auth_entry *) repalloc(auth_info, est_rows * sizeof(auth_entry)); } auth_info[curr_role].roleid = HeapTupleGetOid(tuple); auth_info[curr_role].rolsuper = aform->rolsuper; auth_info[curr_role].rolcanlogin = aform->rolcanlogin; auth_info[curr_role].rolname = pstrdup(NameStr(aform->rolname)); auth_info[curr_role].member_of = NIL; /* * We can't use heap_getattr() here because during startup we will not * have any tupdesc for pg_authid. Fortunately it's not too hard to * work around this. rolpassword is the first possibly-null field so * we can compute its offset directly. */ tp = (char *) tup + tup->t_hoff; off = offsetof(FormData_pg_authid, rolpassword); if (HeapTupleHasNulls(tuple) && att_isnull(Anum_pg_authid_rolpassword - 1, bp)) { /* passwd is null, emit as an empty string */ auth_info[curr_role].rolpassword = pstrdup(""); } else { /* assume passwd is pass-by-ref */ datum = PointerGetDatum(tp + off); /* * The password probably shouldn't ever be out-of-line toasted; if * it is, ignore it, since we can't handle that in startup mode. */ if (VARATT_IS_EXTERNAL(DatumGetPointer(datum))) auth_info[curr_role].rolpassword = pstrdup(""); else auth_info[curr_role].rolpassword = DatumGetCString(DirectFunctionCall1(textout, datum)); /* assume passwd has attlen -1 */ off = att_addlength(off, -1, PointerGetDatum(tp + off)); } if (HeapTupleHasNulls(tuple) && att_isnull(Anum_pg_authid_rolvaliduntil - 1, bp)) { /* rolvaliduntil is null, emit as an empty string */ auth_info[curr_role].rolvaliduntil = pstrdup(""); } else { /* * rolvaliduntil is timestamptz, which we assume is double * alignment and pass-by-value. */ off = att_align(off, 'd'); datum = fetch_att(tp + off, true, sizeof(TimestampTz)); auth_info[curr_role].rolvaliduntil = DatumGetCString(DirectFunctionCall1(timestamptz_out, datum)); } /* * Check for illegal characters in the user name and password. */ if (!name_okay(auth_info[curr_role].rolname)) { ereport(LOG, (errmsg("invalid role name \"%s\"", auth_info[curr_role].rolname))); continue; } if (!name_okay(auth_info[curr_role].rolpassword)) { ereport(LOG, (errmsg("invalid role password \"%s\"", auth_info[curr_role].rolpassword))); continue; } curr_role++; total_roles++; } heap_endscan(scan); *auth_info_out = auth_info; *total_roles_out = total_roles; }
/* * brin_deconstruct_tuple * Guts of attribute extraction from an on-disk BRIN tuple. * * Its arguments are: * brdesc BRIN descriptor for the stored tuple * tp pointer to the tuple data area * nullbits pointer to the tuple nulls bitmask * nulls "has nulls" bit in tuple infomask * values output values, array of size brdesc->bd_totalstored * allnulls output "allnulls", size brdesc->bd_tupdesc->natts * hasnulls output "hasnulls", size brdesc->bd_tupdesc->natts * * Output arrays must have been allocated by caller. */ static inline void brin_deconstruct_tuple(BrinDesc *brdesc, char *tp, bits8 *nullbits, bool nulls, Datum *values, bool *allnulls, bool *hasnulls) { int attnum; int stored; TupleDesc diskdsc; long off; /* * First iterate to natts to obtain both null flags for each attribute. * Note that we reverse the sense of the att_isnull test, because we store * 1 for a null value (rather than a 1 for a not null value as is the * att_isnull convention used elsewhere.) See brin_form_tuple. */ for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++) { /* * the "all nulls" bit means that all values in the page range for * this column are nulls. Therefore there are no values in the tuple * data area. */ allnulls[attnum] = nulls && !att_isnull(attnum, nullbits); /* * the "has nulls" bit means that some tuples have nulls, but others * have not-null values. Therefore we know the tuple contains data * for this column. * * The hasnulls bits follow the allnulls bits in the same bitmask. */ hasnulls[attnum] = nulls && !att_isnull(brdesc->bd_tupdesc->natts + attnum, nullbits); } /* * Iterate to obtain each attribute's stored values. Note that since we * may reuse attribute entries for more than one column, we cannot cache * offsets here. */ diskdsc = brtuple_disk_tupdesc(brdesc); stored = 0; off = 0; for (attnum = 0; attnum < brdesc->bd_tupdesc->natts; attnum++) { int datumno; if (allnulls[attnum]) { stored += brdesc->bd_info[attnum]->oi_nstored; continue; } for (datumno = 0; datumno < brdesc->bd_info[attnum]->oi_nstored; datumno++) { Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored); if (thisatt->attlen == -1) { off = att_align_pointer(off, thisatt->attalign, -1, tp + off); } else { /* not varlena, so safe to use att_align_nominal */ off = att_align_nominal(off, thisatt->attalign); } values[stored++] = fetchatt(thisatt, tp + off); off = att_addlength_pointer(off, thisatt->attlen, tp + off); } } }
/* * 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). * * 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->tts_nvalid is the number of attributes already extracted. */ static void slot_deform_tuple(TupleTableSlot *slot, int natts) { HeapTuple tuple = TupGetHeapTuple(slot); TupleDesc tupleDesc = slot->tts_tupleDescriptor; Datum *values = slot->PRIVATE_tts_values; bool *isnull = slot->PRIVATE_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 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->PRIVATE_tts_nvalid; if (attnum == 0) { /* Start from the first attribute */ off = 0; slow = false; } else { /* Restore state from previous execution */ off = slot->PRIVATE_tts_off; slow = slot->PRIVATE_tts_slow; } tp = (char *) tup + tup->t_hoff; for (; 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 { /* if it's a varlena it may or may not be aligned, so check for * something that looks like a padding byte before aligning. If * we're already aligned it may be the leading byte of a 4-byte * header but then the att_align is harmless. Don't bother looking * if it's not a varlena though.*/ if (thisatt->attlen != -1 || !tp[off]) off = att_align(off, thisatt->attalign); if (!slow && thisatt->attlen != -1) thisatt->attcacheoff = off; } if (!slow && thisatt->attlen < 0) slow = true; values[attnum] = fetchatt(thisatt, tp + off); off = att_addlength(off, thisatt->attlen, PointerGetDatum(tp + off)); } /* * Save state for next execution */ slot->PRIVATE_tts_nvalid = attnum; slot->PRIVATE_tts_off = off; slot->PRIVATE_tts_slow = slow; }