From 6f29356c60a6f44c4f2eaa56cf1e32b9c4f25524 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Thu, 5 Apr 2018 19:00:40 +0000
Subject: [PATCH v7 08/12] Skip VACUUM/ANALYZE with VACOPT_SKIP_LOCKED if
 indexes can't be opened.

---
 src/backend/commands/analyze.c    | 29 ++++++++++++++++++++++++-----
 src/backend/commands/vacuum.c     | 23 +++++++++++++++++++----
 src/backend/commands/vacuumlazy.c | 23 +++++++++++++++++++++--
 src/include/commands/vacuum.h     |  2 +-
 4 files changed, 65 insertions(+), 12 deletions(-)

diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 03e0bad1f3..0421d77ad7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -84,7 +84,7 @@ static BufferAccessStrategy vac_strategy;
 static void do_analyze_rel(Relation onerel, int options,
 			   VacuumParams *params, List *va_cols,
 			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
-			   bool inh, bool in_outer_xact, int elevel);
+			   bool inh, bool in_outer_xact, int elevel, RangeVar *rangeVar);
 static void compute_index_stats(Relation onerel, double totalrows,
 					AnlIndexData *indexdata, int nindexes,
 					HeapTuple *rows, int numrows,
@@ -294,14 +294,14 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	 */
 	if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc,
-					   relpages, false, in_outer_xact, elevel);
+					   relpages, false, in_outer_xact, elevel, relation);
 
 	/*
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
-					   true, in_outer_xact, elevel);
+					   true, in_outer_xact, elevel, relation);
 
 	/*
 	 * Close source relation now, but keep lock so that no one deletes it
@@ -331,7 +331,7 @@ static void
 do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 			   List *va_cols, AcquireSampleRowsFunc acquirefunc,
 			   BlockNumber relpages, bool inh, bool in_outer_xact,
-			   int elevel)
+			   int elevel, RangeVar *rangeVar)
 {
 	int			attr_cnt,
 				tcnt,
@@ -454,7 +454,26 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * all.
 	 */
 	if (!inh)
-		vac_open_indexes(onerel, AccessShareLock, &nindexes, &Irel, false, NULL);
+	{
+		bool		skipped = false;
+
+		vac_open_indexes(onerel, AccessShareLock, &nindexes, &Irel,
+						 (options & VACOPT_SKIP_LOCKED), &skipped);
+
+		/*
+		 * If SKIP LOCKED is specified and we would have to wait for a lock on
+		 * an index, skip analyzing the relation.
+		 */
+		if (skipped)
+		{
+			do_analyze_rel_cleanup(save_nestlevel, save_userid,
+								   save_sec_context, caller_context,
+								   rangeVar, params);
+
+			return;
+		}
+
+	}
 	else
 	{
 		Irel = NULL;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index d7967dda6b..fb7dd8d536 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1314,6 +1314,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	int			save_sec_context;
 	int			save_nestlevel;
 	bool		rel_lock = true;
+	bool		skipped = false;
 
 	Assert(params != NULL);
 
@@ -1539,7 +1540,18 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 					(options & VACOPT_VERBOSE) != 0);
 	}
 	else
-		lazy_vacuum_rel(onerel, options, params, vac_strategy);
+		skipped = !lazy_vacuum_rel(onerel, options, params, vac_strategy);
+
+	if (skipped)
+	{
+		int			elevel = get_elevel_for_skipped_relation(relation, params);
+
+		if (elevel != 0)
+			ereport(elevel,
+					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+					 errmsg("skipping vacuum of \"%s\" --- lock not available",
+							relation->relname)));
+	}
 
 	/* Roll back any GUC changes executed by index functions */
 	AtEOXact_GUC(false, save_nestlevel);
@@ -1563,8 +1575,11 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	 * "analyze" will not get done on the toast table.  This is good, because
 	 * the toaster always uses hardcoded index access and statistics are
 	 * totally unimportant for toast relations.
+	 *
+	 * If we skipped vacuuming the main relation, skip vacuuming the secondary
+	 * toast rel as well.
 	 */
-	if (toast_relid != InvalidOid)
+	if (!skipped && toast_relid != InvalidOid)
 		vacuum_rel(toast_relid, NULL, options, params);
 
 	/*
@@ -1572,8 +1587,8 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	 */
 	UnlockRelationIdForSession(&onerelid, lmode);
 
-	/* Report that we really did it. */
-	return true;
+	/* Report if we really did it. */
+	return !skipped;
 }
 
 
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 13e5bd452f..3a584b5115 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -186,8 +186,12 @@ static bool heap_page_is_all_visible(Relation rel, Buffer buf,
  *
  *		At entry, we have already established a transaction and opened
  *		and locked the relation.
+ *
+ *		If SKIP LOCKED is specified in the options and lazy_vacuum_rel()
+ *		skips the relation due to a conflicting lock, false is returned.
+ *		Else, true is returned.
  */
-void
+bool
 lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 				BufferAccessStrategy bstrategy)
 {
@@ -209,6 +213,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	double		new_live_tuples;
 	TransactionId new_frozen_xid;
 	MultiXactId new_min_multi;
+	bool		skipped = false;
 
 	Assert(params != NULL);
 
@@ -259,7 +264,19 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	vacrelstats->lock_waiter_detected = false;
 
 	/* Open all indexes of the relation */
-	vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel, false, NULL);
+	vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel,
+					 (options & VACOPT_SKIP_LOCKED), &skipped);
+
+	/*
+	 * If SKIP LOCKED is specified and we would have to wait for a lock on an
+	 * index, skip vacuuming the relation.
+	 */
+	if (skipped)
+	{
+		pgstat_progress_end_command();
+		return false;
+	}
+
 	vacrelstats->hasindex = (nindexes > 0);
 
 	/* Do the vacuuming */
@@ -409,6 +426,8 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 			pfree(buf.data);
 		}
 	}
+
+	return true;
 }
 
 /*
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 64215ea60a..23fd8392c5 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -189,7 +189,7 @@ extern void vacuum_delay_point(void);
 extern int	get_elevel_for_skipped_relation(RangeVar *relation, VacuumParams *params);
 
 /* in commands/vacuumlazy.c */
-extern void lazy_vacuum_rel(Relation onerel, int options,
+extern bool lazy_vacuum_rel(Relation onerel, int options,
 				VacuumParams *params, BufferAccessStrategy bstrategy);
 
 /* in commands/analyze.c */
-- 
2.16.2

