LCOV - code coverage report
Current view: top level - src/bin/scripts - vacuumdb.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 408 505 80.8 %
Date: 2025-02-22 07:14:56 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14