PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_amcheck.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_amcheck.c
4 * Detects corruption within database relations.
5 *
6 * Copyright (c) 2017-2026, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * src/bin/pg_amcheck/pg_amcheck.c
10 *
11 *-------------------------------------------------------------------------
12 */
13#include "postgres_fe.h"
14
15#include <limits.h>
16#include <time.h>
17
18#include "catalog/pg_am_d.h"
19#include "catalog/pg_class_d.h"
20#include "catalog/pg_namespace_d.h"
21#include "common/logging.h"
22#include "common/username.h"
23#include "fe_utils/cancel.h"
29#include "getopt_long.h"
30#include "pgtime.h"
31#include "storage/block.h"
32
33typedef struct PatternInfo
34{
35 const char *pattern; /* Unaltered pattern from the command line */
36 char *db_regex; /* Database regexp parsed from pattern, or
37 * NULL */
38 char *nsp_regex; /* Schema regexp parsed from pattern, or NULL */
39 char *rel_regex; /* Relation regexp parsed from pattern, or
40 * NULL */
41 bool heap_only; /* true if rel_regex should only match heap
42 * tables */
43 bool btree_only; /* true if rel_regex should only match btree
44 * indexes */
45 bool matched; /* true if the pattern matched in any database */
47
53
54/* pg_amcheck command line options controlled by user flags */
55typedef struct AmcheckOptions
56{
58 bool alldb;
59 bool echo;
60 bool verbose;
63 int jobs;
64
65 /*
66 * Whether to install missing extensions, and optionally the name of the
67 * schema in which to install the extension's objects.
68 */
71
72 /* Objects to check or not to check, as lists of PatternInfo structs. */
75
76 /*
77 * As an optimization, if any pattern in the exclude list applies to heap
78 * tables, or similarly if any such pattern applies to btree indexes, or
79 * to schemas, then these will be true, otherwise false. These should
80 * always agree with what you'd conclude by grep'ing through the exclude
81 * list.
82 */
86
87 /*
88 * If any inclusion pattern exists, then we should only be checking
89 * matching relations rather than all relations, so this is true iff
90 * include is empty.
91 */
92 bool allrel;
93
94 /* heap table checking options */
100 const char *skip;
101
102 /* btree index checking options */
107
108 /* heap and btree hybrid option */
111
113 .dbpattern = false,
114 .alldb = false,
115 .echo = false,
116 .verbose = false,
117 .strict_names = true,
118 .show_progress = false,
119 .jobs = 1,
120 .install_missing = false,
121 .install_schema = "pg_catalog",
122 .include = {NULL, 0},
123 .exclude = {NULL, 0},
124 .excludetbl = false,
125 .excludeidx = false,
126 .excludensp = false,
127 .allrel = true,
128 .no_toast_expansion = false,
129 .reconcile_toast = true,
130 .on_error_stop = false,
131 .startblock = -1,
132 .endblock = -1,
133 .skip = "none",
134 .parent_check = false,
135 .rootdescend = false,
136 .heapallindexed = false,
137 .checkunique = false,
138 .no_btree_expansion = false
139};
140
141static const char *progname = NULL;
142
143/* Whether all relations have so far passed their corruption checks */
144static bool all_checks_pass = true;
145
146/* Time last progress report was displayed */
148static bool progress_since_last_stderr = false;
149
150typedef struct DatabaseInfo
151{
152 char *datname;
153 char *amcheck_schema; /* escaped, quoted literal */
156
157typedef struct RelationInfo
158{
159 const DatabaseInfo *datinfo; /* shared by other relinfos */
161 bool is_heap; /* true if heap, false if btree */
162 char *nspname;
163 char *relname;
166 char *sql; /* set during query run, pg_free'd after */
168
169/*
170 * Query for determining if contrib's amcheck is installed. If so, selects the
171 * namespace name where amcheck's functions can be found.
172 */
173static const char *const amcheck_sql =
174"SELECT n.nspname, x.extversion FROM pg_catalog.pg_extension x"
175"\nJOIN pg_catalog.pg_namespace n ON x.extnamespace = n.oid"
176"\nWHERE x.extname = 'amcheck'";
177
178static void prepare_heap_command(PQExpBuffer sql, RelationInfo *rel,
179 PGconn *conn);
181 PGconn *conn);
182static void run_command(ParallelSlot *slot, const char *sql);
184 void *context);
185static bool verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context);
186static void help(const char *progname);
189 const char *datname, bool force, bool finished);
190
191static void append_database_pattern(PatternInfoArray *pia, const char *pattern,
192 int encoding);
193static void append_schema_pattern(PatternInfoArray *pia, const char *pattern,
194 int encoding);
195static void append_relation_pattern(PatternInfoArray *pia, const char *pattern,
196 int encoding);
197static void append_heap_pattern(PatternInfoArray *pia, const char *pattern,
198 int encoding);
199static void append_btree_pattern(PatternInfoArray *pia, const char *pattern,
200 int encoding);
201static void compile_database_list(PGconn *conn, SimplePtrList *databases,
202 const char *initial_dbname);
204 const DatabaseInfo *dat,
206
207#define log_no_match(...) do { \
208 if (opts.strict_names) \
209 pg_log_error(__VA_ARGS__); \
210 else \
211 pg_log_warning(__VA_ARGS__); \
212 } while(0)
213
214#define FREE_AND_SET_NULL(x) do { \
215 pg_free(x); \
216 (x) = NULL; \
217 } while (0)
218
219int
220main(int argc, char *argv[])
221{
222 PGconn *conn = NULL;
223 SimplePtrListCell *cell;
224 SimplePtrList databases = {NULL, NULL};
225 SimplePtrList relations = {NULL, NULL};
226 bool failed = false;
227 const char *latest_datname;
228 int parallel_workers;
230 PQExpBufferData sql;
231 uint64 reltotal = 0;
233 uint64 pagestotal = 0;
235
236 static struct option long_options[] = {
237 /* Connection options */
238 {"host", required_argument, NULL, 'h'},
239 {"port", required_argument, NULL, 'p'},
240 {"username", required_argument, NULL, 'U'},
241 {"no-password", no_argument, NULL, 'w'},
242 {"password", no_argument, NULL, 'W'},
243 {"maintenance-db", required_argument, NULL, 1},
244
245 /* check options */
246 {"all", no_argument, NULL, 'a'},
247 {"database", required_argument, NULL, 'd'},
248 {"exclude-database", required_argument, NULL, 'D'},
249 {"echo", no_argument, NULL, 'e'},
250 {"index", required_argument, NULL, 'i'},
251 {"exclude-index", required_argument, NULL, 'I'},
252 {"jobs", required_argument, NULL, 'j'},
253 {"progress", no_argument, NULL, 'P'},
254 {"relation", required_argument, NULL, 'r'},
255 {"exclude-relation", required_argument, NULL, 'R'},
256 {"schema", required_argument, NULL, 's'},
257 {"exclude-schema", required_argument, NULL, 'S'},
258 {"table", required_argument, NULL, 't'},
259 {"exclude-table", required_argument, NULL, 'T'},
260 {"verbose", no_argument, NULL, 'v'},
261 {"no-dependent-indexes", no_argument, NULL, 2},
262 {"no-dependent-toast", no_argument, NULL, 3},
263 {"exclude-toast-pointers", no_argument, NULL, 4},
264 {"on-error-stop", no_argument, NULL, 5},
265 {"skip", required_argument, NULL, 6},
266 {"startblock", required_argument, NULL, 7},
267 {"endblock", required_argument, NULL, 8},
268 {"rootdescend", no_argument, NULL, 9},
269 {"no-strict-names", no_argument, NULL, 10},
270 {"heapallindexed", no_argument, NULL, 11},
271 {"parent-check", no_argument, NULL, 12},
272 {"install-missing", optional_argument, NULL, 13},
273 {"checkunique", no_argument, NULL, 14},
274
275 {NULL, 0, NULL, 0}
276 };
277
278 int optindex;
279 int c;
280
281 const char *db = NULL;
282 const char *maintenance_db = NULL;
283
284 const char *host = NULL;
285 const char *port = NULL;
286 const char *username = NULL;
287 enum trivalue prompt_password = TRI_DEFAULT;
289 ConnParams cparams;
290
291 pg_logging_init(argv[0]);
292 progname = get_progname(argv[0]);
293 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_amcheck"));
294
296
297 /* process command-line options */
298 while ((c = getopt_long(argc, argv, "ad:D:eh:Hi:I:j:p:Pr:R:s:S:t:T:U:vwW",
299 long_options, &optindex)) != -1)
300 {
301 char *endptr;
302 unsigned long optval;
303
304 switch (c)
305 {
306 case 'a':
307 opts.alldb = true;
308 break;
309 case 'd':
310 opts.dbpattern = true;
312 break;
313 case 'D':
314 opts.dbpattern = true;
316 break;
317 case 'e':
318 opts.echo = true;
319 break;
320 case 'h':
321 host = pg_strdup(optarg);
322 break;
323 case 'i':
324 opts.allrel = false;
326 break;
327 case 'I':
328 opts.excludeidx = true;
330 break;
331 case 'j':
332 if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
333 &opts.jobs))
334 exit(1);
335 break;
336 case 'p':
338 break;
339 case 'P':
340 opts.show_progress = true;
341 break;
342 case 'r':
343 opts.allrel = false;
345 break;
346 case 'R':
347 opts.excludeidx = true;
348 opts.excludetbl = true;
350 break;
351 case 's':
352 opts.allrel = false;
354 break;
355 case 'S':
356 opts.excludensp = true;
358 break;
359 case 't':
360 opts.allrel = false;
362 break;
363 case 'T':
364 opts.excludetbl = true;
366 break;
367 case 'U':
369 break;
370 case 'v':
371 opts.verbose = true;
373 break;
374 case 'w':
375 prompt_password = TRI_NO;
376 break;
377 case 'W':
378 prompt_password = TRI_YES;
379 break;
380 case 1:
382 break;
383 case 2:
385 break;
386 case 3:
388 break;
389 case 4:
390 opts.reconcile_toast = false;
391 break;
392 case 5:
393 opts.on_error_stop = true;
394 break;
395 case 6:
396 if (pg_strcasecmp(optarg, "all-visible") == 0)
397 opts.skip = "all-visible";
398 else if (pg_strcasecmp(optarg, "all-frozen") == 0)
399 opts.skip = "all-frozen";
400 else if (pg_strcasecmp(optarg, "none") == 0)
401 opts.skip = "none";
402 else
403 pg_fatal("invalid argument for option %s", "--skip");
404 break;
405 case 7:
406 errno = 0;
407 optval = strtoul(optarg, &endptr, 10);
408 if (endptr == optarg || *endptr != '\0' || errno != 0)
409 pg_fatal("invalid start block");
411 pg_fatal("start block out of bounds");
413 break;
414 case 8:
415 errno = 0;
416 optval = strtoul(optarg, &endptr, 10);
417 if (endptr == optarg || *endptr != '\0' || errno != 0)
418 pg_fatal("invalid end block");
420 pg_fatal("end block out of bounds");
422 break;
423 case 9:
424 opts.rootdescend = true;
425 opts.parent_check = true;
426 break;
427 case 10:
428 opts.strict_names = false;
429 break;
430 case 11:
431 opts.heapallindexed = true;
432 break;
433 case 12:
434 opts.parent_check = true;
435 break;
436 case 13:
437 opts.install_missing = true;
438 if (optarg)
440 break;
441 case 14:
442 opts.checkunique = true;
443 break;
444 default:
445 /* getopt_long already emitted a complaint */
446 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
447 exit(1);
448 }
449 }
450
452 pg_fatal("end block precedes start block");
453
454 /*
455 * A single non-option arguments specifies a database name or connection
456 * string.
457 */
458 if (optind < argc)
459 {
460 db = argv[optind];
461 optind++;
462 }
463
464 if (optind < argc)
465 {
466 pg_log_error("too many command-line arguments (first is \"%s\")",
467 argv[optind]);
468 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
469 exit(1);
470 }
471
472 /* fill cparams except for dbname, which is set below */
473 cparams.pghost = host;
474 cparams.pgport = port;
475 cparams.pguser = username;
476 cparams.prompt_password = prompt_password;
477 cparams.dbname = NULL;
478 cparams.override_dbname = NULL;
479
481
482 /* choose the database for our initial connection */
483 if (opts.alldb)
484 {
485 if (db != NULL)
486 pg_fatal("cannot specify a database name with --all");
487 cparams.dbname = maintenance_db;
488 }
489 else if (db != NULL)
490 {
491 if (opts.dbpattern)
492 pg_fatal("cannot specify both a database name and database patterns");
493 cparams.dbname = db;
494 }
495
496 if (opts.alldb || opts.dbpattern)
497 {
499 compile_database_list(conn, &databases, NULL);
500 }
501 else
502 {
503 if (cparams.dbname == NULL)
504 {
505 if (getenv("PGDATABASE"))
506 cparams.dbname = getenv("PGDATABASE");
507 else if (getenv("PGUSER"))
508 cparams.dbname = getenv("PGUSER");
509 else
511 }
512 conn = connectDatabase(&cparams, progname, opts.echo, false, true);
513 compile_database_list(conn, &databases, PQdb(conn));
514 }
515
516 if (databases.head == NULL)
517 {
518 if (conn != NULL)
520 pg_log_warning("no databases to check");
521 exit(0);
522 }
523
524 /*
525 * Compile a list of all relations spanning all databases to be checked.
526 */
527 for (cell = databases.head; cell; cell = cell->next)
528 {
530 int ntups;
531 const char *amcheck_schema = NULL;
532 DatabaseInfo *dat = (DatabaseInfo *) cell->ptr;
533
534 cparams.override_dbname = dat->datname;
535 if (conn == NULL || strcmp(PQdb(conn), dat->datname) != 0)
536 {
537 if (conn != NULL)
539 conn = connectDatabase(&cparams, progname, opts.echo, false, true);
540 }
541
542 /*
543 * Optionally install amcheck if not already installed in this
544 * database.
545 */
547 {
548 char *schema;
549 char *install_sql;
550
551 /*
552 * Must re-escape the schema name for each database, as the
553 * escaping rules may change.
554 */
557 install_sql = psprintf("CREATE EXTENSION IF NOT EXISTS amcheck WITH SCHEMA %s",
558 schema);
559
562 PQfreemem(schema);
563 }
564
565 /*
566 * Verify that amcheck is installed for this next database. User
567 * error could result in a database not having amcheck that should
568 * have it, but we also could be iterating over multiple databases
569 * where not all of them have amcheck installed (for example,
570 * 'template1').
571 */
574 {
575 /* Querying the catalog failed. */
576 pg_log_error("database \"%s\": %s",
578 pg_log_error_detail("Query was: %s", amcheck_sql);
581 exit(1);
582 }
583 ntups = PQntuples(result);
584 if (ntups == 0)
585 {
586 /* Querying the catalog succeeded, but amcheck is missing. */
587 pg_log_warning("skipping database \"%s\": amcheck is not installed",
588 PQdb(conn));
591 conn = NULL;
592 continue;
593 }
594 amcheck_schema = PQgetvalue(result, 0, 0);
595 if (opts.verbose)
596 pg_log_info("in database \"%s\": using amcheck version \"%s\" in schema \"%s\"",
597 PQdb(conn), PQgetvalue(result, 0, 1), amcheck_schema);
598 dat->amcheck_schema = PQescapeIdentifier(conn, amcheck_schema,
599 strlen(amcheck_schema));
600
601 /*
602 * Check the version of amcheck extension. Skip requested unique
603 * constraint check with warning if it is not yet supported by
604 * amcheck.
605 */
606 if (opts.checkunique == true)
607 {
608 /*
609 * Now amcheck has only major and minor versions in the string but
610 * we also support revision just in case. Now it is expected to be
611 * zero.
612 */
613 int vmaj = 0,
614 vmin = 0,
615 vrev = 0;
616 const char *amcheck_version = PQgetvalue(result, 0, 1);
617
618 sscanf(amcheck_version, "%d.%d.%d", &vmaj, &vmin, &vrev);
619
620 /*
621 * checkunique option is supported in amcheck since version 1.4
622 */
623 if ((vmaj == 1 && vmin < 4) || vmaj == 0)
624 {
625 pg_log_warning("option %s is not supported by amcheck version %s",
626 "--checkunique", amcheck_version);
627 dat->is_checkunique = false;
628 }
629 else
630 dat->is_checkunique = true;
631 }
632
634
636 }
637
638 /*
639 * Check that all inclusion patterns matched at least one schema or
640 * relation that we can check.
641 */
642 for (size_t pattern_id = 0; pattern_id < opts.include.len; pattern_id++)
643 {
645
646 if (!pat->matched && (pat->nsp_regex != NULL || pat->rel_regex != NULL))
647 {
648 failed = opts.strict_names;
649
650 if (pat->heap_only)
651 log_no_match("no heap tables to check matching \"%s\"",
652 pat->pattern);
653 else if (pat->btree_only)
654 log_no_match("no btree indexes to check matching \"%s\"",
655 pat->pattern);
656 else if (pat->rel_regex == NULL)
657 log_no_match("no relations to check in schemas matching \"%s\"",
658 pat->pattern);
659 else
660 log_no_match("no relations to check matching \"%s\"",
661 pat->pattern);
662 }
663 }
664
665 if (failed)
666 {
667 if (conn != NULL)
669 exit(1);
670 }
671
672 /*
673 * Set parallel_workers to the lesser of opts.jobs and the number of
674 * relations.
675 */
676 parallel_workers = 0;
677 for (cell = relations.head; cell; cell = cell->next)
678 {
679 reltotal++;
680 if (parallel_workers < opts.jobs)
681 parallel_workers++;
682 }
683
684 if (reltotal == 0)
685 {
686 if (conn != NULL)
688 pg_fatal("no relations to check");
689 }
691 NULL, true, false);
692
693 /*
694 * Main event loop.
695 *
696 * We use server-side parallelism to check up to parallel_workers
697 * relations in parallel. The list of relations was computed in database
698 * order, which minimizes the number of connects and disconnects as we
699 * process the list.
700 */
702 sa = ParallelSlotsSetup(parallel_workers, &cparams, progname, opts.echo,
703 NULL);
704 if (conn != NULL)
705 {
707 conn = NULL;
708 }
709
710 initPQExpBuffer(&sql);
711 for (relprogress = 0, cell = relations.head; cell; cell = cell->next)
712 {
714 RelationInfo *rel;
715
716 rel = (RelationInfo *) cell->ptr;
717
718 if (CancelRequested)
719 {
720 failed = true;
721 break;
722 }
723
724 /*
725 * The list of relations is in database sorted order. If this next
726 * relation is in a different database than the last one seen, we are
727 * about to start checking this database. Note that other slots may
728 * still be working on relations from prior databases.
729 */
731
733 latest_datname, false, false);
734
735 relprogress++;
737
738 /*
739 * Get a parallel slot for the next amcheck command, blocking if
740 * necessary until one is available, or until a previously issued slot
741 * command fails, indicating that we should abort checking the
742 * remaining objects.
743 */
745 if (!free_slot)
746 {
747 /*
748 * Something failed. We don't need to know what it was, because
749 * the handler should already have emitted the necessary error
750 * messages.
751 */
752 failed = true;
753 break;
754 }
755
756 if (opts.verbose)
758
759 /*
760 * Execute the appropriate amcheck command for this relation using our
761 * slot's database connection. We do not wait for the command to
762 * complete, nor do we perform any error checking, as that is done by
763 * the parallel slots and our handler callback functions.
764 */
765 if (rel->is_heap)
766 {
767 if (opts.verbose)
768 {
770 fprintf(stderr, "\n");
771 pg_log_info("checking heap table \"%s.%s.%s\"",
772 rel->datinfo->datname, rel->nspname, rel->relname);
774 }
775 prepare_heap_command(&sql, rel, free_slot->connection);
776 rel->sql = pstrdup(sql.data); /* pg_free'd after command */
779 }
780 else
781 {
782 if (opts.verbose)
783 {
785 fprintf(stderr, "\n");
786
787 pg_log_info("checking btree index \"%s.%s.%s\"",
788 rel->datinfo->datname, rel->nspname, rel->relname);
790 }
791 prepare_btree_command(&sql, rel, free_slot->connection);
792 rel->sql = pstrdup(sql.data); /* pg_free'd after command */
795 }
796 }
797 termPQExpBuffer(&sql);
798
799 if (!failed)
800 {
801
802 /*
803 * Wait for all slots to complete, or for one to indicate that an
804 * error occurred. Like above, we rely on the handler emitting the
805 * necessary error messages.
806 */
808 failed = true;
809
811 }
812
813 if (sa)
814 {
817 }
818
819 if (failed)
820 exit(1);
821
822 if (!all_checks_pass)
823 exit(2);
824}
825
826/*
827 * prepare_heap_command
828 *
829 * Creates a SQL command for running amcheck checking on the given heap
830 * relation. The command is phrased as a SQL query, with column order and
831 * names matching the expectations of verify_heap_slot_handler, which will
832 * receive and handle each row returned from the verify_heapam() function.
833 *
834 * The constructed SQL command will silently skip temporary tables, as checking
835 * them would needlessly draw errors from the underlying amcheck function.
836 *
837 * sql: buffer into which the heap table checking command will be written
838 * rel: relation information for the heap table to be checked
839 * conn: the connection to be used, for string escaping purposes
840 */
841static void
843{
844 resetPQExpBuffer(sql);
846 "SELECT v.blkno, v.offnum, v.attnum, v.msg "
847 "FROM pg_catalog.pg_class c, %s.verify_heapam("
848 "\nrelation := c.oid, on_error_stop := %s, check_toast := %s, skip := '%s'",
850 opts.on_error_stop ? "true" : "false",
851 opts.reconcile_toast ? "true" : "false",
852 opts.skip);
853
854 if (opts.startblock >= 0)
855 appendPQExpBuffer(sql, ", startblock := " INT64_FORMAT, opts.startblock);
856 if (opts.endblock >= 0)
857 appendPQExpBuffer(sql, ", endblock := " INT64_FORMAT, opts.endblock);
858
860 "\n) v WHERE c.oid = %u "
861 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP),
862 rel->reloid);
863}
864
865/*
866 * prepare_btree_command
867 *
868 * Creates a SQL command for running amcheck checking on the given btree index
869 * relation. The command does not select any columns, as btree checking
870 * functions do not return any, but rather return corruption information by
871 * raising errors, which verify_btree_slot_handler expects.
872 *
873 * The constructed SQL command will silently skip temporary indexes, and
874 * indexes being reindexed concurrently, as checking them would needlessly draw
875 * errors from the underlying amcheck functions.
876 *
877 * sql: buffer into which the heap table checking command will be written
878 * rel: relation information for the index to be checked
879 * conn: the connection to be used, for string escaping purposes
880 */
881static void
883{
884 resetPQExpBuffer(sql);
885
886 if (opts.parent_check)
888 "SELECT %s.bt_index_parent_check("
889 "index := c.oid, heapallindexed := %s, rootdescend := %s "
890 "%s)"
891 "\nFROM pg_catalog.pg_class c, pg_catalog.pg_index i "
892 "WHERE c.oid = %u "
893 "AND c.oid = i.indexrelid "
894 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP) " "
895 "AND i.indisready AND i.indisvalid AND i.indislive",
897 (opts.heapallindexed ? "true" : "false"),
898 (opts.rootdescend ? "true" : "false"),
899 (rel->datinfo->is_checkunique ? ", checkunique := true" : ""),
900 rel->reloid);
901 else
903 "SELECT %s.bt_index_check("
904 "index := c.oid, heapallindexed := %s "
905 "%s)"
906 "\nFROM pg_catalog.pg_class c, pg_catalog.pg_index i "
907 "WHERE c.oid = %u "
908 "AND c.oid = i.indexrelid "
909 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP) " "
910 "AND i.indisready AND i.indisvalid AND i.indislive",
912 (opts.heapallindexed ? "true" : "false"),
913 (rel->datinfo->is_checkunique ? ", checkunique := true" : ""),
914 rel->reloid);
915}
916
917/*
918 * run_command
919 *
920 * Sends a command to the server without waiting for the command to complete.
921 * Logs an error if the command cannot be sent, but otherwise any errors are
922 * expected to be handled by a ParallelSlotHandler.
923 *
924 * If reconnecting to the database is necessary, the cparams argument may be
925 * modified.
926 *
927 * slot: slot with connection to the server we should use for the command
928 * sql: query to send
929 */
930static void
931run_command(ParallelSlot *slot, const char *sql)
932{
933 if (opts.echo)
934 printf("%s\n", sql);
935
936 if (PQsendQuery(slot->connection, sql) == 0)
937 {
938 pg_log_error("error sending command to database \"%s\": %s",
939 PQdb(slot->connection),
941 pg_log_error_detail("Command was: %s", sql);
942 exit(1);
943 }
944}
945
946/*
947 * should_processing_continue
948 *
949 * Checks a query result returned from a query (presumably issued on a slot's
950 * connection) to determine if parallel slots should continue issuing further
951 * commands.
952 *
953 * Note: Heap relation corruption is reported by verify_heapam() via the result
954 * set, rather than an ERROR, but running verify_heapam() on a corrupted heap
955 * table may still result in an error being returned from the server due to
956 * missing relation files, bad checksums, etc. The btree corruption checking
957 * functions always use errors to communicate corruption messages. We can't
958 * just abort processing because we got a mere ERROR.
959 *
960 * res: result from an executed sql query
961 */
962static bool
964{
965 const char *severity;
966
967 switch (PQresultStatus(res))
968 {
969 /* These are expected and ok */
970 case PGRES_COMMAND_OK:
971 case PGRES_TUPLES_OK:
973 break;
974
975 /* This is expected but requires closer scrutiny */
978 if (severity == NULL)
979 return false; /* libpq failure, probably lost connection */
980 if (strcmp(severity, "FATAL") == 0)
981 return false;
982 if (strcmp(severity, "PANIC") == 0)
983 return false;
984 break;
985
986 /* These are unexpected */
989 case PGRES_COPY_OUT:
990 case PGRES_COPY_IN:
991 case PGRES_COPY_BOTH:
996 return false;
997 }
998 return true;
999}
1000
1001/*
1002 * Returns a copy of the argument string with all lines indented four spaces.
1003 *
1004 * The caller should pg_free the result when finished with it.
1005 */
1006static char *
1007indent_lines(const char *str)
1008{
1010 const char *c;
1011 char *result;
1012
1015 for (c = str; *c; c++)
1016 {
1018 if (c[0] == '\n' && c[1] != '\0')
1020 }
1021 result = pstrdup(buf.data);
1023
1024 return result;
1025}
1026
1027/*
1028 * verify_heap_slot_handler
1029 *
1030 * ParallelSlotHandler that receives results from a heap table checking command
1031 * created by prepare_heap_command and outputs the results for the user.
1032 *
1033 * res: result from an executed sql query
1034 * conn: connection on which the sql query was executed
1035 * context: the sql query being handled, as a cstring
1036 */
1037static bool
1039{
1040 RelationInfo *rel = (RelationInfo *) context;
1041
1042 if (PQresultStatus(res) == PGRES_TUPLES_OK)
1043 {
1044 int i;
1045 int ntups = PQntuples(res);
1046
1047 if (ntups > 0)
1048 all_checks_pass = false;
1049
1050 for (i = 0; i < ntups; i++)
1051 {
1052 const char *msg;
1053
1054 /* The message string should never be null, but check */
1055 if (PQgetisnull(res, i, 3))
1056 msg = "NO MESSAGE";
1057 else
1058 msg = PQgetvalue(res, i, 3);
1059
1060 if (!PQgetisnull(res, i, 2))
1061 printf(_("heap table \"%s.%s.%s\", block %s, offset %s, attribute %s:\n"),
1062 rel->datinfo->datname, rel->nspname, rel->relname,
1063 PQgetvalue(res, i, 0), /* blkno */
1064 PQgetvalue(res, i, 1), /* offnum */
1065 PQgetvalue(res, i, 2)); /* attnum */
1066
1067 else if (!PQgetisnull(res, i, 1))
1068 printf(_("heap table \"%s.%s.%s\", block %s, offset %s:\n"),
1069 rel->datinfo->datname, rel->nspname, rel->relname,
1070 PQgetvalue(res, i, 0), /* blkno */
1071 PQgetvalue(res, i, 1)); /* offnum */
1072
1073 else if (!PQgetisnull(res, i, 0))
1074 printf(_("heap table \"%s.%s.%s\", block %s:\n"),
1075 rel->datinfo->datname, rel->nspname, rel->relname,
1076 PQgetvalue(res, i, 0)); /* blkno */
1077
1078 else
1079 printf(_("heap table \"%s.%s.%s\":\n"),
1080 rel->datinfo->datname, rel->nspname, rel->relname);
1081
1082 printf(" %s\n", msg);
1083 }
1084 }
1085 else if (PQresultStatus(res) != PGRES_TUPLES_OK)
1086 {
1087 char *msg = indent_lines(PQerrorMessage(conn));
1088
1089 all_checks_pass = false;
1090 printf(_("heap table \"%s.%s.%s\":\n"),
1091 rel->datinfo->datname, rel->nspname, rel->relname);
1092 printf("%s", msg);
1093 if (opts.verbose)
1094 printf(_("query was: %s\n"), rel->sql);
1095 FREE_AND_SET_NULL(msg);
1096 }
1097
1098 FREE_AND_SET_NULL(rel->sql);
1101
1102 return should_processing_continue(res);
1103}
1104
1105/*
1106 * verify_btree_slot_handler
1107 *
1108 * ParallelSlotHandler that receives results from a btree checking command
1109 * created by prepare_btree_command and outputs them for the user. The results
1110 * from the btree checking command is assumed to be empty, but when the results
1111 * are an error code, the useful information about the corruption is expected
1112 * in the connection's error message.
1113 *
1114 * res: result from an executed sql query
1115 * conn: connection on which the sql query was executed
1116 * context: unused
1117 */
1118static bool
1120{
1121 RelationInfo *rel = (RelationInfo *) context;
1122
1123 if (PQresultStatus(res) == PGRES_TUPLES_OK)
1124 {
1125 int ntups = PQntuples(res);
1126
1127 if (ntups > 1)
1128 {
1129 /*
1130 * We expect the btree checking functions to return one void row
1131 * each, or zero rows if the check was skipped due to the object
1132 * being in the wrong state to be checked, so we should output
1133 * some sort of warning if we get anything more, not because it
1134 * indicates corruption, but because it suggests a mismatch
1135 * between amcheck and pg_amcheck versions.
1136 *
1137 * In conjunction with --progress, anything written to stderr at
1138 * this time would present strangely to the user without an extra
1139 * newline, so we print one. If we were multithreaded, we'd have
1140 * to avoid splitting this across multiple calls, but we're in an
1141 * event loop, so it doesn't matter.
1142 */
1144 fprintf(stderr, "\n");
1145 pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d",
1146 rel->datinfo->datname, rel->nspname, rel->relname, ntups);
1147 if (opts.verbose)
1148 pg_log_warning_detail("Query was: %s", rel->sql);
1149 pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
1150 progname);
1152 }
1153 }
1154 else
1155 {
1156 char *msg = indent_lines(PQerrorMessage(conn));
1157
1158 all_checks_pass = false;
1159 printf(_("btree index \"%s.%s.%s\":\n"),
1160 rel->datinfo->datname, rel->nspname, rel->relname);
1161 printf("%s", msg);
1162 if (opts.verbose)
1163 printf(_("query was: %s\n"), rel->sql);
1164 FREE_AND_SET_NULL(msg);
1165 }
1166
1167 FREE_AND_SET_NULL(rel->sql);
1170
1171 return should_processing_continue(res);
1172}
1173
1174/*
1175 * help
1176 *
1177 * Prints help page for the program
1178 *
1179 * progname: the name of the executed program, such as "pg_amcheck"
1180 */
1181static void
1182help(const char *progname)
1183{
1184 printf(_("%s checks objects in a PostgreSQL database for corruption.\n\n"), progname);
1185 printf(_("Usage:\n"));
1186 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1187 printf(_("\nTarget options:\n"));
1188 printf(_(" -a, --all check all databases\n"));
1189 printf(_(" -d, --database=PATTERN check matching database(s)\n"));
1190 printf(_(" -D, --exclude-database=PATTERN do NOT check matching database(s)\n"));
1191 printf(_(" -i, --index=PATTERN check matching index(es)\n"));
1192 printf(_(" -I, --exclude-index=PATTERN do NOT check matching index(es)\n"));
1193 printf(_(" -r, --relation=PATTERN check matching relation(s)\n"));
1194 printf(_(" -R, --exclude-relation=PATTERN do NOT check matching relation(s)\n"));
1195 printf(_(" -s, --schema=PATTERN check matching schema(s)\n"));
1196 printf(_(" -S, --exclude-schema=PATTERN do NOT check matching schema(s)\n"));
1197 printf(_(" -t, --table=PATTERN check matching table(s)\n"));
1198 printf(_(" -T, --exclude-table=PATTERN do NOT check matching table(s)\n"));
1199 printf(_(" --no-dependent-indexes do NOT expand list of relations to include indexes\n"));
1200 printf(_(" --no-dependent-toast do NOT expand list of relations to include TOAST tables\n"));
1201 printf(_(" --no-strict-names do NOT require patterns to match objects\n"));
1202 printf(_("\nTable checking options:\n"));
1203 printf(_(" --exclude-toast-pointers do NOT follow relation TOAST pointers\n"));
1204 printf(_(" --on-error-stop stop checking at end of first corrupt page\n"));
1205 printf(_(" --skip=OPTION do NOT check \"all-frozen\" or \"all-visible\" blocks\n"));
1206 printf(_(" --startblock=BLOCK begin checking table(s) at the given block number\n"));
1207 printf(_(" --endblock=BLOCK check table(s) only up to the given block number\n"));
1208 printf(_("\nB-tree index checking options:\n"));
1209 printf(_(" --checkunique check unique constraint if index is unique\n"));
1210 printf(_(" --heapallindexed check that all heap tuples are found within indexes\n"));
1211 printf(_(" --parent-check check index parent/child relationships\n"));
1212 printf(_(" --rootdescend search from root page to refind tuples\n"));
1213 printf(_("\nConnection options:\n"));
1214 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1215 printf(_(" -p, --port=PORT database server port\n"));
1216 printf(_(" -U, --username=USERNAME user name to connect as\n"));
1217 printf(_(" -w, --no-password never prompt for password\n"));
1218 printf(_(" -W, --password force password prompt\n"));
1219 printf(_(" --maintenance-db=DBNAME alternate maintenance database\n"));
1220 printf(_("\nOther options:\n"));
1221 printf(_(" -e, --echo show the commands being sent to the server\n"));
1222 printf(_(" -j, --jobs=NUM use this many concurrent connections to the server\n"));
1223 printf(_(" -P, --progress show progress information\n"));
1224 printf(_(" -v, --verbose write a lot of output\n"));
1225 printf(_(" -V, --version output version information, then exit\n"));
1226 printf(_(" --install-missing install missing extensions\n"));
1227 printf(_(" -?, --help show this help, then exit\n"));
1228
1229 printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1230 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1231}
1232
1233/*
1234 * Print a progress report based on the global variables.
1235 *
1236 * Progress report is written at maximum once per second, unless the force
1237 * parameter is set to true.
1238 *
1239 * If finished is set to true, this is the last progress report. The cursor
1240 * is moved to the next line.
1241 */
1242static void
1245 const char *datname, bool force, bool finished)
1246{
1247 int percent_rel = 0;
1248 int percent_pages = 0;
1249 char checked_rel[32];
1250 char total_rel[32];
1251 char checked_pages[32];
1252 char total_pages[32];
1253 pg_time_t now;
1254
1255 if (!opts.show_progress)
1256 return;
1257
1258 now = time(NULL);
1259 if (now == last_progress_report && !force && !finished)
1260 return; /* Max once per second */
1261
1263 if (relations_total)
1265 if (relpages_total)
1267
1272
1273#define VERBOSE_DATNAME_LENGTH 35
1274 if (opts.verbose)
1275 {
1276 if (!datname)
1277
1278 /*
1279 * No datname given, so clear the status line (used for first and
1280 * last call)
1281 */
1283 _("%*s/%s relations (%d%%), %*s/%s pages (%d%%) %*s"),
1284 (int) strlen(total_rel),
1286 (int) strlen(total_pages),
1288 VERBOSE_DATNAME_LENGTH + 2, "");
1289 else
1290 {
1291 bool truncate = (strlen(datname) > VERBOSE_DATNAME_LENGTH);
1292
1294 _("%*s/%s relations (%d%%), %*s/%s pages (%d%%) (%s%-*.*s)"),
1295 (int) strlen(total_rel),
1297 (int) strlen(total_pages),
1299 /* Prefix with "..." if we do leading truncation */
1300 truncate ? "..." : "",
1303 /* Truncate datname at beginning if it's too long */
1304 truncate ? datname + strlen(datname) - VERBOSE_DATNAME_LENGTH + 3 : datname);
1305 }
1306 }
1307 else
1309 _("%*s/%s relations (%d%%), %*s/%s pages (%d%%)"),
1310 (int) strlen(total_rel),
1312 (int) strlen(total_pages),
1314
1315 /*
1316 * Stay on the same line if reporting to a terminal and we're not done
1317 * yet.
1318 */
1319 if (!finished && isatty(fileno(stderr)))
1320 {
1321 fputc('\r', stderr);
1323 }
1324 else
1325 fputc('\n', stderr);
1326}
1327
1328/*
1329 * Extend the pattern info array to hold one additional initialized pattern
1330 * info entry.
1331 *
1332 * Returns a pointer to the new entry.
1333 */
1334static PatternInfo *
1336{
1338
1339 pia->len++;
1340 pia->data = pg_realloc_array(pia->data, PatternInfo, pia->len);
1341 result = &pia->data[pia->len - 1];
1342 memset(result, 0, sizeof(*result));
1343
1344 return result;
1345}
1346
1347/*
1348 * append_database_pattern
1349 *
1350 * Adds the given pattern interpreted as a database name pattern.
1351 *
1352 * pia: the pattern info array to be appended
1353 * pattern: the database name pattern
1354 * encoding: client encoding for parsing the pattern
1355 */
1356static void
1358{
1360 int dotcnt;
1362
1364 patternToSQLRegex(encoding, NULL, NULL, &buf, pattern, false, false,
1365 &dotcnt);
1366 if (dotcnt > 0)
1367 {
1368 pg_log_error("improper qualified name (too many dotted names): %s", pattern);
1369 exit(2);
1370 }
1371 info->pattern = pattern;
1372 info->db_regex = pstrdup(buf.data);
1373
1375}
1376
1377/*
1378 * append_schema_pattern
1379 *
1380 * Adds the given pattern interpreted as a schema name pattern.
1381 *
1382 * pia: the pattern info array to be appended
1383 * pattern: the schema name pattern
1384 * encoding: client encoding for parsing the pattern
1385 */
1386static void
1388{
1391 int dotcnt;
1393
1396
1397 patternToSQLRegex(encoding, NULL, &dbbuf, &nspbuf, pattern, false, false,
1398 &dotcnt);
1399 if (dotcnt > 1)
1400 {
1401 pg_log_error("improper qualified name (too many dotted names): %s", pattern);
1402 exit(2);
1403 }
1404 info->pattern = pattern;
1405 if (dbbuf.data[0])
1406 {
1407 opts.dbpattern = true;
1408 info->db_regex = pstrdup(dbbuf.data);
1409 }
1410 if (nspbuf.data[0])
1411 info->nsp_regex = pstrdup(nspbuf.data);
1412
1415}
1416
1417/*
1418 * append_relation_pattern_helper
1419 *
1420 * Adds to a list the given pattern interpreted as a relation pattern.
1421 *
1422 * pia: the pattern info array to be appended
1423 * pattern: the relation name pattern
1424 * encoding: client encoding for parsing the pattern
1425 * heap_only: whether the pattern should only be matched against heap tables
1426 * btree_only: whether the pattern should only be matched against btree indexes
1427 */
1428static void
1430 int encoding, bool heap_only, bool btree_only)
1431{
1435 int dotcnt;
1437
1441
1442 patternToSQLRegex(encoding, &dbbuf, &nspbuf, &relbuf, pattern, false,
1443 false, &dotcnt);
1444 if (dotcnt > 2)
1445 {
1446 pg_log_error("improper relation name (too many dotted names): %s", pattern);
1447 exit(2);
1448 }
1449 info->pattern = pattern;
1450 if (dbbuf.data[0])
1451 {
1452 opts.dbpattern = true;
1453 info->db_regex = pstrdup(dbbuf.data);
1454 }
1455 if (nspbuf.data[0])
1456 info->nsp_regex = pstrdup(nspbuf.data);
1457 if (relbuf.data[0])
1458 info->rel_regex = pstrdup(relbuf.data);
1459
1463
1464 info->heap_only = heap_only;
1465 info->btree_only = btree_only;
1466}
1467
1468/*
1469 * append_relation_pattern
1470 *
1471 * Adds the given pattern interpreted as a relation pattern, to be matched
1472 * against both heap tables and btree indexes.
1473 *
1474 * pia: the pattern info array to be appended
1475 * pattern: the relation name pattern
1476 * encoding: client encoding for parsing the pattern
1477 */
1478static void
1480{
1481 append_relation_pattern_helper(pia, pattern, encoding, false, false);
1482}
1483
1484/*
1485 * append_heap_pattern
1486 *
1487 * Adds the given pattern interpreted as a relation pattern, to be matched only
1488 * against heap tables.
1489 *
1490 * pia: the pattern info array to be appended
1491 * pattern: the relation name pattern
1492 * encoding: client encoding for parsing the pattern
1493 */
1494static void
1496{
1497 append_relation_pattern_helper(pia, pattern, encoding, true, false);
1498}
1499
1500/*
1501 * append_btree_pattern
1502 *
1503 * Adds the given pattern interpreted as a relation pattern, to be matched only
1504 * against btree indexes.
1505 *
1506 * pia: the pattern info array to be appended
1507 * pattern: the relation name pattern
1508 * encoding: client encoding for parsing the pattern
1509 */
1510static void
1512{
1513 append_relation_pattern_helper(pia, pattern, encoding, false, true);
1514}
1515
1516/*
1517 * append_db_pattern_cte
1518 *
1519 * Appends to the buffer the body of a Common Table Expression (CTE) containing
1520 * the database portions filtered from the list of patterns expressed as two
1521 * columns:
1522 *
1523 * pattern_id: the index of this pattern in pia->data[]
1524 * rgx: the database regular expression parsed from the pattern
1525 *
1526 * Patterns without a database portion are skipped. Patterns with more than
1527 * just a database portion are optionally skipped, depending on argument
1528 * 'inclusive'.
1529 *
1530 * buf: the buffer to be appended
1531 * pia: the array of patterns to be inserted into the CTE
1532 * conn: the database connection
1533 * inclusive: whether to include patterns with schema and/or relation parts
1534 *
1535 * Returns whether any database patterns were appended.
1536 */
1537static bool
1539 PGconn *conn, bool inclusive)
1540{
1541 const char *comma;
1542 bool have_values;
1543
1544 comma = "";
1545 have_values = false;
1546 for (size_t pattern_id = 0; pattern_id < pia->len; pattern_id++)
1547 {
1548 PatternInfo *info = &pia->data[pattern_id];
1549
1550 if (info->db_regex != NULL &&
1551 (inclusive || (info->nsp_regex == NULL && info->rel_regex == NULL)))
1552 {
1553 if (!have_values)
1554 appendPQExpBufferStr(buf, "\nVALUES");
1555 have_values = true;
1556 appendPQExpBuffer(buf, "%s\n(%zu, ", comma, pattern_id);
1559 comma = ",";
1560 }
1561 }
1562
1563 if (!have_values)
1564 appendPQExpBufferStr(buf, "\nSELECT NULL, NULL, NULL WHERE false");
1565
1566 return have_values;
1567}
1568
1569/*
1570 * compile_database_list
1571 *
1572 * If any database patterns exist, or if --all was given, compiles a distinct
1573 * list of databases to check using a SQL query based on the patterns plus the
1574 * literal initial database name, if given. If no database patterns exist and
1575 * --all was not given, the query is not necessary, and only the initial
1576 * database name (if any) is added to the list.
1577 *
1578 * conn: connection to the initial database
1579 * databases: the list onto which databases should be appended
1580 * initial_dbname: an optional extra database name to include in the list
1581 */
1582static void
1584 const char *initial_dbname)
1585{
1586 PGresult *res;
1587 PQExpBufferData sql;
1588 int ntups;
1589 int i;
1590 bool fatal;
1591
1592 if (initial_dbname)
1593 {
1595
1596 /* This database is included. Add to list */
1597 if (opts.verbose)
1598 pg_log_info("including database \"%s\"", initial_dbname);
1599
1600 dat->datname = pstrdup(initial_dbname);
1601 simple_ptr_list_append(databases, dat);
1602 }
1603
1604 initPQExpBuffer(&sql);
1605
1606 /* Append the include patterns CTE. */
1607 appendPQExpBufferStr(&sql, "WITH include_raw (pattern_id, rgx) AS (");
1608 if (!append_db_pattern_cte(&sql, &opts.include, conn, true) &&
1609 !opts.alldb)
1610 {
1611 /*
1612 * None of the inclusion patterns (if any) contain database portions,
1613 * so there is no need to query the database to resolve database
1614 * patterns.
1615 *
1616 * Since we're also not operating under --all, we don't need to query
1617 * the exhaustive list of connectable databases, either.
1618 */
1619 termPQExpBuffer(&sql);
1620 return;
1621 }
1622
1623 /* Append the exclude patterns CTE. */
1624 appendPQExpBufferStr(&sql, "),\nexclude_raw (pattern_id, rgx) AS (");
1625 append_db_pattern_cte(&sql, &opts.exclude, conn, false);
1626 appendPQExpBufferStr(&sql, "),");
1627
1628 /*
1629 * Append the database CTE, which includes whether each database is
1630 * connectable and also joins against exclude_raw to determine whether
1631 * each database is excluded.
1632 */
1634 "\ndatabase (datname) AS ("
1635 "\nSELECT d.datname "
1636 "FROM pg_catalog.pg_database d "
1637 "LEFT OUTER JOIN exclude_raw e "
1638 "ON d.datname ~ e.rgx "
1639 "\nWHERE d.datallowconn AND datconnlimit != -2 "
1640 "AND e.pattern_id IS NULL"
1641 "),"
1642
1643 /*
1644 * Append the include_pat CTE, which joins the include_raw CTE against the
1645 * databases CTE to determine if all the inclusion patterns had matches,
1646 * and whether each matched pattern had the misfortune of only matching
1647 * excluded or unconnectable databases.
1648 */
1649 "\ninclude_pat (pattern_id, checkable) AS ("
1650 "\nSELECT i.pattern_id, "
1651 "COUNT(*) FILTER ("
1652 "WHERE d IS NOT NULL"
1653 ") AS checkable"
1654 "\nFROM include_raw i "
1655 "LEFT OUTER JOIN database d "
1656 "ON d.datname ~ i.rgx"
1657 "\nGROUP BY i.pattern_id"
1658 "),"
1659
1660 /*
1661 * Append the filtered_databases CTE, which selects from the database CTE
1662 * optionally joined against the include_raw CTE to only select databases
1663 * that match an inclusion pattern. This appears to duplicate what the
1664 * include_pat CTE already did above, but here we want only databases, and
1665 * there we wanted patterns.
1666 */
1667 "\nfiltered_databases (datname) AS ("
1668 "\nSELECT DISTINCT d.datname "
1669 "FROM database d");
1670 if (!opts.alldb)
1672 " INNER JOIN include_raw i "
1673 "ON d.datname ~ i.rgx");
1675 ")"
1676
1677 /*
1678 * Select the checkable databases and the unmatched inclusion patterns.
1679 */
1680 "\nSELECT pattern_id, datname FROM ("
1681 "\nSELECT pattern_id, NULL::TEXT AS datname "
1682 "FROM include_pat "
1683 "WHERE checkable = 0 "
1684 "UNION ALL"
1685 "\nSELECT NULL, datname "
1686 "FROM filtered_databases"
1687 ") AS combined_records"
1688 "\nORDER BY pattern_id NULLS LAST, datname");
1689
1690 res = executeQuery(conn, sql.data, opts.echo);
1691 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1692 {
1693 pg_log_error("query failed: %s", PQerrorMessage(conn));
1694 pg_log_error_detail("Query was: %s", sql.data);
1696 exit(1);
1697 }
1698 termPQExpBuffer(&sql);
1699
1700 ntups = PQntuples(res);
1701 for (fatal = false, i = 0; i < ntups; i++)
1702 {
1703 int pattern_id = -1;
1704 const char *datname = NULL;
1705
1706 if (!PQgetisnull(res, i, 0))
1707 pattern_id = atoi(PQgetvalue(res, i, 0));
1708 if (!PQgetisnull(res, i, 1))
1709 datname = PQgetvalue(res, i, 1);
1710
1711 if (pattern_id >= 0)
1712 {
1713 /*
1714 * Current record pertains to an inclusion pattern that matched no
1715 * checkable databases.
1716 */
1718 if (pattern_id >= opts.include.len)
1719 pg_fatal("internal error: received unexpected database pattern_id %d",
1720 pattern_id);
1721 log_no_match("no connectable databases to check matching \"%s\"",
1723 }
1724 else
1725 {
1727
1728 /* Current record pertains to a database */
1729 Assert(datname != NULL);
1730
1731 /* Avoid entering a duplicate entry matching the initial_dbname */
1733 continue;
1734
1735 /* This database is included. Add to list */
1736 if (opts.verbose)
1737 pg_log_info("including database \"%s\"", datname);
1738
1740 dat->datname = pstrdup(datname);
1741 simple_ptr_list_append(databases, dat);
1742 }
1743 }
1744 PQclear(res);
1745
1746 if (fatal)
1747 {
1748 if (conn != NULL)
1750 exit(1);
1751 }
1752}
1753
1754/*
1755 * append_rel_pattern_raw_cte
1756 *
1757 * Appends to the buffer the body of a Common Table Expression (CTE) containing
1758 * the given patterns as six columns:
1759 *
1760 * pattern_id: the index of this pattern in pia->data[]
1761 * db_regex: the database regexp parsed from the pattern, or NULL if the
1762 * pattern had no database part
1763 * nsp_regex: the namespace regexp parsed from the pattern, or NULL if the
1764 * pattern had no namespace part
1765 * rel_regex: the relname regexp parsed from the pattern, or NULL if the
1766 * pattern had no relname part
1767 * heap_only: true if the pattern applies only to heap tables (not indexes)
1768 * btree_only: true if the pattern applies only to btree indexes (not tables)
1769 *
1770 * buf: the buffer to be appended
1771 * patterns: the array of patterns to be inserted into the CTE
1772 * conn: the database connection
1773 */
1774static void
1776 PGconn *conn)
1777{
1778 const char *comma;
1779 bool have_values;
1780
1781 comma = "";
1782 have_values = false;
1783 for (size_t pattern_id = 0; pattern_id < pia->len; pattern_id++)
1784 {
1785 PatternInfo *info = &pia->data[pattern_id];
1786
1787 if (!have_values)
1788 appendPQExpBufferStr(buf, "\nVALUES");
1789 have_values = true;
1790 appendPQExpBuffer(buf, "%s\n(%zu::INTEGER, ", comma, pattern_id);
1791 if (info->db_regex == NULL)
1792 appendPQExpBufferStr(buf, "NULL");
1793 else
1795 appendPQExpBufferStr(buf, "::TEXT, ");
1796 if (info->nsp_regex == NULL)
1797 appendPQExpBufferStr(buf, "NULL");
1798 else
1800 appendPQExpBufferStr(buf, "::TEXT, ");
1801 if (info->rel_regex == NULL)
1802 appendPQExpBufferStr(buf, "NULL");
1803 else
1805 if (info->heap_only)
1806 appendPQExpBufferStr(buf, "::TEXT, true::BOOLEAN");
1807 else
1808 appendPQExpBufferStr(buf, "::TEXT, false::BOOLEAN");
1809 if (info->btree_only)
1810 appendPQExpBufferStr(buf, ", true::BOOLEAN");
1811 else
1812 appendPQExpBufferStr(buf, ", false::BOOLEAN");
1814 comma = ",";
1815 }
1816
1817 if (!have_values)
1819 "\nSELECT NULL::INTEGER, NULL::TEXT, NULL::TEXT, "
1820 "NULL::TEXT, NULL::BOOLEAN, NULL::BOOLEAN "
1821 "WHERE false");
1822}
1823
1824/*
1825 * append_rel_pattern_filtered_cte
1826 *
1827 * Appends to the buffer a Common Table Expression (CTE) which selects
1828 * all patterns from the named raw CTE, filtered by database. All patterns
1829 * which have no database portion or whose database portion matches our
1830 * connection's database name are selected, with other patterns excluded.
1831 *
1832 * The basic idea here is that if we're connected to database "foo" and we have
1833 * patterns "foo.bar.baz", "alpha.beta" and "one.two.three", we only want to
1834 * use the first two while processing relations in this database, as the third
1835 * one is not relevant.
1836 *
1837 * buf: the buffer to be appended
1838 * raw: the name of the CTE to select from
1839 * filtered: the name of the CTE to create
1840 * conn: the database connection
1841 */
1842static void
1844 const char *filtered, PGconn *conn)
1845{
1847 "\n%s (pattern_id, nsp_regex, rel_regex, heap_only, btree_only) AS ("
1848 "\nSELECT pattern_id, nsp_regex, rel_regex, heap_only, btree_only "
1849 "FROM %s r"
1850 "\nWHERE (r.db_regex IS NULL "
1851 "OR ",
1852 filtered, raw);
1854 appendPQExpBufferStr(buf, " ~ r.db_regex)");
1856 " AND (r.nsp_regex IS NOT NULL"
1857 " OR r.rel_regex IS NOT NULL)"
1858 "),");
1859}
1860
1861/*
1862 * compile_relation_list_one_db
1863 *
1864 * Compiles a list of relations to check within the currently connected
1865 * database based on the user supplied options, sorted by descending size,
1866 * and appends them to the given list of relations.
1867 *
1868 * The cells of the constructed list contain all information about the relation
1869 * necessary to connect to the database and check the object, including which
1870 * database to connect to, where contrib/amcheck is installed, and the Oid and
1871 * type of object (heap table vs. btree index). Rather than duplicating the
1872 * database details per relation, the relation structs use references to the
1873 * same database object, provided by the caller.
1874 *
1875 * conn: connection to this next database, which should be the same as in 'dat'
1876 * relations: list onto which the relations information should be appended
1877 * dat: the database info struct for use by each relation
1878 * pagecount: gets incremented by the number of blocks to check in all
1879 * relations added
1880 */
1881static void
1883 const DatabaseInfo *dat,
1885{
1886 PGresult *res;
1887 PQExpBufferData sql;
1888 int ntups;
1889 int i;
1890
1891 initPQExpBuffer(&sql);
1892 appendPQExpBufferStr(&sql, "WITH");
1893
1894 /* Append CTEs for the relation inclusion patterns, if any */
1895 if (!opts.allrel)
1896 {
1898 " include_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS (");
1900 appendPQExpBufferStr(&sql, "\n),");
1901 append_rel_pattern_filtered_cte(&sql, "include_raw", "include_pat", conn);
1902 }
1903
1904 /* Append CTEs for the relation exclusion patterns, if any */
1906 {
1908 " exclude_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS (");
1910 appendPQExpBufferStr(&sql, "\n),");
1911 append_rel_pattern_filtered_cte(&sql, "exclude_raw", "exclude_pat", conn);
1912 }
1913
1914 /* Append the relation CTE. */
1916 " relation (pattern_id, oid, nspname, relname, reltoastrelid, relpages, is_heap, is_btree) AS ("
1917 "\nSELECT DISTINCT ON (c.oid");
1918 if (!opts.allrel)
1919 appendPQExpBufferStr(&sql, ", ip.pattern_id) ip.pattern_id,");
1920 else
1921 appendPQExpBufferStr(&sql, ") NULL::INTEGER AS pattern_id,");
1922 appendPQExpBuffer(&sql,
1923 "\nc.oid, n.nspname, c.relname, c.reltoastrelid, c.relpages, "
1924 "c.relam = %u AS is_heap, "
1925 "c.relam = %u AS is_btree"
1926 "\nFROM pg_catalog.pg_class c "
1927 "INNER JOIN pg_catalog.pg_namespace n "
1928 "ON c.relnamespace = n.oid",
1930 if (!opts.allrel)
1931 appendPQExpBuffer(&sql,
1932 "\nINNER JOIN include_pat ip"
1933 "\nON (n.nspname ~ ip.nsp_regex OR ip.nsp_regex IS NULL)"
1934 "\nAND (c.relname ~ ip.rel_regex OR ip.rel_regex IS NULL)"
1935 "\nAND (c.relam = %u OR NOT ip.heap_only)"
1936 "\nAND (c.relam = %u OR NOT ip.btree_only)",
1939 appendPQExpBuffer(&sql,
1940 "\nLEFT OUTER JOIN exclude_pat ep"
1941 "\nON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL)"
1942 "\nAND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL)"
1943 "\nAND (c.relam = %u OR NOT ep.heap_only OR ep.rel_regex IS NULL)"
1944 "\nAND (c.relam = %u OR NOT ep.btree_only OR ep.rel_regex IS NULL)",
1946
1947 /*
1948 * Exclude temporary tables and indexes, which must necessarily belong to
1949 * other sessions. (We don't create any ourselves.) We must ultimately
1950 * exclude indexes marked invalid or not ready, but we delay that decision
1951 * until firing off the amcheck command, as the state of an index may
1952 * change by then.
1953 */
1954 appendPQExpBufferStr(&sql, "\nWHERE c.relpersistence != "
1957 appendPQExpBufferStr(&sql, "\nAND ep.pattern_id IS NULL");
1958
1959 /*
1960 * We need to be careful not to break the --no-dependent-toast and
1961 * --no-dependent-indexes options. By default, the btree indexes, toast
1962 * tables, and toast table btree indexes associated with primary heap
1963 * tables are included, using their own CTEs below. We implement the
1964 * --exclude-* options by not creating those CTEs, but that's no use if
1965 * we've already selected the toast and indexes here. On the other hand,
1966 * we want inclusion patterns that match indexes or toast tables to be
1967 * honored. So, if inclusion patterns were given, we want to select all
1968 * tables, toast tables, or indexes that match the patterns. But if no
1969 * inclusion patterns were given, and we're simply matching all relations,
1970 * then we only want to match the primary tables here.
1971 */
1972 if (opts.allrel)
1973 appendPQExpBuffer(&sql,
1974 " AND c.relam = %u "
1975 "AND c.relkind IN ("
1980 "AND c.relnamespace != %u",
1982 else
1983 appendPQExpBuffer(&sql,
1984 " AND c.relam IN (%u, %u)"
1985 "AND c.relkind IN ("
1991 "AND ((c.relam = %u AND c.relkind IN ("
1996 "(c.relam = %u AND c.relkind = "
2000
2002 "\nORDER BY c.oid)");
2003
2005 {
2006 /*
2007 * Include a CTE for toast tables associated with primary heap tables
2008 * selected above, filtering by exclusion patterns (if any) that match
2009 * toast table names.
2010 */
2012 ", toast (oid, nspname, relname, relpages) AS ("
2013 "\nSELECT t.oid, 'pg_toast', t.relname, t.relpages"
2014 "\nFROM pg_catalog.pg_class t "
2015 "INNER JOIN relation r "
2016 "ON r.reltoastrelid = t.oid");
2019 "\nLEFT OUTER JOIN exclude_pat ep"
2020 "\nON ('pg_toast' ~ ep.nsp_regex OR ep.nsp_regex IS NULL)"
2021 "\nAND (t.relname ~ ep.rel_regex OR ep.rel_regex IS NULL)"
2022 "\nAND ep.heap_only"
2023 "\nWHERE ep.pattern_id IS NULL"
2024 "\nAND t.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP));
2026 "\n)");
2027 }
2029 {
2030 /*
2031 * Include a CTE for btree indexes associated with primary heap tables
2032 * selected above, filtering by exclusion patterns (if any) that match
2033 * btree index names.
2034 */
2036 ", index (oid, nspname, relname, relpages) AS ("
2037 "\nSELECT c.oid, r.nspname, c.relname, c.relpages "
2038 "FROM relation r"
2039 "\nINNER JOIN pg_catalog.pg_index i "
2040 "ON r.oid = i.indrelid "
2041 "INNER JOIN pg_catalog.pg_class c "
2042 "ON i.indexrelid = c.oid "
2043 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP));
2046 "\nINNER JOIN pg_catalog.pg_namespace n "
2047 "ON c.relnamespace = n.oid"
2048 "\nLEFT OUTER JOIN exclude_pat ep "
2049 "ON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL) "
2050 "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) "
2051 "AND ep.btree_only"
2052 "\nWHERE ep.pattern_id IS NULL");
2053 else
2055 "\nWHERE true");
2056 appendPQExpBuffer(&sql,
2057 " AND c.relam = %u "
2058 "AND c.relkind = " CppAsString2(RELKIND_INDEX),
2059 BTREE_AM_OID);
2061 appendPQExpBuffer(&sql,
2062 " AND c.relnamespace != %u",
2064 appendPQExpBufferStr(&sql, "\n)");
2065 }
2066
2068 {
2069 /*
2070 * Include a CTE for btree indexes associated with toast tables of
2071 * primary heap tables selected above, filtering by exclusion patterns
2072 * (if any) that match the toast index names.
2073 */
2075 ", toast_index (oid, nspname, relname, relpages) AS ("
2076 "\nSELECT c.oid, 'pg_toast', c.relname, c.relpages "
2077 "FROM toast t "
2078 "INNER JOIN pg_catalog.pg_index i "
2079 "ON t.oid = i.indrelid"
2080 "\nINNER JOIN pg_catalog.pg_class c "
2081 "ON i.indexrelid = c.oid "
2082 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP));
2083 if (opts.excludeidx)
2085 "\nLEFT OUTER JOIN exclude_pat ep "
2086 "ON ('pg_toast' ~ ep.nsp_regex OR ep.nsp_regex IS NULL) "
2087 "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) "
2088 "AND ep.btree_only "
2089 "WHERE ep.pattern_id IS NULL");
2090 else
2092 "\nWHERE true");
2093 appendPQExpBuffer(&sql,
2094 " AND c.relam = %u"
2095 " AND c.relkind = " CppAsString2(RELKIND_INDEX) ")",
2096 BTREE_AM_OID);
2097 }
2098
2099 /*
2100 * Roll-up distinct rows from CTEs.
2101 *
2102 * Relations that match more than one pattern may occur more than once in
2103 * the list, and indexes and toast for primary relations may also have
2104 * matched in their own right, so we rely on UNION to deduplicate the
2105 * list.
2106 */
2108 "\nSELECT pattern_id, is_heap, is_btree, oid, nspname, relname, relpages "
2109 "FROM (");
2111 /* Inclusion patterns that failed to match */
2112 "\nSELECT pattern_id, is_heap, is_btree, "
2113 "NULL::OID AS oid, "
2114 "NULL::TEXT AS nspname, "
2115 "NULL::TEXT AS relname, "
2116 "NULL::INTEGER AS relpages"
2117 "\nFROM relation "
2118 "WHERE pattern_id IS NOT NULL "
2119 "UNION"
2120 /* Primary relations */
2121 "\nSELECT NULL::INTEGER AS pattern_id, "
2122 "is_heap, is_btree, oid, nspname, relname, relpages "
2123 "FROM relation");
2126 " UNION"
2127 /* Toast tables for primary relations */
2128 "\nSELECT NULL::INTEGER AS pattern_id, TRUE AS is_heap, "
2129 "FALSE AS is_btree, oid, nspname, relname, relpages "
2130 "FROM toast");
2133 " UNION"
2134 /* Indexes for primary relations */
2135 "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, "
2136 "TRUE AS is_btree, oid, nspname, relname, relpages "
2137 "FROM index");
2140 " UNION"
2141 /* Indexes for toast relations */
2142 "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, "
2143 "TRUE AS is_btree, oid, nspname, relname, relpages "
2144 "FROM toast_index");
2146 "\n) AS combined_records "
2147 "ORDER BY relpages DESC NULLS FIRST, oid");
2148
2149 res = executeQuery(conn, sql.data, opts.echo);
2150 if (PQresultStatus(res) != PGRES_TUPLES_OK)
2151 {
2152 pg_log_error("query failed: %s", PQerrorMessage(conn));
2153 pg_log_error_detail("Query was: %s", sql.data);
2155 exit(1);
2156 }
2157 termPQExpBuffer(&sql);
2158
2159 ntups = PQntuples(res);
2160 for (i = 0; i < ntups; i++)
2161 {
2162 int pattern_id = -1;
2163 bool is_heap = false;
2165 Oid oid = InvalidOid;
2166 const char *nspname = NULL;
2167 const char *relname = NULL;
2168 int relpages = 0;
2169
2170 if (!PQgetisnull(res, i, 0))
2171 pattern_id = atoi(PQgetvalue(res, i, 0));
2172 if (!PQgetisnull(res, i, 1))
2173 is_heap = (PQgetvalue(res, i, 1)[0] == 't');
2174 if (!PQgetisnull(res, i, 2))
2175 is_btree = (PQgetvalue(res, i, 2)[0] == 't');
2176 if (!PQgetisnull(res, i, 3))
2177 oid = atooid(PQgetvalue(res, i, 3));
2178 if (!PQgetisnull(res, i, 4))
2179 nspname = PQgetvalue(res, i, 4);
2180 if (!PQgetisnull(res, i, 5))
2181 relname = PQgetvalue(res, i, 5);
2182 if (!PQgetisnull(res, i, 6))
2183 relpages = atoi(PQgetvalue(res, i, 6));
2184
2185 if (pattern_id >= 0)
2186 {
2187 /*
2188 * Current record pertains to an inclusion pattern. Record that
2189 * it matched.
2190 */
2191
2192 if (pattern_id >= opts.include.len)
2193 pg_fatal("internal error: received unexpected relation pattern_id %d",
2194 pattern_id);
2195
2197 }
2198 else
2199 {
2200 /* Current record pertains to a relation */
2201
2203
2204 Assert(OidIsValid(oid));
2205 Assert((is_heap && !is_btree) || (is_btree && !is_heap));
2206
2207 rel->datinfo = dat;
2208 rel->reloid = oid;
2209 rel->is_heap = is_heap;
2210 rel->nspname = pstrdup(nspname);
2211 rel->relname = pstrdup(relname);
2212 rel->relpages = relpages;
2213 rel->blocks_to_check = relpages;
2214 if (is_heap && (opts.startblock >= 0 || opts.endblock >= 0))
2215 {
2216 /*
2217 * We apply --startblock and --endblock to heap tables, but
2218 * not btree indexes, and for progress purposes we need to
2219 * track how many blocks we expect to check.
2220 */
2221 if (opts.endblock >= 0 && rel->blocks_to_check > opts.endblock)
2222 rel->blocks_to_check = opts.endblock + 1;
2223 if (opts.startblock >= 0)
2224 {
2225 if (rel->blocks_to_check > opts.startblock)
2227 else
2228 rel->blocks_to_check = 0;
2229 }
2230 }
2231 *pagecount += rel->blocks_to_check;
2232
2233 simple_ptr_list_append(relations, rel);
2234 }
2235 }
2236 PQclear(res);
2237}
Datum now(PG_FUNCTION_ARGS)
Definition timestamp.c:1613
static void help(void)
Definition pg_config.c:71
#define MaxBlockNumber
Definition block.h:35
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308
#define INT64_FORMAT
Definition c.h:693
#define Assert(condition)
Definition c.h:1002
#define PG_TEXTDOMAIN(domain)
Definition c.h:1343
int64_t int64
Definition c.h:680
#define UINT64_FORMAT
Definition c.h:694
#define CppAsString2(x)
Definition c.h:565
uint64_t uint64
Definition c.h:684
#define OidIsValid(objectId)
Definition c.h:917
volatile sig_atomic_t CancelRequested
Definition cancel.c:89
void setup_cancel_handler(void(*query_cancel_callback)(void))
Definition cancel.c:213
uint32 result
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition exec.c:430
int main(void)
void disconnectDatabase(PGconn *conn)
PGconn * connectMaintenanceDatabase(ConnParams *cparams, const char *progname, bool echo)
PGconn * connectDatabase(const ConnParams *cparams, const char *progname, bool echo, bool fail_ok, bool allow_password_reuse)
PGresult * executeQuery(PGconn *conn, const char *query)
Definition connectdb.c:278
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
#define _(x)
Definition elog.c:96
char * PQdb(const PGconn *conn)
PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
char * PQerrorMessage(const PGconn *conn)
void PQfreemem(void *ptr)
Definition fe-exec.c:4068
int PQsendQuery(PGconn *conn, const char *query)
Definition fe-exec.c:1433
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition fe-exec.c:4424
char * pg_strdup(const char *in)
Definition fe_memutils.c:91
#define pg_realloc_array(pointer, type, count)
Definition fe_memutils.h:74
#define pg_malloc0_object(type)
Definition fe_memutils.h:61
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition getopt_long.c:60
#define no_argument
Definition getopt_long.h:25
#define required_argument
Definition getopt_long.h:26
#define optional_argument
Definition getopt_long.h:27
const char * str
#define comma
static char * username
Definition initdb.c:153
static char * encoding
Definition initdb.c:139
int i
Definition isn.c:77
#define PQgetvalue
#define PQclear
#define PQresultErrorField
#define PQresultStatus
#define PQgetisnull
#define PQntuples
@ PGRES_COPY_IN
Definition libpq-fe.h:138
@ PGRES_COPY_BOTH
Definition libpq-fe.h:143
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
@ PGRES_TUPLES_CHUNK
Definition libpq-fe.h:148
@ PGRES_FATAL_ERROR
Definition libpq-fe.h:142
@ PGRES_SINGLE_TUPLE
Definition libpq-fe.h:144
@ PGRES_COPY_OUT
Definition libpq-fe.h:137
@ PGRES_EMPTY_QUERY
Definition libpq-fe.h:130
@ PGRES_PIPELINE_SYNC
Definition libpq-fe.h:145
@ PGRES_BAD_RESPONSE
Definition libpq-fe.h:139
@ PGRES_PIPELINE_ABORTED
Definition libpq-fe.h:146
@ PGRES_NONFATAL_ERROR
Definition libpq-fe.h:141
@ PGRES_TUPLES_OK
Definition libpq-fe.h:134
@ PQERRORS_VERBOSE
Definition libpq-fe.h:164
void pg_logging_increase_verbosity(void)
Definition logging.c:187
void pg_logging_init(const char *argv0)
Definition logging.c:85
#define pg_log_error(...)
Definition logging.h:108
#define pg_log_error_hint(...)
Definition logging.h:114
#define pg_log_info(...)
Definition logging.h:126
#define pg_log_warning_hint(...)
Definition logging.h:123
#define pg_log_warning_detail(...)
Definition logging.h:120
#define pg_log_error_detail(...)
Definition logging.h:111
char * pstrdup(const char *in)
Definition mcxt.c:1910
void pfree(void *pointer)
Definition mcxt.c:1619
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
void handle_help_version_opts(int argc, char *argv[], const char *fixed_progname, help_handler hlp)
ParallelSlotArray * ParallelSlotsSetup(int numslots, ConnParams *cparams, const char *progname, bool echo, const char *initcmd)
bool ParallelSlotsWaitCompletion(ParallelSlotArray *sa)
ParallelSlot * ParallelSlotsGetIdle(ParallelSlotArray *sa, const char *dbname)
void ParallelSlotsTerminate(ParallelSlotArray *sa)
void ParallelSlotsAdoptConn(ParallelSlotArray *sa, PGconn *conn)
static void ParallelSlotSetHandler(ParallelSlot *slot, ParallelSlotResultHandler handler, void *context)
#define VERBOSE_DATNAME_LENGTH
static bool should_processing_continue(PGresult *res)
Definition pg_amcheck.c:963
static void progress_report(uint64 relations_total, uint64 relations_checked, uint64 relpages_total, uint64 relpages_checked, const char *datname, bool force, bool finished)
static void append_rel_pattern_filtered_cte(PQExpBuffer buf, const char *raw, const char *filtered, PGconn *conn)
static AmcheckOptions opts
Definition pg_amcheck.c:112
static void compile_database_list(PGconn *conn, SimplePtrList *databases, const char *initial_dbname)
static void prepare_btree_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
Definition pg_amcheck.c:882
static void append_database_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
static void append_rel_pattern_raw_cte(PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn)
static bool verify_heap_slot_handler(PGresult *res, PGconn *conn, void *context)
static pg_time_t last_progress_report
Definition pg_amcheck.c:147
#define FREE_AND_SET_NULL(x)
Definition pg_amcheck.c:214
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
static char * indent_lines(const char *str)
static const char *const amcheck_sql
Definition pg_amcheck.c:173
static void run_command(ParallelSlot *slot, const char *sql)
Definition pg_amcheck.c:931
static void append_heap_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
#define log_no_match(...)
Definition pg_amcheck.c:207
static void append_schema_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
static bool verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
static bool progress_since_last_stderr
Definition pg_amcheck.c:148
static const char * progname
Definition pg_amcheck.c:141
static void append_relation_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
static bool append_db_pattern_cte(PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn, bool inclusive)
static void prepare_heap_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
Definition pg_amcheck.c:842
static bool all_checks_pass
Definition pg_amcheck.c:144
static void compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, const DatabaseInfo *dat, uint64 *pagecount)
static void append_btree_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
static PatternInfo * extend_pattern_info_array(PatternInfoArray *pia)
#define pg_fatal(...)
NameData relname
Definition pg_class.h:40
NameData datname
Definition pg_database.h:37
static void executeCommand(PGconn *conn, const char *query)
PGDLLIMPORT int optind
Definition getopt.c:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
static int port
Definition pg_regress.c:117
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define pg_log_warning(...)
Definition pgfnames.c:24
int64 pg_time_t
Definition pgtime.h:23
int pg_strcasecmp(const char *s1, const char *s2)
#define snprintf
Definition port.h:261
const char * get_progname(const char *argv0)
Definition path.c:669
int pg_get_encoding_from_locale(const char *ctype, bool write_message)
Definition chklocale.c:301
#define printf(...)
Definition port.h:267
#define InvalidOid
unsigned int Oid
#define atooid(x)
#define PG_DIAG_SEVERITY_NONLOCALIZED
void initPQExpBuffer(PQExpBuffer str)
Definition pqexpbuffer.c:90
void resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
void termPQExpBuffer(PQExpBuffer str)
char * c
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
void simple_ptr_list_append(SimplePtrList *list, void *ptr)
PGconn * conn
Definition streamutil.c:52
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
void patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf, PQExpBuffer namebuf, const char *pattern, bool force_escape, bool want_literal_dbname, int *dotcnt)
bool no_btree_expansion
Definition pg_amcheck.c:109
bool install_missing
Definition pg_amcheck.c:69
bool no_toast_expansion
Definition pg_amcheck.c:95
char * install_schema
Definition pg_amcheck.c:70
bool reconcile_toast
Definition pg_amcheck.c:96
PatternInfoArray exclude
Definition pg_amcheck.c:74
PatternInfoArray include
Definition pg_amcheck.c:73
const char * skip
Definition pg_amcheck.c:100
char * amcheck_schema
Definition pg_amcheck.c:153
char * datname
Definition pg_amcheck.c:152
bool is_checkunique
Definition pg_amcheck.c:154
PGconn * connection
PatternInfo * data
Definition pg_amcheck.c:50
const char * pattern
Definition pg_amcheck.c:35
char * rel_regex
Definition pg_amcheck.c:39
char * db_regex
Definition pg_amcheck.c:36
char * nsp_regex
Definition pg_amcheck.c:38
bool btree_only
Definition pg_amcheck.c:43
bool heap_only
Definition pg_amcheck.c:41
const DatabaseInfo * datinfo
Definition pg_amcheck.c:159
char * nspname
Definition pg_amcheck.c:162
int blocks_to_check
Definition pg_amcheck.c:165
char * relname
Definition pg_amcheck.c:163
struct SimplePtrListCell * next
Definition simple_list.h:48
SimplePtrListCell * head
Definition simple_list.h:54
const char * pguser
char * override_dbname
Definition pg_backup.h:94
char * pgport
Definition pg_backup.h:88
char * pghost
Definition pg_backup.h:89
char * dbname
Definition pg_backup.h:87
enum trivalue prompt_password
const char * get_user_name_or_exit(const char *progname)
Definition username.c:74
trivalue
Definition vacuumlo.c:35
@ TRI_YES
Definition vacuumlo.c:38
@ TRI_DEFAULT
Definition vacuumlo.c:36
@ TRI_NO
Definition vacuumlo.c:37