dedupe_vacuum_relations_v3.patch

application/octet-stream

Filename: dedupe_vacuum_relations_v3.patch
Type: application/octet-stream
Part: 0
Message: Re: [Proposal] Allow users to specify multiple tables in VACUUM commands

Patch

Format: unified
Series: patch v3
File+
src/backend/commands/vacuum.c 87 0
src/test/regress/expected/vacuum.out 1 0
src/test/regress/sql/vacuum.sql 1 0
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;