LCOV - code coverage report
Current view: top level - src/bin/scripts - reindexdb.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 322 388 83.0 %
Date: 2024-11-21 08:14:44 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14