From c9c072748b54e6dbde8bde5d932af52c229141f5 Mon Sep 17 00:00:00 2001
From: Alexey Kondratov <kondratov.aleksey@gmail.com>
Date: Wed, 18 Sep 2019 15:22:04 +0300
Subject: [PATCH v3 2/2] Use SET TABLESPACE syntax with NOWAIT option

---
 doc/src/sgml/ref/reindex.sgml             |  8 ++++---
 src/backend/commands/indexcmds.c          |  4 ++--
 src/backend/parser/gram.y                 | 26 +++++++++++++-------
 src/backend/tcop/utility.c                | 10 ++++++--
 src/include/nodes/parsenodes.h            |  1 +
 src/test/regress/input/tablespace.source  | 15 ++++++------
 src/test/regress/output/tablespace.source | 29 +++++++++++++----------
 7 files changed, 57 insertions(+), 36 deletions(-)

diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml
index 96c9363ad9..4f0f21b7f9 100644
--- a/doc/src/sgml/ref/reindex.sgml
+++ b/doc/src/sgml/ref/reindex.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 REINDEX [ ( VERBOSE ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } [ CONCURRENTLY ] <replaceable class="parameter">name</replaceable>
-REINDEX [ ( VERBOSE ) ] { INDEX | TABLE } [ CONCURRENTLY ] <replaceable class="parameter">name</replaceable> [ TABLESPACE <replaceable class="parameter">new_tablespace</replaceable> ]
+REINDEX [ ( VERBOSE ) ] { INDEX | TABLE } [ CONCURRENTLY ] <replaceable class="parameter">name</replaceable> [ SET TABLESPACE <replaceable class="parameter">new_tablespace</replaceable> [NOWAIT] ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -167,14 +167,16 @@ REINDEX [ ( VERBOSE ) ] { INDEX | TABLE } [ CONCURRENTLY ] <replaceable class="p
    </varlistentry>
 
    <varlistentry>
-    <term><literal>TABLESPACE</literal></term>
+    <term><literal>SET TABLESPACE</literal></term>
     <listitem>
      <para>
       This specifies a tablespace, where all rebuilt indexes will be created.
       Can be used only with <literal>REINDEX INDEX</literal> and
       <literal>REINDEX TABLE</literal>, since the system indexes are not
       movable, but <literal>SCHEMA</literal>, <literal>DATABASE</literal> or
-      <literal>SYSTEM</literal> very likely will has one. 
+      <literal>SYSTEM</literal> very likely will has one.  If the
+      <literal>NOWAIT</literal> option is specified then the command will fail
+      if it is unable to acquire all of the locks required immediately.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 7f00dac426..6eab0d335d 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -2329,7 +2329,7 @@ ReindexIndex(RangeVar *indexRelation, char *newTableSpaceName, int options, bool
 	state.locked_table_oid = InvalidOid;
 	indOid = RangeVarGetRelidExtended(indexRelation,
 									  concurrent ? ShareUpdateExclusiveLock : AccessExclusiveLock,
-									  0,
+									  options & REINDEXOPT_NOWAIT ? RVR_NOWAIT : 0,
 									  RangeVarCallbackForReindexIndex,
 									  &state);
 
@@ -2443,7 +2443,7 @@ ReindexTable(RangeVar *relation, char *newTableSpaceName, int options, bool conc
 	/* The lock level used here should match reindex_relation(). */
 	heapOid = RangeVarGetRelidExtended(relation,
 									   concurrent ? ShareUpdateExclusiveLock : ShareLock,
-									   0,
+									   options & REINDEXOPT_NOWAIT ? RVR_NOWAIT : 0,
 									   RangeVarCallbackOwnsTable, NULL);
 
 	if (newTableSpaceName)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index cba361f1bc..01b2fac200 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -549,7 +549,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <list>	constraints_set_list
 %type <boolean> constraints_set_mode
-%type <str>		OptTableSpace OptConsTableSpace opt_tablespace_name
+%type <str>		OptTableSpace OptConsTableSpace opt_set_tablespace_name
 %type <rolespec> OptTableSpaceOwner
 %type <ival>	opt_check_option
 
@@ -3937,9 +3937,9 @@ OptTableSpace:   TABLESPACE name					{ $$ = $2; }
 			| /*EMPTY*/								{ $$ = NULL; }
 		;
 
-opt_tablespace_name:
-			TABLESPACE name						{ $$ = $2; }
-			| /*EMPTY*/							{ $$ = NULL; }
+opt_set_tablespace_name:
+			SET TABLESPACE name						{ $$ = $3; }
+			| /*EMPTY*/								{ $$ = NULL; }
 		;
 
 OptConsTableSpace:   USING INDEX TABLESPACE name	{ $$ = $4; }
@@ -8361,11 +8361,11 @@ DropTransformStmt: DROP TRANSFORM opt_if_exists FOR Typename LANGUAGE name opt_d
  *
  *		QUERY:
  *
- *		REINDEX [ (options) ] type [CONCURRENTLY] <name> [ TABLESPACE <tablespace_name> ]
+ *		REINDEX [ (options) ] type [CONCURRENTLY] <name> [ SET TABLESPACE <tablespace_name> ]
  *****************************************************************************/
 
 ReindexStmt:
-			REINDEX reindex_target_type opt_concurrently qualified_name opt_tablespace_name
+			REINDEX reindex_target_type opt_concurrently qualified_name opt_set_tablespace_name opt_nowait
 				{
 					ReindexStmt *n = makeNode(ReindexStmt);
 					n->kind = $2;
@@ -8374,9 +8374,11 @@ ReindexStmt:
 					n->tablespacename = $5;
 					n->name = NULL;
 					n->options = 0;
+					if ($6)
+						n->options |= REINDEXOPT_NOWAIT;
 					$$ = (Node *)n;
 				}
-			| REINDEX reindex_target_multitable opt_concurrently name opt_tablespace_name
+			| REINDEX reindex_target_multitable opt_concurrently name opt_set_tablespace_name opt_nowait
 				{
 					ReindexStmt *n = makeNode(ReindexStmt);
 					n->kind = $2;
@@ -8385,9 +8387,11 @@ ReindexStmt:
 					n->tablespacename = $5;
 					n->relation = NULL;
 					n->options = 0;
+					if ($6)
+						n->options |= REINDEXOPT_NOWAIT;
 					$$ = (Node *)n;
 				}
-			| REINDEX '(' reindex_option_list ')' reindex_target_type opt_concurrently qualified_name opt_tablespace_name
+			| REINDEX '(' reindex_option_list ')' reindex_target_type opt_concurrently qualified_name opt_set_tablespace_name opt_nowait
 				{
 					ReindexStmt *n = makeNode(ReindexStmt);
 					n->kind = $5;
@@ -8396,9 +8400,11 @@ ReindexStmt:
 					n->name = NULL;
 					n->options = $3;
 					n->tablespacename = $8;
+					if ($9)
+						n->options |= REINDEXOPT_NOWAIT;
 					$$ = (Node *)n;
 				}
-			| REINDEX '(' reindex_option_list ')' reindex_target_multitable opt_concurrently name opt_tablespace_name
+			| REINDEX '(' reindex_option_list ')' reindex_target_multitable opt_concurrently name opt_set_tablespace_name opt_nowait
 				{
 					ReindexStmt *n = makeNode(ReindexStmt);
 					n->kind = $5;
@@ -8407,6 +8413,8 @@ ReindexStmt:
 					n->relation = NULL;
 					n->options = $3;
 					n->tablespacename = $8;
+					if ($9)
+						n->options |= REINDEXOPT_NOWAIT;
 					$$ = (Node *)n;
 				}
 		;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index fed40471e0..88d6b80263 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -778,6 +778,12 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 					PreventInTransactionBlock(isTopLevel,
 											  "REINDEX CONCURRENTLY");
 
+				if (stmt->options & REINDEXOPT_NOWAIT && stmt->tablespacename == NULL)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("incompatible NOWAIT option"),
+							errdetail("You can only use NOWAIT with SET TABLESPACE.")));
+
 				/* we choose to allow this during "read only" transactions */
 				PreventCommandDuringRecovery("REINDEX");
 				/* forbidden in parallel mode due to CommandIsReadOnly */
@@ -800,8 +806,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 						 */
 						if (stmt->tablespacename)
 							ereport(ERROR,
-								(errmsg("incompatible TABLESPACE option"),
-								errdetail("You can only use TABLESPACE with REINDEX { INDEX | TABLE }.")));
+								(errmsg("incompatible SET TABLESPACE option"),
+								errdetail("You can only use SET TABLESPACE with REINDEX { INDEX | TABLE }.")));
 
 						/*
 						 * This cannot run inside a user transaction block; if
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ed2c385050..2fef8b249d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3314,6 +3314,7 @@ typedef struct ConstraintsSetStmt
 /* Reindex options */
 #define REINDEXOPT_VERBOSE (1 << 0)	/* print progress info */
 #define REINDEXOPT_REPORT_PROGRESS (1 << 1)	/* report pgstat progress */
+#define REINDEXOPT_NOWAIT (1 << 2)	/* error if relation cannot be locked */
 
 typedef enum ReindexObjectType
 {
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index d2dacd85d9..0b9df9f0d8 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -25,12 +25,13 @@ INSERT INTO regress_tblspace_test_tbl (num1, num2, num3)
 CREATE INDEX regress_tblspace_test_tbl_idx ON regress_tblspace_test_tbl (num1);
 
 -- check REINDEX with TABLESPACE change
-REINDEX INDEX regress_tblspace_test_tbl_idx TABLESPACE regress_tblspace; -- ok
-REINDEX TABLE regress_tblspace_test_tbl TABLESPACE regress_tblspace; -- ok
-REINDEX TABLE pg_authid TABLESPACE regress_tblspace; -- fail
-REINDEX SCHEMA pg_catalog TABLESPACE regress_tblspace; -- fail
-REINDEX DATABASE postgres TABLESPACE regress_tblspace; -- fail
-REINDEX SYSTEM postgres TABLESPACE regress_tblspace; -- fail
+REINDEX INDEX regress_tblspace_test_tbl_idx SET TABLESPACE regress_tblspace; -- ok
+REINDEX TABLE regress_tblspace_test_tbl SET TABLESPACE regress_tblspace; -- ok
+REINDEX TABLE pg_authid NOWAIT; -- fail
+REINDEX TABLE pg_authid SET TABLESPACE regress_tblspace; -- fail
+REINDEX SCHEMA pg_catalog SET TABLESPACE regress_tblspace; -- fail
+REINDEX DATABASE postgres SET TABLESPACE regress_tblspace; -- fail
+REINDEX SYSTEM postgres SET TABLESPACE regress_tblspace; -- fail
 
 -- check that all relations moved to new tablespace
 SELECT relname FROM pg_class
@@ -38,7 +39,7 @@ WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspa
 AND relname IN ('regress_tblspace_test_tbl_idx');
 
 -- move back to pg_default tablespace
-REINDEX TABLE CONCURRENTLY regress_tblspace_test_tbl TABLESPACE pg_default; -- ok
+REINDEX TABLE CONCURRENTLY regress_tblspace_test_tbl SET TABLESPACE pg_default NOWAIT; -- ok
 
 -- check that all relations moved back to pg_default
 SELECT relname FROM pg_class
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index fb2f33c0b1..4b5a1ae2fb 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -27,19 +27,22 @@ INSERT INTO regress_tblspace_test_tbl (num1, num2, num3)
   FROM generate_series(1, 20000) s(i);
 CREATE INDEX regress_tblspace_test_tbl_idx ON regress_tblspace_test_tbl (num1);
 -- check REINDEX with TABLESPACE change
-REINDEX INDEX regress_tblspace_test_tbl_idx TABLESPACE regress_tblspace; -- ok
-REINDEX TABLE regress_tblspace_test_tbl TABLESPACE regress_tblspace; -- ok
-REINDEX TABLE pg_authid TABLESPACE regress_tblspace; -- fail
+REINDEX INDEX regress_tblspace_test_tbl_idx SET TABLESPACE regress_tblspace; -- ok
+REINDEX TABLE regress_tblspace_test_tbl SET TABLESPACE regress_tblspace; -- ok
+REINDEX TABLE pg_authid NOWAIT; -- fail
+ERROR:  incompatible NOWAIT option
+DETAIL:  You can only use NOWAIT with SET TABLESPACE.
+REINDEX TABLE pg_authid SET TABLESPACE regress_tblspace; -- fail
 ERROR:  cannot move system relation "pg_authid_rolname_index"
-REINDEX SCHEMA pg_catalog TABLESPACE regress_tblspace; -- fail
-ERROR:  incompatible TABLESPACE option
-DETAIL:  You can only use TABLESPACE with REINDEX { INDEX | TABLE }.
-REINDEX DATABASE postgres TABLESPACE regress_tblspace; -- fail
-ERROR:  incompatible TABLESPACE option
-DETAIL:  You can only use TABLESPACE with REINDEX { INDEX | TABLE }.
-REINDEX SYSTEM postgres TABLESPACE regress_tblspace; -- fail
-ERROR:  incompatible TABLESPACE option
-DETAIL:  You can only use TABLESPACE with REINDEX { INDEX | TABLE }.
+REINDEX SCHEMA pg_catalog SET TABLESPACE regress_tblspace; -- fail
+ERROR:  incompatible SET TABLESPACE option
+DETAIL:  You can only use SET TABLESPACE with REINDEX { INDEX | TABLE }.
+REINDEX DATABASE postgres SET TABLESPACE regress_tblspace; -- fail
+ERROR:  incompatible SET TABLESPACE option
+DETAIL:  You can only use SET TABLESPACE with REINDEX { INDEX | TABLE }.
+REINDEX SYSTEM postgres SET TABLESPACE regress_tblspace; -- fail
+ERROR:  incompatible SET TABLESPACE option
+DETAIL:  You can only use SET TABLESPACE with REINDEX { INDEX | TABLE }.
 -- check that all relations moved to new tablespace
 SELECT relname FROM pg_class
 WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace')
@@ -50,7 +53,7 @@ AND relname IN ('regress_tblspace_test_tbl_idx');
 (1 row)
 
 -- move back to pg_default tablespace
-REINDEX TABLE CONCURRENTLY regress_tblspace_test_tbl TABLESPACE pg_default; -- ok
+REINDEX TABLE CONCURRENTLY regress_tblspace_test_tbl SET TABLESPACE pg_default NOWAIT; -- ok
 -- check that all relations moved back to pg_default
 SELECT relname FROM pg_class
 WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace')
-- 
2.17.1

