Age Owner Branch data TLA 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
6834 bruce@momjian.us 61 :CBC 41 : 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 : :
6756 89 : 41 : const char *dbname = NULL;
4513 rhaas@postgresql.org 90 : 41 : const char *maintenance_db = NULL;
6756 bruce@momjian.us 91 : 41 : const char *host = NULL;
92 : 41 : const char *port = NULL;
93 : 41 : const char *username = NULL;
1138 michael@paquier.xyz 94 : 41 : const char *tablespace = NULL;
5526 peter_e@gmx.net 95 : 41 : enum trivalue prompt_password = TRI_DEFAULT;
96 : : ConnParams cparams;
6834 bruce@momjian.us 97 : 41 : bool syscatalog = false;
98 : 41 : bool alldb = false;
99 : 41 : bool echo = false;
100 : 41 : bool quiet = false;
3257 fujii@postgresql.org 101 : 41 : bool verbose = false;
1843 peter@eisentraut.org 102 : 41 : bool concurrently = false;
4105 magnus@hagander.net 103 : 41 : SimpleStringList indexes = {NULL, NULL};
104 : 41 : SimpleStringList tables = {NULL, NULL};
3414 simon@2ndQuadrant.co 105 : 41 : SimpleStringList schemas = {NULL, NULL};
1723 michael@paquier.xyz 106 : 41 : int concurrentCons = 1;
107 : :
1840 peter@eisentraut.org 108 : 41 : pg_logging_init(argv[0]);
6834 bruce@momjian.us 109 : 41 : progname = get_progname(argv[0]);
5603 peter_e@gmx.net 110 : 41 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
111 : :
6834 bruce@momjian.us 112 : 41 : handle_help_version_opts(argc, argv, "reindexdb", help);
113 : :
114 : : /* process command-line options */
489 peter@eisentraut.org 115 [ + + ]: 113 : while ((c = getopt_long(argc, argv, "ad:eh:i:j:qp:sS:t:U:vwW", long_options, &optindex)) != -1)
116 : : {
6834 bruce@momjian.us 117 [ + + + - : 75 : switch (c)
+ + - - +
+ + - + -
- + - +
+ ]
118 : : {
489 peter@eisentraut.org 119 : 7 : case 'a':
120 : 7 : alldb = true;
6834 bruce@momjian.us 121 : 7 : break;
489 peter@eisentraut.org 122 : 2 : case 'd':
123 : 2 : dbname = pg_strdup(optarg);
6834 bruce@momjian.us 124 : 2 : break;
489 peter@eisentraut.org 125 : 4 : case 'e':
126 : 4 : echo = true;
6834 bruce@momjian.us 127 : 4 : break;
489 peter@eisentraut.org 128 :UBC 0 : case 'h':
129 : 0 : host = pg_strdup(optarg);
5526 peter_e@gmx.net 130 : 0 : break;
489 peter@eisentraut.org 131 :CBC 8 : case 'i':
132 : 8 : simple_string_list_append(&indexes, optarg);
6834 bruce@momjian.us 133 : 8 : break;
489 peter@eisentraut.org 134 : 5 : case 'j':
135 [ - + ]: 5 : if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
136 : : &concurrentCons))
489 peter@eisentraut.org 137 :UBC 0 : exit(1);
6834 bruce@momjian.us 138 :CBC 5 : break;
6834 bruce@momjian.us 139 :UBC 0 : case 'q':
140 : 0 : quiet = true;
141 : 0 : break;
489 peter@eisentraut.org 142 : 0 : case 'p':
143 : 0 : port = pg_strdup(optarg);
6834 bruce@momjian.us 144 : 0 : break;
6834 bruce@momjian.us 145 :CBC 8 : case 's':
146 : 8 : syscatalog = true;
147 : 8 : break;
489 peter@eisentraut.org 148 : 7 : case 'S':
149 : 7 : simple_string_list_append(&schemas, optarg);
150 : 7 : break;
6834 bruce@momjian.us 151 : 12 : case 't':
4105 magnus@hagander.net 152 : 12 : simple_string_list_append(&tables, optarg);
6834 bruce@momjian.us 153 : 12 : break;
489 peter@eisentraut.org 154 :UBC 0 : case 'U':
155 : 0 : username = pg_strdup(optarg);
1723 michael@paquier.xyz 156 : 0 : break;
3257 fujii@postgresql.org 157 :CBC 4 : case 'v':
158 : 4 : verbose = true;
159 : 4 : break;
489 peter@eisentraut.org 160 :UBC 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;
1843 peter@eisentraut.org 166 :CBC 10 : case 1:
167 : 10 : concurrently = true;
168 : 10 : break;
4513 rhaas@postgresql.org 169 :UBC 0 : case 2:
4202 bruce@momjian.us 170 : 0 : maintenance_db = pg_strdup(optarg);
4513 rhaas@postgresql.org 171 : 0 : break;
1138 michael@paquier.xyz 172 :CBC 7 : case 3:
173 : 7 : tablespace = pg_strdup(optarg);
174 : 7 : break;
6834 bruce@momjian.us 175 : 1 : default:
176 : : /* getopt_long already emitted a complaint */
737 tgl@sss.pgh.pa.us 177 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
6834 bruce@momjian.us 178 : 1 : 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 : : */
4380 andrew@dunslane.net 186 [ + + + - ]: 38 : if (optind < argc && dbname == NULL)
187 : : {
188 : 28 : dbname = argv[optind];
189 : 28 : optind++;
190 : : }
191 : :
192 [ - + ]: 38 : if (optind < argc)
193 : : {
1840 peter@eisentraut.org 194 :UBC 0 : pg_log_error("too many command-line arguments (first is \"%s\")",
195 : : argv[optind]);
737 tgl@sss.pgh.pa.us 196 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
4380 andrew@dunslane.net 197 : 0 : exit(1);
198 : : }
199 : :
200 : : /* fill cparams except for dbname, which is set below */
1273 tgl@sss.pgh.pa.us 201 :CBC 38 : cparams.pghost = host;
202 : 38 : cparams.pgport = port;
203 : 38 : cparams.pguser = username;
204 : 38 : cparams.prompt_password = prompt_password;
205 : 38 : cparams.override_dbname = NULL;
206 : :
1595 michael@paquier.xyz 207 : 38 : setup_cancel_handler(NULL);
208 : :
20 akorotkov@postgresql 209 [ + + + + ]:GNC 38 : if (concurrentCons > 1 && syscatalog)
210 : 1 : pg_fatal("cannot use multiple jobs to reindex system catalogs");
211 : :
6834 bruce@momjian.us 212 [ + + ]:CBC 37 : if (alldb)
213 : : {
214 [ - + ]: 7 : if (dbname)
737 tgl@sss.pgh.pa.us 215 :UBC 0 : pg_fatal("cannot reindex all databases and a specific one at the same time");
216 : :
1273 tgl@sss.pgh.pa.us 217 :CBC 7 : cparams.dbname = maintenance_db;
218 : :
219 : 7 : reindex_all_databases(&cparams, progname, echo, quiet, verbose,
220 : : concurrently, concurrentCons, tablespace,
221 : : syscatalog, &schemas, &tables, &indexes);
222 : : }
223 : : else
224 : : {
6834 bruce@momjian.us 225 [ + + ]: 30 : if (dbname == NULL)
226 : : {
227 [ + - ]: 1 : if (getenv("PGDATABASE"))
228 : 1 : dbname = getenv("PGDATABASE");
6834 bruce@momjian.us 229 [ # # ]:UBC 0 : else if (getenv("PGUSER"))
230 : 0 : dbname = getenv("PGUSER");
231 : : else
3770 232 : 0 : dbname = get_user_name_or_exit(progname);
233 : : }
234 : :
1273 tgl@sss.pgh.pa.us 235 :CBC 30 : cparams.dbname = dbname;
236 : :
34 nathan@postgresql.or 237 [ + + ]:GNC 30 : if (syscatalog)
238 : 6 : reindex_one_database(&cparams, REINDEX_SYSTEM, NULL,
239 : : progname, echo, verbose,
240 : : concurrently, 1, tablespace);
241 : :
3414 simon@2ndQuadrant.co 242 [ + + ]:CBC 29 : if (schemas.head != NULL)
1273 tgl@sss.pgh.pa.us 243 : 5 : reindex_one_database(&cparams, REINDEX_SCHEMA, &schemas,
244 : : progname, echo, verbose,
245 : : concurrently, concurrentCons, tablespace);
246 : :
4105 magnus@hagander.net 247 [ + + ]: 29 : if (indexes.head != NULL)
1273 tgl@sss.pgh.pa.us 248 : 6 : reindex_one_database(&cparams, REINDEX_INDEX, &indexes,
249 : : progname, echo, verbose,
250 : : concurrently, concurrentCons, tablespace);
251 : :
4105 magnus@hagander.net 252 [ + + ]: 27 : if (tables.head != NULL)
1273 tgl@sss.pgh.pa.us 253 : 11 : 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 : : */
34 nathan@postgresql.or 261 [ + + + + ]:GNC 25 : if (!syscatalog && indexes.head == NULL &&
262 [ + + + + ]: 17 : tables.head == NULL && schemas.head == NULL)
1273 tgl@sss.pgh.pa.us 263 :CBC 5 : reindex_one_database(&cparams, REINDEX_DATABASE, NULL,
264 : : progname, echo, verbose,
265 : : concurrently, concurrentCons, tablespace);
266 : : }
267 : :
6834 bruce@momjian.us 268 : 31 : exit(0);
269 : : }
270 : :
271 : : static void
1130 rhaas@postgresql.org 272 : 52 : 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;
20 akorotkov@postgresql 280 :GNC 52 : SimpleStringListCell *indices_tables_cell = NULL;
1723 michael@paquier.xyz 281 :CBC 52 : bool parallel = concurrentCons > 1;
282 : 52 : SimpleStringList *process_list = user_list;
20 akorotkov@postgresql 283 :GNC 52 : SimpleStringList *indices_tables_list = NULL;
1723 michael@paquier.xyz 284 :CBC 52 : ReindexType process_type = type;
285 : : ParallelSlotArray *sa;
286 : 52 : bool failed = false;
287 : 52 : int items_count = 0;
20 akorotkov@postgresql 288 :GNC 52 : char *prev_index_table_name = NULL;
289 : 52 : ParallelSlot *free_slot = NULL;
290 : :
261 nathan@postgresql.or 291 : 52 : conn = connectDatabase(cparams, progname, echo, false, true);
292 : :
1843 peter@eisentraut.org 293 [ + + - + ]:CBC 51 : if (concurrently && PQserverVersion(conn) < 120000)
294 : : {
1843 peter@eisentraut.org 295 :UBC 0 : PQfinish(conn);
737 tgl@sss.pgh.pa.us 296 : 0 : pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
297 : : "concurrently", "12");
298 : : }
299 : :
1138 michael@paquier.xyz 300 [ + + - + ]:CBC 51 : if (tablespace && PQserverVersion(conn) < 140000)
301 : : {
1138 michael@paquier.xyz 302 :UBC 0 : PQfinish(conn);
737 tgl@sss.pgh.pa.us 303 : 0 : pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
304 : : "tablespace", "14");
305 : : }
306 : :
1723 michael@paquier.xyz 307 [ + + ]:CBC 51 : if (!parallel)
308 : : {
309 [ + + - ]: 47 : switch (process_type)
310 : : {
311 : 22 : 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 [ - + ]: 22 : Assert(user_list == NULL);
319 : 22 : process_list = pg_malloc0(sizeof(SimpleStringList));
320 : 22 : simple_string_list_append(process_list, PQdb(conn));
321 : 22 : break;
322 : :
323 : 25 : case REINDEX_INDEX:
324 : : case REINDEX_SCHEMA:
325 : : case REINDEX_TABLE:
326 [ - + ]: 25 : Assert(user_list != NULL);
327 : 25 : break;
328 : : }
329 : : }
330 : : else
331 : : {
332 [ + + + - : 4 : switch (process_type)
- - ]
333 : : {
334 : 1 : case REINDEX_DATABASE:
335 : :
336 : : /* Build a list of relations from the database */
337 : 1 : process_list = get_parallel_object_list(conn, process_type,
338 : : user_list, echo);
339 : 1 : process_type = REINDEX_TABLE;
340 : :
341 : : /* Bail out if nothing to process */
342 [ - + ]: 1 : if (process_list == NULL)
1723 michael@paquier.xyz 343 :UBC 0 : return;
1723 michael@paquier.xyz 344 :CBC 1 : break;
345 : :
346 : 2 : case REINDEX_SCHEMA:
347 [ - + ]: 2 : Assert(user_list != NULL);
348 : :
349 : : /* Build a list of relations from all the schemas */
350 : 2 : process_list = get_parallel_object_list(conn, process_type,
351 : : user_list, echo);
352 : 2 : process_type = REINDEX_TABLE;
353 : :
354 : : /* Bail out if nothing to process */
355 [ + + ]: 2 : if (process_list == NULL)
356 : 1 : return;
357 : 1 : break;
358 : :
1723 michael@paquier.xyz 359 :GIC 1 : case REINDEX_INDEX:
20 akorotkov@postgresql 360 [ - + ]:GNC 1 : 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 : 1 : 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 [ - + ]: 1 : if (user_list->head == NULL)
20 akorotkov@postgresql 374 :UNC 0 : return;
375 : :
376 : : /*
377 : : * Assuming 'user_list' is not empty, 'indices_tables_list'
378 : : * shouldn't be empty as well.
379 : : */
20 akorotkov@postgresql 380 [ - + ]:GNC 1 : Assert(indices_tables_list != NULL);
381 : 1 : indices_tables_cell = indices_tables_list->head;
382 : :
383 : 1 : break;
384 : :
20 akorotkov@postgresql 385 :UNC 0 : case REINDEX_SYSTEM:
386 : : /* not supported */
1723 michael@paquier.xyz 387 :UBC 0 : Assert(false);
388 : : 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 : : }
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 : : */
1723 michael@paquier.xyz 405 [ + - ]:CBC 53 : for (cell = process_list->head; cell; cell = cell->next)
406 : : {
407 : 53 : items_count++;
408 : :
409 : : /* no need to continue if there are more elements than jobs */
410 [ + + ]: 53 : if (items_count >= concurrentCons)
411 : 50 : break;
412 : : }
413 : 50 : concurrentCons = Min(concurrentCons, items_count);
414 [ - + ]: 50 : Assert(concurrentCons > 0);
415 : :
416 [ - + ]: 50 : Assert(process_list != NULL);
417 : :
1130 rhaas@postgresql.org 418 : 50 : sa = ParallelSlotsSetup(concurrentCons, cparams, progname, echo, NULL);
419 : 50 : ParallelSlotsAdoptConn(sa, conn);
420 : :
1723 michael@paquier.xyz 421 : 50 : cell = process_list->head;
422 : : do
423 : : {
424 : 59 : const char *objname = cell->val;
20 akorotkov@postgresql 425 :GNC 59 : bool need_new_slot = true;
426 : :
1723 michael@paquier.xyz 427 [ - + ]:CBC 59 : if (CancelRequested)
428 : : {
1723 michael@paquier.xyz 429 :UBC 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 : : */
20 akorotkov@postgresql 439 [ + + + + ]:GNC 59 : if (parallel && process_type == REINDEX_INDEX)
440 : : {
441 [ + + ]: 2 : if (prev_index_table_name != NULL &&
442 [ - + ]: 1 : strcmp(prev_index_table_name, indices_tables_cell->val) == 0)
20 akorotkov@postgresql 443 :UNC 0 : need_new_slot = false;
20 akorotkov@postgresql 444 :GNC 2 : prev_index_table_name = indices_tables_cell->val;
445 : 2 : indices_tables_cell = indices_tables_cell->next;
446 : : }
447 : :
448 [ + - ]: 59 : if (need_new_slot)
449 : : {
450 : 59 : free_slot = ParallelSlotsGetIdle(sa, NULL);
451 [ - + ]: 59 : if (!free_slot)
452 : : {
20 akorotkov@postgresql 453 :UNC 0 : failed = true;
454 : 0 : goto finish;
455 : : }
456 : :
20 akorotkov@postgresql 457 :GNC 59 : ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
458 : : }
459 : :
1723 michael@paquier.xyz 460 :CBC 59 : run_reindex_command(free_slot->connection, process_type, objname,
461 : : echo, verbose, concurrently, true, tablespace);
462 : :
463 : 59 : cell = cell->next;
464 [ + + ]: 59 : } while (cell != NULL);
465 : :
1130 rhaas@postgresql.org 466 [ + + ]: 50 : if (!ParallelSlotsWaitCompletion(sa))
1723 michael@paquier.xyz 467 : 5 : failed = true;
468 : :
469 : 45 : finish:
1720 470 [ + + ]: 50 : if (process_list != user_list)
471 : : {
472 : 24 : simple_string_list_destroy(process_list);
473 : 24 : pg_free(process_list);
474 : : }
475 : :
20 akorotkov@postgresql 476 [ + + ]:GNC 50 : if (indices_tables_list)
477 : : {
478 : 1 : simple_string_list_destroy(indices_tables_list);
479 : 1 : pg_free(indices_tables_list);
480 : : }
481 : :
1130 rhaas@postgresql.org 482 :CBC 50 : ParallelSlotsTerminate(sa);
483 : 50 : pfree(sa);
484 : :
1723 michael@paquier.xyz 485 [ + + ]: 50 : if (failed)
486 : 5 : exit(1);
487 : : }
488 : :
489 : : static void
490 : 59 : run_reindex_command(PGconn *conn, ReindexType type, const char *name,
491 : : bool echo, bool verbose, bool concurrently, bool async,
492 : : const char *tablespace)
493 : : {
1138 494 : 59 : const char *paren = "(";
495 : 59 : const char *comma = ", ";
496 : 59 : const char *sep = paren;
497 : : PQExpBufferData sql;
498 : : bool status;
499 : :
1723 500 [ - + ]: 59 : Assert(name);
501 : :
502 : : /* build the REINDEX query */
6834 bruce@momjian.us 503 : 59 : initPQExpBuffer(&sql);
504 : :
2239 noah@leadboat.com 505 : 59 : appendPQExpBufferStr(&sql, "REINDEX ");
506 : :
3257 fujii@postgresql.org 507 [ + + ]: 59 : if (verbose)
508 : : {
1138 michael@paquier.xyz 509 : 4 : appendPQExpBuffer(&sql, "%sVERBOSE", sep);
510 : 4 : sep = comma;
511 : : }
512 : :
513 [ + + ]: 59 : if (tablespace)
514 : : {
515 : 7 : appendPQExpBuffer(&sql, "%sTABLESPACE %s", sep, fmtId(tablespace));
516 : 7 : sep = comma;
517 : : }
518 : :
519 [ + + ]: 59 : if (sep != paren)
520 : 9 : appendPQExpBufferStr(&sql, ") ");
521 : :
522 : : /* object type */
1748 523 [ + + + + : 59 : switch (type)
+ - ]
524 : : {
525 : 14 : case REINDEX_DATABASE:
526 : 14 : appendPQExpBufferStr(&sql, "DATABASE ");
527 : 14 : break;
528 : 9 : case REINDEX_INDEX:
529 : 9 : appendPQExpBufferStr(&sql, "INDEX ");
530 : 9 : break;
531 : 5 : case REINDEX_SCHEMA:
532 : 5 : appendPQExpBufferStr(&sql, "SCHEMA ");
533 : 5 : break;
534 : 8 : case REINDEX_SYSTEM:
535 : 8 : appendPQExpBufferStr(&sql, "SYSTEM ");
536 : 8 : break;
537 : 23 : case REINDEX_TABLE:
538 : 23 : appendPQExpBufferStr(&sql, "TABLE ");
539 : 23 : 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 : : */
1843 peter@eisentraut.org 547 [ + + ]: 59 : if (concurrently)
548 : 17 : appendPQExpBufferStr(&sql, "CONCURRENTLY ");
549 : :
550 : : /* object name */
1748 michael@paquier.xyz 551 [ + + + - ]: 59 : switch (type)
552 : : {
553 : 22 : case REINDEX_DATABASE:
554 : : case REINDEX_SYSTEM:
1723 555 : 22 : appendPQExpBufferStr(&sql, fmtId(name));
1748 556 : 22 : break;
557 : 32 : case REINDEX_INDEX:
558 : : case REINDEX_TABLE:
1731 559 : 32 : appendQualifiedRelation(&sql, name, conn, echo);
1748 560 : 32 : break;
561 : 5 : case REINDEX_SCHEMA:
562 : 5 : appendPQExpBufferStr(&sql, name);
563 : 5 : break;
564 : : }
565 : :
566 : : /* finish the query */
3209 heikki.linnakangas@i 567 : 59 : appendPQExpBufferChar(&sql, ';');
568 : :
1723 michael@paquier.xyz 569 [ + - ]: 59 : if (async)
570 : : {
571 [ + + ]: 59 : if (echo)
572 : 10 : printf("%s\n", sql.data);
573 : :
574 : 59 : status = PQsendQuery(conn, sql.data) == 1;
575 : : }
576 : : else
1723 michael@paquier.xyz 577 :UBC 0 : status = executeMaintenanceCommand(conn, sql.data, echo);
578 : :
1723 michael@paquier.xyz 579 [ - + ]:CBC 59 : if (!status)
580 : : {
1748 michael@paquier.xyz 581 [ # # # # :UBC 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:
1437 peter@eisentraut.org 596 : 0 : pg_log_error("reindexing of system catalogs in database \"%s\" failed: %s",
597 : : PQdb(conn), PQerrorMessage(conn));
1748 michael@paquier.xyz 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 : : }
1723 604 [ # # ]: 0 : if (!async)
605 : : {
606 : 0 : PQfinish(conn);
607 : 0 : exit(1);
608 : : }
609 : : }
610 : :
6834 bruce@momjian.us 611 :CBC 59 : termPQExpBuffer(&sql);
612 : 59 : }
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 *
1723 michael@paquier.xyz 623 : 4 : 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 : 4 : 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 [ + + + - : 4 : switch (type)
- ]
640 : : {
641 : 1 : case REINDEX_DATABASE:
642 [ - + ]: 1 : Assert(user_list == NULL);
1277 drowley@postgresql.o 643 : 1 : 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;");
1723 michael@paquier.xyz 653 : 1 : break;
654 : :
655 : 2 : case REINDEX_SCHEMA:
656 : : {
657 : : SimpleStringListCell *cell;
658 : 2 : bool nsp_listed = false;
659 : :
660 [ - + ]: 2 : Assert(user_list != NULL);
661 : :
662 : : /*
663 : : * All the tables from all the listed schemas are grabbed at
664 : : * once.
665 : : */
1277 drowley@postgresql.o 666 : 2 : 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 : :
1723 michael@paquier.xyz 676 [ + + ]: 5 : for (cell = user_list->head; cell; cell = cell->next)
677 : : {
678 : 3 : const char *nspname = cell->val;
679 : :
680 [ + + ]: 3 : if (nsp_listed)
1277 drowley@postgresql.o 681 : 1 : appendPQExpBufferStr(&catalog_query, ", ");
682 : : else
1723 michael@paquier.xyz 683 : 2 : nsp_listed = true;
684 : :
685 : 3 : appendStringLiteralConn(&catalog_query, nspname, conn);
686 : : }
687 : :
1277 drowley@postgresql.o 688 : 2 : appendPQExpBufferStr(&catalog_query, ")\n"
689 : : " ORDER BY c.relpages DESC;");
690 : : }
1723 michael@paquier.xyz 691 : 2 : break;
692 : :
1723 michael@paquier.xyz 693 :GIC 1 : case REINDEX_INDEX:
694 : : {
695 : : SimpleStringListCell *cell;
696 : :
20 akorotkov@postgresql 697 [ - + ]:GNC 1 : 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 : 1 : 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 [ + + ]: 3 : for (cell = user_list->head; cell; cell = cell->next)
720 : : {
721 [ + + ]: 2 : if (cell != user_list->head)
722 : 1 : appendPQExpBufferStr(&catalog_query, "', '");
723 : :
724 : 2 : 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 : 1 : 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 : 1 : simple_string_list_destroy(user_list);
743 : 1 : user_list->head = user_list->tail = NULL;
744 : : }
745 : 1 : break;
746 : :
20 akorotkov@postgresql 747 :UNC 0 : case REINDEX_SYSTEM:
748 : : case REINDEX_TABLE:
1723 michael@paquier.xyz 749 :UBC 0 : Assert(false);
750 : : break;
751 : : }
752 : :
1723 michael@paquier.xyz 753 :CBC 4 : res = executeQuery(conn, catalog_query.data, echo);
754 : 4 : termPQExpBuffer(&catalog_query);
755 : :
756 : : /*
757 : : * If no rows are returned, there are no matching tables, so we are done.
758 : : */
759 : 4 : ntups = PQntuples(res);
760 [ + + ]: 4 : if (ntups == 0)
761 : : {
762 : 1 : PQclear(res);
763 : 1 : PQfinish(conn);
764 : 1 : return NULL;
765 : : }
766 : :
767 : 3 : tables = pg_malloc0(sizeof(SimpleStringList));
768 : :
769 : : /* Build qualified identifiers for each table */
770 : 3 : initPQExpBuffer(&buf);
771 [ + + ]: 15 : for (i = 0; i < ntups; i++)
772 : : {
773 : 12 : appendPQExpBufferStr(&buf,
774 : 12 : fmtQualifiedId(PQgetvalue(res, i, 1),
775 : 12 : PQgetvalue(res, i, 0)));
776 : :
777 : 12 : simple_string_list_append(tables, buf.data);
778 : 12 : resetPQExpBuffer(&buf);
779 : :
20 akorotkov@postgresql 780 [ + + ]:GNC 12 : 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 : 2 : appendPQExpBufferStr(&buf,
787 : 2 : fmtQualifiedId(PQgetvalue(res, i, 1),
788 : 2 : PQgetvalue(res, i, 2)));
789 : :
790 : 2 : simple_string_list_append(user_list, buf.data);
791 : 2 : resetPQExpBuffer(&buf);
792 : : }
793 : : }
1723 michael@paquier.xyz 794 :CBC 3 : termPQExpBuffer(&buf);
795 : 3 : PQclear(res);
796 : :
797 : 3 : return tables;
798 : : }
799 : :
800 : : static void
1273 tgl@sss.pgh.pa.us 801 : 7 : 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 : 7 : conn = connectMaintenanceDatabase(cparams, progname, echo);
276 andres@anarazel.de 813 : 7 : result = executeQuery(conn,
814 : : "SELECT datname FROM pg_database WHERE datallowconn AND datconnlimit <> -2 ORDER BY 1;",
815 : : echo);
6834 bruce@momjian.us 816 : 7 : PQfinish(conn);
817 : :
818 [ + + ]: 26 : for (i = 0; i < PQntuples(result); i++)
819 : : {
6756 820 : 19 : char *dbname = PQgetvalue(result, i, 0);
821 : :
6834 822 [ + - ]: 19 : if (!quiet)
823 : : {
6159 peter_e@gmx.net 824 : 19 : printf(_("%s: reindexing database \"%s\"\n"), progname, dbname);
825 : 19 : fflush(stdout);
826 : : }
827 : :
1273 tgl@sss.pgh.pa.us 828 : 19 : cparams->override_dbname = dbname;
829 : :
34 nathan@postgresql.or 830 [ + + ]:GNC 19 : if (syscatalog)
831 : 2 : reindex_one_database(cparams, REINDEX_SYSTEM, NULL,
832 : : progname, echo, verbose,
833 : : concurrently, 1, tablespace);
834 : :
835 [ + + ]: 19 : if (schemas->head != NULL)
836 : 2 : reindex_one_database(cparams, REINDEX_SCHEMA, schemas,
837 : : progname, echo, verbose,
838 : : concurrently, concurrentCons, tablespace);
839 : :
840 [ + + ]: 19 : if (indexes->head != NULL)
841 : 2 : reindex_one_database(cparams, REINDEX_INDEX, indexes,
842 : : progname, echo, verbose,
843 : : concurrently, 1, tablespace);
844 : :
845 [ + + ]: 19 : if (tables->head != NULL)
846 : 2 : 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 [ + + + + ]: 19 : if (!syscatalog && indexes->head == NULL &&
855 [ + + + + ]: 15 : tables->head == NULL && schemas->head == NULL)
856 : 11 : reindex_one_database(cparams, REINDEX_DATABASE, NULL,
857 : : progname, echo, verbose,
858 : : concurrently, concurrentCons, tablespace);
859 : : }
860 : :
6834 bruce@momjian.us 861 :CBC 7 : PQclear(result);
862 : 7 : }
863 : :
864 : : static void
865 : 1 : help(const char *progname)
866 : : {
867 : 1 : printf(_("%s reindexes a PostgreSQL database.\n\n"), progname);
868 : 1 : printf(_("Usage:\n"));
869 : 1 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
870 : 1 : printf(_("\nOptions:\n"));
1138 michael@paquier.xyz 871 : 1 : printf(_(" -a, --all reindex all databases\n"));
872 : 1 : printf(_(" --concurrently reindex concurrently\n"));
873 : 1 : printf(_(" -d, --dbname=DBNAME database to reindex\n"));
874 : 1 : printf(_(" -e, --echo show the commands being sent to the server\n"));
875 : 1 : printf(_(" -i, --index=INDEX recreate specific index(es) only\n"));
876 : 1 : printf(_(" -j, --jobs=NUM use this many concurrent connections to reindex\n"));
877 : 1 : printf(_(" -q, --quiet don't write any messages\n"));
900 magnus@hagander.net 878 : 1 : printf(_(" -s, --system reindex system catalogs only\n"));
1138 michael@paquier.xyz 879 : 1 : printf(_(" -S, --schema=SCHEMA reindex specific schema(s) only\n"));
880 : 1 : printf(_(" -t, --table=TABLE reindex specific table(s) only\n"));
881 : 1 : printf(_(" --tablespace=TABLESPACE tablespace where indexes are rebuilt\n"));
882 : 1 : printf(_(" -v, --verbose write a lot of output\n"));
883 : 1 : printf(_(" -V, --version output version information, then exit\n"));
884 : 1 : printf(_(" -?, --help show this help, then exit\n"));
6834 bruce@momjian.us 885 : 1 : printf(_("\nConnection options:\n"));
1138 michael@paquier.xyz 886 : 1 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
887 : 1 : printf(_(" -p, --port=PORT database server port\n"));
888 : 1 : printf(_(" -U, --username=USERNAME user name to connect as\n"));
889 : 1 : printf(_(" -w, --no-password never prompt for password\n"));
890 : 1 : printf(_(" -W, --password force password prompt\n"));
891 : 1 : printf(_(" --maintenance-db=DBNAME alternate maintenance database\n"));
6834 bruce@momjian.us 892 : 1 : printf(_("\nRead the description of the SQL command REINDEX for details.\n"));
1507 peter@eisentraut.org 893 : 1 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
894 : 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
6834 bruce@momjian.us 895 : 1 : }
|