Introduce CompactAttribute array in TupleDesc, take 2
authorDavid Rowley <drowley@postgresql.org>
Fri, 20 Dec 2024 09:31:26 +0000 (22:31 +1300)
committerDavid Rowley <drowley@postgresql.org>
Fri, 20 Dec 2024 09:31:26 +0000 (22:31 +1300)
The new compact_attrs array stores a few select fields from
FormData_pg_attribute in a more compact way, using only 16 bytes per
column instead of the 104 bytes that FormData_pg_attribute uses.  Using
CompactAttribute allows performance-critical operations such as tuple
deformation to be performed without looking at the FormData_pg_attribute
element in TupleDesc which means fewer cacheline accesses.

For some workloads, tuple deformation can be the most CPU intensive part
of processing the query.  Some testing with 16 columns on a table
where the first column is variable length showed around a 10% increase in
transactions per second for an OLAP type query performing aggregation on
the 16th column.  However, in certain cases, the increases were much
higher, up to ~25% on one AMD Zen4 machine.

This also makes pg_attribute.attcacheoff redundant.  A follow-on commit
will remove it, thus shrinking the FormData_pg_attribute struct by 4
bytes.

Author: David Rowley
Reviewed-by: Andres Freund, Victor Yegorov
Discussion: https://postgr.es/m/CAApHDvrBztXP3yx=NKNmo3xwFAFhEdyPnvrDg3=M0RhDs+4vYw@mail.gmail.com

44 files changed:
contrib/amcheck/verify_heapam.c
contrib/pageinspect/gistfuncs.c
contrib/pageinspect/heapfuncs.c
contrib/postgres_fdw/postgres_fdw.c
src/backend/access/brin/brin_inclusion.c
src/backend/access/brin/brin_tuple.c
src/backend/access/common/attmap.c
src/backend/access/common/heaptuple.c
src/backend/access/common/indextuple.c
src/backend/access/common/tupdesc.c
src/backend/access/gin/ginbulk.c
src/backend/access/gin/ginget.c
src/backend/access/gist/gist.c
src/backend/access/gist/gistbuild.c
src/backend/access/heap/heapam.c
src/backend/access/heap/heapam_handler.c
src/backend/access/heap/heaptoast.c
src/backend/access/nbtree/nbtutils.c
src/backend/access/spgist/spgdoinsert.c
src/backend/access/spgist/spgutils.c
src/backend/access/table/toast_helper.c
src/backend/catalog/index.c
src/backend/commands/copy.c
src/backend/commands/tablecmds.c
src/backend/executor/execExpr.c
src/backend/executor/execExprInterp.c
src/backend/executor/execJunk.c
src/backend/executor/execTuples.c
src/backend/executor/functions.c
src/backend/executor/nodeMemoize.c
src/backend/executor/nodeModifyTable.c
src/backend/executor/nodeValuesscan.c
src/backend/executor/tstoreReceiver.c
src/backend/jit/llvm/llvmjit_deform.c
src/backend/optimizer/util/plancat.c
src/backend/replication/pgoutput/pgoutput.c
src/backend/utils/adt/expandedrecord.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/cache/relcache.c
src/include/access/htup_details.h
src/include/access/itup.h
src/include/access/tupdesc.h
src/include/access/tupmacs.h
src/tools/pgindent/typedefs.list

index e16557aca36493326b73af0afe18d47353e12314..c1847d1b8a408499c2bfa0d5ff153b1a3b53a909 100644 (file)
@@ -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;
 
index 029072567a4484d2508abcfa74031ba0e216f79b..68d59cb3ecd1662bb4776c0659026b20b6fb9b96 100644 (file)
@@ -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;
        }
 
index 3dc705e43f7ff1826e92387caaf02e4624bca850..8c1b7d38aa6b1c13ac06c7950c0c652575e00fd9 100644 (file)
@@ -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
index c0810fbd7c87f5b16f358fda536faf22dbbd5e2e..cf564341184256f3b5ed1724c51d686c9045d0c7 100644 (file)
@@ -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;
 
index 750276998c5a18b585d0a574dda6afafa93657ac..faec0a9da8453f6550908c27b65a853c33ebbfb6 100644 (file)
@@ -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]) &&
index 997eb6d822cd67ffb68506d0184c14aa497e02e0..aae646be5ddfcadf1b0269bdb4d831d0d597630f 100644 (file)
@@ -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)
                        {
index b0fe27ef57da419f4a968ed11fb45ae33700dd7d..0805c4121ee0af11d959a709f03289d9708b3942 100644 (file)
@@ -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
index 9e3407bf987aa2a0c9c40172473c04a7c8d085e8..982e7222c49bafd0cfcda79e99173eca242135a1 100644 (file)
 #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))
                {
index bb2c6a2bcc15da4de3925ca2be1db46732a0afe0..38aeb230879693abf7b15d0c56e5681c4dae825c 100644 (file)
@@ -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);
index 47379fef220b7a26f5c62f1c949af8406b268dbd..4d89acbe5e2f9dd16a6d9bed6d36bb97c5900a14 100644 (file)
@@ -56,6 +56,33 @@ ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
        ResourceOwnerForget(owner, PointerGetDatum(tupdesc), &tupdesc_resowner_desc);
 }
 
+/*
+ * 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);
 }
 
 /*
index f08b66ab7914f3e2beb3ded8f0de8399bf685e3b..d86b75bd6392150aace324d7f90959dfb3f2ef67 100644 (file)
@@ -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
index e8e0eab655213c47c2e490ea176d266bd4b5e33a..c11ae0029c2bd7a60ef4194b427ff62c9b887fbe 100644 (file)
@@ -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
index ebc65449c1bb7eb9b49e6930d9d3137568163419..272390ff67d2c4017224c2eb5add93142ecb8fc2 100644 (file)
@@ -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++)
        {
index 63d1914b37f6f9bff641535dd8efc85e2fb3d4aa..3a2759b446873dc0a8daf80ed40b5e05a05a9744 100644 (file)
@@ -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 */
index 6cdc68d981a47927156ec72c18eeee3bfab85c93..329e727f80d7cbec5157342237fd70fb9a3e9537 100644 (file)
@@ -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;
 
                /*
index 2da4e4da13e07dae589c629f83614ef55f2eb008..9f17baea5d6ecec7f84b699c597d8065aca75d1c 100644 (file)
@@ -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;
        }
 
index aae72bc2abf72af1dd7b146045c8cf9f8a6c6ea5..eda81a35d50a540e95eefba063a13c62ec3a30b2 100644 (file)
@@ -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;
 
index afff7d6392285e36220cdc2df7e3ae4697eb50e5..a531d37908ad515496654394e108dae236943fea 100644 (file)
@@ -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;
index a4995c168b44dcb17afab40b3472e38523fae267..2cb7ce43ba12bbe4c16b2fa43775a9c411854c92 100644 (file)
@@ -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];
index e93d9869b27df58664c996e64a63cc99b5fab812..da858182173ffc90924ef1c8bdf2555dabedc2ae 100644 (file)
@@ -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;
 }
index 53224932f0db4ea55f9ba6238062e0df54eb0aec..b16fd21b8d015f4e08c196c8f1a732bb8c9d1d14 100644 (file)
@@ -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];
 
index 05dc6add7eb7b4819545a143294af7b9630bec59..6200a0da5013b0b65b00b4f48e0f2d7fcd536350 100644 (file)
@@ -477,6 +477,8 @@ ConstructTupleDescriptor(Relation heapRelation,
 
                        ReleaseSysCache(tuple);
                }
+
+               populate_compact_attribute(indexTupDesc, i);
        }
 
        pfree(amroutine);
index 2d98ecf3f4e8c294a96368f2a0da8bd65894703b..edcdb7c2d40878477a43928422eb868e7315a1be 100644 (file)
@@ -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);
                }
index 6ccae4cb4a8613b0a93d4b38a752c7475e92029c..49374782625c2339a84a0c5119f09ccc494d6cb3 100644 (file)
@@ -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;
index 81714341f07b784284895ecd17be2d3ac6ab42a4..3d01a90bd64ee89f0694b5c8b5e494ca1e722f6d 100644 (file)
@@ -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)
                {
index 60dcbcbe596e6db881ea27d33abfc756f065b240..56e13d20a87eb1570a6009e815945f7d9b949a44 100644 (file)
@@ -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 */
index b962c3138345f8850aaf737a982caf6e17e58a1a..7d0afca4181ae8ad04d580c6f36eb03a0834eaf1 100644 (file)
@@ -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 (;;)
                        {
index 00dc3396156c5add54524fe950307bda3561f835..875515777b6f282fdbbf952fbf1abf5f36bd76fa 100644 (file)
@@ -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],
index 3b2f78b2197f6164c3d103d14f8bfcc4f1ac7282..3a2d51c5ad0de420c19d9a9da01f93bba8c61331 100644 (file)
@@ -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",
index df8e3fff0824bf25e4fdcca9cd27d1045b5a33d9..aff3d9d921de0c3f605970b08bd8798f1ea30df1 100644 (file)
@@ -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))
                        {
index 1161520f76bbd2a7821c4ecb99155c1a61de9b96..c445c433df4a510b77d06783b41eb30081986cc7 100644 (file)
@@ -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;
 
index 92948917a08b41c5bef3c25a1f18e97e2d23e60b..9838977f08a868c5088d18217ea9d6732b7b19a9 100644 (file)
@@ -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,
index de4646b5c26efb9f64b01750ee71e3c4226df183..658d1724c838528879f396be5747efa77e4c6f38 100644 (file)
@@ -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])
                {
index b07f8e7f75675dc369eb096472549a7c49747056..f49e7bce7d0d18ad9e32760bc5e040a7c6a17cec 100644 (file)
@@ -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);
index 153390f2dc92d02a31b3ca0c98ae5ad603fd5880..c31cc3ee69f21989c3d4e37e54c5d08780bbcf1a 100644 (file)
@@ -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
index b50b3d62e3dbb9b43c6475c7ed492f059d35be0e..6db780d733c6e65578b7eebb4a6642acbb442f41 100644 (file)
@@ -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
index d2842495b5761484d8d68d94ccd33f3dee45ac4c..913c08b06e07c1b7f20080b34c46cc60cec3eb3d 100644 (file)
@@ -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)))
index 3185f48afa6b86942cf3e639710d4f43976f4216..093a3f1b66ba00a30e6310ac2ebf6f70f6e92134 100644 (file)
@@ -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;
index 422509f18d786faf28db6cb890d3af64466b7046..5658e4accbd259044446a079e54e705b779628d9 100644 (file)
@@ -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 */
index 5e38ef869697ace7567dec9c648bfb4884f8b6a7..0d1adff540f7b9c4239aa2594ff5fdac2262a56a 100644 (file)
@@ -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);
index 94885751e590f93eba33dc7ebfd198de9df047c2..4393b19a7fdecd04a7a9bc7de3f5d260a65d4575 100644 (file)
@@ -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);
index 8930a28d6602eaa37120b33b51549122a6357a1c..4406617fea6802ec880c61cd49b0687d8615f50e 100644 (file)
@@ -45,6 +45,39 @@ typedef struct TupleConstr
        bool            has_generated_stored;
 } 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
@@ -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);
index 58b3a58cfd06c064a2071609ca43ba1ea9eaf167..fcf09ed95f43c51b1786d8db4ed75ee3f738a2e6 100644 (file)
@@ -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.
index fbdb932e6b64298d5b70041dd9e5941ef7c5029e..e1c4f913f848ccc468e9c4fdc120ca74f961f60f 100644 (file)
@@ -454,6 +454,7 @@ CommitTimestampEntry
 CommitTimestampShared
 CommonEntry
 CommonTableExpr
+CompactAttribute
 CompareScalarsContext
 CompiledExprState
 CompositeIOData