PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
pg_amcheck.c File Reference
#include "postgres_fe.h"
#include <limits.h>
#include <time.h>
#include "catalog/pg_am_d.h"
#include "catalog/pg_class_d.h"
#include "catalog/pg_namespace_d.h"
#include "common/logging.h"
#include "common/username.h"
#include "fe_utils/cancel.h"
#include "fe_utils/option_utils.h"
#include "fe_utils/parallel_slot.h"
#include "fe_utils/query_utils.h"
#include "fe_utils/simple_list.h"
#include "fe_utils/string_utils.h"
#include "getopt_long.h"
#include "pgtime.h"
#include "storage/block.h"
Include dependency graph for pg_amcheck.c:

Go to the source code of this file.

Data Structures

struct  PatternInfo
 
struct  PatternInfoArray
 
struct  AmcheckOptions
 
struct  DatabaseInfo
 
struct  RelationInfo
 

Macros

#define log_no_match(...)
 
#define FREE_AND_SET_NULL(x)
 
#define VERBOSE_DATNAME_LENGTH   35
 

Typedefs

typedef struct PatternInfo PatternInfo
 
typedef struct PatternInfoArray PatternInfoArray
 
typedef struct AmcheckOptions AmcheckOptions
 
typedef struct DatabaseInfo DatabaseInfo
 
typedef struct RelationInfo RelationInfo
 

Functions

static void prepare_heap_command (PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
 
static void prepare_btree_command (PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
 
static void run_command (ParallelSlot *slot, const char *sql)
 
static bool verify_heap_slot_handler (PGresult *res, PGconn *conn, void *context)
 
static bool verify_btree_slot_handler (PGresult *res, PGconn *conn, void *context)
 
static void help (const char *progname)
 
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_database_pattern (PatternInfoArray *pia, const char *pattern, int encoding)
 
static void append_schema_pattern (PatternInfoArray *pia, const char *pattern, int encoding)
 
static void append_relation_pattern (PatternInfoArray *pia, const char *pattern, int encoding)
 
static void append_heap_pattern (PatternInfoArray *pia, const char *pattern, int encoding)
 
static void append_btree_pattern (PatternInfoArray *pia, const char *pattern, int encoding)
 
static void compile_database_list (PGconn *conn, SimplePtrList *databases, const char *initial_dbname)
 
static void compile_relation_list_one_db (PGconn *conn, SimplePtrList *relations, const DatabaseInfo *dat, uint64 *pagecount)
 
int main (int argc, char *argv[])
 
static bool should_processing_continue (PGresult *res)
 
static char * indent_lines (const char *str)
 
static PatternInfoextend_pattern_info_array (PatternInfoArray *pia)
 
static void append_relation_pattern_helper (PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
 
static bool append_db_pattern_cte (PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn, bool inclusive)
 
static void append_rel_pattern_raw_cte (PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn)
 
static void append_rel_pattern_filtered_cte (PQExpBuffer buf, const char *raw, const char *filtered, PGconn *conn)
 

Variables

static AmcheckOptions opts
 
static const char * progname = NULL
 
static bool all_checks_pass = true
 
static pg_time_t last_progress_report = 0
 
static bool progress_since_last_stderr = false
 
static const char *const amcheck_sql
 

Macro Definition Documentation

◆ FREE_AND_SET_NULL

#define FREE_AND_SET_NULL (   x)
Value:
do { \
pg_free(x); \
(x) = NULL; \
} while (0)
int x
Definition: isn.c:75

Definition at line 214 of file pg_amcheck.c.

◆ log_no_match

#define log_no_match (   ...)
Value:
do { \
pg_log_error(__VA_ARGS__); \
pg_log_warning(__VA_ARGS__); \
} while(0)
static AmcheckOptions opts
Definition: pg_amcheck.c:112
#define pg_log_warning(...)
Definition: pgfnames.c:24
bool strict_names
Definition: pg_amcheck.c:61

Definition at line 207 of file pg_amcheck.c.

◆ VERBOSE_DATNAME_LENGTH

#define VERBOSE_DATNAME_LENGTH   35

Typedef Documentation

◆ AmcheckOptions

◆ DatabaseInfo

typedef struct DatabaseInfo DatabaseInfo

◆ PatternInfo

typedef struct PatternInfo PatternInfo

◆ PatternInfoArray

◆ RelationInfo

typedef struct RelationInfo RelationInfo

Function Documentation

◆ append_btree_pattern()

static void append_btree_pattern ( PatternInfoArray pia,
const char *  pattern,
int  encoding 
)
static

Definition at line 1512 of file pg_amcheck.c.

1513{
1514 append_relation_pattern_helper(pia, pattern, encoding, false, true);
1515}
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
Definition: pg_amcheck.c:1430
int32 encoding
Definition: pg_database.h:41

References append_relation_pattern_helper(), and encoding.

Referenced by main().

◆ append_database_pattern()

static void append_database_pattern ( PatternInfoArray pia,
const char *  pattern,
int  encoding 
)
static

Definition at line 1358 of file pg_amcheck.c.

1359{
1361 int dotcnt;
1363
1365 patternToSQLRegex(encoding, NULL, NULL, &buf, pattern, false, false,
1366 &dotcnt);
1367 if (dotcnt > 0)
1368 {
1369 pg_log_error("improper qualified name (too many dotted names): %s", pattern);
1370 exit(2);
1371 }
1372 info->pattern = pattern;
1373 info->db_regex = pstrdup(buf.data);
1374
1376}
#define pg_log_error(...)
Definition: logging.h:106
char * pstrdup(const char *in)
Definition: mcxt.c:2322
static PatternInfo * extend_pattern_info_array(PatternInfoArray *pia)
Definition: pg_amcheck.c:1336
static char * buf
Definition: pg_test_fsync.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
void patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf, PQExpBuffer namebuf, const char *pattern, bool force_escape, bool want_literal_dbname, int *dotcnt)
const char * pattern
Definition: pg_amcheck.c:35
char * db_regex
Definition: pg_amcheck.c:36

References buf, PatternInfo::db_regex, encoding, extend_pattern_info_array(), initPQExpBuffer(), PatternInfo::pattern, patternToSQLRegex(), pg_log_error, pstrdup(), and termPQExpBuffer().

Referenced by main().

◆ append_db_pattern_cte()

static bool append_db_pattern_cte ( PQExpBuffer  buf,
const PatternInfoArray pia,
PGconn conn,
bool  inclusive 
)
static

Definition at line 1539 of file pg_amcheck.c.

1541{
1542 int pattern_id;
1543 const char *comma;
1544 bool have_values;
1545
1546 comma = "";
1547 have_values = false;
1548 for (pattern_id = 0; pattern_id < pia->len; pattern_id++)
1549 {
1550 PatternInfo *info = &pia->data[pattern_id];
1551
1552 if (info->db_regex != NULL &&
1553 (inclusive || (info->nsp_regex == NULL && info->rel_regex == NULL)))
1554 {
1555 if (!have_values)
1556 appendPQExpBufferStr(buf, "\nVALUES");
1557 have_values = true;
1558 appendPQExpBuffer(buf, "%s\n(%d, ", comma, pattern_id);
1561 comma = ",";
1562 }
1563 }
1564
1565 if (!have_values)
1566 appendPQExpBufferStr(buf, "\nSELECT NULL, NULL, NULL WHERE false");
1567
1568 return have_values;
1569}
#define comma
Definition: indent_codes.h:48
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
PGconn * conn
Definition: streamutil.c:52
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
Definition: string_utils.c:446
PatternInfo * data
Definition: pg_amcheck.c:50
char * rel_regex
Definition: pg_amcheck.c:39
char * nsp_regex
Definition: pg_amcheck.c:38

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), appendStringLiteralConn(), buf, comma, conn, PatternInfoArray::data, PatternInfo::db_regex, PatternInfoArray::len, PatternInfo::nsp_regex, and PatternInfo::rel_regex.

Referenced by compile_database_list().

◆ append_heap_pattern()

static void append_heap_pattern ( PatternInfoArray pia,
const char *  pattern,
int  encoding 
)
static

Definition at line 1496 of file pg_amcheck.c.

1497{
1498 append_relation_pattern_helper(pia, pattern, encoding, true, false);
1499}

References append_relation_pattern_helper(), and encoding.

Referenced by main().

◆ append_rel_pattern_filtered_cte()

static void append_rel_pattern_filtered_cte ( PQExpBuffer  buf,
const char *  raw,
const char *  filtered,
PGconn conn 
)
static

Definition at line 1846 of file pg_amcheck.c.

1848{
1850 "\n%s (pattern_id, nsp_regex, rel_regex, heap_only, btree_only) AS ("
1851 "\nSELECT pattern_id, nsp_regex, rel_regex, heap_only, btree_only "
1852 "FROM %s r"
1853 "\nWHERE (r.db_regex IS NULL "
1854 "OR ",
1855 filtered, raw);
1857 appendPQExpBufferStr(buf, " ~ r.db_regex)");
1859 " AND (r.nsp_regex IS NOT NULL"
1860 " OR r.rel_regex IS NOT NULL)"
1861 "),");
1862}
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:7447

References appendPQExpBuffer(), appendPQExpBufferStr(), appendStringLiteralConn(), buf, conn, and PQdb().

Referenced by compile_relation_list_one_db().

◆ append_rel_pattern_raw_cte()

static void append_rel_pattern_raw_cte ( PQExpBuffer  buf,
const PatternInfoArray pia,
PGconn conn 
)
static

Definition at line 1777 of file pg_amcheck.c.

1779{
1780 int pattern_id;
1781 const char *comma;
1782 bool have_values;
1783
1784 comma = "";
1785 have_values = false;
1786 for (pattern_id = 0; pattern_id < pia->len; pattern_id++)
1787 {
1788 PatternInfo *info = &pia->data[pattern_id];
1789
1790 if (!have_values)
1791 appendPQExpBufferStr(buf, "\nVALUES");
1792 have_values = true;
1793 appendPQExpBuffer(buf, "%s\n(%d::INTEGER, ", comma, pattern_id);
1794 if (info->db_regex == NULL)
1795 appendPQExpBufferStr(buf, "NULL");
1796 else
1798 appendPQExpBufferStr(buf, "::TEXT, ");
1799 if (info->nsp_regex == NULL)
1800 appendPQExpBufferStr(buf, "NULL");
1801 else
1803 appendPQExpBufferStr(buf, "::TEXT, ");
1804 if (info->rel_regex == NULL)
1805 appendPQExpBufferStr(buf, "NULL");
1806 else
1808 if (info->heap_only)
1809 appendPQExpBufferStr(buf, "::TEXT, true::BOOLEAN");
1810 else
1811 appendPQExpBufferStr(buf, "::TEXT, false::BOOLEAN");
1812 if (info->btree_only)
1813 appendPQExpBufferStr(buf, ", true::BOOLEAN");
1814 else
1815 appendPQExpBufferStr(buf, ", false::BOOLEAN");
1817 comma = ",";
1818 }
1819
1820 if (!have_values)
1822 "\nSELECT NULL::INTEGER, NULL::TEXT, NULL::TEXT, "
1823 "NULL::TEXT, NULL::BOOLEAN, NULL::BOOLEAN "
1824 "WHERE false");
1825}
bool btree_only
Definition: pg_amcheck.c:43
bool heap_only
Definition: pg_amcheck.c:41

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), appendStringLiteralConn(), PatternInfo::btree_only, buf, comma, conn, PatternInfoArray::data, PatternInfo::db_regex, PatternInfo::heap_only, PatternInfoArray::len, PatternInfo::nsp_regex, and PatternInfo::rel_regex.

Referenced by compile_relation_list_one_db().

◆ append_relation_pattern()

static void append_relation_pattern ( PatternInfoArray pia,
const char *  pattern,
int  encoding 
)
static

Definition at line 1480 of file pg_amcheck.c.

1481{
1482 append_relation_pattern_helper(pia, pattern, encoding, false, false);
1483}

References append_relation_pattern_helper(), and encoding.

Referenced by main().

◆ append_relation_pattern_helper()

static void append_relation_pattern_helper ( PatternInfoArray pia,
const char *  pattern,
int  encoding,
bool  heap_only,
bool  btree_only 
)
static

Definition at line 1430 of file pg_amcheck.c.

1432{
1433 PQExpBufferData dbbuf;
1434 PQExpBufferData nspbuf;
1435 PQExpBufferData relbuf;
1436 int dotcnt;
1438
1439 initPQExpBuffer(&dbbuf);
1440 initPQExpBuffer(&nspbuf);
1441 initPQExpBuffer(&relbuf);
1442
1443 patternToSQLRegex(encoding, &dbbuf, &nspbuf, &relbuf, pattern, false,
1444 false, &dotcnt);
1445 if (dotcnt > 2)
1446 {
1447 pg_log_error("improper relation name (too many dotted names): %s", pattern);
1448 exit(2);
1449 }
1450 info->pattern = pattern;
1451 if (dbbuf.data[0])
1452 {
1453 opts.dbpattern = true;
1454 info->db_regex = pstrdup(dbbuf.data);
1455 }
1456 if (nspbuf.data[0])
1457 info->nsp_regex = pstrdup(nspbuf.data);
1458 if (relbuf.data[0])
1459 info->rel_regex = pstrdup(relbuf.data);
1460
1461 termPQExpBuffer(&dbbuf);
1462 termPQExpBuffer(&nspbuf);
1463 termPQExpBuffer(&relbuf);
1464
1465 info->heap_only = heap_only;
1466 info->btree_only = btree_only;
1467}

References PatternInfo::btree_only, PQExpBufferData::data, PatternInfo::db_regex, AmcheckOptions::dbpattern, encoding, extend_pattern_info_array(), PatternInfo::heap_only, initPQExpBuffer(), PatternInfo::nsp_regex, opts, PatternInfo::pattern, patternToSQLRegex(), pg_log_error, pstrdup(), PatternInfo::rel_regex, and termPQExpBuffer().

Referenced by append_btree_pattern(), append_heap_pattern(), and append_relation_pattern().

◆ append_schema_pattern()

static void append_schema_pattern ( PatternInfoArray pia,
const char *  pattern,
int  encoding 
)
static

Definition at line 1388 of file pg_amcheck.c.

1389{
1390 PQExpBufferData dbbuf;
1391 PQExpBufferData nspbuf;
1392 int dotcnt;
1394
1395 initPQExpBuffer(&dbbuf);
1396 initPQExpBuffer(&nspbuf);
1397
1398 patternToSQLRegex(encoding, NULL, &dbbuf, &nspbuf, pattern, false, false,
1399 &dotcnt);
1400 if (dotcnt > 1)
1401 {
1402 pg_log_error("improper qualified name (too many dotted names): %s", pattern);
1403 exit(2);
1404 }
1405 info->pattern = pattern;
1406 if (dbbuf.data[0])
1407 {
1408 opts.dbpattern = true;
1409 info->db_regex = pstrdup(dbbuf.data);
1410 }
1411 if (nspbuf.data[0])
1412 info->nsp_regex = pstrdup(nspbuf.data);
1413
1414 termPQExpBuffer(&dbbuf);
1415 termPQExpBuffer(&nspbuf);
1416}

References PQExpBufferData::data, PatternInfo::db_regex, AmcheckOptions::dbpattern, encoding, extend_pattern_info_array(), initPQExpBuffer(), PatternInfo::nsp_regex, opts, PatternInfo::pattern, patternToSQLRegex(), pg_log_error, pstrdup(), and termPQExpBuffer().

Referenced by main().

◆ compile_database_list()

static void compile_database_list ( PGconn conn,
SimplePtrList databases,
const char *  initial_dbname 
)
static

Definition at line 1585 of file pg_amcheck.c.

1587{
1588 PGresult *res;
1589 PQExpBufferData sql;
1590 int ntups;
1591 int i;
1592 bool fatal;
1593
1594 if (initial_dbname)
1595 {
1597
1598 /* This database is included. Add to list */
1599 if (opts.verbose)
1600 pg_log_info("including database \"%s\"", initial_dbname);
1601
1602 dat->datname = pstrdup(initial_dbname);
1603 simple_ptr_list_append(databases, dat);
1604 }
1605
1606 initPQExpBuffer(&sql);
1607
1608 /* Append the include patterns CTE. */
1609 appendPQExpBufferStr(&sql, "WITH include_raw (pattern_id, rgx) AS (");
1610 if (!append_db_pattern_cte(&sql, &opts.include, conn, true) &&
1611 !opts.alldb)
1612 {
1613 /*
1614 * None of the inclusion patterns (if any) contain database portions,
1615 * so there is no need to query the database to resolve database
1616 * patterns.
1617 *
1618 * Since we're also not operating under --all, we don't need to query
1619 * the exhaustive list of connectable databases, either.
1620 */
1621 termPQExpBuffer(&sql);
1622 return;
1623 }
1624
1625 /* Append the exclude patterns CTE. */
1626 appendPQExpBufferStr(&sql, "),\nexclude_raw (pattern_id, rgx) AS (");
1627 append_db_pattern_cte(&sql, &opts.exclude, conn, false);
1628 appendPQExpBufferStr(&sql, "),");
1629
1630 /*
1631 * Append the database CTE, which includes whether each database is
1632 * connectable and also joins against exclude_raw to determine whether
1633 * each database is excluded.
1634 */
1636 "\ndatabase (datname) AS ("
1637 "\nSELECT d.datname "
1638 "FROM pg_catalog.pg_database d "
1639 "LEFT OUTER JOIN exclude_raw e "
1640 "ON d.datname ~ e.rgx "
1641 "\nWHERE d.datallowconn AND datconnlimit != -2 "
1642 "AND e.pattern_id IS NULL"
1643 "),"
1644
1645 /*
1646 * Append the include_pat CTE, which joins the include_raw CTE against the
1647 * databases CTE to determine if all the inclusion patterns had matches,
1648 * and whether each matched pattern had the misfortune of only matching
1649 * excluded or unconnectable databases.
1650 */
1651 "\ninclude_pat (pattern_id, checkable) AS ("
1652 "\nSELECT i.pattern_id, "
1653 "COUNT(*) FILTER ("
1654 "WHERE d IS NOT NULL"
1655 ") AS checkable"
1656 "\nFROM include_raw i "
1657 "LEFT OUTER JOIN database d "
1658 "ON d.datname ~ i.rgx"
1659 "\nGROUP BY i.pattern_id"
1660 "),"
1661
1662 /*
1663 * Append the filtered_databases CTE, which selects from the database CTE
1664 * optionally joined against the include_raw CTE to only select databases
1665 * that match an inclusion pattern. This appears to duplicate what the
1666 * include_pat CTE already did above, but here we want only databases, and
1667 * there we wanted patterns.
1668 */
1669 "\nfiltered_databases (datname) AS ("
1670 "\nSELECT DISTINCT d.datname "
1671 "FROM database d");
1672 if (!opts.alldb)
1674 " INNER JOIN include_raw i "
1675 "ON d.datname ~ i.rgx");
1677 ")"
1678
1679 /*
1680 * Select the checkable databases and the unmatched inclusion patterns.
1681 */
1682 "\nSELECT pattern_id, datname FROM ("
1683 "\nSELECT pattern_id, NULL::TEXT AS datname "
1684 "FROM include_pat "
1685 "WHERE checkable = 0 "
1686 "UNION ALL"
1687 "\nSELECT NULL, datname "
1688 "FROM filtered_databases"
1689 ") AS combined_records"
1690 "\nORDER BY pattern_id NULLS LAST, datname");
1691
1692 res = executeQuery(conn, sql.data, opts.echo);
1693 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1694 {
1695 pg_log_error("query failed: %s", PQerrorMessage(conn));
1696 pg_log_error_detail("Query was: %s", sql.data);
1698 exit(1);
1699 }
1700 termPQExpBuffer(&sql);
1701
1702 ntups = PQntuples(res);
1703 for (fatal = false, i = 0; i < ntups; i++)
1704 {
1705 int pattern_id = -1;
1706 const char *datname = NULL;
1707
1708 if (!PQgetisnull(res, i, 0))
1709 pattern_id = atoi(PQgetvalue(res, i, 0));
1710 if (!PQgetisnull(res, i, 1))
1711 datname = PQgetvalue(res, i, 1);
1712
1713 if (pattern_id >= 0)
1714 {
1715 /*
1716 * Current record pertains to an inclusion pattern that matched no
1717 * checkable databases.
1718 */
1719 fatal = opts.strict_names;
1720 if (pattern_id >= opts.include.len)
1721 pg_fatal("internal error: received unexpected database pattern_id %d",
1722 pattern_id);
1723 log_no_match("no connectable databases to check matching \"%s\"",
1724 opts.include.data[pattern_id].pattern);
1725 }
1726 else
1727 {
1728 DatabaseInfo *dat;
1729
1730 /* Current record pertains to a database */
1731 Assert(datname != NULL);
1732
1733 /* Avoid entering a duplicate entry matching the initial_dbname */
1734 if (initial_dbname != NULL && strcmp(initial_dbname, datname) == 0)
1735 continue;
1736
1737 /* This database is included. Add to list */
1738 if (opts.verbose)
1739 pg_log_info("including database \"%s\"", datname);
1740
1741 dat = (DatabaseInfo *) pg_malloc0(sizeof(DatabaseInfo));
1742 dat->datname = pstrdup(datname);
1743 simple_ptr_list_append(databases, dat);
1744 }
1745 }
1746 PQclear(res);
1747
1748 if (fatal)
1749 {
1750 if (conn != NULL)
1752 exit(1);
1753 }
1754}
void disconnectDatabase(PGconn *conn)
PGresult * executeQuery(PGconn *conn, const char *query)
Definition: connectdb.c:277
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7619
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3876
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3411
void PQclear(PGresult *res)
Definition: fe-exec.c:721
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3481
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3901
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
Assert(PointerIsAligned(start, uint64))
int i
Definition: isn.c:77
@ PGRES_TUPLES_OK
Definition: libpq-fe.h:128
#define pg_log_info(...)
Definition: logging.h:124
#define pg_log_error_detail(...)
Definition: logging.h:109
#define log_no_match(...)
Definition: pg_amcheck.c:207
static bool append_db_pattern_cte(PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn, bool inclusive)
Definition: pg_amcheck.c:1539
#define pg_fatal(...)
NameData datname
Definition: pg_database.h:35
void simple_ptr_list_append(SimplePtrList *list, void *ptr)
Definition: simple_list.c:162
PatternInfoArray exclude
Definition: pg_amcheck.c:74
PatternInfoArray include
Definition: pg_amcheck.c:73
char * datname
Definition: pg_amcheck.c:152

References AmcheckOptions::alldb, append_db_pattern_cte(), appendPQExpBufferStr(), Assert(), conn, PatternInfoArray::data, PQExpBufferData::data, DatabaseInfo::datname, datname, disconnectDatabase(), AmcheckOptions::echo, AmcheckOptions::exclude, executeQuery(), i, AmcheckOptions::include, initPQExpBuffer(), PatternInfoArray::len, log_no_match, opts, PatternInfo::pattern, pg_fatal, pg_log_error, pg_log_error_detail, pg_log_info, pg_malloc0(), PGRES_TUPLES_OK, PQclear(), PQerrorMessage(), PQgetisnull(), PQgetvalue(), PQntuples(), PQresultStatus(), pstrdup(), simple_ptr_list_append(), AmcheckOptions::strict_names, termPQExpBuffer(), and AmcheckOptions::verbose.

Referenced by main().

◆ compile_relation_list_one_db()

static void compile_relation_list_one_db ( PGconn conn,
SimplePtrList relations,
const DatabaseInfo dat,
uint64 pagecount 
)
static

Definition at line 1885 of file pg_amcheck.c.

1888{
1889 PGresult *res;
1890 PQExpBufferData sql;
1891 int ntups;
1892 int i;
1893
1894 initPQExpBuffer(&sql);
1895 appendPQExpBufferStr(&sql, "WITH");
1896
1897 /* Append CTEs for the relation inclusion patterns, if any */
1898 if (!opts.allrel)
1899 {
1901 " include_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS (");
1903 appendPQExpBufferStr(&sql, "\n),");
1904 append_rel_pattern_filtered_cte(&sql, "include_raw", "include_pat", conn);
1905 }
1906
1907 /* Append CTEs for the relation exclusion patterns, if any */
1909 {
1911 " exclude_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS (");
1913 appendPQExpBufferStr(&sql, "\n),");
1914 append_rel_pattern_filtered_cte(&sql, "exclude_raw", "exclude_pat", conn);
1915 }
1916
1917 /* Append the relation CTE. */
1919 " relation (pattern_id, oid, nspname, relname, reltoastrelid, relpages, is_heap, is_btree) AS ("
1920 "\nSELECT DISTINCT ON (c.oid");
1921 if (!opts.allrel)
1922 appendPQExpBufferStr(&sql, ", ip.pattern_id) ip.pattern_id,");
1923 else
1924 appendPQExpBufferStr(&sql, ") NULL::INTEGER AS pattern_id,");
1925 appendPQExpBuffer(&sql,
1926 "\nc.oid, n.nspname, c.relname, c.reltoastrelid, c.relpages, "
1927 "c.relam = %u AS is_heap, "
1928 "c.relam = %u AS is_btree"
1929 "\nFROM pg_catalog.pg_class c "
1930 "INNER JOIN pg_catalog.pg_namespace n "
1931 "ON c.relnamespace = n.oid",
1932 HEAP_TABLE_AM_OID, BTREE_AM_OID);
1933 if (!opts.allrel)
1934 appendPQExpBuffer(&sql,
1935 "\nINNER JOIN include_pat ip"
1936 "\nON (n.nspname ~ ip.nsp_regex OR ip.nsp_regex IS NULL)"
1937 "\nAND (c.relname ~ ip.rel_regex OR ip.rel_regex IS NULL)"
1938 "\nAND (c.relam = %u OR NOT ip.heap_only)"
1939 "\nAND (c.relam = %u OR NOT ip.btree_only)",
1940 HEAP_TABLE_AM_OID, BTREE_AM_OID);
1942 appendPQExpBuffer(&sql,
1943 "\nLEFT OUTER JOIN exclude_pat ep"
1944 "\nON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL)"
1945 "\nAND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL)"
1946 "\nAND (c.relam = %u OR NOT ep.heap_only OR ep.rel_regex IS NULL)"
1947 "\nAND (c.relam = %u OR NOT ep.btree_only OR ep.rel_regex IS NULL)",
1948 HEAP_TABLE_AM_OID, BTREE_AM_OID);
1949
1950 /*
1951 * Exclude temporary tables and indexes, which must necessarily belong to
1952 * other sessions. (We don't create any ourselves.) We must ultimately
1953 * exclude indexes marked invalid or not ready, but we delay that decision
1954 * until firing off the amcheck command, as the state of an index may
1955 * change by then.
1956 */
1957 appendPQExpBufferStr(&sql, "\nWHERE c.relpersistence != "
1958 CppAsString2(RELPERSISTENCE_TEMP));
1960 appendPQExpBufferStr(&sql, "\nAND ep.pattern_id IS NULL");
1961
1962 /*
1963 * We need to be careful not to break the --no-dependent-toast and
1964 * --no-dependent-indexes options. By default, the btree indexes, toast
1965 * tables, and toast table btree indexes associated with primary heap
1966 * tables are included, using their own CTEs below. We implement the
1967 * --exclude-* options by not creating those CTEs, but that's no use if
1968 * we've already selected the toast and indexes here. On the other hand,
1969 * we want inclusion patterns that match indexes or toast tables to be
1970 * honored. So, if inclusion patterns were given, we want to select all
1971 * tables, toast tables, or indexes that match the patterns. But if no
1972 * inclusion patterns were given, and we're simply matching all relations,
1973 * then we only want to match the primary tables here.
1974 */
1975 if (opts.allrel)
1976 appendPQExpBuffer(&sql,
1977 " AND c.relam = %u "
1978 "AND c.relkind IN ("
1979 CppAsString2(RELKIND_RELATION) ", "
1980 CppAsString2(RELKIND_SEQUENCE) ", "
1981 CppAsString2(RELKIND_MATVIEW) ", "
1982 CppAsString2(RELKIND_TOASTVALUE) ") "
1983 "AND c.relnamespace != %u",
1984 HEAP_TABLE_AM_OID, PG_TOAST_NAMESPACE);
1985 else
1986 appendPQExpBuffer(&sql,
1987 " AND c.relam IN (%u, %u)"
1988 "AND c.relkind IN ("
1989 CppAsString2(RELKIND_RELATION) ", "
1990 CppAsString2(RELKIND_SEQUENCE) ", "
1991 CppAsString2(RELKIND_MATVIEW) ", "
1992 CppAsString2(RELKIND_TOASTVALUE) ", "
1993 CppAsString2(RELKIND_INDEX) ") "
1994 "AND ((c.relam = %u AND c.relkind IN ("
1995 CppAsString2(RELKIND_RELATION) ", "
1996 CppAsString2(RELKIND_SEQUENCE) ", "
1997 CppAsString2(RELKIND_MATVIEW) ", "
1998 CppAsString2(RELKIND_TOASTVALUE) ")) OR "
1999 "(c.relam = %u AND c.relkind = "
2000 CppAsString2(RELKIND_INDEX) "))",
2001 HEAP_TABLE_AM_OID, BTREE_AM_OID,
2002 HEAP_TABLE_AM_OID, BTREE_AM_OID);
2003
2005 "\nORDER BY c.oid)");
2006
2008 {
2009 /*
2010 * Include a CTE for toast tables associated with primary heap tables
2011 * selected above, filtering by exclusion patterns (if any) that match
2012 * toast table names.
2013 */
2015 ", toast (oid, nspname, relname, relpages) AS ("
2016 "\nSELECT t.oid, 'pg_toast', t.relname, t.relpages"
2017 "\nFROM pg_catalog.pg_class t "
2018 "INNER JOIN relation r "
2019 "ON r.reltoastrelid = t.oid");
2022 "\nLEFT OUTER JOIN exclude_pat ep"
2023 "\nON ('pg_toast' ~ ep.nsp_regex OR ep.nsp_regex IS NULL)"
2024 "\nAND (t.relname ~ ep.rel_regex OR ep.rel_regex IS NULL)"
2025 "\nAND ep.heap_only"
2026 "\nWHERE ep.pattern_id IS NULL"
2027 "\nAND t.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP));
2029 "\n)");
2030 }
2032 {
2033 /*
2034 * Include a CTE for btree indexes associated with primary heap tables
2035 * selected above, filtering by exclusion patterns (if any) that match
2036 * btree index names.
2037 */
2039 ", index (oid, nspname, relname, relpages) AS ("
2040 "\nSELECT c.oid, r.nspname, c.relname, c.relpages "
2041 "FROM relation r"
2042 "\nINNER JOIN pg_catalog.pg_index i "
2043 "ON r.oid = i.indrelid "
2044 "INNER JOIN pg_catalog.pg_class c "
2045 "ON i.indexrelid = c.oid "
2046 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP));
2049 "\nINNER JOIN pg_catalog.pg_namespace n "
2050 "ON c.relnamespace = n.oid"
2051 "\nLEFT OUTER JOIN exclude_pat ep "
2052 "ON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL) "
2053 "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) "
2054 "AND ep.btree_only"
2055 "\nWHERE ep.pattern_id IS NULL");
2056 else
2058 "\nWHERE true");
2059 appendPQExpBuffer(&sql,
2060 " AND c.relam = %u "
2061 "AND c.relkind = " CppAsString2(RELKIND_INDEX),
2062 BTREE_AM_OID);
2064 appendPQExpBuffer(&sql,
2065 " AND c.relnamespace != %u",
2066 PG_TOAST_NAMESPACE);
2067 appendPQExpBufferStr(&sql, "\n)");
2068 }
2069
2071 {
2072 /*
2073 * Include a CTE for btree indexes associated with toast tables of
2074 * primary heap tables selected above, filtering by exclusion patterns
2075 * (if any) that match the toast index names.
2076 */
2078 ", toast_index (oid, nspname, relname, relpages) AS ("
2079 "\nSELECT c.oid, 'pg_toast', c.relname, c.relpages "
2080 "FROM toast t "
2081 "INNER JOIN pg_catalog.pg_index i "
2082 "ON t.oid = i.indrelid"
2083 "\nINNER JOIN pg_catalog.pg_class c "
2084 "ON i.indexrelid = c.oid "
2085 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP));
2086 if (opts.excludeidx)
2088 "\nLEFT OUTER JOIN exclude_pat ep "
2089 "ON ('pg_toast' ~ ep.nsp_regex OR ep.nsp_regex IS NULL) "
2090 "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) "
2091 "AND ep.btree_only "
2092 "WHERE ep.pattern_id IS NULL");
2093 else
2095 "\nWHERE true");
2096 appendPQExpBuffer(&sql,
2097 " AND c.relam = %u"
2098 " AND c.relkind = " CppAsString2(RELKIND_INDEX) ")",
2099 BTREE_AM_OID);
2100 }
2101
2102 /*
2103 * Roll-up distinct rows from CTEs.
2104 *
2105 * Relations that match more than one pattern may occur more than once in
2106 * the list, and indexes and toast for primary relations may also have
2107 * matched in their own right, so we rely on UNION to deduplicate the
2108 * list.
2109 */
2111 "\nSELECT pattern_id, is_heap, is_btree, oid, nspname, relname, relpages "
2112 "FROM (");
2114 /* Inclusion patterns that failed to match */
2115 "\nSELECT pattern_id, is_heap, is_btree, "
2116 "NULL::OID AS oid, "
2117 "NULL::TEXT AS nspname, "
2118 "NULL::TEXT AS relname, "
2119 "NULL::INTEGER AS relpages"
2120 "\nFROM relation "
2121 "WHERE pattern_id IS NOT NULL "
2122 "UNION"
2123 /* Primary relations */
2124 "\nSELECT NULL::INTEGER AS pattern_id, "
2125 "is_heap, is_btree, oid, nspname, relname, relpages "
2126 "FROM relation");
2129 " UNION"
2130 /* Toast tables for primary relations */
2131 "\nSELECT NULL::INTEGER AS pattern_id, TRUE AS is_heap, "
2132 "FALSE AS is_btree, oid, nspname, relname, relpages "
2133 "FROM toast");
2136 " UNION"
2137 /* Indexes for primary relations */
2138 "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, "
2139 "TRUE AS is_btree, oid, nspname, relname, relpages "
2140 "FROM index");
2143 " UNION"
2144 /* Indexes for toast relations */
2145 "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, "
2146 "TRUE AS is_btree, oid, nspname, relname, relpages "
2147 "FROM toast_index");
2149 "\n) AS combined_records "
2150 "ORDER BY relpages DESC NULLS FIRST, oid");
2151
2152 res = executeQuery(conn, sql.data, opts.echo);
2153 if (PQresultStatus(res) != PGRES_TUPLES_OK)
2154 {
2155 pg_log_error("query failed: %s", PQerrorMessage(conn));
2156 pg_log_error_detail("Query was: %s", sql.data);
2158 exit(1);
2159 }
2160 termPQExpBuffer(&sql);
2161
2162 ntups = PQntuples(res);
2163 for (i = 0; i < ntups; i++)
2164 {
2165 int pattern_id = -1;
2166 bool is_heap = false;
2167 bool is_btree PG_USED_FOR_ASSERTS_ONLY = false;
2168 Oid oid = InvalidOid;
2169 const char *nspname = NULL;
2170 const char *relname = NULL;
2171 int relpages = 0;
2172
2173 if (!PQgetisnull(res, i, 0))
2174 pattern_id = atoi(PQgetvalue(res, i, 0));
2175 if (!PQgetisnull(res, i, 1))
2176 is_heap = (PQgetvalue(res, i, 1)[0] == 't');
2177 if (!PQgetisnull(res, i, 2))
2178 is_btree = (PQgetvalue(res, i, 2)[0] == 't');
2179 if (!PQgetisnull(res, i, 3))
2180 oid = atooid(PQgetvalue(res, i, 3));
2181 if (!PQgetisnull(res, i, 4))
2182 nspname = PQgetvalue(res, i, 4);
2183 if (!PQgetisnull(res, i, 5))
2184 relname = PQgetvalue(res, i, 5);
2185 if (!PQgetisnull(res, i, 6))
2186 relpages = atoi(PQgetvalue(res, i, 6));
2187
2188 if (pattern_id >= 0)
2189 {
2190 /*
2191 * Current record pertains to an inclusion pattern. Record that
2192 * it matched.
2193 */
2194
2195 if (pattern_id >= opts.include.len)
2196 pg_fatal("internal error: received unexpected relation pattern_id %d",
2197 pattern_id);
2198
2199 opts.include.data[pattern_id].matched = true;
2200 }
2201 else
2202 {
2203 /* Current record pertains to a relation */
2204
2206
2207 Assert(OidIsValid(oid));
2208 Assert((is_heap && !is_btree) || (is_btree && !is_heap));
2209
2210 rel->datinfo = dat;
2211 rel->reloid = oid;
2212 rel->is_heap = is_heap;
2213 rel->nspname = pstrdup(nspname);
2214 rel->relname = pstrdup(relname);
2215 rel->relpages = relpages;
2216 rel->blocks_to_check = relpages;
2217 if (is_heap && (opts.startblock >= 0 || opts.endblock >= 0))
2218 {
2219 /*
2220 * We apply --startblock and --endblock to heap tables, but
2221 * not btree indexes, and for progress purposes we need to
2222 * track how many blocks we expect to check.
2223 */
2224 if (opts.endblock >= 0 && rel->blocks_to_check > opts.endblock)
2225 rel->blocks_to_check = opts.endblock + 1;
2226 if (opts.startblock >= 0)
2227 {
2228 if (rel->blocks_to_check > opts.startblock)
2230 else
2231 rel->blocks_to_check = 0;
2232 }
2233 }
2234 *pagecount += rel->blocks_to_check;
2235
2236 simple_ptr_list_append(relations, rel);
2237 }
2238 }
2239 PQclear(res);
2240}
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:224
#define CppAsString2(x)
Definition: c.h:363
#define OidIsValid(objectId)
Definition: c.h:746
static void append_rel_pattern_filtered_cte(PQExpBuffer buf, const char *raw, const char *filtered, PGconn *conn)
Definition: pg_amcheck.c:1846
static void append_rel_pattern_raw_cte(PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn)
Definition: pg_amcheck.c:1777
NameData relname
Definition: pg_class.h:38
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
#define atooid(x)
Definition: postgres_ext.h:41
int64 startblock
Definition: pg_amcheck.c:98
bool no_btree_expansion
Definition: pg_amcheck.c:109
bool no_toast_expansion
Definition: pg_amcheck.c:95
int64 endblock
Definition: pg_amcheck.c:99
bool matched
Definition: pg_amcheck.c:45
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

References AmcheckOptions::allrel, append_rel_pattern_filtered_cte(), append_rel_pattern_raw_cte(), appendPQExpBuffer(), appendPQExpBufferStr(), Assert(), atooid, RelationInfo::blocks_to_check, conn, CppAsString2, PatternInfoArray::data, PQExpBufferData::data, RelationInfo::datinfo, disconnectDatabase(), AmcheckOptions::echo, AmcheckOptions::endblock, AmcheckOptions::exclude, AmcheckOptions::excludeidx, AmcheckOptions::excludensp, AmcheckOptions::excludetbl, executeQuery(), i, AmcheckOptions::include, initPQExpBuffer(), InvalidOid, RelationInfo::is_heap, PatternInfoArray::len, PatternInfo::matched, AmcheckOptions::no_btree_expansion, AmcheckOptions::no_toast_expansion, RelationInfo::nspname, OidIsValid, opts, pg_fatal, pg_log_error, pg_log_error_detail, pg_malloc0(), PG_USED_FOR_ASSERTS_ONLY, PGRES_TUPLES_OK, PQclear(), PQerrorMessage(), PQgetisnull(), PQgetvalue(), PQntuples(), PQresultStatus(), pstrdup(), RelationInfo::relname, relname, RelationInfo::reloid, RelationInfo::relpages, simple_ptr_list_append(), AmcheckOptions::startblock, and termPQExpBuffer().

Referenced by main().

◆ extend_pattern_info_array()

static PatternInfo * extend_pattern_info_array ( PatternInfoArray pia)
static

Definition at line 1336 of file pg_amcheck.c.

1337{
1338 PatternInfo *result;
1339
1340 pia->len++;
1341 pia->data = (PatternInfo *) pg_realloc(pia->data, pia->len * sizeof(PatternInfo));
1342 result = &pia->data[pia->len - 1];
1343 memset(result, 0, sizeof(*result));
1344
1345 return result;
1346}
void * pg_realloc(void *ptr, size_t size)
Definition: fe_memutils.c:65

References PatternInfoArray::data, PatternInfoArray::len, and pg_realloc().

Referenced by append_database_pattern(), append_relation_pattern_helper(), and append_schema_pattern().

◆ help()

static void help ( const char *  progname)
static

Definition at line 1183 of file pg_amcheck.c.

1184{
1185 printf(_("%s checks objects in a PostgreSQL database for corruption.\n\n"), progname);
1186 printf(_("Usage:\n"));
1187 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1188 printf(_("\nTarget options:\n"));
1189 printf(_(" -a, --all check all databases\n"));
1190 printf(_(" -d, --database=PATTERN check matching database(s)\n"));
1191 printf(_(" -D, --exclude-database=PATTERN do NOT check matching database(s)\n"));
1192 printf(_(" -i, --index=PATTERN check matching index(es)\n"));
1193 printf(_(" -I, --exclude-index=PATTERN do NOT check matching index(es)\n"));
1194 printf(_(" -r, --relation=PATTERN check matching relation(s)\n"));
1195 printf(_(" -R, --exclude-relation=PATTERN do NOT check matching relation(s)\n"));
1196 printf(_(" -s, --schema=PATTERN check matching schema(s)\n"));
1197 printf(_(" -S, --exclude-schema=PATTERN do NOT check matching schema(s)\n"));
1198 printf(_(" -t, --table=PATTERN check matching table(s)\n"));
1199 printf(_(" -T, --exclude-table=PATTERN do NOT check matching table(s)\n"));
1200 printf(_(" --no-dependent-indexes do NOT expand list of relations to include indexes\n"));
1201 printf(_(" --no-dependent-toast do NOT expand list of relations to include TOAST tables\n"));
1202 printf(_(" --no-strict-names do NOT require patterns to match objects\n"));
1203 printf(_("\nTable checking options:\n"));
1204 printf(_(" --exclude-toast-pointers do NOT follow relation TOAST pointers\n"));
1205 printf(_(" --on-error-stop stop checking at end of first corrupt page\n"));
1206 printf(_(" --skip=OPTION do NOT check \"all-frozen\" or \"all-visible\" blocks\n"));
1207 printf(_(" --startblock=BLOCK begin checking table(s) at the given block number\n"));
1208 printf(_(" --endblock=BLOCK check table(s) only up to the given block number\n"));
1209 printf(_("\nB-tree index checking options:\n"));
1210 printf(_(" --checkunique check unique constraint if index is unique\n"));
1211 printf(_(" --heapallindexed check that all heap tuples are found within indexes\n"));
1212 printf(_(" --parent-check check index parent/child relationships\n"));
1213 printf(_(" --rootdescend search from root page to refind tuples\n"));
1214 printf(_("\nConnection options:\n"));
1215 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1216 printf(_(" -p, --port=PORT database server port\n"));
1217 printf(_(" -U, --username=USERNAME user name to connect as\n"));
1218 printf(_(" -w, --no-password never prompt for password\n"));
1219 printf(_(" -W, --password force password prompt\n"));
1220 printf(_(" --maintenance-db=DBNAME alternate maintenance database\n"));
1221 printf(_("\nOther options:\n"));
1222 printf(_(" -e, --echo show the commands being sent to the server\n"));
1223 printf(_(" -j, --jobs=NUM use this many concurrent connections to the server\n"));
1224 printf(_(" -P, --progress show progress information\n"));
1225 printf(_(" -v, --verbose write a lot of output\n"));
1226 printf(_(" -V, --version output version information, then exit\n"));
1227 printf(_(" --install-missing install missing extensions\n"));
1228 printf(_(" -?, --help show this help, then exit\n"));
1229
1230 printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1231 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1232}
#define _(x)
Definition: elog.c:91
static const char * progname
Definition: pg_amcheck.c:141
#define printf(...)
Definition: port.h:245

References _, printf, and progname.

Referenced by main().

◆ indent_lines()

static char * indent_lines ( const char *  str)
static

Definition at line 1008 of file pg_amcheck.c.

1009{
1011 const char *c;
1012 char *result;
1013
1016 for (c = str; *c; c++)
1017 {
1019 if (c[0] == '\n' && c[1] != '\0')
1021 }
1022 result = pstrdup(buf.data);
1024
1025 return result;
1026}
const char * str
char * c

References appendPQExpBufferChar(), appendPQExpBufferStr(), buf, initPQExpBuffer(), pstrdup(), str, and termPQExpBuffer().

Referenced by verify_btree_slot_handler(), and verify_heap_slot_handler().

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 220 of file pg_amcheck.c.

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;
232 uint64 pageschecked = 0;
233 uint64 pagestotal = 0;
234 uint64 relprogress = 0;
235 int pattern_id;
236
237 static struct option long_options[] = {
238 /* Connection options */
239 {"host", required_argument, NULL, 'h'},
240 {"port", required_argument, NULL, 'p'},
241 {"username", required_argument, NULL, 'U'},
242 {"no-password", no_argument, NULL, 'w'},
243 {"password", no_argument, NULL, 'W'},
244 {"maintenance-db", required_argument, NULL, 1},
245
246 /* check options */
247 {"all", no_argument, NULL, 'a'},
248 {"database", required_argument, NULL, 'd'},
249 {"exclude-database", required_argument, NULL, 'D'},
250 {"echo", no_argument, NULL, 'e'},
251 {"index", required_argument, NULL, 'i'},
252 {"exclude-index", required_argument, NULL, 'I'},
253 {"jobs", required_argument, NULL, 'j'},
254 {"progress", no_argument, NULL, 'P'},
255 {"relation", required_argument, NULL, 'r'},
256 {"exclude-relation", required_argument, NULL, 'R'},
257 {"schema", required_argument, NULL, 's'},
258 {"exclude-schema", required_argument, NULL, 'S'},
259 {"table", required_argument, NULL, 't'},
260 {"exclude-table", required_argument, NULL, 'T'},
261 {"verbose", no_argument, NULL, 'v'},
262 {"no-dependent-indexes", no_argument, NULL, 2},
263 {"no-dependent-toast", no_argument, NULL, 3},
264 {"exclude-toast-pointers", no_argument, NULL, 4},
265 {"on-error-stop", no_argument, NULL, 5},
266 {"skip", required_argument, NULL, 6},
267 {"startblock", required_argument, NULL, 7},
268 {"endblock", required_argument, NULL, 8},
269 {"rootdescend", no_argument, NULL, 9},
270 {"no-strict-names", no_argument, NULL, 10},
271 {"heapallindexed", no_argument, NULL, 11},
272 {"parent-check", no_argument, NULL, 12},
273 {"install-missing", optional_argument, NULL, 13},
274 {"checkunique", no_argument, NULL, 14},
275
276 {NULL, 0, NULL, 0}
277 };
278
279 int optindex;
280 int c;
281
282 const char *db = NULL;
283 const char *maintenance_db = NULL;
284
285 const char *host = NULL;
286 const char *port = NULL;
287 const char *username = NULL;
288 enum trivalue prompt_password = TRI_DEFAULT;
289 int encoding = pg_get_encoding_from_locale(NULL, false);
290 ConnParams cparams;
291
292 pg_logging_init(argv[0]);
293 progname = get_progname(argv[0]);
294 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_amcheck"));
295
297
298 /* process command-line options */
299 while ((c = getopt_long(argc, argv, "ad:D:eh:Hi:I:j:p:Pr:R:s:S:t:T:U:vwW",
300 long_options, &optindex)) != -1)
301 {
302 char *endptr;
303 unsigned long optval;
304
305 switch (c)
306 {
307 case 'a':
308 opts.alldb = true;
309 break;
310 case 'd':
311 opts.dbpattern = true;
313 break;
314 case 'D':
315 opts.dbpattern = true;
317 break;
318 case 'e':
319 opts.echo = true;
320 break;
321 case 'h':
322 host = pg_strdup(optarg);
323 break;
324 case 'i':
325 opts.allrel = false;
327 break;
328 case 'I':
329 opts.excludeidx = true;
331 break;
332 case 'j':
333 if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
334 &opts.jobs))
335 exit(1);
336 break;
337 case 'p':
339 break;
340 case 'P':
341 opts.show_progress = true;
342 break;
343 case 'r':
344 opts.allrel = false;
346 break;
347 case 'R':
348 opts.excludeidx = true;
349 opts.excludetbl = true;
351 break;
352 case 's':
353 opts.allrel = false;
355 break;
356 case 'S':
357 opts.excludensp = true;
359 break;
360 case 't':
361 opts.allrel = false;
363 break;
364 case 'T':
365 opts.excludetbl = true;
367 break;
368 case 'U':
370 break;
371 case 'v':
372 opts.verbose = true;
374 break;
375 case 'w':
376 prompt_password = TRI_NO;
377 break;
378 case 'W':
379 prompt_password = TRI_YES;
380 break;
381 case 1:
382 maintenance_db = pg_strdup(optarg);
383 break;
384 case 2:
386 break;
387 case 3:
389 break;
390 case 4:
391 opts.reconcile_toast = false;
392 break;
393 case 5:
394 opts.on_error_stop = true;
395 break;
396 case 6:
397 if (pg_strcasecmp(optarg, "all-visible") == 0)
398 opts.skip = "all-visible";
399 else if (pg_strcasecmp(optarg, "all-frozen") == 0)
400 opts.skip = "all-frozen";
401 else if (pg_strcasecmp(optarg, "none") == 0)
402 opts.skip = "none";
403 else
404 pg_fatal("invalid argument for option %s", "--skip");
405 break;
406 case 7:
407 errno = 0;
408 optval = strtoul(optarg, &endptr, 10);
409 if (endptr == optarg || *endptr != '\0' || errno != 0)
410 pg_fatal("invalid start block");
411 if (optval > MaxBlockNumber)
412 pg_fatal("start block out of bounds");
413 opts.startblock = optval;
414 break;
415 case 8:
416 errno = 0;
417 optval = strtoul(optarg, &endptr, 10);
418 if (endptr == optarg || *endptr != '\0' || errno != 0)
419 pg_fatal("invalid end block");
420 if (optval > MaxBlockNumber)
421 pg_fatal("end block out of bounds");
422 opts.endblock = optval;
423 break;
424 case 9:
425 opts.rootdescend = true;
426 opts.parent_check = true;
427 break;
428 case 10:
429 opts.strict_names = false;
430 break;
431 case 11:
432 opts.heapallindexed = true;
433 break;
434 case 12:
435 opts.parent_check = true;
436 break;
437 case 13:
438 opts.install_missing = true;
439 if (optarg)
441 break;
442 case 14:
443 opts.checkunique = true;
444 break;
445 default:
446 /* getopt_long already emitted a complaint */
447 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
448 exit(1);
449 }
450 }
451
453 pg_fatal("end block precedes start block");
454
455 /*
456 * A single non-option arguments specifies a database name or connection
457 * string.
458 */
459 if (optind < argc)
460 {
461 db = argv[optind];
462 optind++;
463 }
464
465 if (optind < argc)
466 {
467 pg_log_error("too many command-line arguments (first is \"%s\")",
468 argv[optind]);
469 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
470 exit(1);
471 }
472
473 /* fill cparams except for dbname, which is set below */
474 cparams.pghost = host;
475 cparams.pgport = port;
476 cparams.pguser = username;
477 cparams.prompt_password = prompt_password;
478 cparams.dbname = NULL;
479 cparams.override_dbname = NULL;
480
482
483 /* choose the database for our initial connection */
484 if (opts.alldb)
485 {
486 if (db != NULL)
487 pg_fatal("cannot specify a database name with --all");
488 cparams.dbname = maintenance_db;
489 }
490 else if (db != NULL)
491 {
492 if (opts.dbpattern)
493 pg_fatal("cannot specify both a database name and database patterns");
494 cparams.dbname = db;
495 }
496
497 if (opts.alldb || opts.dbpattern)
498 {
500 compile_database_list(conn, &databases, NULL);
501 }
502 else
503 {
504 if (cparams.dbname == NULL)
505 {
506 if (getenv("PGDATABASE"))
507 cparams.dbname = getenv("PGDATABASE");
508 else if (getenv("PGUSER"))
509 cparams.dbname = getenv("PGUSER");
510 else
512 }
513 conn = connectDatabase(&cparams, progname, opts.echo, false, true);
514 compile_database_list(conn, &databases, PQdb(conn));
515 }
516
517 if (databases.head == NULL)
518 {
519 if (conn != NULL)
521 pg_log_warning("no databases to check");
522 exit(0);
523 }
524
525 /*
526 * Compile a list of all relations spanning all databases to be checked.
527 */
528 for (cell = databases.head; cell; cell = cell->next)
529 {
530 PGresult *result;
531 int ntups;
532 const char *amcheck_schema = NULL;
533 DatabaseInfo *dat = (DatabaseInfo *) cell->ptr;
534
535 cparams.override_dbname = dat->datname;
536 if (conn == NULL || strcmp(PQdb(conn), dat->datname) != 0)
537 {
538 if (conn != NULL)
540 conn = connectDatabase(&cparams, progname, opts.echo, false, true);
541 }
542
543 /*
544 * Optionally install amcheck if not already installed in this
545 * database.
546 */
548 {
549 char *schema;
550 char *install_sql;
551
552 /*
553 * Must re-escape the schema name for each database, as the
554 * escaping rules may change.
555 */
557 strlen(opts.install_schema));
558 install_sql = psprintf("CREATE EXTENSION IF NOT EXISTS amcheck WITH SCHEMA %s",
559 schema);
560
561 executeCommand(conn, install_sql, opts.echo);
562 pfree(install_sql);
563 PQfreemem(schema);
564 }
565
566 /*
567 * Verify that amcheck is installed for this next database. User
568 * error could result in a database not having amcheck that should
569 * have it, but we also could be iterating over multiple databases
570 * where not all of them have amcheck installed (for example,
571 * 'template1').
572 */
574 if (PQresultStatus(result) != PGRES_TUPLES_OK)
575 {
576 /* Querying the catalog failed. */
577 pg_log_error("database \"%s\": %s",
579 pg_log_error_detail("Query was: %s", amcheck_sql);
580 PQclear(result);
582 exit(1);
583 }
584 ntups = PQntuples(result);
585 if (ntups == 0)
586 {
587 /* Querying the catalog succeeded, but amcheck is missing. */
588 pg_log_warning("skipping database \"%s\": amcheck is not installed",
589 PQdb(conn));
590 PQclear(result);
592 conn = NULL;
593 continue;
594 }
595 amcheck_schema = PQgetvalue(result, 0, 0);
596 if (opts.verbose)
597 pg_log_info("in database \"%s\": using amcheck version \"%s\" in schema \"%s\"",
598 PQdb(conn), PQgetvalue(result, 0, 1), amcheck_schema);
599 dat->amcheck_schema = PQescapeIdentifier(conn, amcheck_schema,
600 strlen(amcheck_schema));
601
602 /*
603 * Check the version of amcheck extension. Skip requested unique
604 * constraint check with warning if it is not yet supported by
605 * amcheck.
606 */
607 if (opts.checkunique == true)
608 {
609 /*
610 * Now amcheck has only major and minor versions in the string but
611 * we also support revision just in case. Now it is expected to be
612 * zero.
613 */
614 int vmaj = 0,
615 vmin = 0,
616 vrev = 0;
617 const char *amcheck_version = PQgetvalue(result, 0, 1);
618
619 sscanf(amcheck_version, "%d.%d.%d", &vmaj, &vmin, &vrev);
620
621 /*
622 * checkunique option is supported in amcheck since version 1.4
623 */
624 if ((vmaj == 1 && vmin < 4) || vmaj == 0)
625 {
626 pg_log_warning("option %s is not supported by amcheck version %s",
627 "--checkunique", amcheck_version);
628 dat->is_checkunique = false;
629 }
630 else
631 dat->is_checkunique = true;
632 }
633
634 PQclear(result);
635
636 compile_relation_list_one_db(conn, &relations, dat, &pagestotal);
637 }
638
639 /*
640 * Check that all inclusion patterns matched at least one schema or
641 * relation that we can check.
642 */
643 for (pattern_id = 0; pattern_id < opts.include.len; pattern_id++)
644 {
645 PatternInfo *pat = &opts.include.data[pattern_id];
646
647 if (!pat->matched && (pat->nsp_regex != NULL || pat->rel_regex != NULL))
648 {
649 failed = opts.strict_names;
650
651 if (pat->heap_only)
652 log_no_match("no heap tables to check matching \"%s\"",
653 pat->pattern);
654 else if (pat->btree_only)
655 log_no_match("no btree indexes to check matching \"%s\"",
656 pat->pattern);
657 else if (pat->rel_regex == NULL)
658 log_no_match("no relations to check in schemas matching \"%s\"",
659 pat->pattern);
660 else
661 log_no_match("no relations to check matching \"%s\"",
662 pat->pattern);
663 }
664 }
665
666 if (failed)
667 {
668 if (conn != NULL)
670 exit(1);
671 }
672
673 /*
674 * Set parallel_workers to the lesser of opts.jobs and the number of
675 * relations.
676 */
677 parallel_workers = 0;
678 for (cell = relations.head; cell; cell = cell->next)
679 {
680 reltotal++;
681 if (parallel_workers < opts.jobs)
682 parallel_workers++;
683 }
684
685 if (reltotal == 0)
686 {
687 if (conn != NULL)
689 pg_fatal("no relations to check");
690 }
691 progress_report(reltotal, relprogress, pagestotal, pageschecked,
692 NULL, true, false);
693
694 /*
695 * Main event loop.
696 *
697 * We use server-side parallelism to check up to parallel_workers
698 * relations in parallel. The list of relations was computed in database
699 * order, which minimizes the number of connects and disconnects as we
700 * process the list.
701 */
702 latest_datname = NULL;
703 sa = ParallelSlotsSetup(parallel_workers, &cparams, progname, opts.echo,
704 NULL);
705 if (conn != NULL)
706 {
708 conn = NULL;
709 }
710
711 initPQExpBuffer(&sql);
712 for (relprogress = 0, cell = relations.head; cell; cell = cell->next)
713 {
714 ParallelSlot *free_slot;
715 RelationInfo *rel;
716
717 rel = (RelationInfo *) cell->ptr;
718
720 {
721 failed = true;
722 break;
723 }
724
725 /*
726 * The list of relations is in database sorted order. If this next
727 * relation is in a different database than the last one seen, we are
728 * about to start checking this database. Note that other slots may
729 * still be working on relations from prior databases.
730 */
731 latest_datname = rel->datinfo->datname;
732
733 progress_report(reltotal, relprogress, pagestotal, pageschecked,
734 latest_datname, false, false);
735
736 relprogress++;
737 pageschecked += rel->blocks_to_check;
738
739 /*
740 * Get a parallel slot for the next amcheck command, blocking if
741 * necessary until one is available, or until a previously issued slot
742 * command fails, indicating that we should abort checking the
743 * remaining objects.
744 */
745 free_slot = ParallelSlotsGetIdle(sa, rel->datinfo->datname);
746 if (!free_slot)
747 {
748 /*
749 * Something failed. We don't need to know what it was, because
750 * the handler should already have emitted the necessary error
751 * messages.
752 */
753 failed = true;
754 break;
755 }
756
757 if (opts.verbose)
759
760 /*
761 * Execute the appropriate amcheck command for this relation using our
762 * slot's database connection. We do not wait for the command to
763 * complete, nor do we perform any error checking, as that is done by
764 * the parallel slots and our handler callback functions.
765 */
766 if (rel->is_heap)
767 {
768 if (opts.verbose)
769 {
771 fprintf(stderr, "\n");
772 pg_log_info("checking heap table \"%s.%s.%s\"",
773 rel->datinfo->datname, rel->nspname, rel->relname);
775 }
776 prepare_heap_command(&sql, rel, free_slot->connection);
777 rel->sql = pstrdup(sql.data); /* pg_free'd after command */
779 run_command(free_slot, rel->sql);
780 }
781 else
782 {
783 if (opts.verbose)
784 {
786 fprintf(stderr, "\n");
787
788 pg_log_info("checking btree index \"%s.%s.%s\"",
789 rel->datinfo->datname, rel->nspname, rel->relname);
791 }
792 prepare_btree_command(&sql, rel, free_slot->connection);
793 rel->sql = pstrdup(sql.data); /* pg_free'd after command */
795 run_command(free_slot, rel->sql);
796 }
797 }
798 termPQExpBuffer(&sql);
799
800 if (!failed)
801 {
802
803 /*
804 * Wait for all slots to complete, or for one to indicate that an
805 * error occurred. Like above, we rely on the handler emitting the
806 * necessary error messages.
807 */
809 failed = true;
810
811 progress_report(reltotal, relprogress, pagestotal, pageschecked, NULL, true, true);
812 }
813
814 if (sa)
815 {
818 }
819
820 if (failed)
821 exit(1);
822
823 if (!all_checks_pass)
824 exit(2);
825}
#define MaxBlockNumber
Definition: block.h:35
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1185
uint64_t uint64
Definition: c.h:503
volatile sig_atomic_t CancelRequested
Definition: cancel.c:59
void setup_cancel_handler(void(*query_cancel_callback)(void))
Definition: cancel.c:183
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:429
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)
Definition: connect_utils.c:32
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
Definition: fe-connect.c:7759
void PQfreemem(void *ptr)
Definition: fe-exec.c:4032
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4369
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
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
static char * username
Definition: initdb.c:153
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
@ PQERRORS_VERBOSE
Definition: libpq-fe.h:158
void pg_logging_increase_verbosity(void)
Definition: logging.c:185
void pg_logging_init(const char *argv0)
Definition: logging.c:83
#define pg_log_error_hint(...)
Definition: logging.h:112
void pfree(void *pointer)
Definition: mcxt.c:2147
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
Definition: option_utils.c:50
void handle_help_version_opts(int argc, char *argv[], const char *fixed_progname, help_handler hlp)
Definition: option_utils.c:24
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)
Definition: parallel_slot.h:47
static void progress_report(uint64 relations_total, uint64 relations_checked, uint64 relpages_total, uint64 relpages_checked, const char *datname, bool force, bool finished)
Definition: pg_amcheck.c:1244
static void compile_database_list(PGconn *conn, SimplePtrList *databases, const char *initial_dbname)
Definition: pg_amcheck.c:1585
static void prepare_btree_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
Definition: pg_amcheck.c:883
static void help(const char *progname)
Definition: pg_amcheck.c:1183
static void append_database_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1358
static bool verify_heap_slot_handler(PGresult *res, PGconn *conn, void *context)
Definition: pg_amcheck.c:1039
#define FREE_AND_SET_NULL(x)
Definition: pg_amcheck.c:214
static const char *const amcheck_sql
Definition: pg_amcheck.c:173
static void run_command(ParallelSlot *slot, const char *sql)
Definition: pg_amcheck.c:932
static void append_heap_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1496
static void append_schema_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1388
static bool verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
Definition: pg_amcheck.c:1120
static bool progress_since_last_stderr
Definition: pg_amcheck.c:148
static void append_relation_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1480
static void prepare_heap_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
Definition: pg_amcheck.c:843
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)
Definition: pg_amcheck.c:1885
static void append_btree_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1512
static void executeCommand(PGconn *conn, const char *query)
Definition: pg_dumpall.c:1855
PGDLLIMPORT int optind
Definition: getopt.c:51
PGDLLIMPORT char * optarg
Definition: getopt.c:53
static int port
Definition: pg_regress.c:115
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
const char * get_progname(const char *argv0)
Definition: path.c:652
int pg_get_encoding_from_locale(const char *ctype, bool write_message)
Definition: chklocale.c:301
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
bool install_missing
Definition: pg_amcheck.c:69
char * install_schema
Definition: pg_amcheck.c:70
bool reconcile_toast
Definition: pg_amcheck.c:96
bool show_progress
Definition: pg_amcheck.c:62
bool on_error_stop
Definition: pg_amcheck.c:97
bool heapallindexed
Definition: pg_amcheck.c:105
const char * skip
Definition: pg_amcheck.c:100
char * amcheck_schema
Definition: pg_amcheck.c:153
bool is_checkunique
Definition: pg_amcheck.c:154
PGconn * connection
Definition: parallel_slot.h:23
char * sql
Definition: pg_amcheck.c:166
struct SimplePtrListCell * next
Definition: simple_list.h:48
SimplePtrListCell * head
Definition: simple_list.h:54
const char * pguser
Definition: connect_utils.h:31
char * override_dbname
Definition: pg_backup.h:93
char * pgport
Definition: pg_backup.h:87
char * pghost
Definition: pg_backup.h:88
char * dbname
Definition: pg_backup.h:86
enum trivalue prompt_password
Definition: connect_utils.h:32
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

References all_checks_pass, AmcheckOptions::alldb, AmcheckOptions::allrel, DatabaseInfo::amcheck_schema, amcheck_sql, append_btree_pattern(), append_database_pattern(), append_heap_pattern(), append_relation_pattern(), append_schema_pattern(), RelationInfo::blocks_to_check, PatternInfo::btree_only, CancelRequested, AmcheckOptions::checkunique, compile_database_list(), compile_relation_list_one_db(), conn, connectDatabase(), ParallelSlot::connection, connectMaintenanceDatabase(), PatternInfoArray::data, PQExpBufferData::data, RelationInfo::datinfo, DatabaseInfo::datname, _connParams::dbname, AmcheckOptions::dbpattern, disconnectDatabase(), AmcheckOptions::echo, encoding, AmcheckOptions::endblock, AmcheckOptions::exclude, AmcheckOptions::excludeidx, AmcheckOptions::excludensp, AmcheckOptions::excludetbl, executeCommand(), executeQuery(), fprintf, FREE_AND_SET_NULL, get_progname(), get_user_name_or_exit(), getopt_long(), handle_help_version_opts(), SimplePtrList::head, PatternInfo::heap_only, AmcheckOptions::heapallindexed, help(), if(), AmcheckOptions::include, initPQExpBuffer(), AmcheckOptions::install_missing, AmcheckOptions::install_schema, DatabaseInfo::is_checkunique, RelationInfo::is_heap, AmcheckOptions::jobs, PatternInfoArray::len, log_no_match, PatternInfo::matched, MaxBlockNumber, SimplePtrListCell::next, no_argument, AmcheckOptions::no_btree_expansion, AmcheckOptions::no_toast_expansion, PatternInfo::nsp_regex, RelationInfo::nspname, AmcheckOptions::on_error_stop, optarg, optind, option_parse_int(), optional_argument, opts, _connParams::override_dbname, ParallelSlotsAdoptConn(), ParallelSlotSetHandler(), ParallelSlotsGetIdle(), ParallelSlotsSetup(), ParallelSlotsTerminate(), ParallelSlotsWaitCompletion(), AmcheckOptions::parent_check, PatternInfo::pattern, pfree(), pg_fatal, pg_get_encoding_from_locale(), pg_log_error, pg_log_error_detail, pg_log_error_hint, pg_log_info, pg_log_warning, pg_logging_increase_verbosity(), pg_logging_init(), pg_strcasecmp(), pg_strdup(), PG_TEXTDOMAIN, _connParams::pghost, _connParams::pgport, PGRES_TUPLES_OK, _connParams::pguser, port, PQclear(), PQdb(), PQerrorMessage(), PQERRORS_VERBOSE, PQescapeIdentifier(), PQfreemem(), PQgetvalue(), PQntuples(), PQresultStatus(), PQsetErrorVerbosity(), prepare_btree_command(), prepare_heap_command(), progname, progress_report(), progress_since_last_stderr, _connParams::prompt_password, psprintf(), pstrdup(), SimplePtrListCell::ptr, AmcheckOptions::reconcile_toast, PatternInfo::rel_regex, RelationInfo::relname, required_argument, AmcheckOptions::rootdescend, run_command(), set_pglocale_pgservice(), setup_cancel_handler(), AmcheckOptions::show_progress, AmcheckOptions::skip, RelationInfo::sql, AmcheckOptions::startblock, AmcheckOptions::strict_names, termPQExpBuffer(), TRI_DEFAULT, TRI_NO, TRI_YES, username, AmcheckOptions::verbose, verify_btree_slot_handler(), and verify_heap_slot_handler().

◆ prepare_btree_command()

static void prepare_btree_command ( PQExpBuffer  sql,
RelationInfo rel,
PGconn conn 
)
static

Definition at line 883 of file pg_amcheck.c.

884{
885 resetPQExpBuffer(sql);
886
887 if (opts.parent_check)
889 "SELECT %s.bt_index_parent_check("
890 "index := c.oid, heapallindexed := %s, rootdescend := %s "
891 "%s)"
892 "\nFROM pg_catalog.pg_class c, pg_catalog.pg_index i "
893 "WHERE c.oid = %u "
894 "AND c.oid = i.indexrelid "
895 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP) " "
896 "AND i.indisready AND i.indisvalid AND i.indislive",
898 (opts.heapallindexed ? "true" : "false"),
899 (opts.rootdescend ? "true" : "false"),
900 (rel->datinfo->is_checkunique ? ", checkunique := true" : ""),
901 rel->reloid);
902 else
904 "SELECT %s.bt_index_check("
905 "index := c.oid, heapallindexed := %s "
906 "%s)"
907 "\nFROM pg_catalog.pg_class c, pg_catalog.pg_index i "
908 "WHERE c.oid = %u "
909 "AND c.oid = i.indexrelid "
910 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP) " "
911 "AND i.indisready AND i.indisvalid AND i.indislive",
913 (opts.heapallindexed ? "true" : "false"),
914 (rel->datinfo->is_checkunique ? ", checkunique := true" : ""),
915 rel->reloid);
916}
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:146

References DatabaseInfo::amcheck_schema, appendPQExpBuffer(), CppAsString2, RelationInfo::datinfo, AmcheckOptions::heapallindexed, DatabaseInfo::is_checkunique, opts, AmcheckOptions::parent_check, RelationInfo::reloid, resetPQExpBuffer(), and AmcheckOptions::rootdescend.

Referenced by main().

◆ prepare_heap_command()

static void prepare_heap_command ( PQExpBuffer  sql,
RelationInfo rel,
PGconn conn 
)
static

Definition at line 843 of file pg_amcheck.c.

844{
845 resetPQExpBuffer(sql);
847 "SELECT v.blkno, v.offnum, v.attnum, v.msg "
848 "FROM pg_catalog.pg_class c, %s.verify_heapam("
849 "\nrelation := c.oid, on_error_stop := %s, check_toast := %s, skip := '%s'",
851 opts.on_error_stop ? "true" : "false",
852 opts.reconcile_toast ? "true" : "false",
853 opts.skip);
854
855 if (opts.startblock >= 0)
856 appendPQExpBuffer(sql, ", startblock := " INT64_FORMAT, opts.startblock);
857 if (opts.endblock >= 0)
858 appendPQExpBuffer(sql, ", endblock := " INT64_FORMAT, opts.endblock);
859
861 "\n) v WHERE c.oid = %u "
862 "AND c.relpersistence != " CppAsString2(RELPERSISTENCE_TEMP),
863 rel->reloid);
864}
#define INT64_FORMAT
Definition: c.h:520

References DatabaseInfo::amcheck_schema, appendPQExpBuffer(), CppAsString2, RelationInfo::datinfo, AmcheckOptions::endblock, INT64_FORMAT, AmcheckOptions::on_error_stop, opts, AmcheckOptions::reconcile_toast, RelationInfo::reloid, resetPQExpBuffer(), AmcheckOptions::skip, and AmcheckOptions::startblock.

Referenced by main().

◆ progress_report()

static void progress_report ( uint64  relations_total,
uint64  relations_checked,
uint64  relpages_total,
uint64  relpages_checked,
const char *  datname,
bool  force,
bool  finished 
)
static

Definition at line 1244 of file pg_amcheck.c.

1247{
1248 int percent_rel = 0;
1249 int percent_pages = 0;
1250 char checked_rel[32];
1251 char total_rel[32];
1252 char checked_pages[32];
1253 char total_pages[32];
1254 pg_time_t now;
1255
1256 if (!opts.show_progress)
1257 return;
1258
1259 now = time(NULL);
1260 if (now == last_progress_report && !force && !finished)
1261 return; /* Max once per second */
1262
1264 if (relations_total)
1265 percent_rel = (int) (relations_checked * 100 / relations_total);
1266 if (relpages_total)
1267 percent_pages = (int) (relpages_checked * 100 / relpages_total);
1268
1269 snprintf(checked_rel, sizeof(checked_rel), UINT64_FORMAT, relations_checked);
1270 snprintf(total_rel, sizeof(total_rel), UINT64_FORMAT, relations_total);
1271 snprintf(checked_pages, sizeof(checked_pages), UINT64_FORMAT, relpages_checked);
1272 snprintf(total_pages, sizeof(total_pages), UINT64_FORMAT, relpages_total);
1273
1274#define VERBOSE_DATNAME_LENGTH 35
1275 if (opts.verbose)
1276 {
1277 if (!datname)
1278
1279 /*
1280 * No datname given, so clear the status line (used for first and
1281 * last call)
1282 */
1283 fprintf(stderr,
1284 _("%*s/%s relations (%d%%), %*s/%s pages (%d%%) %*s"),
1285 (int) strlen(total_rel),
1286 checked_rel, total_rel, percent_rel,
1287 (int) strlen(total_pages),
1288 checked_pages, total_pages, percent_pages,
1289 VERBOSE_DATNAME_LENGTH + 2, "");
1290 else
1291 {
1292 bool truncate = (strlen(datname) > VERBOSE_DATNAME_LENGTH);
1293
1294 fprintf(stderr,
1295 _("%*s/%s relations (%d%%), %*s/%s pages (%d%%) (%s%-*.*s)"),
1296 (int) strlen(total_rel),
1297 checked_rel, total_rel, percent_rel,
1298 (int) strlen(total_pages),
1299 checked_pages, total_pages, percent_pages,
1300 /* Prefix with "..." if we do leading truncation */
1301 truncate ? "..." : "",
1304 /* Truncate datname at beginning if it's too long */
1305 truncate ? datname + strlen(datname) - VERBOSE_DATNAME_LENGTH + 3 : datname);
1306 }
1307 }
1308 else
1309 fprintf(stderr,
1310 _("%*s/%s relations (%d%%), %*s/%s pages (%d%%)"),
1311 (int) strlen(total_rel),
1312 checked_rel, total_rel, percent_rel,
1313 (int) strlen(total_pages),
1314 checked_pages, total_pages, percent_pages);
1315
1316 /*
1317 * Stay on the same line if reporting to a terminal and we're not done
1318 * yet.
1319 */
1320 if (!finished && isatty(fileno(stderr)))
1321 {
1322 fputc('\r', stderr);
1324 }
1325 else
1326 fputc('\n', stderr);
1327}
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1609
#define UINT64_FORMAT
Definition: c.h:521
#define VERBOSE_DATNAME_LENGTH
static pg_time_t last_progress_report
Definition: pg_amcheck.c:147
int64 pg_time_t
Definition: pgtime.h:23
#define snprintf
Definition: port.h:239

References _, datname, fprintf, last_progress_report, now(), opts, progress_since_last_stderr, AmcheckOptions::show_progress, snprintf, UINT64_FORMAT, AmcheckOptions::verbose, and VERBOSE_DATNAME_LENGTH.

Referenced by main(), and write_target_range().

◆ run_command()

static void run_command ( ParallelSlot slot,
const char *  sql 
)
static

Definition at line 932 of file pg_amcheck.c.

933{
934 if (opts.echo)
935 printf("%s\n", sql);
936
937 if (PQsendQuery(slot->connection, sql) == 0)
938 {
939 pg_log_error("error sending command to database \"%s\": %s",
940 PQdb(slot->connection),
942 pg_log_error_detail("Command was: %s", sql);
943 exit(1);
944 }
945}
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1416

References ParallelSlot::connection, AmcheckOptions::echo, opts, pg_log_error, pg_log_error_detail, PQdb(), PQerrorMessage(), PQsendQuery(), and printf.

Referenced by main().

◆ should_processing_continue()

static bool should_processing_continue ( PGresult res)
static

Definition at line 964 of file pg_amcheck.c.

965{
966 const char *severity;
967
968 switch (PQresultStatus(res))
969 {
970 /* These are expected and ok */
971 case PGRES_COMMAND_OK:
972 case PGRES_TUPLES_OK:
974 break;
975
976 /* This is expected but requires closer scrutiny */
979 if (severity == NULL)
980 return false; /* libpq failure, probably lost connection */
981 if (strcmp(severity, "FATAL") == 0)
982 return false;
983 if (strcmp(severity, "PANIC") == 0)
984 return false;
985 break;
986
987 /* These are unexpected */
990 case PGRES_COPY_OUT:
991 case PGRES_COPY_IN:
992 case PGRES_COPY_BOTH:
997 return false;
998 }
999 return true;
1000}
char * PQresultErrorField(const PGresult *res, int fieldcode)
Definition: fe-exec.c:3466
@ PGRES_COPY_IN
Definition: libpq-fe.h:132
@ PGRES_COPY_BOTH
Definition: libpq-fe.h:137
@ PGRES_COMMAND_OK
Definition: libpq-fe.h:125
@ PGRES_TUPLES_CHUNK
Definition: libpq-fe.h:142
@ PGRES_FATAL_ERROR
Definition: libpq-fe.h:136
@ PGRES_SINGLE_TUPLE
Definition: libpq-fe.h:138
@ PGRES_COPY_OUT
Definition: libpq-fe.h:131
@ PGRES_EMPTY_QUERY
Definition: libpq-fe.h:124
@ PGRES_PIPELINE_SYNC
Definition: libpq-fe.h:139
@ PGRES_BAD_RESPONSE
Definition: libpq-fe.h:133
@ PGRES_PIPELINE_ABORTED
Definition: libpq-fe.h:140
@ PGRES_NONFATAL_ERROR
Definition: libpq-fe.h:135
#define PG_DIAG_SEVERITY_NONLOCALIZED
Definition: postgres_ext.h:51

References PG_DIAG_SEVERITY_NONLOCALIZED, PGRES_BAD_RESPONSE, PGRES_COMMAND_OK, PGRES_COPY_BOTH, PGRES_COPY_IN, PGRES_COPY_OUT, PGRES_EMPTY_QUERY, PGRES_FATAL_ERROR, PGRES_NONFATAL_ERROR, PGRES_PIPELINE_ABORTED, PGRES_PIPELINE_SYNC, PGRES_SINGLE_TUPLE, PGRES_TUPLES_CHUNK, PGRES_TUPLES_OK, PQresultErrorField(), and PQresultStatus().

Referenced by verify_btree_slot_handler(), and verify_heap_slot_handler().

◆ verify_btree_slot_handler()

static bool verify_btree_slot_handler ( PGresult res,
PGconn conn,
void *  context 
)
static

Definition at line 1120 of file pg_amcheck.c.

1121{
1122 RelationInfo *rel = (RelationInfo *) context;
1123
1124 if (PQresultStatus(res) == PGRES_TUPLES_OK)
1125 {
1126 int ntups = PQntuples(res);
1127
1128 if (ntups > 1)
1129 {
1130 /*
1131 * We expect the btree checking functions to return one void row
1132 * each, or zero rows if the check was skipped due to the object
1133 * being in the wrong state to be checked, so we should output
1134 * some sort of warning if we get anything more, not because it
1135 * indicates corruption, but because it suggests a mismatch
1136 * between amcheck and pg_amcheck versions.
1137 *
1138 * In conjunction with --progress, anything written to stderr at
1139 * this time would present strangely to the user without an extra
1140 * newline, so we print one. If we were multithreaded, we'd have
1141 * to avoid splitting this across multiple calls, but we're in an
1142 * event loop, so it doesn't matter.
1143 */
1145 fprintf(stderr, "\n");
1146 pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d",
1147 rel->datinfo->datname, rel->nspname, rel->relname, ntups);
1148 if (opts.verbose)
1149 pg_log_warning_detail("Query was: %s", rel->sql);
1150 pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
1151 progname);
1153 }
1154 }
1155 else
1156 {
1157 char *msg = indent_lines(PQerrorMessage(conn));
1158
1159 all_checks_pass = false;
1160 printf(_("btree index \"%s.%s.%s\":\n"),
1161 rel->datinfo->datname, rel->nspname, rel->relname);
1162 printf("%s", msg);
1163 if (opts.verbose)
1164 printf(_("query was: %s\n"), rel->sql);
1165 FREE_AND_SET_NULL(msg);
1166 }
1167
1168 FREE_AND_SET_NULL(rel->sql);
1171
1172 return should_processing_continue(res);
1173}
#define pg_log_warning_hint(...)
Definition: logging.h:121
#define pg_log_warning_detail(...)
Definition: logging.h:118
static bool should_processing_continue(PGresult *res)
Definition: pg_amcheck.c:964
static char * indent_lines(const char *str)
Definition: pg_amcheck.c:1008

References _, all_checks_pass, conn, RelationInfo::datinfo, DatabaseInfo::datname, fprintf, FREE_AND_SET_NULL, indent_lines(), RelationInfo::nspname, opts, pg_log_warning, pg_log_warning_detail, pg_log_warning_hint, PGRES_TUPLES_OK, PQerrorMessage(), PQntuples(), PQresultStatus(), printf, progname, progress_since_last_stderr, RelationInfo::relname, should_processing_continue(), AmcheckOptions::show_progress, RelationInfo::sql, and AmcheckOptions::verbose.

Referenced by main().

◆ verify_heap_slot_handler()

static bool verify_heap_slot_handler ( PGresult res,
PGconn conn,
void *  context 
)
static

Definition at line 1039 of file pg_amcheck.c.

1040{
1041 RelationInfo *rel = (RelationInfo *) context;
1042
1043 if (PQresultStatus(res) == PGRES_TUPLES_OK)
1044 {
1045 int i;
1046 int ntups = PQntuples(res);
1047
1048 if (ntups > 0)
1049 all_checks_pass = false;
1050
1051 for (i = 0; i < ntups; i++)
1052 {
1053 const char *msg;
1054
1055 /* The message string should never be null, but check */
1056 if (PQgetisnull(res, i, 3))
1057 msg = "NO MESSAGE";
1058 else
1059 msg = PQgetvalue(res, i, 3);
1060
1061 if (!PQgetisnull(res, i, 2))
1062 printf(_("heap table \"%s.%s.%s\", block %s, offset %s, attribute %s:\n"),
1063 rel->datinfo->datname, rel->nspname, rel->relname,
1064 PQgetvalue(res, i, 0), /* blkno */
1065 PQgetvalue(res, i, 1), /* offnum */
1066 PQgetvalue(res, i, 2)); /* attnum */
1067
1068 else if (!PQgetisnull(res, i, 1))
1069 printf(_("heap table \"%s.%s.%s\", block %s, offset %s:\n"),
1070 rel->datinfo->datname, rel->nspname, rel->relname,
1071 PQgetvalue(res, i, 0), /* blkno */
1072 PQgetvalue(res, i, 1)); /* offnum */
1073
1074 else if (!PQgetisnull(res, i, 0))
1075 printf(_("heap table \"%s.%s.%s\", block %s:\n"),
1076 rel->datinfo->datname, rel->nspname, rel->relname,
1077 PQgetvalue(res, i, 0)); /* blkno */
1078
1079 else
1080 printf(_("heap table \"%s.%s.%s\":\n"),
1081 rel->datinfo->datname, rel->nspname, rel->relname);
1082
1083 printf(" %s\n", msg);
1084 }
1085 }
1086 else if (PQresultStatus(res) != PGRES_TUPLES_OK)
1087 {
1088 char *msg = indent_lines(PQerrorMessage(conn));
1089
1090 all_checks_pass = false;
1091 printf(_("heap table \"%s.%s.%s\":\n"),
1092 rel->datinfo->datname, rel->nspname, rel->relname);
1093 printf("%s", msg);
1094 if (opts.verbose)
1095 printf(_("query was: %s\n"), rel->sql);
1096 FREE_AND_SET_NULL(msg);
1097 }
1098
1099 FREE_AND_SET_NULL(rel->sql);
1102
1103 return should_processing_continue(res);
1104}

References _, all_checks_pass, conn, RelationInfo::datinfo, DatabaseInfo::datname, FREE_AND_SET_NULL, i, indent_lines(), RelationInfo::nspname, opts, PGRES_TUPLES_OK, PQerrorMessage(), PQgetisnull(), PQgetvalue(), PQntuples(), PQresultStatus(), printf, RelationInfo::relname, should_processing_continue(), RelationInfo::sql, and AmcheckOptions::verbose.

Referenced by main().

Variable Documentation

◆ all_checks_pass

bool all_checks_pass = true
static

Definition at line 144 of file pg_amcheck.c.

Referenced by main(), verify_btree_slot_handler(), and verify_heap_slot_handler().

◆ amcheck_sql

const char* const amcheck_sql
static
Initial value:
=
"SELECT n.nspname, x.extversion FROM pg_catalog.pg_extension x"
"\nJOIN pg_catalog.pg_namespace n ON x.extnamespace = n.oid"
"\nWHERE x.extname = 'amcheck'"

Definition at line 173 of file pg_amcheck.c.

Referenced by main().

◆ last_progress_report

pg_time_t last_progress_report = 0
static

Definition at line 147 of file pg_amcheck.c.

Referenced by progress_report().

◆ opts

AmcheckOptions opts
static
Initial value:
= {
.dbpattern = false,
.alldb = false,
.echo = false,
.verbose = false,
.strict_names = true,
.show_progress = false,
.jobs = 1,
.install_missing = false,
.install_schema = "pg_catalog",
.include = {NULL, 0},
.exclude = {NULL, 0},
.excludetbl = false,
.excludeidx = false,
.excludensp = false,
.allrel = true,
.no_toast_expansion = false,
.reconcile_toast = true,
.on_error_stop = false,
.startblock = -1,
.endblock = -1,
.skip = "none",
.parent_check = false,
.rootdescend = false,
.heapallindexed = false,
.checkunique = false,
.no_btree_expansion = false
}

Definition at line 112 of file pg_amcheck.c.

Referenced by AlterSubscription(), append_relation_pattern_helper(), append_schema_pattern(), ArchiveEntry(), BloomFillMetapage(), brin_bloom_add_value(), brin_bloom_get_ndistinct(), brin_minmax_multi_add_value(), brin_minmax_multi_get_values(), build_local_reloptions(), compile_database_list(), compile_relation_list_one_db(), copy_connection(), CopyFromGetRoutine(), CopyIndexAttOptions(), CopyToGetRoutine(), CreateSubscription(), get_attribute_options(), get_tablespace(), getFormattedTypeName(), initBloomState(), InitDumpOptions(), libpqrcv_check_conninfo(), libpqrcv_get_dbname_from_conninfo(), main(), makeDefaultBloomOptions(), NewDumpOptions(), NewRestoreOptions(), parse_re_flags(), parse_subscription_options(), parse_test_flags(), prepare_btree_command(), prepare_heap_command(), progress_report(), read_restore_filters(), RelationGetIndexAttOptions(), restore_all_databases(), restore_one_database(), run_command(), sql_exec_dumpalldbs(), sql_exec_dumpalltables(), sql_exec_dumpalltbspc(), sql_exec_searchtables(), test_protocol_version(), vacuum_rel(), verify_btree_slot_handler(), and verify_heap_slot_handler().

◆ progname

const char* progname = NULL
static

Definition at line 141 of file pg_amcheck.c.

Referenced by help(), main(), and verify_btree_slot_handler().

◆ progress_since_last_stderr

bool progress_since_last_stderr = false
static

Definition at line 148 of file pg_amcheck.c.

Referenced by main(), progress_report(), and verify_btree_slot_handler().