Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * vacuumlo.c
4 : : * This removes orphaned large objects from a database.
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * contrib/vacuumlo/vacuumlo.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres_fe.h"
16 : :
17 : : #include <sys/stat.h>
18 : : #include <fcntl.h>
19 : : #include <unistd.h>
20 : : #ifdef HAVE_TERMIOS_H
21 : : #include <termios.h>
22 : : #endif
23 : :
24 : : #include "catalog/pg_class_d.h"
25 : : #include "common/connect.h"
26 : : #include "common/logging.h"
27 : : #include "common/string.h"
28 : : #include "getopt_long.h"
29 : : #include "libpq-fe.h"
30 : : #include "pg_getopt.h"
31 : :
32 : : #define BUFSIZE 1024
33 : :
34 : : enum trivalue
35 : : {
36 : : TRI_DEFAULT,
37 : : TRI_NO,
38 : : TRI_YES,
39 : : };
40 : :
41 : : struct _param
42 : : {
43 : : char *pg_user;
44 : : enum trivalue pg_prompt;
45 : : char *pg_port;
46 : : char *pg_host;
47 : : const char *progname;
48 : : int verbose;
49 : : int dry_run;
50 : : long transaction_limit;
51 : : };
52 : :
53 : : static int vacuumlo(const char *database, const struct _param *param);
54 : : static void usage(const char *progname);
55 : :
56 : :
57 : :
58 : : /*
59 : : * This vacuums LOs of one database. It returns 0 on success, -1 on failure.
60 : : */
61 : : static int
2489 tgl@sss.pgh.pa.us 62 :UBC 0 : vacuumlo(const char *database, const struct _param *param)
63 : : {
64 : : PGconn *conn;
65 : : PGresult *res,
66 : : *res2;
67 : : char buf[BUFSIZE];
68 : : long matched;
69 : : long deleted;
70 : : int i;
71 : : bool new_pass;
4326 bruce@momjian.us 72 : 0 : bool success = true;
73 : : static char *password = NULL;
74 : :
75 : : /* Note: password can be carried over from a previous call */
1319 tgl@sss.pgh.pa.us 76 [ # # # # ]: 0 : if (param->pg_prompt == TRI_YES && !password)
77 : 0 : password = simple_prompt("Password: ", false);
78 : :
79 : : /*
80 : : * Start the connection. Loop until we have a password if requested by
81 : : * backend.
82 : : */
83 : : do
84 : : {
85 : : #define PARAMS_ARRAY_SIZE 7
86 : :
87 : : const char *keywords[PARAMS_ARRAY_SIZE];
88 : : const char *values[PARAMS_ARRAY_SIZE];
89 : :
4302 rhaas@postgresql.org 90 : 0 : keywords[0] = "host";
91 : 0 : values[0] = param->pg_host;
92 : 0 : keywords[1] = "port";
93 : 0 : values[1] = param->pg_port;
94 : 0 : keywords[2] = "user";
95 : 0 : values[2] = param->pg_user;
96 : 0 : keywords[3] = "password";
1319 tgl@sss.pgh.pa.us 97 : 0 : values[3] = password;
4302 rhaas@postgresql.org 98 : 0 : keywords[4] = "dbname";
99 : 0 : values[4] = database;
100 : 0 : keywords[5] = "fallback_application_name";
101 : 0 : values[5] = param->progname;
102 : 0 : keywords[6] = NULL;
103 : 0 : values[6] = NULL;
104 : :
105 : 0 : new_pass = false;
106 : 0 : conn = PQconnectdbParams(keywords, values, true);
5969 tgl@sss.pgh.pa.us 107 [ # # ]: 0 : if (!conn)
108 : : {
1682 michael@paquier.xyz 109 : 0 : pg_log_error("connection to database \"%s\" failed", database);
5969 tgl@sss.pgh.pa.us 110 : 0 : return -1;
111 : : }
112 : :
113 [ # # # # ]: 0 : if (PQstatus(conn) == CONNECTION_BAD &&
114 : 0 : PQconnectionNeedsPassword(conn) &&
1319 115 [ # # ]: 0 : !password &&
5526 peter_e@gmx.net 116 [ # # ]: 0 : param->pg_prompt != TRI_NO)
117 : : {
5969 tgl@sss.pgh.pa.us 118 : 0 : PQfinish(conn);
1319 119 : 0 : password = simple_prompt("Password: ", false);
5969 120 : 0 : new_pass = true;
121 : : }
122 [ # # ]: 0 : } while (new_pass);
123 : :
124 : : /* check to see that the backend connection was successfully made */
9091 bruce@momjian.us 125 [ # # ]: 0 : if (PQstatus(conn) == CONNECTION_BAD)
126 : : {
1178 tgl@sss.pgh.pa.us 127 : 0 : pg_log_error("%s", PQerrorMessage(conn));
8545 128 : 0 : PQfinish(conn);
9091 bruce@momjian.us 129 : 0 : return -1;
130 : : }
131 : :
7893 132 [ # # ]: 0 : if (param->verbose)
133 : : {
4408 tgl@sss.pgh.pa.us 134 : 0 : fprintf(stdout, "Connected to database \"%s\"\n", database);
7893 bruce@momjian.us 135 [ # # ]: 0 : if (param->dry_run)
136 : 0 : fprintf(stdout, "Test run: no large objects will be removed!\n");
137 : : }
138 : :
2239 noah@leadboat.com 139 : 0 : res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
140 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
141 : : {
1682 michael@paquier.xyz 142 : 0 : pg_log_error("failed to set search_path: %s", PQerrorMessage(conn));
7849 bruce@momjian.us 143 : 0 : PQclear(res);
144 : 0 : PQfinish(conn);
145 : 0 : return -1;
146 : : }
147 : 0 : PQclear(res);
148 : :
149 : : /*
150 : : * First we create and populate the LO temp table
151 : : */
9091 152 : 0 : buf[0] = '\0';
7508 tgl@sss.pgh.pa.us 153 : 0 : strcat(buf, "CREATE TEMP TABLE vacuum_l AS ");
5170 154 [ # # ]: 0 : if (PQserverVersion(conn) >= 90000)
5235 itagaki.takahiro@gma 155 : 0 : strcat(buf, "SELECT oid AS lo FROM pg_largeobject_metadata");
156 : : else
157 : 0 : strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject");
8545 tgl@sss.pgh.pa.us 158 : 0 : res = PQexec(conn, buf);
159 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
160 : : {
1682 michael@paquier.xyz 161 : 0 : pg_log_error("failed to create temp table: %s", PQerrorMessage(conn));
8545 tgl@sss.pgh.pa.us 162 : 0 : PQclear(res);
163 : 0 : PQfinish(conn);
164 : 0 : return -1;
165 : : }
166 : 0 : PQclear(res);
167 : :
168 : : /*
169 : : * Analyze the temp table so that planner will generate decent plans for
170 : : * the DELETEs below.
171 : : */
172 : 0 : buf[0] = '\0';
5389 bruce@momjian.us 173 : 0 : strcat(buf, "ANALYZE vacuum_l");
8545 tgl@sss.pgh.pa.us 174 : 0 : res = PQexec(conn, buf);
175 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
176 : : {
1682 michael@paquier.xyz 177 : 0 : pg_log_error("failed to vacuum temp table: %s", PQerrorMessage(conn));
8545 tgl@sss.pgh.pa.us 178 : 0 : PQclear(res);
9136 peter@retep.org.uk 179 : 0 : PQfinish(conn);
180 : 0 : return -1;
181 : : }
9091 bruce@momjian.us 182 : 0 : PQclear(res);
183 : :
184 : : /*
185 : : * Now find any candidate tables that have columns of type oid.
186 : : *
187 : : * NOTE: we ignore system tables and temp tables by the expedient of
188 : : * rejecting tables in schemas named 'pg_*'. In particular, the temp
189 : : * table formed above is ignored, and pg_largeobject will be too. If
190 : : * either of these were scanned, obviously we'd end up with nothing to
191 : : * delete...
192 : : */
193 : 0 : buf[0] = '\0';
7508 tgl@sss.pgh.pa.us 194 : 0 : strcat(buf, "SELECT s.nspname, c.relname, a.attname ");
7559 195 : 0 : strcat(buf, "FROM pg_class c, pg_attribute a, pg_namespace s, pg_type t ");
7508 196 : 0 : strcat(buf, "WHERE a.attnum > 0 AND NOT a.attisdropped ");
9091 bruce@momjian.us 197 : 0 : strcat(buf, " AND a.attrelid = c.oid ");
198 : 0 : strcat(buf, " AND a.atttypid = t.oid ");
7559 tgl@sss.pgh.pa.us 199 : 0 : strcat(buf, " AND c.relnamespace = s.oid ");
8245 inoue@tpf.co.jp 200 : 0 : strcat(buf, " AND t.typname in ('oid', 'lo') ");
2593 tgl@sss.pgh.pa.us 201 : 0 : strcat(buf, " AND c.relkind in (" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_MATVIEW) ")");
6625 202 : 0 : strcat(buf, " AND s.nspname !~ '^pg_'");
8545 203 : 0 : res = PQexec(conn, buf);
204 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
205 : : {
1682 michael@paquier.xyz 206 : 0 : pg_log_error("failed to find OID columns: %s", PQerrorMessage(conn));
8545 tgl@sss.pgh.pa.us 207 : 0 : PQclear(res);
9136 peter@retep.org.uk 208 : 0 : PQfinish(conn);
209 : 0 : return -1;
210 : : }
211 : :
9091 bruce@momjian.us 212 [ # # ]: 0 : for (i = 0; i < PQntuples(res); i++)
213 : : {
214 : : char *schema,
215 : : *table,
216 : : *field;
217 : :
7508 tgl@sss.pgh.pa.us 218 : 0 : schema = PQgetvalue(res, i, 0);
219 : 0 : table = PQgetvalue(res, i, 1);
220 : 0 : field = PQgetvalue(res, i, 2);
221 : :
8026 bruce@momjian.us 222 [ # # ]: 0 : if (param->verbose)
7508 tgl@sss.pgh.pa.us 223 : 0 : fprintf(stdout, "Checking %s in %s.%s\n", field, schema, table);
224 : :
4408 225 : 0 : schema = PQescapeIdentifier(conn, schema, strlen(schema));
226 : 0 : table = PQescapeIdentifier(conn, table, strlen(table));
227 : 0 : field = PQescapeIdentifier(conn, field, strlen(field));
228 : :
229 [ # # # # : 0 : if (!schema || !table || !field)
# # ]
230 : : {
1682 michael@paquier.xyz 231 : 0 : pg_log_error("%s", PQerrorMessage(conn));
4408 tgl@sss.pgh.pa.us 232 : 0 : PQclear(res);
233 : 0 : PQfinish(conn);
597 peter@eisentraut.org 234 : 0 : PQfreemem(schema);
235 : 0 : PQfreemem(table);
236 : 0 : PQfreemem(field);
4408 tgl@sss.pgh.pa.us 237 : 0 : return -1;
238 : : }
239 : :
7559 240 : 0 : snprintf(buf, BUFSIZE,
241 : : "DELETE FROM vacuum_l "
242 : : "WHERE lo IN (SELECT %s FROM %s.%s)",
243 : : field, schema, table);
8545 244 : 0 : res2 = PQexec(conn, buf);
9091 bruce@momjian.us 245 [ # # ]: 0 : if (PQresultStatus(res2) != PGRES_COMMAND_OK)
246 : : {
1682 michael@paquier.xyz 247 : 0 : pg_log_error("failed to check %s in table %s.%s: %s",
248 : : field, schema, table, PQerrorMessage(conn));
9091 bruce@momjian.us 249 : 0 : PQclear(res2);
250 : 0 : PQclear(res);
251 : 0 : PQfinish(conn);
3378 rhaas@postgresql.org 252 : 0 : PQfreemem(schema);
253 : 0 : PQfreemem(table);
254 : 0 : PQfreemem(field);
9091 bruce@momjian.us 255 : 0 : return -1;
256 : : }
257 : 0 : PQclear(res2);
258 : :
4408 tgl@sss.pgh.pa.us 259 : 0 : PQfreemem(schema);
260 : 0 : PQfreemem(table);
261 : 0 : PQfreemem(field);
262 : : }
9091 bruce@momjian.us 263 : 0 : PQclear(res);
264 : :
265 : : /*
266 : : * Now, those entries remaining in vacuum_l are orphans. Delete 'em.
267 : : *
268 : : * We don't want to run each delete as an individual transaction, because
269 : : * the commit overhead would be high. However, since 9.0 the backend will
270 : : * acquire a lock per deleted LO, so deleting too many LOs per transaction
271 : : * risks running out of room in the shared-memory lock table. Accordingly,
272 : : * we delete up to transaction_limit LOs per transaction.
273 : : */
274 : 0 : res = PQexec(conn, "begin");
4408 tgl@sss.pgh.pa.us 275 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
276 : : {
1682 michael@paquier.xyz 277 : 0 : pg_log_error("failed to start transaction: %s", PQerrorMessage(conn));
4408 tgl@sss.pgh.pa.us 278 : 0 : PQclear(res);
279 : 0 : PQfinish(conn);
280 : 0 : return -1;
281 : : }
9091 bruce@momjian.us 282 : 0 : PQclear(res);
283 : :
284 : 0 : buf[0] = '\0';
3926 rhaas@postgresql.org 285 : 0 : strcat(buf,
286 : : "DECLARE myportal CURSOR WITH HOLD FOR SELECT lo FROM vacuum_l");
8545 tgl@sss.pgh.pa.us 287 : 0 : res = PQexec(conn, buf);
3926 rhaas@postgresql.org 288 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
289 : : {
1682 michael@paquier.xyz 290 : 0 : pg_log_error("DECLARE CURSOR failed: %s", PQerrorMessage(conn));
8545 tgl@sss.pgh.pa.us 291 : 0 : PQclear(res);
9091 bruce@momjian.us 292 : 0 : PQfinish(conn);
293 : 0 : return -1;
294 : : }
3926 rhaas@postgresql.org 295 : 0 : PQclear(res);
296 : :
297 : 0 : snprintf(buf, BUFSIZE, "FETCH FORWARD %ld IN myportal",
298 [ # # ]: 0 : param->transaction_limit > 0 ? param->transaction_limit : 1000L);
299 : :
8545 tgl@sss.pgh.pa.us 300 : 0 : deleted = 0;
301 : :
302 : : do
303 : : {
3926 rhaas@postgresql.org 304 : 0 : res = PQexec(conn, buf);
305 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
306 : : {
1682 michael@paquier.xyz 307 : 0 : pg_log_error("FETCH FORWARD failed: %s", PQerrorMessage(conn));
3926 rhaas@postgresql.org 308 : 0 : PQclear(res);
309 : 0 : PQfinish(conn);
310 : 0 : return -1;
311 : : }
312 : :
313 : 0 : matched = PQntuples(res);
314 [ # # ]: 0 : if (matched <= 0)
315 : : {
316 : : /* at end of resultset */
317 : 0 : PQclear(res);
318 : 0 : break;
319 : : }
320 : :
321 [ # # ]: 0 : for (i = 0; i < matched; i++)
322 : : {
323 : 0 : Oid lo = atooid(PQgetvalue(res, i, 0));
324 : :
325 [ # # ]: 0 : if (param->verbose)
326 : : {
327 : 0 : fprintf(stdout, "\rRemoving lo %6u ", lo);
328 : 0 : fflush(stdout);
329 : : }
330 : :
331 [ # # ]: 0 : if (param->dry_run == 0)
332 : : {
333 [ # # ]: 0 : if (lo_unlink(conn, lo) < 0)
334 : : {
1682 michael@paquier.xyz 335 : 0 : pg_log_error("failed to remove lo %u: %s", lo,
336 : : PQerrorMessage(conn));
3926 rhaas@postgresql.org 337 [ # # ]: 0 : if (PQtransactionStatus(conn) == PQTRANS_INERROR)
338 : : {
339 : 0 : success = false;
1848 tgl@sss.pgh.pa.us 340 : 0 : break; /* out of inner for-loop */
341 : : }
342 : : }
343 : : else
3926 rhaas@postgresql.org 344 : 0 : deleted++;
345 : : }
346 : : else
7893 bruce@momjian.us 347 : 0 : deleted++;
348 : :
3926 rhaas@postgresql.org 349 [ # # ]: 0 : if (param->transaction_limit > 0 &&
350 [ # # ]: 0 : (deleted % param->transaction_limit) == 0)
351 : : {
352 : 0 : res2 = PQexec(conn, "commit");
353 [ # # ]: 0 : if (PQresultStatus(res2) != PGRES_COMMAND_OK)
354 : : {
1682 michael@paquier.xyz 355 : 0 : pg_log_error("failed to commit transaction: %s",
356 : : PQerrorMessage(conn));
3926 rhaas@postgresql.org 357 : 0 : PQclear(res2);
358 : 0 : PQclear(res);
359 : 0 : PQfinish(conn);
360 : 0 : return -1;
361 : : }
4408 tgl@sss.pgh.pa.us 362 : 0 : PQclear(res2);
3926 rhaas@postgresql.org 363 : 0 : res2 = PQexec(conn, "begin");
364 [ # # ]: 0 : if (PQresultStatus(res2) != PGRES_COMMAND_OK)
365 : : {
1682 michael@paquier.xyz 366 : 0 : pg_log_error("failed to start transaction: %s",
367 : : PQerrorMessage(conn));
3926 rhaas@postgresql.org 368 : 0 : PQclear(res2);
369 : 0 : PQclear(res);
370 : 0 : PQfinish(conn);
371 : 0 : return -1;
372 : : }
4408 tgl@sss.pgh.pa.us 373 : 0 : PQclear(res2);
374 : : }
375 : : }
376 : :
3926 rhaas@postgresql.org 377 : 0 : PQclear(res);
1848 tgl@sss.pgh.pa.us 378 [ # # ]: 0 : } while (success);
379 : :
380 : : /*
381 : : * That's all folks!
382 : : */
4408 383 : 0 : res = PQexec(conn, "commit");
384 [ # # ]: 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
385 : : {
1682 michael@paquier.xyz 386 : 0 : pg_log_error("failed to commit transaction: %s",
387 : : PQerrorMessage(conn));
4408 tgl@sss.pgh.pa.us 388 : 0 : PQclear(res);
389 : 0 : PQfinish(conn);
390 : 0 : return -1;
391 : : }
9091 bruce@momjian.us 392 : 0 : PQclear(res);
393 : :
394 : 0 : PQfinish(conn);
395 : :
8026 396 [ # # ]: 0 : if (param->verbose)
397 : : {
4633 rhaas@postgresql.org 398 [ # # ]: 0 : if (param->dry_run)
4408 tgl@sss.pgh.pa.us 399 : 0 : fprintf(stdout, "\rWould remove %ld large objects from database \"%s\".\n",
400 : : deleted, database);
4633 rhaas@postgresql.org 401 [ # # ]: 0 : else if (success)
402 : 0 : fprintf(stdout,
403 : : "\rSuccessfully removed %ld large objects from database \"%s\".\n",
404 : : deleted, database);
405 : : else
4408 tgl@sss.pgh.pa.us 406 : 0 : fprintf(stdout, "\rRemoval from database \"%s\" failed at object %ld of %ld.\n",
407 : : database, deleted, matched);
408 : : }
409 : :
4633 rhaas@postgresql.org 410 [ # # # # ]: 0 : return ((param->dry_run || success) ? 0 : -1);
411 : : }
412 : :
413 : : static void
5525 peter_e@gmx.net 414 :CBC 1 : usage(const char *progname)
415 : : {
416 : 1 : printf("%s removes unreferenced large objects from databases.\n\n", progname);
417 : 1 : printf("Usage:\n %s [OPTION]... DBNAME...\n\n", progname);
5527 418 : 1 : printf("Options:\n");
2056 michael@paquier.xyz 419 : 1 : printf(" -l, --limit=LIMIT commit after removing each LIMIT large objects\n");
420 : 1 : printf(" -n, --dry-run don't remove large objects, just show what would be done\n");
421 : 1 : printf(" -v, --verbose write a lot of progress messages\n");
422 : 1 : printf(" -V, --version output version information, then exit\n");
423 : 1 : printf(" -?, --help show this help, then exit\n");
4318 peter_e@gmx.net 424 : 1 : printf("\nConnection options:\n");
2056 michael@paquier.xyz 425 : 1 : printf(" -h, --host=HOSTNAME database server host or socket directory\n");
426 : 1 : printf(" -p, --port=PORT database server port\n");
427 : 1 : printf(" -U, --username=USERNAME user name to connect as\n");
428 : 1 : printf(" -w, --no-password never prompt for password\n");
429 : 1 : printf(" -W, --password force password prompt\n");
5527 peter_e@gmx.net 430 : 1 : printf("\n");
1507 peter@eisentraut.org 431 : 1 : printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
432 : 1 : printf("%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL);
8026 bruce@momjian.us 433 : 1 : }
434 : :
435 : :
436 : : int
9136 peter@retep.org.uk 437 : 3 : main(int argc, char **argv)
438 : : {
439 : : static struct option long_options[] = {
440 : : {"host", required_argument, NULL, 'h'},
441 : : {"limit", required_argument, NULL, 'l'},
442 : : {"dry-run", no_argument, NULL, 'n'},
443 : : {"port", required_argument, NULL, 'p'},
444 : : {"username", required_argument, NULL, 'U'},
445 : : {"verbose", no_argument, NULL, 'v'},
446 : : {"version", no_argument, NULL, 'V'},
447 : : {"no-password", no_argument, NULL, 'w'},
448 : : {"password", no_argument, NULL, 'W'},
449 : : {"help", no_argument, NULL, '?'},
450 : : {NULL, 0, NULL, 0}
451 : : };
452 : :
9091 bruce@momjian.us 453 : 3 : int rc = 0;
454 : : struct _param param;
455 : : int c;
456 : : int port;
457 : : const char *progname;
458 : : int optindex;
459 : :
1682 michael@paquier.xyz 460 : 3 : pg_logging_init(argv[0]);
5525 peter_e@gmx.net 461 : 3 : progname = get_progname(argv[0]);
462 : :
463 : : /* Set default parameter values */
7893 bruce@momjian.us 464 : 3 : param.pg_user = NULL;
5526 peter_e@gmx.net 465 : 3 : param.pg_prompt = TRI_DEFAULT;
8026 bruce@momjian.us 466 : 3 : param.pg_host = NULL;
7096 neilc@samurai.com 467 : 3 : param.pg_port = NULL;
4302 rhaas@postgresql.org 468 : 3 : param.progname = progname;
7893 bruce@momjian.us 469 : 3 : param.verbose = 0;
8026 470 : 3 : param.dry_run = 0;
4408 tgl@sss.pgh.pa.us 471 : 3 : param.transaction_limit = 1000;
472 : :
473 : : /* Process command-line arguments */
5525 peter_e@gmx.net 474 [ + - ]: 3 : if (argc > 1)
475 : : {
476 [ + + - + ]: 3 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
477 : : {
478 : 1 : usage(progname);
479 : 1 : exit(0);
480 : : }
481 [ + + - + ]: 2 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
482 : : {
483 : 1 : puts("vacuumlo (PostgreSQL) " PG_VERSION);
484 : 1 : exit(0);
485 : : }
486 : : }
487 : :
2056 michael@paquier.xyz 488 [ + - ]: 1 : while ((c = getopt_long(argc, argv, "h:l:np:U:vwW", long_options, &optindex)) != -1)
489 : : {
7893 bruce@momjian.us 490 [ - - - - : 1 : switch (c)
- - - -
+ ]
491 : : {
2056 michael@paquier.xyz 492 :UBC 0 : case 'h':
493 : 0 : param.pg_host = pg_strdup(optarg);
7893 bruce@momjian.us 494 : 0 : break;
4408 tgl@sss.pgh.pa.us 495 : 0 : case 'l':
496 : 0 : param.transaction_limit = strtol(optarg, NULL, 10);
497 [ # # ]: 0 : if (param.transaction_limit < 0)
737 498 : 0 : pg_fatal("transaction limit must not be negative (0 disables)");
4408 499 : 0 : break;
2056 michael@paquier.xyz 500 : 0 : case 'n':
501 : 0 : param.dry_run = 1;
502 : 0 : param.verbose = 1;
7893 bruce@momjian.us 503 : 0 : break;
504 : 0 : case 'p':
505 : 0 : port = strtol(optarg, NULL, 10);
506 [ # # # # ]: 0 : if ((port < 1) || (port > 65535))
737 tgl@sss.pgh.pa.us 507 : 0 : pg_fatal("invalid port number: %s", optarg);
2784 508 : 0 : param.pg_port = pg_strdup(optarg);
7893 bruce@momjian.us 509 : 0 : break;
2056 michael@paquier.xyz 510 : 0 : case 'U':
511 : 0 : param.pg_user = pg_strdup(optarg);
7893 bruce@momjian.us 512 : 0 : break;
2056 michael@paquier.xyz 513 : 0 : case 'v':
514 : 0 : param.verbose = 1;
515 : 0 : break;
516 : 0 : case 'w':
517 : 0 : param.pg_prompt = TRI_NO;
518 : 0 : break;
519 : 0 : case 'W':
520 : 0 : param.pg_prompt = TRI_YES;
521 : 0 : break;
2056 michael@paquier.xyz 522 :CBC 1 : default:
523 : : /* getopt_long already emitted a complaint */
737 tgl@sss.pgh.pa.us 524 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2056 michael@paquier.xyz 525 : 1 : exit(1);
526 : : }
527 : : }
528 : :
529 : : /* No database given? Show usage */
7803 tgl@sss.pgh.pa.us 530 [ # # ]:UBC 0 : if (optind >= argc)
531 : : {
1682 michael@paquier.xyz 532 : 0 : pg_log_error("missing required argument: database name");
737 tgl@sss.pgh.pa.us 533 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
7893 bruce@momjian.us 534 : 0 : exit(1);
535 : : }
536 : :
537 [ # # ]: 0 : for (c = optind; c < argc; c++)
538 : : {
539 : : /* Work on selected database */
540 : 0 : rc += (vacuumlo(argv[c], ¶m) != 0);
541 : : }
542 : :
9091 543 : 0 : return rc;
544 : : }
|