Here is an updated patch rebased over 3e2e0d5ad7.

The 0001 patch stands on its own, but I also tacked on two additional WIP patches that simplify some pg_attribute handling and make these kinds of refactorings simpler in the future. See description in the patches.


On 05.12.23 13:52, Peter Eisentraut wrote:
In [0] it was discussed that we could make attstattarget a nullable column, instead of always storing an explicit -1 default value for most columns.  This patch implements this.

This changes the pg_attribute field attstattarget into a nullable field in the variable-length part of the row.  If no value is set by the user for attstattarget, it is now null instead of previously -1.  This saves space in pg_attribute and tuple descriptors for most practical scenarios.  (ATTRIBUTE_FIXED_PART_SIZE is reduced from 108 to 104.) Also, null is the semantically more correct value.

The ANALYZE code internally continues to represent the default statistics target by -1, so that that code can avoid having to deal with null values.  But that is now contained to ANALYZE code.  The DDL code deals with attstattarget possibly null.

For system columns, the field is now always null but the effective value 0 (don't analyze) is assumed.

To set a column's statistics target to the default value, the new command form ALTER TABLE ... SET STATISTICS DEFAULT can be used.  (SET STATISTICS -1 still works.)


[0]: https://www.postgresql.org/message-id/flat/d07ffc2b-e0e8-77f7-38fb-be921dff71af%40enterprisedb.com
From f370eceec0cbb9b6bf76d3394e56a5df4280c906 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Sat, 23 Dec 2023 10:47:19 +0100
Subject: [PATCH v2 1/3] Make attstattarget nullable

This changes the pg_attribute field attstattarget into a nullable
field in the variable-length part of the row.  If no value is set by
the user for attstattarget, it is now null instead of previously -1.
This saves space in pg_attribute and tuple descriptors for most
practical scenarios.  (ATTRIBUTE_FIXED_PART_SIZE is reduced from 108
to 104.)  Also, null is the semantically more correct value.

The ANALYZE code internally continues to represent the default
statistics target by -1, so that that code can avoid having to deal
with null values.  But that is now contained to ANALYZE code.  The DDL
code deals with attstattarget possibly null.

For system columns, the field is now always null but the effective
value 0 (don't analyze) is assumed.

To set a column's statistics target to the default value, the new
command form ALTER TABLE ... SET STATISTICS DEFAULT can be used.  (SET
STATISTICS -1 still works.)

Discussion: 
https://www.postgresql.org/message-id/flat/4da8d211-d54d-44b9-9847-f2a9f1184...@eisentraut.org

TODO: move get_attstattarget() into analyze.c?
TODO: catversion
---
 doc/src/sgml/ref/alter_table.sgml          |  4 +-
 src/backend/access/common/tupdesc.c        |  4 --
 src/backend/bootstrap/bootstrap.c          |  1 -
 src/backend/catalog/genbki.pl              |  1 -
 src/backend/catalog/heap.c                 | 14 +++----
 src/backend/catalog/index.c                | 21 ++++++++---
 src/backend/commands/analyze.c             |  7 +++-
 src/backend/commands/tablecmds.c           | 44 +++++++++++++++++-----
 src/backend/parser/gram.y                  | 18 ++++++---
 src/backend/utils/cache/lsyscache.c        | 22 +++++++++--
 src/bin/pg_dump/pg_dump.c                  |  7 +++-
 src/include/catalog/pg_attribute.h         | 16 ++++----
 src/include/commands/vacuum.h              |  2 +-
 src/test/regress/expected/create_index.out |  4 +-
 14 files changed, 109 insertions(+), 56 deletions(-)

diff --git a/doc/src/sgml/ref/alter_table.sgml 
b/doc/src/sgml/ref/alter_table.sgml
index e1d207bc60..9d637157eb 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -50,7 +50,7 @@
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( 
<replaceable>sequence_options</replaceable> ) ]
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
{ SET GENERATED { ALWAYS | BY DEFAULT } | SET 
<replaceable>sequence_option</replaceable> | RESTART [ [ WITH ] <replaceable 
class="parameter">restart</replaceable> ] } [...]
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
DROP IDENTITY [ IF EXISTS ]
-    ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
SET STATISTICS <replaceable class="parameter">integer</replaceable>
+    ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
SET STATISTICS { <replaceable class="parameter">integer</replaceable> | DEFAULT 
}
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
SET ( <replaceable class="parameter">attribute_option</replaceable> = 
<replaceable class="parameter">value</replaceable> [, ... ] )
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> 
SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT }
@@ -317,7 +317,7 @@ <title>Description</title>
       sets the per-column statistics-gathering target for subsequent
       <link linkend="sql-analyze"><command>ANALYZE</command></link> operations.
       The target can be set in the range 0 to 10000; alternatively, set it
-      to -1 to revert to using the system default statistics
+      to <literal>DEFAULT</literal> to revert to using the system default 
statistics
       target (<xref linkend="guc-default-statistics-target"/>).
       For more information on the use of statistics by the
       <productname>PostgreSQL</productname> query planner, refer to
diff --git a/src/backend/access/common/tupdesc.c 
b/src/backend/access/common/tupdesc.c
index 8826519e5e..054ccff1e2 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -453,8 +453,6 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
                        return false;
                if (attr1->atttypid != attr2->atttypid)
                        return false;
-               if (attr1->attstattarget != attr2->attstattarget)
-                       return false;
                if (attr1->attlen != attr2->attlen)
                        return false;
                if (attr1->attndims != attr2->attndims)
@@ -639,7 +637,6 @@ TupleDescInitEntry(TupleDesc desc,
        else if (attributeName != NameStr(att->attname))
                namestrcpy(&(att->attname), attributeName);
 
-       att->attstattarget = -1;
        att->attcacheoff = -1;
        att->atttypmod = typmod;
 
@@ -702,7 +699,6 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
        Assert(attributeName != NULL);
        namestrcpy(&(att->attname), attributeName);
 
-       att->attstattarget = -1;
        att->attcacheoff = -1;
        att->atttypmod = typmod;
 
diff --git a/src/backend/bootstrap/bootstrap.c 
b/src/backend/bootstrap/bootstrap.c
index e01dca9b7c..c7546da51e 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -552,7 +552,6 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
        if (OidIsValid(attrtypes[attnum]->attcollation))
                attrtypes[attnum]->attcollation = C_COLLATION_OID;
 
-       attrtypes[attnum]->attstattarget = -1;
        attrtypes[attnum]->attcacheoff = -1;
        attrtypes[attnum]->atttypmod = -1;
        attrtypes[attnum]->attislocal = true;
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 380bc23c82..13cd2fee14 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -840,7 +840,6 @@ sub gen_pg_attribute
                                my %row;
                                $row{attnum} = $attnum;
                                $row{attrelid} = $table->{relation_oid};
-                               $row{attstattarget} = '0';
 
                                morph_row_for_pgattr(\%row, $schema, $attr, 1);
                                print_bki_insert(\%row, $schema);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index b93894889d..52b4485c4b 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -749,14 +749,16 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
                slot[slotCount]->tts_values[Anum_pg_attribute_attisdropped - 1] 
= BoolGetDatum(attrs->attisdropped);
                slot[slotCount]->tts_values[Anum_pg_attribute_attislocal - 1] = 
BoolGetDatum(attrs->attislocal);
                slot[slotCount]->tts_values[Anum_pg_attribute_attinhcount - 1] 
= Int16GetDatum(attrs->attinhcount);
-               slot[slotCount]->tts_values[Anum_pg_attribute_attstattarget - 
1] = Int16GetDatum(attrs->attstattarget);
                slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] 
= ObjectIdGetDatum(attrs->attcollation);
                if (attoptions && attoptions[natts] != (Datum) 0)
                        
slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = 
attoptions[natts];
                else
                        
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = true;
 
-               /* start out with empty permissions and empty options */
+               /*
+                * The remaining fields are not set for new columns.
+                */
+               slot[slotCount]->tts_isnull[Anum_pg_attribute_attstattarget - 
1] = true;
                slot[slotCount]->tts_isnull[Anum_pg_attribute_attacl - 1] = 
true;
                slot[slotCount]->tts_isnull[Anum_pg_attribute_attfdwoptions - 
1] = true;
                slot[slotCount]->tts_isnull[Anum_pg_attribute_attmissingval - 
1] = true;
@@ -818,9 +820,6 @@ AddNewAttributeTuples(Oid new_rel_oid,
 
        indstate = CatalogOpenIndexes(rel);
 
-       /* set stats detail level to a sane default */
-       for (int i = 0; i < natts; i++)
-               tupdesc->attrs[i].attstattarget = -1;
        InsertPgAttributeTuples(rel, tupdesc, new_rel_oid, NULL, indstate);
 
        /* add dependencies on their datatypes and collations */
@@ -1685,9 +1684,6 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
        /* Remove any not-null constraint the column may have */
        attStruct->attnotnull = false;
 
-       /* We don't want to keep stats for it anymore */
-       attStruct->attstattarget = 0;
-
        /* Unset this so no one tries to look up the generation expression */
        attStruct->attgenerated = '\0';
 
@@ -1707,6 +1703,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
         * Clear the other variable-length fields.  This saves some space in
         * pg_attribute and removes no longer useful information.
         */
+       nullsAtt[Anum_pg_attribute_attstattarget - 1] = true;
+       replacesAtt[Anum_pg_attribute_attstattarget - 1] = true;
        nullsAtt[Anum_pg_attribute_attacl - 1] = true;
        replacesAtt[Anum_pg_attribute_attacl - 1] = true;
        nullsAtt[Anum_pg_attribute_attoptions - 1] = true;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b186c0220..b2759df311 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -325,7 +325,6 @@ ConstructTupleDescriptor(Relation heapRelation,
 
                MemSet(to, 0, ATTRIBUTE_FIXED_PART_SIZE);
                to->attnum = i + 1;
-               to->attstattarget = -1;
                to->attcacheoff = -1;
                to->attislocal = true;
                to->attcollation = (i < numkeyatts) ? collationIds[i] : 
InvalidOid;
@@ -1780,10 +1779,12 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, 
const char *oldName)
                while (HeapTupleIsValid((attrTuple = systable_getnext(scan))))
                {
                        Form_pg_attribute att = (Form_pg_attribute) 
GETSTRUCT(attrTuple);
+                       HeapTuple       tp;
+                       Datum           dat;
+                       bool            isnull;
                        Datum           repl_val[Natts_pg_attribute];
                        bool            repl_null[Natts_pg_attribute];
                        bool            repl_repl[Natts_pg_attribute];
-                       int                     attstattarget;
                        HeapTuple       newTuple;
 
                        /* Ignore dropped columns */
@@ -1793,10 +1794,18 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, 
const char *oldName)
                        /*
                         * Get attstattarget from the old index and refresh the 
new value.
                         */
-                       attstattarget = get_attstattarget(oldIndexId, 
att->attnum);
+                       tp = SearchSysCache2(ATTNUM, 
ObjectIdGetDatum(oldIndexId), Int16GetDatum(att->attnum));
+                       if (!HeapTupleIsValid(tp))
+                               elog(ERROR, "cache lookup failed for attribute 
%d of relation %u",
+                                        att->attnum, oldIndexId);
+                       dat = SysCacheGetAttr(ATTNUM, tp, 
Anum_pg_attribute_attstattarget, &isnull);
+                       ReleaseSysCache(tp);
 
-                       /* no need for a refresh if both match */
-                       if (attstattarget == att->attstattarget)
+                       /*
+                        * No need for a refresh if old index value is null.  
(All new
+                        * index values are null at this point.)
+                        */
+                       if (isnull)
                                continue;
 
                        memset(repl_val, 0, sizeof(repl_val));
@@ -1804,7 +1813,7 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, 
const char *oldName)
                        memset(repl_repl, false, sizeof(repl_repl));
 
                        repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
-                       repl_val[Anum_pg_attribute_attstattarget - 1] = 
Int16GetDatum(attstattarget);
+                       repl_val[Anum_pg_attribute_attstattarget - 1] = dat;
 
                        newTuple = heap_modify_tuple(attrTuple,
                                                                                
 RelationGetDescr(pg_attribute),
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 1f4a951681..fd2202cbb8 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1004,6 +1004,7 @@ static VacAttrStats *
 examine_attribute(Relation onerel, int attnum, Node *index_expr)
 {
        Form_pg_attribute attr = TupleDescAttr(onerel->rd_att, attnum - 1);
+       int                     attstattarget;
        HeapTuple       typtuple;
        VacAttrStats *stats;
        int                     i;
@@ -1013,15 +1014,17 @@ examine_attribute(Relation onerel, int attnum, Node 
*index_expr)
        if (attr->attisdropped)
                return NULL;
 
+       attstattarget = get_attstattarget(RelationGetRelid(onerel), attnum);
+
        /* Don't analyze column if user has specified not to */
-       if (attr->attstattarget == 0)
+       if (attstattarget == 0)
                return NULL;
 
        /*
         * Create the VacAttrStats struct.
         */
        stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats));
-       stats->attstattarget = attr->attstattarget;
+       stats->attstattarget = attstattarget;
 
        /*
         * When analyzing an expression index, believe the expression tree's 
type
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6b0a20010e..77bb17c479 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7124,7 +7124,6 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, 
Relation rel,
        attribute.attrelid = myrelid;
        namestrcpy(&(attribute.attname), colDef->colname);
        attribute.atttypid = typeOid;
-       attribute.attstattarget = -1;
        attribute.attlen = tform->typlen;
        attribute.attnum = newattnum;
        if (list_length(colDef->typeName->arrayBounds) > PG_INT16_MAX)
@@ -8453,10 +8452,14 @@ ATExecSetStatistics(Relation rel, const char *colName, 
int16 colNum, Node *newVa
 {
        int                     newtarget;
        Relation        attrelation;
-       HeapTuple       tuple;
+       HeapTuple       tuple,
+                               newtuple;
        Form_pg_attribute attrtuple;
        AttrNumber      attnum;
        ObjectAddress address;
+       Datum           repl_val[Natts_pg_attribute];
+       bool            repl_null[Natts_pg_attribute];
+       bool            repl_repl[Natts_pg_attribute];
 
        /*
         * We allow referencing columns by numbers only for indexes, since table
@@ -8469,8 +8472,18 @@ ATExecSetStatistics(Relation rel, const char *colName, 
int16 colNum, Node *newVa
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("cannot refer to non-index column by 
number")));
 
-       Assert(IsA(newValue, Integer));
-       newtarget = intVal(newValue);
+       if (newValue)
+       {
+               Assert(IsA(newValue, Integer));
+               newtarget = intVal(newValue);
+       }
+       else
+       {
+               /*
+                * -1 was used in previous versions to represent the default 
setting
+                */
+               newtarget = -1;
+       }
 
        /*
         * Limit target to a sane range
@@ -8495,7 +8508,7 @@ ATExecSetStatistics(Relation rel, const char *colName, 
int16 colNum, Node *newVa
 
        if (colName)
        {
-               tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), 
colName);
+               tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
 
                if (!HeapTupleIsValid(tuple))
                        ereport(ERROR,
@@ -8505,7 +8518,7 @@ ATExecSetStatistics(Relation rel, const char *colName, 
int16 colNum, Node *newVa
        }
        else
        {
-               tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
+               tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
 
                if (!HeapTupleIsValid(tuple))
                        ereport(ERROR,
@@ -8539,16 +8552,27 @@ ATExecSetStatistics(Relation rel, const char *colName, 
int16 colNum, Node *newVa
                                         errhint("Alter statistics on table 
column instead.")));
        }
 
-       attrtuple->attstattarget = newtarget;
-
-       CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
+       /* Build new tuple. */
+       memset(repl_null, false, sizeof(repl_null));
+       memset(repl_repl, false, sizeof(repl_repl));
+       if (newtarget != -1)
+               repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
+       else
+               repl_null[Anum_pg_attribute_attstattarget - 1] = true;
+       repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
+       newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
+                                                                repl_val, 
repl_null, repl_repl);
+       CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
 
        InvokeObjectPostAlterHook(RelationRelationId,
                                                          RelationGetRelid(rel),
                                                          attrtuple->attnum);
        ObjectAddressSubSet(address, RelationRelationId,
                                                RelationGetRelid(rel), attnum);
-       heap_freetuple(tuple);
+
+       heap_freetuple(newtuple);
+
+       ReleaseSysCache(tuple);
 
        table_close(attrelation, RowExclusiveLock);
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 63f172e175..b6f23e26e7 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -337,6 +337,7 @@ static Node *makeRecursiveViewSelect(char *relname, List 
*aliases, Node *query);
 %type <list>   alter_table_cmds alter_type_cmds
 %type <list>    alter_identity_column_option_list
 %type <defelt>  alter_identity_column_option
+%type <node>   set_statistics_value
 
 %type <list>   createdb_opt_list createdb_opt_items copy_opt_list
                                transaction_mode_list
@@ -2436,18 +2437,18 @@ alter_table_cmd:
                                        n->missing_ok = true;
                                        $$ = (Node *) n;
                                }
-                       /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET 
STATISTICS <SignedIconst> */
-                       | ALTER opt_column ColId SET STATISTICS SignedIconst
+                       /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET 
STATISTICS */
+                       | ALTER opt_column ColId SET STATISTICS 
set_statistics_value
                                {
                                        AlterTableCmd *n = 
makeNode(AlterTableCmd);
 
                                        n->subtype = AT_SetStatistics;
                                        n->name = $3;
-                                       n->def = (Node *) makeInteger($6);
+                                       n->def = $6;
                                        $$ = (Node *) n;
                                }
-                       /* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET 
STATISTICS <SignedIconst> */
-                       | ALTER opt_column Iconst SET STATISTICS SignedIconst
+                       /* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET 
STATISTICS */
+                       | ALTER opt_column Iconst SET STATISTICS 
set_statistics_value
                                {
                                        AlterTableCmd *n = 
makeNode(AlterTableCmd);
 
@@ -2459,7 +2460,7 @@ alter_table_cmd:
 
                                        n->subtype = AT_SetStatistics;
                                        n->num = (int16) $3;
-                                       n->def = (Node *) makeInteger($6);
+                                       n->def = $6;
                                        $$ = (Node *) n;
                                }
                        /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( 
column_parameter = value [, ... ] ) */
@@ -3060,6 +3061,11 @@ alter_identity_column_option:
                                }
                ;
 
+set_statistics_value:
+                       SignedIconst                                    { $$ = 
(Node *) makeInteger($1); }
+                       | DEFAULT                                               
{ $$ = NULL; }
+               ;
+
 PartitionBoundSpec:
                        /* a HASH partition */
                        FOR VALUES WITH '(' hash_partbound ')'
diff --git a/src/backend/utils/cache/lsyscache.c 
b/src/backend/utils/cache/lsyscache.c
index fc6d267e44..41f877a305 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -878,23 +878,39 @@ get_attnum(Oid relid, const char *attname)
  *             Given the relation id and the attribute number,
  *             return the "attstattarget" field from the attribute relation.
  *
+ *             Returns -1 if attstattarget is null, except 0 for dropped 
columns.
+ *
+ *             Always returns 0 for system columns.
+ *
  *             Errors if not found.
  */
 int
 get_attstattarget(Oid relid, AttrNumber attnum)
 {
        HeapTuple       tp;
-       Form_pg_attribute att_tup;
+       Datum           dat;
+       bool            isnull;
        int                     result;
 
+       if (attnum < 0)
+               return 0;
+
        tp = SearchSysCache2(ATTNUM,
                                                 ObjectIdGetDatum(relid),
                                                 Int16GetDatum(attnum));
        if (!HeapTupleIsValid(tp))
                elog(ERROR, "cache lookup failed for attribute %d of relation 
%u",
                         attnum, relid);
-       att_tup = (Form_pg_attribute) GETSTRUCT(tp);
-       result = att_tup->attstattarget;
+       dat = SysCacheGetAttr(ATTNUM, tp, Anum_pg_attribute_attstattarget, 
&isnull);
+       if (isnull)
+       {
+               if (((Form_pg_attribute) GETSTRUCT(tp))->attisdropped)
+                       result = 0;
+               else
+                       result = -1;
+       }
+       else
+               result = DatumGetInt16(dat);
        ReleaseSysCache(tp);
        return result;
 }
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 8c0b5486b9..bc3651a73a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -8682,7 +8682,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int 
numTables)
                                                 tbinfo->dobj.name);
                        tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, 
i_attname));
                        tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, 
i_atttypname));
-                       tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, 
i_attstattarget));
+                       if (PQgetisnull(res, r, i_attstattarget))
+                               tbinfo->attstattarget[j] = -1;
+                       else
+                               tbinfo->attstattarget[j] = atoi(PQgetvalue(res, 
r, i_attstattarget));
                        tbinfo->attstorage[j] = *(PQgetvalue(res, r, 
i_attstorage));
                        tbinfo->typstorage[j] = *(PQgetvalue(res, r, 
i_typstorage));
                        tbinfo->attidentity[j] = *(PQgetvalue(res, r, 
i_attidentity));
@@ -16261,7 +16264,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
                        /*
                         * Dump per-column statistics information. We only 
issue an ALTER
                         * TABLE statement if the attstattarget entry for this 
column is
-                        * non-negative (i.e. it's not the default value)
+                        * not the default value.
                         */
                        if (tbinfo->attstattarget[j] >= 0)
                                appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s 
ALTER COLUMN %s SET STATISTICS %d;\n",
diff --git a/src/include/catalog/pg_attribute.h 
b/src/include/catalog/pg_attribute.h
index 672a5a16ff..337a2d5bf9 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -158,22 +158,22 @@ CATALOG(pg_attribute,1249,AttributeRelationId) 
BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
        /* Number of times inherited from direct parent relation(s) */
        int16           attinhcount BKI_DEFAULT(0);
 
+       /* attribute's collation, if any */
+       Oid                     attcollation BKI_LOOKUP_OPT(pg_collation);
+
+#ifdef CATALOG_VARLEN                  /* variable-length fields start here */
+       /* NOTE: The following fields are not present in tuple descriptors. */
+
        /*
         * attstattarget is the target number of statistics datapoints to 
collect
         * during VACUUM ANALYZE of this column.  A zero here indicates that we 
do
-        * not wish to collect any stats about this column. A "-1" here 
indicates
+        * not wish to collect any stats about this column. A NULL here 
indicates
         * that no value has been explicitly set for this column, so ANALYZE
         * should use the default setting.
         *
         * int16 is sufficient for the current max value 
(MAX_STATISTICS_TARGET).
         */
-       int16           attstattarget BKI_DEFAULT(-1);
-
-       /* attribute's collation, if any */
-       Oid                     attcollation BKI_LOOKUP_OPT(pg_collation);
-
-#ifdef CATALOG_VARLEN                  /* variable-length fields start here */
-       /* NOTE: The following fields are not present in tuple descriptors. */
+       int16           attstattarget BKI_DEFAULT(_null_) BKI_FORCE_NULL;
 
        /* Column-level access permissions */
        aclitem         attacl[1] BKI_DEFAULT(_null_);
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 4af02940c5..ea096ee8a8 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -121,7 +121,7 @@ typedef struct VacAttrStats
         * than the underlying column/expression.  Therefore, use these fields 
for
         * information about the datatype being fed to the typanalyze function.
         */
-       int                     attstattarget;
+       int                     attstattarget;  /* -1 to use default */
        Oid                     attrtypid;              /* type of data being 
analyzed */
        int32           attrtypmod;             /* typmod of data being 
analyzed */
        Form_pg_type attrtype;          /* copy of pg_type row for attrtypid */
diff --git a/src/test/regress/expected/create_index.out 
b/src/test/regress/expected/create_index.out
index 446cfa678b..79fa117cb5 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2707,8 +2707,8 @@ SELECT attrelid::regclass, attnum, attstattarget
          attrelid          | attnum | attstattarget 
 ---------------------------+--------+---------------
  concur_exprs_index_expr   |      1 |           100
- concur_exprs_index_pred   |      1 |            -1
- concur_exprs_index_pred_2 |      1 |            -1
+ concur_exprs_index_pred   |      1 |              
+ concur_exprs_index_pred_2 |      1 |              
 (3 rows)
 
 DROP TABLE concur_exprs_tab;

base-commit: 3e2e0d5ad7fcb89d18a71cbfc885ef184e1b6f2e
-- 
2.43.0

From 5f1f4bd9518a9c10c74de775125ea3549ba38d8c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Sat, 23 Dec 2023 11:41:17 +0100
Subject: [PATCH v2 2/3] WIP: Generalize handling of nullable pg_attribute
 columns in DDL

DDL code uses tuple descriptors to pass around pg_attribute values
during table and index creation.  But tuple descriptors don't include
the variable-length/nullable columns of pg_attribute, so they have to
be handled separately.  Right now, the attoptions field is handled in
a one-off way with a separate argument passed to
InsertPgAttributeTuples().  The other affected fields of pg_attribute
are right now not needed at relation creation time.

The goal of this patch is to generalize this to allow handling
additional variable-length/nullable columns of pg_attribute in a
similar manner.  For that, create a new struct
Form_pg_attribute_extra, which is to be passed around in parallel to
the tuple descriptor and optionally supplies the additional columns.
Right now, this struct only contains one field for attoptions, so no
functionality is actually changed by this.
---
 src/backend/catalog/heap.c         | 12 +++++++++---
 src/backend/catalog/index.c        | 16 +++++++++++++++-
 src/include/catalog/heap.h         |  2 +-
 src/include/catalog/pg_attribute.h |  7 +++++++
 4 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 52b4485c4b..40b23fbead 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -697,7 +697,7 @@ void
 InsertPgAttributeTuples(Relation pg_attribute_rel,
                                                TupleDesc tupdesc,
                                                Oid new_rel_oid,
-                                               const Datum *attoptions,
+                                               const 
FormData_pg_attribute_extra tupdesc_extra[],
                                                CatalogIndexState indstate)
 {
        TupleTableSlot **slot;
@@ -719,6 +719,7 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
        while (natts < tupdesc->natts)
        {
                Form_pg_attribute attrs = TupleDescAttr(tupdesc, natts);
+               const FormData_pg_attribute_extra *attrs_extra = tupdesc_extra 
? &tupdesc_extra[natts] : NULL;
 
                ExecClearTuple(slot[slotCount]);
 
@@ -750,10 +751,15 @@ 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] 
= Int16GetDatum(attrs->attinhcount);
                slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] 
= ObjectIdGetDatum(attrs->attcollation);
-               if (attoptions && attoptions[natts] != (Datum) 0)
-                       
slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = 
attoptions[natts];
+               if (attrs_extra)
+               {
+                       
slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = 
attrs_extra->attoptions.value;
+                       
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = 
attrs_extra->attoptions.isnull;
+               }
                else
+               {
                        
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = true;
+               }
 
                /*
                 * The remaining fields are not set for new columns.
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index b2759df311..eaeae568a3 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -517,6 +517,20 @@ AppendAttributeTuples(Relation indexRelation, const Datum 
*attopts)
        Relation        pg_attribute;
        CatalogIndexState indstate;
        TupleDesc       indexTupDesc;
+       FormData_pg_attribute_extra *attrs_extra = NULL;
+
+       if (attopts)
+       {
+               attrs_extra = palloc0_array(FormData_pg_attribute_extra, 
indexRelation->rd_att->natts);
+
+               for (int i = 0; i < indexRelation->rd_att->natts; i++)
+               {
+                       if (attopts[i])
+                               attrs_extra[i].attoptions.value = attopts[i];
+                       else
+                               attrs_extra[i].attoptions.isnull = true;
+               }
+       }
 
        /*
         * open the attribute relation and its indexes
@@ -530,7 +544,7 @@ AppendAttributeTuples(Relation indexRelation, const Datum 
*attopts)
         */
        indexTupDesc = RelationGetDescr(indexRelation);
 
-       InsertPgAttributeTuples(pg_attribute, indexTupDesc, InvalidOid, 
attopts, indstate);
+       InsertPgAttributeTuples(pg_attribute, indexTupDesc, InvalidOid, 
attrs_extra, indstate);
 
        CatalogCloseIndexes(indstate);
 
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 51f7b12aa3..dbd65c4d5e 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -98,7 +98,7 @@ extern List *heap_truncate_find_FKs(List *relationIds);
 extern void InsertPgAttributeTuples(Relation pg_attribute_rel,
                                                                        
TupleDesc tupdesc,
                                                                        Oid 
new_rel_oid,
-                                                                       const 
Datum *attoptions,
+                                                                       const 
FormData_pg_attribute_extra tupdesc_extra[],
                                                                        
CatalogIndexState indstate);
 
 extern void InsertPgClassTuple(Relation pg_class_desc,
diff --git a/src/include/catalog/pg_attribute.h 
b/src/include/catalog/pg_attribute.h
index 337a2d5bf9..7336d7f2fd 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -208,6 +208,13 @@ CATALOG(pg_attribute,1249,AttributeRelationId) 
BKI_BOOTSTRAP BKI_ROWTYPE_OID(75,
  */
 typedef FormData_pg_attribute *Form_pg_attribute;
 
+typedef struct FormData_pg_attribute_extra
+{
+       NullableDatum attoptions;
+} FormData_pg_attribute_extra;
+
+typedef FormData_pg_attribute_extra *Form_pg_attribute_extra;
+
 DECLARE_UNIQUE_INDEX(pg_attribute_relid_attnam_index, 2658, 
AttributeRelidNameIndexId, pg_attribute, btree(attrelid oid_ops, attname 
name_ops));
 DECLARE_UNIQUE_INDEX_PKEY(pg_attribute_relid_attnum_index, 2659, 
AttributeRelidNumIndexId, pg_attribute, btree(attrelid oid_ops, attnum 
int2_ops));
 
-- 
2.43.0

From eb616c2156163a3ca1d74f122377987b6c358764 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Sat, 23 Dec 2023 12:14:42 +0100
Subject: [PATCH v2 3/3] WIP: Add attstattarget to Form_pg_attribute_extra

This allows setting attstattarget when a relation is created.

We make use of this by having index_concurrently_create_copy() copy
over the attstattarget values when the new index is created, instead
of having index_concurrently_swap() fix it up later.
---
 src/backend/catalog/heap.c         |  5 +-
 src/backend/catalog/index.c        | 97 +++++++++---------------------
 src/backend/catalog/toasting.c     |  2 +-
 src/backend/commands/indexcmds.c   |  2 +-
 src/include/catalog/index.h        |  1 +
 src/include/catalog/pg_attribute.h |  1 +
 6 files changed, 36 insertions(+), 72 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 40b23fbead..f61c510b8d 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -753,18 +753,21 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
                slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] 
= ObjectIdGetDatum(attrs->attcollation);
                if (attrs_extra)
                {
+                       
slot[slotCount]->tts_values[Anum_pg_attribute_attstattarget - 1] = 
attrs_extra->attstattarget.value;
+                       
slot[slotCount]->tts_isnull[Anum_pg_attribute_attstattarget - 1] = 
attrs_extra->attstattarget.isnull;
+
                        
slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = 
attrs_extra->attoptions.value;
                        
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = 
attrs_extra->attoptions.isnull;
                }
                else
                {
+                       
slot[slotCount]->tts_isnull[Anum_pg_attribute_attstattarget - 1] = true;
                        
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = true;
                }
 
                /*
                 * The remaining fields are not set for new columns.
                 */
-               slot[slotCount]->tts_isnull[Anum_pg_attribute_attstattarget - 
1] = true;
                slot[slotCount]->tts_isnull[Anum_pg_attribute_attacl - 1] = 
true;
                slot[slotCount]->tts_isnull[Anum_pg_attribute_attfdwoptions - 
1] = true;
                slot[slotCount]->tts_isnull[Anum_pg_attribute_attmissingval - 
1] = true;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index eaeae568a3..e2ee775df1 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -112,7 +112,7 @@ static TupleDesc ConstructTupleDescriptor(Relation 
heapRelation,
                                                                                
  const Oid *opclassIds);
 static void InitializeAttributeOids(Relation indexRelation,
                                                                        int 
numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, const Datum 
*attopts);
+static void AppendAttributeTuples(Relation indexRelation, const Datum 
*attopts, const NullableDatum *stattargets);
 static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
                                                                Oid 
parentIndexId,
                                                                const IndexInfo 
*indexInfo,
@@ -512,7 +512,7 @@ InitializeAttributeOids(Relation indexRelation,
  * ----------------------------------------------------------------
  */
 static void
-AppendAttributeTuples(Relation indexRelation, const Datum *attopts)
+AppendAttributeTuples(Relation indexRelation, const Datum *attopts, const 
NullableDatum *stattargets)
 {
        Relation        pg_attribute;
        CatalogIndexState indstate;
@@ -529,6 +529,11 @@ AppendAttributeTuples(Relation indexRelation, const Datum 
*attopts)
                                attrs_extra[i].attoptions.value = attopts[i];
                        else
                                attrs_extra[i].attoptions.isnull = true;
+
+                       if (stattargets)
+                               attrs_extra[i].attstattarget = stattargets[i];
+                       else
+                               attrs_extra[i].attstattarget.isnull = true;
                }
        }
 
@@ -735,6 +740,7 @@ index_create(Relation heapRelation,
                         const Oid *opclassIds,
                         const Datum *opclassOptions,
                         const int16 *coloptions,
+                        const NullableDatum *stattargets,
                         Datum reloptions,
                         bits16 flags,
                         bits16 constr_flags,
@@ -1029,7 +1035,7 @@ index_create(Relation heapRelation,
        /*
         * append ATTRIBUTE tuples for the index
         */
-       AppendAttributeTuples(indexRelation, opclassOptions);
+       AppendAttributeTuples(indexRelation, opclassOptions, stattargets);
 
        /* ----------------
         *        update pg_index
@@ -1308,6 +1314,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid 
oldIndexId,
        Datum      *opclassOptions;
        oidvector  *indclass;
        int2vector *indcoloptions;
+       NullableDatum *stattargets;
        bool            isnull;
        List       *indexColNames = NIL;
        List       *indexExprs = NIL;
@@ -1412,6 +1419,23 @@ index_concurrently_create_copy(Relation heapRelation, 
Oid oldIndexId,
        for (int i = 0; i < newInfo->ii_NumIndexAttrs; i++)
                opclassOptions[i] = get_attoptions(oldIndexId, i + 1);
 
+       /* Extra statistic targets for each attribute */
+       stattargets = palloc0_array(NullableDatum, newInfo->ii_NumIndexAttrs);
+       for (int i = 0; i < newInfo->ii_NumIndexAttrs; i++)
+       {
+               HeapTuple   tp;
+               Datum       dat;
+
+               tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(oldIndexId), 
Int16GetDatum(i + 1));
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for attribute %d of 
relation %u",
+                                i + 1, oldIndexId);
+               dat = SysCacheGetAttr(ATTNUM, tp, 
Anum_pg_attribute_attstattarget, &isnull);
+               ReleaseSysCache(tp);
+               stattargets[i].value = dat;
+               stattargets[i].isnull = isnull;
+       }
+
        /*
         * Now create the new index.
         *
@@ -1433,6 +1457,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid 
oldIndexId,
                                                          indclass->values,
                                                          opclassOptions,
                                                          indcoloptions->values,
+                                                         stattargets,
                                                          reloptionsDatum,
                                                          
INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT,
                                                          0,
@@ -1775,72 +1800,6 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, 
const char *oldName)
        /* Copy data of pg_statistic from the old index to the new one */
        CopyStatistics(oldIndexId, newIndexId);
 
-       /* Copy pg_attribute.attstattarget for each index attribute */
-       {
-               HeapTuple       attrTuple;
-               Relation        pg_attribute;
-               SysScanDesc scan;
-               ScanKeyData key[1];
-
-               pg_attribute = table_open(AttributeRelationId, 
RowExclusiveLock);
-               ScanKeyInit(&key[0],
-                                       Anum_pg_attribute_attrelid,
-                                       BTEqualStrategyNumber, F_OIDEQ,
-                                       ObjectIdGetDatum(newIndexId));
-               scan = systable_beginscan(pg_attribute, 
AttributeRelidNumIndexId,
-                                                                 true, NULL, 
1, key);
-
-               while (HeapTupleIsValid((attrTuple = systable_getnext(scan))))
-               {
-                       Form_pg_attribute att = (Form_pg_attribute) 
GETSTRUCT(attrTuple);
-                       HeapTuple       tp;
-                       Datum           dat;
-                       bool            isnull;
-                       Datum           repl_val[Natts_pg_attribute];
-                       bool            repl_null[Natts_pg_attribute];
-                       bool            repl_repl[Natts_pg_attribute];
-                       HeapTuple       newTuple;
-
-                       /* Ignore dropped columns */
-                       if (att->attisdropped)
-                               continue;
-
-                       /*
-                        * Get attstattarget from the old index and refresh the 
new value.
-                        */
-                       tp = SearchSysCache2(ATTNUM, 
ObjectIdGetDatum(oldIndexId), Int16GetDatum(att->attnum));
-                       if (!HeapTupleIsValid(tp))
-                               elog(ERROR, "cache lookup failed for attribute 
%d of relation %u",
-                                        att->attnum, oldIndexId);
-                       dat = SysCacheGetAttr(ATTNUM, tp, 
Anum_pg_attribute_attstattarget, &isnull);
-                       ReleaseSysCache(tp);
-
-                       /*
-                        * No need for a refresh if old index value is null.  
(All new
-                        * index values are null at this point.)
-                        */
-                       if (isnull)
-                               continue;
-
-                       memset(repl_val, 0, sizeof(repl_val));
-                       memset(repl_null, false, sizeof(repl_null));
-                       memset(repl_repl, false, sizeof(repl_repl));
-
-                       repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
-                       repl_val[Anum_pg_attribute_attstattarget - 1] = dat;
-
-                       newTuple = heap_modify_tuple(attrTuple,
-                                                                               
 RelationGetDescr(pg_attribute),
-                                                                               
 repl_val, repl_null, repl_repl);
-                       CatalogTupleUpdate(pg_attribute, &newTuple->t_self, 
newTuple);
-
-                       heap_freetuple(newTuple);
-               }
-
-               systable_endscan(scan);
-               table_close(pg_attribute, RowExclusiveLock);
-       }
-
        /* Close relations */
        table_close(pg_class, RowExclusiveLock);
        table_close(pg_index, RowExclusiveLock);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 989f820791..35a0dbeeb0 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -326,7 +326,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid 
toastIndexOid,
                                 list_make2("chunk_id", "chunk_seq"),
                                 BTREE_AM_OID,
                                 rel->rd_rel->reltablespace,
-                                collationIds, opclassIds, NULL, coloptions, 
(Datum) 0,
+                                collationIds, opclassIds, NULL, coloptions, 
NULL, (Datum) 0,
                                 INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL);
 
        table_close(toast_rel, NoLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index e56205abd8..dbd363589f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1193,7 +1193,7 @@ DefineIndex(Oid tableId,
                                         stmt->oldNumber, indexInfo, 
indexColNames,
                                         accessMethodId, tablespaceId,
                                         collationIds, opclassIds, 
opclassOptions,
-                                        coloptions, reloptions,
+                                        coloptions, NULL, reloptions,
                                         flags, constr_flags,
                                         allowSystemTableMods, !check_rights,
                                         &createdConstraintId);
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 41a0b9aee1..cb9d380616 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -80,6 +80,7 @@ extern Oid    index_create(Relation heapRelation,
                                                 const Oid *opclassIds,
                                                 const Datum *opclassOptions,
                                                 const int16 *coloptions,
+                                                const NullableDatum 
*stattargets,
                                                 Datum reloptions,
                                                 bits16 flags,
                                                 bits16 constr_flags,
diff --git a/src/include/catalog/pg_attribute.h 
b/src/include/catalog/pg_attribute.h
index 7336d7f2fd..98ab4ae538 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -210,6 +210,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
 
 typedef struct FormData_pg_attribute_extra
 {
+       NullableDatum attstattarget;
        NullableDatum attoptions;
 } FormData_pg_attribute_extra;
 
-- 
2.43.0

Reply via email to