Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vSffm-003DKg-0O for pgsql-hackers@arkaria.postgresql.org; Mon, 08 Dec 2025 18:09:22 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vSffk-000hVE-2F for pgsql-hackers@arkaria.postgresql.org; Mon, 08 Dec 2025 18:09:21 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vSffk-000hV6-0u for pgsql-hackers@lists.postgresql.org; Mon, 08 Dec 2025 18:09:20 +0000 Received: from mail-oi1-x231.google.com ([2607:f8b0:4864:20::231]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vSffi-003tJf-0A for pgsql-hackers@postgresql.org; Mon, 08 Dec 2025 18:09:20 +0000 Received: by mail-oi1-x231.google.com with SMTP id 5614622812f47-450ccefe6ffso2910359b6e.1 for ; Mon, 08 Dec 2025 10:09:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1765217355; x=1765822155; darn=postgresql.org; h=in-reply-to:content-transfer-encoding:content-disposition :mime-version:references:message-id:subject:cc:to:from:date:from:to :cc:subject:date:message-id:reply-to; bh=07Zq/uV45z/ZJxWvmRjJTDCpmEIwuz0FSaFtAtg16kc=; b=FNc7w07gM8JPHe3GkicnYv/UgVfYU4/IlcE22K+JITnaaME6TuZCtMiLzkju1lgUhz KtEmep6z2qrwGwcOESzyTVB+Tarz/ivxLfjh1HCgzv3xFWODhnI2JsYDaCjdcwl4XtXl 34+nB8rlca+3BjSrJiqiEQZOCSMLqlXlE2R3tugsIX92qAQMrobTPruZYZhZlH16n8yp /bu1VOKKLKTxbaOfjBXKpG3oyDk/d252bhTDUei+Dl5W9KHK3SPtvZINx7WpZ7BAjBlb hSQWOI6kZ9Q052ryB5y105toOXBHUCyOgj5SmfHCULfPvv0e5VRibXOVW4pTHnRoFB29 no2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765217355; x=1765822155; h=in-reply-to:content-transfer-encoding:content-disposition :mime-version:references:message-id:subject:cc:to:from:date:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=07Zq/uV45z/ZJxWvmRjJTDCpmEIwuz0FSaFtAtg16kc=; b=N5DE3sgAKkr1Z1ZCz137n3jWPdjx4rrVy8DyA0tdRj9nUmdjajbFWp9xX2DTzS7V6e kF1OoSlqz2j6P/LzrWFPE+biZQYZLQstit5tI6PK6KjOly9iUkJGY1rzV1DeFcKS5gjh m+9pdNYwslBKNymOkSc3JA5KHsWIr5Ju7OaQNYNcpAQZ58eEVuiHLssg45TSSmizhJ4v ++CXLCgAE95jO8dZckgDxdEPnXJmRWz0Ni0WcmADxaJp2M71vMB6GxdB3xn/jp4P1OTF PUr+0kmWHz+Om7SnMcELY1n+wb6Mu0nPRPTQlEBjT9LPntx6kGqV8v038qcZ/vHbvmGx S60g== X-Forwarded-Encrypted: i=1; AJvYcCULyIf7rvD9HmyKwZjy35BTJXpQNvPotXKhYkEn7od9PC+kVFc+B7bUkyGcFeIr08Oar+DczyLu/RPgY+Ip@postgresql.org X-Gm-Message-State: AOJu0YxZjcmq88dhTf8HhasbFoVpA63WIR6sW+R30F4j2k8c75F560Fa GvJR+GJj/GJHF1L6fnv4La/XiOzSeViLE0hM6mV6WAVSX7xJHaqxepz5 X-Gm-Gg: ASbGncu10at/YMLcodJD+UaODG5IYPB2i3n7I+s+DJLDEMfDVNNBW7pDtQYVPhyq+uC Fi8MvrtGRz6wTpYB/2+hI0ytOXbM8wLaE6mTO23piZO53LNkecGivH9MUujC8HU785cMWyXDWvT C6f8qenUDslLCGCPHRAy3c+0HtnPIip2eWy7MXa5LfjlYLU4vQmi3dS4OLPsTub1nmFOhQEAFz0 zemmLEbd2wWUmrbpAoH++6FXm/FVhXsI0h8T/uTtjApMURQjQotd+OhofhkR6ttZTPlQ2P4HHJW drwsK1vqyy8vPdaHOPLbqO88Co/wxf9+/ve0vC/shyaQ9sHe4PRsv2i6lL0ID+X9oPDGumTIHJu bRAzReUJLZOsFawshyy1PAzZ2Q1XWTuKftN4XqNBwqDFo0PkBIDPRtOyHaXjNHxwFYR2xbEXlGS OsXThndbtGqXi+PfAx5J506vMo5Jb6MKpCQ0y2oC+PU8Ue/DTKpp1/dsAQT/k9YTZLRav0/D8Id h2V X-Google-Smtp-Source: AGHT+IGcie7V6k7L2iawx5A4JqRm6YLMaxE3GU12aoRRppkL5P7i5fvvykn5K2CRxaSdDJnAg4iX6Q== X-Received: by 2002:a05:6808:8303:b0:44d:9f05:7159 with SMTP id 5614622812f47-45572c093f3mr119045b6e.29.1765217355122; Mon, 08 Dec 2025 10:09:15 -0800 (PST) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-3f50b5d39cbsm7783954fac.20.2025.12.08.10.09.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Dec 2025 10:09:14 -0800 (PST) Date: Mon, 8 Dec 2025 12:09:12 -0600 From: Nathan Bossart To: Chao Li Cc: Corey Huinker , pgsql-hackers@postgresql.org Subject: Re: vacuumdb: add --dry-run Message-ID: References: <3DB353E5-944D-4E18-9DA2-4151F44AA59B@gmail.com> <6460D2FC-A909-4C21-A1AE-6C94A22060ED@gmail.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="jUzroBV5RYIlyQib" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <6460D2FC-A909-4C21-A1AE-6C94A22060ED@gmail.com> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --jUzroBV5RYIlyQib Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit On Sat, Dec 06, 2025 at 07:48:22AM +0800, Chao Li wrote: > I just reviewed v5, and overall looks very good patch quality. Just a few > nit comments on 0001 and 0003. I've attached an updated patch set. After giving the "dry-run" messages another look, I think we should just print a note at the very beginning of vacuumdb execution and leave it at that. The per-database messages weren't translator friendly and IMHO didn't add much, and the "-- not executed" comments were noisy and didn't reflect the commands that would've been sent to the server. > Now echo and print are moved into vacopts and their default values are > false. Here, memset() have properly initialized their values. But this > piece of code still explicitly set boolean values to vacopts fields. So, > to make it consistent, I feel we can also add explicit assignments to > echo and print here, or remove those “false” assignments. This is not a > correctness issue, just to keep in a consistent style. We are already pretty inconsistent about this. If anything, I think we should do the opposite, i.e., remove any unnecessary initializations to 0/false/NULL. The memset() makes those redundant and should suffice in most cases. > * As run_vacuum_command() takes both echo and dry_run, and both of them > are defined in vcaopts, why not change this function to take a const > vcaopts * instead of two bools? > > * The function comment needs to be updated. Now it won’t always send a > command to server, with “dry_run”, it behaves differently. Done. > ``` > + if (vacopts.dry_run) > + pg_log_info("Executing in dry-run mode.”); > ``` > > Feels like “Running” is better than “Executing”. Done. -- nathan --jUzroBV5RYIlyQib Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v6-0001-vacuumdb-Move-some-options-to-vacuumingOptions-st.patch From f7629cec0198cdd2f4c00a41f271cf576eb66967 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Wed, 3 Dec 2025 16:12:37 -0600 Subject: [PATCH v6 1/3] vacuumdb: Move some options to vacuumingOptions struct. TODO Reviewed-by: Corey Huinker Reviewed-by: Kirill Reshke Reviewed-by: Chao Li Discussion: https://postgr.es/m/CADkLM%3DckHkX7Of5SrK7g0LokPUwJ%3Dkk8JU1GXGF5pZ1eBVr0%3DQ%40mail.gmail.com --- src/bin/scripts/vacuumdb.c | 12 ++------ src/bin/scripts/vacuuming.c | 61 ++++++++++++++++++------------------- src/bin/scripts/vacuuming.h | 4 ++- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index e117dac2242..6783c843637 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -68,8 +68,6 @@ main(int argc, char *argv[]) const char *dbname = NULL; const char *maintenance_db = NULL; ConnParams cparams; - bool echo = false; - bool quiet = false; vacuumingOptions vacopts; SimpleStringList objects = {NULL, NULL}; int concurrentCons = 1; @@ -78,11 +76,7 @@ main(int argc, char *argv[]) /* initialize options */ memset(&vacopts, 0, sizeof(vacopts)); - vacopts.objfilter = 0; /* no filter */ vacopts.parallel_workers = -1; - vacopts.buffer_usage_limit = NULL; - vacopts.no_index_cleanup = false; - vacopts.force_index_cleanup = false; vacopts.do_truncate = true; vacopts.process_main = true; vacopts.process_toast = true; @@ -110,7 +104,7 @@ main(int argc, char *argv[]) dbname = pg_strdup(optarg); break; case 'e': - echo = true; + vacopts.echo = true; break; case 'f': vacopts.full = true; @@ -143,7 +137,7 @@ main(int argc, char *argv[]) exit(1); break; case 'q': - quiet = true; + vacopts.quiet = true; break; case 't': vacopts.objfilter |= OBJFILTER_TABLE; @@ -312,7 +306,7 @@ main(int argc, char *argv[]) ret = vacuuming_main(&cparams, dbname, maintenance_db, &vacopts, &objects, tbl_count, concurrentCons, - progname, echo, quiet); + progname); exit(ret); } diff --git a/src/bin/scripts/vacuuming.c b/src/bin/scripts/vacuuming.c index f836f21fb03..74a1f20e0f3 100644 --- a/src/bin/scripts/vacuuming.c +++ b/src/bin/scripts/vacuuming.c @@ -30,21 +30,20 @@ static int vacuum_one_database(ConnParams *cparams, SimpleStringList *objects, SimpleStringList **found_objs, int concurrentCons, - const char *progname, bool echo, bool quiet); + const char *progname); static int vacuum_all_databases(ConnParams *cparams, vacuumingOptions *vacopts, SimpleStringList *objects, int concurrentCons, - const char *progname, bool echo, bool quiet); + const char *progname); static SimpleStringList *retrieve_objects(PGconn *conn, vacuumingOptions *vacopts, - SimpleStringList *objects, - bool echo); + SimpleStringList *objects); 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, const char *sql, bool echo, - const char *table); +static void run_vacuum_command(PGconn *conn, vacuumingOptions *vacopts, + const char *sql, const char *table); /* * Executes vacuum/analyze as indicated. Returns 0 if the plan is carried @@ -56,7 +55,7 @@ vacuuming_main(ConnParams *cparams, const char *dbname, const char *maintenance_db, vacuumingOptions *vacopts, SimpleStringList *objects, unsigned int tbl_count, int concurrentCons, - const char *progname, bool echo, bool quiet) + const char *progname) { setup_cancel_handler(NULL); @@ -71,7 +70,7 @@ vacuuming_main(ConnParams *cparams, const char *dbname, return vacuum_all_databases(cparams, vacopts, objects, concurrentCons, - progname, echo, quiet); + progname); } else { @@ -100,7 +99,7 @@ vacuuming_main(ConnParams *cparams, const char *dbname, objects, vacopts->missing_stats_only ? &found_objs : NULL, concurrentCons, - progname, echo, quiet); + progname); if (ret != 0) { free_retrieved_objects(found_objs); @@ -116,7 +115,7 @@ vacuuming_main(ConnParams *cparams, const char *dbname, ANALYZE_NO_STAGE, objects, NULL, concurrentCons, - progname, echo, quiet); + progname); } } @@ -167,7 +166,7 @@ vacuum_one_database(ConnParams *cparams, SimpleStringList *objects, SimpleStringList **found_objs, int concurrentCons, - const char *progname, bool echo, bool quiet) + const char *progname) { PQExpBufferData sql; PGconn *conn; @@ -192,7 +191,7 @@ vacuum_one_database(ConnParams *cparams, Assert(stage == ANALYZE_NO_STAGE || (stage >= 0 && stage < ANALYZE_NUM_STAGES)); - conn = connectDatabase(cparams, progname, echo, false, true); + conn = connectDatabase(cparams, progname, vacopts->echo, false, true); if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600) { @@ -281,7 +280,7 @@ vacuum_one_database(ConnParams *cparams, /* skip_database_stats is used automatically if server supports it */ vacopts->skip_database_stats = (PQserverVersion(conn) >= 160000); - if (!quiet) + if (!vacopts->quiet) { if (vacopts->mode == MODE_ANALYZE_IN_STAGES) printf(_("%s: processing database \"%s\": %s\n"), @@ -302,7 +301,7 @@ vacuum_one_database(ConnParams *cparams, retobjs = *found_objs; else { - retobjs = retrieve_objects(conn, vacopts, objects, echo); + retobjs = retrieve_objects(conn, vacopts, objects); if (found_objs) *found_objs = retobjs; else @@ -341,7 +340,7 @@ vacuum_one_database(ConnParams *cparams, if (vacopts->mode == MODE_ANALYZE_IN_STAGES) { initcmd = stage_commands[stage]; - executeCommand(conn, initcmd, echo); + executeCommand(conn, initcmd, vacopts->echo); } else initcmd = NULL; @@ -351,7 +350,8 @@ vacuum_one_database(ConnParams *cparams, * for the first slot. If not in parallel mode, the first slot in the * array contains the connection. */ - sa = ParallelSlotsSetup(concurrentCons, cparams, progname, echo, initcmd); + sa = ParallelSlotsSetup(concurrentCons, cparams, progname, + vacopts->echo, initcmd); ParallelSlotsAdoptConn(sa, conn); initPQExpBuffer(&sql); @@ -383,8 +383,7 @@ vacuum_one_database(ConnParams *cparams, * through ParallelSlotsGetIdle. */ ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL); - run_vacuum_command(free_slot->connection, sql.data, - echo, tabname); + run_vacuum_command(free_slot->connection, vacopts, sql.data, tabname); cell = cell->next; } while (cell != NULL); @@ -408,7 +407,7 @@ vacuum_one_database(ConnParams *cparams, } ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL); - run_vacuum_command(free_slot->connection, cmd, echo, NULL); + run_vacuum_command(free_slot->connection, vacopts, cmd, NULL); if (!ParallelSlotsWaitCompletion(sa)) ret = EXIT_FAILURE; /* error already reported by handler */ @@ -436,17 +435,17 @@ vacuum_all_databases(ConnParams *cparams, vacuumingOptions *vacopts, SimpleStringList *objects, int concurrentCons, - const char *progname, bool echo, bool quiet) + const char *progname) { int ret = EXIT_SUCCESS; PGconn *conn; PGresult *result; int numdbs; - conn = connectMaintenanceDatabase(cparams, progname, echo); + conn = connectMaintenanceDatabase(cparams, progname, vacopts->echo); result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn AND datconnlimit <> -2 ORDER BY 1;", - echo); + vacopts->echo); numdbs = PQntuples(result); PQfinish(conn); @@ -474,7 +473,7 @@ vacuum_all_databases(ConnParams *cparams, objects, vacopts->missing_stats_only ? &found_objs[i] : NULL, concurrentCons, - progname, echo, quiet); + progname); if (ret != EXIT_SUCCESS) break; } @@ -499,7 +498,7 @@ vacuum_all_databases(ConnParams *cparams, objects, NULL, concurrentCons, - progname, echo, quiet); + progname); if (ret != EXIT_SUCCESS) break; } @@ -524,7 +523,7 @@ vacuum_all_databases(ConnParams *cparams, */ static SimpleStringList * retrieve_objects(PGconn *conn, vacuumingOptions *vacopts, - SimpleStringList *objects, bool echo) + SimpleStringList *objects) { PQExpBufferData buf; PQExpBufferData catalog_query; @@ -776,10 +775,10 @@ retrieve_objects(PGconn *conn, vacuumingOptions *vacopts, * query for consistency with table lookups done elsewhere by the user. */ appendPQExpBufferStr(&catalog_query, " ORDER BY c.relpages DESC;"); - executeCommand(conn, "RESET search_path;", echo); - res = executeQuery(conn, catalog_query.data, echo); + executeCommand(conn, "RESET search_path;", vacopts->echo); + res = executeQuery(conn, catalog_query.data, vacopts->echo); termPQExpBuffer(&catalog_query); - PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo)); + PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, vacopts->echo)); /* * Build qualified identifiers for each table, including the column list @@ -1001,12 +1000,12 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql, * Any errors during command execution are reported to stderr. */ static void -run_vacuum_command(PGconn *conn, const char *sql, bool echo, - const char *table) +run_vacuum_command(PGconn *conn, vacuumingOptions *vacopts, + const char *sql, const char *table) { bool status; - if (echo) + if (vacopts->echo) printf("%s\n", sql); status = PQsendQuery(conn, sql) == 1; diff --git a/src/bin/scripts/vacuuming.h b/src/bin/scripts/vacuuming.h index 49f968b32e5..90db4fa1a64 100644 --- a/src/bin/scripts/vacuuming.h +++ b/src/bin/scripts/vacuuming.h @@ -51,6 +51,8 @@ typedef struct vacuumingOptions bool skip_database_stats; char *buffer_usage_limit; bool missing_stats_only; + bool echo; + bool quiet; } vacuumingOptions; /* Valid values for vacuumingOptions->objfilter */ @@ -65,7 +67,7 @@ extern int vacuuming_main(ConnParams *cparams, const char *dbname, SimpleStringList *objects, unsigned int tbl_count, int concurrentCons, - const char *progname, bool echo, bool quiet); + const char *progname); extern char *escape_quotes(const char *src); -- 2.39.5 (Apple Git-154) --jUzroBV5RYIlyQib Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v6-0002-Add-ParallelSlotSetIdle.patch From 355e93d9a9ff61019520f76edd690fafd0399790 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Thu, 20 Nov 2025 14:16:02 -0600 Subject: [PATCH v6 2/3] Add ParallelSlotSetIdle(). TODO Reviewed-by: Corey Huinker Reviewed-by: Kirill Reshke Reviewed-by: Chao Li slots[i].inUse = false; - ParallelSlotClearHandler(&sa->slots[i]); + ParallelSlotSetIdle(&sa->slots[i]); break; } } @@ -509,8 +508,7 @@ ParallelSlotsWaitCompletion(ParallelSlotArray *sa) if (!consumeQueryResult(&sa->slots[i])) return false; /* Mark connection as idle */ - sa->slots[i].inUse = false; - ParallelSlotClearHandler(&sa->slots[i]); + ParallelSlotSetIdle(&sa->slots[i]); } return true; diff --git a/src/include/fe_utils/parallel_slot.h b/src/include/fe_utils/parallel_slot.h index 7770a20de34..4a5fbfb3d8c 100644 --- a/src/include/fe_utils/parallel_slot.h +++ b/src/include/fe_utils/parallel_slot.h @@ -58,6 +58,13 @@ ParallelSlotClearHandler(ParallelSlot *slot) slot->handler_context = NULL; } +static inline void +ParallelSlotSetIdle(ParallelSlot *slot) +{ + slot->inUse = false; + ParallelSlotClearHandler(slot); +} + extern ParallelSlot *ParallelSlotsGetIdle(ParallelSlotArray *sa, const char *dbname); -- 2.39.5 (Apple Git-154) --jUzroBV5RYIlyQib Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v6-0003-vacuumdb-Add-dry-run.patch From df0228f6378ffa1861dd5afc2dfd3187d6fa943b Mon Sep 17 00:00:00 2001 From: Corey Huinker Date: Mon, 10 Nov 2025 14:33:41 -0500 Subject: [PATCH v6 3/3] vacuumdb: Add --dry-run. TODO Author: Corey Huinker Reviewed-by: Chao Li Reviewed-by: Kirill Reshke Discussion: https://postgr.es/m/CADkLM%3DckHkX7Of5SrK7g0LokPUwJ%3Dkk8JU1GXGF5pZ1eBVr0%3DQ%40mail.gmail.com --- doc/src/sgml/ref/vacuumdb.sgml | 10 ++++++++++ src/bin/scripts/t/100_vacuumdb.pl | 12 ++++++++++++ src/bin/scripts/vacuumdb.c | 8 ++++++++ src/bin/scripts/vacuuming.c | 30 ++++++++++++++++++++---------- src/bin/scripts/vacuuming.h | 1 + 5 files changed, 51 insertions(+), 10 deletions(-) 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..6e4c809e806 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,9 @@ 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("Running in dry-run mode."); + ret = vacuuming_main(&cparams, dbname, maintenance_db, &vacopts, &objects, tbl_count, concurrentCons, @@ -345,6 +352,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 74a1f20e0f3..73239e86e0a 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 */ -- 2.39.5 (Apple Git-154) --jUzroBV5RYIlyQib--