diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml
index 84c76d7350c..508c8dfa146 100644
--- a/doc/src/sgml/ref/vacuumdb.sgml
+++ b/doc/src/sgml/ref/vacuumdb.sgml
@@ -171,6 +171,16 @@ PostgreSQL documentation
+
+
+
+
+ Print, but do not execute, the vacuum and analyze commands that would
+ have been sent to the server.
+
+
+
+
diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index a16fad593f7..fb2fecdd3c6 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -169,6 +169,10 @@ $node->issues_sql_like(
[ 'vacuumdb', '--schema' => '"Foo"', 'postgres' ],
qr/VACUUM \(SKIP_DATABASE_STATS\) "Foo".bar/,
'vacuumdb --schema');
+$node->issues_sql_unlike(
+ [ 'vacuumdb', '--schema' => '"Foo"', 'postgres', '--dry-run' ],
+ qr/VACUUM \(SKIP_DATABASE_STATS\) "Foo".bar/,
+ 'vacuumdb --dry-run');
$node->issues_sql_like(
[ 'vacuumdb', '--schema' => '"Foo"', '--schema' => '"Bar"', 'postgres' ],
qr/VACUUM\ \(SKIP_DATABASE_STATS\)\ "Foo".bar
@@ -241,6 +245,14 @@ $node->safe_psql('postgres', q|
CREATE TABLE regression_vacuumdb_test AS select generate_series(1, 10) a, generate_series(2, 11) b;
ALTER TABLE regression_vacuumdb_test ADD COLUMN c INT GENERATED ALWAYS AS (a + b);
|);
+$node->issues_sql_unlike(
+ [
+ 'vacuumdb', '--analyze-only', '--dry-run',
+ '--missing-stats-only', '-t',
+ 'regression_vacuumdb_test', 'postgres'
+ ],
+ qr/statement:\ ANALYZE/sx,
+ '--missing-stats-only --dry-run');
$node->issues_sql_like(
[
'vacuumdb', '--analyze-only',
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 6783c843637..0bc443be348 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -59,6 +59,7 @@ main(int argc, char *argv[])
{"no-process-main", no_argument, NULL, 12},
{"buffer-usage-limit", required_argument, NULL, 13},
{"missing-stats-only", no_argument, NULL, 14},
+ {"dry-run", no_argument, NULL, 15},
{NULL, 0, NULL, 0}
};
@@ -207,6 +208,9 @@ main(int argc, char *argv[])
case 14:
vacopts.missing_stats_only = true;
break;
+ case 15:
+ vacopts.dry_run = true;
+ break;
default:
/* getopt_long already emitted a complaint */
pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -303,6 +307,10 @@ main(int argc, char *argv[])
pg_fatal("cannot use the \"%s\" option without \"%s\" or \"%s\"",
"missing-stats-only", "analyze-only", "analyze-in-stages");
+ if (vacopts.dry_run && !vacopts.quiet)
+ pg_log_info("Executing in dry-run mode.\n"
+ "No commands will be sent to the server.");
+
ret = vacuuming_main(&cparams, dbname, maintenance_db, &vacopts,
&objects, tbl_count,
concurrentCons,
@@ -345,6 +353,7 @@ help(const char *progname)
printf(_(" --buffer-usage-limit=SIZE size of ring buffer used for vacuum\n"));
printf(_(" -d, --dbname=DBNAME database to vacuum\n"));
printf(_(" --disable-page-skipping disable all page-skipping behavior\n"));
+ printf(_(" --dry-run show the commands that would be sent to the server\n"));
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"));
diff --git a/src/bin/scripts/vacuuming.c b/src/bin/scripts/vacuuming.c
index 4f665c9f7d6..5d2d8a64961 100644
--- a/src/bin/scripts/vacuuming.c
+++ b/src/bin/scripts/vacuuming.c
@@ -42,8 +42,9 @@ static SimpleStringList *retrieve_objects(PGconn *conn,
static void free_retrieved_objects(SimpleStringList *list);
static void prepare_vacuum_command(PGconn *conn, PQExpBuffer sql,
vacuumingOptions *vacopts, const char *table);
-static void run_vacuum_command(PGconn *conn, vacuumingOptions *vacopts,
- const char *sql, const char *table);
+static void run_vacuum_command(ParallelSlot *free_slot,
+ vacuumingOptions *vacopts, const char *sql,
+ const char *table);
/*
* Executes vacuum/analyze as indicated. Returns 0 if the plan is carried
@@ -340,7 +341,11 @@ vacuum_one_database(ConnParams *cparams,
if (vacopts->mode == MODE_ANALYZE_IN_STAGES)
{
initcmd = stage_commands[stage];
- executeCommand(conn, initcmd, vacopts->echo);
+
+ if (vacopts->dry_run)
+ printf("%s\n", initcmd);
+ else
+ executeCommand(conn, initcmd, vacopts->echo);
}
else
initcmd = NULL;
@@ -383,7 +388,7 @@ vacuum_one_database(ConnParams *cparams,
* through ParallelSlotsGetIdle.
*/
ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
- run_vacuum_command(free_slot->connection, vacopts, sql.data, tabname);
+ run_vacuum_command(free_slot, vacopts, sql.data, tabname);
cell = cell->next;
} while (cell != NULL);
@@ -407,7 +412,7 @@ vacuum_one_database(ConnParams *cparams,
}
ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
- run_vacuum_command(free_slot->connection, vacopts, cmd, NULL);
+ run_vacuum_command(free_slot, vacopts, cmd, NULL);
if (!ParallelSlotsWaitCompletion(sa))
ret = EXIT_FAILURE; /* error already reported by handler */
@@ -995,20 +1000,25 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql,
/*
* Send a vacuum/analyze command to the server, returning after sending the
- * command.
+ * command. If dry_run is true, the command is printed but not sent to the
+ * server.
*
* Any errors during command execution are reported to stderr.
*/
static void
-run_vacuum_command(PGconn *conn, vacuumingOptions *vacopts,
+run_vacuum_command(ParallelSlot *free_slot, vacuumingOptions *vacopts,
const char *sql, const char *table)
{
- bool status;
+ bool status = true;
+ PGconn *conn = free_slot->connection;
- if (vacopts->echo)
+ if (vacopts->echo || vacopts->dry_run)
printf("%s\n", sql);
- status = PQsendQuery(conn, sql) == 1;
+ if (vacopts->dry_run)
+ ParallelSlotSetIdle(free_slot);
+ else
+ status = PQsendQuery(conn, sql) == 1;
if (!status)
{
diff --git a/src/bin/scripts/vacuuming.h b/src/bin/scripts/vacuuming.h
index 90db4fa1a64..586b6caa3d6 100644
--- a/src/bin/scripts/vacuuming.h
+++ b/src/bin/scripts/vacuuming.h
@@ -53,6 +53,7 @@ typedef struct vacuumingOptions
bool missing_stats_only;
bool echo;
bool quiet;
+ bool dry_run;
} vacuumingOptions;
/* Valid values for vacuumingOptions->objfilter */