diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a9fe412351..fbc442f1a3 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -71,6 +71,7 @@ static BufferAccessStrategy vac_strategy; /* non-export function prototypes */ static void get_rel_oids(List **vacrels); static void check_columns_exist(List *relations); +static void dedupe_relations(List **relations); static void vac_truncate_clog(TransactionId frozenXID, MultiXactId minMulti, TransactionId lastSaneFrozenXid, @@ -269,6 +270,17 @@ vacuum(int options, List *relations, VacuumParams *params, check_columns_exist(relations); /* + * Now dedupe the list to avoid any redundant work (e.g. user specifies + * the same relation twice). We also take care of combining any + * separate column lists for duplicate relations. + * + * We do this after resolving the OIDs so that we do not miss entries + * that have unequal RangeVars but resolve to the same OIDs. For + * example, "foo" and "public.foo" could be the same relation. + */ + dedupe_relations(&relations); + + /* * Decide whether we need to start/commit our own transactions. * * For VACUUM (with or without ANALYZE): always do so, so that we can @@ -609,6 +621,81 @@ check_columns_exist(List *relations) } /* + * De-duplicate all relations and columns specified. The OIDs for each relation + * must already be determined before calling this function. + */ +static void +dedupe_relations(List **relations) +{ + int i; + int next_elem_index = 0; + List *duplicates = NIL; /* indexes of entries to remove */ + ListCell *lc; + List *temp_relations = NIL; + + if (*relations == NIL) /* nothing to do */ + return; + + /* mark any duplicates and combine the column lists into the first match */ + foreach(lc, *relations) + { + VacuumRelation *relation = lfirst_node(VacuumRelation, lc); + + ++next_elem_index; + + /* + * If this one has already been marked as a duplicate, it has + * already been processed, and there's nothing left to do. + */ + if (list_member_int(duplicates, next_elem_index - 1)) + continue; + + /* remove any duplicates in the column list */ + relation->va_cols = list_concat_unique(NIL, relation->va_cols); + + /* now look for any duplicates in the remainder of the list */ + for (i = next_elem_index; i < list_length(*relations); ++i) + { + VacuumRelation *nth_rel = list_nth_node(VacuumRelation, *relations, i); + + /* if already processed or not equal, skip */ + if (list_member_int(duplicates, i) || relation->oid != nth_rel->oid) + continue; + + /* + * For ANALYZE, if a specified relation has an empty + * column list, it means that all columns should be + * analyzed. Therefore, any time a relation is + * specified with an empty column list, it overrides any + * other column lists specified for that relation. + */ + if (relation->va_cols == NIL || nth_rel->va_cols == NIL) + relation->va_cols = NIL; + else /* combine the column lists */ + relation->va_cols = list_concat_unique(relation->va_cols, nth_rel->va_cols); + + duplicates = lappend_int(duplicates, i); + } + } + + /* nothing else needed if we did not find any duplicates */ + if (duplicates == NIL) + return; + + for (i = 0; i < list_length(*relations); ++i) + { + /* if we are a duplicate entry, skip */ + if (list_member_int(duplicates, i)) + continue; + + /* add non-duplicate entry to the final list */ + temp_relations = lappend(temp_relations, list_nth_node(VacuumRelation, *relations, i)); + } + + *relations = temp_relations; +} + +/* * vacuum_set_xid_limits() -- compute oldest-Xmin and freeze cutoff points * * The output parameters are: diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index d29dd8f3a3..113f59cfa9 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -120,6 +120,7 @@ ANALYZE VERBOSE vactst (i), vacparted (does_not_exist); ERROR: column "does_not_exist" of relation "vacparted" does not exist ANALYZE vactst, vactst, vactst; ANALYZE vacparted (a), vacparted (b), vacparted; +ANALYZE vactst (i, i), vacparted (a), vactst (i), vacparted (a, b, a); 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 991bea7752..1134a58be5 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -93,6 +93,7 @@ ANALYZE vacparted (b), vactst; ANALYZE VERBOSE vactst (i), vacparted (does_not_exist); ANALYZE vactst, vactst, vactst; ANALYZE vacparted (a), vacparted (b), vacparted; +ANALYZE vactst (i, i), vacparted (a), vactst (i), vacparted (a, b, a); DROP TABLE vaccluster; DROP TABLE vactst;