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 : }
|