PostgreSQL Source Code  git master
pg_amcheck.c File Reference
#include "postgres_fe.h"
#include <limits.h>
#include <time.h>
#include "catalog/pg_am_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 *datinfo, 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 * amcheck_sql
 

Macro Definition Documentation

◆ FREE_AND_SET_NULL

#define FREE_AND_SET_NULL (   x)
Value:
do { \
pg_free(x); \
(x) = NULL; \
} while (0)

Definition at line 210 of file pg_amcheck.c.

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

◆ log_no_match

#define log_no_match (   ...)
Value:
do { \
pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
} while(0)
static AmcheckOptions opts
Definition: pg_amcheck.c:110
void pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
Definition: logging.c:197
bool strict_names
Definition: pg_amcheck.c:60

Definition at line 203 of file pg_amcheck.c.

Referenced by compile_database_list(), and main().

◆ VERBOSE_DATNAME_LENGTH

#define VERBOSE_DATNAME_LENGTH   35

Referenced by progress_report().

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 1467 of file pg_amcheck.c.

References append_relation_pattern_helper().

Referenced by main().

1468 {
1469  append_relation_pattern_helper(pia, pattern, encoding, false, true);
1470 }
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
Definition: pg_amcheck.c:1392
int32 encoding
Definition: pg_database.h:41

◆ append_database_pattern()

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

Definition at line 1334 of file pg_amcheck.c.

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

Referenced by main().

1335 {
1338 
1339  initPQExpBuffer(&buf);
1340  patternToSQLRegex(encoding, NULL, NULL, &buf, pattern, false);
1341  info->pattern = pattern;
1342  info->db_regex = pstrdup(buf.data);
1343 
1344  termPQExpBuffer(&buf);
1345 }
char * db_regex
Definition: pg_amcheck.c:35
const char * pattern
Definition: pg_amcheck.c:34
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
char * pstrdup(const char *in)
Definition: mcxt.c:1299
void patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf, PQExpBuffer namebuf, const char *pattern, bool force_escape)
Definition: string_utils.c:967
static char * buf
Definition: pg_test_fsync.c:68
int32 encoding
Definition: pg_database.h:41
static PatternInfo * extend_pattern_info_array(PatternInfoArray *pia)
Definition: pg_amcheck.c:1312
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92

◆ append_db_pattern_cte()

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

Definition at line 1494 of file pg_amcheck.c.

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

Referenced by compile_database_list().

1496 {
1497  int pattern_id;
1498  const char *comma;
1499  bool have_values;
1500 
1501  comma = "";
1502  have_values = false;
1503  for (pattern_id = 0; pattern_id < pia->len; pattern_id++)
1504  {
1505  PatternInfo *info = &pia->data[pattern_id];
1506 
1507  if (info->db_regex != NULL &&
1508  (inclusive || (info->nsp_regex == NULL && info->rel_regex == NULL)))
1509  {
1510  if (!have_values)
1511  appendPQExpBufferStr(buf, "\nVALUES");
1512  have_values = true;
1513  appendPQExpBuffer(buf, "%s\n(%d, ", comma, pattern_id);
1514  appendStringLiteralConn(buf, info->db_regex, conn);
1515  appendPQExpBufferStr(buf, ")");
1516  comma = ",";
1517  }
1518  }
1519 
1520  if (!have_values)
1521  appendPQExpBufferStr(buf, "\nSELECT NULL, NULL, NULL WHERE false");
1522 
1523  return have_values;
1524 }
char * db_regex
Definition: pg_amcheck.c:35
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
Definition: string_utils.c:293
char * rel_regex
Definition: pg_amcheck.c:38
char * nsp_regex
Definition: pg_amcheck.c:37
PatternInfo * data
Definition: pg_amcheck.c:49

◆ append_heap_pattern()

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

Definition at line 1451 of file pg_amcheck.c.

References append_relation_pattern_helper().

Referenced by main().

1452 {
1453  append_relation_pattern_helper(pia, pattern, encoding, true, false);
1454 }
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
Definition: pg_amcheck.c:1392
int32 encoding
Definition: pg_database.h:41

◆ 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 1804 of file pg_amcheck.c.

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

Referenced by compile_relation_list_one_db().

1806 {
1807  appendPQExpBuffer(buf,
1808  "\n%s (pattern_id, nsp_regex, rel_regex, heap_only, btree_only) AS ("
1809  "\nSELECT pattern_id, nsp_regex, rel_regex, heap_only, btree_only "
1810  "FROM %s r"
1811  "\nWHERE (r.db_regex IS NULL "
1812  "OR ",
1813  filtered, raw);
1814  appendStringLiteralConn(buf, PQdb(conn), conn);
1815  appendPQExpBufferStr(buf, " ~ r.db_regex)");
1817  " AND (r.nsp_regex IS NOT NULL"
1818  " OR r.rel_regex IS NOT NULL)"
1819  "),");
1820 }
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:6590
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
Definition: string_utils.c:293

◆ append_rel_pattern_raw_cte()

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

Definition at line 1735 of file pg_amcheck.c.

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

Referenced by compile_relation_list_one_db().

1737 {
1738  int pattern_id;
1739  const char *comma;
1740  bool have_values;
1741 
1742  comma = "";
1743  have_values = false;
1744  for (pattern_id = 0; pattern_id < pia->len; pattern_id++)
1745  {
1746  PatternInfo *info = &pia->data[pattern_id];
1747 
1748  if (!have_values)
1749  appendPQExpBufferStr(buf, "\nVALUES");
1750  have_values = true;
1751  appendPQExpBuffer(buf, "%s\n(%d::INTEGER, ", comma, pattern_id);
1752  if (info->db_regex == NULL)
1753  appendPQExpBufferStr(buf, "NULL");
1754  else
1755  appendStringLiteralConn(buf, info->db_regex, conn);
1756  appendPQExpBufferStr(buf, "::TEXT, ");
1757  if (info->nsp_regex == NULL)
1758  appendPQExpBufferStr(buf, "NULL");
1759  else
1760  appendStringLiteralConn(buf, info->nsp_regex, conn);
1761  appendPQExpBufferStr(buf, "::TEXT, ");
1762  if (info->rel_regex == NULL)
1763  appendPQExpBufferStr(buf, "NULL");
1764  else
1765  appendStringLiteralConn(buf, info->rel_regex, conn);
1766  if (info->heap_only)
1767  appendPQExpBufferStr(buf, "::TEXT, true::BOOLEAN");
1768  else
1769  appendPQExpBufferStr(buf, "::TEXT, false::BOOLEAN");
1770  if (info->btree_only)
1771  appendPQExpBufferStr(buf, ", true::BOOLEAN");
1772  else
1773  appendPQExpBufferStr(buf, ", false::BOOLEAN");
1774  appendPQExpBufferStr(buf, ")");
1775  comma = ",";
1776  }
1777 
1778  if (!have_values)
1780  "\nSELECT NULL::INTEGER, NULL::TEXT, NULL::TEXT, "
1781  "NULL::TEXT, NULL::BOOLEAN, NULL::BOOLEAN "
1782  "WHERE false");
1783 }
char * db_regex
Definition: pg_amcheck.c:35
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
bool heap_only
Definition: pg_amcheck.c:40
bool btree_only
Definition: pg_amcheck.c:42
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
Definition: string_utils.c:293
char * rel_regex
Definition: pg_amcheck.c:38
char * nsp_regex
Definition: pg_amcheck.c:37
PatternInfo * data
Definition: pg_amcheck.c:49

◆ append_relation_pattern()

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

Definition at line 1435 of file pg_amcheck.c.

References append_relation_pattern_helper().

Referenced by main().

1436 {
1437  append_relation_pattern_helper(pia, pattern, encoding, false, false);
1438 }
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
Definition: pg_amcheck.c:1392
int32 encoding
Definition: pg_database.h:41

◆ 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 1392 of file pg_amcheck.c.

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

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

1394 {
1395  PQExpBufferData dbbuf;
1396  PQExpBufferData nspbuf;
1397  PQExpBufferData relbuf;
1399 
1400  initPQExpBuffer(&dbbuf);
1401  initPQExpBuffer(&nspbuf);
1402  initPQExpBuffer(&relbuf);
1403 
1404  patternToSQLRegex(encoding, &dbbuf, &nspbuf, &relbuf, pattern, false);
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  if (relbuf.data[0])
1414  info->rel_regex = pstrdup(relbuf.data);
1415 
1416  termPQExpBuffer(&dbbuf);
1417  termPQExpBuffer(&nspbuf);
1418  termPQExpBuffer(&relbuf);
1419 
1420  info->heap_only = heap_only;
1421  info->btree_only = btree_only;
1422 }
char * db_regex
Definition: pg_amcheck.c:35
const char * pattern
Definition: pg_amcheck.c:34
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
char * pstrdup(const char *in)
Definition: mcxt.c:1299
void patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf, PQExpBuffer namebuf, const char *pattern, bool force_escape)
Definition: string_utils.c:967
static AmcheckOptions opts
Definition: pg_amcheck.c:110
bool heap_only
Definition: pg_amcheck.c:40
bool btree_only
Definition: pg_amcheck.c:42
int32 encoding
Definition: pg_database.h:41
char * rel_regex
Definition: pg_amcheck.c:38
char * nsp_regex
Definition: pg_amcheck.c:37
static PatternInfo * extend_pattern_info_array(PatternInfoArray *pia)
Definition: pg_amcheck.c:1312
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92

◆ append_schema_pattern()

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

Definition at line 1357 of file pg_amcheck.c.

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

Referenced by main().

1358 {
1359  PQExpBufferData dbbuf;
1360  PQExpBufferData nspbuf;
1362 
1363  initPQExpBuffer(&dbbuf);
1364  initPQExpBuffer(&nspbuf);
1365 
1366  patternToSQLRegex(encoding, NULL, &dbbuf, &nspbuf, pattern, false);
1367  info->pattern = pattern;
1368  if (dbbuf.data[0])
1369  {
1370  opts.dbpattern = true;
1371  info->db_regex = pstrdup(dbbuf.data);
1372  }
1373  if (nspbuf.data[0])
1374  info->nsp_regex = pstrdup(nspbuf.data);
1375 
1376  termPQExpBuffer(&dbbuf);
1377  termPQExpBuffer(&nspbuf);
1378 }
char * db_regex
Definition: pg_amcheck.c:35
const char * pattern
Definition: pg_amcheck.c:34
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
char * pstrdup(const char *in)
Definition: mcxt.c:1299
void patternToSQLRegex(int encoding, PQExpBuffer dbnamebuf, PQExpBuffer schemabuf, PQExpBuffer namebuf, const char *pattern, bool force_escape)
Definition: string_utils.c:967
static AmcheckOptions opts
Definition: pg_amcheck.c:110
int32 encoding
Definition: pg_database.h:41
char * nsp_regex
Definition: pg_amcheck.c:37
static PatternInfo * extend_pattern_info_array(PatternInfoArray *pia)
Definition: pg_amcheck.c:1312
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92

◆ compile_database_list()

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

Definition at line 1540 of file pg_amcheck.c.

References AmcheckOptions::alldb, append_db_pattern_cte(), appendPQExpBufferStr(), Assert, PQExpBufferData::data, PatternInfoArray::data, datname, DatabaseInfo::datname, disconnectDatabase(), AmcheckOptions::echo, AmcheckOptions::exclude, executeQuery(), fatal, i, AmcheckOptions::include, initPQExpBuffer(), PatternInfoArray::len, log_no_match, PatternInfo::pattern, pg_log_error, 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().

1542 {
1543  PGresult *res;
1544  PQExpBufferData sql;
1545  int ntups;
1546  int i;
1547  bool fatal;
1548 
1549  if (initial_dbname)
1550  {
1551  DatabaseInfo *dat = (DatabaseInfo *) pg_malloc0(sizeof(DatabaseInfo));
1552 
1553  /* This database is included. Add to list */
1554  if (opts.verbose)
1555  pg_log_info("including database \"%s\"", initial_dbname);
1556 
1557  dat->datname = pstrdup(initial_dbname);
1558  simple_ptr_list_append(databases, dat);
1559  }
1560 
1561  initPQExpBuffer(&sql);
1562 
1563  /* Append the include patterns CTE. */
1564  appendPQExpBufferStr(&sql, "WITH include_raw (pattern_id, rgx) AS (");
1565  if (!append_db_pattern_cte(&sql, &opts.include, conn, true) &&
1566  !opts.alldb)
1567  {
1568  /*
1569  * None of the inclusion patterns (if any) contain database portions,
1570  * so there is no need to query the database to resolve database
1571  * patterns.
1572  *
1573  * Since we're also not operating under --all, we don't need to query
1574  * the exhaustive list of connectable databases, either.
1575  */
1576  termPQExpBuffer(&sql);
1577  return;
1578  }
1579 
1580  /* Append the exclude patterns CTE. */
1581  appendPQExpBufferStr(&sql, "),\nexclude_raw (pattern_id, rgx) AS (");
1582  append_db_pattern_cte(&sql, &opts.exclude, conn, false);
1583  appendPQExpBufferStr(&sql, "),");
1584 
1585  /*
1586  * Append the database CTE, which includes whether each database is
1587  * connectable and also joins against exclude_raw to determine whether
1588  * each database is excluded.
1589  */
1590  appendPQExpBufferStr(&sql,
1591  "\ndatabase (datname) AS ("
1592  "\nSELECT d.datname "
1593  "FROM pg_catalog.pg_database d "
1594  "LEFT OUTER JOIN exclude_raw e "
1595  "ON d.datname ~ e.rgx "
1596  "\nWHERE d.datallowconn "
1597  "AND e.pattern_id IS NULL"
1598  "),"
1599 
1600  /*
1601  * Append the include_pat CTE, which joins the include_raw CTE against the
1602  * databases CTE to determine if all the inclusion patterns had matches,
1603  * and whether each matched pattern had the misfortune of only matching
1604  * excluded or unconnectable databases.
1605  */
1606  "\ninclude_pat (pattern_id, checkable) AS ("
1607  "\nSELECT i.pattern_id, "
1608  "COUNT(*) FILTER ("
1609  "WHERE d IS NOT NULL"
1610  ") AS checkable"
1611  "\nFROM include_raw i "
1612  "LEFT OUTER JOIN database d "
1613  "ON d.datname ~ i.rgx"
1614  "\nGROUP BY i.pattern_id"
1615  "),"
1616 
1617  /*
1618  * Append the filtered_databases CTE, which selects from the database CTE
1619  * optionally joined against the include_raw CTE to only select databases
1620  * that match an inclusion pattern. This appears to duplicate what the
1621  * include_pat CTE already did above, but here we want only databases, and
1622  * there we wanted patterns.
1623  */
1624  "\nfiltered_databases (datname) AS ("
1625  "\nSELECT DISTINCT d.datname "
1626  "FROM database d");
1627  if (!opts.alldb)
1628  appendPQExpBufferStr(&sql,
1629  " INNER JOIN include_raw i "
1630  "ON d.datname ~ i.rgx");
1631  appendPQExpBufferStr(&sql,
1632  ")"
1633 
1634  /*
1635  * Select the checkable databases and the unmatched inclusion patterns.
1636  */
1637  "\nSELECT pattern_id, datname FROM ("
1638  "\nSELECT pattern_id, NULL::TEXT AS datname "
1639  "FROM include_pat "
1640  "WHERE checkable = 0 "
1641  "UNION ALL"
1642  "\nSELECT NULL, datname "
1643  "FROM filtered_databases"
1644  ") AS combined_records"
1645  "\nORDER BY pattern_id NULLS LAST, datname");
1646 
1647  res = executeQuery(conn, sql.data, opts.echo);
1648  if (PQresultStatus(res) != PGRES_TUPLES_OK)
1649  {
1650  pg_log_error("query failed: %s", PQerrorMessage(conn));
1651  pg_log_info("query was: %s", sql.data);
1652  disconnectDatabase(conn);
1653  exit(1);
1654  }
1655  termPQExpBuffer(&sql);
1656 
1657  ntups = PQntuples(res);
1658  for (fatal = false, i = 0; i < ntups; i++)
1659  {
1660  int pattern_id = -1;
1661  const char *datname = NULL;
1662 
1663  if (!PQgetisnull(res, i, 0))
1664  pattern_id = atoi(PQgetvalue(res, i, 0));
1665  if (!PQgetisnull(res, i, 1))
1666  datname = PQgetvalue(res, i, 1);
1667 
1668  if (pattern_id >= 0)
1669  {
1670  /*
1671  * Current record pertains to an inclusion pattern that matched no
1672  * checkable databases.
1673  */
1674  fatal = opts.strict_names;
1675  if (pattern_id >= opts.include.len)
1676  {
1677  pg_log_error("internal error: received unexpected database pattern_id %d",
1678  pattern_id);
1679  exit(1);
1680  }
1681  log_no_match("no connectable databases to check matching \"%s\"",
1682  opts.include.data[pattern_id].pattern);
1683  }
1684  else
1685  {
1686  DatabaseInfo *dat;
1687 
1688  /* Current record pertains to a database */
1689  Assert(datname != NULL);
1690 
1691  /* Avoid entering a duplicate entry matching the initial_dbname */
1692  if (initial_dbname != NULL && strcmp(initial_dbname, datname) == 0)
1693  continue;
1694 
1695  /* This database is included. Add to list */
1696  if (opts.verbose)
1697  pg_log_info("including database \"%s\"", datname);
1698 
1699  dat = (DatabaseInfo *) pg_malloc0(sizeof(DatabaseInfo));
1700  dat->datname = pstrdup(datname);
1701  simple_ptr_list_append(databases, dat);
1702  }
1703  }
1704  PQclear(res);
1705 
1706  if (fatal)
1707  {
1708  if (conn != NULL)
1709  disconnectDatabase(conn);
1710  exit(1);
1711  }
1712 }
static PGresult * executeQuery(PGconn *conn, const char *query)
Definition: pg_dumpall.c:1879
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6744
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3642
const char * pattern
Definition: pg_amcheck.c:34
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
#define pg_log_error(...)
Definition: logging.h:80
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
char * pstrdup(const char *in)
Definition: mcxt.c:1299
NameData datname
Definition: pg_database.h:35
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3248
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
static bool append_db_pattern_cte(PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn, bool inclusive)
Definition: pg_amcheck.c:1494
void disconnectDatabase(PGconn *conn)
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
PatternInfoArray include
Definition: pg_amcheck.c:72
static AmcheckOptions opts
Definition: pg_amcheck.c:110
char * datname
Definition: pg_amcheck.c:149
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define Assert(condition)
Definition: c.h:804
#define fatal(...)
PatternInfoArray exclude
Definition: pg_amcheck.c:73
void simple_ptr_list_append(SimplePtrList *list, void *ptr)
Definition: simple_list.c:162
int i
bool strict_names
Definition: pg_amcheck.c:60
PatternInfo * data
Definition: pg_amcheck.c:49
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3667
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
#define pg_log_info(...)
Definition: logging.h:88
#define log_no_match(...)
Definition: pg_amcheck.c:203

◆ compile_relation_list_one_db()

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

Definition at line 1843 of file pg_amcheck.c.

References AmcheckOptions::allrel, append_rel_pattern_filtered_cte(), append_rel_pattern_raw_cte(), appendPQExpBuffer(), appendPQExpBufferStr(), Assert, atooid, RelationInfo::blocks_to_check, PQExpBufferData::data, PatternInfoArray::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, pg_log_error, pg_log_info, pg_malloc0(), PG_USED_FOR_ASSERTS_ONLY, PGRES_TUPLES_OK, PQclear(), PQerrorMessage(), PQgetisnull(), PQgetvalue(), PQntuples(), PQresultStatus(), pstrdup(), relname, RelationInfo::relname, RelationInfo::reloid, RelationInfo::relpages, simple_ptr_list_append(), AmcheckOptions::startblock, and termPQExpBuffer().

Referenced by main().

1846 {
1847  PGresult *res;
1848  PQExpBufferData sql;
1849  int ntups;
1850  int i;
1851 
1852  initPQExpBuffer(&sql);
1853  appendPQExpBufferStr(&sql, "WITH");
1854 
1855  /* Append CTEs for the relation inclusion patterns, if any */
1856  if (!opts.allrel)
1857  {
1858  appendPQExpBufferStr(&sql,
1859  " include_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS (");
1860  append_rel_pattern_raw_cte(&sql, &opts.include, conn);
1861  appendPQExpBufferStr(&sql, "\n),");
1862  append_rel_pattern_filtered_cte(&sql, "include_raw", "include_pat", conn);
1863  }
1864 
1865  /* Append CTEs for the relation exclusion patterns, if any */
1867  {
1868  appendPQExpBufferStr(&sql,
1869  " exclude_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS (");
1870  append_rel_pattern_raw_cte(&sql, &opts.exclude, conn);
1871  appendPQExpBufferStr(&sql, "\n),");
1872  append_rel_pattern_filtered_cte(&sql, "exclude_raw", "exclude_pat", conn);
1873  }
1874 
1875  /* Append the relation CTE. */
1876  appendPQExpBufferStr(&sql,
1877  " relation (pattern_id, oid, nspname, relname, reltoastrelid, relpages, is_heap, is_btree) AS ("
1878  "\nSELECT DISTINCT ON (c.oid");
1879  if (!opts.allrel)
1880  appendPQExpBufferStr(&sql, ", ip.pattern_id) ip.pattern_id,");
1881  else
1882  appendPQExpBufferStr(&sql, ") NULL::INTEGER AS pattern_id,");
1883  appendPQExpBuffer(&sql,
1884  "\nc.oid, n.nspname, c.relname, c.reltoastrelid, c.relpages, "
1885  "c.relam = %u AS is_heap, "
1886  "c.relam = %u AS is_btree"
1887  "\nFROM pg_catalog.pg_class c "
1888  "INNER JOIN pg_catalog.pg_namespace n "
1889  "ON c.relnamespace = n.oid",
1890  HEAP_TABLE_AM_OID, BTREE_AM_OID);
1891  if (!opts.allrel)
1892  appendPQExpBuffer(&sql,
1893  "\nINNER JOIN include_pat ip"
1894  "\nON (n.nspname ~ ip.nsp_regex OR ip.nsp_regex IS NULL)"
1895  "\nAND (c.relname ~ ip.rel_regex OR ip.rel_regex IS NULL)"
1896  "\nAND (c.relam = %u OR NOT ip.heap_only)"
1897  "\nAND (c.relam = %u OR NOT ip.btree_only)",
1898  HEAP_TABLE_AM_OID, BTREE_AM_OID);
1900  appendPQExpBuffer(&sql,
1901  "\nLEFT OUTER JOIN exclude_pat ep"
1902  "\nON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL)"
1903  "\nAND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL)"
1904  "\nAND (c.relam = %u OR NOT ep.heap_only OR ep.rel_regex IS NULL)"
1905  "\nAND (c.relam = %u OR NOT ep.btree_only OR ep.rel_regex IS NULL)",
1906  HEAP_TABLE_AM_OID, BTREE_AM_OID);
1907 
1908  /*
1909  * Exclude temporary tables and indexes, which must necessarily belong to
1910  * other sessions. (We don't create any ourselves.) We must ultimately
1911  * exclude indexes marked invalid or not ready, but we delay that decision
1912  * until firing off the amcheck command, as the state of an index may
1913  * change by then.
1914  */
1915  appendPQExpBufferStr(&sql, "\nWHERE c.relpersistence != 't'");
1917  appendPQExpBufferStr(&sql, "\nAND ep.pattern_id IS NULL");
1918 
1919  /*
1920  * We need to be careful not to break the --no-dependent-toast and
1921  * --no-dependent-indexes options. By default, the btree indexes, toast
1922  * tables, and toast table btree indexes associated with primary heap
1923  * tables are included, using their own CTEs below. We implement the
1924  * --exclude-* options by not creating those CTEs, but that's no use if
1925  * we've already selected the toast and indexes here. On the other hand,
1926  * we want inclusion patterns that match indexes or toast tables to be
1927  * honored. So, if inclusion patterns were given, we want to select all
1928  * tables, toast tables, or indexes that match the patterns. But if no
1929  * inclusion patterns were given, and we're simply matching all relations,
1930  * then we only want to match the primary tables here.
1931  */
1932  if (opts.allrel)
1933  appendPQExpBuffer(&sql,
1934  " AND c.relam = %u "
1935  "AND c.relkind IN ('r', 'S', 'm', 't') "
1936  "AND c.relnamespace != %u",
1937  HEAP_TABLE_AM_OID, PG_TOAST_NAMESPACE);
1938  else
1939  appendPQExpBuffer(&sql,
1940  " AND c.relam IN (%u, %u)"
1941  "AND c.relkind IN ('r', 'S', 'm', 't', 'i') "
1942  "AND ((c.relam = %u AND c.relkind IN ('r', 'S', 'm', 't')) OR "
1943  "(c.relam = %u AND c.relkind = 'i'))",
1944  HEAP_TABLE_AM_OID, BTREE_AM_OID,
1945  HEAP_TABLE_AM_OID, BTREE_AM_OID);
1946 
1947  appendPQExpBufferStr(&sql,
1948  "\nORDER BY c.oid)");
1949 
1950  if (!opts.no_toast_expansion)
1951  {
1952  /*
1953  * Include a CTE for toast tables associated with primary heap tables
1954  * selected above, filtering by exclusion patterns (if any) that match
1955  * toast table names.
1956  */
1957  appendPQExpBufferStr(&sql,
1958  ", toast (oid, nspname, relname, relpages) AS ("
1959  "\nSELECT t.oid, 'pg_toast', t.relname, t.relpages"
1960  "\nFROM pg_catalog.pg_class t "
1961  "INNER JOIN relation r "
1962  "ON r.reltoastrelid = t.oid");
1963  if (opts.excludetbl || opts.excludensp)
1964  appendPQExpBufferStr(&sql,
1965  "\nLEFT OUTER JOIN exclude_pat ep"
1966  "\nON ('pg_toast' ~ ep.nsp_regex OR ep.nsp_regex IS NULL)"
1967  "\nAND (t.relname ~ ep.rel_regex OR ep.rel_regex IS NULL)"
1968  "\nAND ep.heap_only"
1969  "\nWHERE ep.pattern_id IS NULL"
1970  "\nAND t.relpersistence != 't'");
1971  appendPQExpBufferStr(&sql,
1972  "\n)");
1973  }
1974  if (!opts.no_btree_expansion)
1975  {
1976  /*
1977  * Include a CTE for btree indexes associated with primary heap tables
1978  * selected above, filtering by exclusion patterns (if any) that match
1979  * btree index names.
1980  */
1981  appendPQExpBuffer(&sql,
1982  ", index (oid, nspname, relname, relpages) AS ("
1983  "\nSELECT c.oid, r.nspname, c.relname, c.relpages "
1984  "FROM relation r"
1985  "\nINNER JOIN pg_catalog.pg_index i "
1986  "ON r.oid = i.indrelid "
1987  "INNER JOIN pg_catalog.pg_class c "
1988  "ON i.indexrelid = c.oid "
1989  "AND c.relpersistence != 't'");
1990  if (opts.excludeidx || opts.excludensp)
1991  appendPQExpBufferStr(&sql,
1992  "\nINNER JOIN pg_catalog.pg_namespace n "
1993  "ON c.relnamespace = n.oid"
1994  "\nLEFT OUTER JOIN exclude_pat ep "
1995  "ON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL) "
1996  "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) "
1997  "AND ep.btree_only"
1998  "\nWHERE ep.pattern_id IS NULL");
1999  else
2000  appendPQExpBufferStr(&sql,
2001  "\nWHERE true");
2002  appendPQExpBuffer(&sql,
2003  " AND c.relam = %u "
2004  "AND c.relkind = 'i'",
2005  BTREE_AM_OID);
2007  appendPQExpBuffer(&sql,
2008  " AND c.relnamespace != %u",
2009  PG_TOAST_NAMESPACE);
2010  appendPQExpBufferStr(&sql, "\n)");
2011  }
2012 
2014  {
2015  /*
2016  * Include a CTE for btree indexes associated with toast tables of
2017  * primary heap tables selected above, filtering by exclusion patterns
2018  * (if any) that match the toast index names.
2019  */
2020  appendPQExpBuffer(&sql,
2021  ", toast_index (oid, nspname, relname, relpages) AS ("
2022  "\nSELECT c.oid, 'pg_toast', c.relname, c.relpages "
2023  "FROM toast t "
2024  "INNER JOIN pg_catalog.pg_index i "
2025  "ON t.oid = i.indrelid"
2026  "\nINNER JOIN pg_catalog.pg_class c "
2027  "ON i.indexrelid = c.oid "
2028  "AND c.relpersistence != 't'");
2029  if (opts.excludeidx)
2030  appendPQExpBufferStr(&sql,
2031  "\nLEFT OUTER JOIN exclude_pat ep "
2032  "ON ('pg_toast' ~ ep.nsp_regex OR ep.nsp_regex IS NULL) "
2033  "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) "
2034  "AND ep.btree_only "
2035  "WHERE ep.pattern_id IS NULL");
2036  else
2037  appendPQExpBufferStr(&sql,
2038  "\nWHERE true");
2039  appendPQExpBuffer(&sql,
2040  " AND c.relam = %u"
2041  " AND c.relkind = 'i')",
2042  BTREE_AM_OID);
2043  }
2044 
2045  /*
2046  * Roll-up distinct rows from CTEs.
2047  *
2048  * Relations that match more than one pattern may occur more than once in
2049  * the list, and indexes and toast for primary relations may also have
2050  * matched in their own right, so we rely on UNION to deduplicate the
2051  * list.
2052  */
2053  appendPQExpBuffer(&sql,
2054  "\nSELECT pattern_id, is_heap, is_btree, oid, nspname, relname, relpages "
2055  "FROM (");
2056  appendPQExpBufferStr(&sql,
2057  /* Inclusion patterns that failed to match */
2058  "\nSELECT pattern_id, is_heap, is_btree, "
2059  "NULL::OID AS oid, "
2060  "NULL::TEXT AS nspname, "
2061  "NULL::TEXT AS relname, "
2062  "NULL::INTEGER AS relpages"
2063  "\nFROM relation "
2064  "WHERE pattern_id IS NOT NULL "
2065  "UNION"
2066  /* Primary relations */
2067  "\nSELECT NULL::INTEGER AS pattern_id, "
2068  "is_heap, is_btree, oid, nspname, relname, relpages "
2069  "FROM relation");
2070  if (!opts.no_toast_expansion)
2071  appendPQExpBufferStr(&sql,
2072  " UNION"
2073  /* Toast tables for primary relations */
2074  "\nSELECT NULL::INTEGER AS pattern_id, TRUE AS is_heap, "
2075  "FALSE AS is_btree, oid, nspname, relname, relpages "
2076  "FROM toast");
2077  if (!opts.no_btree_expansion)
2078  appendPQExpBufferStr(&sql,
2079  " UNION"
2080  /* Indexes for primary relations */
2081  "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, "
2082  "TRUE AS is_btree, oid, nspname, relname, relpages "
2083  "FROM index");
2085  appendPQExpBufferStr(&sql,
2086  " UNION"
2087  /* Indexes for toast relations */
2088  "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, "
2089  "TRUE AS is_btree, oid, nspname, relname, relpages "
2090  "FROM toast_index");
2091  appendPQExpBufferStr(&sql,
2092  "\n) AS combined_records "
2093  "ORDER BY relpages DESC NULLS FIRST, oid");
2094 
2095  res = executeQuery(conn, sql.data, opts.echo);
2096  if (PQresultStatus(res) != PGRES_TUPLES_OK)
2097  {
2098  pg_log_error("query failed: %s", PQerrorMessage(conn));
2099  pg_log_info("query was: %s", sql.data);
2100  disconnectDatabase(conn);
2101  exit(1);
2102  }
2103  termPQExpBuffer(&sql);
2104 
2105  ntups = PQntuples(res);
2106  for (i = 0; i < ntups; i++)
2107  {
2108  int pattern_id = -1;
2109  bool is_heap = false;
2110  bool is_btree PG_USED_FOR_ASSERTS_ONLY = false;
2111  Oid oid = InvalidOid;
2112  const char *nspname = NULL;
2113  const char *relname = NULL;
2114  int relpages = 0;
2115 
2116  if (!PQgetisnull(res, i, 0))
2117  pattern_id = atoi(PQgetvalue(res, i, 0));
2118  if (!PQgetisnull(res, i, 1))
2119  is_heap = (PQgetvalue(res, i, 1)[0] == 't');
2120  if (!PQgetisnull(res, i, 2))
2121  is_btree = (PQgetvalue(res, i, 2)[0] == 't');
2122  if (!PQgetisnull(res, i, 3))
2123  oid = atooid(PQgetvalue(res, i, 3));
2124  if (!PQgetisnull(res, i, 4))
2125  nspname = PQgetvalue(res, i, 4);
2126  if (!PQgetisnull(res, i, 5))
2127  relname = PQgetvalue(res, i, 5);
2128  if (!PQgetisnull(res, i, 6))
2129  relpages = atoi(PQgetvalue(res, i, 6));
2130 
2131  if (pattern_id >= 0)
2132  {
2133  /*
2134  * Current record pertains to an inclusion pattern. Record that
2135  * it matched.
2136  */
2137 
2138  if (pattern_id >= opts.include.len)
2139  {
2140  pg_log_error("internal error: received unexpected relation pattern_id %d",
2141  pattern_id);
2142  exit(1);
2143  }
2144 
2145  opts.include.data[pattern_id].matched = true;
2146  }
2147  else
2148  {
2149  /* Current record pertains to a relation */
2150 
2151  RelationInfo *rel = (RelationInfo *) pg_malloc0(sizeof(RelationInfo));
2152 
2153  Assert(OidIsValid(oid));
2154  Assert((is_heap && !is_btree) || (is_btree && !is_heap));
2155 
2156  rel->datinfo = dat;
2157  rel->reloid = oid;
2158  rel->is_heap = is_heap;
2159  rel->nspname = pstrdup(nspname);
2160  rel->relname = pstrdup(relname);
2161  rel->relpages = relpages;
2162  rel->blocks_to_check = relpages;
2163  if (is_heap && (opts.startblock >= 0 || opts.endblock >= 0))
2164  {
2165  /*
2166  * We apply --startblock and --endblock to heap tables, but
2167  * not btree indexes, and for progress purposes we need to
2168  * track how many blocks we expect to check.
2169  */
2170  if (opts.endblock >= 0 && rel->blocks_to_check > opts.endblock)
2171  rel->blocks_to_check = opts.endblock + 1;
2172  if (opts.startblock >= 0)
2173  {
2174  if (rel->blocks_to_check > opts.startblock)
2176  else
2177  rel->blocks_to_check = 0;
2178  }
2179  }
2180  *pagecount += rel->blocks_to_check;
2181 
2182  simple_ptr_list_append(relations, rel);
2183  }
2184  }
2185  PQclear(res);
2186 }
int64 endblock
Definition: pg_amcheck.c:98
static PGresult * executeQuery(PGconn *conn, const char *query)
Definition: pg_dumpall.c:1879
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6744
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3642
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
#define pg_log_error(...)
Definition: logging.h:80
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
char * pstrdup(const char *in)
Definition: mcxt.c:1299
char * relname
Definition: pg_amcheck.c:159
char * nspname
Definition: pg_amcheck.c:158
bool matched
Definition: pg_amcheck.c:44
bool no_toast_expansion
Definition: pg_amcheck.c:94
NameData relname
Definition: pg_class.h:38
unsigned int Oid
Definition: postgres_ext.h:31
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3248
#define OidIsValid(objectId)
Definition: c.h:710
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
void disconnectDatabase(PGconn *conn)
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:155
bool no_btree_expansion
Definition: pg_amcheck.c:107
int64 startblock
Definition: pg_amcheck.c:97
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
PatternInfoArray include
Definition: pg_amcheck.c:72
#define atooid(x)
Definition: postgres_ext.h:42
static AmcheckOptions opts
Definition: pg_amcheck.c:110
int blocks_to_check
Definition: pg_amcheck.c:161
#define InvalidOid
Definition: postgres_ext.h:36
void PQclear(PGresult *res)
Definition: fe-exec.c:694
#define Assert(condition)
Definition: c.h:804
static void append_rel_pattern_filtered_cte(PQExpBuffer buf, const char *raw, const char *filtered, PGconn *conn)
Definition: pg_amcheck.c:1804
PatternInfoArray exclude
Definition: pg_amcheck.c:73
void simple_ptr_list_append(SimplePtrList *list, void *ptr)
Definition: simple_list.c:162
int i
static void append_rel_pattern_raw_cte(PQExpBuffer buf, const PatternInfoArray *pia, PGconn *conn)
Definition: pg_amcheck.c:1735
PatternInfo * data
Definition: pg_amcheck.c:49
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3667
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
#define pg_log_info(...)
Definition: logging.h:88
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:155

◆ extend_pattern_info_array()

static PatternInfo* extend_pattern_info_array ( PatternInfoArray pia)
static

Definition at line 1312 of file pg_amcheck.c.

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

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

1313 {
1314  PatternInfo *result;
1315 
1316  pia->len++;
1317  pia->data = (PatternInfo *) pg_realloc(pia->data, pia->len * sizeof(PatternInfo));
1318  result = &pia->data[pia->len - 1];
1319  memset(result, 0, sizeof(*result));
1320 
1321  return result;
1322 }
void * pg_realloc(void *ptr, size_t size)
Definition: fe_memutils.c:65
PatternInfo * data
Definition: pg_amcheck.c:49

◆ help()

static void help ( const char *  progname)
static

Definition at line 1160 of file pg_amcheck.c.

References _, and printf.

Referenced by main().

1161 {
1162  printf(_("%s checks objects in a PostgreSQL database for corruption.\n\n"), progname);
1163  printf(_("Usage:\n"));
1164  printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1165  printf(_("\nTarget options:\n"));
1166  printf(_(" -a, --all check all databases\n"));
1167  printf(_(" -d, --database=PATTERN check matching database(s)\n"));
1168  printf(_(" -D, --exclude-database=PATTERN do NOT check matching database(s)\n"));
1169  printf(_(" -i, --index=PATTERN check matching index(es)\n"));
1170  printf(_(" -I, --exclude-index=PATTERN do NOT check matching index(es)\n"));
1171  printf(_(" -r, --relation=PATTERN check matching relation(s)\n"));
1172  printf(_(" -R, --exclude-relation=PATTERN do NOT check matching relation(s)\n"));
1173  printf(_(" -s, --schema=PATTERN check matching schema(s)\n"));
1174  printf(_(" -S, --exclude-schema=PATTERN do NOT check matching schema(s)\n"));
1175  printf(_(" -t, --table=PATTERN check matching table(s)\n"));
1176  printf(_(" -T, --exclude-table=PATTERN do NOT check matching table(s)\n"));
1177  printf(_(" --no-dependent-indexes do NOT expand list of relations to include indexes\n"));
1178  printf(_(" --no-dependent-toast do NOT expand list of relations to include TOAST tables\n"));
1179  printf(_(" --no-strict-names do NOT require patterns to match objects\n"));
1180  printf(_("\nTable checking options:\n"));
1181  printf(_(" --exclude-toast-pointers do NOT follow relation TOAST pointers\n"));
1182  printf(_(" --on-error-stop stop checking at end of first corrupt page\n"));
1183  printf(_(" --skip=OPTION do NOT check \"all-frozen\" or \"all-visible\" blocks\n"));
1184  printf(_(" --startblock=BLOCK begin checking table(s) at the given block number\n"));
1185  printf(_(" --endblock=BLOCK check table(s) only up to the given block number\n"));
1186  printf(_("\nB-tree index checking options:\n"));
1187  printf(_(" --heapallindexed check that all heap tuples are found within indexes\n"));
1188  printf(_(" --parent-check check index parent/child relationships\n"));
1189  printf(_(" --rootdescend search from root page to refind tuples\n"));
1190  printf(_("\nConnection options:\n"));
1191  printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1192  printf(_(" -p, --port=PORT database server port\n"));
1193  printf(_(" -U, --username=USERNAME user name to connect as\n"));
1194  printf(_(" -w, --no-password never prompt for password\n"));
1195  printf(_(" -W, --password force password prompt\n"));
1196  printf(_(" --maintenance-db=DBNAME alternate maintenance database\n"));
1197  printf(_("\nOther options:\n"));
1198  printf(_(" -e, --echo show the commands being sent to the server\n"));
1199  printf(_(" -j, --jobs=NUM use this many concurrent connections to the server\n"));
1200  printf(_(" -P, --progress show progress information\n"));
1201  printf(_(" -v, --verbose write a lot of output\n"));
1202  printf(_(" -V, --version output version information, then exit\n"));
1203  printf(_(" --install-missing install missing extensions\n"));
1204  printf(_(" -?, --help show this help, then exit\n"));
1205 
1206  printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1207  printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1208 }
#define printf(...)
Definition: port.h:223
static const char * progname
Definition: pg_amcheck.c:138
#define _(x)
Definition: elog.c:89

◆ indent_lines()

static char* indent_lines ( const char *  str)
static

Definition at line 985 of file pg_amcheck.c.

References appendPQExpBufferChar(), appendPQExpBufferStr(), buf, PQExpBufferData::data, initPQExpBuffer(), pstrdup(), and termPQExpBuffer().

Referenced by verify_btree_slot_handler(), and verify_heap_slot_handler().

986 {
988  const char *c;
989  char *result;
990 
991  initPQExpBuffer(&buf);
992  appendPQExpBufferStr(&buf, " ");
993  for (c = str; *c; c++)
994  {
995  appendPQExpBufferChar(&buf, *c);
996  if (c[0] == '\n' && c[1] != '\0')
997  appendPQExpBufferStr(&buf, " ");
998  }
999  result = pstrdup(buf.data);
1000  termPQExpBuffer(&buf);
1001 
1002  return result;
1003 }
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
char * pstrdup(const char *in)
Definition: mcxt.c:1299
char * c
static char * buf
Definition: pg_test_fsync.c:68
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:380
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92

◆ main()

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

Definition at line 216 of file pg_amcheck.c.

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, compile_database_list(), compile_relation_list_one_db(), conn, connectDatabase(), ParallelSlot::connection, connectMaintenanceDatabase(), PQExpBufferData::data, PatternInfoArray::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(), AmcheckOptions::include, initPQExpBuffer(), AmcheckOptions::install_missing, AmcheckOptions::install_schema, 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, _connParams::override_dbname, ParallelSlotsAdoptConn(), ParallelSlotSetHandler(), ParallelSlotsGetIdle(), ParallelSlotsSetup(), ParallelSlotsTerminate(), ParallelSlotsWaitCompletion(), AmcheckOptions::parent_check, PatternInfo::pattern, pfree(), pg_get_encoding_from_locale(), pg_log_error, 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(), 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().

217 {
218  PGconn *conn = NULL;
219  SimplePtrListCell *cell;
220  SimplePtrList databases = {NULL, NULL};
221  SimplePtrList relations = {NULL, NULL};
222  bool failed = false;
223  const char *latest_datname;
224  int parallel_workers;
226  PQExpBufferData sql;
227  uint64 reltotal = 0;
228  uint64 pageschecked = 0;
229  uint64 pagestotal = 0;
230  uint64 relprogress = 0;
231  int pattern_id;
232 
233  static struct option long_options[] = {
234  /* Connection options */
235  {"host", required_argument, NULL, 'h'},
236  {"port", required_argument, NULL, 'p'},
237  {"username", required_argument, NULL, 'U'},
238  {"no-password", no_argument, NULL, 'w'},
239  {"password", no_argument, NULL, 'W'},
240  {"maintenance-db", required_argument, NULL, 1},
241 
242  /* check options */
243  {"all", no_argument, NULL, 'a'},
244  {"database", required_argument, NULL, 'd'},
245  {"exclude-database", required_argument, NULL, 'D'},
246  {"echo", no_argument, NULL, 'e'},
247  {"index", required_argument, NULL, 'i'},
248  {"exclude-index", required_argument, NULL, 'I'},
249  {"jobs", required_argument, NULL, 'j'},
250  {"progress", no_argument, NULL, 'P'},
251  {"relation", required_argument, NULL, 'r'},
252  {"exclude-relation", required_argument, NULL, 'R'},
253  {"schema", required_argument, NULL, 's'},
254  {"exclude-schema", required_argument, NULL, 'S'},
255  {"table", required_argument, NULL, 't'},
256  {"exclude-table", required_argument, NULL, 'T'},
257  {"verbose", no_argument, NULL, 'v'},
258  {"no-dependent-indexes", no_argument, NULL, 2},
259  {"no-dependent-toast", no_argument, NULL, 3},
260  {"exclude-toast-pointers", no_argument, NULL, 4},
261  {"on-error-stop", no_argument, NULL, 5},
262  {"skip", required_argument, NULL, 6},
263  {"startblock", required_argument, NULL, 7},
264  {"endblock", required_argument, NULL, 8},
265  {"rootdescend", no_argument, NULL, 9},
266  {"no-strict-names", no_argument, NULL, 10},
267  {"heapallindexed", no_argument, NULL, 11},
268  {"parent-check", no_argument, NULL, 12},
269  {"install-missing", optional_argument, NULL, 13},
270 
271  {NULL, 0, NULL, 0}
272  };
273 
274  int optindex;
275  int c;
276 
277  const char *db = NULL;
278  const char *maintenance_db = NULL;
279 
280  const char *host = NULL;
281  const char *port = NULL;
282  const char *username = NULL;
283  enum trivalue prompt_password = TRI_DEFAULT;
284  int encoding = pg_get_encoding_from_locale(NULL, false);
285  ConnParams cparams;
286 
287  pg_logging_init(argv[0]);
288  progname = get_progname(argv[0]);
289  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_amcheck"));
290 
291  handle_help_version_opts(argc, argv, progname, help);
292 
293  /* process command-line options */
294  while ((c = getopt_long(argc, argv, "ad:D:eh:Hi:I:j:p:Pr:R:s:S:t:T:U:wWv",
295  long_options, &optindex)) != -1)
296  {
297  char *endptr;
298  unsigned long optval;
299 
300  switch (c)
301  {
302  case 'a':
303  opts.alldb = true;
304  break;
305  case 'd':
306  opts.dbpattern = true;
308  break;
309  case 'D':
310  opts.dbpattern = true;
312  break;
313  case 'e':
314  opts.echo = true;
315  break;
316  case 'h':
317  host = pg_strdup(optarg);
318  break;
319  case 'i':
320  opts.allrel = false;
322  break;
323  case 'I':
324  opts.excludeidx = true;
326  break;
327  case 'j':
328  if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX,
329  &opts.jobs))
330  exit(1);
331  break;
332  case 'p':
333  port = pg_strdup(optarg);
334  break;
335  case 'P':
336  opts.show_progress = true;
337  break;
338  case 'r':
339  opts.allrel = false;
341  break;
342  case 'R':
343  opts.excludeidx = true;
344  opts.excludetbl = true;
346  break;
347  case 's':
348  opts.allrel = false;
350  break;
351  case 'S':
352  opts.excludensp = true;
354  break;
355  case 't':
356  opts.allrel = false;
357  append_heap_pattern(&opts.include, optarg, encoding);
358  break;
359  case 'T':
360  opts.excludetbl = true;
361  append_heap_pattern(&opts.exclude, optarg, encoding);
362  break;
363  case 'U':
364  username = pg_strdup(optarg);
365  break;
366  case 'w':
367  prompt_password = TRI_NO;
368  break;
369  case 'W':
370  prompt_password = TRI_YES;
371  break;
372  case 'v':
373  opts.verbose = true;
375  break;
376  case 1:
377  maintenance_db = pg_strdup(optarg);
378  break;
379  case 2:
380  opts.no_btree_expansion = true;
381  break;
382  case 3:
383  opts.no_toast_expansion = true;
384  break;
385  case 4:
386  opts.reconcile_toast = false;
387  break;
388  case 5:
389  opts.on_error_stop = true;
390  break;
391  case 6:
392  if (pg_strcasecmp(optarg, "all-visible") == 0)
393  opts.skip = "all-visible";
394  else if (pg_strcasecmp(optarg, "all-frozen") == 0)
395  opts.skip = "all-frozen";
396  else if (pg_strcasecmp(optarg, "none") == 0)
397  opts.skip = "none";
398  else
399  {
400  pg_log_error("invalid argument for option %s", "--skip");
401  exit(1);
402  }
403  break;
404  case 7:
405  errno = 0;
406  optval = strtoul(optarg, &endptr, 10);
407  if (endptr == optarg || *endptr != '\0' || errno != 0)
408  {
409  pg_log_error("invalid start block");
410  exit(1);
411  }
412  if (optval > MaxBlockNumber)
413  {
414  pg_log_error("start block out of bounds");
415  exit(1);
416  }
417  opts.startblock = optval;
418  break;
419  case 8:
420  errno = 0;
421  optval = strtoul(optarg, &endptr, 10);
422  if (endptr == optarg || *endptr != '\0' || errno != 0)
423  {
424  pg_log_error("invalid end block");
425  exit(1);
426  }
427  if (optval > MaxBlockNumber)
428  {
429  pg_log_error("end block out of bounds");
430  exit(1);
431  }
432  opts.endblock = optval;
433  break;
434  case 9:
435  opts.rootdescend = true;
436  opts.parent_check = true;
437  break;
438  case 10:
439  opts.strict_names = false;
440  break;
441  case 11:
442  opts.heapallindexed = true;
443  break;
444  case 12:
445  opts.parent_check = true;
446  break;
447  case 13:
448  opts.install_missing = true;
449  if (optarg)
451  break;
452  default:
453  fprintf(stderr,
454  _("Try \"%s --help\" for more information.\n"),
455  progname);
456  exit(1);
457  }
458  }
459 
460  if (opts.endblock >= 0 && opts.endblock < opts.startblock)
461  {
462  pg_log_error("end block precedes start block");
463  exit(1);
464  }
465 
466  /*
467  * A single non-option arguments specifies a database name or connection
468  * string.
469  */
470  if (optind < argc)
471  {
472  db = argv[optind];
473  optind++;
474  }
475 
476  if (optind < argc)
477  {
478  pg_log_error("too many command-line arguments (first is \"%s\")",
479  argv[optind]);
480  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
481  exit(1);
482  }
483 
484  /* fill cparams except for dbname, which is set below */
485  cparams.pghost = host;
486  cparams.pgport = port;
487  cparams.pguser = username;
488  cparams.prompt_password = prompt_password;
489  cparams.dbname = NULL;
490  cparams.override_dbname = NULL;
491 
492  setup_cancel_handler(NULL);
493 
494  /* choose the database for our initial connection */
495  if (opts.alldb)
496  {
497  if (db != NULL)
498  {
499  pg_log_error("cannot specify a database name with --all");
500  exit(1);
501  }
502  cparams.dbname = maintenance_db;
503  }
504  else if (db != NULL)
505  {
506  if (opts.dbpattern)
507  {
508  pg_log_error("cannot specify both a database name and database patterns");
509  exit(1);
510  }
511  cparams.dbname = db;
512  }
513 
514  if (opts.alldb || opts.dbpattern)
515  {
516  conn = connectMaintenanceDatabase(&cparams, progname, opts.echo);
517  compile_database_list(conn, &databases, NULL);
518  }
519  else
520  {
521  if (cparams.dbname == NULL)
522  {
523  if (getenv("PGDATABASE"))
524  cparams.dbname = getenv("PGDATABASE");
525  else if (getenv("PGUSER"))
526  cparams.dbname = getenv("PGUSER");
527  else
529  }
530  conn = connectDatabase(&cparams, progname, opts.echo, false, true);
531  compile_database_list(conn, &databases, PQdb(conn));
532  }
533 
534  if (databases.head == NULL)
535  {
536  if (conn != NULL)
537  disconnectDatabase(conn);
538  pg_log_error("no databases to check");
539  exit(0);
540  }
541 
542  /*
543  * Compile a list of all relations spanning all databases to be checked.
544  */
545  for (cell = databases.head; cell; cell = cell->next)
546  {
547  PGresult *result;
548  int ntups;
549  const char *amcheck_schema = NULL;
550  DatabaseInfo *dat = (DatabaseInfo *) cell->ptr;
551 
552  cparams.override_dbname = dat->datname;
553  if (conn == NULL || strcmp(PQdb(conn), dat->datname) != 0)
554  {
555  if (conn != NULL)
556  disconnectDatabase(conn);
557  conn = connectDatabase(&cparams, progname, opts.echo, false, true);
558  }
559 
560  /*
561  * Optionally install amcheck if not already installed in this
562  * database.
563  */
564  if (opts.install_missing)
565  {
566  char *schema;
567  char *install_sql;
568 
569  /*
570  * Must re-escape the schema name for each database, as the
571  * escaping rules may change.
572  */
573  schema = PQescapeIdentifier(conn, opts.install_schema,
574  strlen(opts.install_schema));
575  install_sql = psprintf("CREATE EXTENSION IF NOT EXISTS amcheck WITH SCHEMA %s",
576  schema);
577 
578  executeCommand(conn, install_sql, opts.echo);
579  pfree(install_sql);
580  pfree(schema);
581  }
582 
583  /*
584  * Verify that amcheck is installed for this next database. User
585  * error could result in a database not having amcheck that should
586  * have it, but we also could be iterating over multiple databases
587  * where not all of them have amcheck installed (for example,
588  * 'template1').
589  */
590  result = executeQuery(conn, amcheck_sql, opts.echo);
591  if (PQresultStatus(result) != PGRES_TUPLES_OK)
592  {
593  /* Querying the catalog failed. */
594  pg_log_error("database \"%s\": %s",
595  PQdb(conn), PQerrorMessage(conn));
596  pg_log_info("query was: %s", amcheck_sql);
597  PQclear(result);
598  disconnectDatabase(conn);
599  exit(1);
600  }
601  ntups = PQntuples(result);
602  if (ntups == 0)
603  {
604  /* Querying the catalog succeeded, but amcheck is missing. */
605  pg_log_warning("skipping database \"%s\": amcheck is not installed",
606  PQdb(conn));
607  disconnectDatabase(conn);
608  conn = NULL;
609  continue;
610  }
611  amcheck_schema = PQgetvalue(result, 0, 0);
612  if (opts.verbose)
613  pg_log_info("in database \"%s\": using amcheck version \"%s\" in schema \"%s\"",
614  PQdb(conn), PQgetvalue(result, 0, 1), amcheck_schema);
615  dat->amcheck_schema = PQescapeIdentifier(conn, amcheck_schema,
616  strlen(amcheck_schema));
617  PQclear(result);
618 
619  compile_relation_list_one_db(conn, &relations, dat, &pagestotal);
620  }
621 
622  /*
623  * Check that all inclusion patterns matched at least one schema or
624  * relation that we can check.
625  */
626  for (pattern_id = 0; pattern_id < opts.include.len; pattern_id++)
627  {
628  PatternInfo *pat = &opts.include.data[pattern_id];
629 
630  if (!pat->matched && (pat->nsp_regex != NULL || pat->rel_regex != NULL))
631  {
632  failed = opts.strict_names;
633 
634  if (pat->heap_only)
635  log_no_match("no heap tables to check matching \"%s\"",
636  pat->pattern);
637  else if (pat->btree_only)
638  log_no_match("no btree indexes to check matching \"%s\"",
639  pat->pattern);
640  else if (pat->rel_regex == NULL)
641  log_no_match("no relations to check in schemas matching \"%s\"",
642  pat->pattern);
643  else
644  log_no_match("no relations to check matching \"%s\"",
645  pat->pattern);
646  }
647  }
648 
649  if (failed)
650  {
651  if (conn != NULL)
652  disconnectDatabase(conn);
653  exit(1);
654  }
655 
656  /*
657  * Set parallel_workers to the lesser of opts.jobs and the number of
658  * relations.
659  */
660  parallel_workers = 0;
661  for (cell = relations.head; cell; cell = cell->next)
662  {
663  reltotal++;
664  if (parallel_workers < opts.jobs)
665  parallel_workers++;
666  }
667 
668  if (reltotal == 0)
669  {
670  if (conn != NULL)
671  disconnectDatabase(conn);
672  pg_log_error("no relations to check");
673  exit(1);
674  }
675  progress_report(reltotal, relprogress, pagestotal, pageschecked,
676  NULL, true, false);
677 
678  /*
679  * Main event loop.
680  *
681  * We use server-side parallelism to check up to parallel_workers
682  * relations in parallel. The list of relations was computed in database
683  * order, which minimizes the number of connects and disconnects as we
684  * process the list.
685  */
686  latest_datname = NULL;
687  sa = ParallelSlotsSetup(parallel_workers, &cparams, progname, opts.echo,
688  NULL);
689  if (conn != NULL)
690  {
691  ParallelSlotsAdoptConn(sa, conn);
692  conn = NULL;
693  }
694 
695  initPQExpBuffer(&sql);
696  for (relprogress = 0, cell = relations.head; cell; cell = cell->next)
697  {
698  ParallelSlot *free_slot;
699  RelationInfo *rel;
700 
701  rel = (RelationInfo *) cell->ptr;
702 
703  if (CancelRequested)
704  {
705  failed = true;
706  break;
707  }
708 
709  /*
710  * The list of relations is in database sorted order. If this next
711  * relation is in a different database than the last one seen, we are
712  * about to start checking this database. Note that other slots may
713  * still be working on relations from prior databases.
714  */
715  latest_datname = rel->datinfo->datname;
716 
717  progress_report(reltotal, relprogress, pagestotal, pageschecked,
718  latest_datname, false, false);
719 
720  relprogress++;
721  pageschecked += rel->blocks_to_check;
722 
723  /*
724  * Get a parallel slot for the next amcheck command, blocking if
725  * necessary until one is available, or until a previously issued slot
726  * command fails, indicating that we should abort checking the
727  * remaining objects.
728  */
729  free_slot = ParallelSlotsGetIdle(sa, rel->datinfo->datname);
730  if (!free_slot)
731  {
732  /*
733  * Something failed. We don't need to know what it was, because
734  * the handler should already have emitted the necessary error
735  * messages.
736  */
737  failed = true;
738  break;
739  }
740 
741  if (opts.verbose)
743 
744  /*
745  * Execute the appropriate amcheck command for this relation using our
746  * slot's database connection. We do not wait for the command to
747  * complete, nor do we perform any error checking, as that is done by
748  * the parallel slots and our handler callback functions.
749  */
750  if (rel->is_heap)
751  {
752  if (opts.verbose)
753  {
755  fprintf(stderr, "\n");
756  pg_log_info("checking heap table \"%s.%s.%s\"",
757  rel->datinfo->datname, rel->nspname, rel->relname);
759  }
760  prepare_heap_command(&sql, rel, free_slot->connection);
761  rel->sql = pstrdup(sql.data); /* pg_free'd after command */
763  run_command(free_slot, rel->sql);
764  }
765  else
766  {
767  if (opts.verbose)
768  {
770  fprintf(stderr, "\n");
771 
772  pg_log_info("checking btree index \"%s.%s.%s\"",
773  rel->datinfo->datname, rel->nspname, rel->relname);
775  }
776  prepare_btree_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  }
782  termPQExpBuffer(&sql);
783 
784  if (!failed)
785  {
786 
787  /*
788  * Wait for all slots to complete, or for one to indicate that an
789  * error occurred. Like above, we rely on the handler emitting the
790  * necessary error messages.
791  */
792  if (sa && !ParallelSlotsWaitCompletion(sa))
793  failed = true;
794 
795  progress_report(reltotal, relprogress, pagestotal, pageschecked, NULL, true, true);
796  }
797 
798  if (sa)
799  {
801  FREE_AND_SET_NULL(sa);
802  }
803 
804  if (failed)
805  exit(1);
806 
807  if (!all_checks_pass)
808  exit(2);
809 }
bool heapallindexed
Definition: pg_amcheck.c:104
int64 endblock
Definition: pg_amcheck.c:98
static PGresult * executeQuery(PGconn *conn, const char *query)
Definition: pg_dumpall.c:1879
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6744
char * pgport
Definition: pg_backup.h:66
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3642
static void compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, const DatabaseInfo *datinfo, uint64 *pagecount)
Definition: pg_amcheck.c:1843
static bool verify_heap_slot_handler(PGresult *res, PGconn *conn, void *context)
Definition: pg_amcheck.c:1016
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
Definition: option_utils.c:50
static void append_relation_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1435
const char * get_progname(const char *argv0)
Definition: path.c:453
const char * pattern
Definition: pg_amcheck.c:34
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
#define pg_log_error(...)
Definition: logging.h:80
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:57
static bool verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
Definition: pg_amcheck.c:1097
static void compile_database_list(PGconn *conn, SimplePtrList *databases, const char *initial_dbname)
Definition: pg_amcheck.c:1540
char * pstrdup(const char *in)
Definition: mcxt.c:1299
void pg_logging_init(const char *argv0)
Definition: logging.c:81
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
void ParallelSlotsAdoptConn(ParallelSlotArray *sa, PGconn *conn)
char * relname
Definition: pg_amcheck.c:159
char * nspname
Definition: pg_amcheck.c:158
bool matched
Definition: pg_amcheck.c:44
static void ParallelSlotSetHandler(ParallelSlot *slot, ParallelSlotResultHandler handler, void *context)
Definition: parallel_slot.h:47
static void setup_cancel_handler(void)
Definition: parallel.c:613
char * amcheck_schema
Definition: pg_amcheck.c:150
bool no_toast_expansion
Definition: pg_amcheck.c:94
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
bool on_error_stop
Definition: pg_amcheck.c:96
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3248
#define fprintf
Definition: port.h:221
static void append_schema_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1357
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
ParallelSlotArray * ParallelSlotsSetup(int numslots, ConnParams *cparams, const char *progname, bool echo, const char *initcmd)
char * dbname
Definition: pg_backup.h:65
ParallelSlot * ParallelSlotsGetIdle(ParallelSlotArray *sa, const char *dbname)
void disconnectDatabase(PGconn *conn)
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:155
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
Definition: fe-exec.c:4075
bool install_missing
Definition: pg_amcheck.c:68
#define required_argument
Definition: getopt_long.h:25
void pfree(void *pointer)
Definition: mcxt.c:1169
int optind
Definition: getopt.c:50
bool no_btree_expansion
Definition: pg_amcheck.c:107
int64 startblock
Definition: pg_amcheck.c:97
#define MaxBlockNumber
Definition: block.h:35
static void append_database_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1334
void handle_help_version_opts(int argc, char *argv[], const char *fixed_progname, help_handler hlp)
Definition: option_utils.c:24
PGconn * conn
Definition: streamutil.c:54
static void append_btree_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1467
#define FREE_AND_SET_NULL(x)
Definition: pg_amcheck.c:210
static bool progress_since_last_stderr
Definition: pg_amcheck.c:145
char * sql
Definition: pg_amcheck.c:162
static void executeCommand(PGconn *conn, const char *query)
Definition: pg_dumpall.c:1902
char * c
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
PGconn * connection
Definition: parallel_slot.h:23
PatternInfoArray include
Definition: pg_amcheck.c:72
const char * username
Definition: pgbench.c:282
char * override_dbname
Definition: pg_backup.h:72
static int port
Definition: pg_regress.c:92
static AmcheckOptions opts
Definition: pg_amcheck.c:110
char * datname
Definition: pg_amcheck.c:149
char * pghost
Definition: pg_backup.h:67
static void prepare_btree_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
Definition: pg_amcheck.c:867
enum trivalue prompt_password
Definition: connect_utils.h:32
bool heap_only
Definition: pg_amcheck.c:40
trivalue
Definition: vacuumlo.c:34
#define no_argument
Definition: getopt_long.h:24
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1215
int blocks_to_check
Definition: pg_amcheck.c:161
static const char * amcheck_sql
Definition: pg_amcheck.c:169
bool ParallelSlotsWaitCompletion(ParallelSlotArray *sa)
static void help(const char *progname)
Definition: pg_amcheck.c:1160
PGconn * connectMaintenanceDatabase(ConnParams *cparams, const char *progname, bool echo)
bool reconcile_toast
Definition: pg_amcheck.c:95
int pg_get_encoding_from_locale(const char *ctype, bool write_message)
Definition: chklocale.c:452
void PQclear(PGresult *res)
Definition: fe-exec.c:694
static void prepare_heap_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn)
Definition: pg_amcheck.c:827
static void append_heap_pattern(PatternInfoArray *pia, const char *pattern, int encoding)
Definition: pg_amcheck.c:1451
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:6590
struct SimplePtrListCell * next
Definition: simple_list.h:48
static void run_command(ParallelSlot *slot, const char *sql)
Definition: pg_amcheck.c:912
bool btree_only
Definition: pg_amcheck.c:42
static PGconn * connectDatabase(const char *dbname, const char *connstr, const char *pghost, const char *pgport, const char *pguser, trivalue prompt_password, bool fail_on_error)
Definition: pg_dumpall.c:1641
static const char * progname
Definition: pg_amcheck.c:138
void pg_logging_increase_verbosity(void)
Definition: logging.c:174
#define optional_argument
Definition: getopt_long.h:26
const char * skip
Definition: pg_amcheck.c:99
int32 encoding
Definition: pg_database.h:41
PatternInfoArray exclude
Definition: pg_amcheck.c:73
bool show_progress
Definition: pg_amcheck.c:61
volatile sig_atomic_t CancelRequested
Definition: cancel.c:52
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:433
char * install_schema
Definition: pg_amcheck.c:69
SimplePtrListCell * head
Definition: simple_list.h:54
char * optarg
Definition: getopt.c:52
void ParallelSlotsTerminate(ParallelSlotArray *sa)
char * rel_regex
Definition: pg_amcheck.c:38
bool strict_names
Definition: pg_amcheck.c:60
char * nsp_regex
Definition: pg_amcheck.c:37
#define pg_log_warning(...)
Definition: pgfnames.c:24
PatternInfo * data
Definition: pg_amcheck.c:49
PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
Definition: fe-connect.c:6871
#define _(x)
Definition: elog.c:89
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:1220
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
#define pg_log_info(...)
Definition: logging.h:88
static bool all_checks_pass
Definition: pg_amcheck.c:141
#define log_no_match(...)
Definition: pg_amcheck.c:203
const char * pguser
Definition: connect_utils.h:31
const char * get_user_name_or_exit(const char *progname)
Definition: username.c:74

◆ prepare_btree_command()

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

Definition at line 867 of file pg_amcheck.c.

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

Referenced by main().

868 {
869  resetPQExpBuffer(sql);
870 
871  if (opts.parent_check)
872  appendPQExpBuffer(sql,
873  "SELECT %s.bt_index_parent_check("
874  "index := c.oid, heapallindexed := %s, rootdescend := %s)"
875  "\nFROM pg_catalog.pg_class c, pg_catalog.pg_index i "
876  "WHERE c.oid = %u "
877  "AND c.oid = i.indexrelid "
878  "AND c.relpersistence != 't' "
879  "AND i.indisready AND i.indisvalid AND i.indislive",
880  rel->datinfo->amcheck_schema,
881  (opts.heapallindexed ? "true" : "false"),
882  (opts.rootdescend ? "true" : "false"),
883  rel->reloid);
884  else
885  appendPQExpBuffer(sql,
886  "SELECT %s.bt_index_check("
887  "index := c.oid, heapallindexed := %s)"
888  "\nFROM pg_catalog.pg_class c, pg_catalog.pg_index i "
889  "WHERE c.oid = %u "
890  "AND c.oid = i.indexrelid "
891  "AND c.relpersistence != 't' "
892  "AND i.indisready AND i.indisvalid AND i.indislive",
893  rel->datinfo->amcheck_schema,
894  (opts.heapallindexed ? "true" : "false"),
895  rel->reloid);
896 }
bool heapallindexed
Definition: pg_amcheck.c:104
char * amcheck_schema
Definition: pg_amcheck.c:150
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:155
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
static AmcheckOptions opts
Definition: pg_amcheck.c:110
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:148

◆ prepare_heap_command()

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

Definition at line 827 of file pg_amcheck.c.

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

Referenced by main().

828 {
829  resetPQExpBuffer(sql);
830  appendPQExpBuffer(sql,
831  "SELECT v.blkno, v.offnum, v.attnum, v.msg "
832  "FROM pg_catalog.pg_class c, %s.verify_heapam("
833  "\nrelation := c.oid, on_error_stop := %s, check_toast := %s, skip := '%s'",
834  rel->datinfo->amcheck_schema,
835  opts.on_error_stop ? "true" : "false",
836  opts.reconcile_toast ? "true" : "false",
837  opts.skip);
838 
839  if (opts.startblock >= 0)
840  appendPQExpBuffer(sql, ", startblock := " INT64_FORMAT, opts.startblock);
841  if (opts.endblock >= 0)
842  appendPQExpBuffer(sql, ", endblock := " INT64_FORMAT, opts.endblock);
843 
844  appendPQExpBuffer(sql,
845  "\n) v WHERE c.oid = %u "
846  "AND c.relpersistence != 't'",
847  rel->reloid);
848 }
int64 endblock
Definition: pg_amcheck.c:98
char * amcheck_schema
Definition: pg_amcheck.c:150
bool on_error_stop
Definition: pg_amcheck.c:96
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:155
int64 startblock
Definition: pg_amcheck.c:97
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
static AmcheckOptions opts
Definition: pg_amcheck.c:110
bool reconcile_toast
Definition: pg_amcheck.c:95
const char * skip
Definition: pg_amcheck.c:99
#define INT64_FORMAT
Definition: c.h:483
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:148

◆ 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 1220 of file pg_amcheck.c.

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

Referenced by main(), and write_target_range().

1223 {
1224  int percent_rel = 0;
1225  int percent_pages = 0;
1226  char checked_rel[32];
1227  char total_rel[32];
1228  char checked_pages[32];
1229  char total_pages[32];
1230  pg_time_t now;
1231 
1232  if (!opts.show_progress)
1233  return;
1234 
1235  now = time(NULL);
1236  if (now == last_progress_report && !force && !finished)
1237  return; /* Max once per second */
1238 
1240  if (relations_total)
1241  percent_rel = (int) (relations_checked * 100 / relations_total);
1242  if (relpages_total)
1243  percent_pages = (int) (relpages_checked * 100 / relpages_total);
1244 
1245  snprintf(checked_rel, sizeof(checked_rel), UINT64_FORMAT, relations_checked);
1246  snprintf(total_rel, sizeof(total_rel), UINT64_FORMAT, relations_total);
1247  snprintf(checked_pages, sizeof(checked_pages), UINT64_FORMAT, relpages_checked);
1248  snprintf(total_pages, sizeof(total_pages), UINT64_FORMAT, relpages_total);
1249 
1250 #define VERBOSE_DATNAME_LENGTH 35
1251  if (opts.verbose)
1252  {
1253  if (!datname)
1254 
1255  /*
1256  * No datname given, so clear the status line (used for first and
1257  * last call)
1258  */
1259  fprintf(stderr,
1260  _("%*s/%s relations (%d%%), %*s/%s pages (%d%%) %*s"),
1261  (int) strlen(total_rel),
1262  checked_rel, total_rel, percent_rel,
1263  (int) strlen(total_pages),
1264  checked_pages, total_pages, percent_pages,
1265  VERBOSE_DATNAME_LENGTH + 2, "");
1266  else
1267  {
1268  bool truncate = (strlen(datname) > VERBOSE_DATNAME_LENGTH);
1269 
1270  fprintf(stderr,
1271  _("%*s/%s relations (%d%%), %*s/%s pages (%d%%) (%s%-*.*s)"),
1272  (int) strlen(total_rel),
1273  checked_rel, total_rel, percent_rel,
1274  (int) strlen(total_pages),
1275  checked_pages, total_pages, percent_pages,
1276  /* Prefix with "..." if we do leading truncation */
1277  truncate ? "..." : "",
1280  /* Truncate datname at beginning if it's too long */
1281  truncate ? datname + strlen(datname) - VERBOSE_DATNAME_LENGTH + 3 : datname);
1282  }
1283  }
1284  else
1285  fprintf(stderr,
1286  _("%*s/%s relations (%d%%), %*s/%s pages (%d%%)"),
1287  (int) strlen(total_rel),
1288  checked_rel, total_rel, percent_rel,
1289  (int) strlen(total_pages),
1290  checked_pages, total_pages, percent_pages);
1291 
1292  /*
1293  * Stay on the same line if reporting to a terminal and we're not done
1294  * yet.
1295  */
1296  if (!finished && isatty(fileno(stderr)))
1297  {
1298  fputc('\r', stderr);
1300  }
1301  else
1302  fputc('\n', stderr);
1303 }
#define VERBOSE_DATNAME_LENGTH
int64 pg_time_t
Definition: pgtime.h:23
NameData datname
Definition: pg_database.h:35
#define fprintf
Definition: port.h:221
static bool progress_since_last_stderr
Definition: pg_amcheck.c:145
static AmcheckOptions opts
Definition: pg_amcheck.c:110
static pg_time_t last_progress_report
Definition: pg_amcheck.c:144
bool show_progress
Definition: pg_amcheck.c:61
#define snprintf
Definition: port.h:217
#define _(x)
Definition: elog.c:89
#define UINT64_FORMAT
Definition: c.h:484
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1544

◆ run_command()

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

Definition at line 912 of file pg_amcheck.c.

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

Referenced by main().

913 {
914  if (opts.echo)
915  printf("%s\n", sql);
916 
917  if (PQsendQuery(slot->connection, sql) == 0)
918  {
919  pg_log_error("error sending command to database \"%s\": %s",
920  PQdb(slot->connection),
921  PQerrorMessage(slot->connection));
922  pg_log_error("command was: %s", sql);
923  exit(1);
924  }
925 }
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6744
#define pg_log_error(...)
Definition: logging.h:80
#define printf(...)
Definition: port.h:223
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1326
PGconn * connection
Definition: parallel_slot.h:23
static AmcheckOptions opts
Definition: pg_amcheck.c:110
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:6590

◆ should_processing_continue()

static bool should_processing_continue ( PGresult res)
static

Definition at line 944 of file pg_amcheck.c.

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_OK, PQresultErrorField(), and PQresultStatus().

Referenced by verify_btree_slot_handler(), and verify_heap_slot_handler().

945 {
946  const char *severity;
947 
948  switch (PQresultStatus(res))
949  {
950  /* These are expected and ok */
951  case PGRES_COMMAND_OK:
952  case PGRES_TUPLES_OK:
954  break;
955 
956  /* This is expected but requires closer scrutiny */
957  case PGRES_FATAL_ERROR:
959  if (strcmp(severity, "FATAL") == 0)
960  return false;
961  if (strcmp(severity, "PANIC") == 0)
962  return false;
963  break;
964 
965  /* These are unexpected */
966  case PGRES_BAD_RESPONSE:
967  case PGRES_EMPTY_QUERY:
968  case PGRES_COPY_OUT:
969  case PGRES_COPY_IN:
970  case PGRES_COPY_BOTH:
971  case PGRES_SINGLE_TUPLE:
972  case PGRES_PIPELINE_SYNC:
974  return false;
975  }
976  return true;
977 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
#define PG_DIAG_SEVERITY_NONLOCALIZED
Definition: postgres_ext.h:56
char * PQresultErrorField(const PGresult *res, int fieldcode)
Definition: fe-exec.c:3233

◆ verify_btree_slot_handler()

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

Definition at line 1097 of file pg_amcheck.c.

References _, all_checks_pass, RelationInfo::datinfo, DatabaseInfo::datname, fprintf, FREE_AND_SET_NULL, indent_lines(), RelationInfo::nspname, pg_log_info, pg_log_warning, 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().

1098 {
1099  RelationInfo *rel = (RelationInfo *) context;
1100 
1101  if (PQresultStatus(res) == PGRES_TUPLES_OK)
1102  {
1103  int ntups = PQntuples(res);
1104 
1105  if (ntups > 1)
1106  {
1107  /*
1108  * We expect the btree checking functions to return one void row
1109  * each, or zero rows if the check was skipped due to the object
1110  * being in the wrong state to be checked, so we should output some
1111  * sort of warning if we get anything more, not because it
1112  * indicates corruption, but because it suggests a mismatch between
1113  * amcheck and pg_amcheck versions.
1114  *
1115  * In conjunction with --progress, anything written to stderr at
1116  * this time would present strangely to the user without an extra
1117  * newline, so we print one. If we were multithreaded, we'd have
1118  * to avoid splitting this across multiple calls, but we're in an
1119  * event loop, so it doesn't matter.
1120  */
1122  fprintf(stderr, "\n");
1123  pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d",
1124  rel->datinfo->datname, rel->nspname, rel->relname, ntups);
1125  if (opts.verbose)
1126  pg_log_info("query was: %s", rel->sql);
1127  pg_log_warning("Are %s's and amcheck's versions compatible?",
1128  progname);
1130  }
1131  }
1132  else
1133  {
1134  char *msg = indent_lines(PQerrorMessage(conn));
1135 
1136  all_checks_pass = false;
1137  printf(_("btree index \"%s.%s.%s\":\n"),
1138  rel->datinfo->datname, rel->nspname, rel->relname);
1139  printf("%s", msg);
1140  if (opts.verbose)
1141  printf(_("query was: %s\n"), rel->sql);
1142  FREE_AND_SET_NULL(msg);
1143  }
1144 
1145  FREE_AND_SET_NULL(rel->sql);
1146  FREE_AND_SET_NULL(rel->nspname);
1147  FREE_AND_SET_NULL(rel->relname);
1148 
1149  return should_processing_continue(res);
1150 }
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6744
static char * indent_lines(const char *str)
Definition: pg_amcheck.c:985
char * relname
Definition: pg_amcheck.c:159
char * nspname
Definition: pg_amcheck.c:158
#define printf(...)
Definition: port.h:223
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3248
#define fprintf
Definition: port.h:221
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
static bool should_processing_continue(PGresult *res)
Definition: pg_amcheck.c:944
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:155
#define FREE_AND_SET_NULL(x)
Definition: pg_amcheck.c:210
static bool progress_since_last_stderr
Definition: pg_amcheck.c:145
char * sql
Definition: pg_amcheck.c:162
static AmcheckOptions opts
Definition: pg_amcheck.c:110
char * datname
Definition: pg_amcheck.c:149
static const char * progname
Definition: pg_amcheck.c:138
bool show_progress
Definition: pg_amcheck.c:61
#define pg_log_warning(...)
Definition: pgfnames.c:24
#define _(x)
Definition: elog.c:89
#define pg_log_info(...)
Definition: logging.h:88
static bool all_checks_pass
Definition: pg_amcheck.c:141

◆ verify_heap_slot_handler()

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

Definition at line 1016 of file pg_amcheck.c.

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

Referenced by main().

1017 {
1018  RelationInfo *rel = (RelationInfo *) context;
1019 
1020  if (PQresultStatus(res) == PGRES_TUPLES_OK)
1021  {
1022  int i;
1023  int ntups = PQntuples(res);
1024 
1025  if (ntups > 0)
1026  all_checks_pass = false;
1027 
1028  for (i = 0; i < ntups; i++)
1029  {
1030  const char *msg;
1031 
1032  /* The message string should never be null, but check */
1033  if (PQgetisnull(res, i, 3))
1034  msg = "NO MESSAGE";
1035  else
1036  msg = PQgetvalue(res, i, 3);
1037 
1038  if (!PQgetisnull(res, i, 2))
1039  printf(_("heap table \"%s.%s.%s\", block %s, offset %s, attribute %s:\n"),
1040  rel->datinfo->datname, rel->nspname, rel->relname,
1041  PQgetvalue(res, i, 0), /* blkno */
1042  PQgetvalue(res, i, 1), /* offnum */
1043  PQgetvalue(res, i, 2)); /* attnum */
1044 
1045  else if (!PQgetisnull(res, i, 1))
1046  printf(_("heap table \"%s.%s.%s\", block %s, offset %s:\n"),
1047  rel->datinfo->datname, rel->nspname, rel->relname,
1048  PQgetvalue(res, i, 0), /* blkno */
1049  PQgetvalue(res, i, 1)); /* offnum */
1050 
1051  else if (!PQgetisnull(res, i, 0))
1052  printf(_("heap table \"%s.%s.%s\", block %s:\n"),
1053  rel->datinfo->datname, rel->nspname, rel->relname,
1054  PQgetvalue(res, i, 0)); /* blkno */
1055 
1056  else
1057  printf(_("heap table \"%s.%s.%s\":\n"),
1058  rel->datinfo->datname, rel->nspname, rel->relname);
1059 
1060  printf(" %s\n", msg);
1061  }
1062  }
1063  else if (PQresultStatus(res) != PGRES_TUPLES_OK)
1064  {
1065  char *msg = indent_lines(PQerrorMessage(conn));
1066 
1067  all_checks_pass = false;
1068  printf(_("heap table \"%s.%s.%s\":\n"),
1069  rel->datinfo->datname, rel->nspname, rel->relname);
1070  printf("%s", msg);
1071  if (opts.verbose)
1072  printf(_("query was: %s\n"), rel->sql);
1073  FREE_AND_SET_NULL(msg);
1074  }
1075 
1076  FREE_AND_SET_NULL(rel->sql);
1077  FREE_AND_SET_NULL(rel->nspname);
1078  FREE_AND_SET_NULL(rel->relname);
1079 
1080  return should_processing_continue(res);
1081 }
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6744
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3642
static char * indent_lines(const char *str)
Definition: pg_amcheck.c:985
char * relname
Definition: pg_amcheck.c:159
char * nspname
Definition: pg_amcheck.c:158
#define printf(...)
Definition: port.h:223
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3248
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3178
static bool should_processing_continue(PGresult *res)
Definition: pg_amcheck.c:944
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:155
#define FREE_AND_SET_NULL(x)
Definition: pg_amcheck.c:210
char * sql
Definition: pg_amcheck.c:162
static AmcheckOptions opts
Definition: pg_amcheck.c:110
char * datname
Definition: pg_amcheck.c:149
int i
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3667
#define _(x)
Definition: elog.c:89
static bool all_checks_pass
Definition: pg_amcheck.c:141

Variable Documentation

◆ all_checks_pass

bool all_checks_pass = true
static

Definition at line 141 of file pg_amcheck.c.

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

◆ amcheck_sql

const char* 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 169 of file pg_amcheck.c.

Referenced by main().

◆ last_progress_report

pg_time_t last_progress_report = 0
static

Definition at line 144 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,
.no_btree_expansion = false
}

Definition at line 110 of file pg_amcheck.c.

Referenced by AlterSubscription(), BloomFillMetapage(), brin_bloom_add_value(), brin_minmax_multi_add_value(), build_local_reloptions(), CopyIndexAttOptions(), CreateSubscription(), get_attribute_options(), get_tablespace(), initBloomState(), libpqrcv_check_conninfo(), main(), makeDefaultBloomOptions(), NewDumpOptions(), NewRestoreOptions(), and RelationGetIndexAttOptions().

◆ progname

const char* progname = NULL
static

Definition at line 138 of file pg_amcheck.c.

Referenced by main(), and verify_btree_slot_handler().

◆ progress_since_last_stderr

bool progress_since_last_stderr = false
static

Definition at line 145 of file pg_amcheck.c.

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