diff options
44 files changed, 374 insertions, 154 deletions
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c index e16557aca36..c1847d1b8a4 100644 --- a/contrib/amcheck/verify_heapam.c +++ b/contrib/amcheck/verify_heapam.c @@ -1571,11 +1571,11 @@ check_tuple_attribute(HeapCheckContext *ctx) struct varlena *attr; char *tp; /* pointer to the tuple data */ uint16 infomask; - Form_pg_attribute thisatt; + CompactAttribute *thisatt; struct varatt_external toast_pointer; infomask = ctx->tuphdr->t_infomask; - thisatt = TupleDescAttr(RelationGetDescr(ctx->rel), ctx->attnum); + thisatt = TupleDescCompactAttr(RelationGetDescr(ctx->rel), ctx->attnum); tp = (char *) ctx->tuphdr + ctx->tuphdr->t_hoff; diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c index 029072567a4..68d59cb3ecd 100644 --- a/contrib/pageinspect/gistfuncs.c +++ b/contrib/pageinspect/gistfuncs.c @@ -242,8 +242,8 @@ gist_page_items(PG_FUNCTION_ARGS) } else { - tupdesc = CreateTupleDescCopy(RelationGetDescr(indexRel)); - tupdesc->natts = IndexRelationGetNumberOfKeyAttributes(indexRel); + tupdesc = CreateTupleDescTruncatedCopy(RelationGetDescr(indexRel), + IndexRelationGetNumberOfKeyAttributes(indexRel)); printflags |= RULE_INDEXDEF_KEYS_ONLY; } diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c index 3dc705e43f7..8c1b7d38aa6 100644 --- a/contrib/pageinspect/heapfuncs.c +++ b/contrib/pageinspect/heapfuncs.c @@ -334,11 +334,11 @@ tuple_data_split_internal(Oid relid, char *tupdata, for (i = 0; i < nattrs; i++) { - Form_pg_attribute attr; + CompactAttribute *attr; bool is_null; bytea *attr_data = NULL; - attr = TupleDescAttr(tupdesc, i); + attr = TupleDescCompactAttr(tupdesc, i); /* * Tuple header can specify fewer attributes than tuple descriptor as diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index c0810fbd7c8..cf564341184 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -1818,7 +1818,7 @@ postgresPlanForeignModify(PlannerInfo *root, for (attnum = 1; attnum <= tupdesc->natts; attnum++) { - Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); + CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1); if (!attr->attisdropped) targetAttrs = lappend_int(targetAttrs, attnum); @@ -2191,7 +2191,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, /* We transmit all columns that are defined in the foreign table. */ for (attnum = 1; attnum <= tupdesc->natts; attnum++) { - Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); + CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1); if (!attr->attisdropped) targetAttrs = lappend_int(targetAttrs, attnum); @@ -4311,7 +4311,7 @@ convert_prep_stmt_params(PgFdwModifyState *fmstate, foreach(lc, fmstate->target_attrs) { int attnum = lfirst_int(lc); - Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); + CompactAttribute *attr = TupleDescCompactAttr(tupdesc, attnum - 1); Datum value; bool isnull; diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c index 750276998c5..faec0a9da84 100644 --- a/src/backend/access/brin/brin_inclusion.c +++ b/src/backend/access/brin/brin_inclusion.c @@ -146,12 +146,12 @@ brin_inclusion_add_value(PG_FUNCTION_ARGS) Datum result; bool new = false; AttrNumber attno; - Form_pg_attribute attr; + CompactAttribute *attr; Assert(!isnull); attno = column->bv_attno; - attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + attr = TupleDescCompactAttr(bdesc->bd_tupdesc, attno - 1); /* * If the recorded value is null, copy the new value (which we know to be @@ -479,7 +479,7 @@ brin_inclusion_union(PG_FUNCTION_ARGS) BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); Oid colloid = PG_GET_COLLATION(); AttrNumber attno; - Form_pg_attribute attr; + CompactAttribute *attr; FmgrInfo *finfo; Datum result; @@ -487,7 +487,7 @@ brin_inclusion_union(PG_FUNCTION_ARGS) Assert(!col_a->bv_allnulls && !col_b->bv_allnulls); attno = col_a->bv_attno; - attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); + attr = TupleDescCompactAttr(bdesc->bd_tupdesc, attno - 1); /* If B includes empty elements, mark A similarly, if needed. */ if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c index 997eb6d822c..aae646be5dd 100644 --- a/src/backend/access/brin/brin_tuple.c +++ b/src/backend/access/brin/brin_tuple.c @@ -699,7 +699,7 @@ brin_deconstruct_tuple(BrinDesc *brdesc, datumno < brdesc->bd_info[attnum]->oi_nstored; datumno++) { - Form_pg_attribute thisatt = TupleDescAttr(diskdsc, stored); + CompactAttribute *thisatt = TupleDescCompactAttr(diskdsc, stored); if (thisatt->attlen == -1) { diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index b0fe27ef57d..0805c4121ee 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -135,7 +135,7 @@ build_attrmap_by_position(TupleDesc indesc, /* Check for unused input columns */ for (; j < indesc->natts; j++) { - if (TupleDescAttr(indesc, j)->attisdropped) + if (TupleDescCompactAttr(indesc, j)->attisdropped) continue; nincols++; same = false; /* we'll complain below */ @@ -299,8 +299,8 @@ check_attrmap_match(TupleDesc indesc, for (i = 0; i < attrMap->maplen; i++) { - Form_pg_attribute inatt = TupleDescAttr(indesc, i); - Form_pg_attribute outatt = TupleDescAttr(outdesc, i); + CompactAttribute *inatt = TupleDescCompactAttr(indesc, i); + CompactAttribute *outatt; /* * If the input column has a missing attribute, we need a conversion. @@ -311,6 +311,8 @@ check_attrmap_match(TupleDesc indesc, if (attrMap->attnums[i] == (i + 1)) continue; + outatt = TupleDescCompactAttr(outdesc, i); + /* * If it's a dropped column and the corresponding input column is also * dropped, we don't need a conversion. However, attlen and attalign diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 9e3407bf987..982e7222c49 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -83,6 +83,10 @@ #define VARLENA_ATT_IS_PACKABLE(att) \ ((att)->attstorage != TYPSTORAGE_PLAIN) +/* FormData_pg_attribute.attstorage != TYPSTORAGE_PLAIN and an attlen of -1 */ +#define COMPACT_ATTR_IS_PACKABLE(att) \ + ((att)->attlen == -1 && (att)->attispackable) + /* * Setup for caching pass-by-ref missing attributes in a way that survives * tupleDesc destruction. @@ -147,12 +151,12 @@ Datum getmissingattr(TupleDesc tupleDesc, int attnum, bool *isnull) { - Form_pg_attribute att; + CompactAttribute *att; Assert(attnum <= tupleDesc->natts); Assert(attnum > 0); - att = TupleDescAttr(tupleDesc, attnum - 1); + att = TupleDescCompactAttr(tupleDesc, attnum - 1); if (att->atthasmissing) { @@ -223,15 +227,15 @@ heap_compute_data_size(TupleDesc tupleDesc, for (i = 0; i < numberOfAttributes; i++) { Datum val; - Form_pg_attribute atti; + CompactAttribute *atti; if (isnull[i]) continue; val = values[i]; - atti = TupleDescAttr(tupleDesc, i); + atti = TupleDescCompactAttr(tupleDesc, i); - if (ATT_IS_PACKABLE(atti) && + if (COMPACT_ATTR_IS_PACKABLE(atti) && VARATT_CAN_MAKE_SHORT(DatumGetPointer(val))) { /* @@ -268,7 +272,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(CompactAttribute *att, bits8 **bit, int *bitmask, char **dataP, @@ -349,8 +353,7 @@ fill_val(Form_pg_attribute att, data_length = VARSIZE_SHORT(val); memcpy(data, val, data_length); } - else if (VARLENA_ATT_IS_PACKABLE(att) && - VARATT_CAN_MAKE_SHORT(val)) + else if (att->attispackable && VARATT_CAN_MAKE_SHORT(val)) { /* convert to short varlena -- no alignment */ data_length = VARATT_CONVERTED_SHORT_SIZE(val); @@ -427,7 +430,7 @@ heap_fill_tuple(TupleDesc tupleDesc, for (i = 0; i < numberOfAttributes; i++) { - Form_pg_attribute attr = TupleDescAttr(tupleDesc, i); + CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, i); fill_val(attr, bitP ? &bitP : NULL, @@ -461,7 +464,8 @@ heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc) Assert(!tupleDesc || attnum <= tupleDesc->natts); if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data)) { - if (tupleDesc && TupleDescAttr(tupleDesc, attnum - 1)->atthasmissing) + if (tupleDesc && + TupleDescCompactAttr(tupleDesc, attnum - 1)->atthasmissing) return false; else return true; @@ -570,13 +574,13 @@ nocachegetattr(HeapTuple tup, if (!slow) { - Form_pg_attribute att; + CompactAttribute *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 = TupleDescCompactAttr(tupleDesc, attnum); if (att->attcacheoff >= 0) return fetchatt(att, tp + att->attcacheoff); @@ -591,7 +595,7 @@ nocachegetattr(HeapTuple tup, for (j = 0; j <= attnum; j++) { - if (TupleDescAttr(tupleDesc, j)->attlen <= 0) + if (TupleDescCompactAttr(tupleDesc, j)->attlen <= 0) { slow = true; break; @@ -614,18 +618,18 @@ nocachegetattr(HeapTuple tup, * fixed-width columns, in hope of avoiding future visits to this * routine. */ - TupleDescAttr(tupleDesc, 0)->attcacheoff = 0; + TupleDescCompactAttr(tupleDesc, 0)->attcacheoff = 0; /* we might have set some offsets in the slow path previously */ - while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0) + while (j < natts && TupleDescCompactAttr(tupleDesc, j)->attcacheoff > 0) j++; - off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff + - TupleDescAttr(tupleDesc, j - 1)->attlen; + off = TupleDescCompactAttr(tupleDesc, j - 1)->attcacheoff + + TupleDescCompactAttr(tupleDesc, j - 1)->attlen; for (; j < natts; j++) { - Form_pg_attribute att = TupleDescAttr(tupleDesc, j); + CompactAttribute *att = TupleDescCompactAttr(tupleDesc, j); if (att->attlen <= 0) break; @@ -639,7 +643,7 @@ nocachegetattr(HeapTuple tup, Assert(j > attnum); - off = TupleDescAttr(tupleDesc, attnum)->attcacheoff; + off = TupleDescCompactAttr(tupleDesc, attnum)->attcacheoff; } else { @@ -659,7 +663,7 @@ nocachegetattr(HeapTuple tup, off = 0; for (i = 0;; i++) /* loop exit is at "break" */ { - Form_pg_attribute att = TupleDescAttr(tupleDesc, i); + CompactAttribute *att = TupleDescCompactAttr(tupleDesc, i); if (HeapTupleHasNulls(tup) && att_isnull(i, bp)) { @@ -707,7 +711,7 @@ nocachegetattr(HeapTuple tup, } } - return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off); + return fetchatt(TupleDescCompactAttr(tupleDesc, attnum), tp + off); } /* ---------------- @@ -892,7 +896,7 @@ expand_tuple(HeapTuple *targetHeapTuple, { if (attrmiss[attnum].am_present) { - Form_pg_attribute att = TupleDescAttr(tupleDesc, attnum); + CompactAttribute *att = TupleDescCompactAttr(tupleDesc, attnum); targetDataLen = att_align_datum(targetDataLen, att->attalign, @@ -1020,8 +1024,7 @@ expand_tuple(HeapTuple *targetHeapTuple, /* Now fill in the missing values */ for (attnum = sourceNatts; attnum < natts; attnum++) { - - Form_pg_attribute attr = TupleDescAttr(tupleDesc, attnum); + CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, attnum); if (attrmiss && attrmiss[attnum].am_present) { @@ -1370,7 +1373,7 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, for (attnum = 0; attnum < natts; attnum++) { - Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); + CompactAttribute *thisatt = TupleDescCompactAttr(tupleDesc, attnum); if (hasnulls && att_isnull(attnum, bp)) { diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c index bb2c6a2bcc1..38aeb230879 100644 --- a/src/backend/access/common/indextuple.c +++ b/src/backend/access/common/indextuple.c @@ -303,13 +303,13 @@ nocache_index_getattr(IndexTuple tup, if (!slow) { - Form_pg_attribute att; + CompactAttribute *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 = TupleDescCompactAttr(tupleDesc, attnum); if (att->attcacheoff >= 0) return fetchatt(att, tp + att->attcacheoff); @@ -324,7 +324,7 @@ nocache_index_getattr(IndexTuple tup, for (j = 0; j <= attnum; j++) { - if (TupleDescAttr(tupleDesc, j)->attlen <= 0) + if (TupleDescCompactAttr(tupleDesc, j)->attlen <= 0) { slow = true; break; @@ -347,18 +347,18 @@ nocache_index_getattr(IndexTuple tup, * fixed-width columns, in hope of avoiding future visits to this * routine. */ - TupleDescAttr(tupleDesc, 0)->attcacheoff = 0; + TupleDescCompactAttr(tupleDesc, 0)->attcacheoff = 0; /* we might have set some offsets in the slow path previously */ - while (j < natts && TupleDescAttr(tupleDesc, j)->attcacheoff > 0) + while (j < natts && TupleDescCompactAttr(tupleDesc, j)->attcacheoff > 0) j++; - off = TupleDescAttr(tupleDesc, j - 1)->attcacheoff + - TupleDescAttr(tupleDesc, j - 1)->attlen; + off = TupleDescCompactAttr(tupleDesc, j - 1)->attcacheoff + + TupleDescCompactAttr(tupleDesc, j - 1)->attlen; for (; j < natts; j++) { - Form_pg_attribute att = TupleDescAttr(tupleDesc, j); + CompactAttribute *att = TupleDescCompactAttr(tupleDesc, j); if (att->attlen <= 0) break; @@ -372,7 +372,7 @@ nocache_index_getattr(IndexTuple tup, Assert(j > attnum); - off = TupleDescAttr(tupleDesc, attnum)->attcacheoff; + off = TupleDescCompactAttr(tupleDesc, attnum)->attcacheoff; } else { @@ -392,7 +392,7 @@ nocache_index_getattr(IndexTuple tup, off = 0; for (i = 0;; i++) /* loop exit is at "break" */ { - Form_pg_attribute att = TupleDescAttr(tupleDesc, i); + CompactAttribute *att = TupleDescCompactAttr(tupleDesc, i); if (IndexTupleHasNulls(tup) && att_isnull(i, bp)) { @@ -440,7 +440,7 @@ nocache_index_getattr(IndexTuple tup, } } - return fetchatt(TupleDescAttr(tupleDesc, attnum), tp + off); + return fetchatt(TupleDescCompactAttr(tupleDesc, attnum), tp + off); } /* @@ -490,7 +490,7 @@ index_deform_tuple_internal(TupleDesc tupleDescriptor, for (attnum = 0; attnum < natts; attnum++) { - Form_pg_attribute thisatt = TupleDescAttr(tupleDescriptor, attnum); + CompactAttribute *thisatt = TupleDescCompactAttr(tupleDescriptor, attnum); if (hasnulls && att_isnull(attnum, bp)) { @@ -587,10 +587,8 @@ index_truncate_tuple(TupleDesc sourceDescriptor, IndexTuple source, if (leavenatts == sourceDescriptor->natts) return CopyIndexTuple(source); - /* Create temporary descriptor to scribble on */ - truncdesc = palloc(TupleDescSize(sourceDescriptor)); - TupleDescCopy(truncdesc, sourceDescriptor); - truncdesc->natts = leavenatts; + /* Create temporary truncated tuple descriptor */ + truncdesc = CreateTupleDescTruncatedCopy(sourceDescriptor, leavenatts); /* Deform, form copy of tuple with fewer attributes */ index_deform_tuple(source, truncdesc, values, isnull); diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 47379fef220..4d89acbe5e2 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -57,6 +57,33 @@ ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc) } /* + * populate_compact_attribute + * Fill in the corresponding CompactAttribute element from the + * Form_pg_attribute for the given attribute number. This must be called + * whenever a change is made to a Form_pg_attribute in the TupleDesc. + */ +void +populate_compact_attribute(TupleDesc tupdesc, int attnum) +{ + Form_pg_attribute src = TupleDescAttr(tupdesc, attnum); + CompactAttribute *dst = &tupdesc->compact_attrs[attnum]; + + memset(dst, 0, sizeof(CompactAttribute)); + + dst->attcacheoff = -1; + dst->attlen = src->attlen; + + dst->attbyval = src->attbyval; + dst->attispackable = (src->attstorage != TYPSTORAGE_PLAIN); + dst->atthasmissing = src->atthasmissing; + dst->attisdropped = src->attisdropped; + dst->attgenerated = (src->attgenerated != '\0'); + dst->attnotnull = src->attnotnull; + + dst->attalign = src->attalign; +} + +/* * CreateTemplateTupleDesc * This function allocates an empty tuple descriptor structure. * @@ -74,18 +101,20 @@ CreateTemplateTupleDesc(int natts) Assert(natts >= 0); /* - * Allocate enough memory for the tuple descriptor, including the - * attribute rows. + * Allocate enough memory for the tuple descriptor, the CompactAttribute + * array and also an array of FormData_pg_attribute. * - * Note: the attribute array stride is sizeof(FormData_pg_attribute), - * since we declare the array elements as FormData_pg_attribute for - * notational convenience. However, we only guarantee that the first - * ATTRIBUTE_FIXED_PART_SIZE bytes of each entry are valid; most code that - * copies tupdesc entries around copies just that much. In principle that - * could be less due to trailing padding, although with the current - * definition of pg_attribute there probably isn't any padding. + * Note: the FormData_pg_attribute array stride is + * sizeof(FormData_pg_attribute), since we declare the array elements as + * FormData_pg_attribute for notational convenience. However, we only + * guarantee that the first ATTRIBUTE_FIXED_PART_SIZE bytes of each entry + * are valid; most code that copies tupdesc entries around copies just + * that much. In principle that 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(offsetof(struct TupleDescData, compact_attrs) + + natts * sizeof(CompactAttribute) + natts * sizeof(FormData_pg_attribute)); /* @@ -117,8 +146,10 @@ CreateTupleDesc(int natts, Form_pg_attribute *attrs) desc = CreateTemplateTupleDesc(natts); for (i = 0; i < natts; ++i) + { memcpy(TupleDescAttr(desc, i), attrs[i], ATTRIBUTE_FIXED_PART_SIZE); - + populate_compact_attribute(desc, i); + } return desc; } @@ -155,6 +186,54 @@ CreateTupleDescCopy(TupleDesc tupdesc) att->atthasmissing = false; att->attidentity = '\0'; att->attgenerated = '\0'; + + populate_compact_attribute(desc, i); + } + + /* We can copy the tuple type identification, too */ + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; + + return desc; +} + +/* + * CreateTupleDescTruncatedCopy + * This function creates a new TupleDesc with only the first 'natts' + * attributes from an existing TupleDesc + * + * !!! Constraints and defaults are not copied !!! + */ +TupleDesc +CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts) +{ + TupleDesc desc; + int i; + + Assert(natts <= tupdesc->natts); + + desc = CreateTemplateTupleDesc(natts); + + /* Flat-copy the attribute array */ + memcpy(TupleDescAttr(desc, 0), + TupleDescAttr(tupdesc, 0), + desc->natts * sizeof(FormData_pg_attribute)); + + /* + * Since we're not copying constraints and defaults, clear fields + * associated with them. + */ + for (i = 0; i < desc->natts; i++) + { + Form_pg_attribute att = TupleDescAttr(desc, i); + + att->attnotnull = false; + att->atthasdef = false; + att->atthasmissing = false; + att->attidentity = '\0'; + att->attgenerated = '\0'; + + populate_compact_attribute(desc, i); } /* We can copy the tuple type identification, too */ @@ -183,6 +262,9 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) TupleDescAttr(tupdesc, 0), desc->natts * sizeof(FormData_pg_attribute)); + for (i = 0; i < desc->natts; i++) + populate_compact_attribute(desc, i); + /* Copy the TupleConstr data structure, if any */ if (constr) { @@ -207,7 +289,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) { if (constr->missing[i].am_present) { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i); cpy->missing[i].am_value = datumCopy(constr->missing[i].am_value, attr->attbyval, @@ -252,7 +334,7 @@ TupleDescCopy(TupleDesc dst, TupleDesc src) { int i; - /* Flat-copy the header and attribute array */ + /* Flat-copy the header and attribute arrays */ memcpy(dst, src, TupleDescSize(src)); /* @@ -268,6 +350,8 @@ TupleDescCopy(TupleDesc dst, TupleDesc src) att->atthasmissing = false; att->attidentity = '\0'; att->attgenerated = '\0'; + + populate_compact_attribute(dst, i); } dst->constr = NULL; @@ -322,6 +406,8 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno, dstAtt->atthasmissing = false; dstAtt->attidentity = '\0'; dstAtt->attgenerated = '\0'; + + populate_compact_attribute(dst, dstAttno - 1); } /* @@ -521,7 +607,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) return false; if (missval1->am_present) { - Form_pg_attribute missatt1 = TupleDescAttr(tupdesc1, i); + CompactAttribute *missatt1 = TupleDescCompactAttr(tupdesc1, i); if (!datumIsEqual(missval1->am_value, missval2->am_value, missatt1->attbyval, missatt1->attlen)) @@ -714,6 +800,8 @@ TupleDescInitEntry(TupleDesc desc, att->attcompression = InvalidCompressionMethod; att->attcollation = typeForm->typcollation; + populate_compact_attribute(desc, attributeNumber - 1); + ReleaseSysCache(tuple); } @@ -821,6 +909,8 @@ TupleDescInitBuiltinEntry(TupleDesc desc, default: elog(ERROR, "unsupported type %u", oidtypeid); } + + populate_compact_attribute(desc, attributeNumber - 1); } /* diff --git a/src/backend/access/gin/ginbulk.c b/src/backend/access/gin/ginbulk.c index f08b66ab791..d86b75bd639 100644 --- a/src/backend/access/gin/ginbulk.c +++ b/src/backend/access/gin/ginbulk.c @@ -127,10 +127,10 @@ ginInitBA(BuildAccumulator *accum) static Datum getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum value) { - Form_pg_attribute att; + CompactAttribute *att; Datum res; - att = TupleDescAttr(accum->ginstate->origTupdesc, attnum - 1); + att = TupleDescCompactAttr(accum->ginstate->origTupdesc, attnum - 1); if (att->attbyval) res = value; else diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c index e8e0eab6552..c11ae0029c2 100644 --- a/src/backend/access/gin/ginget.c +++ b/src/backend/access/gin/ginget.c @@ -122,7 +122,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry scanEntry, Snapshot snapshot) { OffsetNumber attnum; - Form_pg_attribute attr; + CompactAttribute *attr; /* Initialize empty bitmap result */ scanEntry->matchBitmap = tbm_create(work_mem * 1024L, NULL); @@ -134,7 +134,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, /* Locate tupdesc entry for key column (for attbyval/attlen data) */ attnum = scanEntry->attnum; - attr = TupleDescAttr(btree->ginstate->origTupdesc, attnum - 1); + attr = TupleDescCompactAttr(btree->ginstate->origTupdesc, attnum - 1); /* * Predicate lock entry leaf page, following pages will be locked by diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index ebc65449c1b..272390ff67d 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -1557,9 +1557,8 @@ initGISTstate(Relation index) * tuples during page split. Also, B-tree is not adjusting tuples on * internal pages the way GiST does. */ - giststate->nonLeafTupdesc = CreateTupleDescCopyConstr(index->rd_att); - giststate->nonLeafTupdesc->natts = - IndexRelationGetNumberOfKeyAttributes(index); + giststate->nonLeafTupdesc = CreateTupleDescTruncatedCopy(index->rd_att, + IndexRelationGetNumberOfKeyAttributes(index)); for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(index); i++) { diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c index 63d1914b37f..3a2759b4468 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -657,10 +657,12 @@ gistInitBuffering(GISTBuildState *buildstate) itupMinSize = (Size) MAXALIGN(sizeof(IndexTupleData)); for (i = 0; i < index->rd_att->natts; i++) { - if (TupleDescAttr(index->rd_att, i)->attlen < 0) + CompactAttribute *attr = TupleDescCompactAttr(index->rd_att, i); + + if (attr->attlen < 0) itupMinSize += VARHDRSZ; else - itupMinSize += TupleDescAttr(index->rd_att, i)->attlen; + itupMinSize += attr->attlen; } /* Calculate average and maximal number of index tuples which fit to page */ diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 6cdc68d981a..329e727f80d 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -4200,8 +4200,6 @@ static bool heap_attr_equals(TupleDesc tupdesc, int attrnum, Datum value1, Datum value2, bool isnull1, bool isnull2) { - Form_pg_attribute att; - /* * If one value is NULL and other is not, then they are certainly not * equal @@ -4231,8 +4229,10 @@ heap_attr_equals(TupleDesc tupdesc, int attrnum, Datum value1, Datum value2, } else { + CompactAttribute *att; + Assert(attrnum <= tupdesc->natts); - att = TupleDescAttr(tupdesc, attrnum - 1); + att = TupleDescCompactAttr(tupdesc, attrnum - 1); return datumIsEqual(value1, value2, att->attbyval, att->attlen); } } @@ -4314,7 +4314,7 @@ HeapDetermineColumnsInfo(Relation relation, * that system attributes can't be stored externally. */ if (attrnum < 0 || isnull1 || - TupleDescAttr(tupdesc, attrnum - 1)->attlen != -1) + TupleDescCompactAttr(tupdesc, attrnum - 1)->attlen != -1) continue; /* diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 2da4e4da13e..9f17baea5d6 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -2550,7 +2550,7 @@ reform_and_rewrite_tuple(HeapTuple tuple, /* Be sure to null out any dropped columns */ for (i = 0; i < newTupDesc->natts; i++) { - if (TupleDescAttr(newTupDesc, i)->attisdropped) + if (TupleDescCompactAttr(newTupDesc, i)->attisdropped) isnull[i] = true; } diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index aae72bc2abf..eda81a35d50 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -369,7 +369,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] && TupleDescCompactAttr(tupleDesc, i)->attlen == -1) { struct varlena *new_value; @@ -483,7 +483,7 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup, */ if (toast_isnull[i]) has_nulls = true; - else if (TupleDescAttr(tupleDesc, i)->attlen == -1) + else if (TupleDescCompactAttr(tupleDesc, i)->attlen == -1) { struct varlena *new_value; @@ -584,7 +584,7 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, /* * Look at non-null varlena attributes */ - if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1) + if (!isnull[i] && TupleDescCompactAttr(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 afff7d63922..a531d37908a 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -4890,11 +4890,11 @@ _bt_keep_natts_fast(Relation rel, IndexTuple lastleft, IndexTuple firstright) datum2; bool isNull1, isNull2; - Form_pg_attribute att; + CompactAttribute *att; datum1 = index_getattr(lastleft, attnum, itupdesc, &isNull1); datum2 = index_getattr(firstright, attnum, itupdesc, &isNull2); - att = TupleDescAttr(itupdesc, attnum - 1); + att = TupleDescCompactAttr(itupdesc, attnum - 1); if (isNull1 != isNull2) break; diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c index a4995c168b4..2cb7ce43ba1 100644 --- a/src/backend/access/spgist/spgdoinsert.c +++ b/src/backend/access/spgist/spgdoinsert.c @@ -1974,7 +1974,7 @@ spgdoinsert(Relation index, SpGistState *state, { if (!isnulls[i]) { - if (TupleDescAttr(leafDescriptor, i)->attlen == -1) + if (TupleDescCompactAttr(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 e93d9869b27..da858182173 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -331,7 +331,9 @@ getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType) 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; + TupleDescCompactAttr(outTupDesc, i)->attcacheoff = -1; + + populate_compact_attribute(outTupDesc, spgKeyColumn); } return outTupDesc; } diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c index 53224932f0d..b16fd21b8d0 100644 --- a/src/backend/access/table/toast_helper.c +++ b/src/backend/access/table/toast_helper.c @@ -324,7 +324,7 @@ toast_delete_external(Relation rel, const Datum *values, const bool *isnull, for (i = 0; i < numAttrs; i++) { - if (TupleDescAttr(tupleDesc, i)->attlen == -1) + if (TupleDescCompactAttr(tupleDesc, i)->attlen == -1) { Datum value = values[i]; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 05dc6add7eb..6200a0da501 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -477,6 +477,8 @@ ConstructTupleDescriptor(Relation heapRelation, ReleaseSysCache(tuple); } + + populate_compact_attribute(indexTupDesc, i); } pfree(amroutine); diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 2d98ecf3f4e..edcdb7c2d40 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -954,9 +954,9 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) for (i = 0; i < attr_count; i++) { - if (TupleDescAttr(tupDesc, i)->attisdropped) - continue; - if (TupleDescAttr(tupDesc, i)->attgenerated) + CompactAttribute *attr = TupleDescCompactAttr(tupDesc, i); + + if (attr->attisdropped || attr->attgenerated) continue; attnums = lappend_int(attnums, i + 1); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 6ccae4cb4a8..49374782625 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -980,6 +980,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, cookedDefaults = lappend(cookedDefaults, cooked); attr->atthasdef = true; } + + populate_compact_attribute(descriptor, attnum - 1); } /* @@ -1396,6 +1398,8 @@ BuildDescForRelation(const List *columns) att->attstorage = entry->storage; else if (entry->storage_name) att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name); + + populate_compact_attribute(desc, attnum - 1); } return desc; diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 81714341f07..3d01a90bd64 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -598,7 +598,7 @@ ExecBuildUpdateProjection(List *targetList, */ for (int attnum = relDesc->natts; attnum > 0; attnum--) { - Form_pg_attribute attr = TupleDescAttr(relDesc, attnum - 1); + CompactAttribute *attr = TupleDescCompactAttr(relDesc, attnum - 1); if (attr->attisdropped) continue; @@ -694,7 +694,7 @@ ExecBuildUpdateProjection(List *targetList, */ for (int attnum = 1; attnum <= relDesc->natts; attnum++) { - Form_pg_attribute attr = TupleDescAttr(relDesc, attnum - 1); + CompactAttribute *attr = TupleDescCompactAttr(relDesc, attnum - 1); if (attr->attisdropped) { diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 60dcbcbe596..56e13d20a87 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -3152,7 +3152,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, for (int att = 1; att <= tupDesc->natts; att++) { /* ignore dropped columns */ - if (TupleDescAttr(tupDesc, att - 1)->attisdropped) + if (TupleDescCompactAttr(tupDesc, att - 1)->attisdropped) continue; if (heap_attisnull(&tmptup, att, tupDesc)) { @@ -5296,8 +5296,8 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext) for (int i = 0; i < var_tupdesc->natts; i++) { - Form_pg_attribute vattr = TupleDescAttr(var_tupdesc, i); - Form_pg_attribute sattr = TupleDescAttr(tupleDesc, i); + CompactAttribute *vattr = TupleDescCompactAttr(var_tupdesc, i); + CompactAttribute *sattr = TupleDescCompactAttr(tupleDesc, i); if (!vattr->attisdropped) continue; /* already checked non-dropped cols */ diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c index b962c313834..7d0afca4181 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -169,7 +169,7 @@ ExecInitJunkFilterConversion(List *targetList, t = list_head(targetList); for (i = 0; i < cleanLength; i++) { - if (TupleDescAttr(cleanTupType, i)->attisdropped) + if (TupleDescCompactAttr(cleanTupType, i)->attisdropped) continue; /* map entry is already zero */ for (;;) { diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 00dc3396156..875515777b6 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -187,7 +187,7 @@ 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); + CompactAttribute *att = TupleDescCompactAttr(desc, natt); Datum val; if (att->attbyval || slot->tts_isnull[natt]) @@ -223,7 +223,7 @@ 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); + CompactAttribute *att = TupleDescCompactAttr(desc, natt); Datum val; if (att->attbyval || slot->tts_isnull[natt]) @@ -1044,7 +1044,7 @@ slot_deform_heap_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, for (; attnum < natts; attnum++) { - Form_pg_attribute thisatt = TupleDescAttr(tupleDesc, attnum); + CompactAttribute *thisatt = TupleDescCompactAttr(tupleDesc, attnum); if (hasnulls && att_isnull(attnum, bp)) { @@ -2237,7 +2237,7 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) */ for (i = 0; i < natts; i++) { - if (!TupleDescAttr(tupdesc, i)->attisdropped) + if (!TupleDescCompactAttr(tupdesc, i)->attisdropped) { /* Non-dropped attributes */ dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i], diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 3b2f78b2197..3a2d51c5ad0 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1886,7 +1886,7 @@ check_sql_fn_retval(List *queryTreeLists, /* remaining columns in rettupdesc had better all be dropped */ for (colindex++; colindex <= tupnatts; colindex++) { - if (!TupleDescAttr(rettupdesc, colindex - 1)->attisdropped) + if (!TupleDescCompactAttr(rettupdesc, colindex - 1)->attisdropped) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("return type mismatch in function declared to return %s", diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c index df8e3fff082..aff3d9d921d 100644 --- a/src/backend/executor/nodeMemoize.c +++ b/src/backend/executor/nodeMemoize.c @@ -175,10 +175,10 @@ MemoizeHash_hash(struct memoize_hash *tb, const MemoizeKey *key) if (!pslot->tts_isnull[i]) /* treat nulls as having hash key 0 */ { - Form_pg_attribute attr; + CompactAttribute *attr; uint32 hkey; - attr = TupleDescAttr(pslot->tts_tupleDescriptor, i); + attr = TupleDescCompactAttr(pslot->tts_tupleDescriptor, i); hkey = datum_image_hash(pslot->tts_values[i], attr->attbyval, attr->attlen); @@ -242,7 +242,7 @@ MemoizeHash_equal(struct memoize_hash *tb, const MemoizeKey *key1, for (int i = 0; i < numkeys; i++) { - Form_pg_attribute attr; + CompactAttribute *attr; if (tslot->tts_isnull[i] != pslot->tts_isnull[i]) { @@ -255,7 +255,7 @@ MemoizeHash_equal(struct memoize_hash *tb, const MemoizeKey *key1, continue; /* perform binary comparison on the two datums */ - attr = TupleDescAttr(tslot->tts_tupleDescriptor, i); + attr = TupleDescCompactAttr(tslot->tts_tupleDescriptor, i); if (!datum_image_eq(tslot->tts_values[i], pslot->tts_values[i], attr->attbyval, attr->attlen)) { diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 1161520f76b..c445c433df4 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -496,14 +496,14 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, for (int i = 0; i < natts; i++) { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i); if (ri_GeneratedExprs[i]) { Datum val; bool isnull; - Assert(attr->attgenerated == ATTRIBUTE_GENERATED_STORED); + Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED); econtext->ecxt_scantuple = slot; diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 92948917a08..9838977f08a 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -142,8 +142,8 @@ ValuesNext(ValuesScanState *node) foreach(lc, exprstatelist) { ExprState *estate = (ExprState *) lfirst(lc); - Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, - resind); + CompactAttribute *attr = TupleDescCompactAttr(slot->tts_tupleDescriptor, + resind); values[resind] = ExecEvalExpr(estate, econtext, diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c index de4646b5c26..658d1724c83 100644 --- a/src/backend/executor/tstoreReceiver.c +++ b/src/backend/executor/tstoreReceiver.c @@ -65,7 +65,7 @@ tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo) { for (i = 0; i < natts; i++) { - Form_pg_attribute attr = TupleDescAttr(typeinfo, i); + CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i); if (attr->attisdropped) continue; @@ -154,7 +154,7 @@ tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self) for (i = 0; i < natts; i++) { Datum val = slot->tts_values[i]; - Form_pg_attribute attr = TupleDescAttr(typeinfo, i); + CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i); if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i]) { diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c index b07f8e7f756..f49e7bce7d0 100644 --- a/src/backend/jit/llvm/llvmjit_deform.c +++ b/src/backend/jit/llvm/llvmjit_deform.c @@ -110,7 +110,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, */ for (attnum = 0; attnum < desc->natts; attnum++) { - Form_pg_attribute att = TupleDescAttr(desc, attnum); + CompactAttribute *att = TupleDescCompactAttr(desc, attnum); /* * If the column is declared NOT NULL then it must be present in every @@ -393,7 +393,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, */ for (attnum = 0; attnum < natts; attnum++) { - Form_pg_attribute att = TupleDescAttr(desc, attnum); + CompactAttribute *att = TupleDescCompactAttr(desc, attnum); LLVMValueRef v_incby; int alignto; LLVMValueRef l_attno = l_int16_const(lc, attnum); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 153390f2dc9..c31cc3ee69f 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -175,12 +175,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, { for (int i = 0; i < relation->rd_att->natts; i++) { - Form_pg_attribute attr = TupleDescAttr(relation->rd_att, i); + CompactAttribute *attr = TupleDescCompactAttr(relation->rd_att, i); if (attr->attnotnull) { rel->notnullattnums = bms_add_member(rel->notnullattnums, - attr->attnum); + i + 1); /* * Per RemoveAttributeById(), dropped columns will have their diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index b50b3d62e3d..6db780d733c 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1344,7 +1344,7 @@ pgoutput_row_filter(Relation relation, TupleTableSlot *old_slot, */ for (i = 0; i < desc->natts; i++) { - Form_pg_attribute att = TupleDescAttr(desc, i); + CompactAttribute *att = TupleDescCompactAttr(desc, i); /* * if the column in the new tuple or old tuple is null, nothing to do diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index d2842495b57..913c08b06e0 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -699,7 +699,7 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) { for (i = 0; i < erh->nfields; i++) { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i); if (!erh->dnulls[i] && !attr->attbyval && attr->attlen == -1 && @@ -1115,7 +1115,7 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, bool check_constraints) { TupleDesc tupdesc; - Form_pg_attribute attr; + CompactAttribute *attr; Datum *dvalues; bool *dnulls; char *oldValue; @@ -1146,7 +1146,7 @@ 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); + attr = TupleDescCompactAttr(tupdesc, fnumber - 1); if (!isnull && !attr->attbyval) { MemoryContext oldcxt; @@ -1279,7 +1279,7 @@ expanded_record_set_fields(ExpandedRecordHeader *erh, for (fnumber = 0; fnumber < erh->nfields; fnumber++) { - Form_pg_attribute attr = TupleDescAttr(tupdesc, fnumber); + CompactAttribute *attr = TupleDescCompactAttr(tupdesc, fnumber); Datum newValue; bool isnull; @@ -1541,7 +1541,7 @@ check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber, */ if (!isnull) { - Form_pg_attribute attr = TupleDescAttr(erh->er_tupdesc, fnumber - 1); + CompactAttribute *attr = TupleDescCompactAttr(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 3185f48afa6..093a3f1b66b 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -2932,7 +2932,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); + CompactAttribute *att = TupleDescCompactAttr(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/relcache.c b/src/backend/utils/cache/relcache.c index 422509f18d7..5658e4accbd 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -585,6 +585,8 @@ RelationBuildTupleDesc(Relation relation) attp, ATTRIBUTE_FIXED_PART_SIZE); + populate_compact_attribute(relation->rd_att, attnum - 1); + /* Update constraint/default info */ if (attp->attnotnull) constr->has_not_null = true; @@ -661,8 +663,7 @@ RelationBuildTupleDesc(Relation relation) /* * The attcacheoff values we read from pg_attribute should all be -1 - * ("unknown"). Verify this if assert checking is on. They will be - * computed when and if needed during tuple access. + * ("unknown"). Verify this if assert checking is on. */ #ifdef USE_ASSERT_CHECKING { @@ -674,12 +675,12 @@ RelationBuildTupleDesc(Relation relation) #endif /* - * However, we can easily set the attcacheoff value for the first - * attribute: it must be zero. This eliminates the need for special cases - * for attnum=1 that used to exist in fastgetattr() and index_getattr(). + * We can easily set the attcacheoff value for the first attribute: it + * must be zero. This eliminates the need for special cases for attnum=1 + * that used to exist in fastgetattr() and index_getattr(). */ if (RelationGetNumberOfAttributes(relation) > 0) - TupleDescAttr(relation->rd_att, 0)->attcacheoff = 0; + TupleDescCompactAttr(relation->rd_att, 0)->attcacheoff = 0; /* * Set up constraint/default info @@ -1965,10 +1966,12 @@ formrdesc(const char *relationName, Oid relationReltype, has_not_null |= attrs[i].attnotnull; /* make sure attcacheoff is valid */ TupleDescAttr(relation->rd_att, i)->attcacheoff = -1; + + populate_compact_attribute(relation->rd_att, i); } /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */ - TupleDescAttr(relation->rd_att, 0)->attcacheoff = 0; + TupleDescCompactAttr(relation->rd_att, 0)->attcacheoff = 0; /* mark not-null status */ if (has_not_null) @@ -3579,6 +3582,7 @@ RelationBuildLocalRelation(const char *relname, datt->attgenerated = satt->attgenerated; datt->attnotnull = satt->attnotnull; has_not_null |= satt->attnotnull; + populate_compact_attribute(rel->rd_att, i); } if (has_not_null) @@ -4399,10 +4403,12 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs) memcpy(TupleDescAttr(result, i), &attrs[i], ATTRIBUTE_FIXED_PART_SIZE); /* make sure attcacheoff is valid */ TupleDescAttr(result, i)->attcacheoff = -1; + + populate_compact_attribute(result, i); } /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */ - TupleDescAttr(result, 0)->attcacheoff = 0; + TupleDescCompactAttr(result, 0)->attcacheoff = 0; /* Note: we don't bother to set up a TupleConstr entry */ @@ -6199,6 +6205,8 @@ load_relcache_init_file(bool shared) goto read_failed; has_not_null |= attr->attnotnull; + + populate_compact_attribute(rel->rd_att, i); } /* next read the access method specific field */ diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index 5e38ef86969..0d1adff540f 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -758,9 +758,9 @@ fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) *isnull = false; if (HeapTupleNoNulls(tup)) { - Form_pg_attribute att; + CompactAttribute *att; - att = TupleDescAttr(tupleDesc, attnum - 1); + att = TupleDescCompactAttr(tupleDesc, attnum - 1); if (att->attcacheoff >= 0) return fetchatt(att, (char *) tup->t_data + tup->t_data->t_hoff + att->attcacheoff); diff --git a/src/include/access/itup.h b/src/include/access/itup.h index 94885751e59..4393b19a7fd 100644 --- a/src/include/access/itup.h +++ b/src/include/access/itup.h @@ -124,11 +124,13 @@ index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) if (!IndexTupleHasNulls(tup)) { - if (TupleDescAttr(tupleDesc, attnum - 1)->attcacheoff >= 0) + CompactAttribute *attr = TupleDescCompactAttr(tupleDesc, attnum - 1); + + if (attr->attcacheoff >= 0) { - return fetchatt(TupleDescAttr(tupleDesc, attnum - 1), - (char *) tup + IndexInfoFindDataOffset(tup->t_info) - + TupleDescAttr(tupleDesc, attnum - 1)->attcacheoff); + return fetchatt(attr, + (char *) tup + IndexInfoFindDataOffset(tup->t_info) + + attr->attcacheoff); } else return nocache_index_getattr(tup, attnum, tupleDesc); diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 8930a28d660..4406617fea6 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -46,6 +46,39 @@ typedef struct TupleConstr } TupleConstr; /* + * CompactAttribute + * Cut-down version of FormData_pg_attribute for faster access for tasks + * such as tuple deformation. The fields of this struct are populated + * using the populate_compact_attribute() function, which must be called + * directly after the FormData_pg_attribute struct is populated or + * altered in any way. + * + * Currently, this struct is 16 bytes. Any code changes which enlarge this + * struct should be considered very carefully. + * + * Code which must access a TupleDesc's attribute data should always make use + * the fields of this struct when required fields are available here. It's + * more efficient to access the memory in CompactAttribute due to it being a + * more compact representation of FormData_pg_attribute and also because + * accessing the FormData_pg_attribute requires an additional calculations to + * obtain the base address of the array within the TupleDesc. + */ +typedef struct CompactAttribute +{ + int32 attcacheoff; /* fixed offset into tuple, if known, or -1 */ + int16 attlen; /* attr len in bytes or -1 = varlen, -2 = + * cstring */ + bool attbyval; /* as FormData_pg_attribute.attbyval */ + bool attispackable; /* FormData_pg_attribute.attstorage != + * TYPSTORAGE_PLAIN */ + bool atthasmissing; /* as FormData_pg_attribute.atthasmissing */ + bool attisdropped; /* as FormData_pg_attribute.attisdropped */ + bool attgenerated; /* FormData_pg_attribute.attgenerated != '\0' */ + bool attnotnull; /* as FormData_pg_attribute.attnotnull */ + char attalign; /* alignment requirement */ +} CompactAttribute; + +/* * This struct is passed around within the backend to describe the structure * of tuples. For tuples coming from on-disk relations, the information is * collected from the pg_attribute, pg_attrdef, and pg_constraint catalogs. @@ -75,6 +108,21 @@ typedef struct TupleConstr * context and go away when the context is freed. We set the tdrefcount * field of such a descriptor to -1, while reference-counted descriptors * always have tdrefcount >= 0. + * + * Beyond the compact_attrs variable length array, the TupleDesc stores an + * array of FormData_pg_attribute. The TupleDescAttr() function, as defined + * below, takes care of calculating the address of the elements of the + * FormData_pg_attribute array. + * + * The array of CompactAttribute is effectively an abbreviated version of the + * array of FormData_pg_attribute. Because CompactAttribute is significantly + * smaller than FormData_pg_attribute, code, especially performance-critical + * code, should prioritize using the fields from the CompactAttribute over the + * equivalent fields in FormData_pg_attribute. + * + * Any code making changes manually to and fields in the FormData_pg_attribute + * array must subsequently call populate_compact_attribute() to flush the + * changes out to the corresponding 'compact_attrs' element. */ typedef struct TupleDescData { @@ -83,13 +131,68 @@ typedef struct TupleDescData int32 tdtypmod; /* typmod for tuple type */ int tdrefcount; /* reference count, or -1 if not counting */ TupleConstr *constr; /* constraints, or NULL if none */ - /* attrs[N] is the description of Attribute Number N+1 */ - FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER]; + /* compact_attrs[N] is the compact metadata of Attribute Number N+1 */ + CompactAttribute compact_attrs[FLEXIBLE_ARRAY_MEMBER]; } TupleDescData; typedef struct TupleDescData *TupleDesc; -/* Accessor for the i'th attribute of tupdesc. */ -#define TupleDescAttr(tupdesc, i) (&(tupdesc)->attrs[(i)]) +extern void populate_compact_attribute(TupleDesc tupdesc, int attnum); + +/* + * Calculates the base address of the Form_pg_attribute at the end of the + * TupleDescData struct. + */ +#define TupleDescAttrAddress(desc) \ + (Form_pg_attribute) ((char *) (desc) + \ + (offsetof(struct TupleDescData, compact_attrs) + \ + (desc)->natts * sizeof(CompactAttribute))) + +/* Accessor for the i'th FormData_pg_attribute element of tupdesc. */ +static inline FormData_pg_attribute * +TupleDescAttr(TupleDesc tupdesc, int i) +{ + FormData_pg_attribute *attrs = TupleDescAttrAddress(tupdesc); + + return &attrs[i]; +} + +#undef TupleDescAttrAddress + +/* + * Accessor for the i'th CompactAttribute element of tupdesc. + */ +static inline CompactAttribute * +TupleDescCompactAttr(TupleDesc tupdesc, int i) +{ + CompactAttribute *cattr = &tupdesc->compact_attrs[i]; +#ifdef USE_ASSERT_CHECKING + CompactAttribute snapshot; + + /* + * In Assert enabled builds we verify that the CompactAttribute is + * populated correctly. This helps find bugs in places such as ALTER + * TABLE where code makes changes to the FormData_pg_attribute but forgets + * to call populate_compact_attribute. + */ + + /* + * Take a snapshot of how the CompactAttribute is now before calling + * populate_compact_attribute to make it up-to-date with the + * FormData_pg_attribute. + */ + memcpy(&snapshot, cattr, sizeof(CompactAttribute)); + + populate_compact_attribute(tupdesc, i); + + /* reset attcacheoff back to what it was */ + cattr->attcacheoff = snapshot.attcacheoff; + + /* Ensure the snapshot matches the freshly populated CompactAttribute */ + Assert(memcmp(&snapshot, cattr, sizeof(CompactAttribute)) == 0); +#endif + + return cattr; +} extern TupleDesc CreateTemplateTupleDesc(int natts); @@ -97,10 +200,13 @@ extern TupleDesc CreateTupleDesc(int natts, Form_pg_attribute *attrs); extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); +extern TupleDesc CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts); + extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc); #define TupleDescSize(src) \ - (offsetof(struct TupleDescData, attrs) + \ + (offsetof(struct TupleDescData, compact_attrs) + \ + (src)->natts * sizeof(CompactAttribute) + \ (src)->natts * sizeof(FormData_pg_attribute)) extern void TupleDescCopy(TupleDesc dst, TupleDesc src); diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h index 58b3a58cfd0..fcf09ed95f4 100644 --- a/src/include/access/tupmacs.h +++ b/src/include/access/tupmacs.h @@ -30,13 +30,14 @@ att_isnull(int ATT, const bits8 *BITS) #ifndef FRONTEND /* - * Given a Form_pg_attribute and a pointer into a tuple's data area, - * return the correct value or pointer. + * Given an attbyval and an attlen from either a Form_pg_attribute or + * CompactAttribute and a pointer into a tuple's data area, return the + * correct value or pointer. * - * We return a Datum value in all cases. If the attribute has "byval" false, - * we return the same pointer into the tuple data area that we're passed. - * Otherwise, we return the correct number of bytes fetched from the data - * area and extended to Datum form. + * We return a Datum value in all cases. If attbyval is false, we return the + * same pointer into the tuple data area that we're passed. Otherwise, we + * return the correct number of bytes fetched from the data area and extended + * to Datum form. * * On machines where Datum is 8 bytes, we support fetching 8-byte byval * attributes; otherwise, only 1, 2, and 4-byte values are supported. diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index fbdb932e6b6..e1c4f913f84 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -454,6 +454,7 @@ CommitTimestampEntry CommitTimestampShared CommonEntry CommonTableExpr +CompactAttribute CompareScalarsContext CompiledExprState CompositeIOData |