v13-0001-Adding-function-verify_btreeam-and-bumping-versi.patch
application/octet-stream
Filename: v13-0001-Adding-function-verify_btreeam-and-bumping-versi.patch
Type: application/octet-stream
Part: 0
Message:
Re: new heapcheck contrib module
Patch
Format: format-patch
Series: patch v13-0001
Subject: Adding function verify_btreeam and bumping version
| File | + | − |
|---|---|---|
| contrib/amcheck/amcheck.control | 1 | 1 |
| contrib/amcheck/expected/check_btree.out | 35 | 0 |
| contrib/amcheck/Makefile | 1 | 1 |
| contrib/amcheck/sql/check_btree.sql | 13 | 0 |
| contrib/amcheck/verify_nbtree.c | 465 | 369 |
| src/tools/pgindent/typedefs.list | 1 | 0 |
From 980ba0a318b1d876fd0e9c8b0cf8167a600a5c71 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 27 Jul 2020 08:02:24 -0700
Subject: [PATCH v13 1/3] Adding function verify_btreeam and bumping version
For most errors found while verifying a btree index, the new
function verify_btreeam returns one row per error containing the
block number where the error was discovered and an error message
describing the problem. The pre-existing behavior for functions
bt_index_parent_check and bt_index_check is unchanged.
---
contrib/amcheck/Makefile | 2 +-
contrib/amcheck/amcheck.control | 2 +-
contrib/amcheck/expected/check_btree.out | 35 +
contrib/amcheck/sql/check_btree.sql | 13 +
contrib/amcheck/verify_nbtree.c | 834 +++++++++++++----------
src/tools/pgindent/typedefs.list | 1 +
6 files changed, 516 insertions(+), 371 deletions(-)
diff --git a/contrib/amcheck/Makefile b/contrib/amcheck/Makefile
index a2b1b1036b..b288c28fa0 100644
--- a/contrib/amcheck/Makefile
+++ b/contrib/amcheck/Makefile
@@ -6,7 +6,7 @@ OBJS = \
verify_nbtree.o
EXTENSION = amcheck
-DATA = amcheck--1.1--1.2.sql amcheck--1.0--1.1.sql amcheck--1.0.sql
+DATA = amcheck--1.2--1.3.sql amcheck--1.1--1.2.sql amcheck--1.0--1.1.sql amcheck--1.0.sql
PGFILEDESC = "amcheck - function for verifying relation integrity"
REGRESS = check check_btree
diff --git a/contrib/amcheck/amcheck.control b/contrib/amcheck/amcheck.control
index c6e310046d..ab50931f75 100644
--- a/contrib/amcheck/amcheck.control
+++ b/contrib/amcheck/amcheck.control
@@ -1,5 +1,5 @@
# amcheck extension
comment = 'functions for verifying relation integrity'
-default_version = '1.2'
+default_version = '1.3'
module_pathname = '$libdir/amcheck'
relocatable = true
diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out
index f82f48d23b..7297abb577 100644
--- a/contrib/amcheck/expected/check_btree.out
+++ b/contrib/amcheck/expected/check_btree.out
@@ -21,6 +21,8 @@ SELECT bt_index_check('bttest_a_idx'::regclass);
ERROR: permission denied for function bt_index_check
SELECT bt_index_parent_check('bttest_a_idx'::regclass);
ERROR: permission denied for function bt_index_parent_check
+SELECT * FROM verify_btreeam('bttest_a_idx'::regclass);
+ERROR: permission denied for function verify_btreeam
RESET ROLE;
-- we, intentionally, don't check relation permissions - it's useful
-- to run this cluster-wide with a restricted account, and as tested
@@ -29,6 +31,7 @@ GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO regress_bttest_role;
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass) TO regress_bttest_role;
GRANT EXECUTE ON FUNCTION bt_index_check(regclass, boolean) TO regress_bttest_role;
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass, boolean) TO regress_bttest_role;
+GRANT EXECUTE ON FUNCTION verify_btreeam(regclass, boolean) TO regress_bttest_role;
SET ROLE regress_bttest_role;
SELECT bt_index_check('bttest_a_idx');
bt_index_check
@@ -42,17 +45,23 @@ SELECT bt_index_parent_check('bttest_a_idx');
(1 row)
+SELECT * FROM verify_btreeam('bttest_a_idx');
+ERROR: permission denied for function verify_btreeam
RESET ROLE;
-- verify plain tables are rejected (error)
SELECT bt_index_check('bttest_a');
ERROR: "bttest_a" is not an index
SELECT bt_index_parent_check('bttest_a');
ERROR: "bttest_a" is not an index
+SELECT * FROM verify_btreeam('bttest_a');
+ERROR: "bttest_a" is not an index
-- verify non-existing indexes are rejected (error)
SELECT bt_index_check(17);
ERROR: could not open relation with OID 17
SELECT bt_index_parent_check(17);
ERROR: could not open relation with OID 17
+SELECT * FROM verify_btreeam(17);
+ERROR: could not open relation with OID 17
-- verify wrong index types are rejected (error)
BEGIN;
CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id);
@@ -60,6 +69,12 @@ SELECT bt_index_parent_check('bttest_a_brin_idx');
ERROR: only B-Tree indexes are supported as targets for verification
DETAIL: Relation "bttest_a_brin_idx" is not a B-Tree index.
ROLLBACK;
+BEGIN;
+CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id);
+SELECT * FROM verify_btreeam('bttest_a_brin_idx');
+ERROR: only B-Tree indexes are supported as targets for verification
+DETAIL: Relation "bttest_a_brin_idx" is not a B-Tree index.
+ROLLBACK;
-- normal check outside of xact
SELECT bt_index_check('bttest_a_idx');
bt_index_check
@@ -67,6 +82,11 @@ SELECT bt_index_check('bttest_a_idx');
(1 row)
+SELECT * FROM verify_btreeam('bttest_a_idx');
+ blkno | msg
+-------+-----
+(0 rows)
+
-- more expansive tests
SELECT bt_index_check('bttest_a_idx', true);
bt_index_check
@@ -93,6 +113,11 @@ SELECT bt_index_parent_check('bttest_b_idx');
(1 row)
+SELECT * FROM verify_btreeam('bttest_a_idx');
+ blkno | msg
+-------+-----
+(0 rows)
+
-- make sure we don't have any leftover locks
SELECT * FROM pg_locks
WHERE relation = ANY(ARRAY['bttest_a', 'bttest_a_idx', 'bttest_b', 'bttest_b_idx']::regclass[])
@@ -118,6 +143,11 @@ SELECT bt_index_check('bttest_multi_idx');
(1 row)
+SELECT * FROM verify_btreeam('bttest_multi_idx');
+ blkno | msg
+-------+-----
+(0 rows)
+
-- more expansive tests for index with included columns
SELECT bt_index_parent_check('bttest_multi_idx', true, true);
bt_index_parent_check
@@ -134,6 +164,11 @@ SELECT bt_index_parent_check('bttest_multi_idx', true, true);
(1 row)
+SELECT * FROM verify_btreeam('bttest_multi_idx');
+ blkno | msg
+-------+-----
+(0 rows)
+
--
-- Test for multilevel page deletion/downlink present checks, and rootdescend
-- checks
diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql
index a1fef644cb..816ca9d033 100644
--- a/contrib/amcheck/sql/check_btree.sql
+++ b/contrib/amcheck/sql/check_btree.sql
@@ -24,6 +24,7 @@ CREATE ROLE regress_bttest_role;
SET ROLE regress_bttest_role;
SELECT bt_index_check('bttest_a_idx'::regclass);
SELECT bt_index_parent_check('bttest_a_idx'::regclass);
+SELECT * FROM verify_btreeam('bttest_a_idx'::regclass);
RESET ROLE;
-- we, intentionally, don't check relation permissions - it's useful
@@ -33,27 +34,36 @@ GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO regress_bttest_role;
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass) TO regress_bttest_role;
GRANT EXECUTE ON FUNCTION bt_index_check(regclass, boolean) TO regress_bttest_role;
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass, boolean) TO regress_bttest_role;
+GRANT EXECUTE ON FUNCTION verify_btreeam(regclass, boolean) TO regress_bttest_role;
SET ROLE regress_bttest_role;
SELECT bt_index_check('bttest_a_idx');
SELECT bt_index_parent_check('bttest_a_idx');
+SELECT * FROM verify_btreeam('bttest_a_idx');
RESET ROLE;
-- verify plain tables are rejected (error)
SELECT bt_index_check('bttest_a');
SELECT bt_index_parent_check('bttest_a');
+SELECT * FROM verify_btreeam('bttest_a');
-- verify non-existing indexes are rejected (error)
SELECT bt_index_check(17);
SELECT bt_index_parent_check(17);
+SELECT * FROM verify_btreeam(17);
-- verify wrong index types are rejected (error)
BEGIN;
CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id);
SELECT bt_index_parent_check('bttest_a_brin_idx');
ROLLBACK;
+BEGIN;
+CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id);
+SELECT * FROM verify_btreeam('bttest_a_brin_idx');
+ROLLBACK;
-- normal check outside of xact
SELECT bt_index_check('bttest_a_idx');
+SELECT * FROM verify_btreeam('bttest_a_idx');
-- more expansive tests
SELECT bt_index_check('bttest_a_idx', true);
SELECT bt_index_parent_check('bttest_b_idx', true);
@@ -61,6 +71,7 @@ SELECT bt_index_parent_check('bttest_b_idx', true);
BEGIN;
SELECT bt_index_check('bttest_a_idx');
SELECT bt_index_parent_check('bttest_b_idx');
+SELECT * FROM verify_btreeam('bttest_a_idx');
-- make sure we don't have any leftover locks
SELECT * FROM pg_locks
WHERE relation = ANY(ARRAY['bttest_a', 'bttest_a_idx', 'bttest_b', 'bttest_b_idx']::regclass[])
@@ -74,6 +85,7 @@ SELECT bt_index_check('bttest_a_idx', true);
-- normal check outside of xact for index with included columns
SELECT bt_index_check('bttest_multi_idx');
+SELECT * FROM verify_btreeam('bttest_multi_idx');
-- more expansive tests for index with included columns
SELECT bt_index_parent_check('bttest_multi_idx', true, true);
@@ -81,6 +93,7 @@ SELECT bt_index_parent_check('bttest_multi_idx', true, true);
TRUNCATE bttest_multi;
INSERT INTO bttest_multi SELECT i, i%2 FROM generate_series(1, 100000) as i;
SELECT bt_index_parent_check('bttest_multi_idx', true, true);
+SELECT * FROM verify_btreeam('bttest_multi_idx');
--
-- Test for multilevel page deletion/downlink present checks, and rootdescend
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index e4d501a85d..ea70fc41a9 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -32,16 +32,22 @@
#include "catalog/index.h"
#include "catalog/pg_am.h"
#include "commands/tablecmds.h"
+#include "funcapi.h"
#include "lib/bloomfilter.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
+#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
-
+#include "amcheck.h"
PG_MODULE_MAGIC;
+PG_FUNCTION_INFO_V1(bt_index_check);
+PG_FUNCTION_INFO_V1(bt_index_parent_check);
+PG_FUNCTION_INFO_V1(verify_btreeam);
+
/*
* A B-Tree cannot possibly have this many levels, since there must be one
* block per level, which is bound by the range of BlockNumber:
@@ -50,6 +56,20 @@ PG_MODULE_MAGIC;
#define BTreeTupleGetNKeyAtts(itup, rel) \
Min(IndexRelationGetNumberOfKeyAttributes(rel), BTreeTupleGetNAtts(itup, rel))
+/*
+ * Context for use within verify_btreeam()
+ */
+typedef struct BtreeCheckContext
+{
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ bool is_corrupt;
+ bool on_error_stop;
+} BtreeCheckContext;
+
+#define CONTINUE_CHECKING(ctx) \
+ (ctx == NULL || !((ctx)->is_corrupt && (ctx)->on_error_stop))
+
/*
* State associated with verifying a B-Tree index
*
@@ -116,6 +136,9 @@ typedef struct BtreeCheckState
bloom_filter *filter;
/* Debug counter */
int64 heaptuplespresent;
+
+ /* Error reporting context */
+ BtreeCheckContext *ctx;
} BtreeCheckState;
/*
@@ -133,28 +156,28 @@ typedef struct BtreeLevel
bool istruerootlevel;
} BtreeLevel;
-PG_FUNCTION_INFO_V1(bt_index_check);
-PG_FUNCTION_INFO_V1(bt_index_parent_check);
-
static void bt_index_check_internal(Oid indrelid, bool parentcheck,
- bool heapallindexed, bool rootdescend);
+ bool heapallindexed, bool rootdescend,
+ BtreeCheckContext * ctx);
static inline void btree_index_checkable(Relation rel);
static inline bool btree_index_mainfork_expected(Relation rel);
static void bt_check_every_level(Relation rel, Relation heaprel,
bool heapkeyspace, bool readonly, bool heapallindexed,
- bool rootdescend);
+ bool rootdescend, BtreeCheckContext * ctx);
static BtreeLevel bt_check_level_from_leftmost(BtreeCheckState *state,
- BtreeLevel level);
-static void bt_target_page_check(BtreeCheckState *state);
-static BTScanInsert bt_right_page_check_scankey(BtreeCheckState *state);
+ BtreeLevel level, BtreeCheckContext * ctx);
+static void bt_target_page_check(BtreeCheckState *state, BtreeCheckContext * ctx);
+static BTScanInsert bt_right_page_check_scankey(BtreeCheckState *state, BtreeCheckContext * ctx);
static void bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
- OffsetNumber downlinkoffnum);
+ OffsetNumber downlinkoffnum, BtreeCheckContext * ctx);
static void bt_child_highkey_check(BtreeCheckState *state,
OffsetNumber target_downlinkoffnum,
Page loaded_child,
- uint32 target_level);
+ uint32 target_level,
+ BtreeCheckContext * ctx);
static void bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
- BlockNumber targetblock, Page target);
+ BlockNumber targetblock, Page target,
+ BtreeCheckContext * ctx);
static void bt_tuple_present_callback(Relation index, ItemPointer tid,
Datum *values, bool *isnull,
bool tupleIsAlive, void *checkstate);
@@ -176,7 +199,7 @@ static inline bool invariant_l_nontarget_offset(BtreeCheckState *state,
BlockNumber nontargetblock,
Page nontarget,
OffsetNumber upperbound);
-static Page palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum);
+static Page palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum, BtreeCheckContext * ctx);
static inline BTScanInsert bt_mkscankey_pivotsearch(Relation rel,
IndexTuple itup);
static ItemId PageGetItemIdCareful(BtreeCheckState *state, BlockNumber block,
@@ -185,6 +208,26 @@ static inline ItemPointer BTreeTupleGetHeapTIDCareful(BtreeCheckState *state,
IndexTuple itup, bool nonpivot);
static inline ItemPointer BTreeTupleGetPointsToTID(IndexTuple itup);
+static TupleDesc verify_btreeam_tupdesc(void);
+static void confess(BtreeCheckContext * ctx, BlockNumber blkno, char *msg);
+
+/*
+ * Macro for either calling ereport(...) or confess(...) depending on whether
+ * a context for returning the error message exists. Prior to version 1.3,
+ * all functions reported any detected corruption via ereport, but starting in
+ * 1.3, the new function verify_btreeam reports detected corruption back to
+ * the caller as a set of rows, and pre-existing functions continue to report
+ * corruption via ereport. This macro allows the shared implementation to
+ * to do the right thing depending on context.
+ */
+#define econfess(ctx, blkno, code, ...) \
+ do { \
+ if (ctx) \
+ confess(ctx, blkno, psprintf(__VA_ARGS__)); \
+ else \
+ ereport(ERROR, (errcode(code), errmsg(__VA_ARGS__))); \
+ } while(0)
+
/*
* bt_index_check(index regclass, heapallindexed boolean)
*
@@ -203,7 +246,7 @@ bt_index_check(PG_FUNCTION_ARGS)
if (PG_NARGS() == 2)
heapallindexed = PG_GETARG_BOOL(1);
- bt_index_check_internal(indrelid, false, heapallindexed, false);
+ bt_index_check_internal(indrelid, false, heapallindexed, false, NULL);
PG_RETURN_VOID();
}
@@ -229,17 +272,64 @@ bt_index_parent_check(PG_FUNCTION_ARGS)
if (PG_NARGS() == 3)
rootdescend = PG_GETARG_BOOL(2);
- bt_index_check_internal(indrelid, true, heapallindexed, rootdescend);
+ bt_index_check_internal(indrelid, true, heapallindexed, rootdescend, NULL);
PG_RETURN_VOID();
}
+Datum
+verify_btreeam(PG_FUNCTION_ARGS)
+{
+#define BTREECHECK_RELATION_COLS 2
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ MemoryContext oldcontext;
+ BtreeCheckContext ctx;
+ bool randomAccess;
+ Oid indrelid;
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("materialize mode required, but it is not allowed in this context")));
+
+ /* check supplied arguments */
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("missing required parameter for 'rel'")));
+ indrelid = PG_GETARG_OID(0);
+
+ memset(&ctx, 0, sizeof(BtreeCheckContext));
+
+ ctx.on_error_stop = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
+
+ /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
+ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
+ randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
+ ctx.tupdesc = verify_btreeam_tupdesc();
+ ctx.tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = ctx.tupstore;
+ rsinfo->setDesc = ctx.tupdesc;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ bt_index_check_internal(indrelid, true, true, true, &ctx);
+
+ PG_RETURN_NULL();
+}
+
/*
* Helper for bt_index_[parent_]check, coordinating the bulk of the work.
*/
static void
bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed,
- bool rootdescend)
+ bool rootdescend, BtreeCheckContext * ctx)
{
Oid heapid;
Relation indrel;
@@ -300,15 +390,16 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed,
RelationOpenSmgr(indrel);
if (!smgrexists(indrel->rd_smgr, MAIN_FORKNUM))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("index \"%s\" lacks a main relation fork",
- RelationGetRelationName(indrel))));
+ econfess(ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "index \"%s\" lacks a main relation fork",
+ RelationGetRelationName(indrel));
/* Check index, possibly against table it is an index on */
- _bt_metaversion(indrel, &heapkeyspace, &allequalimage);
- bt_check_every_level(indrel, heaprel, heapkeyspace, parentcheck,
- heapallindexed, rootdescend);
+ if (CONTINUE_CHECKING(ctx))
+ _bt_metaversion(indrel, &heapkeyspace, &allequalimage);
+ if (CONTINUE_CHECKING(ctx))
+ bt_check_every_level(indrel, heaprel, heapkeyspace, parentcheck,
+ heapallindexed, rootdescend, ctx);
}
/*
@@ -402,7 +493,8 @@ btree_index_mainfork_expected(Relation rel)
*/
static void
bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
- bool readonly, bool heapallindexed, bool rootdescend)
+ bool readonly, bool heapallindexed, bool rootdescend,
+ BtreeCheckContext * ctx)
{
BtreeCheckState *state;
Page metapage;
@@ -434,6 +526,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
state->readonly = readonly;
state->heapallindexed = heapallindexed;
state->rootdescend = rootdescend;
+ state->ctx = ctx;
if (state->heapallindexed)
{
@@ -506,7 +599,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
state->checkstrategy = GetAccessStrategy(BAS_BULKREAD);
/* Get true root block from meta-page */
- metapage = palloc_btree_page(state, BTREE_METAPAGE);
+ metapage = palloc_btree_page(state, BTREE_METAPAGE, ctx);
metad = BTPageGetMeta(metapage);
/*
@@ -535,19 +628,18 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
current.level = metad->btm_level;
current.leftmost = metad->btm_root;
current.istruerootlevel = true;
- while (current.leftmost != P_NONE)
+ while (CONTINUE_CHECKING(state->ctx) && current.leftmost != P_NONE)
{
/*
* Verify this level, and get left most page for next level down, if
* not at leaf level
*/
- current = bt_check_level_from_leftmost(state, current);
+ current = bt_check_level_from_leftmost(state, current, ctx);
if (current.leftmost == InvalidBlockNumber)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("index \"%s\" has no valid pages on level below %u or first level",
- RelationGetRelationName(rel), previouslevel)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "index \"%s\" has no valid pages on level below %u or first level",
+ RelationGetRelationName(rel), previouslevel);
previouslevel = current.level;
}
@@ -555,7 +647,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
/*
* * Check whether heap contains unindexed/malformed tuples *
*/
- if (state->heapallindexed)
+ if (CONTINUE_CHECKING(state->ctx) && state->heapallindexed)
{
IndexInfo *indexinfo = BuildIndexInfo(state->rel);
TableScanDesc scan;
@@ -639,7 +731,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
* each call to bt_target_page_check().
*/
static BtreeLevel
-bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level)
+bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level, BtreeCheckContext * ctx)
{
/* State to establish early, concerning entire level */
BTPageOpaque opaque;
@@ -672,7 +764,7 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level)
/* Initialize state for this iteration */
state->targetblock = current;
- state->target = palloc_btree_page(state, state->targetblock);
+ state->target = palloc_btree_page(state, state->targetblock, ctx);
state->targetlsn = PageGetLSN(state->target);
opaque = (BTPageOpaque) PageGetSpecialPointer(state->target);
@@ -691,18 +783,16 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level)
* checked.
*/
if (state->readonly && P_ISDELETED(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("downlink or sibling link points to deleted block in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Block=%u left block=%u left link from block=%u.",
- current, leftcurrent, opaque->btpo_prev)));
+ econfess(state->ctx, current, ERRCODE_INDEX_CORRUPTED,
+ "downlink or sibling link points to deleted block in index \"%s\" "
+ "(Block=%u left block=%u left link from block=%u)",
+ RelationGetRelationName(state->rel),
+ current, leftcurrent, opaque->btpo_prev);
if (P_RIGHTMOST(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("block %u fell off the end of index \"%s\"",
- current, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, current, ERRCODE_INDEX_CORRUPTED,
+ "block %u fell off the end of index \"%s\"",
+ current, RelationGetRelationName(state->rel));
else
ereport(DEBUG1,
(errcode(ERRCODE_NO_DATA),
@@ -722,16 +812,14 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level)
if (state->readonly)
{
if (!P_LEFTMOST(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("block %u is not leftmost in index \"%s\"",
- current, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, current, ERRCODE_INDEX_CORRUPTED,
+ "block %u is not leftmost in index \"%s\"",
+ current, RelationGetRelationName(state->rel));
if (level.istruerootlevel && !P_ISROOT(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("block %u is not true root in index \"%s\"",
- current, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, current, ERRCODE_INDEX_CORRUPTED,
+ "block %u is not true root in index \"%s\"",
+ current, RelationGetRelationName(state->rel));
}
/*
@@ -780,33 +868,30 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level)
* so sibling pointers should always be in mutual agreement
*/
if (state->readonly && opaque->btpo_prev != leftcurrent)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("left link/right link pair in index \"%s\" not in agreement",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Block=%u left block=%u left link from block=%u.",
- current, leftcurrent, opaque->btpo_prev)));
+ econfess(state->ctx, current, ERRCODE_INDEX_CORRUPTED,
+ "left link/right link pair in index \"%s\" not in agreement "
+ "(Block=%u left block=%u left link from block=%u)",
+ RelationGetRelationName(state->rel),
+ current, leftcurrent, opaque->btpo_prev);
/* Check level, which must be valid for non-ignorable page */
if (level.level != opaque->btpo.level)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("leftmost down link for level points to block in index \"%s\" whose level is not one level down",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Block pointed to=%u expected level=%u level in pointed to block=%u.",
- current, level.level, opaque->btpo.level)));
+ econfess(state->ctx, current, ERRCODE_INDEX_CORRUPTED,
+ "leftmost down link for level points to block in index \"%s\" whose level is not one level down "
+ "(Block pointed to=%u expected level=%u level in pointed to block=%u)",
+ RelationGetRelationName(state->rel),
+ current, level.level, opaque->btpo.level);
/* Verify invariants for page */
- bt_target_page_check(state);
+ bt_target_page_check(state, ctx);
nextpage:
/* Try to detect circular links */
if (current == leftcurrent || current == opaque->btpo_prev)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("circular link chain found in block %u of index \"%s\"",
- current, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, current, ERRCODE_INDEX_CORRUPTED,
+ "circular link chain found in block %u of index \"%s\"",
+ current, RelationGetRelationName(state->rel));
leftcurrent = current;
current = opaque->btpo_next;
@@ -850,7 +935,7 @@ nextpage:
/* Free page and associated memory for this iteration */
MemoryContextReset(state->targetcontext);
}
- while (current != P_NONE);
+ while (CONTINUE_CHECKING(state->ctx) && current != P_NONE);
if (state->lowkey)
{
@@ -902,7 +987,7 @@ nextpage:
* resetting state->targetcontext.
*/
static void
-bt_target_page_check(BtreeCheckState *state)
+bt_target_page_check(BtreeCheckState *state, BtreeCheckContext * ctx)
{
OffsetNumber offset;
OffsetNumber max;
@@ -930,16 +1015,15 @@ bt_target_page_check(BtreeCheckState *state)
P_HIKEY))
{
itup = (IndexTuple) PageGetItem(state->target, itemid);
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("wrong number of high key index tuple attributes in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index block=%u natts=%u block type=%s page lsn=%X/%X.",
- state->targetblock,
- BTreeTupleGetNAtts(itup, state->rel),
- P_ISLEAF(topaque) ? "heap" : "index",
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "wrong number of high key index tuple attributes in index \"%s\" "
+ "(Index block=%u natts=%u block type=%s page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock,
+ BTreeTupleGetNAtts(itup, state->rel),
+ P_ISLEAF(topaque) ? "heap" : "index",
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
}
@@ -949,7 +1033,7 @@ bt_target_page_check(BtreeCheckState *state)
* real item (if any).
*/
for (offset = P_FIRSTDATAKEY(topaque);
- offset <= max;
+ offset <= max && CONTINUE_CHECKING(state->ctx);
offset = OffsetNumberNext(offset))
{
ItemId itemid;
@@ -973,16 +1057,15 @@ bt_target_page_check(BtreeCheckState *state)
* frequently, and is surprisingly tolerant of corrupt lp_len fields.
*/
if (tupsize != ItemIdGetLength(itemid))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("index tuple size does not equal lp_len in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=(%u,%u) tuple size=%zu lp_len=%u page lsn=%X/%X.",
- state->targetblock, offset,
- tupsize, ItemIdGetLength(itemid),
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn),
- errhint("This could be a torn page problem.")));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "index tuple size does not equal lp_len in index \"%s\" "
+ "(Index tid=(%u,%u) tuple size=%zu lp_len=%u page lsn=%X/%X) "
+ "(This could be a torn page problem)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, offset,
+ tupsize, ItemIdGetLength(itemid),
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
/* Check the number of index tuple attributes */
if (!_bt_check_natts(state->rel, state->heapkeyspace, state->target,
@@ -998,17 +1081,16 @@ bt_target_page_check(BtreeCheckState *state)
ItemPointerGetBlockNumberNoCheck(tid),
ItemPointerGetOffsetNumberNoCheck(tid));
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("wrong number of index tuple attributes in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=%s natts=%u points to %s tid=%s page lsn=%X/%X.",
- itid,
- BTreeTupleGetNAtts(itup, state->rel),
- P_ISLEAF(topaque) ? "heap" : "index",
- htid,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "wrong number of index tuple attributes in index \"%s\" "
+ "(Index tid=%s natts=%u points to %s tid=%s page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ itid,
+ BTreeTupleGetNAtts(itup, state->rel),
+ P_ISLEAF(topaque) ? "heap" : "index",
+ htid,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
/*
@@ -1027,7 +1109,8 @@ bt_target_page_check(BtreeCheckState *state)
bt_child_highkey_check(state,
offset,
NULL,
- topaque->btpo.level);
+ topaque->btpo.level,
+ ctx);
}
continue;
}
@@ -1049,14 +1132,13 @@ bt_target_page_check(BtreeCheckState *state)
htid = psprintf("(%u,%u)", ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid));
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("could not find tuple using search from root page in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=%s points to heap tid=%s page lsn=%X/%X.",
- itid, htid,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "could not find tuple using search from root page in index \"%s\" "
+ "(Index tid=%s points to heap tid=%s page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ itid, htid,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
/*
@@ -1079,14 +1161,13 @@ bt_target_page_check(BtreeCheckState *state)
{
char *itid = psprintf("(%u,%u)", state->targetblock, offset);
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg_internal("posting list contains misplaced TID in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=%s posting list offset=%d page lsn=%X/%X.",
- itid, i,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "posting list contains misplaced TID in index \"%s\" "
+ "(Index tid=%s posting list offset=%d page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ itid, i,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
ItemPointerCopy(current, &last);
@@ -1134,16 +1215,15 @@ bt_target_page_check(BtreeCheckState *state)
ItemPointerGetBlockNumberNoCheck(tid),
ItemPointerGetOffsetNumberNoCheck(tid));
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("index row size %zu exceeds maximum for index \"%s\"",
- tupsize, RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=%s points to %s tid=%s page lsn=%X/%X.",
- itid,
- P_ISLEAF(topaque) ? "heap" : "index",
- htid,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "index row size %zu exceeds maximum for index \"%s\" "
+ "(Index tid=%s points to %s tid=%s page lsn=%X/%X)",
+ tupsize, RelationGetRelationName(state->rel),
+ itid,
+ P_ISLEAF(topaque) ? "heap" : "index",
+ htid,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
/* Fingerprint leaf page tuples (those that point to the heap) */
@@ -1242,16 +1322,15 @@ bt_target_page_check(BtreeCheckState *state)
ItemPointerGetBlockNumberNoCheck(tid),
ItemPointerGetOffsetNumberNoCheck(tid));
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("high key invariant violated for index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=%s points to %s tid=%s page lsn=%X/%X.",
- itid,
- P_ISLEAF(topaque) ? "heap" : "index",
- htid,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "high key invariant violated for index \"%s\" "
+ "(Index tid=%s points to %s tid=%s page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ itid,
+ P_ISLEAF(topaque) ? "heap" : "index",
+ htid,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
/* Reset, in case scantid was set to (itup) posting tuple's max TID */
skey->scantid = scantid;
@@ -1289,21 +1368,20 @@ bt_target_page_check(BtreeCheckState *state)
ItemPointerGetBlockNumberNoCheck(tid),
ItemPointerGetOffsetNumberNoCheck(tid));
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("item order invariant violated for index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Lower index tid=%s (points to %s tid=%s) "
- "higher index tid=%s (points to %s tid=%s) "
- "page lsn=%X/%X.",
- itid,
- P_ISLEAF(topaque) ? "heap" : "index",
- htid,
- nitid,
- P_ISLEAF(topaque) ? "heap" : "index",
- nhtid,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "item order invariant violated for index \"%s\" "
+ "(Lower index tid=%s (points to %s tid=%s) "
+ "higher index tid=%s (points to %s tid=%s) "
+ "page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ itid,
+ P_ISLEAF(topaque) ? "heap" : "index",
+ htid,
+ nitid,
+ P_ISLEAF(topaque) ? "heap" : "index",
+ nhtid,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
/*
@@ -1328,7 +1406,7 @@ bt_target_page_check(BtreeCheckState *state)
BTScanInsert rightkey;
/* Get item in next/right page */
- rightkey = bt_right_page_check_scankey(state);
+ rightkey = bt_right_page_check_scankey(state, ctx);
if (rightkey &&
!invariant_g_offset(state, rightkey, max))
@@ -1343,7 +1421,7 @@ bt_target_page_check(BtreeCheckState *state)
if (!state->readonly)
{
/* Get fresh copy of target page */
- state->target = palloc_btree_page(state, state->targetblock);
+ state->target = palloc_btree_page(state, state->targetblock, ctx);
/* Note that we deliberately do not update target LSN */
topaque = (BTPageOpaque) PageGetSpecialPointer(state->target);
@@ -1354,14 +1432,13 @@ bt_target_page_check(BtreeCheckState *state)
return;
}
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("cross page item order invariant violated for index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Last item on page tid=(%u,%u) page lsn=%X/%X.",
- state->targetblock, offset,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "cross page item order invariant violated for index \"%s\" "
+ "(Last item on page tid=(%u,%u) page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, offset,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
}
@@ -1374,7 +1451,7 @@ bt_target_page_check(BtreeCheckState *state)
* because it has no useful value to compare).
*/
if (!P_ISLEAF(topaque) && state->readonly)
- bt_child_check(state, skey, offset);
+ bt_child_check(state, skey, offset, ctx);
}
/*
@@ -1386,10 +1463,11 @@ bt_target_page_check(BtreeCheckState *state)
* right of the child page pointer to by our rightmost downlink. And they
* might have missing downlinks. This final call checks for them.
*/
- if (!P_ISLEAF(topaque) && P_RIGHTMOST(topaque) && state->readonly)
+ if (CONTINUE_CHECKING(state->ctx) &&
+ !P_ISLEAF(topaque) && P_RIGHTMOST(topaque) && state->readonly)
{
bt_child_highkey_check(state, InvalidOffsetNumber,
- NULL, topaque->btpo.level);
+ NULL, topaque->btpo.level, ctx);
}
}
@@ -1410,7 +1488,7 @@ bt_target_page_check(BtreeCheckState *state)
* been concurrently deleted.
*/
static BTScanInsert
-bt_right_page_check_scankey(BtreeCheckState *state)
+bt_right_page_check_scankey(BtreeCheckState *state, BtreeCheckContext * ctx)
{
BTPageOpaque opaque;
ItemId rightitem;
@@ -1455,7 +1533,7 @@ bt_right_page_check_scankey(BtreeCheckState *state)
{
CHECK_FOR_INTERRUPTS();
- rightpage = palloc_btree_page(state, targetnext);
+ rightpage = palloc_btree_page(state, targetnext, ctx);
opaque = (BTPageOpaque) PageGetSpecialPointer(rightpage);
if (!P_IGNORE(opaque) || P_RIGHTMOST(opaque))
@@ -1666,7 +1744,8 @@ static void
bt_child_highkey_check(BtreeCheckState *state,
OffsetNumber target_downlinkoffnum,
Page loaded_child,
- uint32 target_level)
+ uint32 target_level,
+ BtreeCheckContext * ctx)
{
BlockNumber blkno = state->prevrightlink;
Page page;
@@ -1708,7 +1787,7 @@ bt_child_highkey_check(BtreeCheckState *state,
}
/* Move to the right on the child level */
- while (true)
+ while (CONTINUE_CHECKING(state->ctx))
{
/*
* Did we traverse the whole tree level and this is check for pages to
@@ -1723,51 +1802,47 @@ bt_child_highkey_check(BtreeCheckState *state,
/* Did we traverse the whole tree level and don't find next downlink? */
if (blkno == P_NONE)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("can't traverse from downlink %u to downlink %u of index \"%s\"",
- state->prevrightlink, downlink,
- RelationGetRelationName(state->rel))));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "can't traverse from downlink %u to downlink %u of index \"%s\"",
+ state->prevrightlink, downlink,
+ RelationGetRelationName(state->rel));
/* Load page contents */
if (blkno == downlink && loaded_child)
page = loaded_child;
else
- page = palloc_btree_page(state, blkno);
+ page = palloc_btree_page(state, blkno, ctx);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* The first page we visit at the level should be leftmost */
if (first && !BlockNumberIsValid(state->prevrightlink) && !P_LEFTMOST(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("the first child of leftmost target page is not leftmost of its level in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
- state->targetblock, blkno,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "the first child of leftmost target page is not leftmost of its level in index \"%s\" "
+ "(Target block=%u child block=%u target page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, blkno,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
/* Check level for non-ignorable page */
if (!P_IGNORE(opaque) && opaque->btpo.level != target_level - 1)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("block found while following rightlinks from child of index \"%s\" has invalid level",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Block pointed to=%u expected level=%u level in pointed to block=%u.",
- blkno, target_level - 1, opaque->btpo.level)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "block found while following rightlinks from child of index \"%s\" has invalid level "
+ "(Block pointed to=%u expected level=%u level in pointed to block=%u)",
+ RelationGetRelationName(state->rel),
+ blkno, target_level - 1, opaque->btpo.level);
/* Try to detect circular links */
if ((!first && blkno == state->prevrightlink) || blkno == opaque->btpo_prev)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("circular link chain found in block %u of index \"%s\"",
- blkno, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "circular link chain found in block %u of index \"%s\"",
+ blkno, RelationGetRelationName(state->rel));
if (blkno != downlink && !P_IGNORE(opaque))
{
/* blkno probably has missing parent downlink */
- bt_downlink_missing_check(state, rightsplit, blkno, page);
+ bt_downlink_missing_check(state, rightsplit, blkno, page, ctx);
}
rightsplit = P_INCOMPLETE_SPLIT(opaque);
@@ -1825,14 +1900,13 @@ bt_child_highkey_check(BtreeCheckState *state,
if (pivotkey_offset > PageGetMaxOffsetNumber(state->target))
{
if (P_RIGHTMOST(topaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("child high key is greater than rightmost pivot key on target level in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
- state->targetblock, blkno,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "child high key is greater than rightmost pivot key on target level in index \"%s\" "
+ "(Target block=%u child block=%u target page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, blkno,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
pivotkey_offset = P_HIKEY;
}
itemid = PageGetItemIdCareful(state, state->targetblock,
@@ -1856,27 +1930,25 @@ bt_child_highkey_check(BtreeCheckState *state,
* page.
*/
if (!state->lowkey)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("can't find left sibling high key in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
- state->targetblock, blkno,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "can't find left sibling high key in index \"%s\" "
+ "(Target block=%u child block=%u target page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, blkno,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
itup = state->lowkey;
}
if (!bt_pivot_tuple_identical(highkey, itup))
{
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("mismatch between parent key and child high key in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
- state->targetblock, blkno,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "mismatch between parent key and child high key in index \"%s\" "
+ "(Target block=%u child block=%u target page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, blkno,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
}
@@ -1913,7 +1985,7 @@ bt_child_highkey_check(BtreeCheckState *state,
*/
static void
bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
- OffsetNumber downlinkoffnum)
+ OffsetNumber downlinkoffnum, BtreeCheckContext * ctx)
{
ItemId itemid;
IndexTuple itup;
@@ -1978,7 +2050,7 @@ bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
* the operator class obeys the transitive law.
*/
topaque = (BTPageOpaque) PageGetSpecialPointer(state->target);
- child = palloc_btree_page(state, childblock);
+ child = palloc_btree_page(state, childblock, ctx);
copaque = (BTPageOpaque) PageGetSpecialPointer(child);
maxoffset = PageGetMaxOffsetNumber(child);
@@ -1987,7 +2059,7 @@ bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
* check for downlink connectivity.
*/
bt_child_highkey_check(state, downlinkoffnum,
- child, topaque->btpo.level);
+ child, topaque->btpo.level, ctx);
/*
* Since there cannot be a concurrent VACUUM operation in readonly mode,
@@ -2014,17 +2086,16 @@ bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
* to test.
*/
if (P_ISDELETED(copaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("downlink to deleted page found in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Parent block=%u child block=%u parent page lsn=%X/%X.",
- state->targetblock, childblock,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "downlink to deleted page found in index \"%s\" "
+ "(Parent block=%u child block=%u parent page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, childblock,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
for (offset = P_FIRSTDATAKEY(copaque);
- offset <= maxoffset;
+ offset <= maxoffset && CONTINUE_CHECKING(state->ctx);
offset = OffsetNumberNext(offset))
{
/*
@@ -2056,14 +2127,13 @@ bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
if (!invariant_l_nontarget_offset(state, targetkey, childblock, child,
offset))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("down-link lower bound invariant violated for index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Parent block=%u child index tid=(%u,%u) parent page lsn=%X/%X.",
- state->targetblock, childblock, offset,
- (uint32) (state->targetlsn >> 32),
- (uint32) state->targetlsn)));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "down-link lower bound invariant violated for index \"%s\" "
+ "(Parent block=%u child index tid=(%u,%u) parent page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ state->targetblock, childblock, offset,
+ (uint32) (state->targetlsn >> 32),
+ (uint32) state->targetlsn);
}
pfree(child);
@@ -2084,7 +2154,7 @@ bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
*/
static void
bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
- BlockNumber blkno, Page page)
+ BlockNumber blkno, Page page, BtreeCheckContext * ctx)
{
BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
ItemId itemid;
@@ -2150,14 +2220,13 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
* inconsistencies anywhere else.
*/
if (P_ISLEAF(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("leaf index block lacks downlink in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Block=%u page lsn=%X/%X.",
- blkno,
- (uint32) (pagelsn >> 32),
- (uint32) pagelsn)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "leaf index block lacks downlink in index \"%s\" "
+ "(Block=%u page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ blkno,
+ (uint32) (pagelsn >> 32),
+ (uint32) pagelsn);
/* Descend from the given page, which is an internal page */
elog(DEBUG1, "checking for interrupted multi-level deletion due to missing downlink in index \"%s\"",
@@ -2167,11 +2236,11 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
itemid = PageGetItemIdCareful(state, blkno, page, P_FIRSTDATAKEY(opaque));
itup = (IndexTuple) PageGetItem(page, itemid);
childblk = BTreeTupleGetDownLink(itup);
- for (;;)
+ while (CONTINUE_CHECKING(state->ctx))
{
CHECK_FOR_INTERRUPTS();
- child = palloc_btree_page(state, childblk);
+ child = palloc_btree_page(state, childblk, ctx);
copaque = (BTPageOpaque) PageGetSpecialPointer(child);
if (P_ISLEAF(copaque))
@@ -2179,13 +2248,12 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
/* Do an extra sanity check in passing on internal pages */
if (copaque->btpo.level != level - 1)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg_internal("downlink points to block in index \"%s\" whose level is not one level down",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Top parent/under check block=%u block pointed to=%u expected level=%u level in pointed to block=%u.",
- blkno, childblk,
- level - 1, copaque->btpo.level)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "downlink points to block in index \"%s\" whose level is not one level down "
+ "(Top parent/under check block=%u block pointed to=%u expected level=%u level in pointed to block=%u)",
+ RelationGetRelationName(state->rel),
+ blkno, childblk,
+ level - 1, copaque->btpo.level);
level = copaque->btpo.level;
itemid = PageGetItemIdCareful(state, childblk, child,
@@ -2217,14 +2285,13 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
* parent/ancestor page) lacked a downlink is incidental.
*/
if (P_ISDELETED(copaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg_internal("downlink to deleted leaf page found in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Top parent/target block=%u leaf block=%u top parent/under check lsn=%X/%X.",
- blkno, childblk,
- (uint32) (pagelsn >> 32),
- (uint32) pagelsn)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "downlink to deleted leaf page found in index \"%s\" "
+ "(Top parent/target block=%u leaf block=%u top parent/under check lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ blkno, childblk,
+ (uint32) (pagelsn >> 32),
+ (uint32) pagelsn);
/*
* Iff leaf page is half-dead, its high key top parent link should point
@@ -2244,14 +2311,13 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
return;
}
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("internal index block lacks downlink in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Block=%u level=%u page lsn=%X/%X.",
- blkno, opaque->btpo.level,
- (uint32) (pagelsn >> 32),
- (uint32) pagelsn)));
+ econfess(state->ctx, blkno, ERRCODE_INDEX_CORRUPTED,
+ "internal index block lacks downlink in index \"%s\" "
+ "(Block=%u level=%u page lsn=%X/%X)",
+ RelationGetRelationName(state->rel),
+ blkno, opaque->btpo.level,
+ (uint32) (pagelsn >> 32),
+ (uint32) pagelsn);
}
/*
@@ -2327,16 +2393,12 @@ bt_tuple_present_callback(Relation index, ItemPointer tid, Datum *values,
/* Probe Bloom filter -- tuple should be present */
if (bloom_lacks_element(state->filter, (unsigned char *) norm,
IndexTupleSize(norm)))
- ereport(ERROR,
- (errcode(ERRCODE_DATA_CORRUPTED),
- errmsg("heap tuple (%u,%u) from table \"%s\" lacks matching index tuple within index \"%s\"",
- ItemPointerGetBlockNumber(&(itup->t_tid)),
- ItemPointerGetOffsetNumber(&(itup->t_tid)),
- RelationGetRelationName(state->heaprel),
- RelationGetRelationName(state->rel)),
- !state->readonly
- ? errhint("Retrying verification using the function bt_index_parent_check() might provide a more specific error.")
- : 0));
+ econfess(state->ctx, ItemPointerGetBlockNumber(&(itup->t_tid)), ERRCODE_DATA_CORRUPTED,
+ "heap tuple (%u,%u) from table \"%s\" lacks matching index tuple within index \"%s\"",
+ ItemPointerGetBlockNumber(&(itup->t_tid)),
+ ItemPointerGetOffsetNumber(&(itup->t_tid)),
+ RelationGetRelationName(state->heaprel),
+ RelationGetRelationName(state->rel));
state->heaptuplespresent++;
pfree(itup);
@@ -2395,7 +2457,7 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup)
if (!IndexTupleHasVarwidths(itup))
return itup;
- for (i = 0; i < tupleDescriptor->natts; i++)
+ for (i = 0; CONTINUE_CHECKING(state->ctx) && i < tupleDescriptor->natts; i++)
{
Form_pg_attribute att;
@@ -2415,12 +2477,11 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup)
* should never be encountered here
*/
if (VARATT_IS_EXTERNAL(DatumGetPointer(normalized[i])))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("external varlena datum in tuple that references heap row (%u,%u) in index \"%s\"",
- ItemPointerGetBlockNumber(&(itup->t_tid)),
- ItemPointerGetOffsetNumber(&(itup->t_tid)),
- RelationGetRelationName(state->rel))));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "external varlena datum in tuple that references heap row (%u,%u) in index \"%s\"",
+ ItemPointerGetBlockNumber(&(itup->t_tid)),
+ ItemPointerGetOffsetNumber(&(itup->t_tid)),
+ RelationGetRelationName(state->rel));
else if (VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])))
{
formnewtup = true;
@@ -2780,7 +2841,7 @@ invariant_l_nontarget_offset(BtreeCheckState *state, BTScanInsert key,
* misbehaves.
*/
static Page
-palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
+palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum, BtreeCheckContext * ctx)
{
Buffer buffer;
Page page;
@@ -2810,10 +2871,9 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (P_ISMETA(opaque) && blocknum != BTREE_METAPAGE)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("invalid meta page found at block %u in index \"%s\"",
- blocknum, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, blocknum, ERRCODE_INDEX_CORRUPTED,
+ "invalid meta page found at block %u in index \"%s\"",
+ blocknum, RelationGetRelationName(state->rel));
/* Check page from block that ought to be meta page */
if (blocknum == BTREE_METAPAGE)
@@ -2822,20 +2882,18 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
if (!P_ISMETA(opaque) ||
metad->btm_magic != BTREE_MAGIC)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("index \"%s\" meta page is corrupt",
- RelationGetRelationName(state->rel))));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "index \"%s\" meta page is corrupt",
+ RelationGetRelationName(state->rel));
if (metad->btm_version < BTREE_MIN_VERSION ||
metad->btm_version > BTREE_VERSION)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("version mismatch in index \"%s\": file version %d, "
- "current version %d, minimum supported version %d",
- RelationGetRelationName(state->rel),
- metad->btm_version, BTREE_VERSION,
- BTREE_MIN_VERSION)));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "version mismatch in index \"%s\": file version %d, "
+ "current version %d, minimum supported version %d",
+ RelationGetRelationName(state->rel),
+ metad->btm_version, BTREE_VERSION,
+ BTREE_MIN_VERSION);
/* Finished with metapage checks */
return page;
@@ -2846,17 +2904,15 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
* page level
*/
if (P_ISLEAF(opaque) && !P_ISDELETED(opaque) && opaque->btpo.level != 0)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("invalid leaf page level %u for block %u in index \"%s\"",
- opaque->btpo.level, blocknum, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, InvalidBlockNumber, ERRCODE_INDEX_CORRUPTED,
+ "invalid leaf page level %u for block %u in index \"%s\"",
+ opaque->btpo.level, blocknum, RelationGetRelationName(state->rel));
if (!P_ISLEAF(opaque) && !P_ISDELETED(opaque) &&
opaque->btpo.level == 0)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("invalid internal page level 0 for block %u in index \"%s\"",
- blocknum, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, blocknum, ERRCODE_INDEX_CORRUPTED,
+ "invalid internal page level 0 for block %u in index \"%s\"",
+ blocknum, RelationGetRelationName(state->rel));
/*
* Sanity checks for number of items on page.
@@ -2882,23 +2938,20 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
*/
maxoffset = PageGetMaxOffsetNumber(page);
if (maxoffset > MaxIndexTuplesPerPage)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("Number of items on block %u of index \"%s\" exceeds MaxIndexTuplesPerPage (%u)",
- blocknum, RelationGetRelationName(state->rel),
- MaxIndexTuplesPerPage)));
+ econfess(ctx, blocknum, ERRCODE_INDEX_CORRUPTED,
+ "Number of items on block %u of index \"%s\" exceeds MaxIndexTuplesPerPage (%u)",
+ blocknum, RelationGetRelationName(state->rel),
+ MaxIndexTuplesPerPage);
if (!P_ISLEAF(opaque) && !P_ISDELETED(opaque) && maxoffset < P_FIRSTDATAKEY(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("internal block %u in index \"%s\" lacks high key and/or at least one downlink",
- blocknum, RelationGetRelationName(state->rel))));
+ econfess(ctx, blocknum, ERRCODE_INDEX_CORRUPTED,
+ "internal block %u in index \"%s\" lacks high key and/or at least one downlink",
+ blocknum, RelationGetRelationName(state->rel));
if (P_ISLEAF(opaque) && !P_ISDELETED(opaque) && !P_RIGHTMOST(opaque) && maxoffset < P_HIKEY)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("non-rightmost leaf block %u in index \"%s\" lacks high key item",
- blocknum, RelationGetRelationName(state->rel))));
+ econfess(ctx, blocknum, ERRCODE_INDEX_CORRUPTED,
+ "non-rightmost leaf block %u in index \"%s\" lacks high key item",
+ blocknum, RelationGetRelationName(state->rel));
/*
* In general, internal pages are never marked half-dead, except on
@@ -2910,17 +2963,15 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
* Internal pages should never have garbage items, either.
*/
if (!P_ISLEAF(opaque) && P_ISHALFDEAD(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("internal page block %u in index \"%s\" is half-dead",
- blocknum, RelationGetRelationName(state->rel)),
- errhint("This can be caused by an interrupted VACUUM in version 9.3 or older, before upgrade. Please REINDEX it.")));
+ econfess(state->ctx, blocknum, ERRCODE_INDEX_CORRUPTED,
+ "internal page block %u in index \"%s\" is half-dead "
+ "(This can be caused by an interrupted VACUUM in version 9.3 or older, before upgrade. Please REINDEX it)",
+ blocknum, RelationGetRelationName(state->rel));
if (!P_ISLEAF(opaque) && P_HAS_GARBAGE(opaque))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("internal page block %u in index \"%s\" has garbage items",
- blocknum, RelationGetRelationName(state->rel))));
+ econfess(state->ctx, blocknum, ERRCODE_INDEX_CORRUPTED,
+ "internal page block %u in index \"%s\" has garbage items",
+ blocknum, RelationGetRelationName(state->rel));
return page;
}
@@ -2971,14 +3022,13 @@ PageGetItemIdCareful(BtreeCheckState *state, BlockNumber block, Page page,
if (ItemIdGetOffset(itemid) + ItemIdGetLength(itemid) >
BLCKSZ - sizeof(BTPageOpaqueData))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("line pointer points past end of tuple space in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
- block, offset, ItemIdGetOffset(itemid),
- ItemIdGetLength(itemid),
- ItemIdGetFlags(itemid))));
+ econfess(state->ctx, block, ERRCODE_INDEX_CORRUPTED,
+ "line pointer points past end of tuple space in index \"%s\" "
+ "(Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u)",
+ RelationGetRelationName(state->rel),
+ block, offset, ItemIdGetOffset(itemid),
+ ItemIdGetLength(itemid),
+ ItemIdGetFlags(itemid));
/*
* Verify that line pointer isn't LP_REDIRECT or LP_UNUSED, since nbtree
@@ -2987,14 +3037,13 @@ PageGetItemIdCareful(BtreeCheckState *state, BlockNumber block, Page page,
*/
if (ItemIdIsRedirected(itemid) || !ItemIdIsUsed(itemid) ||
ItemIdGetLength(itemid) == 0)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("invalid line pointer storage in index \"%s\"",
- RelationGetRelationName(state->rel)),
- errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
- block, offset, ItemIdGetOffset(itemid),
- ItemIdGetLength(itemid),
- ItemIdGetFlags(itemid))));
+ econfess(state->ctx, block, ERRCODE_INDEX_CORRUPTED,
+ "invalid line pointer storage in index \"%s\" "
+ "(Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u)",
+ RelationGetRelationName(state->rel),
+ block, offset, ItemIdGetOffset(itemid),
+ ItemIdGetLength(itemid),
+ ItemIdGetFlags(itemid));
return itemid;
}
@@ -3016,26 +3065,23 @@ BTreeTupleGetHeapTIDCareful(BtreeCheckState *state, IndexTuple itup,
*/
Assert(state->heapkeyspace);
if (BTreeTupleIsPivot(itup) && nonpivot)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg_internal("block %u or its right sibling block or child block in index \"%s\" has unexpected pivot tuple",
- state->targetblock,
- RelationGetRelationName(state->rel))));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "block %u or its right sibling block or child block in index \"%s\" has unexpected pivot tuple",
+ state->targetblock,
+ RelationGetRelationName(state->rel));
if (!BTreeTupleIsPivot(itup) && !nonpivot)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg_internal("block %u or its right sibling block or child block in index \"%s\" has unexpected non-pivot tuple",
- state->targetblock,
- RelationGetRelationName(state->rel))));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "block %u or its right sibling block or child block in index \"%s\" has unexpected non-pivot tuple",
+ state->targetblock,
+ RelationGetRelationName(state->rel));
htid = BTreeTupleGetHeapTID(itup);
if (!ItemPointerIsValid(htid) && nonpivot)
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("block %u or its right sibling block or child block in index \"%s\" contains non-pivot tuple that lacks a heap TID",
- state->targetblock,
- RelationGetRelationName(state->rel))));
+ econfess(state->ctx, state->targetblock, ERRCODE_INDEX_CORRUPTED,
+ "block %u or its right sibling block or child block in index \"%s\" contains non-pivot tuple that lacks a heap TID",
+ state->targetblock,
+ RelationGetRelationName(state->rel));
return htid;
}
@@ -3066,3 +3112,53 @@ BTreeTupleGetPointsToTID(IndexTuple itup)
/* Pivot tuple returns TID with downlink block (heapkeyspace variant) */
return &itup->t_tid;
}
+
+/*
+ * Helper function to construct the TupleDesc needed by verify_heapam.
+ */
+static TupleDesc
+verify_btreeam_tupdesc(void)
+{
+ TupleDesc tupdesc;
+ AttrNumber a = 0;
+
+ tupdesc = CreateTemplateTupleDesc(BTREECHECK_RELATION_COLS);
+ TupleDescInitEntry(tupdesc, ++a, "blkno", INT8OID, -1, 0);
+ TupleDescInitEntry(tupdesc, ++a, "msg", TEXTOID, -1, 0);
+ Assert(a == BTREECHECK_RELATION_COLS);
+
+ return BlessTupleDesc(tupdesc);
+}
+
+/*
+ * confess
+ *
+ * Return a message about index corruption
+ *
+ * The msg argument is pfree'd by this function.
+ */
+static void
+confess(BtreeCheckContext * ctx, BlockNumber blkno, char *msg)
+{
+ Datum values[BTREECHECK_RELATION_COLS];
+ bool nulls[BTREECHECK_RELATION_COLS];
+ HeapTuple tuple;
+
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, 0, sizeof(nulls));
+ values[0] = Int64GetDatum(blkno);
+ nulls[0] = (blkno == InvalidBlockNumber);
+ values[1] = CStringGetTextDatum(msg);
+
+ /*
+ * In principle, there is nothing to prevent a scan over a large, highly
+ * corrupted table from using workmem worth of memory building up the
+ * tuplestore. That is OK, but leaves no room for leaking all the msg
+ * arguments that are allocated during the scan.
+ */
+ pfree(msg);
+
+ tuple = heap_form_tuple(ctx->tupdesc, values, nulls);
+ tuplestore_puttuple(ctx->tupstore, tuple);
+ ctx->is_corrupt = true;
+}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 7eaaad1e14..467120f1d0 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -275,6 +275,7 @@ BrinSpecialSpace
BrinStatsData
BrinTuple
BrinValues
+BtreeCheckContext
BtreeCheckState
BtreeLevel
Bucket
--
2.21.1 (Apple Git-122.3)