Re-order pg_attribute columns to eliminate some padding space.
authorTom Lane <[email protected]>
Sun, 23 May 2021 16:12:09 +0000 (12:12 -0400)
committerTom Lane <[email protected]>
Sun, 23 May 2021 16:12:09 +0000 (12:12 -0400)
Now that attcompression is just a char, there's a lot of wasted
padding space after it.  Move it into the group of char-wide
columns to save a net of 4 bytes per pg_attribute entry.  While
we're at it, swap the order of attstorage and attalign to make for
a more logical grouping of these columns.

Also re-order actions in related code to match the new field ordering.

This patch also fixes one outright bug: equalTupleDescs() failed to
compare attcompression.  That could, for example, cause relcache
reload to fail to adopt a new value following a change.

Michael Paquier and Tom Lane, per a gripe from Andres Freund.

Discussion: https://postgr.es/m/20210517204803[email protected]

doc/src/sgml/catalogs.sgml
src/backend/access/common/tupdesc.c
src/backend/access/spgist/spgutils.c
src/backend/bootstrap/bootstrap.c
src/backend/catalog/genbki.pl
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/commands/tablecmds.c
src/include/access/spgist_private.h
src/include/catalog/catversion.h
src/include/catalog/pg_attribute.h

index 6d06ad22b92b4cd583b4feff39adc8597700c75a..8aebc4d12f426aaf682dfc6f7fd24062f211a736 100644 (file)
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>attalign</structfield> <type>char</type>
+      </para>
+      <para>
+       A copy of <literal>pg_type.typalign</literal> of this column's type
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>attstorage</structfield> <type>char</type>
 
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
-       <structfield>attalign</structfield> <type>char</type>
+       <structfield>attcompression</structfield> <type>char</type>
       </para>
       <para>
-       A copy of <literal>pg_type.typalign</literal> of this column's type
+       The current compression method of the column.  If it is an invalid
+       compression method (<literal>'\0'</literal>) then column data will not
+       be compressed.  Otherwise, <literal>'p'</literal> = pglz compression or
+       <literal>'l'</literal> = <productname>LZ4</productname> compression.
       </para></entry>
      </row>
 
       </para></entry>
      </row>
 
-     <row>
-      <entry role="catalog_table_entry"><para role="column_definition">
-       <structfield>attcompression</structfield> <type>char</type>
-      </para>
-      <para>
-       The current compression method of the column.  If it is an invalid
-       compression method (<literal>'\0'</literal>) then column data will not
-       be compressed.  Otherwise, <literal>'p'</literal> = pglz compression or
-       <literal>'l'</literal> = <productname>LZ4</productname> compression.
-      </para></entry>
-     </row>
-
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>attacl</structfield> <type>aclitem[]</type>
index ffb275afbee7ae0b19b9f777fb62af628553adee..affbc509bdc681b3294f46762962984b40be79cd 100644 (file)
@@ -424,7 +424,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
         * general it seems safer to check them always.
         *
         * attcacheoff must NOT be checked since it's possibly not set in both
-        * copies.
+        * copies.  We also intentionally ignore atthasmissing, since that's
+        * not very relevant in tupdescs, which lack the attmissingval field.
         */
        if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
            return false;
@@ -440,9 +441,11 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
            return false;
        if (attr1->attbyval != attr2->attbyval)
            return false;
+       if (attr1->attalign != attr2->attalign)
+           return false;
        if (attr1->attstorage != attr2->attstorage)
            return false;
-       if (attr1->attalign != attr2->attalign)
+       if (attr1->attcompression != attr2->attcompression)
            return false;
        if (attr1->attnotnull != attr2->attnotnull)
            return false;
@@ -460,7 +463,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
            return false;
        if (attr1->attcollation != attr2->attcollation)
            return false;
-       /* attacl, attoptions and attfdwoptions are not even present... */
+       /* variable-length fields are not even present... */
    }
 
    if (tupdesc1->constr != NULL)
@@ -639,12 +642,11 @@ TupleDescInitEntry(TupleDesc desc,
    att->attbyval = typeForm->typbyval;
    att->attalign = typeForm->typalign;
    att->attstorage = typeForm->typstorage;
-   att->attcollation = typeForm->typcollation;
-
    if (IsStorageCompressible(typeForm->typstorage))
        att->attcompression = GetDefaultToastCompression();
    else
        att->attcompression = InvalidCompressionMethod;
+   att->attcollation = typeForm->typcollation;
 
    ReleaseSysCache(tuple);
 }
@@ -709,6 +711,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
            att->attbyval = false;
            att->attalign = TYPALIGN_INT;
            att->attstorage = TYPSTORAGE_EXTENDED;
+           att->attcompression = GetDefaultToastCompression();
            att->attcollation = DEFAULT_COLLATION_OID;
            break;
 
@@ -717,6 +720,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
            att->attbyval = true;
            att->attalign = TYPALIGN_CHAR;
            att->attstorage = TYPSTORAGE_PLAIN;
+           att->attcompression = InvalidCompressionMethod;
            att->attcollation = InvalidOid;
            break;
 
@@ -725,6 +729,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
            att->attbyval = true;
            att->attalign = TYPALIGN_INT;
            att->attstorage = TYPSTORAGE_PLAIN;
+           att->attcompression = InvalidCompressionMethod;
            att->attcollation = InvalidOid;
            break;
 
@@ -733,6 +738,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
            att->attbyval = FLOAT8PASSBYVAL;
            att->attalign = TYPALIGN_DOUBLE;
            att->attstorage = TYPSTORAGE_PLAIN;
+           att->attcompression = InvalidCompressionMethod;
            att->attcollation = InvalidOid;
            break;
 
index 8d99c9b762622b77acde99fa82c996e153c6c448..9ff280a2526badd0ac3e0c732eff148c4bf5ee32 100644 (file)
@@ -165,8 +165,8 @@ fillTypeDesc(SpGistTypeDesc *desc, Oid type)
    typtup = (Form_pg_type) GETSTRUCT(tp);
    desc->attlen = typtup->typlen;
    desc->attbyval = typtup->typbyval;
-   desc->attstorage = typtup->typstorage;
    desc->attalign = typtup->typalign;
+   desc->attstorage = typtup->typstorage;
    ReleaseSysCache(tp);
 }
 
@@ -304,8 +304,8 @@ getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
        att->attalign = keyType->attalign;
        att->attstorage = keyType->attstorage;
        /* We shouldn't need to bother with making these valid: */
-       att->attcollation = InvalidOid;
        att->attcompression = InvalidCompressionMethod;
+       att->attcollation = InvalidOid;
        /* In case we changed typlen, we'd better reset following offsets */
        for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++)
            TupleDescAttr(outTupDesc, i)->attcacheoff = -1;
index b1552374884df9dee07c2c215f3ce855f2aa4ca4..62abd008ccf0065c55a5a207d498440684d0ad8c 100644 (file)
@@ -699,8 +699,8 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
        attrtypes[attnum]->atttypid = Ap->am_oid;
        attrtypes[attnum]->attlen = Ap->am_typ.typlen;
        attrtypes[attnum]->attbyval = Ap->am_typ.typbyval;
-       attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;
        attrtypes[attnum]->attalign = Ap->am_typ.typalign;
+       attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;
        attrtypes[attnum]->attcollation = Ap->am_typ.typcollation;
        /* if an array type, assume 1-dimensional attribute */
        if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0)
@@ -713,8 +713,8 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
        attrtypes[attnum]->atttypid = TypInfo[typeoid].oid;
        attrtypes[attnum]->attlen = TypInfo[typeoid].len;
        attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
-       attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
        attrtypes[attnum]->attalign = TypInfo[typeoid].align;
+       attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
        attrtypes[attnum]->attcollation = TypInfo[typeoid].collation;
        /* if an array type, assume 1-dimensional attribute */
        if (TypInfo[typeoid].elem != InvalidOid &&
@@ -724,6 +724,11 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
            attrtypes[attnum]->attndims = 0;
    }
 
+   if (IsStorageCompressible(attrtypes[attnum]->attstorage))
+       attrtypes[attnum]->attcompression = GetDefaultToastCompression();
+   else
+       attrtypes[attnum]->attcompression = InvalidCompressionMethod;
+
    /*
     * If a system catalog column is collation-aware, force it to use C
     * collation, so that its behavior is independent of the database's
@@ -737,10 +742,6 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
    attrtypes[attnum]->attcacheoff = -1;
    attrtypes[attnum]->atttypmod = -1;
    attrtypes[attnum]->attislocal = true;
-   if (IsStorageCompressible(attrtypes[attnum]->attstorage))
-       attrtypes[attnum]->attcompression = GetDefaultToastCompression();
-   else
-       attrtypes[attnum]->attcompression = InvalidCompressionMethod;
 
    if (nullness == BOOTCOL_NULL_FORCE_NOT_NULL)
    {
index 63a907d50d5bce7b1053c4ca13660f97254d7f97..112b3affdfd750ccb78e724d16bfbe927cfe57b0 100644 (file)
@@ -897,8 +897,11 @@ sub morph_row_for_pgattr
    $row->{atttypid}   = $type->{oid};
    $row->{attlen}     = $type->{typlen};
    $row->{attbyval}   = $type->{typbyval};
-   $row->{attstorage} = $type->{typstorage};
    $row->{attalign}   = $type->{typalign};
+   $row->{attstorage} = $type->{typstorage};
+
+   $row->{attcompression} =
+     $type->{typstorage} ne 'p' && $type->{typstorage} ne 'e' ? 'p' : '\0';
 
    # set attndims if it's an array type
    $row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
@@ -907,9 +910,6 @@ sub morph_row_for_pgattr
    $row->{attcollation} =
      $type->{typcollation} ne '0' ? $C_COLLATION_OID : 0;
 
-   $row->{attcompression} =
-     $type->{typstorage} ne 'p' && $type->{typstorage} ne 'e' ? 'p' : '\0';
-
    if (defined $attr->{forcenotnull})
    {
        $row->{attnotnull} = 't';
index ba3e1ecf459bde2b74cf1b6076a6b1504dd0648a..3dfe2e8a565e2f963476676cd1ff617d6410477c 100644 (file)
@@ -146,7 +146,8 @@ static Node *cookConstraint(ParseState *pstate,
 /*
  * The initializers below do not include trailing variable length fields,
  * but that's OK - we're never going to reference anything beyond the
- * fixed-size portion of the structure anyway.
+ * fixed-size portion of the structure anyway.  Fields that can default
+ * to zeroes are also not mentioned.
  */
 
 static const FormData_pg_attribute a1 = {
@@ -157,8 +158,8 @@ static const FormData_pg_attribute a1 = {
    .attcacheoff = -1,
    .atttypmod = -1,
    .attbyval = false,
-   .attstorage = TYPSTORAGE_PLAIN,
    .attalign = TYPALIGN_SHORT,
+   .attstorage = TYPSTORAGE_PLAIN,
    .attnotnull = true,
    .attislocal = true,
 };
@@ -171,8 +172,8 @@ static const FormData_pg_attribute a2 = {
    .attcacheoff = -1,
    .atttypmod = -1,
    .attbyval = true,
-   .attstorage = TYPSTORAGE_PLAIN,
    .attalign = TYPALIGN_INT,
+   .attstorage = TYPSTORAGE_PLAIN,
    .attnotnull = true,
    .attislocal = true,
 };
@@ -185,8 +186,8 @@ static const FormData_pg_attribute a3 = {
    .attcacheoff = -1,
    .atttypmod = -1,
    .attbyval = true,
-   .attstorage = TYPSTORAGE_PLAIN,
    .attalign = TYPALIGN_INT,
+   .attstorage = TYPSTORAGE_PLAIN,
    .attnotnull = true,
    .attislocal = true,
 };
@@ -199,8 +200,8 @@ static const FormData_pg_attribute a4 = {
    .attcacheoff = -1,
    .atttypmod = -1,
    .attbyval = true,
-   .attstorage = TYPSTORAGE_PLAIN,
    .attalign = TYPALIGN_INT,
+   .attstorage = TYPSTORAGE_PLAIN,
    .attnotnull = true,
    .attislocal = true,
 };
@@ -213,8 +214,8 @@ static const FormData_pg_attribute a5 = {
    .attcacheoff = -1,
    .atttypmod = -1,
    .attbyval = true,
-   .attstorage = TYPSTORAGE_PLAIN,
    .attalign = TYPALIGN_INT,
+   .attstorage = TYPSTORAGE_PLAIN,
    .attnotnull = true,
    .attislocal = true,
 };
@@ -233,8 +234,8 @@ static const FormData_pg_attribute a6 = {
    .attcacheoff = -1,
    .atttypmod = -1,
    .attbyval = true,
-   .attstorage = TYPSTORAGE_PLAIN,
    .attalign = TYPALIGN_INT,
+   .attstorage = TYPSTORAGE_PLAIN,
    .attnotnull = true,
    .attislocal = true,
 };
@@ -779,8 +780,9 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
        slot[slotCount]->tts_values[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(-1);
        slot[slotCount]->tts_values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(attrs->atttypmod);
        slot[slotCount]->tts_values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(attrs->attbyval);
-       slot[slotCount]->tts_values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(attrs->attstorage);
        slot[slotCount]->tts_values[Anum_pg_attribute_attalign - 1] = CharGetDatum(attrs->attalign);
+       slot[slotCount]->tts_values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(attrs->attstorage);
+       slot[slotCount]->tts_values[Anum_pg_attribute_attcompression - 1] = CharGetDatum(attrs->attcompression);
        slot[slotCount]->tts_values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(attrs->attnotnull);
        slot[slotCount]->tts_values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(attrs->atthasdef);
        slot[slotCount]->tts_values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(attrs->atthasmissing);
@@ -790,7 +792,6 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
        slot[slotCount]->tts_values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(attrs->attislocal);
        slot[slotCount]->tts_values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(attrs->attinhcount);
        slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(attrs->attcollation);
-       slot[slotCount]->tts_values[Anum_pg_attribute_attcompression - 1] = CharGetDatum(attrs->attcompression);
        if (attoptions && attoptions[natts] != (Datum) 0)
            slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = attoptions[natts];
        else
index 0f8cfae4ec9d6c0ab55bd63c3f942cb25506689b..50b7a16bce94aaf962a8f435006fd5379b51cebd 100644 (file)
@@ -345,8 +345,8 @@ ConstructTupleDescriptor(Relation heapRelation,
            to->attndims = from->attndims;
            to->atttypmod = from->atttypmod;
            to->attbyval = from->attbyval;
-           to->attstorage = from->attstorage;
            to->attalign = from->attalign;
+           to->attstorage = from->attstorage;
            to->attcompression = from->attcompression;
        }
        else
@@ -373,16 +373,16 @@ ConstructTupleDescriptor(Relation heapRelation,
             */
            to->atttypid = keyType;
            to->attlen = typeTup->typlen;
+           to->atttypmod = exprTypmod(indexkey);
            to->attbyval = typeTup->typbyval;
-           to->attstorage = typeTup->typstorage;
            to->attalign = typeTup->typalign;
-           to->atttypmod = exprTypmod(indexkey);
+           to->attstorage = typeTup->typstorage;
 
            /*
             * For expression columns, set attcompression invalid, since
             * there's no table column from which to copy the value. Whenever
             * we actually need to compress a value, we'll use whatever the
-            * current value of default_compression_method is at that point in
+            * current value of default_toast_compression is at that point in
             * time.
             */
            to->attcompression = InvalidCompressionMethod;
@@ -464,6 +464,8 @@ ConstructTupleDescriptor(Relation heapRelation,
            to->attbyval = typeTup->typbyval;
            to->attalign = typeTup->typalign;
            to->attstorage = typeTup->typstorage;
+           /* As above, use the default compression method in this case */
+           to->attcompression = InvalidCompressionMethod;
 
            ReleaseSysCache(tuple);
        }
index 85dcc330638aa910b1e6a515cd82489cf9790e4c..11e91c4ad33f36a7c5313855451904f85ea135c0 100644 (file)
@@ -2640,8 +2640,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                def->constraints = NIL;
                def->location = -1;
                if (CompressionMethodIsValid(attribute->attcompression))
-                   def->compression = pstrdup(GetCompressionMethodName(
-                                                                       attribute->attcompression));
+                   def->compression =
+                       pstrdup(GetCompressionMethodName(attribute->attcompression));
                else
                    def->compression = NULL;
                inhSchema = lappend(inhSchema, def);
@@ -6596,12 +6596,19 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    attribute.atttypid = typeOid;
    attribute.attstattarget = (newattnum > 0) ? -1 : 0;
    attribute.attlen = tform->typlen;
-   attribute.atttypmod = typmod;
    attribute.attnum = newattnum;
-   attribute.attbyval = tform->typbyval;
    attribute.attndims = list_length(colDef->typeName->arrayBounds);
-   attribute.attstorage = tform->typstorage;
+   attribute.atttypmod = typmod;
+   attribute.attbyval = tform->typbyval;
    attribute.attalign = tform->typalign;
+   attribute.attstorage = tform->typstorage;
+   /* do not set compression in views etc */
+   if (rel->rd_rel->relkind == RELKIND_RELATION ||
+       rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+       attribute.attcompression = GetAttributeCompression(&attribute,
+                                                          colDef->compression);
+   else
+       attribute.attcompression = InvalidCompressionMethod;
    attribute.attnotnull = colDef->is_not_null;
    attribute.atthasdef = false;
    attribute.atthasmissing = false;
@@ -6612,17 +6619,6 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    attribute.attinhcount = colDef->inhcount;
    attribute.attcollation = collOid;
 
-   /*
-    * lookup attribute's compression method and store it in the
-    * attr->attcompression.
-    */
-   if (rel->rd_rel->relkind == RELKIND_RELATION ||
-       rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-       attribute.attcompression = GetAttributeCompression(&attribute,
-                                                          colDef->compression);
-   else
-       attribute.attcompression = InvalidCompressionMethod;
-
    /* attribute.attacl is handled by InsertPgAttributeTuples() */
 
    ReleaseSysCache(typeTuple);
index ba3da5b5404b04aedc6f9d42b413a10e6b0c4c19..40d3b71b89e3f5c77e18220c7844d79b239897f5 100644 (file)
@@ -137,8 +137,8 @@ typedef struct SpGistTypeDesc
    Oid         type;
    int16       attlen;
    bool        attbyval;
-   char        attstorage;
    char        attalign;
+   char        attstorage;
 } SpGistTypeDesc;
 
 typedef struct SpGistState
index d5385d4c550202149312ee6c9a509a82106c8b12..958f12e66a14e3b5e285e2f3ae9bf6a61efe210e 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202105121
+#define CATALOG_VERSION_NO 202105231
 
 #endif
index 560f8f00bb37da569269a9dfb87dfe2e35139478..1e260162146167931513e928d7f16cb949f9d247 100644 (file)
@@ -111,6 +111,12 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
     */
    bool        attbyval;
 
+   /*
+    * attalign is a copy of the typalign field from pg_type for this
+    * attribute.  See atttypid comments above.
+    */
+   char        attalign;
+
    /*----------
     * attstorage tells for VARLENA attributes, what the heap access
     * methods can do to it if a given tuple doesn't fit into a page.
@@ -120,10 +126,10 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
    char        attstorage;
 
    /*
-    * attalign is a copy of the typalign field from pg_type for this
-    * attribute.  See atttypid comments above.
+    * Compression method.  Must be InvalidCompressionMethod if and only if
+    * typstorage is 'plain' or 'external'.
     */
-   char        attalign;
+   char        attcompression BKI_DEFAULT('\0');
 
    /* This flag represents the "NOT NULL" constraint */
    bool        attnotnull;
@@ -160,12 +166,6 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
    /* attribute's collation, if any */
    Oid         attcollation BKI_LOOKUP_OPT(pg_collation);
 
-   /*
-    * compression method.  Must be InvalidCompressionMethod if and only if
-    * typstorage is 'plain' or 'external'.
-    */
-   char        attcompression BKI_DEFAULT('\0');
-
 #ifdef CATALOG_VARLEN          /* variable-length fields start here */
    /* NOTE: The following fields are not present in tuple descriptors. */
 
@@ -190,10 +190,10 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
  * ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
  * guaranteed-not-null part of a pg_attribute row.  This is in fact as much
  * of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attcollation except in a real tuple!
+ * can access the variable-length fields except in a real tuple!
  */
 #define ATTRIBUTE_FIXED_PART_SIZE \
-   (offsetof(FormData_pg_attribute,attcompression) + sizeof(char))
+   (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
 
 /* ----------------
  *     Form_pg_attribute corresponds to a pointer to a tuple with