On Fri, Jun 21, 2019 at 08:09:18AM +0100, Dean Rasheed wrote:
On Thu, 20 Jun 2019 at 23:12, Tomas Vondra <tomas.von...@2ndquadrant.com> wrote:

On Thu, Jun 20, 2019 at 08:08:44AM +0100, Dean Rasheed wrote:
>On Tue, 18 Jun 2019 at 22:34, Tomas Vondra <tomas.von...@2ndquadrant.com> 
wrote:
>>
>> So I'm thinking we should allow tweaking the statistics for extended
>> stats, and serialize it in the pg_statistic_ext catalog. Any opinions
>> why that would be a bad idea?
>
>Seems reasonable to me. This might not be the only option we'll ever
>want to add though, so perhaps a "stxoptions text[]" column along the
>lines of a relation's reloptions would be the way to go.

I don't know - I kinda dislike the idea of stashing stuff like this into
text[] arrays unless there's a clear need for such flexibility (i.e.
vision to have more such options). Which I'm not sure is the case here.
And we kinda have a precedent in pg_attribute.attstattarget, so I'd use
the same approach here.


Hmm, maybe. I can certainly understand your dislike of using text[].
I'm not sure that we can confidently say that multivariate statistics
won't ever need additional tuning knobs, but I have no idea at the
moment what they might be, and nothing else has come up so far in all
the time spent considering MCV lists and histograms, so maybe this is
OK.


OK, attached is a patch implementing this - it adds

   ALTER STATISTICS ... SET STATISTICS ...

modifying a new stxstattarget column in pg_statistic_ext catalog,
following the same logic as pg_attribute.attstattarget.

During analyze, the per-ext-statistic value is determined like this:

1) When pg_statistic_ext.stxstattarget != (-1), we just use this value
and we're done.

2) Otherwise we inspect per-column attstattarget values, and use the
largest value. This is what we do now, so it's backwards-compatible
behavior.

3) If the value is still (-1), we use default_statistics_target.



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 8311b1dd46..d9fe653511 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..2ab0690c30 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,12 @@ 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,
+                                                                               
                natts, 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 +152,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 +165,116 @@ 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);
+               if (!stats)
+               {
+                       if (!IsAutoVacuumWorkerProcess())
+                               ereport(WARNING,
+                                               
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                                errmsg("statistics object 
\"%s.%s\" could not be computed for relation \"%s.%s\"",
+                                                               stat->schema, 
stat->name,
+                                                               
get_namespace_name(onerel->rd_rel->relnamespace),
+                                                               
RelationGetRelationName(onerel)),
+                                                errtable(onerel)));
+                       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 +343,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 5fe61ea0a4..54a8476891 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -160,7 +160,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,
@@ -192,12 +193,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/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7dcf342413..e0a4e56b30 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1837,7 +1837,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 fb03c52f50..c8b7deffbf 100644
--- a/src/include/statistics/extended_stats_internal.h
+++ b/src/include/statistics/extended_stats_internal.h
@@ -70,7 +70,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