From 1f38a54097328bb80a279d63474fdf31bf52fabe Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Sat, 3 Mar 2018 16:09:18 -0800
Subject: [PATCH v5 2/2] Add NOWAIT option to VACUUM and ANALYZE.

FIXME: blocking doesn't work for AEL
FIXME: NOWAIT isn't actually implemented, closer to SKIP LOCKED

Author: Nathan Bossart
Reviewed-By: Michael Paquier, Andres Freund, Masahiko Sawada
Discussion: https://postgr.es/m/D3FC73E2-9B1A-4DB4-8180-55F57D116B4E@amazon.com
---
 doc/src/sgml/ref/analyze.sgml                 | 12 +++++
 doc/src/sgml/ref/vacuum.sgml                  | 12 +++++
 src/backend/parser/gram.y                     |  2 +
 src/include/nodes/parsenodes.h                |  2 +-
 src/test/isolation/expected/vacuum-nowait.out | 64 +++++++++++++++++++++++++++
 src/test/isolation/isolation_schedule         |  1 +
 src/test/isolation/specs/vacuum-nowait.spec   | 42 ++++++++++++++++++
 src/test/regress/expected/vacuum.out          |  9 ++++
 src/test/regress/sql/vacuum.sql               |  8 ++++
 9 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 src/test/isolation/expected/vacuum-nowait.out
 create mode 100644 src/test/isolation/specs/vacuum-nowait.spec

diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml
index 10b3a9a6733..05882c30328 100644
--- a/doc/src/sgml/ref/analyze.sgml
+++ b/doc/src/sgml/ref/analyze.sgml
@@ -27,6 +27,7 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea
 <phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
 
     VERBOSE
+    NOWAIT
 
 <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
 
@@ -76,6 +77,17 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>NOWAIT</literal></term>
+    <listitem>
+     <para>
+      Specifies that <command>ANALYZE</command> should not wait for any
+      conflicting locks to be released: if a relation cannot be locked
+      immediately without waiting, the relation is skipped.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">table_name</replaceable></term>
     <listitem>
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index b760e8ede18..1d7ffef734f 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -31,6 +31,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
     VERBOSE
     ANALYZE
     DISABLE_PAGE_SKIPPING
+    NOWAIT
 
 <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
 
@@ -160,6 +161,17 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>NOWAIT</literal></term>
+    <listitem>
+     <para>
+      Specifies that <command>VACUUM</command> should not wait for any
+      conflicting locks to be released: if a relation cannot be locked
+      immediately without waiting, the relation is skipped.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">table_name</replaceable></term>
     <listitem>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1d7317c6eac..54ac7aaaee6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10538,6 +10538,7 @@ vacuum_option_elem:
 							 errmsg("unrecognized VACUUM option \"%s\"", $1),
 									 parser_errposition(@1)));
 				}
+			| NOWAIT			{ $$ = VACOPT_NOWAIT; }
 		;
 
 AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
@@ -10565,6 +10566,7 @@ analyze_option_list:
 
 analyze_option_elem:
 			VERBOSE				{ $$ = VACOPT_VERBOSE; }
+			| NOWAIT			{ $$ = VACOPT_NOWAIT; }
 		;
 
 analyze_keyword:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac292bc6e7a..56e22ae29a2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3125,7 +3125,7 @@ typedef enum VacuumOption
 	VACOPT_VERBOSE = 1 << 2,	/* print progress info */
 	VACOPT_FREEZE = 1 << 3,		/* FREEZE option */
 	VACOPT_FULL = 1 << 4,		/* FULL (non-concurrent) vacuum */
-	VACOPT_NOWAIT = 1 << 5,		/* don't wait to get lock (autovacuum only) */
+	VACOPT_NOWAIT = 1 << 5,		/* don't wait to get lock */
 	VACOPT_SKIPTOAST = 1 << 6,	/* don't process the TOAST table, if any */
 	VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7	/* don't skip any pages */
 } VacuumOption;
diff --git a/src/test/isolation/expected/vacuum-nowait.out b/src/test/isolation/expected/vacuum-nowait.out
new file mode 100644
index 00000000000..225ec23760e
--- /dev/null
+++ b/src/test/isolation/expected/vacuum-nowait.out
@@ -0,0 +1,64 @@
+Parsed test spec with 2 sessions
+
+starting permutation: lock vac_specified commit
+step lock: 
+	BEGIN;
+	LOCK part1 IN SHARE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_specified: VACUUM (NOWAIT) part1, part2;
+step commit: 
+	COMMIT;
+
+
+starting permutation: lock vac_all_parts commit
+step lock: 
+	BEGIN;
+	LOCK part1 IN SHARE MODE;
+
+step vac_all_parts: VACUUM (NOWAIT) parted;
+step commit: 
+	COMMIT;
+
+
+starting permutation: lock analyze_specified commit
+step lock: 
+	BEGIN;
+	LOCK part1 IN SHARE MODE;
+
+WARNING:  skipping analyze of "part1" --- lock not available
+step analyze_specified: ANALYZE (NOWAIT) part1, part2;
+step commit: 
+	COMMIT;
+
+
+starting permutation: lock analyze_all_parts commit
+step lock: 
+	BEGIN;
+	LOCK part1 IN SHARE MODE;
+
+step analyze_all_parts: ANALYZE (NOWAIT) parted;
+step commit: 
+	COMMIT;
+
+
+starting permutation: lock vac_analyze_specified commit
+step lock: 
+	BEGIN;
+	LOCK part1 IN SHARE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_analyze_specified: VACUUM (ANALYZE, NOWAIT) part1, part2;
+step commit: 
+	COMMIT;
+
+
+starting permutation: lock vac_analyze_all_parts commit
+step lock: 
+	BEGIN;
+	LOCK part1 IN SHARE MODE;
+
+step vac_analyze_all_parts: VACUUM (ANALYZE, NOWAIT) parted;
+step commit: 
+	COMMIT;
+
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 74d7d59546a..140b637eafa 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -66,3 +66,4 @@ test: async-notify
 test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
+test: vacuum-nowait
diff --git a/src/test/isolation/specs/vacuum-nowait.spec b/src/test/isolation/specs/vacuum-nowait.spec
new file mode 100644
index 00000000000..498b0a0583a
--- /dev/null
+++ b/src/test/isolation/specs/vacuum-nowait.spec
@@ -0,0 +1,42 @@
+# Test for the NOWAIT option for VACUUM and ANALYZE commands.
+#
+# This also verifies that log messages are not emitted for skipped relations
+# that were not specified in the VACUUM or ANALYZE command.
+
+setup
+{
+	CREATE TABLE parted (a INT) PARTITION BY LIST (a);
+	CREATE TABLE part1 PARTITION OF parted FOR VALUES IN (1);
+	CREATE TABLE part2 PARTITION OF parted FOR VALUES IN (2);
+}
+
+teardown
+{
+	DROP TABLE IF EXISTS parted;
+}
+
+session "s1"
+step "lock"
+{
+	BEGIN;
+	LOCK part1 IN SHARE MODE;
+}
+step "commit"
+{
+	COMMIT;
+}
+
+session "s2"
+step "vac_specified"		{ VACUUM (NOWAIT) part1, part2; }
+step "vac_all_parts"		{ VACUUM (NOWAIT) parted; }
+step "analyze_specified"	{ ANALYZE (NOWAIT) part1, part2; }
+step "analyze_all_parts"	{ ANALYZE (NOWAIT) parted; }
+step "vac_analyze_specified"	{ VACUUM (ANALYZE, NOWAIT) part1, part2; }
+step "vac_analyze_all_parts"	{ VACUUM (ANALYZE, NOWAIT) parted; }
+
+permutation "lock" "vac_specified" "commit"
+permutation "lock" "vac_all_parts" "commit"
+permutation "lock" "analyze_specified" "commit"
+permutation "lock" "analyze_all_parts" "commit"
+permutation "lock" "vac_analyze_specified" "commit"
+permutation "lock" "vac_analyze_all_parts" "commit"
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d66e2aa3b70..f06d975f26c 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -119,6 +119,15 @@ ANALYZE (nonexistant-arg) does_not_exist;
 ERROR:  syntax error at or near "nonexistant"
 LINE 1: ANALYZE (nonexistant-arg) does_not_exist;
                  ^
+-- ensure argument order independence, and that NOWAIT on non-existing
+-- relation still errors out.
+ANALYZE (NOWAIT, VERBOSE) does_not_exist;
+ERROR:  relation "does_not_exist" does not exist
+ANALYZE (VERBOSE, NOWAIT) does_not_exist;
+ERROR:  relation "does_not_exist" does not exist
+-- nowait option
+VACUUM (NOWAIT) vactst;
+ANALYZE (NOWAIT) 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 275ce2e270f..5fb6ad20051 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -92,6 +92,14 @@ ANALYZE vactst (i), vacparted (does_not_exist);
 -- parenthesized syntax for ANALYZE
 ANALYZE (VERBOSE) does_not_exist;
 ANALYZE (nonexistant-arg) does_not_exist;
+-- ensure argument order independence, and that NOWAIT on non-existing
+-- relation still errors out.
+ANALYZE (NOWAIT, VERBOSE) does_not_exist;
+ANALYZE (VERBOSE, NOWAIT) does_not_exist;
+
+-- nowait option
+VACUUM (NOWAIT) vactst;
+ANALYZE (NOWAIT) vactst;
 
 DROP TABLE vaccluster;
 DROP TABLE vactst;
-- 
2.15.1.354.g95ec6b1b33.dirty

