From 9624034b6d23f7c5812b87bed705cbf2ae781d36 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Sun, 20 Jun 2021 21:16:33 +0200 Subject: [PATCH] Compact TupleDesc->attr access descriptors Evolved from previous iterations, and includes some further work based on comments from Andres Freund [0] [0] https://www.postgresql.org/message-id/20210819125756.lhcp5wlppfv2r47d%40alap3.anarazel.de --- src/backend/access/common/heaptuple.c | 95 +++++++++++++------------ src/backend/access/common/indextuple.c | 52 ++++++++------ src/backend/access/common/relation.c | 2 + src/backend/access/common/tupdesc.c | 73 ++++++++++++++++++- src/backend/access/heap/heapam.c | 6 +- src/backend/access/heap/heaptoast.c | 12 ++-- src/backend/access/nbtree/nbtutils.c | 6 +- src/backend/access/spgist/spgdoinsert.c | 2 +- src/backend/access/spgist/spgutils.c | 3 + src/backend/access/table/toast_helper.c | 6 +- src/backend/catalog/heap.c | 10 +-- src/backend/catalog/index.c | 2 + src/backend/catalog/toasting.c | 4 ++ src/backend/executor/execTuples.c | 10 +-- src/backend/executor/nodeValuesscan.c | 4 +- src/backend/utils/adt/expandedrecord.c | 14 ++-- src/backend/utils/adt/ri_triggers.c | 2 +- src/backend/utils/cache/catcache.c | 6 +- src/backend/utils/cache/relcache.c | 18 ++++- src/include/access/htup_details.h | 54 +++++++------- src/include/access/itup.h | 9 +-- src/include/access/tupdesc.h | 42 +++++++++-- src/include/access/tupmacs.h | 2 +- src/include/catalog/pg_attribute.h | 5 ++ 24 files changed, 297 insertions(+), 142 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 0b56b0fa5a..3600f88b25 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -123,19 +123,18 @@ heap_compute_data_size(TupleDesc tupleDesc, Size data_length = 0; int i; int numberOfAttributes = tupleDesc->natts; + TupleAttrAlign att; - for (i = 0; i < numberOfAttributes; i++) + for (i = 0, att = TupleDescAttrAlign(tupleDesc, i); i < numberOfAttributes; i++, att++) { Datum val; - Form_pg_attribute atti; if (isnull[i]) continue; val = values[i]; - atti = TupleDescAttr(tupleDesc, i); - if (ATT_IS_PACKABLE(atti) && + if (ATT_IS_PACKABLE(att) && VARATT_CAN_MAKE_SHORT(DatumGetPointer(val))) { /* @@ -144,21 +143,21 @@ heap_compute_data_size(TupleDesc tupleDesc, */ data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val)); } - else if (atti->attlen == -1 && + else if (att->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 = att_align_nominal(data_length, att->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, + data_length = att_align_datum(data_length, att->attalign, + att->attlen, val); + data_length = att_addlength_datum(data_length, att->attlen, val); } } @@ -172,7 +171,7 @@ heap_compute_data_size(TupleDesc tupleDesc, * Fill in either a data value or a bit in the null bitmask */ static inline void -fill_val(Form_pg_attribute att, +fill_val(TupleAttrAlign att, bits8 **bit, int *bitmask, char **dataP, @@ -211,7 +210,7 @@ fill_val(Form_pg_attribute att, * 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) + if (att->attbyval && att->attlen >= 0) { /* pass-by-value */ data = (char *) att_align_nominal(data, att->attalign); @@ -310,6 +309,7 @@ heap_fill_tuple(TupleDesc tupleDesc, int bitmask; int i; int numberOfAttributes = tupleDesc->natts; + TupleAttrAlign attr; #ifdef USE_ASSERT_CHECKING char *start = data; @@ -329,10 +329,8 @@ heap_fill_tuple(TupleDesc tupleDesc, *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL); - for (i = 0; i < numberOfAttributes; i++) + for (i = 0, attr = TupleDescAttrAlign(tupleDesc, i); i < numberOfAttributes; i++, attr++) { - Form_pg_attribute attr = TupleDescAttr(tupleDesc, i); - fill_val(attr, bitP ? &bitP : NULL, &bitmask, @@ -429,6 +427,7 @@ nocachegetattr(HeapTuple tuple, bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */ bool slow = false; /* do we have to walk attrs? */ int off; /* current offset within data */ + TupleAttrAlign thisAttrAlign; /* ---------------- * Three cases: @@ -474,13 +473,13 @@ nocachegetattr(HeapTuple tuple, if (!slow) { - Form_pg_attribute att; + TupleAttrAlign 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); + att = TupleDescAttrAlign(tupleDesc, attnum); if (att->attcacheoff >= 0) return fetchatt(att, tp + att->attcacheoff); @@ -492,10 +491,11 @@ nocachegetattr(HeapTuple tuple, if (HeapTupleHasVarWidth(tuple)) { int j; + att = TupleDescAttrAlign(tupleDesc, 0); - for (j = 0; j <= attnum; j++) + for (j = 0; j <= attnum; j++, att++) { - if (TupleDescAttr(tupleDesc, j)->attlen <= 0) + if (att->attlen <= 0) { slow = true; break; @@ -508,6 +508,7 @@ nocachegetattr(HeapTuple tuple, { int natts = tupleDesc->natts; int j = 1; + TupleAttrAlign att; /* * If we get here, we have a tuple with no nulls or var-widths up to @@ -518,19 +519,18 @@ nocachegetattr(HeapTuple tuple, * fixed-width columns, in hope of avoiding future visits to this * routine. */ - TupleDescAttr(tupleDesc, 0)->attcacheoff = 0; + att = TupleDescAttrAlign(tupleDesc, 0); + att->attcacheoff = 0; + att++; /* we might have set some offsets in the slow path previously */ - while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0) - j++; + while (j < natts && att->attcacheoff > 0) + j++, att++; - off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff + - TupleDescAttr(tupleDesc, j - 1)->attlen; + off = (att - 1)->attcacheoff + (att - 1)->attlen; - for (; j < natts; j++) + for (; j < natts; j++, att++) { - Form_pg_attribute att = TupleDescAttr(tupleDesc, j); - if (att->attlen <= 0) break; @@ -543,12 +543,14 @@ nocachegetattr(HeapTuple tuple, Assert(j > attnum); - off = TupleDescAttr(tupleDesc, attnum)->attcacheoff; + thisAttrAlign = TupleDescAttrAlign(tupleDesc, attnum); + off = thisAttrAlign->attcacheoff; } else { bool usecache = true; int i; + TupleAttrAlign att = TupleDescAttrAlign(tupleDesc, 0); /* * Now we know that we have to walk the tuple CAREFULLY. But we still @@ -561,10 +563,8 @@ nocachegetattr(HeapTuple tuple, * attcacheoff until we reach either a null or a var-width attribute. */ off = 0; - for (i = 0;; i++) /* loop exit is at "break" */ + for (i = 0;; i++, att++) /* loop exit is at "break" */ { - Form_pg_attribute att = TupleDescAttr(tupleDesc, i); - if (HeapTupleHasNulls(tuple) && att_isnull(i, bp)) { usecache = false; @@ -602,7 +602,10 @@ nocachegetattr(HeapTuple tuple, } if (i == attnum) + { + thisAttrAlign = att; break; + } off = att_addlength_pointer(off, att->attlen, tp + off); @@ -611,7 +614,7 @@ nocachegetattr(HeapTuple tuple, } } - return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off); + return fetchatt(thisAttrAlign, tp + off); } /* ---------------- @@ -925,7 +928,7 @@ expand_tuple(HeapTuple *targetHeapTuple, for (attnum = sourceNatts; attnum < natts; attnum++) { - Form_pg_attribute attr = TupleDescAttr(tupleDesc, attnum); + TupleAttrAlign attr = TupleDescAttrAlign(tupleDesc, attnum); if (attrmiss && attrmiss[attnum].am_present) { @@ -1258,6 +1261,7 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, 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? */ + TupleAttrAlign att; natts = HeapTupleHeaderGetNatts(tup); @@ -1272,10 +1276,9 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, off = 0; - for (attnum = 0; attnum < natts; attnum++) + for (attnum = 0, att = TupleDescAttrAlign(tupleDesc, attnum); + attnum < natts; attnum++, att++) { - Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); - if (hasnulls && att_isnull(attnum, bp)) { values[attnum] = (Datum) 0; @@ -1286,9 +1289,9 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, isnull[attnum] = false; - if (!slow && thisatt->attcacheoff >= 0) - off = thisatt->attcacheoff; - else if (thisatt->attlen == -1) + if (!slow && att->attcacheoff >= 0) + off = att->attcacheoff; + else if (att->attlen == -1) { /* * We can only cache the offset for a varlena attribute if the @@ -1297,11 +1300,11 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, * an aligned or unaligned value. */ if (!slow && - off == att_align_nominal(off, thisatt->attalign)) - thisatt->attcacheoff = off; + off == att_align_nominal(off, att->attalign)) + att->attcacheoff = off; else { - off = att_align_pointer(off, thisatt->attalign, -1, + off = att_align_pointer(off, att->attalign, -1, tp + off); slow = true; } @@ -1309,17 +1312,17 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, else { /* not varlena, so safe to use att_align_nominal */ - off = att_align_nominal(off, thisatt->attalign); + off = att_align_nominal(off, att->attalign); if (!slow) - thisatt->attcacheoff = off; + att->attcacheoff = off; } - values[attnum] = fetchatt(thisatt, tp + off); + values[attnum] = fetchatt(att, tp + off); - off = att_addlength_pointer(off, thisatt->attlen, tp + off); + off = att_addlength_pointer(off, att->attlen, tp + off); - if (thisatt->attlen <= 0) + if (att->attlen <= 0) slow = true; /* can't use attcacheoff anymore */ } diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c index 8df882da7a..b2c08216db 100644 --- a/src/backend/access/common/indextuple.c +++ b/src/backend/access/common/indextuple.c @@ -228,7 +228,7 @@ nocache_index_getattr(IndexTuple tup, bool slow = false; /* do we have to walk attrs? */ int data_off; /* tuple data offset */ int off; /* current offset within data */ - + TupleAttrAlign thisAttrAlign; /* ---------------- * Three cases: * @@ -242,6 +242,8 @@ nocache_index_getattr(IndexTuple tup, attnum--; + Assert(TupleDescAttrAlignsIsConsistent(tupleDesc)); + if (IndexTupleHasNulls(tup)) { /* @@ -284,13 +286,13 @@ nocache_index_getattr(IndexTuple tup, if (!slow) { - Form_pg_attribute att; + TupleAttrAlign 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); + att = TupleDescAttrAlign(tupleDesc, attnum); if (att->attcacheoff >= 0) return fetchatt(att, tp + att->attcacheoff); @@ -302,10 +304,11 @@ nocache_index_getattr(IndexTuple tup, if (IndexTupleHasVarwidths(tup)) { int j; + att = TupleDescAttrAlign(tupleDesc, 0); - for (j = 0; j <= attnum; j++) + for (j = 0; j <= attnum; j++, att++) { - if (TupleDescAttr(tupleDesc, j)->attlen <= 0) + if (att->attlen <= 0) { slow = true; break; @@ -318,6 +321,7 @@ nocache_index_getattr(IndexTuple tup, { int natts = tupleDesc->natts; int j = 1; + TupleAttrAlign att; /* * If we get here, we have a tuple with no nulls or var-widths up to @@ -328,19 +332,18 @@ nocache_index_getattr(IndexTuple tup, * fixed-width columns, in hope of avoiding future visits to this * routine. */ - TupleDescAttr(tupleDesc, 0)->attcacheoff = 0; + att = TupleDescAttrAlign(tupleDesc, 0); + att->attcacheoff = 0; + att++; /* we might have set some offsets in the slow path previously */ - while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0) - j++; + while (j < natts && att->attcacheoff > 0) + j++, att++; - off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff + - TupleDescAttr(tupleDesc, j - 1)->attlen; + off = (att - 1)->attcacheoff + (att - 1)->attlen; - for (; j < natts; j++) + for (; j < natts; j++, att++) { - Form_pg_attribute att = TupleDescAttr(tupleDesc, j); - if (att->attlen <= 0) break; @@ -353,13 +356,14 @@ nocache_index_getattr(IndexTuple tup, Assert(j > attnum); - off = TupleDescAttr(tupleDesc, attnum)->attcacheoff; + thisAttrAlign = TupleDescAttrAlign(tupleDesc, attnum); + off = thisAttrAlign->attcacheoff; } else { bool usecache = true; int i; - + TupleAttrAlign att; /* * Now we know that we have to walk the tuple CAREFULLY. But we still * might be able to cache some offsets for next time. @@ -371,10 +375,10 @@ nocache_index_getattr(IndexTuple tup, * 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); + att = TupleDescAttrAlign(tupleDesc, 0); + for (i = 0;; i++, att++) /* loop exit is at "break" */ + { if (IndexTupleHasNulls(tup) && att_isnull(i, bp)) { usecache = false; @@ -412,7 +416,10 @@ nocache_index_getattr(IndexTuple tup, } if (i == attnum) + { + thisAttrAlign = att; break; + } off = att_addlength_pointer(off, att->attlen, tp + off); @@ -421,7 +428,7 @@ nocache_index_getattr(IndexTuple tup, } } - return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off); + return fetchatt(thisAttrAlign, tp + off); } /* @@ -465,14 +472,14 @@ index_deform_tuple_internal(TupleDesc tupleDescriptor, int attnum; int off = 0; /* offset in tuple data */ bool slow = false; /* can we use/set attcacheoff? */ + TupleAttrAlign thisatt; /* Assert to protect callers who allocate fixed-size arrays */ Assert(natts <= INDEX_MAX_KEYS); - for (attnum = 0; attnum < natts; attnum++) + for (attnum = 0, thisatt = TupleDescAttrAlign(tupleDescriptor, attnum); + attnum < natts; attnum++, thisatt++) { - Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, attnum); - if (hasnulls && att_isnull(attnum, bp)) { values[attnum] = (Datum) 0; @@ -572,6 +579,7 @@ index_truncate_tuple(TupleDesc sourceDescriptor, IndexTuple source, truncdesc = palloc(TupleDescSize(sourceDescriptor)); TupleDescCopy(truncdesc, sourceDescriptor); truncdesc->natts = leavenatts; + TupleDescInitAttrAligns(truncdesc); /* Deform, form copy of tuple with fewer attributes */ index_deform_tuple(source, truncdesc, values, isnull); diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c index 632d13c1ea..629d60b947 100644 --- a/src/backend/access/common/relation.c +++ b/src/backend/access/common/relation.c @@ -61,6 +61,8 @@ relation_open(Oid relationId, LOCKMODE lockmode) if (!RelationIsValid(r)) elog(ERROR, "could not open relation with OID %u", relationId); + Assert(TupleDescAttrAlignsIsConsistent(r->rd_att)); + /* * If we didn't get the lock ourselves, assert that caller holds one, * except in bootstrap mode where no locks are used. diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 4c63bd4dc6..ecc4160dd9 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -63,7 +63,8 @@ CreateTemplateTupleDesc(int natts) * could be less due to trailing padding, although with the current * definition of pg_attribute there probably isn't any padding. */ - desc = (TupleDesc) palloc(offsetof(struct TupleDescData, attrs) + + desc = (TupleDesc) palloc(MAXALIGN((offsetof(struct TupleDescData, attrs)) + + natts * sizeof(TupleAttrAlignData)) + natts * sizeof(FormData_pg_attribute)); /* @@ -75,6 +76,9 @@ CreateTemplateTupleDesc(int natts) desc->tdtypmod = -1; desc->tdrefcount = -1; /* assume not reference-counted */ + desc->attroff = MAXALIGN((offsetof(struct TupleDescData, attrs)) + + natts * sizeof(TupleAttrAlignData)); + return desc; } @@ -97,6 +101,8 @@ CreateTupleDesc(int natts, Form_pg_attribute *attrs) for (i = 0; i < natts; ++i) memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE); + TupleDescInitAttrAligns(desc); + return desc; } @@ -135,6 +141,8 @@ CreateTupleDescCopy(TupleDesc tupdesc) att->attgenerated = '\0'; } + TupleDescInitAttrAligns(desc); + /* We can copy the tuple type identification, too */ desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; @@ -210,6 +218,10 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) desc->constr = cpy; } + memcpy(TupleDescAttrAlign(desc, 0), + TupleDescAttrAlign(tupdesc, 0), + tupdesc->natts * sizeof(TupleAttrAlignData)); + /* We can copy the tuple type identification, too */ desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; @@ -300,6 +312,10 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno, dstAtt->atthasmissing = false; dstAtt->attidentity = '\0'; dstAtt->attgenerated = '\0'; + + memcpy(TupleDescAttrAlign(dst, dstAttno - 1), + TupleDescAttrAlign(src, srcAttno - 1), + sizeof(TupleAttrAlignData)); } /* @@ -645,6 +661,8 @@ TupleDescInitEntry(TupleDesc desc, att->attcompression = InvalidCompressionMethod; att->attcollation = typeForm->typcollation; + CopyAttrToAlign(att, TupleDescAttrAlign(desc, attributeNumber - 1)); + ReleaseSysCache(tuple); } @@ -662,6 +680,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc, int attdim) { Form_pg_attribute att; + TupleAttrAlign attrAlign; /* sanity checks */ AssertArg(PointerIsValid(desc)); @@ -742,6 +761,10 @@ TupleDescInitBuiltinEntry(TupleDesc desc, default: elog(ERROR, "unsupported type %u", oidtypeid); } + + attrAlign = TupleDescAttrAlign(desc, attributeNumber - 1); + + CopyAttrToAlign(att, attrAlign); } /* @@ -765,6 +788,49 @@ TupleDescInitEntryCollation(TupleDesc desc, TupleDescAttr(desc, attributeNumber - 1)->attcollation = collationid; } +/* + * TupleDescInitAttrAligns + * + * Initialize the attraligns field of the TupleDesc with data from + * tupledesc->attrs. + */ +void +TupleDescInitAttrAligns(TupleDesc desc) +{ + for (int i = 0; i < desc->natts; i++) + { + Form_pg_attribute attr = TupleDescAttr(desc, i); + TupleAttrAlign attrAlign = TupleDescAttrAlign(desc, i); + CopyAttrToAlign(attr, attrAlign); + } +} + +bool +TupleDescAttrAlignsIsConsistent(TupleDesc desc) +{ + for (int i = 0; i < desc->natts; i++) + { + Form_pg_attribute attr = TupleDescAttr(desc, i); + TupleAttrAlign attrAlign = TupleDescAttrAlign(desc, i); + if (!(attrAlign->attcacheoff == attr->attcacheoff || attrAlign->attcacheoff == -1 || attr->attcacheoff == -1)) + return false; + if (attrAlign->attlen != attr->attlen) + return false; + if (attrAlign->attalign != attr->attalign) + return false; + if (attrAlign->attlen < 0) + { + if (attrAlign->attstorage != attr->attstorage) + return false; + } + else + { + if (attrAlign->attbyval != attr->attbyval) + return false; + } + } + return true; +} /* * BuildDescForRelation @@ -801,6 +867,7 @@ BuildDescForRelation(List *schema) ColumnDef *entry = lfirst(l); AclResult aclresult; Form_pg_attribute att; + TupleAttrAlign align; /* * for each entry in the list, get the name and type information from @@ -828,11 +895,15 @@ BuildDescForRelation(List *schema) TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, attdim); att = TupleDescAttr(desc, attnum - 1); + align = TupleDescAttrAlign(desc, attnum - 1); /* Override TupleDescInitEntry's settings as requested */ TupleDescInitEntryCollation(desc, attnum, attcollation); if (entry->storage) + { att->attstorage = entry->storage; + align->attstorage = entry->storage; + } /* Fill in additional stuff not handled by TupleDescInitEntry */ att->attnotnull = entry->is_not_null; diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 2433998f39..59f8f97697 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -1144,11 +1144,11 @@ fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, (*(isnull) = false), HeapTupleNoNulls(tup) ? ( - TupleDescAttr((tupleDesc), (attnum) - 1)->attcacheoff >= 0 ? + TupleDescAttrAlign((tupleDesc), (attnum) - 1)->attcacheoff >= 0 ? ( - fetchatt(TupleDescAttr((tupleDesc), (attnum) - 1), + fetchatt(TupleDescAttrAlign((tupleDesc), (attnum) - 1), (char *) (tup)->t_data + (tup)->t_data->t_hoff + - TupleDescAttr((tupleDesc), (attnum) - 1)->attcacheoff) + TupleDescAttrAlign((tupleDesc), (attnum) - 1)->attcacheoff) ) : nocachegetattr((tup), (attnum), (tupleDesc)) diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..e5fcbe15b8 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -190,10 +190,14 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, if (biggest_attno < 0) break; + Assert(TupleDescAttrAlign(tupleDesc, biggest_attno)->attlen < 0); + /* * Attempt to compress it inline, if it has attstorage EXTENDED + * + * Safe to use attstorage, attlen < 0 */ - if (TupleDescAttr(tupleDesc, biggest_attno)->attstorage == TYPSTORAGE_EXTENDED) + if (TupleDescAttrAlign(tupleDesc, biggest_attno)->attstorage == TYPSTORAGE_EXTENDED) toast_tuple_try_compression(&ttc, biggest_attno); else { @@ -369,7 +373,7 @@ toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc) /* * Look at non-null varlena attributes */ - if (!toast_isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1) + if (!toast_isnull[i] && TupleDescAttrAlign(tupleDesc, i)->attlen == -1) { struct varlena *new_value; @@ -483,7 +487,7 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup, */ if (toast_isnull[i]) has_nulls = true; - else if (TupleDescAttr(tupleDesc, i)->attlen == -1) + else if (TupleDescAttrAlign(tupleDesc, i)->attlen == -1) { struct varlena *new_value; @@ -584,7 +588,7 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, /* * Look at non-null varlena attributes */ - if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1) + if (!isnull[i] && TupleDescAttrAlign(tupleDesc, i)->attlen == -1) { struct varlena *new_value; diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index d524310723..49611bd81f 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -2429,17 +2429,17 @@ _bt_keep_natts_fast(Relation rel, IndexTuple lastleft, IndexTuple firstright) datum2; bool isNull1, isNull2; - Form_pg_attribute att; + TupleAttrAlign att; datum1 = index_getattr(lastleft, attnum, itupdesc, &isNull1); datum2 = index_getattr(firstright, attnum, itupdesc, &isNull2); - att = TupleDescAttr(itupdesc, attnum - 1); + att = TupleDescAttrAlign(itupdesc, attnum - 1); if (isNull1 != isNull2) break; if (!isNull1 && - !datum_image_eq(datum1, datum2, att->attbyval, att->attlen)) + !datum_image_eq(datum1, datum2, att->attlen >= 0 && att->attbyval, att->attlen)) break; keepnatts++; diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c index 70557bcf3d..357320073d 100644 --- a/src/backend/access/spgist/spgdoinsert.c +++ b/src/backend/access/spgist/spgdoinsert.c @@ -1973,7 +1973,7 @@ spgdoinsert(Relation index, SpGistState *state, { if (!isnulls[i]) { - if (TupleDescAttr(leafDescriptor, i)->attlen == -1) + if (TupleDescAttrAlign(leafDescriptor, i)->attlen == -1) leafDatums[i] = PointerGetDatum(PG_DETOAST_DATUM(datums[i])); else leafDatums[i] = datums[i]; diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index 03a9cd36e6..190978f403 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -306,9 +306,12 @@ getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType) /* We shouldn't need to bother with making these valid: */ att->attcompression = InvalidCompressionMethod; att->attcollation = InvalidOid; + /* In case we changed typlen, we'd better reset following offsets */ for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++) TupleDescAttr(outTupDesc, i)->attcacheoff = -1; + + TupleDescInitAttrAligns(outTupDesc); } return outTupDesc; } diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c index 53f78f9c3e..cfe2fb4260 100644 --- a/src/backend/access/table/toast_helper.c +++ b/src/backend/access/table/toast_helper.c @@ -193,7 +193,7 @@ toast_tuple_find_biggest_attribute(ToastTupleContext *ttc, for (i = 0; i < numAttrs; i++) { - Form_pg_attribute att = TupleDescAttr(tupleDesc, i); + TupleAttrAlign att = TupleDescAttrAlign(tupleDesc, i); if ((ttc->ttc_attr[i].tai_colflags & skip_colflags) != 0) continue; @@ -202,6 +202,8 @@ toast_tuple_find_biggest_attribute(ToastTupleContext *ttc, if (for_compression && VARATT_IS_COMPRESSED(DatumGetPointer(ttc->ttc_values[i]))) continue; + if (att->attlen >= 0) + continue; /* can't compress fixed-size attributes */ if (check_main && att->attstorage != TYPSTORAGE_MAIN) continue; if (!check_main && att->attstorage != TYPSTORAGE_EXTENDED && @@ -324,7 +326,7 @@ toast_delete_external(Relation rel, Datum *values, bool *isnull, for (i = 0; i < numAttrs; i++) { - if (TupleDescAttr(tupleDesc, i)->attlen == -1) + if (TupleDescAttrAlign(tupleDesc, i)->attlen == -1) { Datum value = values[i]; diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 83746d3fd9..bd688e4156 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -859,7 +859,7 @@ AddNewAttributeTuples(Oid new_rel_oid, /* set stats detail level to a sane default */ for (int i = 0; i < natts; i++) - tupdesc->attrs[i].attstattarget = -1; + TupleDescAttr(tupdesc, i)->attstattarget = -1; InsertPgAttributeTuples(rel, tupdesc, new_rel_oid, NULL, indstate); /* add dependencies on their datatypes and collations */ @@ -868,15 +868,15 @@ AddNewAttributeTuples(Oid new_rel_oid, /* Add dependency info */ ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1); ObjectAddressSet(referenced, TypeRelationId, - tupdesc->attrs[i].atttypid); + TupleDescAttr(tupdesc, i)->atttypid); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* The default collation is pinned, so don't bother recording it */ - if (OidIsValid(tupdesc->attrs[i].attcollation) && - tupdesc->attrs[i].attcollation != DEFAULT_COLLATION_OID) + if (OidIsValid(TupleDescAttr(tupdesc, i)->attcollation) && + TupleDescAttr(tupdesc, i)->attcollation != DEFAULT_COLLATION_OID) { ObjectAddressSet(referenced, CollationRelationId, - tupdesc->attrs[i].attcollation); + TupleDescAttr(tupdesc, i)->attcollation); recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 26bfa74ce7..b96a4fccf1 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -471,6 +471,8 @@ ConstructTupleDescriptor(Relation heapRelation, } } + TupleDescInitAttrAligns(indexTupDesc); + pfree(amroutine); return indexTupDesc; diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 147b5abc19..efe7b7be26 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -219,6 +219,10 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, TupleDescAttr(tupdesc, 1)->attstorage = TYPSTORAGE_PLAIN; TupleDescAttr(tupdesc, 2)->attstorage = TYPSTORAGE_PLAIN; + CopyAttrToAlign(TupleDescAttr(tupdesc, 0), TupleDescAttrAlign(tupdesc, 0)); + CopyAttrToAlign(TupleDescAttr(tupdesc, 1), TupleDescAttrAlign(tupdesc, 1)); + CopyAttrToAlign(TupleDescAttr(tupdesc, 2), TupleDescAttrAlign(tupdesc, 2)); + /* Toast field should not be compressed */ TupleDescAttr(tupdesc, 0)->attcompression = InvalidCompressionMethod; TupleDescAttr(tupdesc, 1)->attcompression = InvalidCompressionMethod; diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index f447802843..29e978b7d3 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -170,10 +170,10 @@ tts_virtual_materialize(TupleTableSlot *slot) /* compute size of memory required */ for (int natt = 0; natt < desc->natts; natt++) { - Form_pg_attribute att = TupleDescAttr(desc, natt); + TupleAttrAlign att = TupleDescAttrAlign(desc, natt); Datum val; - if (att->attbyval || slot->tts_isnull[natt]) + if ((att->attlen >= 0 && att->attbyval) || slot->tts_isnull[natt]) continue; val = slot->tts_values[natt]; @@ -206,10 +206,10 @@ tts_virtual_materialize(TupleTableSlot *slot) /* and copy all attributes into the pre-allocated space */ for (int natt = 0; natt < desc->natts; natt++) { - Form_pg_attribute att = TupleDescAttr(desc, natt); + TupleAttrAlign att = TupleDescAttrAlign(desc, natt); Datum val; - if (att->attbyval || slot->tts_isnull[natt]) + if ((att->attlen >= 0 && att->attbyval) || slot->tts_isnull[natt]) continue; val = slot->tts_values[natt]; @@ -961,7 +961,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, for (; attnum < natts; attnum++) { - Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); + TupleAttrAlign thisatt = TupleDescAttrAlign(tupleDesc, attnum); if (hasnulls && att_isnull(attnum, bp)) { diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 00bd1271e4..b1cb02da07 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -143,8 +143,8 @@ ValuesNext(ValuesScanState *node) foreach(lc, exprstatelist) { ExprState *estate = (ExprState *) lfirst(lc); - Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, - resind); + TupleAttrAlign attr = TupleDescAttrAlign(slot->tts_tupleDescriptor, + resind); values[resind] = ExecEvalExpr(estate, econtext, diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..c17f4580b9 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -546,8 +546,10 @@ expanded_record_set_tuple(ExpandedRecordHeader *erh, for (i = 0; i < erh->nfields; i++) { + TupleAttrAlign align = TupleDescAttrAlign(tupdesc, i); + if (!erh->dnulls[i] && - !(TupleDescAttr(tupdesc, i)->attbyval)) + !(align->attlen >= 0 && align->attbyval)) { char *oldValue = (char *) DatumGetPointer(erh->dvalues[i]); @@ -699,7 +701,7 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) { for (i = 0; i < erh->nfields; i++) { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + TupleAttrAlign attr = TupleDescAttrAlign(tupdesc, i); if (!erh->dnulls[i] && !attr->attbyval && attr->attlen == -1 && @@ -1115,7 +1117,7 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, bool check_constraints) { TupleDesc tupdesc; - Form_pg_attribute attr; + TupleAttrAlign attr; Datum *dvalues; bool *dnulls; char *oldValue; @@ -1146,8 +1148,8 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, * Copy new field value into record's context, and deal with detoasting, * if needed. */ - attr = TupleDescAttr(tupdesc, fnumber - 1); - if (!isnull && !attr->attbyval) + attr = TupleDescAttrAlign(tupdesc, fnumber - 1); + if (!isnull && !(attr->attlen >= 0 && attr->attbyval)) { MemoryContext oldcxt; @@ -1541,7 +1543,7 @@ check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber, */ if (!isnull) { - Form_pg_attribute attr = TupleDescAttr(erh->er_tupdesc, fnumber - 1); + TupleAttrAlign attr = TupleDescAttrAlign(erh->er_tupdesc, fnumber - 1); if (!attr->attbyval && attr->attlen == -1 && VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 96269fc2ad..a18d662262 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -2762,7 +2762,7 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, * difference for ON UPDATE CASCADE, but for consistency we treat * all changes to the PK the same. */ - Form_pg_attribute att = TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1); + TupleAttrAlign att = TupleDescAttrAlign(oldslot->tts_tupleDescriptor, attnums[i] - 1); if (!datum_image_eq(oldvalue, newvalue, att->attbyval, att->attlen)) return false; diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 4fbdc62d8c..add24fa06c 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1917,14 +1917,14 @@ CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos, Datum *keys) for (i = 0; i < nkeys; i++) { int attnum = attnos[i]; - Form_pg_attribute att; + TupleAttrAlign att; /* system attribute are not supported in caches */ Assert(attnum > 0); - att = TupleDescAttr(tupdesc, attnum - 1); + att = TupleDescAttrAlign(tupdesc, attnum - 1); - if (!att->attbyval) + if (att->attlen < 0 || !att->attbyval) pfree(DatumGetPointer(keys[i])); } } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 13d9994af3..070762a027 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -633,6 +633,8 @@ RelationBuildTupleDesc(Relation relation) systable_endscan(pg_attribute_scan); table_close(pg_attribute_desc, AccessShareLock); + TupleDescInitAttrAligns(relation->rd_att); + if (need != 0) elog(ERROR, "pg_attribute catalog is missing %d attribute(s) for relation OID %u", need, RelationGetRelid(relation)); @@ -647,7 +649,8 @@ RelationBuildTupleDesc(Relation relation) int i; for (i = 0; i < RelationGetNumberOfAttributes(relation); i++) - Assert(TupleDescAttr(relation->rd_att, i)->attcacheoff == -1); + Assert(TupleDescAttr(relation->rd_att, i)->attcacheoff == -1 && + TupleDescAttrAlign(relation->rd_att, i)->attcacheoff == -1); } #endif @@ -657,7 +660,10 @@ RelationBuildTupleDesc(Relation relation) * for attnum=1 that used to exist in fastgetattr() and index_getattr(). */ if (RelationGetNumberOfAttributes(relation) > 0) + { TupleDescAttr(relation->rd_att, 0)->attcacheoff = 0; + TupleDescAttrAlign(relation->rd_att, 0)->attcacheoff = 0; + } /* * Set up constraint/default info @@ -1904,6 +1910,8 @@ formrdesc(const char *relationName, Oid relationReltype, /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */ TupleDescAttr(relation->rd_att, 0)->attcacheoff = 0; + TupleDescInitAttrAligns(relation->rd_att); + /* mark not-null status */ if (has_not_null) { @@ -2038,6 +2046,7 @@ RelationIdGetRelation(Oid relationId) Assert(rd->rd_isvalid || (rd->rd_isnailed && !criticalRelcachesBuilt)); } + Assert(TupleDescAttrAlignsIsConsistent(rd->rd_att)); return rd; } @@ -2047,7 +2056,10 @@ RelationIdGetRelation(Oid relationId) */ rd = RelationBuildDesc(relationId, true); if (RelationIsValid(rd)) + { RelationIncrementReferenceCount(rd); + Assert(TupleDescAttrAlignsIsConsistent(rd->rd_att)); + } return rd; } @@ -4211,6 +4223,8 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs) /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */ TupleDescAttr(result, 0)->attcacheoff = 0; + TupleDescInitAttrAligns(result); + /* Note: we don't bother to set up a TupleConstr entry */ MemoryContextSwitchTo(oldcxt); @@ -6086,6 +6100,8 @@ load_relcache_init_file(bool shared) rel->rd_amcache = NULL; MemSet(&rel->pgstat_info, 0, sizeof(rel->pgstat_info)); + TupleDescInitAttrAligns(rel->rd_att); + /* * Recompute lock and physical addressing info. This is needed in * case the pg_internal.init file was copied from some other database diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index 960772f76b..92c1ac72e4 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -708,33 +708,33 @@ struct MinimalTupleData #if !defined(DISABLE_COMPLEX_MACRO) -#define fastgetattr(tup, attnum, tupleDesc, isnull) \ -( \ - AssertMacro((attnum) > 0), \ - (*(isnull) = false), \ - HeapTupleNoNulls(tup) ? \ - ( \ - TupleDescAttr((tupleDesc), (attnum)-1)->attcacheoff >= 0 ? \ - ( \ - fetchatt(TupleDescAttr((tupleDesc), (attnum)-1), \ - (char *) (tup)->t_data + (tup)->t_data->t_hoff + \ - TupleDescAttr((tupleDesc), (attnum)-1)->attcacheoff)\ - ) \ - : \ - nocachegetattr((tup), (attnum), (tupleDesc)) \ - ) \ - : \ - ( \ - att_isnull((attnum)-1, (tup)->t_data->t_bits) ? \ - ( \ - (*(isnull) = true), \ - (Datum)NULL \ - ) \ - : \ - ( \ - nocachegetattr((tup), (attnum), (tupleDesc)) \ - ) \ - ) \ +#define fastgetattr(tup, attnum, tupleDesc, isnull) \ +( \ + AssertMacro((attnum) > 0), \ + (*(isnull) = false), \ + HeapTupleNoNulls(tup) ? \ + ( \ + TupleDescAttrAlign((tupleDesc), (attnum)-1)->attcacheoff >= 0 ? \ + ( \ + fetchatt(TupleDescAttrAlign((tupleDesc), (attnum)-1), \ + (char *) (tup)->t_data + (tup)->t_data->t_hoff + \ + TupleDescAttrAlign((tupleDesc), (attnum)-1)->attcacheoff) \ + ) \ + : \ + nocachegetattr((tup), (attnum), (tupleDesc)) \ + ) \ + : \ + ( \ + att_isnull((attnum)-1, (tup)->t_data->t_bits) ? \ + ( \ + (*(isnull) = true), \ + (Datum)NULL \ + ) \ + : \ + ( \ + nocachegetattr((tup), (attnum), (tupleDesc)) \ + ) \ + ) \ ) #else /* defined(DISABLE_COMPLEX_MACRO) */ diff --git a/src/include/access/itup.h b/src/include/access/itup.h index 1917375cde..7b739dcd4f 100644 --- a/src/include/access/itup.h +++ b/src/include/access/itup.h @@ -99,15 +99,16 @@ typedef IndexAttributeBitMapData * IndexAttributeBitMap; */ #define index_getattr(tup, attnum, tupleDesc, isnull) \ ( \ - AssertMacro(PointerIsValid(isnull) && (attnum) > 0), \ + AssertMacro(PointerIsValid(isnull) && (attnum) > 0 && \ + TupleDescAttrAlignsIsConsistent(tupleDesc)), \ *(isnull) = false, \ !IndexTupleHasNulls(tup) ? \ ( \ - TupleDescAttr((tupleDesc), (attnum)-1)->attcacheoff >= 0 ? \ + TupleDescAttrAlign((tupleDesc), (attnum)-1)->attcacheoff >= 0 ? \ ( \ - fetchatt(TupleDescAttr((tupleDesc), (attnum)-1), \ + fetchatt(TupleDescAttrAlign((tupleDesc), (attnum)-1), \ (char *) (tup) + IndexInfoFindDataOffset((tup)->t_info) \ - + TupleDescAttr((tupleDesc), (attnum)-1)->attcacheoff) \ + + TupleDescAttrAlign((tupleDesc), (attnum)-1)->attcacheoff) \ ) \ : \ nocache_index_getattr((tup), (attnum), (tupleDesc)) \ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index f45d47aab7..0e0cdc16cf 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -45,6 +45,19 @@ typedef struct TupleConstr bool has_generated_stored; } TupleConstr; +typedef struct TupleAttrAlignData +{ + int32 attcacheoff; + int16 attlen; + char attalign; + union { + char attstorage; + bool attbyval; + }; +} TupleAttrAlignData; + +typedef TupleAttrAlignData *TupleAttrAlign; + /* * This struct is passed around within the backend to describe the structure * of tuples. For tuples coming from on-disk relations, the information is @@ -82,14 +95,18 @@ typedef struct TupleDescData Oid tdtypeid; /* composite type ID for tuple type */ int32 tdtypmod; /* typmod for tuple type */ int tdrefcount; /* reference count, or -1 if not counting */ + int attroff; /* offset to a co-allocated array of FormData_pg_attribute */ TupleConstr *constr; /* constraints, or NULL if none */ - /* attrs[N] is the description of Attribute Number N+1 */ - FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER]; + /* attrs[N] is the alignment data of Attribute Number N+1 */ + TupleAttrAlignData attrs[FLEXIBLE_ARRAY_MEMBER]; } TupleDescData; typedef struct TupleDescData *TupleDesc; /* Accessor for the i'th attribute of tupdesc. */ -#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)]) +//#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)]) +#define TupleDescAttr(tupdesc, i) ((FormData_pg_attribute *) ((char *)(tupdesc) + (tupdesc)->attroff + (i) * sizeof(FormData_pg_attribute))) +/* Accessor for the i'th attribute of attralign. */ +#define TupleDescAttrAlign(tupdesc, i) (&(tupdesc)->attrs[(i)]) extern TupleDesc CreateTemplateTupleDesc(int natts); @@ -100,8 +117,9 @@ extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc); #define TupleDescSize(src) \ - (offsetof(struct TupleDescData, attrs) + \ - (src)->natts * sizeof(FormData_pg_attribute)) + (MAXALIGN(offsetof(struct TupleDescData, attrs) + \ + (src)->natts * sizeof(TupleAttrAlignData)) + \ + MAXALIGN((src)->natts * sizeof(FormData_pg_attribute))) extern void TupleDescCopy(TupleDesc dst, TupleDesc src); @@ -125,6 +143,17 @@ extern void DecrTupleDescRefCount(TupleDesc tupdesc); DecrTupleDescRefCount(tupdesc); \ } while (0) +#define CopyAttrToAlign(att, align) \ + do { \ + (align)->attcacheoff = (att)->attcacheoff; \ + (align)->attlen = (att)->attlen; \ + (align)->attalign = (att)->attalign; \ + if ((att)->attlen < 0) \ + (align)->attstorage = (att)->attstorage; \ + else \ + (align)->attbyval = (att)->attbyval; \ + } while (0) + extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2); extern uint32 hashTupleDesc(TupleDesc tupdesc); @@ -147,6 +176,9 @@ extern void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid); +extern void TupleDescInitAttrAligns(TupleDesc desc); +extern bool TupleDescAttrAlignsIsConsistent(TupleDesc desc); + extern TupleDesc BuildDescForRelation(List *schema); extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods, List *collations); diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h index 65ac1ef3fc..0451dd6bb8 100644 --- a/src/include/access/tupmacs.h +++ b/src/include/access/tupmacs.h @@ -47,7 +47,7 @@ #define fetch_att(T,attbyval,attlen) \ ( \ - (attbyval) ? \ + (attbyval && attlen >= 0) ? \ ( \ (attlen) == (int) sizeof(Datum) ? \ *((Datum *)(T)) \ diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index 5c1ec9313e..0bfb573c41 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -94,6 +94,11 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(75, * no cached value. But when we copy these tuples into a tuple * descriptor, we may then update attcacheoff in the copies. This speeds * up the attribute walking process. + * + * Note: Although the maximum offset encountered in stored tuples is + * limited to the max BLCKSZ (2**15), FormData_pg_attribute is used for + * all internal tuples as well, so attcacheoff may be larger for those + * tuples, and it is therefore not safe to use int16. */ int32 attcacheoff BKI_DEFAULT(-1); -- 2.20.1