Allow setting statistics target for extended statistics

When building statistics, we need to decide how many rows to sample and
how accurate the resulting statistics should be. Until now, it was not
possible to explicitly define statistics target for extended statistics
objects, the value was always computed from the per-attribute targets
with a fallback to the system-wide default statistics target.

That's a bit inconvenient, as it ties together the statistics target set
for per-column and extended statistics. In some cases it may be useful
to require larger sample / higher accuracy for extended statics (or the
other way around), but with this approach that's not possible.

So this commit introduces a new command, allowing to specify statistics
target for individual extended statistics objects, overriding the value
derived from per-attribute targets (and the system default).

  ALTER STATISTICS stat_name SET STATISTICS target_value;

When determining statistics target for an extended statistics object we
first look at this explicitly set value. When this value is -1, we fall
back to the old formula, looking at the per-attribute targets first and
then the system default. This means the behavior is backwards compatible
with older PostgreSQL releases.

Author: Tomas Vondra
Discussion: https://postgr.es/m/20190618213357.vli3i23vpkset2xd@development
Reviewed-by: Kirk Jamison, Dean Rasheed
This commit is contained in:
Tomas Vondra 2019-09-10 20:09:27 +02:00
parent bca6e64354
commit d06215d03b
22 changed files with 458 additions and 19 deletions

View File

@ -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 statistics 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>

View File

@ -307,7 +307,8 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
VacAttrStats **vacattrstats;
AnlIndexData *indexdata;
int targrows,
numrows;
numrows,
minrows;
double totalrows,
totaldeadrows;
HeapTuple *rows;
@ -491,6 +492,16 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
}
}
/*
* Look at extended statistics objects too, as those may define custom
* statistics target. So we may need to sample more rows and then build
* the statistics with enough detail.
*/
minrows = ComputeExtStatisticsRows(onerel, attr_cnt, vacattrstats);
if (targrows < minrows)
targrows = minrows;
/*
* Acquire the sample rows
*/

View File

@ -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,110 @@ 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 statistics 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 statistics object */
stxoid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);
/*
* If we got here and the OID is not valid, it means the statistics
* does not exist, but the command specified IF EXISTS. So report
* this as a simple NOTICE and we're done.
*/
if (!OidIsValid(stxoid))
{
char *schemaname;
char *statname;
Assert(stmt->missing_ok);
DeconstructQualifiedName(stmt->defnames, &schemaname, &statname);
if (schemaname)
ereport(NOTICE,
(errmsg("statistics object \"%s.%s\" does not exist, skipping",
schemaname, statname)));
else
ereport(NOTICE,
(errmsg("statistics object \"%s\" does not exist, skipping",
statname)));
return InvalidObjectAddress;
}
/* Search pg_statistic_ext */
rel = table_open(StatisticExtRelationId, RowExclusiveLock);
oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
/* Must be owner of the existing statistics 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 statistics 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.
*/

View File

@ -3497,6 +3497,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(missing_ok);
return newnode;
}
static CreateFunctionStmt *
_copyCreateFunctionStmt(const CreateFunctionStmt *from)
{
@ -5211,6 +5223,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;

View File

@ -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(missing_ok);
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;

View File

@ -2668,6 +2668,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(missing_ok);
}
static void
_outNotifyStmt(StringInfo str, const NotifyStmt *node)
{
@ -4130,6 +4140,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;

View File

@ -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->missing_ok = false;
n->stxstattarget = $6;
$$ = (Node *)n;
}
| ALTER STATISTICS IF_P EXISTS any_name SET STATISTICS SignedIconst
{
AlterStatsStmt *n = makeNode(AlterStatsStmt);
n->defnames = $5;
n->missing_ok = true;
n->stxstattarget = $8;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
* QUERY :

View File

@ -62,6 +62,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; /* statistics target (-1 for default) */
} StatExtEntry;
@ -71,7 +72,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
@ -107,6 +109,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
@ -131,6 +134,19 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
Assert(bms_num_members(stat->columns) >= 2 &&
bms_num_members(stat->columns) <= STATS_MAX_DIMENSIONS);
/* compute statistics target for this statistics */
stattarget = statext_compute_stattarget(stat->stattarget,
bms_num_members(stat->columns),
stats);
/*
* Don't rebuild statistics objects with statistics target set to 0 (we
* just leave the existing values around, just like we do for regular
* per-column statistics).
*/
if (stattarget == 0)
continue;
/* compute statistic of each requested type */
foreach(lc2, stat->types)
{
@ -144,7 +160,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 */
@ -157,6 +173,135 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
MemoryContextDelete(cxt);
}
/*
* ComputeExtStatisticsRows
* Compute number of rows required by extended statistics on a table.
*
* Computes number of rows we need to sample to build extended statistics on a
* table. This only looks at statistics we can actually build - for example
* when analyzing only some of the columns, this will skip statistics objects
* that would require additional columns.
*
* See statext_compute_stattarget for details about how we compute statistics
* target for a statistics objects (from the object target, attribute targets
* and default statistics target).
*/
int
ComputeExtStatisticsRows(Relation onerel,
int natts, VacAttrStats **vacattrstats)
{
Relation pg_stext;
ListCell *lc;
List *lstats;
MemoryContext cxt;
MemoryContext oldcxt;
int result = 0;
cxt = AllocSetContextCreate(CurrentMemoryContext,
"ComputeExtStatisticsRows",
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 this statistics object based on the columns
* analyzed. If not, ignore it (don't report anything, we'll do that
* during the actual build BuildRelationExtStatistics).
*/
stats = lookup_var_attr_stats(onerel, stat->columns,
natts, vacattrstats);
if (!stats)
continue;
/*
* Compute statistics target, based on what's set for the statistic
* object itself, and for its attributes.
*/
stattarget = statext_compute_stattarget(stat->stattarget,
nattrs, stats);
/* Use the largest value for all statistics objects. */
if (stattarget > result)
result = stattarget;
}
table_close(pg_stext, RowExclusiveLock);
MemoryContextSwitchTo(oldcxt);
MemoryContextDelete(cxt);
/* compute sample size based on the statistics target */
return (300 * result);
}
/*
* statext_compute_stattarget
* compute statistics target for an extended statistic
*
* When computing target for extended statistics objects, we consider three
* places where the target may be set - the statistics object itself,
* attributes the statistics is defined on, and then the default statistics
* target.
*
* First we look at what's set for the statistics object itself, using the
* ALTER STATISTICS ... SET STATISTICS command. If we find a valid value
* there (i.e. not -1) we're done. Otherwise we look at targets set for any
* of the attributes the statistic is defined on, and if there are columns
* with defined target, we use the maximum value. We do this mostly for
* backwards compatibility, because this is what we did before having
* statistics target for extended statistics.
*
* And finally, if we still don't have a statistics target, we use the value
* set in default_statistics_target.
*/
static int
statext_compute_stattarget(int stattarget, int nattrs, VacAttrStats **stats)
{
int i;
/*
* If there's statistics target set for the statistics object, use it.
* It may be set to 0 which disables building of that statistic.
*/
if (stattarget >= 0)
return stattarget;
/*
* The target for the statistics object is set to -1, in which case we
* look at the maximum target set for any of the attributes the object
* is defined on.
*/
for (i = 0; i < nattrs; i++)
{
/* keep the maximmum statistics target */
if (stats[i]->attr->attstattarget > stattarget)
stattarget = stats[i]->attr->attstattarget;
}
/*
* If the value is still negative (so neither the statistics object nor
* any of the columns have custom statistics target set), use the global
* default target.
*/
if (stattarget < 0)
stattarget = default_statistics_target;
/* As this point we should have a valid statistics 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?
@ -225,6 +370,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,

View File

@ -181,7 +181,7 @@ 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,
@ -210,15 +210,12 @@ statext_mcv_build(int numrows, HeapTuple *rows, Bitmapset *attrs,
groups = build_distinct_groups(nitems, items, mss, &ngroups);
/*
* Maximum number of MCV items to store, based on the attribute with the
* largest stats target (and the number of groups we have available).
* Maximum number of MCV items to store, based on the statistics target
* we computed for the statistics object (from target set for the object
* itself, attributes and the system default). In any case, we can't keep
* more groups than 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;

View File

@ -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;

View File

@ -7178,6 +7178,7 @@ getExtendedStatistics(Archive *fout)
int i_stxname;
int i_stxnamespace;
int i_rolname;
int i_stattarget;
int i;
/* Extended statistics were new in v10 */
@ -7186,10 +7187,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);
@ -7200,6 +7207,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));
@ -7214,6 +7222,7 @@ 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);
@ -16501,6 +16510,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));

View File

@ -382,6 +382,7 @@ typedef struct _statsExtInfo
{
DumpableObject dobj;
char *rolname; /* name of owner, or empty string */
int stattarget; /* statistics target */
} StatsExtInfo;
typedef struct _ruleInfo

View File

@ -2542,6 +2542,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

View File

@ -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))

View File

@ -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); /* statistics target */
/*
* variable-length fields start here, but we allow direct access to

View File

@ -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,

View File

@ -420,6 +420,7 @@ typedef enum NodeTag
T_CreateStatsStmt,
T_AlterCollationStmt,
T_CallStmt,
T_AlterStatsStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)

View File

@ -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 */
bool missing_ok; /* skip error if statistics object is missing */
} AlterStatsStmt;
/* ----------------------
* Create Function Statement
* ----------------------

View File

@ -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);

View File

@ -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 ComputeExtStatisticsRows(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,

View File

@ -98,11 +98,28 @@ CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
ANALYZE ab1;
WARNING: statistics object "public.ab1_a_b_stats" could not be computed for relation "public.ab1"
ALTER TABLE ab1 ALTER a SET STATISTICS -1;
-- setting statistics target 0 skips the statistics, without printing any message, so check catalog
ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
ANALYZE ab1;
SELECT stxname, stxdndistinct, stxddependencies, stxdmcv
FROM pg_statistic_ext s, pg_statistic_ext_data d
WHERE s.stxname = 'ab1_a_b_stats'
AND d.stxoid = s.oid;
stxname | stxdndistinct | stxddependencies | stxdmcv
---------------+---------------+------------------+---------
ab1_a_b_stats | | |
(1 row)
ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1;
-- partial analyze doesn't build stats either
ANALYZE ab1 (a);
WARNING: statistics object "public.ab1_a_b_stats" could not be computed for relation "public.ab1"
ANALYZE ab1;
DROP TABLE ab1;
ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
ERROR: statistics object "ab1_a_b_stats" does not exist
ALTER STATISTICS IF EXISTS ab1_a_b_stats SET STATISTICS 0;
NOTICE: statistics object "ab1_a_b_stats" does not exist, skipping
-- Ensure we can build statistics for tables with inheritance.
CREATE TABLE ab1 (a INTEGER, b INTEGER);
CREATE TABLE ab1c () INHERITS (ab1);

View File

@ -68,10 +68,20 @@ INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a;
CREATE STATISTICS ab1_a_b_stats ON a, b FROM ab1;
ANALYZE ab1;
ALTER TABLE ab1 ALTER a SET STATISTICS -1;
-- setting statistics target 0 skips the statistics, without printing any message, so check catalog
ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
ANALYZE ab1;
SELECT stxname, stxdndistinct, stxddependencies, stxdmcv
FROM pg_statistic_ext s, pg_statistic_ext_data d
WHERE s.stxname = 'ab1_a_b_stats'
AND d.stxoid = s.oid;
ALTER STATISTICS ab1_a_b_stats SET STATISTICS -1;
-- partial analyze doesn't build stats either
ANALYZE ab1 (a);
ANALYZE ab1;
DROP TABLE ab1;
ALTER STATISTICS ab1_a_b_stats SET STATISTICS 0;
ALTER STATISTICS IF EXISTS ab1_a_b_stats SET STATISTICS 0;
-- Ensure we can build statistics for tables with inheritance.
CREATE TABLE ab1 (a INTEGER, b INTEGER);