On Sun, Jul 07, 2019 at 12:02:38AM +0200, Tomas Vondra wrote:
Hi,

apparently v1 of the ALTER STATISTICS patch was a bit confused about
length of the VacAttrStats array passed to statext_compute_stattarget,
causing segfaults. Attached v2 patch fixes that, and it also makes sure
we print warnings about ignored statistics only once.


v3 of the patch, adding pg_dump support - it works just like when you
tweak statistics target for a column, for example. When the value stored
in the catalog is not -1, pg_dump emits a separate ALTER STATISTICS
command setting it (for the already created statistics object).

I've considered making it part of CREATE STATISTICS itself, but it seems
a bit cumbersome and we don't do it for columns either. I'm not against
adding it in the future, but at this point I don't see a need.

At this point I'm not aware of any missing or broken pieces, so I'd
welcome feedback.

regards

--
Tomas Vondra                  http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/doc/src/sgml/ref/alter_statistics.sgml 
b/doc/src/sgml/ref/alter_statistics.sgml
index 58c7ed020d..987f9dbc6f 100644
--- a/doc/src/sgml/ref/alter_statistics.sgml
+++ b/doc/src/sgml/ref/alter_statistics.sgml
@@ -26,6 +26,7 @@ PostgreSQL documentation
 ALTER STATISTICS <replaceable class="parameter">name</replaceable> OWNER TO { 
<replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | 
SESSION_USER }
 ALTER STATISTICS <replaceable class="parameter">name</replaceable> RENAME TO 
<replaceable class="parameter">new_name</replaceable>
 ALTER STATISTICS <replaceable class="parameter">name</replaceable> SET SCHEMA 
<replaceable class="parameter">new_schema</replaceable>
+ALTER STATISTICS <replaceable class="parameter">name</replaceable> SET 
STATISTICS <replaceable class="parameter">new_target</replaceable>
 </synopsis>
  </refsynopsisdiv>
 
@@ -93,6 +94,22 @@ ALTER STATISTICS <replaceable 
class="parameter">name</replaceable> SET SCHEMA <r
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="parameter">new_target</replaceable></term>
+      <listitem>
+       <para>
+        The statistic-gathering target for this statistic object for subsequent
+        <xref linkend="sql-analyze"/> 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
+        target (<xref linkend="guc-default-statistics-target"/>).
+        For more information on the use of statistics by the
+        <productname>PostgreSQL</productname> query planner, refer to
+        <xref linkend="planner-stats"/>.
+       </para>
+      </listitem>
+     </varlistentry>
+
     </variablelist>
    </para>
   </refsect1>
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index d7004e5313..caa4fca99d 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -490,6 +490,19 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
                }
        }
 
+       /*
+        * Look at extended statistic objects too, because those may define 
their
+        * own statistic target. So we need to sample enough rows and then build
+        * the statistics with enough detail.
+        */
+       {
+               int nrows = ComputeExtStatisticsTarget(onerel,
+                                                                               
           attr_cnt, vacattrstats);
+
+               if (targrows < nrows)
+                       targrows = nrows;
+       }
+
        /*
         * Acquire the sample rows
         */
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index cf406f6f96..356b6096ac 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -14,6 +14,7 @@
  */
 #include "postgres.h"
 
+#include "access/heapam.h"
 #include "access/relation.h"
 #include "access/relscan.h"
 #include "access/table.h"
@@ -21,6 +22,7 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_statistic_ext.h"
 #include "catalog/pg_statistic_ext_data.h"
@@ -29,6 +31,7 @@
 #include "miscadmin.h"
 #include "statistics/statistics.h"
 #include "utils/builtins.h"
+#include "utils/fmgroids.h"
 #include "utils/inval.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
@@ -336,6 +339,7 @@ CreateStatistics(CreateStatsStmt *stmt)
        values[Anum_pg_statistic_ext_stxrelid - 1] = ObjectIdGetDatum(relid);
        values[Anum_pg_statistic_ext_stxname - 1] = NameGetDatum(&stxname);
        values[Anum_pg_statistic_ext_stxnamespace - 1] = 
ObjectIdGetDatum(namespaceId);
+       values[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(-1);
        values[Anum_pg_statistic_ext_stxowner - 1] = ObjectIdGetDatum(stxowner);
        values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
        values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
@@ -414,6 +418,85 @@ CreateStatistics(CreateStatsStmt *stmt)
        return myself;
 }
 
+
+/*
+ *             ALTER STATISTICS
+ */
+ObjectAddress
+AlterStatistics(AlterStatsStmt *stmt)
+{
+       Relation        rel;
+       Oid                     stxoid;
+       HeapTuple       oldtup;
+       HeapTuple       newtup;
+       Datum           repl_val[Natts_pg_statistic_ext];
+       bool            repl_null[Natts_pg_statistic_ext];
+       bool            repl_repl[Natts_pg_statistic_ext];
+       ObjectAddress   address;
+       int                     newtarget = stmt->stxstattarget;
+
+       /* Limit target to a sane range */
+       if (newtarget < -1)
+       {
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("statistics target %d is too low",
+                                               newtarget)));
+       }
+       else if (newtarget > 10000)
+       {
+               newtarget = 10000;
+               ereport(WARNING,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("lowering statistics target to %d",
+                                               newtarget)));
+       }
+
+       /* lookup OID of the statistic object */
+       stxoid = get_statistics_object_oid(stmt->defnames, false);
+
+       /* Search pg_statistic_ext */
+       rel = table_open(StatisticExtRelationId, RowExclusiveLock);
+
+       oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
+
+       /* Must be owner of the existing statistic object */
+       if (!pg_statistics_object_ownercheck(stxoid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_STATISTIC_EXT,
+                                          NameListToString(stmt->defnames));
+
+       /* Build new tuple. */
+       memset(repl_val, 0, sizeof(repl_val));
+       memset(repl_null, false, sizeof(repl_null));
+       memset(repl_repl, false, sizeof(repl_repl));
+
+       /* replace the stxstattarget column */
+       repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
+       repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = 
Int32GetDatum(newtarget);
+
+       newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
+                                                          repl_val, repl_null, 
repl_repl);
+
+       /* Update system catalog. */
+       CatalogTupleUpdate(rel, &newtup->t_self, newtup);
+
+       InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
+
+       ObjectAddressSet(address, StatisticExtRelationId, stxoid);
+
+       /*
+        * NOTE: because we only support altering the statistic target, not the
+        * other fields, there is no need to update dependencies.
+        */
+
+       heap_freetuple(newtup);
+       ReleaseSysCache(oldtup);
+
+       table_close(rel, RowExclusiveLock);
+
+       return address;
+}
+
 /*
  * Guts of statistics object deletion.
  */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 78deade89b..048239fe34 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3493,6 +3493,18 @@ _copyCreateStatsStmt(const CreateStatsStmt *from)
        return newnode;
 }
 
+static AlterStatsStmt *
+_copyAlterStatsStmt(const AlterStatsStmt *from)
+{
+       AlterStatsStmt *newnode = makeNode(AlterStatsStmt);
+
+       COPY_NODE_FIELD(defnames);
+       COPY_SCALAR_FIELD(stxstattarget);
+       COPY_SCALAR_FIELD(if_not_exists);
+
+       return newnode;
+}
+
 static CreateFunctionStmt *
 _copyCreateFunctionStmt(const CreateFunctionStmt *from)
 {
@@ -5249,6 +5261,9 @@ copyObjectImpl(const void *from)
                case T_CreateStatsStmt:
                        retval = _copyCreateStatsStmt(from);
                        break;
+               case T_AlterStatsStmt:
+                       retval = _copyAlterStatsStmt(from);
+                       break;
                case T_CreateFunctionStmt:
                        retval = _copyCreateFunctionStmt(from);
                        break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4f2ebe5118..4c35cdf41e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1365,6 +1365,16 @@ _equalCreateStatsStmt(const CreateStatsStmt *a, const 
CreateStatsStmt *b)
        return true;
 }
 
+static bool
+_equalAlterStatsStmt(const AlterStatsStmt *a, const AlterStatsStmt *b)
+{
+       COMPARE_NODE_FIELD(defnames);
+       COMPARE_SCALAR_FIELD(stxstattarget);
+       COMPARE_SCALAR_FIELD(if_not_exists);
+
+       return true;
+}
+
 static bool
 _equalCreateFunctionStmt(const CreateFunctionStmt *a, const CreateFunctionStmt 
*b)
 {
@@ -3309,6 +3319,9 @@ equal(const void *a, const void *b)
                case T_CreateStatsStmt:
                        retval = _equalCreateStatsStmt(a, b);
                        break;
+               case T_AlterStatsStmt:
+                       retval = _equalAlterStatsStmt(a, b);
+                       break;
                case T_CreateFunctionStmt:
                        retval = _equalCreateFunctionStmt(a, b);
                        break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8400dd319e..4551d7a023 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2662,6 +2662,16 @@ _outCreateStatsStmt(StringInfo str, const 
CreateStatsStmt *node)
        WRITE_BOOL_FIELD(if_not_exists);
 }
 
+static void
+_outAlterStatsStmt(StringInfo str, const AlterStatsStmt *node)
+{
+       WRITE_NODE_TYPE("ALTERSTATSSTMT");
+
+       WRITE_NODE_FIELD(defnames);
+       WRITE_INT_FIELD(stxstattarget);
+       WRITE_BOOL_FIELD(if_not_exists);
+}
+
 static void
 _outNotifyStmt(StringInfo str, const NotifyStmt *node)
 {
@@ -4124,6 +4134,9 @@ outNode(StringInfo str, const void *obj)
                        case T_CreateStatsStmt:
                                _outCreateStatsStmt(str, obj);
                                break;
+                       case T_AlterStatsStmt:
+                               _outAlterStatsStmt(str, obj);
+                               break;
                        case T_NotifyStmt:
                                _outNotifyStmt(str, obj);
                                break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 208b4a1f28..29e24fdfea 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -252,7 +252,7 @@ static Node *makeRecursiveViewSelect(char *relname, List 
*aliases, Node *query);
                AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
                AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt 
AlterForeignTableStmt
                AlterCompositeTypeStmt AlterUserMappingStmt
-               AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
+               AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt AlterStatsStmt
                AlterDefaultPrivilegesStmt DefACLAction
                AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt
                ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
@@ -852,6 +852,7 @@ stmt :
                        | AlterRoleSetStmt
                        | AlterRoleStmt
                        | AlterSubscriptionStmt
+                       | AlterStatsStmt
                        | AlterTSConfigurationStmt
                        | AlterTSDictionaryStmt
                        | AlterUserMappingStmt
@@ -3984,6 +3985,34 @@ CreateStatsStmt:
                                }
                        ;
 
+
+/*****************************************************************************
+ *
+ *             QUERY :
+ *                             ALTER STATISTICS [IF EXISTS] stats_name
+ *                                     SET STATISTICS  <SignedIconst>
+ *
+ *****************************************************************************/
+
+AlterStatsStmt:
+                       ALTER STATISTICS any_name SET STATISTICS SignedIconst
+                               {
+                                       AlterStatsStmt *n = 
makeNode(AlterStatsStmt);
+                                       n->defnames = $3;
+                                       n->if_not_exists = false;
+                                       n->stxstattarget = $6;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER STATISTICS IF_P EXISTS any_name SET STATISTICS 
SignedIconst
+                               {
+                                       AlterStatsStmt *n = 
makeNode(AlterStatsStmt);
+                                       n->defnames = $5;
+                                       n->if_not_exists = true;
+                                       n->stxstattarget = $8;
+                                       $$ = (Node *)n;
+                               }
+                       ;
+
 /*****************************************************************************
  *
  *             QUERY :
diff --git a/src/backend/statistics/extended_stats.c 
b/src/backend/statistics/extended_stats.c
index c56ed48270..f0d0deabaf 100644
--- a/src/backend/statistics/extended_stats.c
+++ b/src/backend/statistics/extended_stats.c
@@ -61,6 +61,7 @@ typedef struct StatExtEntry
        char       *name;                       /* statistics object's name */
        Bitmapset  *columns;            /* attribute numbers covered by the 
object */
        List       *types;                      /* 'char' list of enabled 
statistic kinds */
+       int                     stattarget;             /* statistic target (-1 
for default) */
 } StatExtEntry;
 
 
@@ -70,7 +71,8 @@ static VacAttrStats **lookup_var_attr_stats(Relation rel, 
Bitmapset *attrs,
 static void statext_store(Oid relid,
                                                  MVNDistinct *ndistinct, 
MVDependencies *dependencies,
                                                  MCVList *mcv, VacAttrStats 
**stats);
-
+static int statext_compute_stattarget(int stattarget,
+                                                                         int 
natts, VacAttrStats **stats);
 
 /*
  * Compute requested extended stats, using the rows sampled for the plain
@@ -106,6 +108,7 @@ BuildRelationExtStatistics(Relation onerel, double 
totalrows,
                MCVList    *mcv = NULL;
                VacAttrStats **stats;
                ListCell   *lc2;
+               int                     stattarget;
 
                /*
                 * Check if we can build these stats based on the column 
analyzed. If
@@ -130,6 +133,13 @@ BuildRelationExtStatistics(Relation onerel, double 
totalrows,
                Assert(bms_num_members(stat->columns) >= 2 &&
                           bms_num_members(stat->columns) <= 
STATS_MAX_DIMENSIONS);
 
+               /* compute current statistic target */
+               stattarget = statext_compute_stattarget(stat->stattarget,
+                                                                               
                bms_num_members(stat->columns),
+                                                                               
                stats);
+
+               /* XXX What if the target is set to 0? Reset the statistic? */
+
                /* compute statistic of each requested type */
                foreach(lc2, stat->types)
                {
@@ -143,7 +153,7 @@ BuildRelationExtStatistics(Relation onerel, double 
totalrows,
                                                                                
                                  stat->columns, stats);
                        else if (t == STATS_EXT_MCV)
                                mcv = statext_mcv_build(numrows, rows, 
stat->columns, stats,
-                                                                               
totalrows);
+                                                                               
totalrows, stattarget);
                }
 
                /* store the statistics in the catalog */
@@ -156,6 +166,108 @@ BuildRelationExtStatistics(Relation onerel, double 
totalrows,
        MemoryContextDelete(cxt);
 }
 
+/*
+ * ComputeExtStatisticsTarget
+ *             Compute the largest statistic target for all extended 
statistics.
+ */
+int
+ComputeExtStatisticsTarget(Relation onerel,
+                                                  int natts, VacAttrStats 
**vacattrstats)
+{
+       Relation        pg_stext;
+       ListCell   *lc;
+       List       *lstats;
+       MemoryContext cxt;
+       MemoryContext oldcxt;
+       int                     result = 0;
+
+       cxt = AllocSetContextCreate(CurrentMemoryContext,
+                                                               
"ComputeExtStatisticsTarget",
+                                                               
ALLOCSET_DEFAULT_SIZES);
+       oldcxt = MemoryContextSwitchTo(cxt);
+
+       pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
+       lstats = fetch_statentries_for_relation(pg_stext, 
RelationGetRelid(onerel));
+
+       foreach(lc, lstats)
+       {
+               StatExtEntry   *stat = (StatExtEntry *) lfirst(lc);
+               int                             stattarget = stat->stattarget;
+               VacAttrStats  **stats;
+               int                             nattrs = 
bms_num_members(stat->columns);
+
+               /*
+                * Check if we can build these stats based on the column 
analyzed. If
+                * not, report this fact (except in autovacuum) and move on.
+                */
+               stats = lookup_var_attr_stats(onerel, stat->columns,
+                                                                         
natts, vacattrstats);
+
+               /* ignore statistics we'll skip later */
+               if (!stats)
+                       continue;
+
+               /* compute statistic target, based on the statistic object and 
attributes */
+               stattarget = statext_compute_stattarget(stat->stattarget, 
nattrs, stats);
+
+               /* Use the largest value for all statistic objects. */
+               if (stattarget > result)
+                       result = stattarget;
+       }
+
+       table_close(pg_stext, RowExclusiveLock);
+
+       MemoryContextSwitchTo(oldcxt);
+       MemoryContextDelete(cxt);
+
+       return (300 * result);
+}
+
+/*
+ * statext_compute_stattarget
+ *             compute statistic target for an extended statistic
+ *
+ * If the statistic object has custom value (set using ALTER STATISTICS ...
+ * SET STATISTICS), then we simply use that. Otherwise we look at targets
+ * for columns the object is defined on and use the largest value. Finally,
+ * if the target is still (-1), we use the default_statistics_target.
+ */
+static int
+statext_compute_stattarget(int stattarget, int nattrs, VacAttrStats **stats)
+{
+       int     i;
+
+       /* if there's statistic target set for the statistic object, use it */
+       if (stattarget >= 0)
+               return stattarget;
+
+       /*
+        * The stattarget column is negative, we look at targets for each
+        * column the statistics is based on, and use use the largest value
+        * for any of the columns.
+        */
+       {
+               for (i = 0; i < nattrs; i++)
+               {
+                       if (stats[i]->attr->attstattarget > stattarget)
+                               stattarget = stats[i]->attr->attstattarget;
+               }
+       }
+
+       /*
+        * If the value is still negative (so neither the extended statistic
+        * object nor any of the columns) have custom statistic target set.
+        * In that case use the default target.
+        */
+       if (stattarget < 0)
+               stattarget = default_statistics_target;
+
+       /* As this point we should have a valid statistic target. */
+       Assert((stattarget >= 0) && (stattarget <= 10000));
+
+       return stattarget;
+}
+
 /*
  * statext_is_kind_built
  *             Is this stat kind built in the given pg_statistic_ext_data 
tuple?
@@ -224,6 +336,7 @@ fetch_statentries_for_relation(Relation pg_statext, Oid 
relid)
                entry->statOid = staForm->oid;
                entry->schema = get_namespace_name(staForm->stxnamespace);
                entry->name = pstrdup(NameStr(staForm->stxname));
+               entry->stattarget = staForm->stxstattarget;
                for (i = 0; i < staForm->stxkeys.dim1; i++)
                {
                        entry->columns = bms_add_member(entry->columns,
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index 913a72ff67..8491b20af3 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -162,7 +162,8 @@ get_mincount_for_mcv_list(int samplerows, double totalrows)
  */
 MCVList *
 statext_mcv_build(int numrows, HeapTuple *rows, Bitmapset *attrs,
-                                 VacAttrStats **stats, double totalrows)
+                                 VacAttrStats **stats, double totalrows,
+                                 int stattarget)
 {
        int                     i,
                                numattrs,
@@ -194,12 +195,7 @@ statext_mcv_build(int numrows, HeapTuple *rows, Bitmapset 
*attrs,
         * Maximum number of MCV items to store, based on the attribute with the
         * largest stats target (and the number of groups we have available).
         */
-       nitems = stats[0]->attr->attstattarget;
-       for (i = 1; i < numattrs; i++)
-       {
-               if (stats[i]->attr->attstattarget > nitems)
-                       nitems = stats[i]->attr->attstattarget;
-       }
+       nitems = stattarget;
        if (nitems > ngroups)
                nitems = ngroups;
 
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 05ec7f3ac6..1299066c63 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1688,6 +1688,10 @@ ProcessUtilitySlow(ParseState *pstate,
                                address = CreateStatistics((CreateStatsStmt *) 
parsetree);
                                break;
 
+                       case T_AlterStatsStmt:
+                               address = AlterStatistics((AlterStatsStmt *) 
parsetree);
+                               break;
+
                        case T_AlterCollationStmt:
                                address = AlterCollation((AlterCollationStmt *) 
parsetree);
                                break;
@@ -2805,6 +2809,10 @@ CreateCommandTag(Node *parsetree)
                        tag = "CREATE STATISTICS";
                        break;
 
+               case T_AlterStatsStmt:
+                       tag = "ALTER STATISTICS";
+                       break;
+
                case T_DeallocateStmt:
                        {
                                DeallocateStmt *stmt = (DeallocateStmt *) 
parsetree;
@@ -3393,6 +3401,10 @@ GetCommandLogLevel(Node *parsetree)
                        lev = LOGSTMT_DDL;
                        break;
 
+               case T_AlterStatsStmt:
+                       lev = LOGSTMT_DDL;
+                       break;
+
                case T_AlterCollationStmt:
                        lev = LOGSTMT_DDL;
                        break;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 806fc78f04..f40cbaae34 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -7172,6 +7172,7 @@ getExtendedStatistics(Archive *fout)
        int                     i_stxname;
        int                     i_stxnamespace;
        int                     i_rolname;
+       int                     i_stattarget;
        int                     i;
 
        /* Extended statistics were new in v10 */
@@ -7180,10 +7181,16 @@ getExtendedStatistics(Archive *fout)
 
        query = createPQExpBuffer();
 
-       appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, "
-                                         "stxnamespace, (%s stxowner) AS 
rolname "
-                                         "FROM pg_catalog.pg_statistic_ext",
-                                         username_subquery);
+       if (fout->remoteVersion < 130000)
+               appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, "
+                                                 "stxnamespace, (%s stxowner) 
AS rolname, (-1) AS stxstattarget "
+                                                 "FROM 
pg_catalog.pg_statistic_ext",
+                                                 username_subquery);
+       else
+               appendPQExpBuffer(query, "SELECT tableoid, oid, stxname, "
+                                                 "stxnamespace, (%s stxowner) 
AS rolname, stxstattarget "
+                                                 "FROM 
pg_catalog.pg_statistic_ext",
+                                                 username_subquery);
 
        res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7194,6 +7201,7 @@ getExtendedStatistics(Archive *fout)
        i_stxname = PQfnumber(res, "stxname");
        i_stxnamespace = PQfnumber(res, "stxnamespace");
        i_rolname = PQfnumber(res, "rolname");
+       i_stattarget = PQfnumber(res, "stxstattarget");
 
        statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
 
@@ -7208,7 +7216,8 @@ getExtendedStatistics(Archive *fout)
                        findNamespace(fout,
                                                  atooid(PQgetvalue(res, i, 
i_stxnamespace)));
                statsextinfo[i].rolname = pg_strdup(PQgetvalue(res, i, 
i_rolname));
-
+               statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, 
i_stattarget));
+ 
                /* Decide whether we want to dump it */
                selectDumpableObject(&(statsextinfo[i].dobj), fout);
 
@@ -16495,6 +16504,19 @@ dumpStatisticsExt(Archive *fout, StatsExtInfo 
*statsextinfo)
        /* Result of pg_get_statisticsobjdef is complete except for semicolon */
        appendPQExpBuffer(q, "%s;\n", stxdef);
 
+       /*
+        * We only issue an ALTER STATISTICS statement if the stxstattarget 
entry
+        * for this statictics object is non-negative (i.e. it's not the default
+        * value).
+        */
+       if (statsextinfo->stattarget >= 0)
+       {
+               appendPQExpBuffer(q, "ALTER STATISTICS %s ",
+                                                 
fmtQualifiedDumpable(statsextinfo));
+               appendPQExpBuffer(q, "SET STATISTICS %d;\n",
+                                                 statsextinfo->stattarget);
+       }
+
        appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
                                          fmtQualifiedDumpable(statsextinfo));
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index c3c2ea1473..baaa2a24a0 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -386,6 +386,7 @@ typedef struct _statsExtInfo
 {
        DumpableObject dobj;
        char       *rolname;            /* name of owner, or empty string */
+       int                     stattarget;             /* statistic target */
 } StatsExtInfo;
 
 typedef struct _ruleInfo
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index c56bf00e4b..7617f35a4f 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2504,6 +2504,17 @@ my %tests = (
                unlike => { exclude_dump_test_schema => 1, },
        },
 
+       'ALTER STATISTICS extended_stats_options' => {
+               create_order => 98,
+               create_sql   => 'ALTER STATISTICS dump_test.test_ext_stats_opts 
SET STATISTICS 1000',
+               regexp => qr/^
+                       \QALTER STATISTICS dump_test.test_ext_stats_opts SET 
STATISTICS 1000;\E
+                   /xms,
+               like =>
+                 { %full_runs, %dump_test_schema_runs, section_post_data => 1, 
},
+               unlike => { exclude_dump_test_schema => 1, },
+       },
+
        'CREATE SEQUENCE test_table_col1_seq' => {
                regexp => qr/^
                        \QCREATE SEQUENCE dump_test.test_table_col1_seq\E
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9009b05e12..35a264c29b 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1840,7 +1840,7 @@ psql_completion(const char *text, int start, int end)
 
        /* ALTER STATISTICS <name> */
        else if (Matches("ALTER", "STATISTICS", MatchAny))
-               COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
+               COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA", "SET 
STATISTICS");
 
        /* ALTER TRIGGER <name>, add ON */
        else if (Matches("ALTER", "TRIGGER", MatchAny))
diff --git a/src/include/catalog/pg_statistic_ext.h 
b/src/include/catalog/pg_statistic_ext.h
index d8c5e0651e..5f6bfdd56c 100644
--- a/src/include/catalog/pg_statistic_ext.h
+++ b/src/include/catalog/pg_statistic_ext.h
@@ -41,6 +41,7 @@ CATALOG(pg_statistic_ext,3381,StatisticExtRelationId)
        Oid                     stxnamespace;   /* OID of statistics object's 
namespace */
 
        Oid                     stxowner;               /* statistics object's 
owner */
+       int32           stxstattarget BKI_DEFAULT(-1);          /* statistic 
target */
 
        /*
         * variable-length fields start here, but we allow direct access to
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index b4e7db67c3..1dc6dc2ca0 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -87,6 +87,7 @@ extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
 
 /* commands/statscmds.c */
 extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
 extern void RemoveStatisticsById(Oid statsOid);
 extern void UpdateStatisticsForTypeChange(Oid statsOid,
                                                                                
  Oid relationOid, int attnum,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 4e2fb39105..57589d4fac 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -420,6 +420,7 @@ typedef enum NodeTag
        T_CreateStatsStmt,
        T_AlterCollationStmt,
        T_CallStmt,
+       T_AlterStatsStmt,
 
        /*
         * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 94ded3c135..a6a3382a46 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2789,6 +2789,18 @@ typedef struct CreateStatsStmt
        bool            if_not_exists;  /* do nothing if stats name already 
exists */
 } CreateStatsStmt;
 
+/* ----------------------
+ *             Alter Statistics Statement
+ * ----------------------
+ */
+typedef struct AlterStatsStmt
+{
+       NodeTag         type;
+       List       *defnames;           /* qualified name (list of Value 
strings) */
+       int                     stxstattarget;  /* statistics target, or NULL */
+       bool            if_not_exists;  /* do nothing if stats name already 
exists */
+} AlterStatsStmt;
+
 /* ----------------------
  *             Create Function Statement
  * ----------------------
diff --git a/src/include/statistics/extended_stats_internal.h 
b/src/include/statistics/extended_stats_internal.h
index 8fc541993f..71fd509f16 100644
--- a/src/include/statistics/extended_stats_internal.h
+++ b/src/include/statistics/extended_stats_internal.h
@@ -71,7 +71,7 @@ extern MVDependencies *statext_dependencies_deserialize(bytea 
*data);
 
 extern MCVList *statext_mcv_build(int numrows, HeapTuple *rows,
                                                                  Bitmapset 
*attrs, VacAttrStats **stats,
-                                                                 double 
totalrows);
+                                                                 double 
totalrows, int stattarget);
 extern bytea *statext_mcv_serialize(MCVList *mcv, VacAttrStats **stats);
 extern MCVList *statext_mcv_deserialize(bytea *data);
 
diff --git a/src/include/statistics/statistics.h 
b/src/include/statistics/statistics.h
index cb7bc630e9..53ec906936 100644
--- a/src/include/statistics/statistics.h
+++ b/src/include/statistics/statistics.h
@@ -100,6 +100,8 @@ extern MCVList *statext_mcv_load(Oid mvoid);
 extern void BuildRelationExtStatistics(Relation onerel, double totalrows,
                                                                           int 
numrows, HeapTuple *rows,
                                                                           int 
natts, VacAttrStats **vacattrstats);
+extern int ComputeExtStatisticsTarget(Relation onerel,
+                                                                         int 
natts, VacAttrStats **stats);
 extern bool statext_is_kind_built(HeapTuple htup, char kind);
 extern Selectivity dependencies_clauselist_selectivity(PlannerInfo *root,
                                                                                
                           List *clauses,

Reply via email to