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

Generated by: LCOV version 1.14