Commit 11d8d72c authored by Tom Lane's avatar Tom Lane

Allow multiple tables to be specified in one VACUUM or ANALYZE command.

Not much to say about this; does what it says on the tin.

However, formerly, if there was a column list then the ANALYZE action was
implied; now it must be specified, or you get an error.  This is because
it would otherwise be a bit unclear what the user meant if some tables
have column lists and some don't.

Nathan Bossart, reviewed by Michael Paquier and Masahiko Sawada, with some
editorialization by me

Discussion: https://postgr.es/m/E061A8E3-5E3D-494D-94F0-E8A9B312BBFC@amazon.com
parent 45f9d086
......@@ -21,7 +21,11 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_and_columns</replaceable> [, ...] ]
<phrase>where <replaceable class="PARAMETER">table_and_columns</replaceable> is:</phrase>
<replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
</synopsis>
</refsynopsisdiv>
......@@ -38,9 +42,11 @@ ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [
</para>
<para>
With no parameter, <command>ANALYZE</command> examines every table in the
current database. With a parameter, <command>ANALYZE</command> examines
only that table. It is further possible to give a list of column names,
Without a <replaceable class="PARAMETER">table_and_columns</replaceable>
list, <command>ANALYZE</command> processes every table and materialized view
in the current database that the current user has permission to analyze.
With a list, <command>ANALYZE</command> processes only those table(s).
It is further possible to give a list of column names for a table,
in which case only the statistics for those columns are collected.
</para>
</refsect1>
......
......@@ -21,9 +21,20 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
VACUUM [ ( <replaceable class="PARAMETER">option</replaceable> [, ...] ) ] [ <replaceable class="PARAMETER">table_and_columns</replaceable> [, ...] ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="PARAMETER">table_and_columns</replaceable> [, ...] ]
<phrase>where <replaceable class="PARAMETER">option</replaceable> can be one of:</phrase>
FULL
FREEZE
VERBOSE
ANALYZE
DISABLE_PAGE_SKIPPING
<phrase>and <replaceable class="PARAMETER">table_and_columns</replaceable> is:</phrase>
<replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
</synopsis>
</refsynopsisdiv>
......@@ -40,9 +51,10 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
</para>
<para>
With no parameter, <command>VACUUM</command> processes every table in the
current database that the current user has permission to vacuum.
With a parameter, <command>VACUUM</command> processes only that table.
Without a <replaceable class="PARAMETER">table_and_columns</replaceable>
list, <command>VACUUM</command> processes every table and materialized view
in the current database that the current user has permission to vacuum.
With a list, <command>VACUUM</command> processes only those table(s).
</para>
<para>
......@@ -141,8 +153,8 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
except when performing an aggressive vacuum, some pages may be skipped
in order to avoid waiting for other sessions to finish using them.
This option disables all page-skipping behavior, and is intended to
be used only the contents of the visibility map are thought to
be suspect, which should happen only if there is a hardware or software
be used only when the contents of the visibility map are
suspect, which should happen only if there is a hardware or software
issue causing database corruption.
</para>
</listitem>
......@@ -152,9 +164,8 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
<term><replaceable class="PARAMETER">table_name</replaceable></term>
<listitem>
<para>
The name (optionally schema-qualified) of a specific table to
vacuum. If omitted, all regular tables and materialized views in the
current database are vacuumed. If the specified table is a partitioned
The name (optionally schema-qualified) of a specific table or
materialized view to vacuum. If the specified table is a partitioned
table, all of its leaf partitions are vacuumed.
</para>
</listitem>
......@@ -165,7 +176,8 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
<listitem>
<para>
The name of a specific column to analyze. Defaults to all columns.
If a column list is specified, <literal>ANALYZE</> is implied.
If a column list is specified, <literal>ANALYZE</> must also be
specified.
</para>
</listitem>
</varlistentry>
......
This diff is collapsed.
......@@ -3766,7 +3766,18 @@ _copyVacuumStmt(const VacuumStmt *from)
VacuumStmt *newnode = makeNode(VacuumStmt);
COPY_SCALAR_FIELD(options);
COPY_NODE_FIELD(rels);
return newnode;
}
static VacuumRelation *
_copyVacuumRelation(const VacuumRelation *from)
{
VacuumRelation *newnode = makeNode(VacuumRelation);
COPY_NODE_FIELD(relation);
COPY_SCALAR_FIELD(oid);
COPY_NODE_FIELD(va_cols);
return newnode;
......@@ -5215,6 +5226,9 @@ copyObjectImpl(const void *from)
case T_VacuumStmt:
retval = _copyVacuumStmt(from);
break;
case T_VacuumRelation:
retval = _copyVacuumRelation(from);
break;
case T_ExplainStmt:
retval = _copyExplainStmt(from);
break;
......
......@@ -1663,7 +1663,16 @@ static bool
_equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
{
COMPARE_SCALAR_FIELD(options);
COMPARE_NODE_FIELD(rels);
return true;
}
static bool
_equalVacuumRelation(const VacuumRelation *a, const VacuumRelation *b)
{
COMPARE_NODE_FIELD(relation);
COMPARE_SCALAR_FIELD(oid);
COMPARE_NODE_FIELD(va_cols);
return true;
......@@ -3361,6 +3370,9 @@ equal(const void *a, const void *b)
case T_VacuumStmt:
retval = _equalVacuumStmt(a, b);
break;
case T_VacuumRelation:
retval = _equalVacuumRelation(a, b);
break;
case T_ExplainStmt:
retval = _equalExplainStmt(a, b);
break;
......
......@@ -611,3 +611,18 @@ makeGroupingSet(GroupingSetKind kind, List *content, int location)
n->location = location;
return n;
}
/*
* makeVacuumRelation -
* create a VacuumRelation node
*/
VacuumRelation *
makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
{
VacuumRelation *v = makeNode(VacuumRelation);
v->relation = relation;
v->oid = oid;
v->va_cols = va_cols;
return v;
}
......@@ -365,6 +365,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> DefACLOptionList
%type <ival> import_qualification_type
%type <importqual> import_qualification
%type <node> vacuum_relation
%type <list> stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
......@@ -396,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
......@@ -10147,7 +10149,7 @@ cluster_index_specification:
*
*****************************************************************************/
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_vacuum_relation_list
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM;
......@@ -10157,22 +10159,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_FREEZE;
if ($4)
n->options |= VACOPT_VERBOSE;
n->relation = NULL;
n->va_cols = NIL;
$$ = (Node *)n;
}
| VACUUM opt_full opt_freeze opt_verbose qualified_name
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM;
if ($2)
n->options |= VACOPT_FULL;
if ($3)
n->options |= VACOPT_FREEZE;
if ($4)
n->options |= VACOPT_VERBOSE;
n->relation = $5;
n->va_cols = NIL;
n->rels = $5;
$$ = (Node *)n;
}
| VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt
......@@ -10187,22 +10174,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VERBOSE;
$$ = (Node *)n;
}
| VACUUM '(' vacuum_option_list ')'
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | $3;
n->relation = NULL;
n->va_cols = NIL;
$$ = (Node *) n;
}
| VACUUM '(' vacuum_option_list ')' qualified_name opt_name_list
| VACUUM '(' vacuum_option_list ')' opt_vacuum_relation_list
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | $3;
n->relation = $5;
n->va_cols = $6;
if (n->va_cols != NIL) /* implies analyze */
n->options |= VACOPT_ANALYZE;
n->rels = $5;
$$ = (Node *) n;
}
;
......@@ -10229,25 +10205,13 @@ vacuum_option_elem:
}
;
AnalyzeStmt:
analyze_keyword opt_verbose
AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_ANALYZE;
if ($2)
n->options |= VACOPT_VERBOSE;
n->relation = NULL;
n->va_cols = NIL;
$$ = (Node *)n;
}
| analyze_keyword opt_verbose qualified_name opt_name_list
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_ANALYZE;
if ($2)
n->options |= VACOPT_VERBOSE;
n->relation = $3;
n->va_cols = $4;
n->rels = $3;
$$ = (Node *)n;
}
;
......@@ -10275,6 +10239,25 @@ opt_name_list:
| /*EMPTY*/ { $$ = NIL; }
;
vacuum_relation:
qualified_name opt_name_list
{
$$ = (Node *) makeVacuumRelation($1, InvalidOid, $2);
}
;
vacuum_relation_list:
vacuum_relation
{ $$ = list_make1($1); }
| vacuum_relation_list ',' vacuum_relation
{ $$ = lappend($1, $3); }
;
opt_vacuum_relation_list:
vacuum_relation_list { $$ = $1; }
| /*EMPTY*/ { $$ = NIL; }
;
/*****************************************************************************
*
......
......@@ -79,6 +79,7 @@
#include "lib/ilist.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/fork_process.h"
......@@ -3081,20 +3082,19 @@ relation_needs_vacanalyze(Oid relid,
static void
autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
{
RangeVar rangevar;
/* Set up command parameters --- use local variables instead of palloc */
MemSet(&rangevar, 0, sizeof(rangevar));
rangevar.schemaname = tab->at_nspname;
rangevar.relname = tab->at_relname;
rangevar.location = -1;
RangeVar *rangevar;
VacuumRelation *rel;
List *rel_list;
/* Let pgstat know what we're doing */
autovac_report_activity(tab);
vacuum(tab->at_vacoptions, &rangevar, tab->at_relid, &tab->at_params, NIL,
bstrategy, true);
/* Set up one VacuumRelation target, identified by OID, for vacuum() */
rangevar = makeRangeVar(tab->at_nspname, tab->at_relname, -1);
rel = makeVacuumRelation(rangevar, tab->at_relid, NIL);
rel_list = list_make1(rel);
vacuum(tab->at_vacoptions, rel_list, &tab->at_params, bstrategy, true);
}
/*
......
......@@ -157,8 +157,7 @@ extern int vacuum_multixact_freeze_table_age;
/* in commands/vacuum.c */
extern void ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel);
extern void vacuum(int options, RangeVar *relation, Oid relid,
VacuumParams *params, List *va_cols,
extern void vacuum(int options, List *relations, VacuumParams *params,
BufferAccessStrategy bstrategy, bool isTopLevel);
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
int *nindexes, Relation **Irel);
......
......@@ -86,4 +86,6 @@ extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int location);
extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols);
#endif /* MAKEFUNC_H */
......@@ -468,6 +468,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
T_VacuumRelation,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
......
......@@ -1778,8 +1778,8 @@ typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
AlterTableType subtype; /* Type of table alteration to apply */
char *name; /* column, constraint, or trigger to act on,
* or tablespace */
int16 num; /* attribute number for columns referenced
* by number */
int16 num; /* attribute number for columns referenced by
* number */
RoleSpec *newowner;
Node *def; /* definition of new column, index,
* constraint, or parent table */
......@@ -3098,12 +3098,26 @@ typedef enum VacuumOption
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
} VacuumOption;
/*
* Info about a single target table of VACUUM/ANALYZE.
*
* If the OID field is set, it always identifies the table to process.
* Then the relation field can be NULL; if it isn't, it's used only to report
* failure to open/lock the relation.
*/
typedef struct VacuumRelation
{
NodeTag type;
RangeVar *relation; /* table name to process, or NULL */
Oid oid; /* table's OID; InvalidOid if not looked up */
List *va_cols; /* list of column names, or NIL for all */
} VacuumRelation;
typedef struct VacuumStmt
{
NodeTag type;
int options; /* OR of VacuumOption flags */
RangeVar *relation; /* single table to process, or NULL */
List *va_cols; /* list of column names, or NIL for all */
List *rels; /* list of VacuumRelation, or NIL for all */
} VacuumStmt;
/* ----------------------
......
......@@ -80,8 +80,6 @@ CONTEXT: SQL function "do_analyze" statement 1
SQL function "wrap_do_analyze" statement 1
VACUUM FULL vactst;
VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
DROP TABLE vaccluster;
DROP TABLE vactst;
-- partitioned table
CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);
......@@ -95,4 +93,25 @@ VACUUM ANALYZE vacparted(a,b,a);
ERROR: column "a" of relation "vacparted" appears more than once
ANALYZE vacparted(a,b,b);
ERROR: column "b" of relation "vacparted" appears more than once
-- multiple tables specified
VACUUM vaccluster, vactst;
VACUUM vacparted, does_not_exist;
ERROR: relation "does_not_exist" does not exist
VACUUM (FREEZE) vacparted, vaccluster, vactst;
VACUUM (FREEZE) does_not_exist, vaccluster;
ERROR: relation "does_not_exist" does not exist
VACUUM ANALYZE vactst, vacparted (a);
VACUUM ANALYZE vactst (does_not_exist), vacparted (b);
ERROR: column "does_not_exist" of relation "vactst" does not exist
VACUUM FULL vacparted, vactst;
VACUUM FULL vactst, vacparted (a, b), vaccluster (i);
ERROR: ANALYZE option must be specified when a column list is provided
ANALYZE vactst, vacparted;
ANALYZE vacparted (b), vactst;
ANALYZE vactst, does_not_exist, vacparted;
ERROR: relation "does_not_exist" does not exist
ANALYZE vactst (i), vacparted (does_not_exist);
ERROR: column "does_not_exist" of relation "vacparted" does not exist
DROP TABLE vaccluster;
DROP TABLE vactst;
DROP TABLE vacparted;
......@@ -62,9 +62,6 @@ VACUUM FULL vactst;
VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
DROP TABLE vaccluster;
DROP TABLE vactst;
-- partitioned table
CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);
......@@ -78,4 +75,20 @@ VACUUM (FREEZE) vacparted;
VACUUM ANALYZE vacparted(a,b,a);
ANALYZE vacparted(a,b,b);
-- multiple tables specified
VACUUM vaccluster, vactst;
VACUUM vacparted, does_not_exist;
VACUUM (FREEZE) vacparted, vaccluster, vactst;
VACUUM (FREEZE) does_not_exist, vaccluster;
VACUUM ANALYZE vactst, vacparted (a);
VACUUM ANALYZE vactst (does_not_exist), vacparted (b);
VACUUM FULL vacparted, vactst;
VACUUM FULL vactst, vacparted (a, b), vaccluster (i);
ANALYZE vactst, vacparted;
ANALYZE vacparted (b), vactst;
ANALYZE vactst, does_not_exist, vacparted;
ANALYZE vactst (i), vacparted (does_not_exist);
DROP TABLE vaccluster;
DROP TABLE vactst;
DROP TABLE vacparted;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment