LCOV - differential code coverage report
Current view: top level - contrib/vacuumlo - vacuumlo.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC CBC EUB ECB DUB
Current: Differential Code Coverage HEAD vs 15 Lines: 15.8 % 273 43 3 2 47 178 1 15 27 47 14 4
Current Date: 2023-04-08 15:15:32 Functions: 66.7 % 3 2 1 2 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a