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