From 7d896c762ca82f2740c7d780f41dffb402ff8610 Mon Sep 17 00:00:00 2001 From: "Andrey M. Borodin" Date: Sun, 5 Feb 2023 15:52:14 -0800 Subject: [PATCH v24 4/4] Add GiST support to pg_amcheck --- src/bin/pg_amcheck/pg_amcheck.c | 205 ++++++++++++++++----------- src/bin/pg_amcheck/t/002_nonesuch.pl | 8 +- src/bin/pg_amcheck/t/003_check.pl | 48 ++++--- 3 files changed, 157 insertions(+), 104 deletions(-) diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c index 68f8180c19..fad6350cd2 100644 --- a/src/bin/pg_amcheck/pg_amcheck.c +++ b/src/bin/pg_amcheck/pg_amcheck.c @@ -39,8 +39,7 @@ typedef struct PatternInfo * NULL */ bool heap_only; /* true if rel_regex should only match heap * tables */ - bool btree_only; /* true if rel_regex should only match btree - * indexes */ + bool index_only; /* true if rel_regex should only match indexes */ bool matched; /* true if the pattern matched in any database */ } PatternInfo; @@ -74,7 +73,7 @@ typedef struct AmcheckOptions /* * As an optimization, if any pattern in the exclude list applies to heap - * tables, or similarly if any such pattern applies to btree indexes, or + * tables, or similarly if any such pattern applies to indexes, or * to schemas, then these will be true, otherwise false. These should * always agree with what you'd conclude by grep'ing through the exclude * list. @@ -98,13 +97,13 @@ typedef struct AmcheckOptions int64 endblock; const char *skip; - /* btree index checking options */ + /* index checking options */ bool parent_check; bool rootdescend; bool heapallindexed; - /* heap and btree hybrid option */ - bool no_btree_expansion; + /* heap and indexes hybrid option */ + bool no_index_expansion; } AmcheckOptions; static AmcheckOptions opts = { @@ -132,7 +131,7 @@ static AmcheckOptions opts = { .parent_check = false, .rootdescend = false, .heapallindexed = false, - .no_btree_expansion = false + .no_index_expansion = false }; static const char *progname = NULL; @@ -154,7 +153,8 @@ typedef struct RelationInfo { const DatabaseInfo *datinfo; /* shared by other relinfos */ Oid reloid; - bool is_heap; /* true if heap, false if btree */ + Oid amoid; + bool is_heap; /* true if heap, false if index */ char *nspname; char *relname; int relpages; @@ -175,10 +175,12 @@ static void prepare_heap_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn); static void prepare_btree_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn); +static void prepare_gist_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 bool verify_index_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, @@ -192,7 +194,7 @@ 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, +static void append_index_pattern(PatternInfoArray *pia, const char *pattern, int encoding); static void compile_database_list(PGconn *conn, SimplePtrList *databases, const char *initial_dbname); @@ -318,11 +320,11 @@ main(int argc, char *argv[]) break; case 'i': opts.allrel = false; - append_btree_pattern(&opts.include, optarg, encoding); + append_index_pattern(&opts.include, optarg, encoding); break; case 'I': opts.excludeidx = true; - append_btree_pattern(&opts.exclude, optarg, encoding); + append_index_pattern(&opts.exclude, optarg, encoding); break; case 'j': if (!option_parse_int(optarg, "-j/--jobs", 1, INT_MAX, @@ -377,7 +379,7 @@ main(int argc, char *argv[]) maintenance_db = pg_strdup(optarg); break; case 2: - opts.no_btree_expansion = true; + opts.no_index_expansion = true; break; case 3: opts.no_toast_expansion = true; @@ -609,8 +611,8 @@ main(int argc, char *argv[]) if (pat->heap_only) log_no_match("no heap tables to check matching \"%s\"", pat->pattern); - else if (pat->btree_only) - log_no_match("no btree indexes to check matching \"%s\"", + else if (pat->index_only) + log_no_match("no indexes to check matching \"%s\"", pat->pattern); else if (pat->rel_regex == NULL) log_no_match("no relations to check in schemas matching \"%s\"", @@ -743,13 +745,20 @@ main(int argc, char *argv[]) if (opts.show_progress && progress_since_last_stderr) fprintf(stderr, "\n"); - pg_log_info("checking btree index \"%s.%s.%s\"", + pg_log_info("checking index \"%s.%s.%s\"", rel->datinfo->datname, rel->nspname, rel->relname); progress_since_last_stderr = false; } - prepare_btree_command(&sql, rel, free_slot->connection); + if (rel->amoid == BTREE_AM_OID) + prepare_btree_command(&sql, rel, free_slot->connection); + else if (rel->amoid == GIST_AM_OID) + prepare_gist_command(&sql, rel, free_slot->connection); + else + /* should not happen at this stage */ + pg_log_info("Verification of index type %u not supported", + rel->amoid); rel->sql = pstrdup(sql.data); /* pg_free'd after command */ - ParallelSlotSetHandler(free_slot, verify_btree_slot_handler, rel); + ParallelSlotSetHandler(free_slot, verify_index_slot_handler, rel); run_command(free_slot, rel->sql); } } @@ -827,7 +836,7 @@ prepare_heap_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn) * Creates a SQL command for running amcheck checking on the given btree index * relation. The command does not select any columns, as btree checking * functions do not return any, but rather return corruption information by - * raising errors, which verify_btree_slot_handler expects. + * raising errors, which verify_index_slot_handler expects. * * The constructed SQL command will silently skip temporary indexes, and * indexes being reindexed concurrently, as checking them would needlessly draw @@ -869,6 +878,28 @@ prepare_btree_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn) rel->reloid); } +/* + * prepare_gist_command + * Similar to btree equivalent prepares command to check GiST index. + */ +static void +prepare_gist_command(PQExpBuffer sql, RelationInfo *rel, PGconn *conn) +{ + resetPQExpBuffer(sql); + + appendPQExpBuffer(sql, + "SELECT %s.gist_index_check(" + "index := c.oid, heapallindexed := %s)" + "\nFROM pg_catalog.pg_class c, pg_catalog.pg_index i " + "WHERE c.oid = %u " + "AND c.oid = i.indexrelid " + "AND c.relpersistence != 't' " + "AND i.indisready AND i.indisvalid AND i.indislive", + rel->datinfo->amcheck_schema, + (opts.heapallindexed ? "true" : "false"), + rel->reloid); +} + /* * run_command * @@ -908,7 +939,7 @@ run_command(ParallelSlot *slot, const char *sql) * Note: Heap relation corruption is reported by verify_heapam() via the result * set, rather than an ERROR, but running verify_heapam() on a corrupted heap * table may still result in an error being returned from the server due to - * missing relation files, bad checksums, etc. The btree corruption checking + * missing relation files, bad checksums, etc. The corruption checking * functions always use errors to communicate corruption messages. We can't * just abort processing because we got a mere ERROR. * @@ -1057,11 +1088,11 @@ verify_heap_slot_handler(PGresult *res, PGconn *conn, void *context) } /* - * verify_btree_slot_handler + * verify_index_slot_handler * - * ParallelSlotHandler that receives results from a btree checking command - * created by prepare_btree_command and outputs them for the user. The results - * from the btree checking command is assumed to be empty, but when the results + * ParallelSlotHandler that receives results from a checking command created by + * prepare_[btree,gist]_command and outputs them for the user. The results + * from the checking command is assumed to be empty, but when the results * are an error code, the useful information about the corruption is expected * in the connection's error message. * @@ -1070,7 +1101,7 @@ verify_heap_slot_handler(PGresult *res, PGconn *conn, void *context) * context: unused */ static bool -verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context) +verify_index_slot_handler(PGresult *res, PGconn *conn, void *context) { RelationInfo *rel = (RelationInfo *) context; @@ -1081,7 +1112,7 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context) if (ntups > 1) { /* - * We expect the btree checking functions to return one void row + * We expect the checking functions to return one void row * each, or zero rows if the check was skipped due to the object * being in the wrong state to be checked, so we should output * some sort of warning if we get anything more, not because it @@ -1096,7 +1127,7 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context) */ if (opts.show_progress && progress_since_last_stderr) fprintf(stderr, "\n"); - pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d", + pg_log_warning("index \"%s.%s.%s\": checking function returned unexpected number of rows: %d", rel->datinfo->datname, rel->nspname, rel->relname, ntups); if (opts.verbose) pg_log_warning_detail("Query was: %s", rel->sql); @@ -1110,7 +1141,7 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context) char *msg = indent_lines(PQerrorMessage(conn)); all_checks_pass = false; - printf(_("btree index \"%s.%s.%s\":\n"), + printf(_("index \"%s.%s.%s\":\n"), rel->datinfo->datname, rel->nspname, rel->relname); printf("%s", msg); if (opts.verbose) @@ -1163,6 +1194,8 @@ help(const char *progname) printf(_(" --heapallindexed check that all heap tuples are found within indexes\n")); printf(_(" --parent-check check index parent/child relationships\n")); printf(_(" --rootdescend search from root page to refind tuples\n")); + printf(_("\nGiST index checking options:\n")); + printf(_(" --heapallindexed check that all heap tuples are found within indexes\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); printf(_(" -p, --port=PORT database server port\n")); @@ -1376,11 +1409,11 @@ append_schema_pattern(PatternInfoArray *pia, const char *pattern, int encoding) * pattern: the relation name pattern * encoding: client encoding for parsing the pattern * heap_only: whether the pattern should only be matched against heap tables - * btree_only: whether the pattern should only be matched against btree indexes + * index_only: whether the pattern should only be matched against indexes */ static void append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, - int encoding, bool heap_only, bool btree_only) + int encoding, bool heap_only, bool index_only) { PQExpBufferData dbbuf; PQExpBufferData nspbuf; @@ -1415,14 +1448,14 @@ append_relation_pattern_helper(PatternInfoArray *pia, const char *pattern, termPQExpBuffer(&relbuf); info->heap_only = heap_only; - info->btree_only = btree_only; + info->index_only = index_only; } /* * append_relation_pattern * * Adds the given pattern interpreted as a relation pattern, to be matched - * against both heap tables and btree indexes. + * against both heap tables and indexes. * * pia: the pattern info array to be appended * pattern: the relation name pattern @@ -1451,17 +1484,17 @@ append_heap_pattern(PatternInfoArray *pia, const char *pattern, int encoding) } /* - * append_btree_pattern + * append_index_pattern * * Adds the given pattern interpreted as a relation pattern, to be matched only - * against btree indexes. + * against indexes. * * pia: the pattern info array to be appended * pattern: the relation name pattern * encoding: client encoding for parsing the pattern */ static void -append_btree_pattern(PatternInfoArray *pia, const char *pattern, int encoding) +append_index_pattern(PatternInfoArray *pia, const char *pattern, int encoding) { append_relation_pattern_helper(pia, pattern, encoding, false, true); } @@ -1719,7 +1752,7 @@ compile_database_list(PGconn *conn, SimplePtrList *databases, * rel_regex: the relname regexp parsed from the pattern, or NULL if the * pattern had no relname part * heap_only: true if the pattern applies only to heap tables (not indexes) - * btree_only: true if the pattern applies only to btree indexes (not tables) + * index_only: true if the pattern applies only to indexes (not tables) * * buf: the buffer to be appended * patterns: the array of patterns to be inserted into the CTE @@ -1761,7 +1794,7 @@ append_rel_pattern_raw_cte(PQExpBuffer buf, const PatternInfoArray *pia, appendPQExpBufferStr(buf, "::TEXT, true::BOOLEAN"); else appendPQExpBufferStr(buf, "::TEXT, false::BOOLEAN"); - if (info->btree_only) + if (info->index_only) appendPQExpBufferStr(buf, ", true::BOOLEAN"); else appendPQExpBufferStr(buf, ", false::BOOLEAN"); @@ -1799,8 +1832,8 @@ append_rel_pattern_filtered_cte(PQExpBuffer buf, const char *raw, const char *filtered, PGconn *conn) { appendPQExpBuffer(buf, - "\n%s (pattern_id, nsp_regex, rel_regex, heap_only, btree_only) AS (" - "\nSELECT pattern_id, nsp_regex, rel_regex, heap_only, btree_only " + "\n%s (pattern_id, nsp_regex, rel_regex, heap_only, index_only) AS (" + "\nSELECT pattern_id, nsp_regex, rel_regex, heap_only, index_only " "FROM %s r" "\nWHERE (r.db_regex IS NULL " "OR ", @@ -1823,7 +1856,7 @@ append_rel_pattern_filtered_cte(PQExpBuffer buf, const char *raw, * The cells of the constructed list contain all information about the relation * necessary to connect to the database and check the object, including which * database to connect to, where contrib/amcheck is installed, and the Oid and - * type of object (heap table vs. btree index). Rather than duplicating the + * type of object (heap table vs. index). Rather than duplicating the * database details per relation, the relation structs use references to the * same database object, provided by the caller. * @@ -1850,7 +1883,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, if (!opts.allrel) { appendPQExpBufferStr(&sql, - " include_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS ("); + " include_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, index_only) AS ("); append_rel_pattern_raw_cte(&sql, &opts.include, conn); appendPQExpBufferStr(&sql, "\n),"); append_rel_pattern_filtered_cte(&sql, "include_raw", "include_pat", conn); @@ -1860,7 +1893,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, if (opts.excludetbl || opts.excludeidx || opts.excludensp) { appendPQExpBufferStr(&sql, - " exclude_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, btree_only) AS ("); + " exclude_raw (pattern_id, db_regex, nsp_regex, rel_regex, heap_only, index_only) AS ("); append_rel_pattern_raw_cte(&sql, &opts.exclude, conn); appendPQExpBufferStr(&sql, "\n),"); append_rel_pattern_filtered_cte(&sql, "exclude_raw", "exclude_pat", conn); @@ -1868,36 +1901,36 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, /* Append the relation CTE. */ appendPQExpBufferStr(&sql, - " relation (pattern_id, oid, nspname, relname, reltoastrelid, relpages, is_heap, is_btree) AS (" + " relation (pattern_id, oid, amoid, nspname, relname, reltoastrelid, relpages, is_heap, is_index) AS (" "\nSELECT DISTINCT ON (c.oid"); if (!opts.allrel) appendPQExpBufferStr(&sql, ", ip.pattern_id) ip.pattern_id,"); else appendPQExpBufferStr(&sql, ") NULL::INTEGER AS pattern_id,"); appendPQExpBuffer(&sql, - "\nc.oid, n.nspname, c.relname, c.reltoastrelid, c.relpages, " - "c.relam = %u AS is_heap, " - "c.relam = %u AS is_btree" + "\nc.oid, c.relam as amoid, n.nspname, c.relname, " + "c.reltoastrelid, c.relpages, c.relam = %u AS is_heap, " + "(c.relam = %u OR c.relam = %u) AS is_index" "\nFROM pg_catalog.pg_class c " "INNER JOIN pg_catalog.pg_namespace n " "ON c.relnamespace = n.oid", - HEAP_TABLE_AM_OID, BTREE_AM_OID); + HEAP_TABLE_AM_OID, BTREE_AM_OID, GIST_AM_OID); if (!opts.allrel) appendPQExpBuffer(&sql, "\nINNER JOIN include_pat ip" "\nON (n.nspname ~ ip.nsp_regex OR ip.nsp_regex IS NULL)" "\nAND (c.relname ~ ip.rel_regex OR ip.rel_regex IS NULL)" "\nAND (c.relam = %u OR NOT ip.heap_only)" - "\nAND (c.relam = %u OR NOT ip.btree_only)", - HEAP_TABLE_AM_OID, BTREE_AM_OID); + "\nAND ((c.relam = %u OR c.relam = %u) OR NOT ip.index_only)", + HEAP_TABLE_AM_OID, BTREE_AM_OID, GIST_AM_OID); if (opts.excludetbl || opts.excludeidx || opts.excludensp) appendPQExpBuffer(&sql, "\nLEFT OUTER JOIN exclude_pat ep" "\nON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL)" "\nAND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL)" "\nAND (c.relam = %u OR NOT ep.heap_only OR ep.rel_regex IS NULL)" - "\nAND (c.relam = %u OR NOT ep.btree_only OR ep.rel_regex IS NULL)", - HEAP_TABLE_AM_OID, BTREE_AM_OID); + "\nAND ((c.relam = %u OR c.relam = %u) OR NOT ep.index_only OR ep.rel_regex IS NULL)", + HEAP_TABLE_AM_OID, BTREE_AM_OID, GIST_AM_OID); /* * Exclude temporary tables and indexes, which must necessarily belong to @@ -1931,12 +1964,12 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, HEAP_TABLE_AM_OID, PG_TOAST_NAMESPACE); else appendPQExpBuffer(&sql, - " AND c.relam IN (%u, %u)" + " AND c.relam IN (%u, %u, %u)" "AND c.relkind IN ('r', 'S', 'm', 't', 'i') " "AND ((c.relam = %u AND c.relkind IN ('r', 'S', 'm', 't')) OR " - "(c.relam = %u AND c.relkind = 'i'))", - HEAP_TABLE_AM_OID, BTREE_AM_OID, - HEAP_TABLE_AM_OID, BTREE_AM_OID); + "((c.relam = %u OR c.relam = %u) AND c.relkind = 'i'))", + HEAP_TABLE_AM_OID, BTREE_AM_OID, GIST_AM_OID, + HEAP_TABLE_AM_OID, BTREE_AM_OID, GIST_AM_OID); appendPQExpBufferStr(&sql, "\nORDER BY c.oid)"); @@ -1965,17 +1998,18 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, appendPQExpBufferStr(&sql, "\n)"); } - if (!opts.no_btree_expansion) + if (!opts.no_index_expansion) { /* * Include a CTE for btree indexes associated with primary heap tables * selected above, filtering by exclusion patterns (if any) that match - * btree index names. + * btree index names. Currently, only btree indexes can be PK, but this + * might chance in future. */ appendPQExpBufferStr(&sql, - ", index (oid, nspname, relname, relpages) AS (" - "\nSELECT c.oid, r.nspname, c.relname, c.relpages " - "FROM relation r" + ", index (oid, amoid, nspname, relname, relpages) AS (" + "\nSELECT c.oid, c.relam as amoid, r.nspname, " + "c.relname, c.relpages FROM relation r" "\nINNER JOIN pg_catalog.pg_index i " "ON r.oid = i.indrelid " "INNER JOIN pg_catalog.pg_class c " @@ -1988,7 +2022,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, "\nLEFT OUTER JOIN exclude_pat ep " "ON (n.nspname ~ ep.nsp_regex OR ep.nsp_regex IS NULL) " "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) " - "AND ep.btree_only" + "AND ep.index_only" "\nWHERE ep.pattern_id IS NULL"); else appendPQExpBufferStr(&sql, @@ -1996,7 +2030,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, appendPQExpBuffer(&sql, " AND c.relam = %u " "AND c.relkind = 'i'", - BTREE_AM_OID); + BTREE_AM_OID); /* Do not expect other AMs here */ if (opts.no_toast_expansion) appendPQExpBuffer(&sql, " AND c.relnamespace != %u", @@ -2004,7 +2038,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, appendPQExpBufferStr(&sql, "\n)"); } - if (!opts.no_toast_expansion && !opts.no_btree_expansion) + if (!opts.no_toast_expansion && !opts.no_index_expansion) { /* * Include a CTE for btree indexes associated with toast tables of @@ -2025,13 +2059,13 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, "\nLEFT OUTER JOIN exclude_pat ep " "ON ('pg_toast' ~ ep.nsp_regex OR ep.nsp_regex IS NULL) " "AND (c.relname ~ ep.rel_regex OR ep.rel_regex IS NULL) " - "AND ep.btree_only " + "AND ep.index_only " "WHERE ep.pattern_id IS NULL"); else appendPQExpBufferStr(&sql, "\nWHERE true"); appendPQExpBuffer(&sql, - " AND c.relam = %u" + " AND c.relam = %u " " AND c.relkind = 'i')", BTREE_AM_OID); } @@ -2045,12 +2079,13 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, * list. */ appendPQExpBufferStr(&sql, - "\nSELECT pattern_id, is_heap, is_btree, oid, nspname, relname, relpages " + "\nSELECT pattern_id, is_heap, is_index, oid, amoid, nspname, relname, relpages " "FROM ("); appendPQExpBufferStr(&sql, /* Inclusion patterns that failed to match */ - "\nSELECT pattern_id, is_heap, is_btree, " + "\nSELECT pattern_id, is_heap, is_index, " "NULL::OID AS oid, " + "NULL::OID AS amoid, " "NULL::TEXT AS nspname, " "NULL::TEXT AS relname, " "NULL::INTEGER AS relpages" @@ -2059,29 +2094,29 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, "UNION" /* Primary relations */ "\nSELECT NULL::INTEGER AS pattern_id, " - "is_heap, is_btree, oid, nspname, relname, relpages " + "is_heap, is_index, oid, amoid, nspname, relname, relpages " "FROM relation"); if (!opts.no_toast_expansion) - appendPQExpBufferStr(&sql, + appendPQExpBuffer(&sql, " UNION" /* Toast tables for primary relations */ "\nSELECT NULL::INTEGER AS pattern_id, TRUE AS is_heap, " - "FALSE AS is_btree, oid, nspname, relname, relpages " + "FALSE AS is_index, oid, 0 as amoid, nspname, relname, relpages " "FROM toast"); - if (!opts.no_btree_expansion) + if (!opts.no_index_expansion) appendPQExpBufferStr(&sql, " UNION" /* Indexes for primary relations */ "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, " - "TRUE AS is_btree, oid, nspname, relname, relpages " + "TRUE AS is_index, oid, amoid, nspname, relname, relpages " "FROM index"); - if (!opts.no_toast_expansion && !opts.no_btree_expansion) - appendPQExpBufferStr(&sql, + if (!opts.no_toast_expansion && !opts.no_index_expansion) + appendPQExpBuffer(&sql, " UNION" /* Indexes for toast relations */ "\nSELECT NULL::INTEGER AS pattern_id, FALSE AS is_heap, " - "TRUE AS is_btree, oid, nspname, relname, relpages " - "FROM toast_index"); + "TRUE AS is_index, oid, %u as amoid, nspname, relname, relpages " + "FROM toast_index", BTREE_AM_OID); appendPQExpBufferStr(&sql, "\n) AS combined_records " "ORDER BY relpages DESC NULLS FIRST, oid"); @@ -2101,8 +2136,9 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, { int pattern_id = -1; bool is_heap = false; - bool is_btree PG_USED_FOR_ASSERTS_ONLY = false; + bool is_index PG_USED_FOR_ASSERTS_ONLY = false; Oid oid = InvalidOid; + Oid amoid = InvalidOid; const char *nspname = NULL; const char *relname = NULL; int relpages = 0; @@ -2112,15 +2148,17 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, if (!PQgetisnull(res, i, 1)) is_heap = (PQgetvalue(res, i, 1)[0] == 't'); if (!PQgetisnull(res, i, 2)) - is_btree = (PQgetvalue(res, i, 2)[0] == 't'); + is_index = (PQgetvalue(res, i, 2)[0] == 't'); if (!PQgetisnull(res, i, 3)) oid = atooid(PQgetvalue(res, i, 3)); if (!PQgetisnull(res, i, 4)) - nspname = PQgetvalue(res, i, 4); + amoid = atooid(PQgetvalue(res, i, 4)); if (!PQgetisnull(res, i, 5)) - relname = PQgetvalue(res, i, 5); + nspname = PQgetvalue(res, i, 5); if (!PQgetisnull(res, i, 6)) - relpages = atoi(PQgetvalue(res, i, 6)); + relname = PQgetvalue(res, i, 6); + if (!PQgetisnull(res, i, 7)) + relpages = atoi(PQgetvalue(res, i, 7)); if (pattern_id >= 0) { @@ -2142,10 +2180,11 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, RelationInfo *rel = (RelationInfo *) pg_malloc0(sizeof(RelationInfo)); Assert(OidIsValid(oid)); - Assert((is_heap && !is_btree) || (is_btree && !is_heap)); + Assert((is_heap && !is_index) || (is_index && !is_heap)); rel->datinfo = dat; rel->reloid = oid; + rel->amoid = amoid; rel->is_heap = is_heap; rel->nspname = pstrdup(nspname); rel->relname = pstrdup(relname); @@ -2155,7 +2194,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations, { /* * We apply --startblock and --endblock to heap tables, but - * not btree indexes, and for progress purposes we need to + * not supported indexes, and for progress purposes we need to * track how many blocks we expect to check. */ if (opts.endblock >= 0 && rel->blocks_to_check > opts.endblock) diff --git a/src/bin/pg_amcheck/t/002_nonesuch.pl b/src/bin/pg_amcheck/t/002_nonesuch.pl index 58be2c694d..5e8a63a844 100644 --- a/src/bin/pg_amcheck/t/002_nonesuch.pl +++ b/src/bin/pg_amcheck/t/002_nonesuch.pl @@ -272,8 +272,8 @@ $node->command_checks_all( [ qr/pg_amcheck: warning: no heap tables to check matching "no_such_table"/, qr/pg_amcheck: warning: no heap tables to check matching "no\*such\*table"/, - qr/pg_amcheck: warning: no btree indexes to check matching "no_such_index"/, - qr/pg_amcheck: warning: no btree indexes to check matching "no\*such\*index"/, + qr/pg_amcheck: warning: no indexes to check matching "no_such_index"/, + qr/pg_amcheck: warning: no indexes to check matching "no\*such\*index"/, qr/pg_amcheck: warning: no relations to check matching "no_such_relation"/, qr/pg_amcheck: warning: no relations to check matching "no\*such\*relation"/, qr/pg_amcheck: warning: no heap tables to check matching "no\*such\*table"/, @@ -319,8 +319,8 @@ $node->command_checks_all( qr/pg_amcheck: warning: no heap tables to check matching "template1\.public\.foo"/, qr/pg_amcheck: warning: no heap tables to check matching "another_db\.public\.foo"/, qr/pg_amcheck: warning: no connectable databases to check matching "no_such_database\.public\.foo"/, - qr/pg_amcheck: warning: no btree indexes to check matching "template1\.public\.foo_idx"/, - qr/pg_amcheck: warning: no btree indexes to check matching "another_db\.public\.foo_idx"/, + qr/pg_amcheck: warning: no indexes to check matching "template1\.public\.foo_idx"/, + qr/pg_amcheck: warning: no indexes to check matching "another_db\.public\.foo_idx"/, qr/pg_amcheck: warning: no connectable databases to check matching "no_such_database\.public\.foo_idx"/, qr/pg_amcheck: error: no relations to check/, ], diff --git a/src/bin/pg_amcheck/t/003_check.pl b/src/bin/pg_amcheck/t/003_check.pl index 359abe25a1..8b6326dd3d 100644 --- a/src/bin/pg_amcheck/t/003_check.pl +++ b/src/bin/pg_amcheck/t/003_check.pl @@ -185,7 +185,7 @@ for my $dbname (qw(db1 db2 db3)) # schemas. The schemas are all identical to start, but # we will corrupt them differently later. # - for my $schema (qw(s1 s2 s3 s4 s5)) + for my $schema (qw(s1 s2 s3 s4 s5 s6)) { $node->safe_psql( $dbname, qq( @@ -288,22 +288,24 @@ plan_to_corrupt_first_page('db1', 's3.t2_btree'); # Corrupt toast table, partitions, and materialized views in schema "s4" plan_to_remove_toast_file('db1', 's4.t2'); -# Corrupt all other object types in schema "s5". We don't have amcheck support +# Corrupt GiST index in schema "s5" +plan_to_remove_relation_file('db1', 's5.t1_gist'); +plan_to_corrupt_first_page('db1', 's5.t2_gist'); + +# Corrupt all other object types in schema "s6". We don't have amcheck support # for these types, but we check that their corruption does not trigger any # errors in pg_amcheck -plan_to_remove_relation_file('db1', 's5.seq1'); -plan_to_remove_relation_file('db1', 's5.t1_hash'); -plan_to_remove_relation_file('db1', 's5.t1_gist'); -plan_to_remove_relation_file('db1', 's5.t1_gin'); -plan_to_remove_relation_file('db1', 's5.t1_brin'); -plan_to_remove_relation_file('db1', 's5.t1_spgist'); +plan_to_remove_relation_file('db1', 's6.seq1'); +plan_to_remove_relation_file('db1', 's6.t1_hash'); +plan_to_remove_relation_file('db1', 's6.t1_gin'); +plan_to_remove_relation_file('db1', 's6.t1_brin'); +plan_to_remove_relation_file('db1', 's6.t1_spgist'); -plan_to_corrupt_first_page('db1', 's5.seq2'); -plan_to_corrupt_first_page('db1', 's5.t2_hash'); -plan_to_corrupt_first_page('db1', 's5.t2_gist'); -plan_to_corrupt_first_page('db1', 's5.t2_gin'); -plan_to_corrupt_first_page('db1', 's5.t2_brin'); -plan_to_corrupt_first_page('db1', 's5.t2_spgist'); +plan_to_corrupt_first_page('db1', 's6.seq2'); +plan_to_corrupt_first_page('db1', 's6.t2_hash'); +plan_to_corrupt_first_page('db1', 's6.t2_gin'); +plan_to_corrupt_first_page('db1', 's6.t2_brin'); +plan_to_corrupt_first_page('db1', 's6.t2_spgist'); # Database 'db2' corruptions @@ -434,10 +436,22 @@ $node->command_checks_all( [$no_output_re], 'pg_amcheck in schema s4 excluding toast reports no corruption'); -# Check that no corruption is reported in schema db1.s5 -$node->command_checks_all([ @cmd, '-s', 's5', 'db1' ], +# In schema db1.s5 we should see GiST corruption messages on stdout, and +# nothing on stderr. +# +$node->command_checks_all( + [ @cmd, '-s', 's5', 'db1' ], + 2, + [ + $missing_file_re, $line_pointer_corruption_re, + ], + [$no_output_re], + 'pg_amcheck schema s5 reports GiST index errors'); + +# Check that no corruption is reported in schema db1.s6 +$node->command_checks_all([ @cmd, '-s', 's6', 'db1' ], 0, [$no_output_re], [$no_output_re], - 'pg_amcheck over schema s5 reports no corruption'); + 'pg_amcheck over schema s6 reports no corruption'); # In schema db1.s1, only indexes are corrupt. Verify that when we exclude # the indexes, no corruption is reported about the schema. -- 2.32.0 (Apple Git-132)