Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * vacuumdb
4 : *
5 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
6 : * Portions Copyright (c) 1994, Regents of the University of California
7 : *
8 : * src/bin/scripts/vacuumdb.c
9 : *
10 : *-------------------------------------------------------------------------
11 : */
12 :
13 : #include "postgres_fe.h"
14 :
15 : #include <limits.h>
16 :
17 : #include "common.h"
18 : #include "common/logging.h"
19 : #include "fe_utils/option_utils.h"
20 : #include "vacuuming.h"
21 :
22 : static void help(const char *progname);
23 : static void check_objfilter(bits32 objfilter);
24 :
25 :
26 : int
27 0 : main(int argc, char *argv[])
28 : {
29 : static struct option long_options[] = {
30 : {"host", required_argument, NULL, 'h'},
31 : {"port", required_argument, NULL, 'p'},
32 : {"username", required_argument, NULL, 'U'},
33 : {"no-password", no_argument, NULL, 'w'},
34 : {"password", no_argument, NULL, 'W'},
35 : {"echo", no_argument, NULL, 'e'},
36 : {"quiet", no_argument, NULL, 'q'},
37 : {"dbname", required_argument, NULL, 'd'},
38 : {"analyze", no_argument, NULL, 'z'},
39 : {"analyze-only", no_argument, NULL, 'Z'},
40 : {"freeze", no_argument, NULL, 'F'},
41 : {"all", no_argument, NULL, 'a'},
42 : {"table", required_argument, NULL, 't'},
43 : {"full", no_argument, NULL, 'f'},
44 : {"verbose", no_argument, NULL, 'v'},
45 : {"jobs", required_argument, NULL, 'j'},
46 : {"parallel", required_argument, NULL, 'P'},
47 : {"schema", required_argument, NULL, 'n'},
48 : {"exclude-schema", required_argument, NULL, 'N'},
49 : {"maintenance-db", required_argument, NULL, 2},
50 : {"analyze-in-stages", no_argument, NULL, 3},
51 : {"disable-page-skipping", no_argument, NULL, 4},
52 : {"skip-locked", no_argument, NULL, 5},
53 : {"min-xid-age", required_argument, NULL, 6},
54 : {"min-mxid-age", required_argument, NULL, 7},
55 : {"no-index-cleanup", no_argument, NULL, 8},
56 : {"force-index-cleanup", no_argument, NULL, 9},
57 : {"no-truncate", no_argument, NULL, 10},
58 : {"no-process-toast", no_argument, NULL, 11},
59 : {"no-process-main", no_argument, NULL, 12},
60 : {"buffer-usage-limit", required_argument, NULL, 13},
61 : {"missing-stats-only", no_argument, NULL, 14},
62 : {"dry-run", no_argument, NULL, 15},
63 : {NULL, 0, NULL, 0}
64 : };
65 :
66 0 : const char *progname;
67 0 : int optindex;
68 0 : int c;
69 0 : const char *dbname = NULL;
70 0 : const char *maintenance_db = NULL;
71 0 : ConnParams cparams;
72 0 : vacuumingOptions vacopts;
73 0 : SimpleStringList objects = {NULL, NULL};
74 0 : int concurrentCons = 1;
75 0 : unsigned int tbl_count = 0;
76 0 : int ret;
77 :
78 : /* initialize options */
79 0 : memset(&vacopts, 0, sizeof(vacopts));
80 0 : vacopts.parallel_workers = -1;
81 0 : vacopts.do_truncate = true;
82 0 : vacopts.process_main = true;
83 0 : vacopts.process_toast = true;
84 :
85 : /* the same for connection parameters */
86 0 : memset(&cparams, 0, sizeof(cparams));
87 0 : cparams.prompt_password = TRI_DEFAULT;
88 :
89 0 : pg_logging_init(argv[0]);
90 0 : progname = get_progname(argv[0]);
91 0 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
92 :
93 0 : handle_help_version_opts(argc, argv, "vacuumdb", help);
94 :
95 0 : while ((c = getopt_long(argc, argv, "ad:efFh:j:n:N:p:P:qt:U:vwWzZ",
96 0 : long_options, &optindex)) != -1)
97 : {
98 0 : switch (c)
99 : {
100 : case 'a':
101 0 : vacopts.objfilter |= OBJFILTER_ALL_DBS;
102 0 : break;
103 : case 'd':
104 0 : vacopts.objfilter |= OBJFILTER_DATABASE;
105 0 : dbname = pg_strdup(optarg);
106 0 : break;
107 : case 'e':
108 0 : vacopts.echo = true;
109 0 : break;
110 : case 'f':
111 0 : vacopts.full = true;
112 0 : break;
113 : case 'F':
114 0 : vacopts.freeze = true;
115 0 : break;
116 : case 'h':
117 0 : cparams.pghost = pg_strdup(optarg);
118 0 : break;
119 : case 'j':
120 0 : if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
121 : &concurrentCons))
122 0 : exit(1);
123 0 : break;
124 : case 'n':
125 0 : vacopts.objfilter |= OBJFILTER_SCHEMA;
126 0 : simple_string_list_append(&objects, optarg);
127 0 : break;
128 : case 'N':
129 0 : vacopts.objfilter |= OBJFILTER_SCHEMA_EXCLUDE;
130 0 : simple_string_list_append(&objects, optarg);
131 0 : break;
132 : case 'p':
133 0 : cparams.pgport = pg_strdup(optarg);
134 0 : break;
135 : case 'P':
136 0 : if (!option_parse_int(optarg, "-P/--parallel", 0, INT_MAX,
137 0 : &vacopts.parallel_workers))
138 0 : exit(1);
139 0 : break;
140 : case 'q':
141 0 : vacopts.quiet = true;
142 0 : break;
143 : case 't':
144 0 : vacopts.objfilter |= OBJFILTER_TABLE;
145 0 : simple_string_list_append(&objects, optarg);
146 0 : tbl_count++;
147 0 : break;
148 : case 'U':
149 0 : cparams.pguser = pg_strdup(optarg);
150 0 : break;
151 : case 'v':
152 0 : vacopts.verbose = true;
153 0 : break;
154 : case 'w':
155 0 : cparams.prompt_password = TRI_NO;
156 0 : break;
157 : case 'W':
158 0 : cparams.prompt_password = TRI_YES;
159 0 : break;
160 : case 'z':
161 0 : vacopts.and_analyze = true;
162 0 : break;
163 : case 'Z':
164 : /* if analyze-in-stages is given, don't override it */
165 0 : if (vacopts.mode != MODE_ANALYZE_IN_STAGES)
166 0 : vacopts.mode = MODE_ANALYZE;
167 0 : break;
168 : case 2:
169 0 : maintenance_db = pg_strdup(optarg);
170 0 : break;
171 : case 3:
172 0 : vacopts.mode = MODE_ANALYZE_IN_STAGES;
173 0 : break;
174 : case 4:
175 0 : vacopts.disable_page_skipping = true;
176 0 : break;
177 : case 5:
178 0 : vacopts.skip_locked = true;
179 0 : break;
180 : case 6:
181 0 : if (!option_parse_int(optarg, "--min-xid-age", 1, INT_MAX,
182 0 : &vacopts.min_xid_age))
183 0 : exit(1);
184 0 : break;
185 : case 7:
186 0 : if (!option_parse_int(optarg, "--min-mxid-age", 1, INT_MAX,
187 0 : &vacopts.min_mxid_age))
188 0 : exit(1);
189 0 : break;
190 : case 8:
191 0 : vacopts.no_index_cleanup = true;
192 0 : break;
193 : case 9:
194 0 : vacopts.force_index_cleanup = true;
195 0 : break;
196 : case 10:
197 0 : vacopts.do_truncate = false;
198 0 : break;
199 : case 11:
200 0 : vacopts.process_toast = false;
201 0 : break;
202 : case 12:
203 0 : vacopts.process_main = false;
204 0 : break;
205 : case 13:
206 0 : vacopts.buffer_usage_limit = escape_quotes(optarg);
207 0 : break;
208 : case 14:
209 0 : vacopts.missing_stats_only = true;
210 0 : break;
211 : case 15:
212 0 : vacopts.dry_run = true;
213 0 : break;
214 : default:
215 : /* getopt_long already emitted a complaint */
216 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
217 0 : exit(1);
218 : }
219 : }
220 :
221 : /*
222 : * Non-option argument specifies database name as long as it wasn't
223 : * already specified with -d / --dbname
224 : */
225 0 : if (optind < argc && dbname == NULL)
226 : {
227 0 : vacopts.objfilter |= OBJFILTER_DATABASE;
228 0 : dbname = argv[optind];
229 0 : optind++;
230 0 : }
231 :
232 0 : if (optind < argc)
233 : {
234 0 : pg_log_error("too many command-line arguments (first is \"%s\")",
235 : argv[optind]);
236 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
237 0 : exit(1);
238 : }
239 :
240 : /*
241 : * Validate the combination of filters specified in the command-line
242 : * options.
243 : */
244 0 : check_objfilter(vacopts.objfilter);
245 :
246 0 : if (vacopts.mode == MODE_ANALYZE ||
247 0 : vacopts.mode == MODE_ANALYZE_IN_STAGES)
248 : {
249 0 : if (vacopts.full)
250 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
251 : "full");
252 0 : if (vacopts.freeze)
253 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
254 : "freeze");
255 0 : if (vacopts.disable_page_skipping)
256 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
257 : "disable-page-skipping");
258 0 : if (vacopts.no_index_cleanup)
259 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
260 : "no-index-cleanup");
261 0 : if (vacopts.force_index_cleanup)
262 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
263 : "force-index-cleanup");
264 0 : if (!vacopts.do_truncate)
265 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
266 : "no-truncate");
267 0 : if (!vacopts.process_main)
268 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
269 : "no-process-main");
270 0 : if (!vacopts.process_toast)
271 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
272 : "no-process-toast");
273 : /* allow 'and_analyze' with 'analyze_only' */
274 0 : }
275 :
276 : /* Prohibit full and analyze_only options with parallel option */
277 0 : if (vacopts.parallel_workers >= 0)
278 : {
279 0 : if (vacopts.mode == MODE_ANALYZE ||
280 0 : vacopts.mode == MODE_ANALYZE_IN_STAGES)
281 0 : pg_fatal("cannot use the \"%s\" option when performing only analyze",
282 : "parallel");
283 0 : if (vacopts.full)
284 0 : pg_fatal("cannot use the \"%s\" option when performing full vacuum",
285 : "parallel");
286 0 : }
287 :
288 : /* Prohibit --no-index-cleanup and --force-index-cleanup together */
289 0 : if (vacopts.no_index_cleanup && vacopts.force_index_cleanup)
290 0 : pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
291 : "no-index-cleanup", "force-index-cleanup");
292 :
293 : /*
294 : * buffer-usage-limit is not allowed with VACUUM FULL unless ANALYZE is
295 : * included too.
296 : */
297 0 : if (vacopts.buffer_usage_limit && vacopts.full && !vacopts.and_analyze)
298 0 : pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
299 : "buffer-usage-limit", "full");
300 :
301 : /*
302 : * Prohibit --missing-stats-only without --analyze-only or
303 : * --analyze-in-stages.
304 : */
305 0 : if (vacopts.missing_stats_only && (vacopts.mode != MODE_ANALYZE &&
306 0 : vacopts.mode != MODE_ANALYZE_IN_STAGES))
307 0 : pg_fatal("cannot use the \"%s\" option without \"%s\" or \"%s\"",
308 : "missing-stats-only", "analyze-only", "analyze-in-stages");
309 :
310 0 : if (vacopts.dry_run && !vacopts.quiet)
311 0 : pg_log_info("Executing in dry-run mode.\n"
312 : "No commands will be sent to the server.");
313 :
314 0 : ret = vacuuming_main(&cparams, dbname, maintenance_db, &vacopts,
315 0 : &objects, tbl_count,
316 0 : concurrentCons,
317 0 : progname);
318 0 : exit(ret);
319 : }
320 :
321 : /*
322 : * Verify that the filters used at command line are compatible.
323 : */
324 : void
325 0 : check_objfilter(bits32 objfilter)
326 : {
327 0 : if ((objfilter & OBJFILTER_ALL_DBS) &&
328 0 : (objfilter & OBJFILTER_DATABASE))
329 0 : pg_fatal("cannot vacuum all databases and a specific one at the same time");
330 :
331 0 : if ((objfilter & OBJFILTER_TABLE) &&
332 0 : (objfilter & OBJFILTER_SCHEMA))
333 0 : pg_fatal("cannot vacuum all tables in schema(s) and specific table(s) at the same time");
334 :
335 0 : if ((objfilter & OBJFILTER_TABLE) &&
336 0 : (objfilter & OBJFILTER_SCHEMA_EXCLUDE))
337 0 : pg_fatal("cannot vacuum specific table(s) and exclude schema(s) at the same time");
338 :
339 0 : if ((objfilter & OBJFILTER_SCHEMA) &&
340 0 : (objfilter & OBJFILTER_SCHEMA_EXCLUDE))
341 0 : pg_fatal("cannot vacuum all tables in schema(s) and exclude schema(s) at the same time");
342 0 : }
343 :
344 :
345 : static void
346 0 : help(const char *progname)
347 : {
348 0 : printf(_("%s cleans and analyzes a PostgreSQL database.\n\n"), progname);
349 0 : printf(_("Usage:\n"));
350 0 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
351 0 : printf(_("\nOptions:\n"));
352 0 : printf(_(" -a, --all vacuum all databases\n"));
353 0 : printf(_(" --buffer-usage-limit=SIZE size of ring buffer used for vacuum\n"));
354 0 : printf(_(" -d, --dbname=DBNAME database to vacuum\n"));
355 0 : printf(_(" --disable-page-skipping disable all page-skipping behavior\n"));
356 0 : printf(_(" --dry-run show the commands that would be sent to the server\n"));
357 0 : printf(_(" -e, --echo show the commands being sent to the server\n"));
358 0 : printf(_(" -f, --full do full vacuuming\n"));
359 0 : printf(_(" -F, --freeze freeze row transaction information\n"));
360 0 : printf(_(" --force-index-cleanup always remove index entries that point to dead tuples\n"));
361 0 : printf(_(" -j, --jobs=NUM use this many concurrent connections to vacuum\n"));
362 0 : printf(_(" --min-mxid-age=MXID_AGE minimum multixact ID age of tables to vacuum\n"));
363 0 : printf(_(" --min-xid-age=XID_AGE minimum transaction ID age of tables to vacuum\n"));
364 0 : printf(_(" --missing-stats-only only analyze relations with missing statistics\n"));
365 0 : printf(_(" --no-index-cleanup don't remove index entries that point to dead tuples\n"));
366 0 : printf(_(" --no-process-main skip the main relation\n"));
367 0 : printf(_(" --no-process-toast skip the TOAST table associated with the table to vacuum\n"));
368 0 : printf(_(" --no-truncate don't truncate empty pages at the end of the table\n"));
369 0 : printf(_(" -n, --schema=SCHEMA vacuum tables in the specified schema(s) only\n"));
370 0 : printf(_(" -N, --exclude-schema=SCHEMA do not vacuum tables in the specified schema(s)\n"));
371 0 : printf(_(" -P, --parallel=PARALLEL_WORKERS use this many background workers for vacuum, if available\n"));
372 0 : printf(_(" -q, --quiet don't write any messages\n"));
373 0 : printf(_(" --skip-locked skip relations that cannot be immediately locked\n"));
374 0 : printf(_(" -t, --table='TABLE[(COLUMNS)]' vacuum specific table(s) only\n"));
375 0 : printf(_(" -v, --verbose write a lot of output\n"));
376 0 : printf(_(" -V, --version output version information, then exit\n"));
377 0 : printf(_(" -z, --analyze update optimizer statistics\n"));
378 0 : printf(_(" -Z, --analyze-only only update optimizer statistics; no vacuum\n"));
379 0 : printf(_(" --analyze-in-stages only update optimizer statistics, in multiple\n"
380 : " stages for faster results; no vacuum\n"));
381 0 : printf(_(" -?, --help show this help, then exit\n"));
382 0 : printf(_("\nConnection options:\n"));
383 0 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
384 0 : printf(_(" -p, --port=PORT database server port\n"));
385 0 : printf(_(" -U, --username=USERNAME user name to connect as\n"));
386 0 : printf(_(" -w, --no-password never prompt for password\n"));
387 0 : printf(_(" -W, --password force password prompt\n"));
388 0 : printf(_(" --maintenance-db=DBNAME alternate maintenance database\n"));
389 0 : printf(_("\nRead the description of the SQL command VACUUM for details.\n"));
390 0 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
391 0 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
392 0 : }
|