LCOV - differential code coverage report
Current view: top level - src/bin/scripts - vacuumdb.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 82.6 % 518 428 20 36 29 5 29 170 122 107 48 244 8 41
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 7 7 6 1 7
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * vacuumdb
       4                 :  *
       5                 :  * Portions Copyright (c) 1996-2023, 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 "catalog/pg_class_d.h"
      18                 : #include "common.h"
      19                 : #include "common/connect.h"
      20                 : #include "common/logging.h"
      21                 : #include "fe_utils/cancel.h"
      22                 : #include "fe_utils/option_utils.h"
      23                 : #include "fe_utils/parallel_slot.h"
      24                 : #include "fe_utils/query_utils.h"
      25                 : #include "fe_utils/simple_list.h"
      26                 : #include "fe_utils/string_utils.h"
      27                 : 
      28                 : 
      29                 : /* vacuum options controlled by user flags */
      30                 : typedef struct vacuumingOptions
      31                 : {
      32                 :     bool        analyze_only;
      33                 :     bool        verbose;
      34                 :     bool        and_analyze;
      35                 :     bool        full;
      36                 :     bool        freeze;
      37                 :     bool        disable_page_skipping;
      38                 :     bool        skip_locked;
      39                 :     int         min_xid_age;
      40                 :     int         min_mxid_age;
      41                 :     int         parallel_workers;   /* >= 0 indicates user specified the
      42                 :                                      * parallel degree, otherwise -1 */
      43                 :     bool        no_index_cleanup;
      44                 :     bool        force_index_cleanup;
      45                 :     bool        do_truncate;
      46                 :     bool        process_main;
      47                 :     bool        process_toast;
      48                 :     bool        skip_database_stats;
      49                 :     char       *buffer_usage_limit;
      50                 : } vacuumingOptions;
      51                 : 
      52                 : /* object filter options */
      53                 : typedef enum
      54                 : {
      55                 :     OBJFILTER_NONE = 0,                 /* no filter used */
      56                 :     OBJFILTER_ALL_DBS = (1 << 0),     /* -a | --all */
      57                 :     OBJFILTER_DATABASE = (1 << 1),        /* -d | --dbname */
      58                 :     OBJFILTER_TABLE = (1 << 2),           /* -t | --table */
      59                 :     OBJFILTER_SCHEMA = (1 << 3),      /* -n | --schema */
      60                 :     OBJFILTER_SCHEMA_EXCLUDE = (1 << 4)   /* -N | --exclude-schema */
      61                 : } VacObjFilter;
      62                 : 
      63                 : VacObjFilter objfilter = OBJFILTER_NONE;
      64                 : 
      65                 : static void vacuum_one_database(ConnParams *cparams,
      66                 :                                 vacuumingOptions *vacopts,
      67                 :                                 int stage,
      68                 :                                 SimpleStringList *objects,
      69                 :                                 int concurrentCons,
      70                 :                                 const char *progname, bool echo, bool quiet);
      71                 : 
      72                 : static void vacuum_all_databases(ConnParams *cparams,
      73                 :                                  vacuumingOptions *vacopts,
      74                 :                                  bool analyze_in_stages,
      75                 :                                  int concurrentCons,
      76                 :                                  const char *progname, bool echo, bool quiet);
      77                 : 
      78                 : static void prepare_vacuum_command(PQExpBuffer sql, int serverVersion,
      79                 :                                    vacuumingOptions *vacopts, const char *table);
      80                 : 
      81                 : static void run_vacuum_command(PGconn *conn, const char *sql, bool echo,
      82                 :                                const char *table);
      83                 : 
      84                 : static void help(const char *progname);
      85                 : 
      86                 : void check_objfilter(void);
      87                 : 
      88                 : /* For analyze-in-stages mode */
      89                 : #define ANALYZE_NO_STAGE    -1
      90                 : #define ANALYZE_NUM_STAGES  3
      91                 : 
      92                 : 
      93                 : int
      94 GIC          54 : main(int argc, char *argv[])
      95                 : {
      96                 :     static struct option long_options[] = {
      97                 :         {"host", required_argument, NULL, 'h'},
      98                 :         {"port", required_argument, NULL, 'p'},
      99                 :         {"username", required_argument, NULL, 'U'},
     100                 :         {"no-password", no_argument, NULL, 'w'},
     101                 :         {"password", no_argument, NULL, 'W'},
     102                 :         {"echo", no_argument, NULL, 'e'},
     103                 :         {"quiet", no_argument, NULL, 'q'},
     104                 :         {"dbname", required_argument, NULL, 'd'},
     105                 :         {"analyze", no_argument, NULL, 'z'},
     106                 :         {"analyze-only", no_argument, NULL, 'Z'},
     107                 :         {"freeze", no_argument, NULL, 'F'},
     108                 :         {"all", no_argument, NULL, 'a'},
     109                 :         {"table", required_argument, NULL, 't'},
     110                 :         {"full", no_argument, NULL, 'f'},
     111 ECB             :         {"verbose", no_argument, NULL, 'v'},
     112                 :         {"jobs", required_argument, NULL, 'j'},
     113                 :         {"parallel", required_argument, NULL, 'P'},
     114                 :         {"schema", required_argument, NULL, 'n'},
     115                 :         {"exclude-schema", required_argument, NULL, 'N'},
     116                 :         {"maintenance-db", required_argument, NULL, 2},
     117                 :         {"analyze-in-stages", no_argument, NULL, 3},
     118                 :         {"disable-page-skipping", no_argument, NULL, 4},
     119                 :         {"skip-locked", no_argument, NULL, 5},
     120                 :         {"min-xid-age", required_argument, NULL, 6},
     121                 :         {"min-mxid-age", required_argument, NULL, 7},
     122                 :         {"no-index-cleanup", no_argument, NULL, 8},
     123                 :         {"force-index-cleanup", no_argument, NULL, 9},
     124                 :         {"no-truncate", no_argument, NULL, 10},
     125                 :         {"no-process-toast", no_argument, NULL, 11},
     126                 :         {"no-process-main", no_argument, NULL, 12},
     127                 :         {"buffer-usage-limit", required_argument, NULL, 13},
     128                 :         {NULL, 0, NULL, 0}
     129                 :     };
     130                 : 
     131                 :     const char *progname;
     132                 :     int         optindex;
     133                 :     int         c;
     134 GIC          54 :     const char *dbname = NULL;
     135              54 :     const char *maintenance_db = NULL;
     136              54 :     char       *host = NULL;
     137              54 :     char       *port = NULL;
     138              54 :     char       *username = NULL;
     139              54 :     enum trivalue prompt_password = TRI_DEFAULT;
     140                 :     ConnParams  cparams;
     141              54 :     bool        echo = false;
     142              54 :     bool        quiet = false;
     143                 :     vacuumingOptions vacopts;
     144              54 :     bool        analyze_in_stages = false;
     145 GNC          54 :     SimpleStringList objects = {NULL, NULL};
     146 GIC          54 :     int         concurrentCons = 1;
     147              54 :     int         tbl_count = 0;
     148                 : 
     149                 :     /* initialize options */
     150              54 :     memset(&vacopts, 0, sizeof(vacopts));
     151              54 :     vacopts.parallel_workers = -1;
     152 GNC          54 :     vacopts.buffer_usage_limit = NULL;
     153 GIC          54 :     vacopts.no_index_cleanup = false;
     154              54 :     vacopts.force_index_cleanup = false;
     155 CBC          54 :     vacopts.do_truncate = true;
     156 GNC          54 :     vacopts.process_main = true;
     157 CBC          54 :     vacopts.process_toast = true;
     158 ECB             : 
     159 CBC          54 :     pg_logging_init(argv[0]);
     160              54 :     progname = get_progname(argv[0]);
     161              54 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
     162                 : 
     163              54 :     handle_help_version_opts(argc, argv, "vacuumdb", help);
     164 ECB             : 
     165 GNC         132 :     while ((c = getopt_long(argc, argv, "ad:efFh:j:n:N:p:P:qt:U:vwWzZ", long_options, &optindex)) != -1)
     166 ECB             :     {
     167 CBC          86 :         switch (c)
     168 ECB             :         {
     169 GNC          10 :             case 'a':
     170              10 :                 objfilter |= OBJFILTER_ALL_DBS;
     171              10 :                 break;
     172               1 :             case 'd':
     173               1 :                 objfilter |= OBJFILTER_DATABASE;
     174               1 :                 dbname = pg_strdup(optarg);
     175               1 :                 break;
     176               1 :             case 'e':
     177               1 :                 echo = true;
     178               1 :                 break;
     179               1 :             case 'f':
     180               1 :                 vacopts.full = true;
     181               1 :                 break;
     182               2 :             case 'F':
     183               2 :                 vacopts.freeze = true;
     184               2 :                 break;
     185 CBC           2 :             case 'h':
     186 GIC           2 :                 host = pg_strdup(optarg);
     187               2 :                 break;
     188 GNC           1 :             case 'j':
     189               1 :                 if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
     190                 :                                       &concurrentCons))
     191 UNC           0 :                     exit(1);
     192 GNC           1 :                 break;
     193               4 :             case 'n':
     194               4 :                 objfilter |= OBJFILTER_SCHEMA;
     195               4 :                 simple_string_list_append(&objects, optarg);
     196               4 :                 break;
     197               4 :             case 'N':
     198               4 :                 objfilter |= OBJFILTER_SCHEMA_EXCLUDE;
     199               4 :                 simple_string_list_append(&objects, optarg);
     200               4 :                 break;
     201 CBC           2 :             case 'p':
     202               2 :                 port = pg_strdup(optarg);
     203               2 :                 break;
     204 GNC           3 :             case 'P':
     205               3 :                 if (!option_parse_int(optarg, "-P/--parallel", 0, INT_MAX,
     206                 :                                       &vacopts.parallel_workers))
     207               1 :                     exit(1);
     208               2 :                 break;
     209 UNC           0 :             case 'q':
     210               0 :                 quiet = true;
     211               0 :                 break;
     212 GNC          15 :             case 't':
     213              15 :                 objfilter |= OBJFILTER_TABLE;
     214              15 :                 simple_string_list_append(&objects, optarg);
     215              15 :                 tbl_count++;
     216              15 :                 break;
     217 CBC           2 :             case 'U':
     218               2 :                 username = pg_strdup(optarg);
     219               2 :                 break;
     220 UNC           0 :             case 'v':
     221               0 :                 vacopts.verbose = true;
     222               0 :                 break;
     223 LBC           0 :             case 'w':
     224               0 :                 prompt_password = TRI_NO;
     225 UIC           0 :                 break;
     226 LBC           0 :             case 'W':
     227               0 :                 prompt_password = TRI_YES;
     228               0 :                 break;
     229 CBC           5 :             case 'z':
     230               5 :                 vacopts.and_analyze = true;
     231               5 :                 break;
     232              14 :             case 'Z':
     233              14 :                 vacopts.analyze_only = true;
     234              14 :                 break;
     235 LBC           0 :             case 2:
     236 UIC           0 :                 maintenance_db = pg_strdup(optarg);
     237 LBC           0 :                 break;
     238 CBC           2 :             case 3:
     239 GBC           2 :                 analyze_in_stages = vacopts.analyze_only = true;
     240               2 :                 break;
     241               2 :             case 4:
     242 CBC           2 :                 vacopts.disable_page_skipping = true;
     243               2 :                 break;
     244               2 :             case 5:
     245               2 :                 vacopts.skip_locked = true;
     246               2 :                 break;
     247               2 :             case 6:
     248               2 :                 if (!option_parse_int(optarg, "--min-xid-age", 1, INT_MAX,
     249 ECB             :                                       &vacopts.min_xid_age))
     250 GBC           1 :                     exit(1);
     251               1 :                 break;
     252               2 :             case 7:
     253               2 :                 if (!option_parse_int(optarg, "--min-mxid-age", 1, INT_MAX,
     254 EUB             :                                       &vacopts.min_mxid_age))
     255 GBC           1 :                     exit(1);
     256               1 :                 break;
     257               2 :             case 8:
     258               2 :                 vacopts.no_index_cleanup = true;
     259 CBC           2 :                 break;
     260 LBC           0 :             case 9:
     261               0 :                 vacopts.force_index_cleanup = true;
     262               0 :                 break;
     263 CBC           2 :             case 10:
     264               2 :                 vacopts.do_truncate = false;
     265 GBC           2 :                 break;
     266               2 :             case 11:
     267               2 :                 vacopts.process_toast = false;
     268 CBC           2 :                 break;
     269 GNC           2 :             case 12:
     270               2 :                 vacopts.process_main = false;
     271               2 :                 break;
     272 UNC           0 :             case 13:
     273               0 :                 vacopts.buffer_usage_limit = pg_strdup(optarg);
     274               0 :                 break;
     275 CBC           1 :             default:
     276 ECB             :                 /* getopt_long already emitted a complaint */
     277 CBC           1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     278               1 :                 exit(1);
     279 ECB             :         }
     280                 :     }
     281                 : 
     282                 :     /*
     283                 :      * Non-option argument specifies database name as long as it wasn't
     284                 :      * already specified with -d / --dbname
     285                 :      */
     286 CBC          46 :     if (optind < argc && dbname == NULL)
     287 ECB             :     {
     288 GNC          37 :         objfilter |= OBJFILTER_DATABASE;
     289 CBC          37 :         dbname = argv[optind];
     290              37 :         optind++;
     291                 :     }
     292 ECB             : 
     293 CBC          46 :     if (optind < argc)
     294 ECB             :     {
     295 LBC           0 :         pg_log_error("too many command-line arguments (first is \"%s\")",
     296 ECB             :                      argv[optind]);
     297 UBC           0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     298               0 :         exit(1);
     299 EUB             :     }
     300 ECB             : 
     301                 :     /*
     302                 :      * Validate the combination of filters specified in the command-line
     303                 :      * options.
     304                 :      */
     305 GNC          46 :     check_objfilter();
     306                 : 
     307 CBC          38 :     if (vacopts.analyze_only)
     308 ECB             :     {
     309 CBC          16 :         if (vacopts.full)
     310 LBC           0 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     311 ECB             :                      "full");
     312 CBC          16 :         if (vacopts.freeze)
     313 LBC           0 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     314 ECB             :                      "freeze");
     315 GBC          16 :         if (vacopts.disable_page_skipping)
     316               1 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     317 EUB             :                      "disable-page-skipping");
     318 CBC          15 :         if (vacopts.no_index_cleanup)
     319 GIC           1 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     320 ECB             :                      "no-index-cleanup");
     321 CBC          14 :         if (vacopts.force_index_cleanup)
     322 UIC           0 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     323                 :                      "force-index-cleanup");
     324 GIC          14 :         if (!vacopts.do_truncate)
     325               1 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     326                 :                      "no-truncate");
     327 GNC          13 :         if (!vacopts.process_main)
     328               1 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     329                 :                      "no-process-main");
     330 GIC          12 :         if (!vacopts.process_toast)
     331               1 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     332 ECB             :                      "no-process-toast");
     333                 :         /* allow 'and_analyze' with 'analyze_only' */
     334                 :     }
     335                 : 
     336                 :     /* Prohibit full and analyze_only options with parallel option */
     337 GIC          33 :     if (vacopts.parallel_workers >= 0)
     338                 :     {
     339 CBC           2 :         if (vacopts.analyze_only)
     340 UIC           0 :             pg_fatal("cannot use the \"%s\" option when performing only analyze",
     341 EUB             :                      "parallel");
     342 GIC           2 :         if (vacopts.full)
     343 UBC           0 :             pg_fatal("cannot use the \"%s\" option when performing full vacuum",
     344 EUB             :                      "parallel");
     345                 :     }
     346                 : 
     347                 :     /* Prohibit --no-index-cleanup and --force-index-cleanup together */
     348 GIC          33 :     if (vacopts.no_index_cleanup && vacopts.force_index_cleanup)
     349 UIC           0 :         pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
     350                 :                  "no-index-cleanup", "force-index-cleanup");
     351 ECB             : 
     352                 :     /*
     353                 :      * buffer-usage-limit is not allowed with VACUUM FULL unless ANALYZE is
     354                 :      * included too.
     355                 :      */
     356 GNC          33 :     if (vacopts.buffer_usage_limit && vacopts.full && !vacopts.and_analyze)
     357 UNC           0 :         pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
     358                 :                  "buffer-usage-limit", "full");
     359                 : 
     360                 :     /* fill cparams except for dbname, which is set below */
     361 CBC          33 :     cparams.pghost = host;
     362 GIC          33 :     cparams.pgport = port;
     363 CBC          33 :     cparams.pguser = username;
     364 GBC          33 :     cparams.prompt_password = prompt_password;
     365 GIC          33 :     cparams.override_dbname = NULL;
     366 ECB             : 
     367 GBC          33 :     setup_cancel_handler(NULL);
     368                 : 
     369 ECB             :     /* Avoid opening extra connections. */
     370 CBC          33 :     if (tbl_count && (concurrentCons > tbl_count))
     371 UIC           0 :         concurrentCons = tbl_count;
     372 ECB             : 
     373 GNC          33 :     if (objfilter & OBJFILTER_ALL_DBS)
     374                 :     {
     375 GIC           5 :         cparams.dbname = maintenance_db;
     376 ECB             : 
     377 CBC           5 :         vacuum_all_databases(&cparams, &vacopts,
     378                 :                              analyze_in_stages,
     379 ECB             :                              concurrentCons,
     380                 :                              progname, echo, quiet);
     381                 :     }
     382                 :     else
     383                 :     {
     384 GIC          28 :         if (dbname == NULL)
     385                 :         {
     386 LBC           0 :             if (getenv("PGDATABASE"))
     387 UIC           0 :                 dbname = getenv("PGDATABASE");
     388 LBC           0 :             else if (getenv("PGUSER"))
     389 UBC           0 :                 dbname = getenv("PGUSER");
     390                 :             else
     391 LBC           0 :                 dbname = get_user_name_or_exit(progname);
     392 EUB             :         }
     393                 : 
     394 GIC          28 :         cparams.dbname = dbname;
     395                 : 
     396              28 :         if (analyze_in_stages)
     397 ECB             :         {
     398 EUB             :             int         stage;
     399                 : 
     400 GIC           4 :             for (stage = 0; stage < ANALYZE_NUM_STAGES; stage++)
     401                 :             {
     402               3 :                 vacuum_one_database(&cparams, &vacopts,
     403                 :                                     stage,
     404                 :                                     &objects,
     405 ECB             :                                     concurrentCons,
     406 EUB             :                                     progname, echo, quiet);
     407                 :             }
     408                 :         }
     409                 :         else
     410 CBC          27 :             vacuum_one_database(&cparams, &vacopts,
     411 ECB             :                                 ANALYZE_NO_STAGE,
     412                 :                                 &objects,
     413                 :                                 concurrentCons,
     414                 :                                 progname, echo, quiet);
     415                 :     }
     416                 : 
     417 GIC          30 :     exit(0);
     418                 : }
     419 ECB             : 
     420                 : /*
     421                 :  * Verify that the filters used at command line are compatible.
     422                 :  */
     423                 : void
     424 GNC          46 : check_objfilter(void)
     425                 : {
     426              46 :     if ((objfilter & OBJFILTER_ALL_DBS) &&
     427              10 :         (objfilter & OBJFILTER_DATABASE))
     428               2 :         pg_fatal("cannot vacuum all databases and a specific one at the same time");
     429                 : 
     430              44 :     if ((objfilter & OBJFILTER_ALL_DBS) &&
     431               8 :         (objfilter & OBJFILTER_TABLE))
     432               1 :         pg_fatal("cannot vacuum specific table(s) in all databases");
     433                 : 
     434              43 :     if ((objfilter & OBJFILTER_ALL_DBS) &&
     435               7 :         (objfilter & OBJFILTER_SCHEMA))
     436               1 :         pg_fatal("cannot vacuum specific schema(s) in all databases");
     437                 : 
     438              42 :     if ((objfilter & OBJFILTER_ALL_DBS) &&
     439               6 :         (objfilter & OBJFILTER_SCHEMA_EXCLUDE))
     440               1 :         pg_fatal("cannot exclude specific schema(s) in all databases");
     441                 : 
     442              41 :     if ((objfilter & OBJFILTER_TABLE) &&
     443              12 :         (objfilter & OBJFILTER_SCHEMA))
     444               1 :         pg_fatal("cannot vacuum all tables in schema(s) and specific table(s) at the same time");
     445                 : 
     446              40 :     if ((objfilter & OBJFILTER_TABLE) &&
     447              11 :         (objfilter & OBJFILTER_SCHEMA_EXCLUDE))
     448               1 :         pg_fatal("cannot vacuum specific table(s) and exclude schema(s) at the same time");
     449                 : 
     450              39 :     if ((objfilter & OBJFILTER_SCHEMA) &&
     451               2 :         (objfilter & OBJFILTER_SCHEMA_EXCLUDE))
     452               1 :         pg_fatal("cannot vacuum all tables in schema(s) and exclude schema(s) at the same time");
     453              38 : }
     454                 : 
     455 EUB             : /*
     456                 :  * vacuum_one_database
     457 ECB             :  *
     458                 :  * Process tables in the given database.  If the 'tables' list is empty,
     459                 :  * process all tables in the database.
     460                 :  *
     461                 :  * Note that this function is only concerned with running exactly one stage
     462                 :  * when in analyze-in-stages mode; caller must iterate on us if necessary.
     463                 :  *
     464                 :  * If concurrentCons is > 1, multiple connections are used to vacuum tables
     465                 :  * in parallel.  In this case and if the table list is empty, we first obtain
     466                 :  * a list of tables from the database.
     467                 :  */
     468                 : static void
     469 GIC          49 : vacuum_one_database(ConnParams *cparams,
     470 EUB             :                     vacuumingOptions *vacopts,
     471                 :                     int stage,
     472                 :                     SimpleStringList *objects,
     473                 :                     int concurrentCons,
     474                 :                     const char *progname, bool echo, bool quiet)
     475                 : {
     476                 :     PQExpBufferData sql;
     477                 :     PQExpBufferData buf;
     478 ECB             :     PQExpBufferData catalog_query;
     479                 :     PGresult   *res;
     480                 :     PGconn     *conn;
     481                 :     SimpleStringListCell *cell;
     482                 :     ParallelSlotArray *sa;
     483 GIC          49 :     SimpleStringList dbtables = {NULL, NULL};
     484 ECB             :     int         i;
     485                 :     int         ntups;
     486 CBC          49 :     bool        failed = false;
     487 GNC          49 :     bool        objects_listed = false;
     488 GIC          49 :     bool        has_where = false;
     489                 :     const char *initcmd;
     490              49 :     const char *stage_commands[] = {
     491                 :         "SET default_statistics_target=1; SET vacuum_cost_delay=0;",
     492                 :         "SET default_statistics_target=10; RESET vacuum_cost_delay;",
     493                 :         "RESET default_statistics_target;"
     494 ECB             :     };
     495 GIC          49 :     const char *stage_messages[] = {
     496                 :         gettext_noop("Generating minimal optimizer statistics (1 target)"),
     497                 :         gettext_noop("Generating medium optimizer statistics (10 targets)"),
     498                 :         gettext_noop("Generating default (full) optimizer statistics")
     499                 :     };
     500                 : 
     501 CBC          49 :     Assert(stage == ANALYZE_NO_STAGE ||
     502                 :            (stage >= 0 && stage < ANALYZE_NUM_STAGES));
     503                 : 
     504 GIC          49 :     conn = connectDatabase(cparams, progname, echo, false, true);
     505                 : 
     506              49 :     if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600)
     507                 :     {
     508 LBC           0 :         PQfinish(conn);
     509 UIC           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     510 ECB             :                  "disable-page-skipping", "9.6");
     511                 :     }
     512                 : 
     513 GIC          49 :     if (vacopts->no_index_cleanup && PQserverVersion(conn) < 120000)
     514 ECB             :     {
     515 LBC           0 :         PQfinish(conn);
     516               0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     517                 :                  "no-index-cleanup", "12");
     518 ECB             :     }
     519                 : 
     520 CBC          49 :     if (vacopts->force_index_cleanup && PQserverVersion(conn) < 120000)
     521                 :     {
     522 LBC           0 :         PQfinish(conn);
     523               0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     524 ECB             :                  "force-index-cleanup", "12");
     525                 :     }
     526                 : 
     527 CBC          49 :     if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
     528 ECB             :     {
     529 UIC           0 :         PQfinish(conn);
     530 LBC           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     531 ECB             :                  "no-truncate", "12");
     532                 :     }
     533                 : 
     534 GNC          49 :     if (!vacopts->process_main && PQserverVersion(conn) < 160000)
     535                 :     {
     536 UNC           0 :         PQfinish(conn);
     537               0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     538                 :                  "no-process-main", "16");
     539                 :     }
     540                 : 
     541 CBC          49 :     if (!vacopts->process_toast && PQserverVersion(conn) < 140000)
     542 ECB             :     {
     543 LBC           0 :         PQfinish(conn);
     544               0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     545                 :                  "no-process-toast", "14");
     546                 :     }
     547                 : 
     548 GIC          49 :     if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
     549                 :     {
     550 UIC           0 :         PQfinish(conn);
     551               0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     552                 :                  "skip-locked", "12");
     553                 :     }
     554                 : 
     555 GIC          49 :     if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
     556 UIC           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     557                 :                  "--min-xid-age", "9.6");
     558                 : 
     559 GIC          49 :     if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600)
     560 LBC           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     561                 :                  "--min-mxid-age", "9.6");
     562                 : 
     563 GIC          49 :     if (vacopts->parallel_workers >= 0 && PQserverVersion(conn) < 130000)
     564 UIC           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     565                 :                  "--parallel", "13");
     566                 : 
     567 GNC          49 :     if (vacopts->buffer_usage_limit && PQserverVersion(conn) < 160000)
     568 UNC           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     569                 :                  "--buffer-usage-limit", "16");
     570                 : 
     571                 :     /* skip_database_stats is used automatically if server supports it */
     572 GNC          49 :     vacopts->skip_database_stats = (PQserverVersion(conn) >= 160000);
     573                 : 
     574 GIC          49 :     if (!quiet)
     575                 :     {
     576              49 :         if (stage != ANALYZE_NO_STAGE)
     577               9 :             printf(_("%s: processing database \"%s\": %s\n"),
     578                 :                    progname, PQdb(conn), _(stage_messages[stage]));
     579                 :         else
     580              40 :             printf(_("%s: vacuuming database \"%s\"\n"),
     581 ECB             :                    progname, PQdb(conn));
     582 GIC          49 :         fflush(stdout);
     583                 :     }
     584 ECB             : 
     585                 :     /*
     586                 :      * Prepare the list of tables to process by querying the catalogs.
     587                 :      *
     588                 :      * Since we execute the constructed query with the default search_path
     589                 :      * (which could be unsafe), everything in this query MUST be fully
     590                 :      * qualified.
     591                 :      *
     592                 :      * First, build a WITH clause for the catalog query if any tables were
     593                 :      * specified, with a set of values made of relation names and their
     594                 :      * optional set of columns.  This is used to match any provided column
     595                 :      * lists with the generated qualified identifiers and to filter for the
     596                 :      * tables provided via --table.  If a listed table does not exist, the
     597                 :      * catalog query will fail.
     598                 :      */
     599 CBC          49 :     initPQExpBuffer(&catalog_query);
     600 GNC          61 :     for (cell = objects ? objects->head : NULL; cell; cell = cell->next)
     601                 :     {
     602              12 :         char       *just_table = NULL;
     603              12 :         const char *just_columns = NULL;
     604 ECB             : 
     605 GNC          12 :         if (!objects_listed)
     606 EUB             :         {
     607 GIC          12 :             appendPQExpBufferStr(&catalog_query,
     608                 :                                  "WITH listed_objects (object_oid, column_list) "
     609                 :                                  "AS (\n  VALUES (");
     610 GNC          12 :             objects_listed = true;
     611                 :         }
     612 EUB             :         else
     613 UBC           0 :             appendPQExpBufferStr(&catalog_query, ",\n  (");
     614                 : 
     615 GNC          12 :         if (objfilter & (OBJFILTER_SCHEMA | OBJFILTER_SCHEMA_EXCLUDE))
     616                 :         {
     617               2 :             appendStringLiteralConn(&catalog_query, cell->val, conn);
     618               2 :             appendPQExpBufferStr(&catalog_query, "::pg_catalog.regnamespace, ");
     619                 :         }
     620                 : 
     621              12 :         if (objfilter & OBJFILTER_TABLE)
     622                 :         {
     623                 :             /*
     624                 :              * Split relation and column names given by the user, this is used
     625                 :              * to feed the CTE with values on which are performed pre-run
     626                 :              * validity checks as well.  For now these happen only on the
     627                 :              * relation name.
     628                 :              */
     629              10 :             splitTableColumnsSpec(cell->val, PQclientEncoding(conn),
     630                 :                                   &just_table, &just_columns);
     631                 : 
     632              10 :             appendStringLiteralConn(&catalog_query, just_table, conn);
     633              10 :             appendPQExpBufferStr(&catalog_query, "::pg_catalog.regclass, ");
     634                 :         }
     635 ECB             : 
     636 GIC          12 :         if (just_columns && just_columns[0] != '\0')
     637 GBC           5 :             appendStringLiteralConn(&catalog_query, just_columns, conn);
     638 EUB             :         else
     639 GIC           7 :             appendPQExpBufferStr(&catalog_query, "NULL");
     640                 : 
     641              12 :         appendPQExpBufferStr(&catalog_query, "::pg_catalog.text)");
     642 ECB             : 
     643 GIC          12 :         pg_free(just_table);
     644 EUB             :     }
     645                 : 
     646                 :     /* Finish formatting the CTE */
     647 GNC          49 :     if (objects_listed)
     648 GIC          12 :         appendPQExpBufferStr(&catalog_query, "\n)\n");
     649 ECB             : 
     650 GIC          49 :     appendPQExpBufferStr(&catalog_query, "SELECT c.relname, ns.nspname");
     651 EUB             : 
     652 GNC          49 :     if (objects_listed)
     653              12 :         appendPQExpBufferStr(&catalog_query, ", listed_objects.column_list");
     654                 : 
     655 GIC          49 :     appendPQExpBufferStr(&catalog_query,
     656 ECB             :                          " FROM pg_catalog.pg_class c\n"
     657                 :                          " JOIN pg_catalog.pg_namespace ns"
     658 EUB             :                          " ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n"
     659                 :                          " LEFT JOIN pg_catalog.pg_class t"
     660                 :                          " ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid\n");
     661                 : 
     662                 :     /* Used to match the tables or schemas listed by the user */
     663 GNC          49 :     if (objects_listed)
     664                 :     {
     665              12 :         appendPQExpBufferStr(&catalog_query, " JOIN listed_objects"
     666                 :                              " ON listed_objects.object_oid ");
     667                 : 
     668              12 :         if (objfilter & OBJFILTER_SCHEMA_EXCLUDE)
     669               1 :             appendPQExpBufferStr(&catalog_query, "OPERATOR(pg_catalog.!=) ");
     670                 :         else
     671              11 :             appendPQExpBufferStr(&catalog_query, "OPERATOR(pg_catalog.=) ");
     672                 : 
     673              12 :         if (objfilter & OBJFILTER_TABLE)
     674              10 :             appendPQExpBufferStr(&catalog_query, "c.oid\n");
     675                 :         else
     676               2 :             appendPQExpBufferStr(&catalog_query, "ns.oid\n");
     677                 :     }
     678                 : 
     679 ECB             :     /*
     680 EUB             :      * If no tables were listed, filter for the relevant relation types.  If
     681                 :      * tables were given via --table, don't bother filtering by relation type.
     682                 :      * Instead, let the server decide whether a given relation can be
     683 ECB             :      * processed in which case the user will know about it.
     684 EUB             :      */
     685 GNC          49 :     if ((objfilter & OBJFILTER_TABLE) == 0)
     686                 :     {
     687 CBC          39 :         appendPQExpBufferStr(&catalog_query, " WHERE c.relkind OPERATOR(pg_catalog.=) ANY (array["
     688 EUB             :                              CppAsString2(RELKIND_RELATION) ", "
     689                 :                              CppAsString2(RELKIND_MATVIEW) "])\n");
     690 GIC          39 :         has_where = true;
     691                 :     }
     692 ECB             : 
     693                 :     /*
     694                 :      * For --min-xid-age and --min-mxid-age, the age of the relation is the
     695                 :      * greatest of the ages of the main relation and its associated TOAST
     696                 :      * table.  The commands generated by vacuumdb will also process the TOAST
     697                 :      * table for the relation if necessary, so it does not need to be
     698                 :      * considered separately.
     699                 :      */
     700 CBC          49 :     if (vacopts->min_xid_age != 0)
     701                 :     {
     702               1 :         appendPQExpBuffer(&catalog_query,
     703                 :                           " %s GREATEST(pg_catalog.age(c.relfrozenxid),"
     704                 :                           " pg_catalog.age(t.relfrozenxid)) "
     705                 :                           " OPERATOR(pg_catalog.>=) '%d'::pg_catalog.int4\n"
     706                 :                           " AND c.relfrozenxid OPERATOR(pg_catalog.!=)"
     707                 :                           " '0'::pg_catalog.xid\n",
     708                 :                           has_where ? "AND" : "WHERE", vacopts->min_xid_age);
     709 GIC           1 :         has_where = true;
     710                 :     }
     711                 : 
     712              49 :     if (vacopts->min_mxid_age != 0)
     713                 :     {
     714               1 :         appendPQExpBuffer(&catalog_query,
     715                 :                           " %s GREATEST(pg_catalog.mxid_age(c.relminmxid),"
     716                 :                           " pg_catalog.mxid_age(t.relminmxid)) OPERATOR(pg_catalog.>=)"
     717                 :                           " '%d'::pg_catalog.int4\n"
     718                 :                           " AND c.relminmxid OPERATOR(pg_catalog.!=)"
     719 ECB             :                           " '0'::pg_catalog.xid\n",
     720                 :                           has_where ? "AND" : "WHERE", vacopts->min_mxid_age);
     721 GIC           1 :         has_where = true;
     722 ECB             :     }
     723                 : 
     724                 :     /*
     725                 :      * Execute the catalog query.  We use the default search_path for this
     726                 :      * query for consistency with table lookups done elsewhere by the user.
     727                 :      */
     728 GIC          49 :     appendPQExpBufferStr(&catalog_query, " ORDER BY c.relpages DESC;");
     729              49 :     executeCommand(conn, "RESET search_path;", echo);
     730 CBC          49 :     res = executeQuery(conn, catalog_query.data, echo);
     731 GIC          48 :     termPQExpBuffer(&catalog_query);
     732              48 :     PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL, echo));
     733 EUB             : 
     734                 :     /*
     735 ECB             :      * If no rows are returned, there are no matching tables, so we are done.
     736                 :      */
     737 CBC          48 :     ntups = PQntuples(res);
     738              48 :     if (ntups == 0)
     739                 :     {
     740 GIC           2 :         PQclear(res);
     741 CBC           2 :         PQfinish(conn);
     742 GIC           2 :         return;
     743                 :     }
     744                 : 
     745                 :     /*
     746                 :      * Build qualified identifiers for each table, including the column list
     747                 :      * if given.
     748                 :      */
     749 CBC          46 :     initPQExpBuffer(&buf);
     750 GIC        2574 :     for (i = 0; i < ntups; i++)
     751                 :     {
     752 CBC        2528 :         appendPQExpBufferStr(&buf,
     753            2528 :                              fmtQualifiedId(PQgetvalue(res, i, 1),
     754 GIC        2528 :                                             PQgetvalue(res, i, 0)));
     755                 : 
     756 GNC        2528 :         if (objects_listed && !PQgetisnull(res, i, 2))
     757 CBC           5 :             appendPQExpBufferStr(&buf, PQgetvalue(res, i, 2));
     758                 : 
     759            2528 :         simple_string_list_append(&dbtables, buf.data);
     760 GIC        2528 :         resetPQExpBuffer(&buf);
     761 ECB             :     }
     762 GIC          46 :     termPQExpBuffer(&buf);
     763 CBC          46 :     PQclear(res);
     764                 : 
     765                 :     /*
     766                 :      * Ensure concurrentCons is sane.  If there are more connections than
     767 ECB             :      * vacuumable relations, we don't need to use them all.
     768                 :      */
     769 GIC          46 :     if (concurrentCons > ntups)
     770 LBC           0 :         concurrentCons = ntups;
     771 GIC          46 :     if (concurrentCons <= 0)
     772 LBC           0 :         concurrentCons = 1;
     773 ECB             : 
     774                 :     /*
     775                 :      * All slots need to be prepared to run the appropriate analyze stage, if
     776                 :      * caller requested that mode.  We have to prepare the initial connection
     777                 :      * ourselves before setting up the slots.
     778                 :      */
     779 GIC          46 :     if (stage == ANALYZE_NO_STAGE)
     780              37 :         initcmd = NULL;
     781                 :     else
     782                 :     {
     783 CBC           9 :         initcmd = stage_commands[stage];
     784 GIC           9 :         executeCommand(conn, initcmd, echo);
     785 ECB             :     }
     786                 : 
     787                 :     /*
     788                 :      * Setup the database connections. We reuse the connection we already have
     789                 :      * for the first slot.  If not in parallel mode, the first slot in the
     790                 :      * array contains the connection.
     791                 :      */
     792 GIC          46 :     sa = ParallelSlotsSetup(concurrentCons, cparams, progname, echo, initcmd);
     793 CBC          46 :     ParallelSlotsAdoptConn(sa, conn);
     794 ECB             : 
     795 GIC          46 :     initPQExpBuffer(&sql);
     796 ECB             : 
     797 GIC          46 :     cell = dbtables.head;
     798                 :     do
     799                 :     {
     800            2528 :         const char *tabname = cell->val;
     801                 :         ParallelSlot *free_slot;
     802                 : 
     803            2528 :         if (CancelRequested)
     804                 :         {
     805 LBC           0 :             failed = true;
     806 UIC           0 :             goto finish;
     807 ECB             :         }
     808                 : 
     809 GIC        2528 :         free_slot = ParallelSlotsGetIdle(sa, NULL);
     810 CBC        2528 :         if (!free_slot)
     811                 :         {
     812 UIC           0 :             failed = true;
     813               0 :             goto finish;
     814                 :         }
     815                 : 
     816 GIC        2528 :         prepare_vacuum_command(&sql, PQserverVersion(free_slot->connection),
     817                 :                                vacopts, tabname);
     818                 : 
     819                 :         /*
     820 ECB             :          * Execute the vacuum.  All errors are handled in processQueryResult
     821                 :          * through ParallelSlotsGetIdle.
     822                 :          */
     823 GIC        2528 :         ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
     824            2528 :         run_vacuum_command(free_slot->connection, sql.data,
     825                 :                            echo, tabname);
     826                 : 
     827            2528 :         cell = cell->next;
     828            2528 :     } while (cell != NULL);
     829 ECB             : 
     830 GIC          46 :     if (!ParallelSlotsWaitCompletion(sa))
     831                 :     {
     832               2 :         failed = true;
     833 GNC           2 :         goto finish;
     834                 :     }
     835                 : 
     836                 :     /* If we used SKIP_DATABASE_STATS, mop up with ONLY_DATABASE_STATS */
     837              44 :     if (vacopts->skip_database_stats && stage == ANALYZE_NO_STAGE)
     838                 :     {
     839              35 :         const char *cmd = "VACUUM (ONLY_DATABASE_STATS);";
     840              35 :         ParallelSlot *free_slot = ParallelSlotsGetIdle(sa, NULL);
     841                 : 
     842              35 :         if (!free_slot)
     843                 :         {
     844 UNC           0 :             failed = true;
     845               0 :             goto finish;
     846                 :         }
     847                 : 
     848 GNC          35 :         ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
     849              35 :         run_vacuum_command(free_slot->connection, cmd, echo, NULL);
     850                 : 
     851              35 :         if (!ParallelSlotsWaitCompletion(sa))
     852 UNC           0 :             failed = true;
     853                 :     }
     854 ECB             : 
     855 GIC          44 : finish:
     856 CBC          46 :     ParallelSlotsTerminate(sa);
     857 GIC          46 :     pg_free(sa);
     858                 : 
     859              46 :     termPQExpBuffer(&sql);
     860                 : 
     861              46 :     if (failed)
     862               2 :         exit(1);
     863 ECB             : }
     864                 : 
     865                 : /*
     866                 :  * Vacuum/analyze all connectable databases.
     867                 :  *
     868                 :  * In analyze-in-stages mode, we process all databases in one stage before
     869                 :  * moving on to the next stage.  That ensure minimal stats are available
     870                 :  * quickly everywhere before generating more detailed ones.
     871                 :  */
     872                 : static void
     873 CBC           5 : vacuum_all_databases(ConnParams *cparams,
     874 ECB             :                      vacuumingOptions *vacopts,
     875                 :                      bool analyze_in_stages,
     876                 :                      int concurrentCons,
     877                 :                      const char *progname, bool echo, bool quiet)
     878                 : {
     879                 :     PGconn     *conn;
     880                 :     PGresult   *result;
     881                 :     int         stage;
     882                 :     int         i;
     883                 : 
     884 CBC           5 :     conn = connectMaintenanceDatabase(cparams, progname, echo);
     885 GIC           5 :     result = executeQuery(conn,
     886                 :                           "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;",
     887                 :                           echo);
     888               5 :     PQfinish(conn);
     889                 : 
     890               5 :     if (analyze_in_stages)
     891 ECB             :     {
     892                 :         /*
     893                 :          * When analyzing all databases in stages, we analyze them all in the
     894                 :          * fastest stage first, so that initial statistics become available
     895                 :          * for all of them as soon as possible.
     896                 :          *
     897                 :          * This means we establish several times as many connections, but
     898                 :          * that's a secondary consideration.
     899                 :          */
     900 GIC           4 :         for (stage = 0; stage < ANALYZE_NUM_STAGES; stage++)
     901 ECB             :         {
     902 CBC           9 :             for (i = 0; i < PQntuples(result); i++)
     903                 :             {
     904               6 :                 cparams->override_dbname = PQgetvalue(result, i, 0);
     905 ECB             : 
     906 GIC           6 :                 vacuum_one_database(cparams, vacopts,
     907                 :                                     stage,
     908                 :                                     NULL,
     909                 :                                     concurrentCons,
     910                 :                                     progname, echo, quiet);
     911 ECB             :             }
     912 EUB             :         }
     913 ECB             :     }
     914 EUB             :     else
     915                 :     {
     916 GIC          17 :         for (i = 0; i < PQntuples(result); i++)
     917                 :         {
     918              13 :             cparams->override_dbname = PQgetvalue(result, i, 0);
     919                 : 
     920              13 :             vacuum_one_database(cparams, vacopts,
     921 ECB             :                                 ANALYZE_NO_STAGE,
     922                 :                                 NULL,
     923                 :                                 concurrentCons,
     924                 :                                 progname, echo, quiet);
     925                 :         }
     926                 :     }
     927                 : 
     928 GIC           5 :     PQclear(result);
     929               5 : }
     930                 : 
     931                 : /*
     932                 :  * Construct a vacuum/analyze command to run based on the given options, in the
     933                 :  * given string buffer, which may contain previous garbage.
     934 ECB             :  *
     935                 :  * The table name used must be already properly quoted.  The command generated
     936                 :  * depends on the server version involved and it is semicolon-terminated.
     937                 :  */
     938                 : static void
     939 CBC        2528 : prepare_vacuum_command(PQExpBuffer sql, int serverVersion,
     940                 :                        vacuumingOptions *vacopts, const char *table)
     941                 : {
     942            2528 :     const char *paren = " (";
     943 GIC        2528 :     const char *comma = ", ";
     944            2528 :     const char *sep = paren;
     945 ECB             : 
     946 GIC        2528 :     resetPQExpBuffer(sql);
     947 EUB             : 
     948 GBC        2528 :     if (vacopts->analyze_only)
     949                 :     {
     950 GIC        1229 :         appendPQExpBufferStr(sql, "ANALYZE");
     951 ECB             : 
     952                 :         /* parenthesized grammar of ANALYZE is supported since v11 */
     953 GIC        1229 :         if (serverVersion >= 110000)
     954 EUB             :         {
     955 GBC        1229 :             if (vacopts->skip_locked)
     956                 :             {
     957                 :                 /* SKIP_LOCKED is supported since v12 */
     958 CBC          68 :                 Assert(serverVersion >= 120000);
     959 GIC          68 :                 appendPQExpBuffer(sql, "%sSKIP_LOCKED", sep);
     960              68 :                 sep = comma;
     961                 :             }
     962            1229 :             if (vacopts->verbose)
     963                 :             {
     964 UIC           0 :                 appendPQExpBuffer(sql, "%sVERBOSE", sep);
     965 LBC           0 :                 sep = comma;
     966 ECB             :             }
     967 GIC        1229 :             if (sep != paren)
     968              68 :                 appendPQExpBufferChar(sql, ')');
     969 ECB             :         }
     970                 :         else
     971                 :         {
     972 LBC           0 :             if (vacopts->verbose)
     973 UIC           0 :                 appendPQExpBufferStr(sql, " VERBOSE");
     974 ECB             :         }
     975                 :     }
     976                 :     else
     977                 :     {
     978 GIC        1299 :         appendPQExpBufferStr(sql, "VACUUM");
     979 ECB             : 
     980                 :         /* parenthesized grammar of VACUUM is supported since v9.0 */
     981 CBC        1299 :         if (serverVersion >= 90000)
     982 ECB             :         {
     983 GIC        1299 :             if (vacopts->disable_page_skipping)
     984 ECB             :             {
     985                 :                 /* DISABLE_PAGE_SKIPPING is supported since v9.6 */
     986 GBC          68 :                 Assert(serverVersion >= 90600);
     987              68 :                 appendPQExpBuffer(sql, "%sDISABLE_PAGE_SKIPPING", sep);
     988 GIC          68 :                 sep = comma;
     989                 :             }
     990 CBC        1299 :             if (vacopts->no_index_cleanup)
     991 ECB             :             {
     992                 :                 /* "INDEX_CLEANUP FALSE" has been supported since v12 */
     993 CBC          68 :                 Assert(serverVersion >= 120000);
     994 GBC          68 :                 Assert(!vacopts->force_index_cleanup);
     995 GIC          68 :                 appendPQExpBuffer(sql, "%sINDEX_CLEANUP FALSE", sep);
     996              68 :                 sep = comma;
     997 ECB             :             }
     998 CBC        1299 :             if (vacopts->force_index_cleanup)
     999 ECB             :             {
    1000                 :                 /* "INDEX_CLEANUP TRUE" has been supported since v12 */
    1001 LBC           0 :                 Assert(serverVersion >= 120000);
    1002 UIC           0 :                 Assert(!vacopts->no_index_cleanup);
    1003 LBC           0 :                 appendPQExpBuffer(sql, "%sINDEX_CLEANUP TRUE", sep);
    1004               0 :                 sep = comma;
    1005                 :             }
    1006 GIC        1299 :             if (!vacopts->do_truncate)
    1007                 :             {
    1008                 :                 /* TRUNCATE is supported since v12 */
    1009              68 :                 Assert(serverVersion >= 120000);
    1010              68 :                 appendPQExpBuffer(sql, "%sTRUNCATE FALSE", sep);
    1011              68 :                 sep = comma;
    1012                 :             }
    1013 GNC        1299 :             if (!vacopts->process_main)
    1014                 :             {
    1015                 :                 /* PROCESS_MAIN is supported since v16 */
    1016              68 :                 Assert(serverVersion >= 160000);
    1017              68 :                 appendPQExpBuffer(sql, "%sPROCESS_MAIN FALSE", sep);
    1018              68 :                 sep = comma;
    1019                 :             }
    1020 GIC        1299 :             if (!vacopts->process_toast)
    1021                 :             {
    1022 ECB             :                 /* PROCESS_TOAST is supported since v14 */
    1023 GIC          68 :                 Assert(serverVersion >= 140000);
    1024              68 :                 appendPQExpBuffer(sql, "%sPROCESS_TOAST FALSE", sep);
    1025              68 :                 sep = comma;
    1026                 :             }
    1027 GNC        1299 :             if (vacopts->skip_database_stats)
    1028                 :             {
    1029                 :                 /* SKIP_DATABASE_STATS is supported since v16 */
    1030            1299 :                 Assert(serverVersion >= 160000);
    1031            1299 :                 appendPQExpBuffer(sql, "%sSKIP_DATABASE_STATS", sep);
    1032            1299 :                 sep = comma;
    1033                 :             }
    1034 GIC        1299 :             if (vacopts->skip_locked)
    1035                 :             {
    1036                 :                 /* SKIP_LOCKED is supported since v12 */
    1037              68 :                 Assert(serverVersion >= 120000);
    1038              68 :                 appendPQExpBuffer(sql, "%sSKIP_LOCKED", sep);
    1039              68 :                 sep = comma;
    1040 ECB             :             }
    1041 CBC        1299 :             if (vacopts->full)
    1042                 :             {
    1043 GIC          68 :                 appendPQExpBuffer(sql, "%sFULL", sep);
    1044 CBC          68 :                 sep = comma;
    1045                 :             }
    1046            1299 :             if (vacopts->freeze)
    1047                 :             {
    1048 GIC         204 :                 appendPQExpBuffer(sql, "%sFREEZE", sep);
    1049             204 :                 sep = comma;
    1050                 :             }
    1051            1299 :             if (vacopts->verbose)
    1052                 :             {
    1053 UIC           0 :                 appendPQExpBuffer(sql, "%sVERBOSE", sep);
    1054               0 :                 sep = comma;
    1055                 :             }
    1056 CBC        1299 :             if (vacopts->and_analyze)
    1057                 :             {
    1058             207 :                 appendPQExpBuffer(sql, "%sANALYZE", sep);
    1059 GIC         207 :                 sep = comma;
    1060 ECB             :             }
    1061 GIC        1299 :             if (vacopts->parallel_workers >= 0)
    1062 ECB             :             {
    1063                 :                 /* PARALLEL is supported since v13 */
    1064 GIC         136 :                 Assert(serverVersion >= 130000);
    1065             136 :                 appendPQExpBuffer(sql, "%sPARALLEL %d", sep,
    1066                 :                                   vacopts->parallel_workers);
    1067             136 :                 sep = comma;
    1068                 :             }
    1069 GNC        1299 :             if (vacopts->buffer_usage_limit)
    1070                 :             {
    1071 UNC           0 :                 Assert(serverVersion >= 160000);
    1072               0 :                 appendPQExpBuffer(sql, "%sBUFFER_USAGE_LIMIT '%s'", sep,
    1073                 :                                   vacopts->buffer_usage_limit);
    1074               0 :                 sep = comma;
    1075                 :             }
    1076 GIC        1299 :             if (sep != paren)
    1077            1299 :                 appendPQExpBufferChar(sql, ')');
    1078                 :         }
    1079 ECB             :         else
    1080                 :         {
    1081 LBC           0 :             if (vacopts->full)
    1082 UIC           0 :                 appendPQExpBufferStr(sql, " FULL");
    1083 LBC           0 :             if (vacopts->freeze)
    1084 UIC           0 :                 appendPQExpBufferStr(sql, " FREEZE");
    1085               0 :             if (vacopts->verbose)
    1086               0 :                 appendPQExpBufferStr(sql, " VERBOSE");
    1087               0 :             if (vacopts->and_analyze)
    1088               0 :                 appendPQExpBufferStr(sql, " ANALYZE");
    1089                 :         }
    1090                 :     }
    1091 ECB             : 
    1092 CBC        2528 :     appendPQExpBuffer(sql, " %s;", table);
    1093 GIC        2528 : }
    1094                 : 
    1095                 : /*
    1096                 :  * Send a vacuum/analyze command to the server, returning after sending the
    1097                 :  * command.
    1098                 :  *
    1099                 :  * Any errors during command execution are reported to stderr.
    1100                 :  */
    1101                 : static void
    1102 CBC        2563 : run_vacuum_command(PGconn *conn, const char *sql, bool echo,
    1103                 :                    const char *table)
    1104                 : {
    1105 ECB             :     bool        status;
    1106                 : 
    1107 CBC        2563 :     if (echo)
    1108 GIC         483 :         printf("%s\n", sql);
    1109 ECB             : 
    1110 GIC        2563 :     status = PQsendQuery(conn, sql) == 1;
    1111 ECB             : 
    1112 GIC        2563 :     if (!status)
    1113 ECB             :     {
    1114 UIC           0 :         if (table)
    1115               0 :             pg_log_error("vacuuming of table \"%s\" in database \"%s\" failed: %s",
    1116 ECB             :                          table, PQdb(conn), PQerrorMessage(conn));
    1117                 :         else
    1118 LBC           0 :             pg_log_error("vacuuming of database \"%s\" failed: %s",
    1119                 :                          PQdb(conn), PQerrorMessage(conn));
    1120                 :     }
    1121 CBC        2563 : }
    1122 ECB             : 
    1123                 : static void
    1124 GIC           1 : help(const char *progname)
    1125 ECB             : {
    1126 GIC           1 :     printf(_("%s cleans and analyzes a PostgreSQL database.\n\n"), progname);
    1127 GBC           1 :     printf(_("Usage:\n"));
    1128               1 :     printf(_("  %s [OPTION]... [DBNAME]\n"), progname);
    1129 GIC           1 :     printf(_("\nOptions:\n"));
    1130 CBC           1 :     printf(_("  -a, --all                       vacuum all databases\n"));
    1131               1 :     printf(_("  -d, --dbname=DBNAME             database to vacuum\n"));
    1132 GIC           1 :     printf(_("      --disable-page-skipping     disable all page-skipping behavior\n"));
    1133               1 :     printf(_("  -e, --echo                      show the commands being sent to the server\n"));
    1134               1 :     printf(_("  -f, --full                      do full vacuuming\n"));
    1135 GBC           1 :     printf(_("  -F, --freeze                    freeze row transaction information\n"));
    1136               1 :     printf(_("      --force-index-cleanup       always remove index entries that point to dead tuples\n"));
    1137 GIC           1 :     printf(_("  -j, --jobs=NUM                  use this many concurrent connections to vacuum\n"));
    1138               1 :     printf(_("      --min-mxid-age=MXID_AGE     minimum multixact ID age of tables to vacuum\n"));
    1139 GNC           1 :     printf(_("      --buffer-usage-limit=BUFSIZE size of ring buffer used for vacuum\n"));
    1140 GIC           1 :     printf(_("      --min-xid-age=XID_AGE       minimum transaction ID age of tables to vacuum\n"));
    1141               1 :     printf(_("      --no-index-cleanup          don't remove index entries that point to dead tuples\n"));
    1142 GNC           1 :     printf(_("      --no-process-main           skip the main relation\n"));
    1143 CBC           1 :     printf(_("      --no-process-toast          skip the TOAST table associated with the table to vacuum\n"));
    1144 GIC           1 :     printf(_("      --no-truncate               don't truncate empty pages at the end of the table\n"));
    1145 GNC           1 :     printf(_("  -n, --schema=PATTERN            vacuum tables in the specified schema(s) only\n"));
    1146               1 :     printf(_("  -N, --exclude-schema=PATTERN    do not vacuum tables in the specified schema(s)\n"));
    1147 GIC           1 :     printf(_("  -P, --parallel=PARALLEL_WORKERS use this many background workers for vacuum, if available\n"));
    1148 CBC           1 :     printf(_("  -q, --quiet                     don't write any messages\n"));
    1149 GIC           1 :     printf(_("      --skip-locked               skip relations that cannot be immediately locked\n"));
    1150 CBC           1 :     printf(_("  -t, --table='TABLE[(COLUMNS)]'  vacuum specific table(s) only\n"));
    1151 GIC           1 :     printf(_("  -v, --verbose                   write a lot of output\n"));
    1152               1 :     printf(_("  -V, --version                   output version information, then exit\n"));
    1153 CBC           1 :     printf(_("  -z, --analyze                   update optimizer statistics\n"));
    1154               1 :     printf(_("  -Z, --analyze-only              only update optimizer statistics; no vacuum\n"));
    1155               1 :     printf(_("      --analyze-in-stages         only update optimizer statistics, in multiple\n"
    1156                 :              "                                  stages for faster results; no vacuum\n"));
    1157               1 :     printf(_("  -?, --help                      show this help, then exit\n"));
    1158 GIC           1 :     printf(_("\nConnection options:\n"));
    1159               1 :     printf(_("  -h, --host=HOSTNAME       database server host or socket directory\n"));
    1160 CBC           1 :     printf(_("  -p, --port=PORT           database server port\n"));
    1161               1 :     printf(_("  -U, --username=USERNAME   user name to connect as\n"));
    1162               1 :     printf(_("  -w, --no-password         never prompt for password\n"));
    1163               1 :     printf(_("  -W, --password            force password prompt\n"));
    1164 GIC           1 :     printf(_("  --maintenance-db=DBNAME   alternate maintenance database\n"));
    1165 CBC           1 :     printf(_("\nRead the description of the SQL command VACUUM for details.\n"));
    1166 GIC           1 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    1167               1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
    1168 GBC           1 : }
        

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