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
This commit is contained in:
Tom Lane 2017-10-03 18:53:44 -04:00
parent 45f9d08684
commit 11d8d72c27
14 changed files with 329 additions and 159 deletions

View File

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

View File

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

View File

@ -37,6 +37,7 @@
#include "commands/cluster.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/bufmgr.h"
@ -67,7 +68,8 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */
static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
static List *expand_vacuum_rel(VacuumRelation *vrel);
static List *get_all_vacuum_rels(void);
static void vac_truncate_clog(TransactionId frozenXID,
MultiXactId minMulti,
TransactionId lastSaneFrozenXid,
@ -90,9 +92,26 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
Assert((vacstmt->options & VACOPT_VACUUM) ||
!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
Assert((vacstmt->options & VACOPT_ANALYZE) || vacstmt->va_cols == NIL);
Assert(!(vacstmt->options & VACOPT_SKIPTOAST));
/*
* Make sure VACOPT_ANALYZE is specified if any column lists are present.
*/
if (!(vacstmt->options & VACOPT_ANALYZE))
{
ListCell *lc;
foreach(lc, vacstmt->rels)
{
VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
if (vrel->va_cols != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ANALYZE option must be specified when a column list is provided")));
}
}
/*
* All freeze ages are zero if the FREEZE option is given; otherwise pass
* them as -1 which means to use the default values.
@ -119,26 +138,22 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
params.log_min_duration = -1;
/* Now go through the common routine */
vacuum(vacstmt->options, vacstmt->relation, InvalidOid, &params,
vacstmt->va_cols, NULL, isTopLevel);
vacuum(vacstmt->options, vacstmt->rels, &params, NULL, isTopLevel);
}
/*
* Primary entry point for VACUUM and ANALYZE commands.
* Internal entry point for VACUUM and ANALYZE commands.
*
* options is a bitmask of VacuumOption flags, indicating what to do.
*
* relid, if not InvalidOid, indicates the relation to process; otherwise,
* if a RangeVar is supplied, that's what to process; otherwise, we process
* all relevant tables in the database. (If both relid and a RangeVar are
* supplied, the relid is what is processed, but we use the RangeVar's name
* to report any open/lock failure.)
* relations, if not NIL, is a list of VacuumRelation to process; otherwise,
* we process all relevant tables in the database. For each VacuumRelation,
* if a valid OID is supplied, the table with that OID is what to process;
* otherwise, the VacuumRelation's RangeVar indicates what to process.
*
* params contains a set of parameters that can be used to customize the
* behavior.
*
* va_cols is a list of columns to analyze, or NIL to process them all.
*
* bstrategy is normally given as NULL, but in autovacuum it can be passed
* in to use the same buffer strategy object across multiple vacuum() calls.
*
@ -148,14 +163,14 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
* memory context that will not disappear at transaction commit.
*/
void
vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
List *va_cols, BufferAccessStrategy bstrategy, bool isTopLevel)
vacuum(int options, List *relations, VacuumParams *params,
BufferAccessStrategy bstrategy, bool isTopLevel)
{
static bool in_vacuum = false;
const char *stmttype;
volatile bool in_outer_xact,
use_own_xacts;
List *relations;
static bool in_vacuum = false;
Assert(params != NULL);
@ -228,10 +243,29 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
vac_strategy = bstrategy;
/*
* Build list of relation OID(s) to process, putting it in vac_context for
* safekeeping.
* Build list of relation(s) to process, putting any new data in
* vac_context for safekeeping.
*/
relations = get_rel_oids(relid, relation);
if (relations != NIL)
{
List *newrels = NIL;
ListCell *lc;
foreach(lc, relations)
{
VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
List *sublist;
MemoryContext old_context;
sublist = expand_vacuum_rel(vrel);
old_context = MemoryContextSwitchTo(vac_context);
newrels = list_concat(newrels, sublist);
MemoryContextSwitchTo(old_context);
}
relations = newrels;
}
else
relations = get_all_vacuum_rels();
/*
* Decide whether we need to start/commit our own transactions.
@ -282,7 +316,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
CommitTransactionCommand();
}
/* Turn vacuum cost accounting on or off */
/* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
PG_TRY();
{
ListCell *cur;
@ -299,11 +333,11 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
*/
foreach(cur, relations)
{
Oid relid = lfirst_oid(cur);
VacuumRelation *vrel = lfirst_node(VacuumRelation, cur);
if (options & VACOPT_VACUUM)
{
if (!vacuum_rel(relid, relation, options, params))
if (!vacuum_rel(vrel->oid, vrel->relation, options, params))
continue;
}
@ -320,8 +354,8 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
PushActiveSnapshot(GetTransactionSnapshot());
}
analyze_rel(relid, relation, options, params,
va_cols, in_outer_xact, vac_strategy);
analyze_rel(vrel->oid, vrel->relation, options, params,
vrel->va_cols, in_outer_xact, vac_strategy);
if (use_own_xacts)
{
@ -375,25 +409,33 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
}
/*
* Build a list of Oids for each relation to be processed
* Given a VacuumRelation, fill in the table OID if it wasn't specified,
* and optionally add VacuumRelations for partitions of the table.
*
* The list is built in vac_context so that it will survive across our
* per-relation transactions.
* If a VacuumRelation does not have an OID supplied and is a partitioned
* table, an extra entry will be added to the output for each partition.
* Presently, only autovacuum supplies OIDs when calling vacuum(), and
* it does not want us to expand partitioned tables.
*
* We take care not to modify the input data structure, but instead build
* new VacuumRelation(s) to return. (But note that they will reference
* unmodified parts of the input, eg column lists.) New data structures
* are made in vac_context.
*/
static List *
get_rel_oids(Oid relid, const RangeVar *vacrel)
expand_vacuum_rel(VacuumRelation *vrel)
{
List *oid_list = NIL;
List *vacrels = NIL;
MemoryContext oldcontext;
/* OID supplied by VACUUM's caller? */
if (OidIsValid(relid))
/* If caller supplied OID, there's nothing we need do here. */
if (OidIsValid(vrel->oid))
{
oldcontext = MemoryContextSwitchTo(vac_context);
oid_list = lappend_oid(oid_list, relid);
vacrels = lappend(vacrels, vrel);
MemoryContextSwitchTo(oldcontext);
}
else if (vacrel)
else
{
/* Process a specific relation, and possibly partitions thereof */
Oid relid;
@ -406,7 +448,16 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
* below, as well as find_all_inheritors's expectation that the caller
* holds some lock on the starting relation.
*/
relid = RangeVarGetRelid(vacrel, AccessShareLock, false);
relid = RangeVarGetRelid(vrel->relation, AccessShareLock, false);
/*
* Make a returnable VacuumRelation for this rel.
*/
oldcontext = MemoryContextSwitchTo(vac_context);
vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
relid,
vrel->va_cols));
MemoryContextSwitchTo(oldcontext);
/*
* To check whether the relation is a partitioned table, fetch its
@ -420,19 +471,36 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
ReleaseSysCache(tuple);
/*
* Make relation list entries for this rel and its partitions, if any.
* Note that the list returned by find_all_inheritors() includes the
* passed-in OID at its head. There's no point in taking locks on the
* individual partitions yet, and doing so would just add unnecessary
* deadlock risk.
* If it is, make relation list entries for its partitions. Note that
* the list returned by find_all_inheritors() includes the passed-in
* OID, so we have to skip that. There's no point in taking locks on
* the individual partitions yet, and doing so would just add
* unnecessary deadlock risk.
*/
oldcontext = MemoryContextSwitchTo(vac_context);
if (include_parts)
oid_list = list_concat(oid_list,
find_all_inheritors(relid, NoLock, NULL));
else
oid_list = lappend_oid(oid_list, relid);
MemoryContextSwitchTo(oldcontext);
{
List *part_oids = find_all_inheritors(relid, NoLock, NULL);
ListCell *part_lc;
foreach(part_lc, part_oids)
{
Oid part_oid = lfirst_oid(part_lc);
if (part_oid == relid)
continue; /* ignore original table */
/*
* We omit a RangeVar since it wouldn't be appropriate to
* complain about failure to open one of these relations
* later.
*/
oldcontext = MemoryContextSwitchTo(vac_context);
vacrels = lappend(vacrels, makeVacuumRelation(NULL,
part_oid,
vrel->va_cols));
MemoryContextSwitchTo(oldcontext);
}
}
/*
* Release lock again. This means that by the time we actually try to
@ -447,45 +515,57 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
*/
UnlockRelationOid(relid, AccessShareLock);
}
else
return vacrels;
}
/*
* Construct a list of VacuumRelations for all vacuumable rels in
* the current database. The list is built in vac_context.
*/
static List *
get_all_vacuum_rels(void)
{
List *vacrels = NIL;
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
pgclass = heap_open(RelationRelationId, AccessShareLock);
scan = heap_beginscan_catalog(pgclass, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
MemoryContext oldcontext;
/*
* Process all plain relations and materialized views listed in
* pg_class
* We include partitioned tables here; depending on which operation is
* to be performed, caller will decide whether to process or ignore
* them.
*/
Relation pgclass;
HeapScanDesc scan;
HeapTuple tuple;
if (classForm->relkind != RELKIND_RELATION &&
classForm->relkind != RELKIND_MATVIEW &&
classForm->relkind != RELKIND_PARTITIONED_TABLE)
continue;
pgclass = heap_open(RelationRelationId, AccessShareLock);
scan = heap_beginscan_catalog(pgclass, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
/*
* We include partitioned tables here; depending on which
* operation is to be performed, caller will decide whether to
* process or ignore them.
*/
if (classForm->relkind != RELKIND_RELATION &&
classForm->relkind != RELKIND_MATVIEW &&
classForm->relkind != RELKIND_PARTITIONED_TABLE)
continue;
/* Make a relation list entry for this rel */
oldcontext = MemoryContextSwitchTo(vac_context);
oid_list = lappend_oid(oid_list, HeapTupleGetOid(tuple));
MemoryContextSwitchTo(oldcontext);
}
heap_endscan(scan);
heap_close(pgclass, AccessShareLock);
/*
* Build VacuumRelation(s) specifying the table OIDs to be processed.
* We omit a RangeVar since it wouldn't be appropriate to complain
* about failure to open one of these relations later.
*/
oldcontext = MemoryContextSwitchTo(vac_context);
vacrels = lappend(vacrels, makeVacuumRelation(NULL,
HeapTupleGetOid(tuple),
NIL));
MemoryContextSwitchTo(oldcontext);
}
return oid_list;
heap_endscan(scan);
heap_close(pgclass, AccessShareLock);
return vacrels;
}
/*

View File

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

View File

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

View File

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

View File

@ -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 ')'
| VACUUM '(' vacuum_option_list ')' opt_vacuum_relation_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
{
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; }
;
/*****************************************************************************
*

View File

@ -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);
}
/*

View File

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

View File

@ -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 */

View File

@ -468,6 +468,7 @@ typedef enum NodeTag
T_PartitionBoundSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
T_VacuumRelation,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)

View File

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

View File

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

View File

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