diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml
index 45dee10..9843658 100644
--- a/doc/src/sgml/ref/analyze.sgml
+++ b/doc/src/sgml/ref/analyze.sgml
@@ -21,7 +21,7 @@ PostgreSQL documentation
-ANALYZE [ VERBOSE ] [ table_name [ ( column_name [, ...] ) ] ]
+ANALYZE [ VERBOSE ] [ table_name [ ( column_name [, ...] ) ] ] [, ...]
@@ -38,10 +38,11 @@ ANALYZE [ VERBOSE ] [ table_name [
- With no parameter, ANALYZE examines every table in the
- current database. With a parameter, ANALYZE examines
- only that table. It is further possible to give a list of column names,
- in which case only the statistics for those columns are collected.
+ With no parameters, ANALYZE examines every table in the
+ current database. With parameters, ANALYZE examines
+ only the tables specified. It is further possible to give a list of
+ column names for each table, in which case only the statistics for those
+ columns are collected.
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index 421c18d..84bd7eb 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -21,9 +21,9 @@ PostgreSQL documentation
-VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ table_name [ (column_name [, ...] ) ] ]
-VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ table_name ]
-VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table_name [ (column_name [, ...] ) ] ]
+VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ table_name [ (column_name [, ...] ) ] ] [, ...]
+VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ table_name ] [, ...]
+VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table_name [ (column_name [, ...] ) ] ] [, ...]
@@ -40,9 +40,10 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [
- With no parameter, VACUUM processes every table in the
+ With no parameters, VACUUM processes every table in the
current database that the current user has permission to vacuum.
- With a parameter, VACUUM processes only that table.
+ With parameters, VACUUM processes only the tables
+ specified.
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 9fbb0eb..ef3106e 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -67,7 +67,7 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */
-static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
+static int get_rel_oids(List **vacrels);
static void vac_truncate_clog(TransactionId frozenXID,
MultiXactId minMulti,
TransactionId lastSaneFrozenXid,
@@ -85,13 +85,16 @@ void
ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
{
VacuumParams params;
+ ListCell *lc;
/* sanity checks on options */
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));
+ foreach(lc, vacstmt->rels)
+ Assert((((RelationAndColumns *) lfirst(lc))->options & VACOPT_ANALYZE) ||
+ ((RelationAndColumns *) lfirst(lc))->va_cols == NIL);
/*
* All freeze ages are zero if the FREEZE option is given; otherwise pass
@@ -119,8 +122,7 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
params.log_min_duration = -1;
/* Now go through the common routine */
- vacuum(vacstmt->options, vacstmt->relation, InvalidOid, ¶ms,
- vacstmt->va_cols, NULL, isTopLevel);
+ vacuum(vacstmt->options, vacstmt->rels, ¶ms, NULL, isTopLevel);
}
/*
@@ -128,15 +130,12 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
*
* options is a bitmask of VacuumOption flags, indicating what to do.
*
- * relid, if not InvalidOid, indicate the relation to process; otherwise,
- * the RangeVar is used. (The latter must always be passed, because it's
- * used for error messages.)
+ * If we intend to process all relations, the 'relations' argument may be
+ * NIL.
*
* 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.
*
@@ -146,14 +145,17 @@ 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)
{
- const char *stmttype;
- volatile bool in_outer_xact,
- use_own_xacts;
- List *relations;
- static bool in_vacuum = false;
+ const char *stmttype;
+ volatile bool in_outer_xact,
+ use_own_xacts;
+ static bool in_vacuum = false;
+ MemoryContext oldcontext;
+ List *temp_relations = NIL; /* for copying the relations list */
+ ListCell *relation;
+ int oid_count;
Assert(params != NULL);
@@ -226,10 +228,20 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
vac_strategy = bstrategy;
/*
+ * Move our relation list to our special memory context so that we do
+ * not lose it among our per-relation transactions.
+ */
+ oldcontext = MemoryContextSwitchTo(vac_context);
+ foreach(relation, relations)
+ temp_relations = lappend(temp_relations, copyObject(lfirst(relation)));
+ MemoryContextSwitchTo(oldcontext);
+ relations = temp_relations;
+
+ /*
* Build list of relations to process, unless caller gave us one. (If we
* build one, we put it in vac_context for safekeeping.)
*/
- relations = get_rel_oids(relid, relation);
+ oid_count = get_rel_oids(&relations);
/*
* Decide whether we need to start/commit our own transactions.
@@ -254,7 +266,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
use_own_xacts = true;
else if (in_outer_xact)
use_own_xacts = false;
- else if (list_length(relations) > 1)
+ else if (oid_count > 1)
use_own_xacts = true;
else
use_own_xacts = false;
@@ -283,8 +295,6 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
/* Turn vacuum cost accounting on or off */
PG_TRY();
{
- ListCell *cur;
-
in_vacuum = true;
VacuumCostActive = (VacuumCostDelay > 0);
VacuumCostBalance = 0;
@@ -295,36 +305,41 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
/*
* Loop to process each selected relation.
*/
- foreach(cur, relations)
+ foreach(relation, relations)
{
- Oid relid = lfirst_oid(cur);
+ RelationAndColumns *relinfo = (RelationAndColumns *) lfirst(relation);
+ int per_table_opts = options | relinfo->options; /* VACOPT_ANALYZE may be set per-table */
+ ListCell *oid;
- if (options & VACOPT_VACUUM)
+ foreach(oid, relinfo->oids)
{
- if (!vacuum_rel(relid, relation, options, params))
- continue;
- }
-
- if (options & VACOPT_ANALYZE)
- {
- /*
- * If using separate xacts, start one for analyze. Otherwise,
- * we can use the outer transaction.
- */
- if (use_own_xacts)
+ if (per_table_opts & VACOPT_VACUUM)
{
- StartTransactionCommand();
- /* functions in indexes may want a snapshot set */
- PushActiveSnapshot(GetTransactionSnapshot());
+ if (!vacuum_rel(lfirst_oid(oid), relinfo->relation, per_table_opts, params))
+ continue;
}
- analyze_rel(relid, relation, options, params,
- va_cols, in_outer_xact, vac_strategy);
-
- if (use_own_xacts)
+ if (per_table_opts & VACOPT_ANALYZE)
{
- PopActiveSnapshot();
- CommitTransactionCommand();
+ /*
+ * If using separate xacts, start one for analyze. Otherwise,
+ * we can use the outer transaction.
+ */
+ if (use_own_xacts)
+ {
+ StartTransactionCommand();
+ /* functions in indexes may want a snapshot set */
+ PushActiveSnapshot(GetTransactionSnapshot());
+ }
+
+ analyze_rel(lfirst_oid(oid), relinfo->relation, per_table_opts, params,
+ relinfo->va_cols, in_outer_xact, vac_strategy);
+
+ if (use_own_xacts)
+ {
+ PopActiveSnapshot();
+ CommitTransactionCommand();
+ }
}
}
}
@@ -378,62 +393,72 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
* The list is built in vac_context so that it will survive across our
* per-relation transactions.
*/
-static List *
-get_rel_oids(Oid relid, const RangeVar *vacrel)
+static int
+get_rel_oids(List **vacrels)
{
- List *oid_list = NIL;
+ RelationAndColumns *relinfo;
MemoryContext oldcontext;
+ int oid_count = 0;
- /* OID supplied by VACUUM's caller? */
- if (OidIsValid(relid))
- {
- oldcontext = MemoryContextSwitchTo(vac_context);
- oid_list = lappend_oid(oid_list, relid);
- MemoryContextSwitchTo(oldcontext);
- }
- else if (vacrel)
+ if (*vacrels != NIL)
{
- /* Process a specific relation */
- Oid relid;
- HeapTuple tuple;
- Form_pg_class classForm;
- bool include_parts;
+ /* Process specific relation(s) */
+ ListCell *lc;
- /*
- * Since we don't take a lock here, the relation might be gone, or the
- * RangeVar might no longer refer to the OID we look up here. In the
- * former case, VACUUM will do nothing; in the latter case, it will
- * process the OID we looked up here, rather than the new one. Neither
- * is ideal, but there's little practical alternative, since we're
- * going to commit this transaction and begin a new one between now
- * and then.
- */
- relid = RangeVarGetRelid(vacrel, NoLock, false);
+ foreach(lc, *vacrels)
+ {
+ Oid relid;
+ HeapTuple tuple;
+ Form_pg_class classForm;
+ bool include_parts;
- /*
- * To check whether the relation is a partitioned table, fetch its
- * syscache entry.
- */
- tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for relation %u", relid);
- classForm = (Form_pg_class) GETSTRUCT(tuple);
- include_parts = (classForm->relkind == RELKIND_PARTITIONED_TABLE);
- ReleaseSysCache(tuple);
+ relinfo = (RelationAndColumns *) lfirst(lc);
- /*
- * Make relation list entries for this guy and its partitions, if any.
- * Note that the list returned by find_all_inheritors() include the
- * passed-in OID at its head. Also note that we did not request a
- * lock to be taken to match what would be done otherwise.
- */
- 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);
+ /* OIDs supplied by VACUUM's caller? */
+ if (relinfo->oids != NIL)
+ {
+ oid_count += list_length(relinfo->oids);
+ continue;
+ }
+
+ /*
+ * Since we don't take a lock here, the relation might be gone, or the
+ * RangeVar might no longer refer to the OID we look up here. In the
+ * former case, VACUUM will do nothing; in the latter case, it will
+ * process the OID we looked up here, rather than the new one. Neither
+ * is ideal, but there's little practical alternative, since we're
+ * going to commit this transaction and begin a new one between now
+ * and then.
+ */
+ relid = RangeVarGetRelid(relinfo->relation, NoLock, false);
+
+ /*
+ * To check whether the relation is a partitioned table, fetch its
+ * syscache entry.
+ */
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
+ include_parts = (classForm->relkind == RELKIND_PARTITIONED_TABLE);
+ ReleaseSysCache(tuple);
+
+ /*
+ * Make relation list entries for this guy and its partitions, if any.
+ * Note that the list returned by find_all_inheritors() include the
+ * passed-in OID at its head. Also note that we did not request a
+ * lock to be taken to match what would be done otherwise.
+ */
+ oldcontext = MemoryContextSwitchTo(vac_context);
+ if (include_parts)
+ relinfo->oids = find_all_inheritors(relid, NoLock, NULL);
+ else
+ relinfo->oids = list_make1_oid(relid);
+
+ oid_count += list_length(relinfo->oids);
+
+ MemoryContextSwitchTo(oldcontext);
+ }
}
else
{
@@ -465,7 +490,20 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
/* Make a relation list entry for this guy */
oldcontext = MemoryContextSwitchTo(vac_context);
- oid_list = lappend_oid(oid_list, HeapTupleGetOid(tuple));
+
+ /*
+ * It is safe to leave everything except the OID empty here.
+ * Since no tables were specified in the VacuumStmt, we know
+ * we don't have any columns or extra options to keep track
+ * of. Also, we do not need the RangeVar, because it is only
+ * used for error messaging for autovacuum, and autovacuum
+ * currently always specifies one relation.
+ */
+ relinfo = makeNode(RelationAndColumns);
+ relinfo->oids = list_make1_oid(HeapTupleGetOid(tuple));
+ *vacrels = lappend(*vacrels, relinfo);
+ ++oid_count;
+
MemoryContextSwitchTo(oldcontext);
}
@@ -473,7 +511,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
heap_close(pgclass, AccessShareLock);
}
- return oid_list;
+ return oid_count;
}
/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6ad3844..0e9af95 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3761,8 +3761,20 @@ _copyVacuumStmt(const VacuumStmt *from)
VacuumStmt *newnode = makeNode(VacuumStmt);
COPY_SCALAR_FIELD(options);
+ COPY_NODE_FIELD(rels);
+
+ return newnode;
+}
+
+static RelationAndColumns *
+_copyRelationAndColumns(const RelationAndColumns *from)
+{
+ RelationAndColumns *newnode = makeNode(RelationAndColumns);
+
+ COPY_SCALAR_FIELD(options);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(va_cols);
+ COPY_NODE_FIELD(oids);
return newnode;
}
@@ -5209,6 +5221,9 @@ copyObjectImpl(const void *from)
case T_VacuumStmt:
retval = _copyVacuumStmt(from);
break;
+ case T_RelationAndColumns:
+ retval = _copyRelationAndColumns(from);
+ break;
case T_ExplainStmt:
retval = _copyExplainStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c9a8c34..755b2b2 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1663,8 +1663,18 @@ static bool
_equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
{
COMPARE_SCALAR_FIELD(options);
+ COMPARE_NODE_FIELD(rels);
+
+ return true;
+}
+
+static bool
+_equalRelationAndColumns(const RelationAndColumns *a, const RelationAndColumns *b)
+{
+ COMPARE_SCALAR_FIELD(options);
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(va_cols);
+ COMPARE_NODE_FIELD(oids);
return true;
}
@@ -3360,6 +3370,9 @@ equal(const void *a, const void *b)
case T_VacuumStmt:
retval = _equalVacuumStmt(a, b);
break;
+ case T_RelationAndColumns:
+ retval = _equalRelationAndColumns(a, b);
+ break;
case T_ExplainStmt:
retval = _equalExplainStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2822331..2a3c5af 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -366,6 +366,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type import_qualification_type
%type import_qualification
+%type relation_and_columns
+
%type stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
OptTypedTableElementList TypedTableElementList
@@ -395,7 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
relation_expr_list dostmt_opt_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
- publication_name_list
+ publication_name_list relation_and_columns_list
%type group_by_list
%type group_by_item empty_grouping_set rollup_clause cube_clause
@@ -10148,11 +10150,10 @@ 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;
+ n->rels = NIL;
+ $$ = (Node *) n;
}
- | VACUUM opt_full opt_freeze opt_verbose qualified_name
+ | VACUUM opt_full opt_freeze opt_verbose relation_and_columns_list
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM;
@@ -10162,9 +10163,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_FREEZE;
if ($4)
n->options |= VACOPT_VERBOSE;
- n->relation = $5;
- n->va_cols = NIL;
- $$ = (Node *)n;
+ n->rels = $5;
+ $$ = (Node *) n;
}
| VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt
{
@@ -10176,24 +10176,20 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_FREEZE;
if ($4)
n->options |= VACOPT_VERBOSE;
- $$ = (Node *)n;
+ $$ = (Node *) n;
}
| VACUUM '(' vacuum_option_list ')'
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | $3;
- n->relation = NULL;
- n->va_cols = NIL;
+ n->rels = NIL;
$$ = (Node *) n;
}
- | VACUUM '(' vacuum_option_list ')' qualified_name opt_name_list
+ | VACUUM '(' vacuum_option_list ')' relation_and_columns_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;
}
;
@@ -10227,19 +10223,17 @@ AnalyzeStmt:
n->options = VACOPT_ANALYZE;
if ($2)
n->options |= VACOPT_VERBOSE;
- n->relation = NULL;
- n->va_cols = NIL;
- $$ = (Node *)n;
+ n->rels = NIL;
+ $$ = (Node *) n;
}
- | analyze_keyword opt_verbose qualified_name opt_name_list
+ | analyze_keyword opt_verbose relation_and_columns_list
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_ANALYZE;
if ($2)
n->options |= VACOPT_VERBOSE;
- n->relation = $3;
- n->va_cols = $4;
- $$ = (Node *)n;
+ n->rels = $3;
+ $$ = (Node *) n;
}
;
@@ -10266,6 +10260,24 @@ opt_name_list:
| /*EMPTY*/ { $$ = NIL; }
;
+relation_and_columns:
+ qualified_name opt_name_list
+ {
+ RelationAndColumns *n = makeNode(RelationAndColumns);
+ n->options = 0;
+ n->relation = $1;
+ n->va_cols = $2;
+ if (n->va_cols != NIL) /* implies analyze */
+ n->options |= VACOPT_ANALYZE;
+ $$ = (Node *) n;
+ }
+ ;
+
+relation_and_columns_list:
+ relation_and_columns { $$ = list_make1($1); }
+ | relation_and_columns_list ',' relation_and_columns { $$ = lappend($1, $3); }
+ ;
+
/*****************************************************************************
*
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 89dd3b3..13475bb 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -3114,6 +3114,7 @@ static void
autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
{
RangeVar rangevar;
+ RelationAndColumns *rel;
/* Set up command parameters --- use local variables instead of palloc */
MemSet(&rangevar, 0, sizeof(rangevar));
@@ -3125,8 +3126,12 @@ autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
/* 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);
+ rel = makeNode(RelationAndColumns);
+ rel->relation = &rangevar;
+ if (OidIsValid(tab->at_relid))
+ rel->oids = list_make1_oid(tab->at_relid);
+
+ vacuum(tab->at_vacoptions, list_make1(rel), &tab->at_params, bstrategy, true);
}
/*
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 541c2fa..6c77fe9 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -158,8 +158,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);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f59d719..f66f18c 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -468,6 +468,7 @@ typedef enum NodeTag
T_PartitionSpec,
T_PartitionBoundSpec,
T_PartitionRangeDatum,
+ T_RelationAndColumns,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d396be3..b2de803 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3062,12 +3062,24 @@ typedef enum VacuumOption
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
} VacuumOption;
+/*
+ * This is used to keep track of a relation and an optional list of
+ * column names, as may be specified in VACUUM and ANALYZE.
+ */
+typedef struct RelationAndColumns
+{
+ NodeTag type;
+ int options; /* OR of extra VacuumOption flags to apply */
+ RangeVar *relation; /* single table to process */
+ List *va_cols; /* list of column names, or NIL for all */
+ List *oids; /* corresponding OIDs (filled in by vacuum.c) */
+} RelationAndColumns;
+
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 */
+ NodeTag type;
+ int options; /* OR of VacuumOption flags */
+ List *rels; /* list of tables/columns to process, or NIL for all */
} VacuumStmt;
/* ----------------------
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index 6f68663..922a3a4 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -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);
@@ -90,4 +88,20 @@ UPDATE vacparted SET b = 'b';
VACUUM (ANALYZE) vacparted;
VACUUM (FULL) vacparted;
VACUUM (FREEZE) vacparted;
+-- multiple tables specified
+VACUUM FREEZE vaccluster, vactst;
+VACUUM FREEZE vacparted, does_not_exist;
+ERROR: relation "does_not_exist" does not exist
+VACUUM (FREEZE) vacparted, vaccluster, vactst;
+VACUUM (FREEZE) vaccluster, does_not_exist;
+ERROR: relation "does_not_exist" does not exist
+VACUUM (FREEZE) vactst (i);
+VACUUM (FREEZE) vactst, vacparted (a);
+VACUUM (FREEZE) vaccluster, vactst (i), vacparted;
+VACUUM (FREEZE) vactst (i), vacparted (a, b), vaccluster (i);
+VACUUM;
+ANALYZE vactst, vacparted;
+ANALYZE vacparted (b), vactst;
+DROP TABLE vaccluster;
+DROP TABLE vactst;
DROP TABLE vacparted;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 7c5fb04..dcd6456 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -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);
@@ -73,4 +70,20 @@ UPDATE vacparted SET b = 'b';
VACUUM (ANALYZE) vacparted;
VACUUM (FULL) vacparted;
VACUUM (FREEZE) vacparted;
+
+-- multiple tables specified
+VACUUM FREEZE vaccluster, vactst;
+VACUUM FREEZE vacparted, does_not_exist;
+VACUUM (FREEZE) vacparted, vaccluster, vactst;
+VACUUM (FREEZE) vaccluster, does_not_exist;
+VACUUM (FREEZE) vactst (i);
+VACUUM (FREEZE) vactst, vacparted (a);
+VACUUM (FREEZE) vaccluster, vactst (i), vacparted;
+VACUUM (FREEZE) vactst (i), vacparted (a, b), vaccluster (i);
+VACUUM;
+ANALYZE vactst, vacparted;
+ANALYZE vacparted (b), vactst;
+
+DROP TABLE vaccluster;
+DROP TABLE vactst;
DROP TABLE vacparted;