From 3dd3f470ab78b8811015c9f374d0bdc44ffef531 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Wed, 23 Jun 2021 20:38:47 +0200 Subject: [PATCH] Some work on storing attribute access fields more compactly This would increase the amount of useful data in each cache line, potentially increasing performance. It _does_ use more memory (8 bytes / attribute extra overhead) --- 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 | 74 +++++++++++++++++++- src/backend/access/spgist/spgutils.c | 3 + src/backend/catalog/index.c | 2 + src/backend/catalog/toasting.c | 4 ++ src/backend/utils/cache/relcache.c | 18 ++++- src/include/access/htup_details.h | 6 +- src/include/access/itup.h | 9 +-- src/include/access/tupdesc.h | 35 +++++++++- src/include/access/tupmacs.h | 2 +- src/include/catalog/pg_attribute.h | 5 ++ 13 files changed, 226 insertions(+), 81 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..3ee74ce9fc 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -63,8 +63,9 @@ 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) + - natts * sizeof(FormData_pg_attribute)); + desc = (TupleDesc) palloc(MAXALIGN((offsetof(struct TupleDescData, attrs)) + + natts * sizeof(FormData_pg_attribute)) + + natts * sizeof(TupleAttrAlignData)); /* * Initialize other fields of the tupdesc. @@ -75,6 +76,9 @@ CreateTemplateTupleDesc(int natts) desc->tdtypmod = -1; desc->tdrefcount = -1; /* assume not reference-counted */ + desc->attralignoff = MAXALIGN((offsetof(struct TupleDescData, attrs)) + + natts * sizeof(FormData_pg_attribute)); + 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); } @@ -742,6 +760,10 @@ TupleDescInitBuiltinEntry(TupleDesc desc, default: elog(ERROR, "unsupported type %u", oidtypeid); } + + TupleAttrAlign attrAlign = TupleDescAttrAlign(desc, attributeNumber - 1); + + CopyAttrToAlign(att, attrAlign); } /* @@ -765,6 +787,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 +866,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 +894,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/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index 9ff280a252..bc46dd1b02 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/catalog/index.c b/src/backend/catalog/index.c index 50b7a16bce..28d1a7a032 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 bf81f6ccc5..a872ce50c7 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -221,6 +221,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/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index d55ae016d0..b1aeab6e06 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 @@ -1901,6 +1907,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) { @@ -2035,6 +2043,7 @@ RelationIdGetRelation(Oid relationId) Assert(rd->rd_isvalid || (rd->rd_isnailed && !criticalRelcachesBuilt)); } + Assert(TupleDescAttrAlignsIsConsistent(rd->rd_att)); return rd; } @@ -2044,7 +2053,10 @@ RelationIdGetRelation(Oid relationId) */ rd = RelationBuildDesc(relationId, true); if (RelationIsValid(rd)) + { RelationIncrementReferenceCount(rd); + Assert(TupleDescAttrAlignsIsConsistent(rd->rd_att)); + } return rd; } @@ -4208,6 +4220,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); @@ -6088,6 +6102,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..bc7892be9b 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -714,11 +714,11 @@ struct MinimalTupleData (*(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/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..9e8ce52cf3 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,6 +95,7 @@ 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 attralignoff; /* offset to a co-allocated array of TupleAttrAlignData */ TupleConstr *constr; /* constraints, or NULL if none */ /* attrs[N] is the description of Attribute Number N+1 */ FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER]; @@ -90,6 +104,8 @@ typedef struct TupleDescData *TupleDesc; /* Accessor for the i'th attribute of tupdesc. */ #define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)]) +/* Accessor for the i'th attribute of attralign. */ +#define TupleDescAttrAlign(tupdesc, i) ((TupleAttrAlign) ((char *)(tupdesc) + (tupdesc)->attralignoff + (i) * sizeof(TupleAttrAlignData))) extern TupleDesc CreateTemplateTupleDesc(int natts); @@ -100,8 +116,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(FormData_pg_attribute)) + \ + (src)->natts * sizeof(TupleAttrAlignData)) extern void TupleDescCopy(TupleDesc dst, TupleDesc src); @@ -125,6 +142,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 +175,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 603392fe81..d3194041ba 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