From 5ee22be88b9d3501266807811ef37136794ff1df Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Thu, 26 Mar 2020 22:08:40 -0500
Subject: [PATCH v13 3/4] Parenthesized syntax: VACUUM (FULL, TABLESPACE ..)..

..it seems to be impossible to use "VACUUM FULL t TABLESPACE ts" since
"tablespace" isn't a fully reserved word?

Parenthesized list is the intended way to add new options anyway.

CLUSTER doesn't take parenthesized list (but maybe it should), otherwise we'd
do this there, too.
---
 src/backend/commands/vacuum.c             | 51 ++++++++++++-----------
 src/backend/nodes/copyfuncs.c             |  1 -
 src/backend/nodes/equalfuncs.c            |  1 -
 src/backend/parser/gram.y                 | 34 ---------------
 src/include/nodes/parsenodes.h            |  1 -
 src/test/regress/input/tablespace.source  |  8 ++--
 src/test/regress/output/tablespace.source |  8 ++--
 7 files changed, 35 insertions(+), 69 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 95bec8d6ba..005fb97a7b 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -109,8 +109,10 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 	bool		disable_page_skipping = false;
 	bool		parallel_option = false;
 	ListCell   *lc;
-	Oid 		tablespaceOid = InvalidOid; /* Oid of tablespace to use for relations
-											 * after VACUUM FULL. */
+
+	/* Name and Oid of tablespace to use for relations after VACUUM FULL. */
+	char		*tablespacename = NULL;
+	Oid 		tablespaceOid = InvalidOid;
 
 	/* Set default value */
 	params.index_cleanup = VACOPT_TERNARY_DEFAULT;
@@ -148,6 +150,8 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 			params.index_cleanup = get_vacopt_ternary_value(opt);
 		else if (strcmp(opt->defname, "truncate") == 0)
 			params.truncate = get_vacopt_ternary_value(opt);
+		else if (strcmp(opt->defname, "tablespace") == 0)
+			tablespacename = defGetString(opt);
 		else if (strcmp(opt->defname, "parallel") == 0)
 		{
 			parallel_option = true;
@@ -209,6 +213,27 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot specify both FULL and PARALLEL options")));
 
+	/* Get tablespace Oid to use. */
+	if (tablespacename)
+	{
+		if ((params.options & VACOPT_FULL) == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("incompatible TABLESPACE option"),
+					errdetail("You can only use TABLESPACE with VACUUM FULL.")));
+
+		tablespaceOid = get_tablespace_oid(tablespacename, false);
+
+		/* Can't move a non-shared relation into pg_global */
+		if (tablespaceOid == GLOBALTABLESPACE_OID)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cannot move non-shared relation to tablespace \"%s\"",
+							tablespacename)));
+
+	}
+	params.tablespace_oid = tablespaceOid;
+
 	/*
 	 * Make sure VACOPT_ANALYZE is specified if any column lists are present.
 	 */
@@ -246,28 +271,6 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 		params.multixact_freeze_table_age = -1;
 	}
 
-	/* Get tablespace Oid to use. */
-	if (vacstmt->tablespacename)
-	{
-		if (params.options & VACOPT_FULL)
-		{
-			tablespaceOid = get_tablespace_oid(vacstmt->tablespacename, false);
-
-			/* Can't move a non-shared relation into pg_global */
-			if (tablespaceOid == GLOBALTABLESPACE_OID)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						errmsg("cannot move non-shared relation to tablespace \"%s\"",
-								vacstmt->tablespacename)));
-		}
-		else
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					errmsg("incompatible TABLESPACE option"),
-					errdetail("You can only use TABLESPACE with VACUUM FULL.")));
-	}
-	params.tablespace_oid = tablespaceOid;
-
 	/* user-invoked vacuum is never "for wraparound" */
 	params.is_wraparound = false;
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 70b0234f44..16c4c0ab18 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3902,7 +3902,6 @@ _copyVacuumStmt(const VacuumStmt *from)
 	COPY_NODE_FIELD(options);
 	COPY_NODE_FIELD(rels);
 	COPY_SCALAR_FIELD(is_vacuumcmd);
-	COPY_STRING_FIELD(tablespacename);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 20cfdd6c32..671e960133 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1703,7 +1703,6 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
 	COMPARE_NODE_FIELD(options);
 	COMPARE_NODE_FIELD(rels);
 	COMPARE_SCALAR_FIELD(is_vacuumcmd);
-	COMPARE_SCALAR_FIELD(tablespacename);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 300e6409f8..77c043642c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10666,28 +10666,6 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_analyze opt_vacuum_relati
 											 makeDefElem("analyze", NULL, @5));
 					n->rels = $6;
 					n->is_vacuumcmd = true;
-					n->tablespacename = NULL;
-					$$ = (Node *)n;
-				}
-			| VACUUM opt_full opt_freeze opt_verbose opt_analyze TABLESPACE name opt_vacuum_relation_list
-				{
-					VacuumStmt *n = makeNode(VacuumStmt);
-					n->options = NIL;
-					if ($2)
-						n->options = lappend(n->options,
-								makeDefElem("full", NULL, @2));
-					if ($3)
-						n->options = lappend(n->options,
-								makeDefElem("freeze", NULL, @3));
-					if ($4)
-						n->options = lappend(n->options,
-								makeDefElem("verbose", NULL, @4));
-					if ($5)
-						n->options = lappend(n->options,
-								makeDefElem("analyze", NULL, @5));
-					n->tablespacename = $7;
-					n->rels = $8;
-					n->is_vacuumcmd = true;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vac_analyze_option_list ')' opt_vacuum_relation_list
@@ -10696,16 +10674,6 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_analyze opt_vacuum_relati
 					n->options = $3;
 					n->rels = $5;
 					n->is_vacuumcmd = true;
-					n->tablespacename = NULL;
-					$$ = (Node *) n;
-				}
-			| VACUUM '(' vac_analyze_option_list ')' TABLESPACE name opt_vacuum_relation_list
-				{
-					VacuumStmt *n = makeNode(VacuumStmt);
-					n->options = $3;
-					n->tablespacename = $6;
-					n->rels = $7;
-					n->is_vacuumcmd = true;
 					$$ = (Node *) n;
 				}
 		;
@@ -10719,7 +10687,6 @@ AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
 											 makeDefElem("verbose", NULL, @2));
 					n->rels = $3;
 					n->is_vacuumcmd = false;
-					n->tablespacename = NULL;
 					$$ = (Node *)n;
 				}
 			| analyze_keyword '(' vac_analyze_option_list ')' opt_vacuum_relation_list
@@ -10728,7 +10695,6 @@ AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
 					n->options = $3;
 					n->rels = $5;
 					n->is_vacuumcmd = false;
-					n->tablespacename = NULL;
 					$$ = (Node *) n;
 				}
 		;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ec58c73ce1..de0f32c59d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3219,7 +3219,6 @@ typedef struct VacuumStmt
 	List	   *options;		/* list of DefElem nodes */
 	List	   *rels;			/* list of VacuumRelation, or NIL for all */
 	bool		is_vacuumcmd;	/* true for VACUUM, false for ANALYZE */
-	char	   *tablespacename; /* tablespace name to use for vacuumed relation */
 } VacuumStmt;
 
 /*
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 352db323c8..0846aa58c3 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -57,10 +57,10 @@ WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspa
 ORDER BY relname;
 
 -- check VACUUM with TABLESPACE change
-VACUUM (FULL, ANALYSE, FREEZE) TABLESPACE pg_default regress_tblspace_test_tbl; -- ok
-VACUUM (FULL) TABLESPACE pg_default pg_authid; -- skip with warning
-VACUUM (ANALYSE) TABLESPACE pg_default; -- fail
-VACUUM (FULL) TABLESPACE pg_global regress_tblspace_test_tbl; -- fail
+VACUUM (FULL, ANALYSE, FREEZE, TABLESPACE pg_default) regress_tblspace_test_tbl; -- ok
+VACUUM (FULL, TABLESPACE pg_default) pg_authid; -- skip with warning
+VACUUM (ANALYSE, TABLESPACE pg_default); -- fail
+VACUUM (FULL, TABLESPACE pg_global) regress_tblspace_test_tbl; -- fail
 
 -- check that all tables 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 a05176069b..db28a23a23 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -76,14 +76,14 @@ ORDER BY relname;
 (2 rows)
 
 -- check VACUUM with TABLESPACE change
-VACUUM (FULL, ANALYSE, FREEZE) TABLESPACE pg_default regress_tblspace_test_tbl; -- ok
-VACUUM (FULL) TABLESPACE pg_default pg_authid; -- skip with warning
+VACUUM (FULL, ANALYSE, FREEZE, TABLESPACE pg_default) regress_tblspace_test_tbl; -- ok
+VACUUM (FULL, TABLESPACE pg_default) pg_authid; -- skip with warning
 WARNING:  skipping tablespace change of "pg_authid"
 DETAIL:  Cannot move system relation, only VACUUM is performed.
-VACUUM (ANALYSE) TABLESPACE pg_default; -- fail
+VACUUM (ANALYSE, TABLESPACE pg_default); -- fail
 ERROR:  incompatible TABLESPACE option
 DETAIL:  You can only use TABLESPACE with VACUUM FULL.
-VACUUM (FULL) TABLESPACE pg_global regress_tblspace_test_tbl; -- fail
+VACUUM (FULL, TABLESPACE pg_global) regress_tblspace_test_tbl; -- fail
 ERROR:  cannot move non-shared relation to tablespace "pg_global"
 -- check that all tables moved back to pg_default
 SELECT relname FROM pg_class
-- 
2.17.0

