LCOV - code coverage report
Current view: top level - src/bin/scripts - reindexdb.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 83.2 % 388 323
Test Date: 2026-10-14 03:15:50 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 79.6 % 181 144

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * reindexdb
       4                 :             :  *
       5                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       6                 :             :  *
       7                 :             :  * src/bin/scripts/reindexdb.c
       8                 :             :  *
       9                 :             :  *-------------------------------------------------------------------------
      10                 :             :  */
      11                 :             : 
      12                 :             : #include "postgres_fe.h"
      13                 :             : 
      14                 :             : #include <limits.h>
      15                 :             : #include <stdlib.h>
      16                 :             : 
      17                 :             : #include "catalog/pg_class_d.h"
      18                 :             : #include "common.h"
      19                 :             : #include "common/logging.h"
      20                 :             : #include "fe_utils/cancel.h"
      21                 :             : #include "fe_utils/option_utils.h"
      22                 :             : #include "fe_utils/parallel_slot.h"
      23                 :             : #include "fe_utils/query_utils.h"
      24                 :             : #include "fe_utils/simple_list.h"
      25                 :             : #include "fe_utils/string_utils.h"
      26                 :             : 
      27                 :             : typedef enum ReindexType
      28                 :             : {
      29                 :             :     REINDEX_DATABASE,
      30                 :             :     REINDEX_INDEX,
      31                 :             :     REINDEX_SCHEMA,
      32                 :             :     REINDEX_SYSTEM,
      33                 :             :     REINDEX_TABLE,
      34                 :             : } ReindexType;
      35                 :             : 
      36                 :             : 
      37                 :             : static SimpleStringList *get_parallel_tables_list(PGconn *conn,
      38                 :             :                                                   ReindexType type,
      39                 :             :                                                   SimpleStringList *user_list,
      40                 :             :                                                   bool echo);
      41                 :             : static void get_parallel_tabidx_list(PGconn *conn,
      42                 :             :                                      SimpleStringList *index_list,
      43                 :             :                                      SimpleOidList **table_list,
      44                 :             :                                      bool echo);
      45                 :             : static void reindex_one_database(ConnParams *cparams, ReindexType type,
      46                 :             :                                  SimpleStringList *user_list,
      47                 :             :                                  const char *progname,
      48                 :             :                                  bool echo, bool verbose, bool concurrently,
      49                 :             :                                  int concurrentCons, const char *tablespace);
      50                 :             : static void reindex_all_databases(ConnParams *cparams,
      51                 :             :                                   const char *progname, bool echo,
      52                 :             :                                   bool quiet, bool verbose, bool concurrently,
      53                 :             :                                   int concurrentCons, const char *tablespace,
      54                 :             :                                   bool syscatalog, SimpleStringList *schemas,
      55                 :             :                                   SimpleStringList *tables,
      56                 :             :                                   SimpleStringList *indexes);
      57                 :             : static void gen_reindex_command(PGconn *conn, ReindexType type,
      58                 :             :                                 const char *name, bool echo, bool verbose,
      59                 :             :                                 bool concurrently, const char *tablespace,
      60                 :             :                                 PQExpBufferData *sql);
      61                 :             : static void run_reindex_command(PGconn *conn, ReindexType type,
      62                 :             :                                 const char *name, bool echo,
      63                 :             :                                 PQExpBufferData *sql);
      64                 :             : 
      65                 :             : static void help(const char *progname);
      66                 :             : 
      67                 :             : int
      68                 :          42 : main(int argc, char *argv[])
      69                 :             : {
      70                 :             :     static struct option long_options[] = {
      71                 :             :         {"host", required_argument, NULL, 'h'},
      72                 :             :         {"port", required_argument, NULL, 'p'},
      73                 :             :         {"username", required_argument, NULL, 'U'},
      74                 :             :         {"no-password", no_argument, NULL, 'w'},
      75                 :             :         {"password", no_argument, NULL, 'W'},
      76                 :             :         {"echo", no_argument, NULL, 'e'},
      77                 :             :         {"quiet", no_argument, NULL, 'q'},
      78                 :             :         {"schema", required_argument, NULL, 'S'},
      79                 :             :         {"dbname", required_argument, NULL, 'd'},
      80                 :             :         {"all", no_argument, NULL, 'a'},
      81                 :             :         {"system", no_argument, NULL, 's'},
      82                 :             :         {"table", required_argument, NULL, 't'},
      83                 :             :         {"index", required_argument, NULL, 'i'},
      84                 :             :         {"jobs", required_argument, NULL, 'j'},
      85                 :             :         {"verbose", no_argument, NULL, 'v'},
      86                 :             :         {"concurrently", no_argument, NULL, 1},
      87                 :             :         {"maintenance-db", required_argument, NULL, 2},
      88                 :             :         {"tablespace", required_argument, NULL, 3},
      89                 :             :         {NULL, 0, NULL, 0}
      90                 :             :     };
      91                 :             : 
      92                 :             :     const char *progname;
      93                 :             :     int         optindex;
      94                 :             :     int         c;
      95                 :             : 
      96                 :          42 :     const char *dbname = NULL;
      97                 :          42 :     const char *maintenance_db = NULL;
      98                 :          42 :     const char *host = NULL;
      99                 :          42 :     const char *port = NULL;
     100                 :          42 :     const char *username = NULL;
     101                 :          42 :     const char *tablespace = NULL;
     102                 :          42 :     enum trivalue prompt_password = TRI_DEFAULT;
     103                 :             :     ConnParams  cparams;
     104                 :          42 :     bool        syscatalog = false;
     105                 :          42 :     bool        alldb = false;
     106                 :          42 :     bool        echo = false;
     107                 :          42 :     bool        quiet = false;
     108                 :          42 :     bool        verbose = false;
     109                 :          42 :     bool        concurrently = false;
     110                 :          42 :     SimpleStringList indexes = {NULL, NULL};
     111                 :          42 :     SimpleStringList tables = {NULL, NULL};
     112                 :          42 :     SimpleStringList schemas = {NULL, NULL};
     113                 :          42 :     int         concurrentCons = 1;
     114                 :             : 
     115                 :          42 :     pg_logging_init(argv[0]);
     116                 :          42 :     progname = get_progname(argv[0]);
     117                 :          42 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
     118                 :             : 
     119                 :          42 :     handle_help_version_opts(argc, argv, "reindexdb", help);
     120                 :             : 
     121                 :             :     /* process command-line options */
     122         [ +  + ]:         120 :     while ((c = getopt_long(argc, argv, "ad:eh:i:j:qp:sS:t:U:vwW", long_options, &optindex)) != -1)
     123                 :             :     {
     124   [ +  +  +  -  :          81 :         switch (c)
          +  +  -  -  +  
          +  +  -  +  -  
             -  +  -  +  
                      + ]
     125                 :             :         {
     126                 :           7 :             case 'a':
     127                 :           7 :                 alldb = true;
     128                 :           7 :                 break;
     129                 :           2 :             case 'd':
     130                 :           2 :                 dbname = pg_strdup(optarg);
     131                 :           2 :                 break;
     132                 :           4 :             case 'e':
     133                 :           4 :                 echo = true;
     134                 :           4 :                 break;
     135                 :           0 :             case 'h':
     136                 :           0 :                 host = pg_strdup(optarg);
     137                 :           0 :                 break;
     138                 :          12 :             case 'i':
     139                 :          12 :                 simple_string_list_append(&indexes, optarg);
     140                 :          12 :                 break;
     141                 :           6 :             case 'j':
     142         [ -  + ]:           6 :                 if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
     143                 :             :                                       &concurrentCons))
     144                 :           0 :                     exit(1);
     145                 :           6 :                 break;
     146                 :           0 :             case 'q':
     147                 :           0 :                 quiet = true;
     148                 :           0 :                 break;
     149                 :           0 :             case 'p':
     150                 :           0 :                 port = pg_strdup(optarg);
     151                 :           0 :                 break;
     152                 :           8 :             case 's':
     153                 :           8 :                 syscatalog = true;
     154                 :           8 :                 break;
     155                 :           7 :             case 'S':
     156                 :           7 :                 simple_string_list_append(&schemas, optarg);
     157                 :           7 :                 break;
     158                 :          12 :             case 't':
     159                 :          12 :                 simple_string_list_append(&tables, optarg);
     160                 :          12 :                 break;
     161                 :           0 :             case 'U':
     162                 :           0 :                 username = pg_strdup(optarg);
     163                 :           0 :                 break;
     164                 :           4 :             case 'v':
     165                 :           4 :                 verbose = true;
     166                 :           4 :                 break;
     167                 :           0 :             case 'w':
     168                 :           0 :                 prompt_password = TRI_NO;
     169                 :           0 :                 break;
     170                 :           0 :             case 'W':
     171                 :           0 :                 prompt_password = TRI_YES;
     172                 :           0 :                 break;
     173                 :          11 :             case 1:
     174                 :          11 :                 concurrently = true;
     175                 :          11 :                 break;
     176                 :           0 :             case 2:
     177                 :           0 :                 maintenance_db = pg_strdup(optarg);
     178                 :           0 :                 break;
     179                 :           7 :             case 3:
     180                 :           7 :                 tablespace = pg_strdup(optarg);
     181                 :           7 :                 break;
     182                 :           1 :             default:
     183                 :             :                 /* getopt_long already emitted a complaint */
     184                 :           1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     185                 :           1 :                 exit(1);
     186                 :             :         }
     187                 :             :     }
     188                 :             : 
     189                 :             :     /*
     190                 :             :      * Non-option argument specifies database name as long as it wasn't
     191                 :             :      * already specified with -d / --dbname
     192                 :             :      */
     193   [ +  +  +  - ]:          39 :     if (optind < argc && dbname == NULL)
     194                 :             :     {
     195                 :          29 :         dbname = argv[optind];
     196                 :          29 :         optind++;
     197                 :             :     }
     198                 :             : 
     199         [ -  + ]:          39 :     if (optind < argc)
     200                 :             :     {
     201                 :           0 :         pg_log_error("too many command-line arguments (first is \"%s\")",
     202                 :             :                      argv[optind]);
     203                 :           0 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     204                 :           0 :         exit(1);
     205                 :             :     }
     206                 :             : 
     207                 :             :     /* fill cparams except for dbname, which is set below */
     208                 :          39 :     cparams.pghost = host;
     209                 :          39 :     cparams.pgport = port;
     210                 :          39 :     cparams.pguser = username;
     211                 :          39 :     cparams.prompt_password = prompt_password;
     212                 :          39 :     cparams.override_dbname = NULL;
     213                 :             : 
     214                 :          39 :     setup_cancel_handler(NULL);
     215                 :             : 
     216   [ +  +  +  + ]:          39 :     if (concurrentCons > 1 && syscatalog)
     217                 :           1 :         pg_fatal("cannot use multiple jobs to reindex system catalogs");
     218                 :             : 
     219   [ +  +  +  +  :          38 :     if (concurrentCons > 1 && concurrently && indexes.head != NULL)
                   +  + ]
     220                 :           1 :         pg_fatal("cannot use multiple jobs to reindex indexes concurrently");
     221                 :             : 
     222         [ +  + ]:          37 :     if (alldb)
     223                 :             :     {
     224         [ -  + ]:           7 :         if (dbname)
     225                 :           0 :             pg_fatal("cannot reindex all databases and a specific one at the same time");
     226                 :             : 
     227                 :           7 :         cparams.dbname = maintenance_db;
     228                 :             : 
     229                 :           7 :         reindex_all_databases(&cparams, progname, echo, quiet, verbose,
     230                 :             :                               concurrently, concurrentCons, tablespace,
     231                 :             :                               syscatalog, &schemas, &tables, &indexes);
     232                 :             :     }
     233                 :             :     else
     234                 :             :     {
     235         [ +  + ]:          30 :         if (dbname == NULL)
     236                 :             :         {
     237         [ +  - ]:           1 :             if (getenv("PGDATABASE"))
     238                 :           1 :                 dbname = getenv("PGDATABASE");
     239         [ #  # ]:           0 :             else if (getenv("PGUSER"))
     240                 :           0 :                 dbname = getenv("PGUSER");
     241                 :             :             else
     242                 :           0 :                 dbname = get_user_name_or_exit(progname);
     243                 :             :         }
     244                 :             : 
     245                 :          30 :         cparams.dbname = dbname;
     246                 :             : 
     247         [ +  + ]:          30 :         if (syscatalog)
     248                 :           6 :             reindex_one_database(&cparams, REINDEX_SYSTEM, NULL,
     249                 :             :                                  progname, echo, verbose,
     250                 :             :                                  concurrently, 1, tablespace);
     251                 :             : 
     252         [ +  + ]:          29 :         if (schemas.head != NULL)
     253                 :           5 :             reindex_one_database(&cparams, REINDEX_SCHEMA, &schemas,
     254                 :             :                                  progname, echo, verbose,
     255                 :             :                                  concurrently, concurrentCons, tablespace);
     256                 :             : 
     257         [ +  + ]:          29 :         if (indexes.head != NULL)
     258                 :           6 :             reindex_one_database(&cparams, REINDEX_INDEX, &indexes,
     259                 :             :                                  progname, echo, verbose,
     260                 :             :                                  concurrently, concurrentCons, tablespace);
     261                 :             : 
     262         [ +  + ]:          27 :         if (tables.head != NULL)
     263                 :          11 :             reindex_one_database(&cparams, REINDEX_TABLE, &tables,
     264                 :             :                                  progname, echo, verbose,
     265                 :             :                                  concurrently, concurrentCons, tablespace);
     266                 :             : 
     267                 :             :         /*
     268                 :             :          * reindex database only if neither index nor table nor schema nor
     269                 :             :          * system catalogs is specified
     270                 :             :          */
     271   [ +  +  +  + ]:          25 :         if (!syscatalog && indexes.head == NULL &&
     272   [ +  +  +  + ]:          17 :             tables.head == NULL && schemas.head == NULL)
     273                 :           5 :             reindex_one_database(&cparams, REINDEX_DATABASE, NULL,
     274                 :             :                                  progname, echo, verbose,
     275                 :             :                                  concurrently, concurrentCons, tablespace);
     276                 :             :     }
     277                 :             : 
     278                 :          31 :     exit(0);
     279                 :             : }
     280                 :             : 
     281                 :             : static void
     282                 :          51 : reindex_one_database(ConnParams *cparams, ReindexType type,
     283                 :             :                      SimpleStringList *user_list,
     284                 :             :                      const char *progname, bool echo,
     285                 :             :                      bool verbose, bool concurrently, int concurrentCons,
     286                 :             :                      const char *tablespace)
     287                 :             : {
     288                 :             :     PGconn     *conn;
     289                 :             :     SimpleStringListCell *cell;
     290                 :          51 :     SimpleOidListCell *indices_tables_cell = NULL;
     291                 :          51 :     bool        parallel = concurrentCons > 1;
     292                 :          51 :     SimpleStringList *process_list = NULL;
     293                 :          51 :     SimpleOidList *tableoid_list = NULL;
     294                 :          51 :     ReindexType process_type = type;
     295                 :             :     ParallelSlotArray *sa;
     296                 :          51 :     bool        failed = false;
     297                 :          51 :     int         items_count = 0;
     298                 :          51 :     ParallelSlot *free_slot = NULL;
     299                 :             : 
     300                 :          51 :     conn = connectDatabase(cparams, progname, echo, false, true);
     301                 :             : 
     302   [ +  +  -  + ]:          50 :     if (concurrently && PQserverVersion(conn) < 120000)
     303                 :             :     {
     304                 :           0 :         PQfinish(conn);
     305                 :           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     306                 :             :                  "concurrently", "12");
     307                 :             :     }
     308                 :             : 
     309   [ +  +  -  + ]:          50 :     if (tablespace && PQserverVersion(conn) < 140000)
     310                 :             :     {
     311                 :           0 :         PQfinish(conn);
     312                 :           0 :         pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
     313                 :             :                  "tablespace", "14");
     314                 :             :     }
     315                 :             : 
     316         [ +  + ]:          50 :     if (!parallel)
     317                 :             :     {
     318      [ +  +  - ]:          46 :         switch (process_type)
     319                 :             :         {
     320                 :          21 :             case REINDEX_DATABASE:
     321                 :             :             case REINDEX_SYSTEM:
     322                 :             : 
     323                 :             :                 /*
     324                 :             :                  * Database and system reindexes only need to work on the
     325                 :             :                  * database itself, so build a list with a single entry.
     326                 :             :                  */
     327                 :             :                 Assert(user_list == NULL);
     328                 :          21 :                 process_list = pg_malloc0_object(SimpleStringList);
     329                 :          21 :                 simple_string_list_append(process_list, PQdb(conn));
     330                 :          21 :                 break;
     331                 :             : 
     332                 :          25 :             case REINDEX_INDEX:
     333                 :             :             case REINDEX_SCHEMA:
     334                 :             :             case REINDEX_TABLE:
     335                 :          25 :                 process_list = user_list;
     336                 :             :                 Assert(user_list != NULL);
     337                 :          25 :                 break;
     338                 :             :         }
     339                 :             :     }
     340                 :             :     else
     341                 :             :     {
     342   [ +  +  -  -  :           4 :         switch (process_type)
                      - ]
     343                 :             :         {
     344                 :           3 :             case REINDEX_SCHEMA:
     345                 :             :                 Assert(user_list != NULL);
     346                 :             :                 pg_fallthrough;
     347                 :             : 
     348                 :             :             case REINDEX_DATABASE:
     349                 :             : 
     350                 :             :                 /* Build a list of relations from the database */
     351                 :           3 :                 process_list = get_parallel_tables_list(conn, process_type,
     352                 :             :                                                         user_list, echo);
     353                 :           3 :                 process_type = REINDEX_TABLE;
     354                 :             : 
     355                 :             :                 /* Bail out if nothing to process */
     356         [ +  + ]:           3 :                 if (process_list == NULL)
     357                 :             :                 {
     358                 :           1 :                     PQfinish(conn);
     359                 :           1 :                     return;
     360                 :             :                 }
     361                 :           2 :                 break;
     362                 :             : 
     363                 :           1 :             case REINDEX_INDEX:
     364                 :             :                 Assert(user_list != NULL);
     365                 :             : 
     366                 :             :                 /*
     367                 :             :                  * Generate a list of indexes and a matching list of table
     368                 :             :                  * OIDs, based on the user-specified index names.
     369                 :             :                  */
     370                 :           1 :                 get_parallel_tabidx_list(conn, user_list, &tableoid_list,
     371                 :             :                                          echo);
     372                 :             : 
     373                 :             :                 /* Bail out if nothing to process */
     374         [ -  + ]:           1 :                 if (tableoid_list == NULL)
     375                 :             :                 {
     376                 :           0 :                     PQfinish(conn);
     377                 :           0 :                     return;
     378                 :             :                 }
     379                 :             : 
     380                 :           1 :                 indices_tables_cell = tableoid_list->head;
     381                 :           1 :                 process_list = user_list;
     382                 :           1 :                 break;
     383                 :             : 
     384                 :           0 :             case REINDEX_SYSTEM:
     385                 :             :                 /* not supported */
     386                 :           0 :                 process_list = NULL;
     387                 :             :                 Assert(false);
     388                 :           0 :                 break;
     389                 :             : 
     390                 :           0 :             case REINDEX_TABLE:
     391                 :           0 :                 process_list = user_list;
     392                 :           0 :                 break;
     393                 :             :         }
     394                 :             :     }
     395                 :             : 
     396                 :             :     /*
     397                 :             :      * Adjust the number of concurrent connections depending on the items in
     398                 :             :      * the list.  We choose the minimum between the number of concurrent
     399                 :             :      * connections and the number of items in the list.
     400                 :             :      */
     401                 :          49 :     items_count = 0;
     402         [ +  - ]:          52 :     for (cell = process_list->head; cell; cell = cell->next)
     403                 :             :     {
     404                 :          52 :         items_count++;
     405                 :             : 
     406                 :             :         /* no need to continue if there are more elements than jobs */
     407         [ +  + ]:          52 :         if (items_count >= concurrentCons)
     408                 :          49 :             break;
     409                 :             :     }
     410                 :          49 :     concurrentCons = Min(concurrentCons, items_count);
     411                 :             :     Assert(concurrentCons > 0);
     412                 :             : 
     413                 :             :     Assert(process_list != NULL);
     414                 :             : 
     415                 :          49 :     sa = ParallelSlotsSetup(concurrentCons, cparams, progname, echo, NULL);
     416                 :          49 :     ParallelSlotsAdoptConn(sa, conn);
     417                 :          49 :     conn = NULL;
     418                 :             : 
     419                 :          49 :     cell = process_list->head;
     420                 :             :     do
     421                 :             :     {
     422                 :             :         PQExpBufferData sql;
     423                 :          58 :         const char *objname = cell->val;
     424                 :             : 
     425         [ -  + ]:          58 :         if (CancelRequested)
     426                 :             :         {
     427                 :           0 :             failed = true;
     428                 :           0 :             goto finish;
     429                 :             :         }
     430                 :             : 
     431                 :          58 :         free_slot = ParallelSlotsGetIdle(sa, NULL);
     432         [ -  + ]:          58 :         if (!free_slot)
     433                 :             :         {
     434                 :           0 :             failed = true;
     435                 :           0 :             goto finish;
     436                 :             :         }
     437                 :             : 
     438                 :          58 :         ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
     439                 :          58 :         initPQExpBuffer(&sql);
     440   [ +  +  +  + ]:          58 :         if (parallel && process_type == REINDEX_INDEX)
     441                 :             :         {
     442                 :             :             /*
     443                 :             :              * For parallel index-level REINDEX, the indices of the same table
     444                 :             :              * are ordered together and they are to be processed by the same
     445                 :             :              * job.  So, we put all the relevant REINDEX commands into the
     446                 :             :              * same SQL query to be processed by this job at once.
     447                 :             :              */
     448                 :           2 :             gen_reindex_command(free_slot->connection, process_type, objname,
     449                 :             :                                 echo, verbose, concurrently, tablespace, &sql);
     450         [ +  + ]:           4 :             while (indices_tables_cell->next &&
     451         [ +  + ]:           3 :                    indices_tables_cell->val == indices_tables_cell->next->val)
     452                 :             :             {
     453                 :           2 :                 indices_tables_cell = indices_tables_cell->next;
     454                 :           2 :                 cell = cell->next;
     455                 :           2 :                 objname = cell->val;
     456                 :           2 :                 appendPQExpBufferChar(&sql, '\n');
     457                 :           2 :                 gen_reindex_command(free_slot->connection, process_type, objname,
     458                 :             :                                     echo, verbose, concurrently, tablespace, &sql);
     459                 :             :             }
     460                 :           2 :             indices_tables_cell = indices_tables_cell->next;
     461                 :             :         }
     462                 :             :         else
     463                 :             :         {
     464                 :          56 :             gen_reindex_command(free_slot->connection, process_type, objname,
     465                 :             :                                 echo, verbose, concurrently, tablespace, &sql);
     466                 :             :         }
     467                 :          58 :         run_reindex_command(free_slot->connection, process_type, objname,
     468                 :             :                             echo, &sql);
     469                 :          58 :         termPQExpBuffer(&sql);
     470                 :             : 
     471                 :          58 :         cell = cell->next;
     472         [ +  + ]:          58 :     } while (cell != NULL);
     473                 :             : 
     474         [ +  + ]:          49 :     if (!ParallelSlotsWaitCompletion(sa))
     475                 :           5 :         failed = true;
     476                 :             : 
     477                 :          44 : finish:
     478         [ +  + ]:          49 :     if (process_list != user_list)
     479                 :             :     {
     480                 :          23 :         simple_string_list_destroy(process_list);
     481                 :          23 :         pg_free(process_list);
     482                 :             :     }
     483                 :             : 
     484         [ +  + ]:          49 :     if (tableoid_list)
     485                 :             :     {
     486                 :           1 :         simple_oid_list_destroy(tableoid_list);
     487                 :           1 :         pg_free(tableoid_list);
     488                 :             :     }
     489                 :             : 
     490                 :          49 :     ParallelSlotsTerminate(sa);
     491                 :          49 :     pfree(sa);
     492                 :             : 
     493         [ +  + ]:          49 :     if (failed)
     494                 :           5 :         exit(1);
     495                 :             : }
     496                 :             : 
     497                 :             : /*
     498                 :             :  * Append a SQL command required to reindex a given database object to the
     499                 :             :  * '*sql' string.
     500                 :             :  */
     501                 :             : static void
     502                 :          60 : gen_reindex_command(PGconn *conn, ReindexType type, const char *name,
     503                 :             :                     bool echo, bool verbose, bool concurrently,
     504                 :             :                     const char *tablespace, PQExpBufferData *sql)
     505                 :             : {
     506                 :          60 :     const char *paren = "(";
     507                 :          60 :     const char *comma = ", ";
     508                 :          60 :     const char *sep = paren;
     509                 :             : 
     510                 :             :     Assert(name);
     511                 :             : 
     512                 :             :     /* build the REINDEX query */
     513                 :          60 :     appendPQExpBufferStr(sql, "REINDEX ");
     514                 :             : 
     515         [ +  + ]:          60 :     if (verbose)
     516                 :             :     {
     517                 :           4 :         appendPQExpBuffer(sql, "%sVERBOSE", sep);
     518                 :           4 :         sep = comma;
     519                 :             :     }
     520                 :             : 
     521         [ +  + ]:          60 :     if (tablespace)
     522                 :             :     {
     523                 :           7 :         appendPQExpBuffer(sql, "%sTABLESPACE %s", sep,
     524                 :             :                           fmtIdEnc(tablespace, PQclientEncoding(conn)));
     525                 :           7 :         sep = comma;
     526                 :             :     }
     527                 :             : 
     528         [ +  + ]:          60 :     if (sep != paren)
     529                 :           9 :         appendPQExpBufferStr(sql, ") ");
     530                 :             : 
     531                 :             :     /* object type */
     532   [ +  +  +  +  :          60 :     switch (type)
                   +  - ]
     533                 :             :     {
     534                 :          13 :         case REINDEX_DATABASE:
     535                 :          13 :             appendPQExpBufferStr(sql, "DATABASE ");
     536                 :          13 :             break;
     537                 :          11 :         case REINDEX_INDEX:
     538                 :          11 :             appendPQExpBufferStr(sql, "INDEX ");
     539                 :          11 :             break;
     540                 :           5 :         case REINDEX_SCHEMA:
     541                 :           5 :             appendPQExpBufferStr(sql, "SCHEMA ");
     542                 :           5 :             break;
     543                 :           8 :         case REINDEX_SYSTEM:
     544                 :           8 :             appendPQExpBufferStr(sql, "SYSTEM ");
     545                 :           8 :             break;
     546                 :          23 :         case REINDEX_TABLE:
     547                 :          23 :             appendPQExpBufferStr(sql, "TABLE ");
     548                 :          23 :             break;
     549                 :             :     }
     550                 :             : 
     551                 :             :     /*
     552                 :             :      * Parenthesized grammar is only supported for CONCURRENTLY since
     553                 :             :      * PostgreSQL 14.  Since 12, CONCURRENTLY can be specified after the
     554                 :             :      * object type.
     555                 :             :      */
     556         [ +  + ]:          60 :     if (concurrently)
     557                 :          17 :         appendPQExpBufferStr(sql, "CONCURRENTLY ");
     558                 :             : 
     559                 :             :     /* object name */
     560   [ +  +  +  - ]:          60 :     switch (type)
     561                 :             :     {
     562                 :          21 :         case REINDEX_DATABASE:
     563                 :             :         case REINDEX_SYSTEM:
     564                 :          21 :             appendPQExpBufferStr(sql,
     565                 :             :                                  fmtIdEnc(name, PQclientEncoding(conn)));
     566                 :          21 :             break;
     567                 :          34 :         case REINDEX_INDEX:
     568                 :             :         case REINDEX_TABLE:
     569                 :          34 :             appendQualifiedRelation(sql, name, conn, echo);
     570                 :          34 :             break;
     571                 :           5 :         case REINDEX_SCHEMA:
     572                 :           5 :             appendPQExpBufferStr(sql, name);
     573                 :           5 :             break;
     574                 :             :     }
     575                 :             : 
     576                 :             :     /* finish the query */
     577                 :          60 :     appendPQExpBufferChar(sql, ';');
     578                 :          60 : }
     579                 :             : 
     580                 :             : /*
     581                 :             :  * Run one or more reindex commands accumulated in the '*sql' string against
     582                 :             :  * a given database connection.
     583                 :             :  */
     584                 :             : static void
     585                 :          58 : run_reindex_command(PGconn *conn, ReindexType type, const char *name,
     586                 :             :                     bool echo, PQExpBufferData *sql)
     587                 :             : {
     588                 :             :     bool        status;
     589                 :             : 
     590         [ +  + ]:          58 :     if (echo)
     591                 :           9 :         printf("%s\n", sql->data);
     592                 :             : 
     593                 :          58 :     status = PQsendQuery(conn, sql->data) == 1;
     594                 :             : 
     595         [ -  + ]:          58 :     if (!status)
     596                 :             :     {
     597   [ #  #  #  #  :           0 :         switch (type)
                   #  # ]
     598                 :             :         {
     599                 :           0 :             case REINDEX_DATABASE:
     600                 :           0 :                 pg_log_error("reindexing of database \"%s\" failed: %s",
     601                 :             :                              PQdb(conn), PQerrorMessage(conn));
     602                 :           0 :                 break;
     603                 :           0 :             case REINDEX_INDEX:
     604                 :           0 :                 pg_log_error("reindexing of index \"%s\" in database \"%s\" failed: %s",
     605                 :             :                              name, PQdb(conn), PQerrorMessage(conn));
     606                 :           0 :                 break;
     607                 :           0 :             case REINDEX_SCHEMA:
     608                 :           0 :                 pg_log_error("reindexing of schema \"%s\" in database \"%s\" failed: %s",
     609                 :             :                              name, PQdb(conn), PQerrorMessage(conn));
     610                 :           0 :                 break;
     611                 :           0 :             case REINDEX_SYSTEM:
     612                 :           0 :                 pg_log_error("reindexing of system catalogs in database \"%s\" failed: %s",
     613                 :             :                              PQdb(conn), PQerrorMessage(conn));
     614                 :           0 :                 break;
     615                 :           0 :             case REINDEX_TABLE:
     616                 :           0 :                 pg_log_error("reindexing of table \"%s\" in database \"%s\" failed: %s",
     617                 :             :                              name, PQdb(conn), PQerrorMessage(conn));
     618                 :           0 :                 break;
     619                 :             :         }
     620                 :             :     }
     621                 :          58 : }
     622                 :             : 
     623                 :             : /*
     624                 :             :  * Prepare the list of tables to process by querying the catalogs.
     625                 :             :  *
     626                 :             :  * This function will return a SimpleStringList object containing the entire
     627                 :             :  * list of tables in the given database that should be processed by a parallel
     628                 :             :  * database-wide reindex (excluding system tables), or NULL if there's no such
     629                 :             :  * table.
     630                 :             :  */
     631                 :             : static SimpleStringList *
     632                 :           3 : get_parallel_tables_list(PGconn *conn, ReindexType type,
     633                 :             :                          SimpleStringList *user_list, bool echo)
     634                 :             : {
     635                 :             :     PQExpBufferData catalog_query;
     636                 :             :     PGresult   *res;
     637                 :             :     SimpleStringList *tables;
     638                 :             :     int         ntups;
     639                 :             : 
     640                 :           3 :     initPQExpBuffer(&catalog_query);
     641                 :             : 
     642                 :             :     /*
     643                 :             :      * The queries here are using a safe search_path, so there's no need to
     644                 :             :      * fully qualify everything.
     645                 :             :      */
     646   [ +  +  -  - ]:           3 :     switch (type)
     647                 :             :     {
     648                 :           1 :         case REINDEX_DATABASE:
     649                 :             :             Assert(user_list == NULL);
     650                 :           1 :             appendPQExpBufferStr(&catalog_query,
     651                 :             :                                  "SELECT c.relname, ns.nspname\n"
     652                 :             :                                  " FROM pg_catalog.pg_class c\n"
     653                 :             :                                  " JOIN pg_catalog.pg_namespace ns"
     654                 :             :                                  " ON c.relnamespace = ns.oid\n"
     655                 :             :                                  " WHERE ns.nspname != 'pg_catalog'\n"
     656                 :             :                                  "   AND c.relkind IN ("
     657                 :             :                                  CppAsString2(RELKIND_RELATION) ", "
     658                 :             :                                  CppAsString2(RELKIND_MATVIEW) ")\n"
     659                 :             :                                  "   AND c.relpersistence != "
     660                 :             :                                  CppAsString2(RELPERSISTENCE_TEMP) "\n"
     661                 :             :                                  " ORDER BY c.relpages DESC;");
     662                 :           1 :             break;
     663                 :             : 
     664                 :           2 :         case REINDEX_SCHEMA:
     665                 :             :             {
     666                 :             :                 SimpleStringListCell *cell;
     667                 :             : 
     668                 :             :                 Assert(user_list != NULL);
     669                 :             : 
     670                 :             :                 /*
     671                 :             :                  * All the tables from all the listed schemas are grabbed at
     672                 :             :                  * once.
     673                 :             :                  */
     674                 :           2 :                 appendPQExpBufferStr(&catalog_query,
     675                 :             :                                      "SELECT c.relname, ns.nspname\n"
     676                 :             :                                      " FROM pg_catalog.pg_class c\n"
     677                 :             :                                      " JOIN pg_catalog.pg_namespace ns"
     678                 :             :                                      " ON c.relnamespace = ns.oid\n"
     679                 :             :                                      " WHERE c.relkind IN ("
     680                 :             :                                      CppAsString2(RELKIND_RELATION) ", "
     681                 :             :                                      CppAsString2(RELKIND_MATVIEW) ")\n"
     682                 :             :                                      "   AND c.relpersistence != "
     683                 :             :                                      CppAsString2(RELPERSISTENCE_TEMP) "\n"
     684                 :             :                                      " AND ns.nspname IN (");
     685                 :             : 
     686         [ +  + ]:           5 :                 for (cell = user_list->head; cell; cell = cell->next)
     687                 :             :                 {
     688         [ +  + ]:           3 :                     if (cell != user_list->head)
     689                 :           1 :                         appendPQExpBufferChar(&catalog_query, ',');
     690                 :             : 
     691                 :           3 :                     appendStringLiteralConn(&catalog_query, cell->val, conn);
     692                 :             :                 }
     693                 :             : 
     694                 :           2 :                 appendPQExpBufferStr(&catalog_query, ")\n"
     695                 :             :                                      " ORDER BY c.relpages DESC;");
     696                 :             :             }
     697                 :           2 :             break;
     698                 :             : 
     699                 :           0 :         case REINDEX_INDEX:
     700                 :             :         case REINDEX_SYSTEM:
     701                 :             :         case REINDEX_TABLE:
     702                 :             :             Assert(false);
     703                 :           0 :             break;
     704                 :             :     }
     705                 :             : 
     706                 :           3 :     res = executeQuery(conn, catalog_query.data, echo);
     707                 :           3 :     termPQExpBuffer(&catalog_query);
     708                 :             : 
     709                 :             :     /*
     710                 :             :      * If no rows are returned, there are no matching tables, so we are done.
     711                 :             :      */
     712                 :           3 :     ntups = PQntuples(res);
     713         [ +  + ]:           3 :     if (ntups == 0)
     714                 :             :     {
     715                 :           1 :         PQclear(res);
     716                 :           1 :         return NULL;
     717                 :             :     }
     718                 :             : 
     719                 :           2 :     tables = pg_malloc0_object(SimpleStringList);
     720                 :             : 
     721                 :             :     /* Build qualified identifiers for each table */
     722         [ +  + ]:          12 :     for (int i = 0; i < ntups; i++)
     723                 :             :     {
     724                 :          20 :         simple_string_list_append(tables,
     725                 :          10 :                                   fmtQualifiedIdEnc(PQgetvalue(res, i, 1),
     726                 :          10 :                                                     PQgetvalue(res, i, 0),
     727                 :             :                                                     PQclientEncoding(conn)));
     728                 :             :     }
     729                 :           2 :     PQclear(res);
     730                 :             : 
     731                 :           2 :     return tables;
     732                 :             : }
     733                 :             : 
     734                 :             : /*
     735                 :             :  * Given a user-specified list of indexes, prepare a matching list
     736                 :             :  * indexes to process, and also a matching list of table OIDs to which each
     737                 :             :  * index belongs.  The latter is needed to avoid scheduling two parallel tasks
     738                 :             :  * with concurrent reindexing of indexes on the same table.
     739                 :             :  *
     740                 :             :  * On input, index_list is the user-specified index list.  table_list is an
     741                 :             :  * output argument which is filled with a list of the tables to process; on
     742                 :             :  * output, index_list is a matching reordered list of indexes.  Caller is
     743                 :             :  * supposed to walk both lists in unison.  Both pointers will be NULL if
     744                 :             :  * there's nothing to process.
     745                 :             :  */
     746                 :             : static void
     747                 :           1 : get_parallel_tabidx_list(PGconn *conn,
     748                 :             :                          SimpleStringList *index_list,
     749                 :             :                          SimpleOidList **table_list,
     750                 :             :                          bool echo)
     751                 :             : {
     752                 :             :     PQExpBufferData catalog_query;
     753                 :             :     PGresult   *res;
     754                 :             :     SimpleStringListCell *cell;
     755                 :             :     int         ntups;
     756                 :             : 
     757                 :             :     Assert(index_list != NULL);
     758                 :             : 
     759                 :           1 :     initPQExpBuffer(&catalog_query);
     760                 :             : 
     761                 :             :     /*
     762                 :             :      * The queries here are using a safe search_path, so there's no need to
     763                 :             :      * fully qualify everything.
     764                 :             :      */
     765                 :             : 
     766                 :             :     /*
     767                 :             :      * We cannot use REINDEX in parallel in a straightforward way, because
     768                 :             :      * we'd be unable to control concurrent processing of multiple indexes on
     769                 :             :      * the same table.  But we can extract the table OID together with each
     770                 :             :      * index, so that we can send all the REINDEX INDEX commands for the same
     771                 :             :      * table together on one parallel job.
     772                 :             :      */
     773                 :           1 :     appendPQExpBufferStr(&catalog_query,
     774                 :             :                          "SELECT x.indrelid, n.nspname, i.relname\n"
     775                 :             :                          "FROM pg_catalog.pg_index x\n"
     776                 :             :                          "JOIN pg_catalog.pg_class i ON i.oid = x.indexrelid\n"
     777                 :             :                          "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = i.relnamespace\n"
     778                 :             :                          "WHERE x.indexrelid = ANY(ARRAY['");
     779                 :             : 
     780         [ +  + ]:           5 :     for (cell = index_list->head; cell; cell = cell->next)
     781                 :             :     {
     782         [ +  + ]:           4 :         if (cell != index_list->head)
     783                 :           3 :             appendPQExpBufferStr(&catalog_query, "', '");
     784                 :             : 
     785                 :           4 :         appendQualifiedRelation(&catalog_query, cell->val, conn, echo);
     786                 :             :     }
     787                 :             : 
     788                 :             :     /*
     789                 :             :      * We want all indexes of the same table together.  Order tables by the
     790                 :             :      * size of its greatest index.  Within each table, order indexes by size.
     791                 :             :      */
     792                 :           1 :     appendPQExpBufferStr(&catalog_query,
     793                 :             :                          "']::pg_catalog.regclass[])\n"
     794                 :             :                          "ORDER BY max(i.relpages) OVER \n"
     795                 :             :                          "    (PARTITION BY x.indrelid),\n"
     796                 :             :                          "  x.indrelid, i.relpages;\n");
     797                 :             : 
     798                 :             :     /* Empty the original index_list to fill it from the query result. */
     799                 :           1 :     simple_string_list_destroy(index_list);
     800                 :           1 :     index_list->head = index_list->tail = NULL;
     801                 :             : 
     802                 :           1 :     res = executeQuery(conn, catalog_query.data, echo);
     803                 :           1 :     termPQExpBuffer(&catalog_query);
     804                 :             : 
     805                 :             :     /*
     806                 :             :      * If no rows are returned, there are no matching tables, so we are done.
     807                 :             :      */
     808                 :           1 :     ntups = PQntuples(res);
     809         [ -  + ]:           1 :     if (ntups == 0)
     810                 :             :     {
     811                 :           0 :         PQclear(res);
     812                 :           0 :         return;
     813                 :             :     }
     814                 :             : 
     815                 :           1 :     *table_list = pg_malloc0_object(SimpleOidList);
     816                 :             : 
     817                 :             :     /*
     818                 :             :      * Build two lists, one with table OIDs and the other with fully-qualified
     819                 :             :      * index names.
     820                 :             :      */
     821         [ +  + ]:           5 :     for (int i = 0; i < ntups; i++)
     822                 :             :     {
     823                 :           4 :         simple_oid_list_append(*table_list, atooid(PQgetvalue(res, i, 0)));
     824                 :           8 :         simple_string_list_append(index_list,
     825                 :           4 :                                   fmtQualifiedIdEnc(PQgetvalue(res, i, 1),
     826                 :           4 :                                                     PQgetvalue(res, i, 2),
     827                 :             :                                                     PQclientEncoding(conn)));
     828                 :             :     }
     829                 :             : 
     830                 :           1 :     PQclear(res);
     831                 :             : }
     832                 :             : 
     833                 :             : static void
     834                 :           7 : reindex_all_databases(ConnParams *cparams,
     835                 :             :                       const char *progname, bool echo, bool quiet, bool verbose,
     836                 :             :                       bool concurrently, int concurrentCons,
     837                 :             :                       const char *tablespace, bool syscatalog,
     838                 :             :                       SimpleStringList *schemas, SimpleStringList *tables,
     839                 :             :                       SimpleStringList *indexes)
     840                 :             : {
     841                 :             :     PGconn     *conn;
     842                 :             :     PGresult   *result;
     843                 :             :     int         i;
     844                 :             : 
     845                 :           7 :     conn = connectMaintenanceDatabase(cparams, progname, echo);
     846                 :           7 :     result = executeQuery(conn,
     847                 :             :                           "SELECT datname FROM pg_database WHERE datallowconn AND datconnlimit <> -2 ORDER BY 1;",
     848                 :             :                           echo);
     849                 :           7 :     PQfinish(conn);
     850                 :             : 
     851         [ +  + ]:          25 :     for (i = 0; i < PQntuples(result); i++)
     852                 :             :     {
     853                 :          18 :         char       *dbname = PQgetvalue(result, i, 0);
     854                 :             : 
     855         [ +  - ]:          18 :         if (!quiet)
     856                 :             :         {
     857                 :          18 :             printf(_("%s: reindexing database \"%s\"\n"), progname, dbname);
     858                 :          18 :             fflush(stdout);
     859                 :             :         }
     860                 :             : 
     861                 :          18 :         cparams->override_dbname = dbname;
     862                 :             : 
     863         [ +  + ]:          18 :         if (syscatalog)
     864                 :           2 :             reindex_one_database(cparams, REINDEX_SYSTEM, NULL,
     865                 :             :                                  progname, echo, verbose,
     866                 :             :                                  concurrently, 1, tablespace);
     867                 :             : 
     868         [ +  + ]:          18 :         if (schemas->head != NULL)
     869                 :           2 :             reindex_one_database(cparams, REINDEX_SCHEMA, schemas,
     870                 :             :                                  progname, echo, verbose,
     871                 :             :                                  concurrently, concurrentCons, tablespace);
     872                 :             : 
     873         [ +  + ]:          18 :         if (indexes->head != NULL)
     874                 :           2 :             reindex_one_database(cparams, REINDEX_INDEX, indexes,
     875                 :             :                                  progname, echo, verbose,
     876                 :             :                                  concurrently, 1, tablespace);
     877                 :             : 
     878         [ +  + ]:          18 :         if (tables->head != NULL)
     879                 :           2 :             reindex_one_database(cparams, REINDEX_TABLE, tables,
     880                 :             :                                  progname, echo, verbose,
     881                 :             :                                  concurrently, concurrentCons, tablespace);
     882                 :             : 
     883                 :             :         /*
     884                 :             :          * reindex database only if neither index nor table nor schema nor
     885                 :             :          * system catalogs is specified
     886                 :             :          */
     887   [ +  +  +  + ]:          18 :         if (!syscatalog && indexes->head == NULL &&
     888   [ +  +  +  + ]:          14 :             tables->head == NULL && schemas->head == NULL)
     889                 :          10 :             reindex_one_database(cparams, REINDEX_DATABASE, NULL,
     890                 :             :                                  progname, echo, verbose,
     891                 :             :                                  concurrently, concurrentCons, tablespace);
     892                 :             :     }
     893                 :             : 
     894                 :           7 :     PQclear(result);
     895                 :           7 : }
     896                 :             : 
     897                 :             : static void
     898                 :           1 : help(const char *progname)
     899                 :             : {
     900                 :           1 :     printf(_("%s reindexes a PostgreSQL database.\n\n"), progname);
     901                 :           1 :     printf(_("Usage:\n"));
     902                 :           1 :     printf(_("  %s [OPTION]... [DBNAME]\n"), progname);
     903                 :           1 :     printf(_("\nOptions:\n"));
     904                 :           1 :     printf(_("  -a, --all                    reindex all databases\n"));
     905                 :           1 :     printf(_("      --concurrently           reindex concurrently\n"));
     906                 :           1 :     printf(_("  -d, --dbname=DBNAME          database to reindex\n"));
     907                 :           1 :     printf(_("  -e, --echo                   show the commands being sent to the server\n"));
     908                 :           1 :     printf(_("  -i, --index=INDEX            recreate specific index(es) only\n"));
     909                 :           1 :     printf(_("  -j, --jobs=NUM               use this many concurrent connections to reindex\n"));
     910                 :           1 :     printf(_("  -q, --quiet                  don't write any messages\n"));
     911                 :           1 :     printf(_("  -s, --system                 reindex system catalogs only\n"));
     912                 :           1 :     printf(_("  -S, --schema=SCHEMA          reindex specific schema(s) only\n"));
     913                 :           1 :     printf(_("  -t, --table=TABLE            reindex specific table(s) only\n"));
     914                 :           1 :     printf(_("      --tablespace=TABLESPACE  tablespace where indexes are rebuilt\n"));
     915                 :           1 :     printf(_("  -v, --verbose                write a lot of output\n"));
     916                 :           1 :     printf(_("  -V, --version                output version information, then exit\n"));
     917                 :           1 :     printf(_("  -?, --help                   show this help, then exit\n"));
     918                 :           1 :     printf(_("\nConnection options:\n"));
     919                 :           1 :     printf(_("  -h, --host=HOSTNAME          database server host or socket directory\n"));
     920                 :           1 :     printf(_("  -p, --port=PORT              database server port\n"));
     921                 :           1 :     printf(_("  -U, --username=USERNAME      user name to connect as\n"));
     922                 :           1 :     printf(_("  -w, --no-password            never prompt for password\n"));
     923                 :           1 :     printf(_("  -W, --password               force password prompt\n"));
     924                 :           1 :     printf(_("  --maintenance-db=DBNAME      alternate maintenance database\n"));
     925                 :           1 :     printf(_("\nRead the description of the SQL command REINDEX for details.\n"));
     926                 :           1 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     927                 :           1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
     928                 :           1 : }
        

Generated by: LCOV version 2.0-1