Age Owner 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-2023, 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
2118 tgl 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;
3955 bruce 72 0 : bool success = true;
73 : static char *password = NULL;
74 :
75 : /* Note: password can be carried over from a previous call */
948 tgl 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 :
3931 rhaas 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";
948 tgl 97 0 : values[3] = password;
3931 rhaas 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);
5598 tgl 107 0 : if (!conn)
108 : {
1311 michael 109 0 : pg_log_error("connection to database \"%s\" failed", database);
5598 tgl 110 0 : return -1;
111 : }
112 :
113 0 : if (PQstatus(conn) == CONNECTION_BAD &&
114 0 : PQconnectionNeedsPassword(conn) &&
948 115 0 : !password &&
5155 peter_e 116 0 : param->pg_prompt != TRI_NO)
117 : {
5598 tgl 118 0 : PQfinish(conn);
948 119 0 : password = simple_prompt("Password: ", false);
5598 120 0 : new_pass = true;
121 : }
122 0 : } while (new_pass);
123 :
124 : /* check to see that the backend connection was successfully made */
8720 bruce 125 0 : if (PQstatus(conn) == CONNECTION_BAD)
126 : {
807 tgl 127 0 : pg_log_error("%s", PQerrorMessage(conn));
8174 128 0 : PQfinish(conn);
8720 bruce 129 0 : return -1;
130 : }
131 :
7522 132 0 : if (param->verbose)
133 : {
4037 tgl 134 0 : fprintf(stdout, "Connected to database \"%s\"\n", database);
7522 bruce 135 0 : if (param->dry_run)
136 0 : fprintf(stdout, "Test run: no large objects will be removed!\n");
137 : }
138 :
1868 noah 139 0 : res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
140 0 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
141 : {
1311 michael 142 0 : pg_log_error("failed to set search_path: %s", PQerrorMessage(conn));
7478 bruce 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 : */
8720 152 0 : buf[0] = '\0';
7137 tgl 153 0 : strcat(buf, "CREATE TEMP TABLE vacuum_l AS ");
4799 154 0 : if (PQserverVersion(conn) >= 90000)
4864 itagaki.takahiro 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");
8174 tgl 158 0 : res = PQexec(conn, buf);
159 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
160 : {
1311 michael 161 0 : pg_log_error("failed to create temp table: %s", PQerrorMessage(conn));
8174 tgl 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';
5018 bruce 173 0 : strcat(buf, "ANALYZE vacuum_l");
8174 tgl 174 0 : res = PQexec(conn, buf);
175 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
176 : {
1311 michael 177 0 : pg_log_error("failed to vacuum temp table: %s", PQerrorMessage(conn));
8174 tgl 178 0 : PQclear(res);
8765 peter 179 0 : PQfinish(conn);
180 0 : return -1;
181 : }
8720 bruce 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';
7137 tgl 194 0 : strcat(buf, "SELECT s.nspname, c.relname, a.attname ");
7188 195 0 : strcat(buf, "FROM pg_class c, pg_attribute a, pg_namespace s, pg_type t ");
7137 196 0 : strcat(buf, "WHERE a.attnum > 0 AND NOT a.attisdropped ");
8720 bruce 197 0 : strcat(buf, " AND a.attrelid = c.oid ");
198 0 : strcat(buf, " AND a.atttypid = t.oid ");
7188 tgl 199 0 : strcat(buf, " AND c.relnamespace = s.oid ");
7874 inoue 200 0 : strcat(buf, " AND t.typname in ('oid', 'lo') ");
2222 tgl 201 0 : strcat(buf, " AND c.relkind in (" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_MATVIEW) ")");
6254 202 0 : strcat(buf, " AND s.nspname !~ '^pg_'");
8174 203 0 : res = PQexec(conn, buf);
204 0 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
205 : {
1311 michael 206 0 : pg_log_error("failed to find OID columns: %s", PQerrorMessage(conn));
8174 tgl 207 0 : PQclear(res);
8765 peter 208 0 : PQfinish(conn);
209 0 : return -1;
210 : }
211 :
8720 bruce 212 0 : for (i = 0; i < PQntuples(res); i++)
213 : {
214 : char *schema,
215 : *table,
216 : *field;
217 :
7137 tgl 218 0 : schema = PQgetvalue(res, i, 0);
219 0 : table = PQgetvalue(res, i, 1);
220 0 : field = PQgetvalue(res, i, 2);
221 :
7655 bruce 222 0 : if (param->verbose)
7137 tgl 223 0 : fprintf(stdout, "Checking %s in %s.%s\n", field, schema, table);
224 :
4037 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 : {
1311 michael 231 0 : pg_log_error("%s", PQerrorMessage(conn));
4037 tgl 232 0 : PQclear(res);
233 0 : PQfinish(conn);
226 peter 234 UNC 0 : PQfreemem(schema);
235 0 : PQfreemem(table);
236 0 : PQfreemem(field);
4037 tgl 237 UBC 0 : return -1;
238 : }
239 :
7188 tgl 240 UIC 0 : snprintf(buf, BUFSIZE,
7188 tgl 241 EUB : "DELETE FROM vacuum_l "
4037 242 : "WHERE lo IN (SELECT %s FROM %s.%s)",
243 : field, schema, table);
8174 tgl 244 UBC 0 : res2 = PQexec(conn, buf);
8720 bruce 245 UIC 0 : if (PQresultStatus(res2) != PGRES_COMMAND_OK)
8720 bruce 246 EUB : {
1311 michael 247 UBC 0 : pg_log_error("failed to check %s in table %s.%s: %s",
1311 michael 248 EUB : field, schema, table, PQerrorMessage(conn));
8720 bruce 249 UBC 0 : PQclear(res2);
250 0 : PQclear(res);
251 0 : PQfinish(conn);
3007 rhaas 252 0 : PQfreemem(schema);
3007 rhaas 253 UIC 0 : PQfreemem(table);
3007 rhaas 254 UBC 0 : PQfreemem(field);
8720 bruce 255 UIC 0 : return -1;
8720 bruce 256 EUB : }
8720 bruce 257 UBC 0 : PQclear(res2);
4037 tgl 258 EUB :
4037 tgl 259 UIC 0 : PQfreemem(schema);
4037 tgl 260 UBC 0 : PQfreemem(table);
4037 tgl 261 UIC 0 : PQfreemem(field);
262 : }
8720 bruce 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
3955 bruce 271 EUB : * risks running out of room in the shared-memory lock table. Accordingly,
272 : * we delete up to transaction_limit LOs per transaction.
273 : */
8720 bruce 274 UBC 0 : res = PQexec(conn, "begin");
4037 tgl 275 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
4037 tgl 276 EUB : {
1311 michael 277 UBC 0 : pg_log_error("failed to start transaction: %s", PQerrorMessage(conn));
4037 tgl 278 UIC 0 : PQclear(res);
4037 tgl 279 UBC 0 : PQfinish(conn);
4037 tgl 280 UIC 0 : return -1;
4037 tgl 281 EUB : }
8720 bruce 282 UBC 0 : PQclear(res);
283 :
284 0 : buf[0] = '\0';
3555 rhaas 285 0 : strcat(buf,
286 : "DECLARE myportal CURSOR WITH HOLD FOR SELECT lo FROM vacuum_l");
8174 tgl 287 0 : res = PQexec(conn, buf);
3555 rhaas 288 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
8765 peter 289 EUB : {
1311 michael 290 UBC 0 : pg_log_error("DECLARE CURSOR failed: %s", PQerrorMessage(conn));
8174 tgl 291 UIC 0 : PQclear(res);
8720 bruce 292 UBC 0 : PQfinish(conn);
8720 bruce 293 UIC 0 : return -1;
8720 bruce 294 EUB : }
3555 rhaas 295 UBC 0 : PQclear(res);
296 :
297 0 : snprintf(buf, BUFSIZE, "FETCH FORWARD %ld IN myportal",
3555 rhaas 298 UIC 0 : param->transaction_limit > 0 ? param->transaction_limit : 1000L);
299 :
8174 tgl 300 0 : deleted = 0;
3555 rhaas 301 EUB :
1477 tgl 302 : do
303 : {
3555 rhaas 304 UBC 0 : res = PQexec(conn, buf);
305 0 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
3555 rhaas 306 EUB : {
1311 michael 307 UBC 0 : pg_log_error("FETCH FORWARD failed: %s", PQerrorMessage(conn));
3555 rhaas 308 UIC 0 : PQclear(res);
309 0 : PQfinish(conn);
3555 rhaas 310 UBC 0 : return -1;
3555 rhaas 311 EUB : }
312 :
3555 rhaas 313 UIC 0 : matched = PQntuples(res);
3555 rhaas 314 UBC 0 : if (matched <= 0)
8720 bruce 315 EUB : {
316 : /* at end of resultset */
3555 rhaas 317 UIC 0 : PQclear(res);
3555 rhaas 318 UBC 0 : break;
319 : }
8720 bruce 320 EUB :
3555 rhaas 321 UIC 0 : for (i = 0; i < matched; i++)
7522 bruce 322 EUB : {
3555 rhaas 323 UIC 0 : Oid lo = atooid(PQgetvalue(res, i, 0));
3555 rhaas 324 EUB :
3555 rhaas 325 UBC 0 : if (param->verbose)
326 : {
3555 rhaas 327 UIC 0 : fprintf(stdout, "\rRemoving lo %6u ", lo);
3555 rhaas 328 UBC 0 : fflush(stdout);
329 : }
3555 rhaas 330 EUB :
3555 rhaas 331 UIC 0 : if (param->dry_run == 0)
7522 bruce 332 EUB : {
3555 rhaas 333 UIC 0 : if (lo_unlink(conn, lo) < 0)
4262 rhaas 334 EUB : {
1311 michael 335 UIC 0 : pg_log_error("failed to remove lo %u: %s", lo,
1311 michael 336 EUB : PQerrorMessage(conn));
3555 rhaas 337 UBC 0 : if (PQtransactionStatus(conn) == PQTRANS_INERROR)
338 : {
3555 rhaas 339 UIC 0 : success = false;
1477 tgl 340 0 : break; /* out of inner for-loop */
3555 rhaas 341 EUB : }
342 : }
343 : else
3555 rhaas 344 UBC 0 : deleted++;
345 : }
7522 bruce 346 EUB : else
7522 bruce 347 UBC 0 : deleted++;
348 :
3555 rhaas 349 0 : if (param->transaction_limit > 0 &&
350 0 : (deleted % param->transaction_limit) == 0)
351 : {
352 0 : res2 = PQexec(conn, "commit");
3555 rhaas 353 UIC 0 : if (PQresultStatus(res2) != PGRES_COMMAND_OK)
3555 rhaas 354 EUB : {
1311 michael 355 UBC 0 : pg_log_error("failed to commit transaction: %s",
1311 michael 356 EUB : PQerrorMessage(conn));
3555 rhaas 357 UBC 0 : PQclear(res2);
3555 rhaas 358 UIC 0 : PQclear(res);
3555 rhaas 359 UBC 0 : PQfinish(conn);
360 0 : return -1;
3555 rhaas 361 EUB : }
4037 tgl 362 UIC 0 : PQclear(res2);
3555 rhaas 363 UBC 0 : res2 = PQexec(conn, "begin");
3555 rhaas 364 UIC 0 : if (PQresultStatus(res2) != PGRES_COMMAND_OK)
3555 rhaas 365 EUB : {
1311 michael 366 UBC 0 : pg_log_error("failed to start transaction: %s",
1311 michael 367 EUB : PQerrorMessage(conn));
3555 rhaas 368 UBC 0 : PQclear(res2);
3555 rhaas 369 UIC 0 : PQclear(res);
3555 rhaas 370 UBC 0 : PQfinish(conn);
3555 rhaas 371 UIC 0 : return -1;
372 : }
4037 tgl 373 0 : PQclear(res2);
4037 tgl 374 EUB : }
375 : }
376 :
3555 rhaas 377 UIC 0 : PQclear(res);
1477 tgl 378 0 : } while (success);
379 :
8720 bruce 380 EUB : /*
381 : * That's all folks!
382 : */
4037 tgl 383 UBC 0 : res = PQexec(conn, "commit");
4037 tgl 384 UIC 0 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
4037 tgl 385 EUB : {
1311 michael 386 UBC 0 : pg_log_error("failed to commit transaction: %s",
1311 michael 387 EUB : PQerrorMessage(conn));
4037 tgl 388 UIC 0 : PQclear(res);
4037 tgl 389 UBC 0 : PQfinish(conn);
4037 tgl 390 UIC 0 : return -1;
4037 tgl 391 EUB : }
8720 bruce 392 UIC 0 : PQclear(res);
8174 tgl 393 EUB :
8720 bruce 394 UIC 0 : PQfinish(conn);
8720 bruce 395 EUB :
7655 bruce 396 UBC 0 : if (param->verbose)
397 : {
4262 rhaas 398 0 : if (param->dry_run)
4037 tgl 399 0 : fprintf(stdout, "\rWould remove %ld large objects from database \"%s\".\n",
400 : deleted, database);
4262 rhaas 401 UIC 0 : else if (success)
402 0 : fprintf(stdout,
4037 tgl 403 EUB : "\rSuccessfully removed %ld large objects from database \"%s\".\n",
404 : deleted, database);
405 : else
4037 tgl 406 UIC 0 : fprintf(stdout, "\rRemoval from database \"%s\" failed at object %ld of %ld.\n",
4262 rhaas 407 EUB : database, deleted, matched);
408 : }
409 :
4262 rhaas 410 UIC 0 : return ((param->dry_run || success) ? 0 : -1);
8765 peter 411 ECB : }
412 :
4037 tgl 413 : static void
5154 peter_e 414 CBC 1 : usage(const char *progname)
7522 bruce 415 ECB : {
5154 peter_e 416 CBC 1 : printf("%s removes unreferenced large objects from databases.\n\n", progname);
417 1 : printf("Usage:\n %s [OPTION]... DBNAME...\n\n", progname);
5156 418 1 : printf("Options:\n");
1685 michael 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");
3947 peter_e 424 1 : printf("\nConnection options:\n");
1685 michael 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");
5156 peter_e 430 1 : printf("\n");
1136 peter 431 GIC 1 : printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
432 1 : printf("%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL);
7655 bruce 433 1 : }
7655 bruce 434 ECB :
435 :
436 : int
8765 peter 437 GIC 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, '?'},
1685 michael 450 ECB : {NULL, 0, NULL, 0}
451 : };
452 :
8720 bruce 453 GIC 3 : int rc = 0;
454 : struct _param param;
455 : int c;
456 : int port;
5154 peter_e 457 ECB : const char *progname;
1685 michael 458 : int optindex;
459 :
1311 michael 460 GIC 3 : pg_logging_init(argv[0]);
5154 peter_e 461 CBC 3 : progname = get_progname(argv[0]);
8720 bruce 462 ECB :
4037 tgl 463 : /* Set default parameter values */
7522 bruce 464 CBC 3 : param.pg_user = NULL;
5155 peter_e 465 3 : param.pg_prompt = TRI_DEFAULT;
7655 bruce 466 3 : param.pg_host = NULL;
6725 neilc 467 3 : param.pg_port = NULL;
3931 rhaas 468 3 : param.progname = progname;
7522 bruce 469 GIC 3 : param.verbose = 0;
7655 470 3 : param.dry_run = 0;
4037 tgl 471 CBC 3 : param.transaction_limit = 1000;
472 :
4037 tgl 473 ECB : /* Process command-line arguments */
5154 peter_e 474 GIC 3 : if (argc > 1)
5154 peter_e 475 ECB : {
5154 peter_e 476 CBC 3 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
477 : {
478 1 : usage(progname);
5154 peter_e 479 GIC 1 : exit(0);
5154 peter_e 480 ECB : }
5154 peter_e 481 CBC 2 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
482 : {
5154 peter_e 483 GIC 1 : puts("vacuumlo (PostgreSQL) " PG_VERSION);
484 1 : exit(0);
5154 peter_e 485 ECB : }
486 : }
487 :
1685 michael 488 GIC 1 : while ((c = getopt_long(argc, argv, "h:l:np:U:vwW", long_options, &optindex)) != -1)
7522 bruce 489 EUB : {
7522 bruce 490 GBC 1 : switch (c)
7522 bruce 491 EUB : {
1685 michael 492 UBC 0 : case 'h':
493 0 : param.pg_host = pg_strdup(optarg);
7522 bruce 494 0 : break;
4037 tgl 495 0 : case 'l':
496 0 : param.transaction_limit = strtol(optarg, NULL, 10);
497 0 : if (param.transaction_limit < 0)
366 498 0 : pg_fatal("transaction limit must not be negative (0 disables)");
4037 499 0 : break;
1685 michael 500 0 : case 'n':
501 0 : param.dry_run = 1;
502 0 : param.verbose = 1;
7522 bruce 503 0 : break;
504 0 : case 'p':
505 0 : port = strtol(optarg, NULL, 10);
506 0 : if ((port < 1) || (port > 65535))
366 tgl 507 0 : pg_fatal("invalid port number: %s", optarg);
2413 508 0 : param.pg_port = pg_strdup(optarg);
7522 bruce 509 0 : break;
1685 michael 510 0 : case 'U':
511 0 : param.pg_user = pg_strdup(optarg);
7522 bruce 512 0 : break;
1685 michael 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;
1685 michael 519 LBC 0 : case 'W':
1685 michael 520 UIC 0 : param.pg_prompt = TRI_YES;
1685 michael 521 LBC 0 : break;
1685 michael 522 CBC 1 : default:
523 : /* getopt_long already emitted a complaint */
366 tgl 524 GIC 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
1685 michael 525 1 : exit(1);
526 : }
7655 bruce 527 EUB : }
528 :
529 : /* No database given? Show usage */
7432 tgl 530 UBC 0 : if (optind >= argc)
7522 bruce 531 EUB : {
1311 michael 532 UIC 0 : pg_log_error("missing required argument: database name");
366 tgl 533 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
7522 bruce 534 UBC 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 :
8720 bruce 543 UIC 0 : return rc;
544 : }
|