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 212 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:111
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:61

Definition at line 205 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 1460 of file pg_amcheck.c.

References append_relation_pattern_helper().

Referenced by main().

1461 {
1462  append_relation_pattern_helper(pia, pattern, encoding, false, true);
1463 }
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
Definition: pg_amcheck.c:1385
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 1327 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().

1328 {
1331 
1332  initPQExpBuffer(&buf);
1333  patternToSQLRegex(encoding, NULL, NULL, &buf, pattern, false);
1334  info->pattern = pattern;
1335  info->db_regex = pstrdup(buf.data);
1336 
1337  termPQExpBuffer(&buf);
1338 }
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:1305
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 1487 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().

1489 {
1490  int pattern_id;
1491  const char *comma;
1492  bool have_values;
1493 
1494  comma = "";
1495  have_values = false;
1496  for (pattern_id = 0; pattern_id < pia->len; pattern_id++)
1497  {
1498  PatternInfo *info = &pia->data[pattern_id];
1499 
1500  if (info->db_regex != NULL &&
1501  (inclusive || (info->nsp_regex == NULL && info->rel_regex == NULL)))
1502  {
1503  if (!have_values)
1504  appendPQExpBufferStr(buf, "\nVALUES");
1505  have_values = true;
1506  appendPQExpBuffer(buf, "%s\n(%d, ", comma, pattern_id);
1507  appendStringLiteralConn(buf, info->db_regex, conn);
1508  appendPQExpBufferStr(buf, ")");
1509  comma = ",";
1510  }
1511  }
1512 
1513  if (!have_values)
1514  appendPQExpBufferStr(buf, "\nSELECT NULL, NULL, NULL WHERE false");
1515 
1516  return have_values;
1517 }
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 1444 of file pg_amcheck.c.

References append_relation_pattern_helper().

Referenced by main().

1445 {
1446  append_relation_pattern_helper(pia, pattern, encoding, true, false);
1447 }
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
Definition: pg_amcheck.c:1385
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 1797 of file pg_amcheck.c.

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

Referenced by compile_relation_list_one_db().

1799 {
1800  appendPQExpBuffer(buf,
1801  "\n%s (pattern_id, nsp_regex, rel_regex, heap_only, btree_only) AS ("
1802  "\nSELECT pattern_id, nsp_regex, rel_regex, heap_only, btree_only "
1803  "FROM %s r"
1804  "\nWHERE (r.db_regex IS NULL "
1805  "OR ",
1806  filtered, raw);
1807  appendStringLiteralConn(buf, PQdb(conn), conn);
1808  appendPQExpBufferStr(buf, " ~ r.db_regex)");
1810  " AND (r.nsp_regex IS NOT NULL"
1811  " OR r.rel_regex IS NOT NULL)"
1812  "),");
1813 }
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:6583
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 1728 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().

1730 {
1731  int pattern_id;
1732  const char *comma;
1733  bool have_values;
1734 
1735  comma = "";
1736  have_values = false;
1737  for (pattern_id = 0; pattern_id < pia->len; pattern_id++)
1738  {
1739  PatternInfo *info = &pia->data[pattern_id];
1740 
1741  if (!have_values)
1742  appendPQExpBufferStr(buf, "\nVALUES");
1743  have_values = true;
1744  appendPQExpBuffer(buf, "%s\n(%d::INTEGER, ", comma, pattern_id);
1745  if (info->db_regex == NULL)
1746  appendPQExpBufferStr(buf, "NULL");
1747  else
1748  appendStringLiteralConn(buf, info->db_regex, conn);
1749  appendPQExpBufferStr(buf, "::TEXT, ");
1750  if (info->nsp_regex == NULL)
1751  appendPQExpBufferStr(buf, "NULL");
1752  else
1753  appendStringLiteralConn(buf, info->nsp_regex, conn);
1754  appendPQExpBufferStr(buf, "::TEXT, ");
1755  if (info->rel_regex == NULL)
1756  appendPQExpBufferStr(buf, "NULL");
1757  else
1758  appendStringLiteralConn(buf, info->rel_regex, conn);
1759  if (info->heap_only)
1760  appendPQExpBufferStr(buf, "::TEXT, true::BOOLEAN");
1761  else
1762  appendPQExpBufferStr(buf, "::TEXT, false::BOOLEAN");
1763  if (info->btree_only)
1764  appendPQExpBufferStr(buf, ", true::BOOLEAN");
1765  else
1766  appendPQExpBufferStr(buf, ", false::BOOLEAN");
1767  appendPQExpBufferStr(buf, ")");
1768  comma = ",";
1769  }
1770 
1771  if (!have_values)
1773  "\nSELECT NULL::INTEGER, NULL::TEXT, NULL::TEXT, "
1774  "NULL::TEXT, NULL::BOOLEAN, NULL::BOOLEAN "
1775  "WHERE false");
1776 }
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 1428 of file pg_amcheck.c.

References append_relation_pattern_helper().

Referenced by main().

1429 {
1430  append_relation_pattern_helper(pia, pattern, encoding, false, false);
1431 }
static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, int encoding, bool heap_only, bool btree_only)
Definition: pg_amcheck.c:1385
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 1385 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().

1387 {
1388  PQExpBufferData dbbuf;
1389  PQExpBufferData nspbuf;
1390  PQExpBufferData relbuf;
1392 
1393  initPQExpBuffer(&dbbuf);
1394  initPQExpBuffer(&nspbuf);
1395  initPQExpBuffer(&relbuf);
1396 
1397  patternToSQLRegex(encoding, &dbbuf, &nspbuf, &relbuf, pattern, false);
1398  info->pattern = pattern;
1399  if (dbbuf.data[0])
1400  {
1401  opts.dbpattern = true;
1402  info->db_regex = pstrdup(dbbuf.data);
1403  }
1404  if (nspbuf.data[0])
1405  info->nsp_regex = pstrdup(nspbuf.data);
1406  if (relbuf.data[0])
1407  info->rel_regex = pstrdup(relbuf.data);
1408 
1409  termPQExpBuffer(&dbbuf);
1410  termPQExpBuffer(&nspbuf);
1411  termPQExpBuffer(&relbuf);
1412 
1413  info->heap_only = heap_only;
1414  info->btree_only = btree_only;
1415 }
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:111
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:1305
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 1350 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().

1351 {
1352  PQExpBufferData dbbuf;
1353  PQExpBufferData nspbuf;
1355 
1356  initPQExpBuffer(&dbbuf);
1357  initPQExpBuffer(&nspbuf);
1358 
1359  patternToSQLRegex(encoding, NULL, &dbbuf, &nspbuf, pattern, false);
1360  info->pattern = pattern;
1361  if (dbbuf.data[0])
1362  {
1363  opts.dbpattern = true;
1364  info->db_regex = pstrdup(dbbuf.data);
1365  }
1366  if (nspbuf.data[0])
1367  info->nsp_regex = pstrdup(nspbuf.data);
1368 
1369  termPQExpBuffer(&dbbuf);
1370  termPQExpBuffer(&nspbuf);
1371 }
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:111
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:1305
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 1533 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().

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

◆ 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 1836 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().

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

1306 {
1307  PatternInfo *result;
1308 
1309  pia->len++;
1310  pia->data = (PatternInfo *) pg_realloc(pia->data, pia->len * sizeof(PatternInfo));
1311  result = &pia->data[pia->len - 1];
1312  memset(result, 0, sizeof(*result));
1313 
1314  return result;
1315 }
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 1147 of file pg_amcheck.c.

References _, and printf.

Referenced by main().

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

◆ indent_lines()

static char* indent_lines ( const char *  str)
static

Definition at line 975 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().

976 {
978  const char *c;
979  char *result;
980 
981  initPQExpBuffer(&buf);
982  appendPQExpBufferStr(&buf, " ");
983  for (c = str; *c; c++)
984  {
985  appendPQExpBufferChar(&buf, *c);
986  if (c[0] == '\n' && c[1] != '\0')
987  appendPQExpBufferStr(&buf, " ");
988  }
989  result = pstrdup(buf.data);
990  termPQExpBuffer(&buf);
991 
992  return result;
993 }
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 218 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_TERSE, 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::quiet, 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().

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

862 {
863  resetPQExpBuffer(sql);
864 
865  /*
866  * Embed the database, schema, and relation name in the query, so if the
867  * check throws an error, the user knows which relation the error came
868  * from.
869  */
870  if (opts.parent_check)
871  appendPQExpBuffer(sql,
872  "SELECT * FROM %s.bt_index_parent_check("
873  "index := '%u'::regclass, heapallindexed := %s, "
874  "rootdescend := %s)",
875  rel->datinfo->amcheck_schema,
876  rel->reloid,
877  (opts.heapallindexed ? "true" : "false"),
878  (opts.rootdescend ? "true" : "false"));
879  else
880  appendPQExpBuffer(sql,
881  "SELECT * FROM %s.bt_index_check("
882  "index := '%u'::regclass, heapallindexed := %s)",
883  rel->datinfo->amcheck_schema,
884  rel->reloid,
885  (opts.heapallindexed ? "true" : "false"));
886 }
bool heapallindexed
Definition: pg_amcheck.c:105
char * amcheck_schema
Definition: pg_amcheck.c:152
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:157
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
static AmcheckOptions opts
Definition: pg_amcheck.c:111
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 828 of file pg_amcheck.c.

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

Referenced by main().

829 {
830  resetPQExpBuffer(sql);
831  appendPQExpBuffer(sql,
832  "SELECT blkno, offnum, attnum, msg FROM %s.verify_heapam("
833  "\nrelation := %u, on_error_stop := %s, check_toast := %s, skip := '%s'",
834  rel->datinfo->amcheck_schema,
835  rel->reloid,
836  opts.on_error_stop ? "true" : "false",
837  opts.reconcile_toast ? "true" : "false",
838  opts.skip);
839 
840  if (opts.startblock >= 0)
841  appendPQExpBuffer(sql, ", startblock := " INT64_FORMAT, opts.startblock);
842  if (opts.endblock >= 0)
843  appendPQExpBuffer(sql, ", endblock := " INT64_FORMAT, opts.endblock);
844 
845  appendPQExpBufferChar(sql, ')');
846 }
int64 endblock
Definition: pg_amcheck.c:99
char * amcheck_schema
Definition: pg_amcheck.c:152
bool on_error_stop
Definition: pg_amcheck.c:97
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:157
int64 startblock
Definition: pg_amcheck.c:98
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
static AmcheckOptions opts
Definition: pg_amcheck.c:111
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:380
bool reconcile_toast
Definition: pg_amcheck.c:96
const char * skip
Definition: pg_amcheck.c:100
#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 1208 of file pg_amcheck.c.

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

Referenced by main(), and write_target_range().

1211 {
1212  int percent_rel = 0;
1213  int percent_pages = 0;
1214  char checked_rel[32];
1215  char total_rel[32];
1216  char checked_pages[32];
1217  char total_pages[32];
1218  pg_time_t now;
1219 
1220  if (!opts.show_progress)
1221  return;
1222 
1223  now = time(NULL);
1224  if (now == last_progress_report && !force && !finished)
1225  return; /* Max once per second */
1226 
1228  if (relations_total)
1229  percent_rel = (int) (relations_checked * 100 / relations_total);
1230  if (relpages_total)
1231  percent_pages = (int) (relpages_checked * 100 / relpages_total);
1232 
1233  /*
1234  * Separate step to keep platform-dependent format code out of fprintf
1235  * calls. We only test for INT64_FORMAT availability in snprintf, not
1236  * fprintf.
1237  */
1238  snprintf(checked_rel, sizeof(checked_rel), INT64_FORMAT, relations_checked);
1239  snprintf(total_rel, sizeof(total_rel), INT64_FORMAT, relations_total);
1240  snprintf(checked_pages, sizeof(checked_pages), INT64_FORMAT, relpages_checked);
1241  snprintf(total_pages, sizeof(total_pages), INT64_FORMAT, relpages_total);
1242 
1243 #define VERBOSE_DATNAME_LENGTH 35
1244  if (opts.verbose)
1245  {
1246  if (!datname)
1247 
1248  /*
1249  * No datname given, so clear the status line (used for first and
1250  * last call)
1251  */
1252  fprintf(stderr,
1253  _("%*s/%s relations (%d%%) %*s/%s pages (%d%%) %*s"),
1254  (int) strlen(total_rel),
1255  checked_rel, total_rel, percent_rel,
1256  (int) strlen(total_pages),
1257  checked_pages, total_pages, percent_pages,
1258  VERBOSE_DATNAME_LENGTH + 2, "");
1259  else
1260  {
1261  bool truncate = (strlen(datname) > VERBOSE_DATNAME_LENGTH);
1262 
1263  fprintf(stderr,
1264  _("%*s/%s relations (%d%%) %*s/%s pages (%d%%), (%s%-*.*s)"),
1265  (int) strlen(total_rel),
1266  checked_rel, total_rel, percent_rel,
1267  (int) strlen(total_pages),
1268  checked_pages, total_pages, percent_pages,
1269  /* Prefix with "..." if we do leading truncation */
1270  truncate ? "..." : "",
1273  /* Truncate datname at beginning if it's too long */
1274  truncate ? datname + strlen(datname) - VERBOSE_DATNAME_LENGTH + 3 : datname);
1275  }
1276  }
1277  else
1278  fprintf(stderr,
1279  _("%*s/%s relations (%d%%) %*s/%s pages (%d%%)"),
1280  (int) strlen(total_rel),
1281  checked_rel, total_rel, percent_rel,
1282  (int) strlen(total_pages),
1283  checked_pages, total_pages, percent_pages);
1284 
1285  /*
1286  * Stay on the same line if reporting to a terminal and we're not done
1287  * yet.
1288  */
1289  if (!finished && isatty(fileno(stderr)))
1290  {
1291  fputc('\r', stderr);
1293  }
1294  else
1295  fputc('\n', stderr);
1296 }
#define VERBOSE_DATNAME_LENGTH
int64 pg_time_t
Definition: pgtime.h:23
NameData datname
Definition: pg_database.h:35
#define fprintf
Definition: port.h:220
static bool progress_since_last_stderr
Definition: pg_amcheck.c:147
static AmcheckOptions opts
Definition: pg_amcheck.c:111
static pg_time_t last_progress_report
Definition: pg_amcheck.c:146
#define INT64_FORMAT
Definition: c.h:483
bool show_progress
Definition: pg_amcheck.c:62
#define snprintf
Definition: port.h:216
#define _(x)
Definition: elog.c:89
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1544

◆ run_command()

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

Definition at line 902 of file pg_amcheck.c.

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

Referenced by main().

903 {
904  if (opts.echo)
905  printf("%s\n", sql);
906 
907  if (PQsendQuery(slot->connection, sql) == 0)
908  {
909  pg_log_error("error sending command to database \"%s\": %s",
910  PQdb(slot->connection),
911  PQerrorMessage(slot->connection));
912  pg_log_error("command was: %s", sql);
913  exit(1);
914  }
915 }
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6737
#define pg_log_error(...)
Definition: logging.h:80
#define printf(...)
Definition: port.h:222
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1312
PGconn * connection
Definition: parallel_slot.h:23
static AmcheckOptions opts
Definition: pg_amcheck.c:111
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:6583

◆ should_processing_continue()

static bool should_processing_continue ( PGresult res)
static

Definition at line 934 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().

935 {
936  const char *severity;
937 
938  switch (PQresultStatus(res))
939  {
940  /* These are expected and ok */
941  case PGRES_COMMAND_OK:
942  case PGRES_TUPLES_OK:
944  break;
945 
946  /* This is expected but requires closer scrutiny */
947  case PGRES_FATAL_ERROR:
949  if (strcmp(severity, "FATAL") == 0)
950  return false;
951  if (strcmp(severity, "PANIC") == 0)
952  return false;
953  break;
954 
955  /* These are unexpected */
956  case PGRES_BAD_RESPONSE:
957  case PGRES_EMPTY_QUERY:
958  case PGRES_COPY_OUT:
959  case PGRES_COPY_IN:
960  case PGRES_COPY_BOTH:
961  case PGRES_SINGLE_TUPLE:
962  case PGRES_PIPELINE_SYNC:
964  return false;
965  }
966  return true;
967 }
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3164
#define PG_DIAG_SEVERITY_NONLOCALIZED
Definition: postgres_ext.h:56
char * PQresultErrorField(const PGresult *res, int fieldcode)
Definition: fe-exec.c:3219

◆ verify_btree_slot_handler()

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

Definition at line 1087 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().

1088 {
1089  RelationInfo *rel = (RelationInfo *) context;
1090 
1091  if (PQresultStatus(res) == PGRES_TUPLES_OK)
1092  {
1093  int ntups = PQntuples(res);
1094 
1095  if (ntups != 1)
1096  {
1097  /*
1098  * We expect the btree checking functions to return one void row
1099  * each, so we should output some sort of warning if we get
1100  * anything else, not because it indicates corruption, but because
1101  * it suggests a mismatch between amcheck and pg_amcheck versions.
1102  *
1103  * In conjunction with --progress, anything written to stderr at
1104  * this time would present strangely to the user without an extra
1105  * newline, so we print one. If we were multithreaded, we'd have
1106  * to avoid splitting this across multiple calls, but we're in an
1107  * event loop, so it doesn't matter.
1108  */
1110  fprintf(stderr, "\n");
1111  pg_log_warning("btree index \"%s\".\"%s\".\"%s\": btree checking function returned unexpected number of rows: %d",
1112  rel->datinfo->datname, rel->nspname, rel->relname, ntups);
1113  if (opts.verbose)
1114  pg_log_info("query was: %s", rel->sql);
1115  pg_log_warning("Are %s's and amcheck's versions compatible?",
1116  progname);
1118  }
1119  }
1120  else
1121  {
1122  char *msg = indent_lines(PQerrorMessage(conn));
1123 
1124  all_checks_pass = false;
1125  printf("btree index \"%s\".\"%s\".\"%s\":\n%s",
1126  rel->datinfo->datname, rel->nspname, rel->relname, msg);
1127  if (opts.verbose)
1128  printf("query was: %s\n", rel->sql);
1129  FREE_AND_SET_NULL(msg);
1130  }
1131 
1132  FREE_AND_SET_NULL(rel->sql);
1133  FREE_AND_SET_NULL(rel->nspname);
1134  FREE_AND_SET_NULL(rel->relname);
1135 
1136  return should_processing_continue(res);
1137 }
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6737
static char * indent_lines(const char *str)
Definition: pg_amcheck.c:975
char * relname
Definition: pg_amcheck.c:161
char * nspname
Definition: pg_amcheck.c:160
#define printf(...)
Definition: port.h:222
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3234
#define fprintf
Definition: port.h:220
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3164
static bool should_processing_continue(PGresult *res)
Definition: pg_amcheck.c:934
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:157
#define FREE_AND_SET_NULL(x)
Definition: pg_amcheck.c:212
static bool progress_since_last_stderr
Definition: pg_amcheck.c:147
char * sql
Definition: pg_amcheck.c:164
static AmcheckOptions opts
Definition: pg_amcheck.c:111
char * datname
Definition: pg_amcheck.c:151
static const char * progname
Definition: pg_amcheck.c:140
bool show_progress
Definition: pg_amcheck.c:62
#define pg_log_warning(...)
Definition: pgfnames.c:24
#define pg_log_info(...)
Definition: logging.h:88
static bool all_checks_pass
Definition: pg_amcheck.c:143

◆ verify_heap_slot_handler()

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

Definition at line 1006 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().

1007 {
1008  RelationInfo *rel = (RelationInfo *) context;
1009 
1010  if (PQresultStatus(res) == PGRES_TUPLES_OK)
1011  {
1012  int i;
1013  int ntups = PQntuples(res);
1014 
1015  if (ntups > 0)
1016  all_checks_pass = false;
1017 
1018  for (i = 0; i < ntups; i++)
1019  {
1020  const char *msg;
1021 
1022  /* The message string should never be null, but check */
1023  if (PQgetisnull(res, i, 3))
1024  msg = "NO MESSAGE";
1025  else
1026  msg = PQgetvalue(res, i, 3);
1027 
1028  if (!PQgetisnull(res, i, 2))
1029  printf("heap table \"%s\".\"%s\".\"%s\", block %s, offset %s, attribute %s:\n %s\n",
1030  rel->datinfo->datname, rel->nspname, rel->relname,
1031  PQgetvalue(res, i, 0), /* blkno */
1032  PQgetvalue(res, i, 1), /* offnum */
1033  PQgetvalue(res, i, 2), /* attnum */
1034  msg);
1035 
1036  else if (!PQgetisnull(res, i, 1))
1037  printf("heap table \"%s\".\"%s\".\"%s\", block %s, offset %s:\n %s\n",
1038  rel->datinfo->datname, rel->nspname, rel->relname,
1039  PQgetvalue(res, i, 0), /* blkno */
1040  PQgetvalue(res, i, 1), /* offnum */
1041  msg);
1042 
1043  else if (!PQgetisnull(res, i, 0))
1044  printf("heap table \"%s\".\"%s\".\"%s\", block %s:\n %s\n",
1045  rel->datinfo->datname, rel->nspname, rel->relname,
1046  PQgetvalue(res, i, 0), /* blkno */
1047  msg);
1048 
1049  else
1050  printf("heap table \"%s\".\"%s\".\"%s\":\n %s\n",
1051  rel->datinfo->datname, rel->nspname, rel->relname, msg);
1052  }
1053  }
1054  else if (PQresultStatus(res) != PGRES_TUPLES_OK)
1055  {
1056  char *msg = indent_lines(PQerrorMessage(conn));
1057 
1058  all_checks_pass = false;
1059  printf("heap table \"%s\".\"%s\".\"%s\":\n%s",
1060  rel->datinfo->datname, rel->nspname, rel->relname, msg);
1061  if (opts.verbose)
1062  printf("query was: %s\n", rel->sql);
1063  FREE_AND_SET_NULL(msg);
1064  }
1065 
1066  FREE_AND_SET_NULL(rel->sql);
1067  FREE_AND_SET_NULL(rel->nspname);
1068  FREE_AND_SET_NULL(rel->relname);
1069 
1070  return should_processing_continue(res);
1071 }
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:6737
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3628
static char * indent_lines(const char *str)
Definition: pg_amcheck.c:975
char * relname
Definition: pg_amcheck.c:161
char * nspname
Definition: pg_amcheck.c:160
#define printf(...)
Definition: port.h:222
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3234
ExecStatusType PQresultStatus(const PGresult *res)
Definition: fe-exec.c:3164
static bool should_processing_continue(PGresult *res)
Definition: pg_amcheck.c:934
const DatabaseInfo * datinfo
Definition: pg_amcheck.c:157
#define FREE_AND_SET_NULL(x)
Definition: pg_amcheck.c:212
char * sql
Definition: pg_amcheck.c:164
static AmcheckOptions opts
Definition: pg_amcheck.c:111
char * datname
Definition: pg_amcheck.c:151
int i
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3653
static bool all_checks_pass
Definition: pg_amcheck.c:143

Variable Documentation

◆ all_checks_pass

bool all_checks_pass = true
static

Definition at line 143 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 171 of file pg_amcheck.c.

Referenced by main().

◆ last_progress_report

pg_time_t last_progress_report = 0
static

Definition at line 146 of file pg_amcheck.c.

Referenced by progress_report().

◆ opts

AmcheckOptions opts
static
Initial value:
= {
.dbpattern = false,
.alldb = false,
.echo = false,
.quiet = 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 111 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 140 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 147 of file pg_amcheck.c.

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