From 8c5a914d14dacfce36ced1877ac392abd28d545b Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Thu, 11 Jun 2020 00:05:31 +0000
Subject: [PATCH v1 1/1] Add --skip-index-cleanup and --skip-truncate to
 vacuumdb.

Both INDEX_CLEANUP and TRUNCATE have been available since v12.
This change adds support for disabling these options from vacuumdb.
---
 doc/src/sgml/ref/vacuumdb.sgml    | 26 ++++++++++++++++
 src/bin/scripts/t/100_vacuumdb.pl | 16 +++++++++-
 src/bin/scripts/vacuumdb.c        | 63 +++++++++++++++++++++++++++++++++++----
 3 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml
index fd1dc140ab..980cd3e9db 100644
--- a/doc/src/sgml/ref/vacuumdb.sgml
+++ b/doc/src/sgml/ref/vacuumdb.sgml
@@ -254,6 +254,19 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--skip-index-cleanup</option></term>
+      <listitem>
+       <para>
+        Skip removing index entries pointing to dead tuples.
+       </para>
+       <note>
+        This option is only available for servers running
+        <productname>PostgreSQL</productname> 12 and later.
+       </note>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--skip-locked</option></term>
       <listitem>
@@ -269,6 +282,19 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--skip-truncate</option></term>
+      <listitem>
+       <para>
+        Skip truncating empty pages at the end of the table.
+       </para>
+       <note>
+        This option is only available for servers running
+        <productname>PostgreSQL</productname> 12 and later.
+       </note>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-t <replaceable class="parameter">table</replaceable> [ (<replaceable class="parameter">column</replaceable> [,...]) ]</option></term>
       <term><option>--table=<replaceable class="parameter">table</replaceable> [ (<replaceable class="parameter">column</replaceable> [,...]) ]</option></term>
diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index b136bd4457..bc15803c2f 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 => 49;
+use Test::More tests => 55;
 
 program_help_ok('vacuumdb');
 program_version_ok('vacuumdb');
@@ -48,6 +48,20 @@ $node->issues_sql_like(
 $node->command_fails(
 	[ 'vacuumdb', '--analyze-only', '--disable-page-skipping', 'postgres' ],
 	'--analyze-only and --disable-page-skipping specified together');
+$node->issues_sql_like(
+	[ 'vacuumdb', '--skip-index-cleanup', 'postgres' ],
+	qr/statement: VACUUM \(INDEX_CLEANUP FALSE\).*;/,
+	'vacuumdb --skip-index-cleanup');
+$node->command_fails(
+	[ 'vacuumdb', '--analyze-only', '--skip-index-cleanup', 'postgres' ],
+	'--analyze-only and --skip-index-cleanup specified together');
+$node->issues_sql_like(
+    [ 'vacuumdb', '--skip-truncate', 'postgres' ],
+    qr/statement: VACUUM \(TRUNCATE FALSE\).*;/,
+    'vacuumdb --skip-truncate');
+$node->command_fails(
+    [ 'vacuumdb', '--analyze-only', '--skip-truncate', 'postgres' ],
+    '--analyze-only and --skip-truncate specified together');
 $node->issues_sql_like(
 	[ 'vacuumdb', '-P', 2, 'postgres' ],
 	qr/statement: VACUUM \(PARALLEL 2\).*;/,
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 154084a086..9f9ebc9f36 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -32,7 +32,9 @@ typedef struct vacuumingOptions
 	bool		full;
 	bool		freeze;
 	bool		disable_page_skipping;
+	bool		skip_index_cleanup;
 	bool		skip_locked;
+	bool		skip_truncate;
 	int			min_xid_age;
 	int			min_mxid_age;
 	int			parallel_workers;	/* >= 0 indicates user specified the
@@ -93,9 +95,11 @@ main(int argc, char *argv[])
 		{"maintenance-db", required_argument, NULL, 2},
 		{"analyze-in-stages", no_argument, NULL, 3},
 		{"disable-page-skipping", no_argument, NULL, 4},
-		{"skip-locked", no_argument, NULL, 5},
-		{"min-xid-age", required_argument, NULL, 6},
-		{"min-mxid-age", required_argument, NULL, 7},
+		{"skip-index-cleanup", no_argument, NULL, 5},
+		{"skip-locked", no_argument, NULL, 6},
+		{"skip-truncate", no_argument, NULL, 7},
+		{"min-xid-age", required_argument, NULL, 8},
+		{"min-mxid-age", required_argument, NULL, 9},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -205,9 +209,15 @@ main(int argc, char *argv[])
 				vacopts.disable_page_skipping = true;
 				break;
 			case 5:
-				vacopts.skip_locked = true;
+				vacopts.skip_index_cleanup = true;
 				break;
 			case 6:
+				vacopts.skip_locked = true;
+				break;
+			case 7:
+				vacopts.skip_truncate = true;
+				break;
+			case 8:
 				vacopts.min_xid_age = atoi(optarg);
 				if (vacopts.min_xid_age <= 0)
 				{
@@ -215,7 +225,7 @@ main(int argc, char *argv[])
 					exit(1);
 				}
 				break;
-			case 7:
+			case 9:
 				vacopts.min_mxid_age = atoi(optarg);
 				if (vacopts.min_mxid_age <= 0)
 				{
@@ -267,6 +277,18 @@ main(int argc, char *argv[])
 						 "disable-page-skipping");
 			exit(1);
 		}
+		if (vacopts.skip_index_cleanup)
+		{
+			pg_log_error("cannot use the \"%s\" option when performing only analyze",
+						 "skip-index-cleanup");
+			exit(1);
+		}
+		if (vacopts.skip_truncate)
+		{
+			pg_log_error("cannot use the \"%s\" option when performing only analyze",
+						 "skip-truncate");
+			exit(1);
+		}
 		/* allow 'and_analyze' with 'analyze_only' */
 	}
 
@@ -412,6 +434,13 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
 		exit(1);
 	}
 
+	if (vacopts->skip_index_cleanup && PQserverVersion(conn) < 120000)
+	{
+		PQfinish(conn);
+		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+					 "skip-index-cleanup", "12");
+	}
+
 	if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
 	{
 		PQfinish(conn);
@@ -420,6 +449,14 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
 		exit(1);
 	}
 
+	if (vacopts->skip_truncate && PQserverVersion(conn) < 120000)
+	{
+		PQfinish(conn);
+		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+					 "skip-truncate", "12");
+		exit(1);
+	}
+
 	if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
 	{
 		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
@@ -832,6 +869,13 @@ prepare_vacuum_command(PQExpBuffer sql, int serverVersion,
 				appendPQExpBuffer(sql, "%sDISABLE_PAGE_SKIPPING", sep);
 				sep = comma;
 			}
+			if (vacopts->skip_index_cleanup)
+			{
+				/* INDEX_CLEANUP is supported since v12 */
+				Assert(serverVersion >= 120000);
+				appendPQExpBuffer(sql, "%sINDEX_CLEANUP FALSE", sep);
+				sep = comma;
+			}
 			if (vacopts->skip_locked)
 			{
 				/* SKIP_LOCKED is supported since v12 */
@@ -839,6 +883,13 @@ prepare_vacuum_command(PQExpBuffer sql, int serverVersion,
 				appendPQExpBuffer(sql, "%sSKIP_LOCKED", sep);
 				sep = comma;
 			}
+			if (vacopts->skip_truncate)
+			{
+				/* TRUNCATE is supported since v12 */
+				Assert(serverVersion >= 120000);
+				appendPQExpBuffer(sql, "%sTRUNCATE FALSE", sep);
+				sep = comma;
+			}
 			if (vacopts->full)
 			{
 				appendPQExpBuffer(sql, "%sFULL", sep);
@@ -932,7 +983,9 @@ help(const char *progname)
 	printf(_("      --min-xid-age=XID_AGE       minimum transaction ID age of tables to vacuum\n"));
 	printf(_("  -P, --parallel=PARALLEL_DEGREE  use this many background workers for vacuum, if available\n"));
 	printf(_("  -q, --quiet                     don't write any messages\n"));
+	printf(_("      --skip-index-cleanup        skip removing index entries that point to dead tuples\n"));
 	printf(_("      --skip-locked               skip relations that cannot be immediately locked\n"));
+	printf(_("      --skip-truncate             skip truncating empty pages at the end of the table\n"));
 	printf(_("  -t, --table='TABLE[(COLUMNS)]'  vacuum specific table(s) only\n"));
 	printf(_("  -v, --verbose                   write a lot of output\n"));
 	printf(_("  -V, --version                   output version information, then exit\n"));
-- 
2.16.6

