diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index 45dee10..7b3ed95 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 [, ...] ) ] [, ...] ] @@ -39,9 +39,10 @@ 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. + 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. @@ -62,10 +63,10 @@ ANALYZE [ VERBOSE ] [ table_name [ table_name - The name (possibly schema-qualified) of a specific table to + The name (possibly schema-qualified) of the specific tables to analyze. If omitted, all regular tables, partitioned tables, and materialized views in the current database are analyzed (but not - foreign tables). If the specified table is a partitioned table, both the + foreign tables). If a specified table is a partitioned table, both the inheritance statistics of the partitioned table as a whole and statistics of the individual partitions are updated. diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index 421c18d..a7a7916 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. @@ -152,9 +153,9 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table_name - The name (optionally schema-qualified) of a specific table to + The names (optionally schema-qualified) of the specific tables to vacuum. If omitted, all regular tables and materialized views in the - current database are vacuumed. If the specified table is a partitioned + current database are vacuumed. If a specified table is a partitioned table, all of its leaf partitions are vacuumed. diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 9fbb0eb..2b1af16 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -37,6 +37,7 @@ #include "commands/cluster.h" #include "commands/vacuum.h" #include "miscadmin.h" +#include "nodes/execnodes.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "storage/bufmgr.h" @@ -67,7 +68,7 @@ static BufferAccessStrategy vac_strategy; /* non-export function prototypes */ -static List *get_rel_oids(Oid relid, const RangeVar *vacrel); +static List *get_vac_info(Oid relid, const List *vacrels); static void vac_truncate_clog(TransactionId frozenXID, MultiXactId minMulti, TransactionId lastSaneFrozenXid, @@ -85,13 +86,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 +123,8 @@ 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, InvalidOid, ¶ms, + NULL, isTopLevel); } /* @@ -128,15 +132,15 @@ 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.) + * relid, if not InvalidOid, indicates the relation to process; + * otherwise, the rels list is used. If we intend to process all + * relations, rels may be NIL, else it must be passed, because it's + * used for error messages. In the case that relid is valid, rels + * must have exactly one element corresponding to the specified relid. * * 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,16 +150,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 *rels, Oid relid, VacuumParams *params, + BufferAccessStrategy bstrategy, bool isTopLevel) { const char *stmttype; volatile bool in_outer_xact, use_own_xacts; - List *relations; + List *vacinfos; static bool in_vacuum = false; Assert(params != NULL); + Assert(!OidIsValid(relid) || list_length(rels) == 1); stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE"; @@ -229,7 +234,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params, * 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); + vacinfos = get_vac_info(relid, rels); /* * Decide whether we need to start/commit our own transactions. @@ -254,7 +259,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 (list_length(vacinfos) > 1) use_own_xacts = true; else use_own_xacts = false; @@ -283,7 +288,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params, /* Turn vacuum cost accounting on or off */ PG_TRY(); { - ListCell *cur; + ListCell *lc; in_vacuum = true; VacuumCostActive = (VacuumCostDelay > 0); @@ -295,17 +300,19 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params, /* * Loop to process each selected relation. */ - foreach(cur, relations) + foreach(lc, vacinfos) { - Oid relid = lfirst_oid(cur); + VacInfo *vacinfo = (VacInfo *) lfirst(lc); + Oid relid = vacinfo->oid; + int per_table_opts = options | vacinfo->opt; /* VACOPT_ANALYZE may be set per-table */ - if (options & VACOPT_VACUUM) + if (per_table_opts & VACOPT_VACUUM) { - if (!vacuum_rel(relid, relation, options, params)) + if (!vacuum_rel(relid, vacinfo->rel, per_table_opts, params)) continue; } - if (options & VACOPT_ANALYZE) + if (per_table_opts & VACOPT_ANALYZE) { /* * If using separate xacts, start one for analyze. Otherwise, @@ -318,8 +325,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(relid, vacinfo->rel, per_table_opts, params, + vacinfo->cols, in_outer_xact, vac_strategy); if (use_own_xacts) { @@ -373,67 +380,103 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params, } /* - * Build a list of Oids for each relation to be processed + * Build a list of VacInfo for each relation to be processed * * 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) +get_vac_info(Oid relid, const List *vacrels) { - List *oid_list = NIL; + List *vacinfos = NIL; + VacInfo *vacinfo = NULL; + RelationAndColumns *relinfo = NULL; MemoryContext oldcontext; + /* + * As explained in vacuum(...), if relid is valid, vacrels must have + * exactly one element. + */ + Assert(!OidIsValid(relid) || list_length(vacrels) == 1); + /* OID supplied by VACUUM's caller? */ if (OidIsValid(relid)) { oldcontext = MemoryContextSwitchTo(vac_context); - oid_list = lappend_oid(oid_list, relid); + + relinfo = (RelationAndColumns *) linitial(vacrels); + vacinfo = makeNode(VacInfo); + vacinfo->oid = relid; + vacinfo->rel = relinfo->relation; + vacinfo->cols = relinfo->va_cols; + vacinfo->opt = relinfo->options; + vacinfos = lappend(vacinfos, vacinfo); + MemoryContextSwitchTo(oldcontext); } - else if (vacrel) + else 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; + List *vacoids = NIL; + ListCell *vacoid; - /* - * 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); + /* + * 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) + vacoids = list_concat(vacoids, find_all_inheritors(relid, NoLock, NULL)); + else + vacoids = lappend_oid(vacoids, relid); + + /* carry over any columns and options */ + foreach(vacoid, vacoids) + { + vacinfo = makeNode(VacInfo); + vacinfo->oid = lfirst_oid(vacoid); + vacinfo->rel = relinfo->relation; + vacinfo->cols = relinfo->va_cols; + vacinfo->opt = relinfo->options; + vacinfos = lappend(vacinfos, vacinfo); + } + + MemoryContextSwitchTo(oldcontext); + } } else { @@ -465,7 +508,19 @@ 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. + */ + vacinfo = makeNode(VacInfo); + vacinfo->oid = HeapTupleGetOid(tuple); + vacinfos = lappend(vacinfos, vacinfo); + MemoryContextSwitchTo(oldcontext); } @@ -473,7 +528,7 @@ get_rel_oids(Oid relid, const RangeVar *vacrel) heap_close(pgclass, AccessShareLock); } - return oid_list; + return vacinfos; } /* @@ -1347,7 +1402,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params) /* * Check that it's a vacuumable relation; we used to do this in - * get_rel_oids() but seems safer to check after we've locked the + * get_vac_info() but seems safer to check after we've locked the * relation. */ if (onerel->rd_rel->relkind != RELKIND_RELATION && diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 2d2a9d0..5c7e406 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3763,12 +3763,36 @@ _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); return newnode; } +static VacInfo * +_copyVacInfo(const VacInfo *from) +{ + VacInfo *newnode = makeNode(VacInfo); + + COPY_NODE_FIELD(rel); + COPY_SCALAR_FIELD(oid); + COPY_NODE_FIELD(cols); + COPY_SCALAR_FIELD(opt); + + return newnode; +} + static ExplainStmt * _copyExplainStmt(const ExplainStmt *from) { @@ -5211,6 +5235,12 @@ copyObjectImpl(const void *from) case T_VacuumStmt: retval = _copyVacuumStmt(from); break; + case T_RelationAndColumns: + retval = _copyRelationAndColumns(from); + break; + case T_VacInfo: + retval = _copyVacInfo(from); + break; case T_ExplainStmt: retval = _copyExplainStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b5459cd..85933f3 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1663,6 +1663,15 @@ 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); @@ -1670,6 +1679,17 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b) } static bool +_equalVacInfo(const VacInfo *a, const VacInfo *b) +{ + COMPARE_NODE_FIELD(rel); + COMPARE_SCALAR_FIELD(oid); + COMPARE_NODE_FIELD(cols); + COMPARE_SCALAR_FIELD(opt); + + return true; +} + +static bool _equalExplainStmt(const ExplainStmt *a, const ExplainStmt *b) { COMPARE_NODE_FIELD(query); @@ -3360,6 +3380,12 @@ 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_VacInfo: + retval = _equalVacInfo(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 65c004c..ba70701 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -367,6 +367,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 @@ -396,7 +398,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 @@ -10155,11 +10157,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; @@ -10169,9 +10170,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 { @@ -10183,24 +10183,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; } ; @@ -10234,19 +10230,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; } ; @@ -10273,6 +10267,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..bddbae2 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,7 +3126,10 @@ 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, + rel = makeNode(RelationAndColumns); + rel->relation = &rangevar; + + vacuum(tab->at_vacoptions, list_make1(rel), tab->at_relid, &tab->at_params, bstrategy, true); } diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 541c2fa..a8d3b9e 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -158,9 +158,9 @@ 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, - BufferAccessStrategy bstrategy, bool isTopLevel); +extern void vacuum(int options, List *rels, Oid relid, + VacuumParams *params, BufferAccessStrategy bstrategy, + bool isTopLevel); extern void vac_open_indexes(Relation relation, LOCKMODE lockmode, int *nindexes, Relation **Irel); extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index f289f3c..5792279 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2008,4 +2008,14 @@ typedef struct LimitState TupleTableSlot *subSlot; /* tuple last obtained from subplan */ } LimitState; +/* convenience struct for various things we need for analyze_rel(...) and vacuum_rel(...) */ +typedef struct VacInfo +{ + NodeTag type; + RangeVar *rel; /* relation we intend to process */ + Oid oid; /* OID of the relation */ + List *cols; /* columns to analyze */ + int opt; /* per-table options (currently only VACOPT_ANALYZE is possible) */ +} VacInfo; + #endif /* EXECNODES_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f59d719..99dfa65 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -37,6 +37,7 @@ typedef enum NodeTag T_ResultRelInfo, T_EState, T_TupleTableSlot, + T_VacInfo, /* * TAGS FOR PLAN NODES (plannodes.h) @@ -468,6 +469,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 46c23c2..35d3429 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3062,12 +3062,23 @@ 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 */ +} 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..51dce66 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,19 @@ 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); +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..e0b812c 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,19 @@ 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); +ANALYZE vactst, vacparted; +ANALYZE vacparted (b), vactst; + +DROP TABLE vaccluster; +DROP TABLE vactst; DROP TABLE vacparted;