From de60d212b50a6412e483c995b83e28c5597089ad Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Thu, 9 May 2019 20:02:05 +0900
Subject: [PATCH v3 1/2] Add --index-cleanup option to vacuumdb.

---
 doc/src/sgml/ref/vacuumdb.sgml    | 18 ++++++++++++++++++
 src/bin/scripts/t/100_vacuumdb.pl | 16 +++++++++++++++-
 src/bin/scripts/vacuumdb.c        | 29 +++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml
index 47d9345..5ec043e 100644
--- a/doc/src/sgml/ref/vacuumdb.sgml
+++ b/doc/src/sgml/ref/vacuumdb.sgml
@@ -149,6 +149,24 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>--index-cleanup=<replaceable class="parameter">boolean</replaceable></option></term>
+      <listitem>
+       <para>
+         Specify that <command>VACUUM</command> should attempt to remove
+         index entries pointing to dead tuples. If this option is not specified
+         the behavior depends on <literal>vacuum_index_cleanup</literal> option
+         for the table to be vacuumed.
+       </para>
+       <note>
+        <para>
+         This option is only available for servers running
+         <productname>PostgreSQL</productname> 12 and later.
+        </para>
+       </note>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-j <replaceable class="parameter">njobs</replaceable></option></term>
       <term><option>--jobs=<replaceable class="parameter">njobs</replaceable></option></term>
       <listitem>
diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7f3a9b1..984f2a8 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -3,7 +3,7 @@ use warnings;
 
 use PostgresNode;
 use TestLib;
-use Test::More tests => 44;
+use Test::More tests => 50;
 
 program_help_ok('vacuumdb');
 program_version_ok('vacuumdb');
@@ -45,9 +45,23 @@ $node->issues_sql_like(
 	[ 'vacuumdb', '--skip-locked', '--analyze-only', 'postgres' ],
 	qr/statement: ANALYZE \(SKIP_LOCKED\).*;/,
 	'vacuumdb --skip-locked --analyze-only');
+$node->issues_sql_like(
+	[ 'vacuumdb', '--index-cleanup=false', 'postgres' ],
+	qr/statement: VACUUM \(INDEX_CLEANUP false\).*;/,
+	'vacuumdb --index-cleanup=false');
+$node->issues_sql_like(
+	[ 'vacuumdb', '--index-cleanup=true', 'postgres' ],
+	qr/statement: VACUUM \(INDEX_CLEANUP true\).*;/,
+	'vacuumdb --index-cleanup=true');
 $node->command_fails(
 	[ 'vacuumdb', '--analyze-only', '--disable-page-skipping', 'postgres' ],
 	'--analyze-only and --disable-page-skipping specified together');
+$node->command_fails(
+	[ 'vacuumdb', '--analyze-only', '--index-cleanup=true', 'postgres' ],
+	'--analyze-only and --index-cleanup specified together');
+$node->command_fails(
+	[ 'vacuumdb', '--index-cleanup=invalid', 'postgres' ],
+	'--index-cleanup with an invalid argument');
 $node->command_ok([qw(vacuumdb -Z --table=pg_am dbname=template1)],
 	'vacuumdb with connection string');
 
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 6d216aa..7f91d18 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -46,6 +46,7 @@ typedef struct vacuumingOptions
 	bool		skip_locked;
 	int			min_xid_age;
 	int			min_mxid_age;
+	char		*index_cleanup;
 } vacuumingOptions;
 
 
@@ -87,6 +88,7 @@ static void init_slot(ParallelSlot *slot, PGconn *conn);
 
 static void help(const char *progname);
 
+
 /* For analyze-in-stages mode */
 #define ANALYZE_NO_STAGE	-1
 #define ANALYZE_NUM_STAGES	3
@@ -118,6 +120,7 @@ main(int argc, char *argv[])
 		{"skip-locked", no_argument, NULL, 5},
 		{"min-xid-age", required_argument, NULL, 6},
 		{"min-mxid-age", required_argument, NULL, 7},
+		{"index-cleanup", required_argument, NULL, 8},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -242,6 +245,9 @@ main(int argc, char *argv[])
 					exit(1);
 				}
 				break;
+			case 8:
+				vacopts.index_cleanup = pg_strdup(optarg);
+				break;
 			default:
 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
 				exit(1);
@@ -286,6 +292,12 @@ main(int argc, char *argv[])
 						 "disable-page-skipping");
 			exit(1);
 		}
+		if (vacopts.index_cleanup != NULL)
+		{
+			pg_log_error("cannot use the \"%s\" option when performing only analyze",
+						 "index-cleanup");
+			exit(1);
+		}
 		/* allow 'and_analyze' with 'analyze_only' */
 	}
 
@@ -414,6 +426,14 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
 		exit(1);
 	}
 
+	if (vacopts->index_cleanup != NULL && PQserverVersion(conn) < 120000)
+	{
+		PQfinish(conn);
+		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL 12",
+					 "index-cleanup");
+		exit(1);
+	}
+
 	if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
 	{
 		PQfinish(conn);
@@ -874,6 +894,14 @@ prepare_vacuum_command(PQExpBuffer sql, int serverVersion,
 				appendPQExpBuffer(sql, "%sSKIP_LOCKED", sep);
 				sep = comma;
 			}
+			if (vacopts->index_cleanup)
+			{
+				/* INDEX_CLEANUP is supported since 12 */
+				Assert(serverVersion >= 120000);
+				appendPQExpBuffer(sql, "%sINDEX_CLEANUP %s", sep,
+								  vacopts->index_cleanup);
+				sep = comma;
+			}
 			if (vacopts->full)
 			{
 				appendPQExpBuffer(sql, "%sFULL", sep);
@@ -1222,6 +1250,7 @@ help(const char *progname)
 	printf(_("  -e, --echo                      show the commands being sent to the server\n"));
 	printf(_("  -f, --full                      do full vacuuming\n"));
 	printf(_("  -F, --freeze                    freeze row transaction information\n"));
+	printf(_("      --index-cleanup=BOOLEAN     do or do not index vacuuming and index cleanup\n"));
 	printf(_("  -j, --jobs=NUM                  use this many concurrent connections to vacuum\n"));
 	printf(_("      --min-mxid-age=MXID_AGE     minimum multixact ID age of tables to vacuum\n"));
 	printf(_("      --min-xid-age=XID_AGE       minimum transaction ID age of tables to vacuum\n"));
-- 
2.10.5

