Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1k06Vr-0005ea-W5 for pgsql-hackers@arkaria.postgresql.org; Mon, 27 Jul 2020 17:02:08 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1k06Vq-0002xq-2g for pgsql-hackers@arkaria.postgresql.org; Mon, 27 Jul 2020 17:02:06 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1k06Vp-0002xj-0P for pgsql-hackers@lists.postgresql.org; Mon, 27 Jul 2020 17:02:05 +0000 Received: from mail-pg1-x541.google.com ([2607:f8b0:4864:20::541]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1k06Vl-0005ed-13 for pgsql-hackers@postgresql.org; Mon, 27 Jul 2020 17:02:03 +0000 Received: by mail-pg1-x541.google.com with SMTP id s189so10061336pgc.13 for ; Mon, 27 Jul 2020 10:02:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=enterprisedb-com.20150623.gappssmtp.com; s=20150623; h=from:message-id:mime-version:subject:date:in-reply-to:cc:to :references; bh=EFtMNW/Q2HwUdxQDPkUnBTAsirLFlHoN6VaktKRjXXM=; b=K8ghx6h/MMP1qmP2CSOJxxLzRs5eyTss5i+W5Y9An8uvnz4f0+LlVUEERs2FXt35Nr 3ur7WhfJ9TrktdwKE6wKw6Yi1G5l1MOmhbGr9PFgeJWNLk9F81+F2v6686/4aRf+MskN qTwxFHJMo0lmgEvRZrl4AQXX6VxGlsjYvPZXZLt80YJgfwKohv0tKIXnUM979kyz7C66 Kn8EOY+LGgKhNugimUW4Q8BgsxelNZdIZPY6HSetY0HgaxjaKEsm5m+YB3zNqG4bcwP6 3arhNdtQhSUrsSnSlaf3PAd93YIZLEVwB3CQx22iJWNbVlhoQ8Dj0Bih0OaTEp1Sl+5l eHsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:message-id:mime-version:subject:date :in-reply-to:cc:to:references; bh=EFtMNW/Q2HwUdxQDPkUnBTAsirLFlHoN6VaktKRjXXM=; b=mdQ0pdDtExNO4yAHatIJU1GouvIMVnITPlKWnkDb0EKj/+c0AGLR+7Kf1HQsPm9JFt g1ZiJy0TItpe95+UYj3flFl/2/xqgTrge+ccjs8J6fPs9Jhi8yxq9U3793YncrypPVbU sSHQIIGRNTRyJOib72UEb8XgzOLfInWS7FkiR6Ja1pUjxg4Ac1VlZ9qDndeXdaHNZljk ppuHfDAoAum7U59ptJOztU6wEURX5KjWMk+zv7mrQNmvFjTEGq9oR+pbOutS0yzado+P tII1bcQEYCla4mRG4RSwauF+SvUqln9fK6bQrY5x5RPgc6QYfWXwkgn9aNq6kgtgNw/h hQiA== X-Gm-Message-State: AOAM5338eIXILBzOdmR4r2kjC5lzh9UriYrHargt+tW+C6gM43FEEUm0 KTKBD4jaeHBVbmyPHhbi3s4ITXvIonb6GVpcnmQQVzCmL+H9sci81dtiaX2H3YzStNoyMsgeJN4 jK8JtM9Wpuqb/qlGy2eTx2vBdVqQkssVgmvPxSO0VzLguOM6nxDf20ZsNZ1VkFEd9fSo9UNlI8o cP+XrEePdwT9uAO2bc10JKS9uSbR/j/YuSjK9L X-Google-Smtp-Source: ABdhPJwqeAZlJzlDsxE8MlnI/i0o3vpmUOcu+skwpubbWp4vJzRQ222QkPT5GWc2A7shZ/cv3IG+JQ== X-Received: by 2002:a65:64c5:: with SMTP id t5mr20690983pgv.28.1595869319582; Mon, 27 Jul 2020 10:01:59 -0700 (PDT) Received: from [192.168.0.29] (24-113-193-150.wavecable.com. [24.113.193.150]) by smtp.gmail.com with ESMTPSA id c187sm15593645pfc.146.2020.07.27.10.01.57 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 27 Jul 2020 10:01:58 -0700 (PDT) From: Mark Dilger Message-Id: <9DF93B83-FE46-4EC4-A99D-77A60729FD2D@enterprisedb.com> Content-Type: multipart/mixed; boundary="Apple-Mail=_268BCDFB-5F95-4F65-8E72-69AFC6227897" Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.80.23.2.2\)) Subject: Re: new heapcheck contrib module Date: Mon, 27 Jul 2020 10:01:57 -0700 In-Reply-To: Cc: Robert Haas , Dilip Kumar , Peter Geoghegan , Andres Freund , PostgreSQL-development To: Amul Sul References: <12ED3DA8-25F0-4B68-937D-D907CFBF08E7@enterprisedb.com> <20200420194210.sevnpi4lyghoanxi@alap3.anarazel.de> <23E60C3C-C20A-4508-84AC-E0CEA59E784A@enterprisedb.com> <5F743835-3399-419C-8324-2D424237E999@enterprisedb.com> <885D2EC9-7D06-4903-A17D-72C184A1E1C5@enterprisedb.com> <5CC85F28-F210-47AC-8DA8-EC4E82AF3FD5@enterprisedb.com> <0C0349EB-DD85-4DA8-82C7-DF70AB714592@enterprisedb.com> X-Mailer: Apple Mail (2.3608.80.23.2.2) X-CLOUD-SEC-AV-Info: enterprisedb,google_mail,monitor X-CLOUD-SEC-AV-Sent: true X-Gm-Spam: 0 X-Gm-Phishy: 0 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --Apple-Mail=_268BCDFB-5F95-4F65-8E72-69AFC6227897 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii > On Jul 26, 2020, at 9:27 PM, Amul Sul wrote: >=20 > On Tue, Jul 21, 2020 at 2:32 AM Mark Dilger > wrote: >> [....] >>>=20 >>> + StaticAssertStmt(InvalidOffsetNumber + 1 =3D=3D = FirstOffsetNumber, >>> + = "InvalidOffsetNumber >>> increments to FirstOffsetNumber"); >>>=20 >>> If you are going to rely on this property, I agree that it is good = to >>> check it. But it would be better to NOT rely on this property, and I >>> suspect the code can be written quite cleanly without relying on it. >>> And actually, that's what you did, because you first set ctx.offnum = =3D >>> InvalidOffsetNumber but then just after that you set ctx.offnum =3D = 0 in >>> the loop initializer. So AFAICS the first initializer, and the = static >>> assert, are pointless. >>=20 >> Ah, right you are. Removed. >>=20 >=20 > I can see the same assert and the unnecessary assignment in v12-0002, = is that > the same thing that is supposed to be removed, or am I missing = something? That's the same thing. I removed it, but obviously I somehow removed = the removal prior to making the patch. My best guess is that I reverted = some set of changes that unintentionally included this one. >=20 >> [....] >>> +confess(HeapCheckContext * ctx, char *msg) >>> +TransactionIdValidInRel(TransactionId xid, HeapCheckContext * ctx) >>> +check_tuphdr_xids(HeapTupleHeader tuphdr, HeapCheckContext * ctx) >>>=20 >>> This is what happens when you pgindent without adding all the right >>> things to typedefs.list first ... or when you don't pgindent and = have >>> odd ideas about how to indent things. >>=20 >> Hmm. I don't see the three lines of code you are quoting. Which = patch is that from? >>=20 >=20 > I think it was the same thing related to my previous suggestion to = list new > structures to typedefs.list. V12 has listed new structures but I = think there > are still some more adjustments needed in the code e.g. see space = between > HeapCheckContext and * (asterisk) that need to be fixed. I am not sure = if the > pgindent will do that or not. Hmm. I'm not seeing an example of HeapCheckContext with wrong spacing. = Can you provide a file and line number? There was a problem with enum = SkipPages. I've added that to the typedefs.list and rerun pgindent. While looking at that, I noticed that the function and variable naming = conventions in this patch were irregular, with names like = TransactionIdValidInRel (init-caps) and tuple_is_visible (underscores), = so I spent some time cleaning that up for v13. > Here are a few more minor comments for the v12-0002 patch & some of = them > apply to other patches as well: >=20 > #include "utils/snapmgr.h" > - > +#include "amcheck.h" >=20 > Doesn't seem to be at the correct place -- need to be in sorted order. Fixed. > + if (!PG_ARGISNULL(3)) > + ereport(ERROR, > + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), > + errmsg("starting block " INT64_FORMAT > + " is out of bounds for relation with no blocks", > + PG_GETARG_INT64(3)))); > + if (!PG_ARGISNULL(4)) > + ereport(ERROR, > + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), > + errmsg("ending block " INT64_FORMAT > + " is out of bounds for relation with no blocks", > + PG_GETARG_INT64(4)))); >=20 > I think these errmsg() strings also should be in one line. I chose not to do so, because the INT64_FORMAT bit breaks up the text = even if placed all on one line. I don't feel strongly about that, = though, so I'll join them for v13. > + if (fatal) > + { > + if (ctx.toast_indexes) > + toast_close_indexes(ctx.toast_indexes, ctx.num_toast_indexes, > + ShareUpdateExclusiveLock); > + if (ctx.toastrel) > + table_close(ctx.toastrel, ShareUpdateExclusiveLock); >=20 > Toast index and rel closing block style is not the same as at the = ending of > verify_heapam(). I've harmonized the two. Thanks for noticing. > + /* If we get this far, we know the relation has at least one block = */ > + startblock =3D PG_ARGISNULL(3) ? 0 : PG_GETARG_INT64(3); > + endblock =3D PG_ARGISNULL(4) ? ((int64) ctx.nblocks) - 1 : = PG_GETARG_INT64(4); > + if (startblock < 0 || endblock >=3D ctx.nblocks || startblock > = endblock) > + ereport(ERROR, > + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), > + errmsg("block range " INT64_FORMAT " .. " INT64_FORMAT > + " is out of bounds for relation with block count %u", > + startblock, endblock, ctx.nblocks))); > + > ... > ... > + if (startblock < 0) > + startblock =3D 0; > + if (endblock < 0 || endblock > ctx.nblocks) > + endblock =3D ctx.nblocks; >=20 > Other than endblock < 0 case This case does not need special checking, either. The combination of = checking that startblock >=3D 0 and that startblock <=3D endblock = already handles it. > , do we really need that? I think due to the above > error check the rest of the cases will not reach this place. We don't need any of that. Removed in v13. > + confess(ctx, psprintf( > + "tuple xmax %u follows last assigned xid %u", > + xmax, ctx->nextKnownValidXid)); > + fatal =3D true; > + } > + } > + > + /* Check for tuple header corruption */ > + if (ctx->tuphdr->t_hoff < SizeofHeapTupleHeader) > + { > + confess(ctx, > + psprintf("tuple's header size is %u bytes which is less than the %u > byte minimum valid header size", > + ctx->tuphdr->t_hoff, > + (unsigned) SizeofHeapTupleHeader)); >=20 > confess() call has two different code styles, first one where = psprintf()'s only > argument got its own line and second style where psprintf has its own = line with > the argument. I think the 2nd style is what we do follow & correct, = not the > former. Ok, standardized in v13. > + if (rel->rd_rel->relam !=3D HEAP_TABLE_AM_OID) > + ereport(ERROR, > + (errcode(ERRCODE_WRONG_OBJECT_TYPE), > + errmsg("\"%s\" is not a heap", > + RelationGetRelationName(rel)))); >=20 > Like elsewhere, can we have errmsg as "only heap AM is supported" and = error > code is ERRCODE_FEATURE_NOT_SUPPORTED ? I'm indifferent about that change. Done for v13. > That all, for now, apologize for multiple review emails. Not at all! I appreciate all the reviews. --Apple-Mail=_268BCDFB-5F95-4F65-8E72-69AFC6227897 Content-Disposition: attachment; filename=v13-0001-Adding-function-verify_btreeam-and-bumping-versi.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v13-0001-Adding-function-verify_btreeam-and-bumping-versi.patch" Content-Transfer-Encoding: quoted-printable =46rom=20980ba0a318b1d876fd0e9c8b0cf8167a600a5c71=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Mark=20Dilger=20=0A= Date:=20Mon,=2027=20Jul=202020=2008:02:24=20-0700=0ASubject:=20[PATCH=20= v13=201/3]=20Adding=20function=20verify_btreeam=20and=20bumping=20= version=0A=0AFor=20most=20errors=20found=20while=20verifying=20a=20btree=20= index,=20the=20new=0Afunction=20verify_btreeam=20returns=20one=20row=20= per=20error=20containing=20the=0Ablock=20number=20where=20the=20error=20= was=20discovered=20and=20an=20error=20message=0Adescribing=20the=20= problem.=20=20The=20pre-existing=20behavior=20for=20functions=0A= bt_index_parent_check=20and=20bt_index_check=20is=20unchanged.=0A---=0A=20= contrib/amcheck/Makefile=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20=202=20+-=0A=20contrib/amcheck/amcheck.control=20=20=20=20=20=20=20= =20=20=20|=20=20=202=20+-=0A=20contrib/amcheck/expected/check_btree.out=20= |=20=2035=20+=0A=20contrib/amcheck/sql/check_btree.sql=20=20=20=20=20=20= |=20=2013=20+=0A=20contrib/amcheck/verify_nbtree.c=20=20=20=20=20=20=20=20= =20=20|=20834=20+++++++++++++----------=0A=20= src/tools/pgindent/typedefs.list=20=20=20=20=20=20=20=20=20|=20=20=201=20= +=0A=206=20files=20changed,=20516=20insertions(+),=20371=20deletions(-)=0A= =0Adiff=20--git=20a/contrib/amcheck/Makefile=20= b/contrib/amcheck/Makefile=0Aindex=20a2b1b1036b..b288c28fa0=20100644=0A= ---=20a/contrib/amcheck/Makefile=0A+++=20b/contrib/amcheck/Makefile=0A@@=20= -6,7=20+6,7=20@@=20OBJS=20=3D=20\=0A=20=09verify_nbtree.o=0A=20=0A=20= EXTENSION=20=3D=20amcheck=0A-DATA=20=3D=20amcheck--1.1--1.2.sql=20= amcheck--1.0--1.1.sql=20amcheck--1.0.sql=0A+DATA=20=3D=20= amcheck--1.2--1.3.sql=20amcheck--1.1--1.2.sql=20amcheck--1.0--1.1.sql=20= amcheck--1.0.sql=0A=20PGFILEDESC=20=3D=20"amcheck=20-=20function=20for=20= verifying=20relation=20integrity"=0A=20=0A=20REGRESS=20=3D=20check=20= check_btree=0Adiff=20--git=20a/contrib/amcheck/amcheck.control=20= b/contrib/amcheck/amcheck.control=0Aindex=20c6e310046d..ab50931f75=20= 100644=0A---=20a/contrib/amcheck/amcheck.control=0A+++=20= b/contrib/amcheck/amcheck.control=0A@@=20-1,5=20+1,5=20@@=0A=20#=20= amcheck=20extension=0A=20comment=20=3D=20'functions=20for=20verifying=20= relation=20integrity'=0A-default_version=20=3D=20'1.2'=0A= +default_version=20=3D=20'1.3'=0A=20module_pathname=20=3D=20= '$libdir/amcheck'=0A=20relocatable=20=3D=20true=0Adiff=20--git=20= a/contrib/amcheck/expected/check_btree.out=20= b/contrib/amcheck/expected/check_btree.out=0Aindex=20= f82f48d23b..7297abb577=20100644=0A---=20= a/contrib/amcheck/expected/check_btree.out=0A+++=20= b/contrib/amcheck/expected/check_btree.out=0A@@=20-21,6=20+21,8=20@@=20= SELECT=20bt_index_check('bttest_a_idx'::regclass);=0A=20ERROR:=20=20= permission=20denied=20for=20function=20bt_index_check=0A=20SELECT=20= bt_index_parent_check('bttest_a_idx'::regclass);=0A=20ERROR:=20=20= permission=20denied=20for=20function=20bt_index_parent_check=0A+SELECT=20= *=20FROM=20verify_btreeam('bttest_a_idx'::regclass);=0A+ERROR:=20=20= permission=20denied=20for=20function=20verify_btreeam=0A=20RESET=20ROLE;=0A= =20--=20we,=20intentionally,=20don't=20check=20relation=20permissions=20= -=20it's=20useful=0A=20--=20to=20run=20this=20cluster-wide=20with=20a=20= restricted=20account,=20and=20as=20tested=0A@@=20-29,6=20+31,7=20@@=20= GRANT=20EXECUTE=20ON=20FUNCTION=20bt_index_check(regclass)=20TO=20= regress_bttest_role;=0A=20GRANT=20EXECUTE=20ON=20FUNCTION=20= bt_index_parent_check(regclass)=20TO=20regress_bttest_role;=0A=20GRANT=20= EXECUTE=20ON=20FUNCTION=20bt_index_check(regclass,=20boolean)=20TO=20= regress_bttest_role;=0A=20GRANT=20EXECUTE=20ON=20FUNCTION=20= bt_index_parent_check(regclass,=20boolean)=20TO=20regress_bttest_role;=0A= +GRANT=20EXECUTE=20ON=20FUNCTION=20verify_btreeam(regclass,=20boolean)=20= TO=20regress_bttest_role;=0A=20SET=20ROLE=20regress_bttest_role;=0A=20= SELECT=20bt_index_check('bttest_a_idx');=0A=20=20bt_index_check=20=0A@@=20= -42,17=20+45,23=20@@=20SELECT=20bt_index_parent_check('bttest_a_idx');=0A= =20=20=0A=20(1=20row)=0A=20=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a_idx');=0A+ERROR:=20=20permission=20denied=20for=20= function=20verify_btreeam=0A=20RESET=20ROLE;=0A=20--=20verify=20plain=20= tables=20are=20rejected=20(error)=0A=20SELECT=20= bt_index_check('bttest_a');=0A=20ERROR:=20=20"bttest_a"=20is=20not=20an=20= index=0A=20SELECT=20bt_index_parent_check('bttest_a');=0A=20ERROR:=20=20= "bttest_a"=20is=20not=20an=20index=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a');=0A+ERROR:=20=20"bttest_a"=20is=20not=20an=20= index=0A=20--=20verify=20non-existing=20indexes=20are=20rejected=20= (error)=0A=20SELECT=20bt_index_check(17);=0A=20ERROR:=20=20could=20not=20= open=20relation=20with=20OID=2017=0A=20SELECT=20= bt_index_parent_check(17);=0A=20ERROR:=20=20could=20not=20open=20= relation=20with=20OID=2017=0A+SELECT=20*=20FROM=20verify_btreeam(17);=0A= +ERROR:=20=20could=20not=20open=20relation=20with=20OID=2017=0A=20--=20= verify=20wrong=20index=20types=20are=20rejected=20(error)=0A=20BEGIN;=0A=20= CREATE=20INDEX=20bttest_a_brin_idx=20ON=20bttest_a=20USING=20brin(id);=0A= @@=20-60,6=20+69,12=20@@=20SELECT=20= bt_index_parent_check('bttest_a_brin_idx');=0A=20ERROR:=20=20only=20= B-Tree=20indexes=20are=20supported=20as=20targets=20for=20verification=0A= =20DETAIL:=20=20Relation=20"bttest_a_brin_idx"=20is=20not=20a=20B-Tree=20= index.=0A=20ROLLBACK;=0A+BEGIN;=0A+CREATE=20INDEX=20bttest_a_brin_idx=20= ON=20bttest_a=20USING=20brin(id);=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a_brin_idx');=0A+ERROR:=20=20only=20B-Tree=20= indexes=20are=20supported=20as=20targets=20for=20verification=0A+DETAIL:=20= =20Relation=20"bttest_a_brin_idx"=20is=20not=20a=20B-Tree=20index.=0A= +ROLLBACK;=0A=20--=20normal=20check=20outside=20of=20xact=0A=20SELECT=20= bt_index_check('bttest_a_idx');=0A=20=20bt_index_check=20=0A@@=20-67,6=20= +82,11=20@@=20SELECT=20bt_index_check('bttest_a_idx');=0A=20=20=0A=20(1=20= row)=0A=20=0A+SELECT=20*=20FROM=20verify_btreeam('bttest_a_idx');=0A+=20= blkno=20|=20msg=20=0A+-------+-----=0A+(0=20rows)=0A+=0A=20--=20more=20= expansive=20tests=0A=20SELECT=20bt_index_check('bttest_a_idx',=20true);=0A= =20=20bt_index_check=20=0A@@=20-93,6=20+113,11=20@@=20SELECT=20= bt_index_parent_check('bttest_b_idx');=0A=20=20=0A=20(1=20row)=0A=20=0A= +SELECT=20*=20FROM=20verify_btreeam('bttest_a_idx');=0A+=20blkno=20|=20= msg=20=0A+-------+-----=0A+(0=20rows)=0A+=0A=20--=20make=20sure=20we=20= don't=20have=20any=20leftover=20locks=0A=20SELECT=20*=20FROM=20pg_locks=0A= =20WHERE=20relation=20=3D=20ANY(ARRAY['bttest_a',=20'bttest_a_idx',=20= 'bttest_b',=20'bttest_b_idx']::regclass[])=0A@@=20-118,6=20+143,11=20@@=20= SELECT=20bt_index_check('bttest_multi_idx');=0A=20=20=0A=20(1=20row)=0A=20= =0A+SELECT=20*=20FROM=20verify_btreeam('bttest_multi_idx');=0A+=20blkno=20= |=20msg=20=0A+-------+-----=0A+(0=20rows)=0A+=0A=20--=20more=20expansive=20= tests=20for=20index=20with=20included=20columns=0A=20SELECT=20= bt_index_parent_check('bttest_multi_idx',=20true,=20true);=0A=20=20= bt_index_parent_check=20=0A@@=20-134,6=20+164,11=20@@=20SELECT=20= bt_index_parent_check('bttest_multi_idx',=20true,=20true);=0A=20=20=0A=20= (1=20row)=0A=20=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_multi_idx');=0A+=20blkno=20|=20msg=20=0A= +-------+-----=0A+(0=20rows)=0A+=0A=20--=0A=20--=20Test=20for=20= multilevel=20page=20deletion/downlink=20present=20checks,=20and=20= rootdescend=0A=20--=20checks=0Adiff=20--git=20= a/contrib/amcheck/sql/check_btree.sql=20= b/contrib/amcheck/sql/check_btree.sql=0Aindex=20a1fef644cb..816ca9d033=20= 100644=0A---=20a/contrib/amcheck/sql/check_btree.sql=0A+++=20= b/contrib/amcheck/sql/check_btree.sql=0A@@=20-24,6=20+24,7=20@@=20CREATE=20= ROLE=20regress_bttest_role;=0A=20SET=20ROLE=20regress_bttest_role;=0A=20= SELECT=20bt_index_check('bttest_a_idx'::regclass);=0A=20SELECT=20= bt_index_parent_check('bttest_a_idx'::regclass);=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a_idx'::regclass);=0A=20RESET=20ROLE;=0A=20=0A=20= --=20we,=20intentionally,=20don't=20check=20relation=20permissions=20-=20= it's=20useful=0A@@=20-33,27=20+34,36=20@@=20GRANT=20EXECUTE=20ON=20= FUNCTION=20bt_index_check(regclass)=20TO=20regress_bttest_role;=0A=20= GRANT=20EXECUTE=20ON=20FUNCTION=20bt_index_parent_check(regclass)=20TO=20= regress_bttest_role;=0A=20GRANT=20EXECUTE=20ON=20FUNCTION=20= bt_index_check(regclass,=20boolean)=20TO=20regress_bttest_role;=0A=20= GRANT=20EXECUTE=20ON=20FUNCTION=20bt_index_parent_check(regclass,=20= boolean)=20TO=20regress_bttest_role;=0A+GRANT=20EXECUTE=20ON=20FUNCTION=20= verify_btreeam(regclass,=20boolean)=20TO=20regress_bttest_role;=0A=20SET=20= ROLE=20regress_bttest_role;=0A=20SELECT=20= bt_index_check('bttest_a_idx');=0A=20SELECT=20= bt_index_parent_check('bttest_a_idx');=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a_idx');=0A=20RESET=20ROLE;=0A=20=0A=20--=20= verify=20plain=20tables=20are=20rejected=20(error)=0A=20SELECT=20= bt_index_check('bttest_a');=0A=20SELECT=20= bt_index_parent_check('bttest_a');=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a');=0A=20=0A=20--=20verify=20non-existing=20= indexes=20are=20rejected=20(error)=0A=20SELECT=20bt_index_check(17);=0A=20= SELECT=20bt_index_parent_check(17);=0A+SELECT=20*=20FROM=20= verify_btreeam(17);=0A=20=0A=20--=20verify=20wrong=20index=20types=20are=20= rejected=20(error)=0A=20BEGIN;=0A=20CREATE=20INDEX=20bttest_a_brin_idx=20= ON=20bttest_a=20USING=20brin(id);=0A=20SELECT=20= bt_index_parent_check('bttest_a_brin_idx');=0A=20ROLLBACK;=0A+BEGIN;=0A= +CREATE=20INDEX=20bttest_a_brin_idx=20ON=20bttest_a=20USING=20brin(id);=0A= +SELECT=20*=20FROM=20verify_btreeam('bttest_a_brin_idx');=0A+ROLLBACK;=0A= =20=0A=20--=20normal=20check=20outside=20of=20xact=0A=20SELECT=20= bt_index_check('bttest_a_idx');=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a_idx');=0A=20--=20more=20expansive=20tests=0A=20= SELECT=20bt_index_check('bttest_a_idx',=20true);=0A=20SELECT=20= bt_index_parent_check('bttest_b_idx',=20true);=0A@@=20-61,6=20+71,7=20@@=20= SELECT=20bt_index_parent_check('bttest_b_idx',=20true);=0A=20BEGIN;=0A=20= SELECT=20bt_index_check('bttest_a_idx');=0A=20SELECT=20= bt_index_parent_check('bttest_b_idx');=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_a_idx');=0A=20--=20make=20sure=20we=20don't=20= have=20any=20leftover=20locks=0A=20SELECT=20*=20FROM=20pg_locks=0A=20= WHERE=20relation=20=3D=20ANY(ARRAY['bttest_a',=20'bttest_a_idx',=20= 'bttest_b',=20'bttest_b_idx']::regclass[])=0A@@=20-74,6=20+85,7=20@@=20= SELECT=20bt_index_check('bttest_a_idx',=20true);=0A=20=0A=20--=20normal=20= check=20outside=20of=20xact=20for=20index=20with=20included=20columns=0A=20= SELECT=20bt_index_check('bttest_multi_idx');=0A+SELECT=20*=20FROM=20= verify_btreeam('bttest_multi_idx');=0A=20--=20more=20expansive=20tests=20= for=20index=20with=20included=20columns=0A=20SELECT=20= bt_index_parent_check('bttest_multi_idx',=20true,=20true);=0A=20=0A@@=20= -81,6=20+93,7=20@@=20SELECT=20bt_index_parent_check('bttest_multi_idx',=20= true,=20true);=0A=20TRUNCATE=20bttest_multi;=0A=20INSERT=20INTO=20= bttest_multi=20SELECT=20i,=20i%2=20=20FROM=20generate_series(1,=20= 100000)=20as=20i;=0A=20SELECT=20= bt_index_parent_check('bttest_multi_idx',=20true,=20true);=0A+SELECT=20*=20= FROM=20verify_btreeam('bttest_multi_idx');=0A=20=0A=20--=0A=20--=20Test=20= for=20multilevel=20page=20deletion/downlink=20present=20checks,=20and=20= rootdescend=0Adiff=20--git=20a/contrib/amcheck/verify_nbtree.c=20= b/contrib/amcheck/verify_nbtree.c=0Aindex=20e4d501a85d..ea70fc41a9=20= 100644=0A---=20a/contrib/amcheck/verify_nbtree.c=0A+++=20= b/contrib/amcheck/verify_nbtree.c=0A@@=20-32,16=20+32,22=20@@=0A=20= #include=20"catalog/index.h"=0A=20#include=20"catalog/pg_am.h"=0A=20= #include=20"commands/tablecmds.h"=0A+#include=20"funcapi.h"=0A=20= #include=20"lib/bloomfilter.h"=0A=20#include=20"miscadmin.h"=0A=20= #include=20"storage/lmgr.h"=0A=20#include=20"storage/smgr.h"=0A+#include=20= "utils/builtins.h"=0A=20#include=20"utils/memutils.h"=0A=20#include=20= "utils/snapmgr.h"=0A-=0A+#include=20"amcheck.h"=0A=20=0A=20= PG_MODULE_MAGIC;=0A=20=0A+PG_FUNCTION_INFO_V1(bt_index_check);=0A= +PG_FUNCTION_INFO_V1(bt_index_parent_check);=0A= +PG_FUNCTION_INFO_V1(verify_btreeam);=0A+=0A=20/*=0A=20=20*=20A=20B-Tree=20= cannot=20possibly=20have=20this=20many=20levels,=20since=20there=20must=20= be=20one=0A=20=20*=20block=20per=20level,=20which=20is=20bound=20by=20= the=20range=20of=20BlockNumber:=0A@@=20-50,6=20+56,20=20@@=20= PG_MODULE_MAGIC;=0A=20#define=20BTreeTupleGetNKeyAtts(itup,=20rel)=20=20=20= \=0A=20=09Min(IndexRelationGetNumberOfKeyAttributes(rel),=20= BTreeTupleGetNAtts(itup,=20rel))=0A=20=0A+/*=0A+=20*=20Context=20for=20= use=20within=20verify_btreeam()=0A+=20*/=0A+typedef=20struct=20= BtreeCheckContext=0A+{=0A+=09TupleDesc=09tupdesc;=0A+=09Tuplestorestate=20= *tupstore;=0A+=09bool=09=09is_corrupt;=0A+=09bool=09=09on_error_stop;=0A= +}=09=09=09BtreeCheckContext;=0A+=0A+#define=20CONTINUE_CHECKING(ctx)=20= \=0A+=09(ctx=20=3D=3D=20NULL=20||=20!((ctx)->is_corrupt=20&&=20= (ctx)->on_error_stop))=0A+=0A=20/*=0A=20=20*=20State=20associated=20with=20= verifying=20a=20B-Tree=20index=0A=20=20*=0A@@=20-116,6=20+136,9=20@@=20= typedef=20struct=20BtreeCheckState=0A=20=09bloom_filter=20*filter;=0A=20=09= /*=20Debug=20counter=20*/=0A=20=09int64=09=09heaptuplespresent;=0A+=0A+=09= /*=20Error=20reporting=20context=20*/=0A+=09BtreeCheckContext=20*ctx;=0A=20= }=20BtreeCheckState;=0A=20=0A=20/*=0A@@=20-133,28=20+156,28=20@@=20= typedef=20struct=20BtreeLevel=0A=20=09bool=09=09istruerootlevel;=0A=20}=20= BtreeLevel;=0A=20=0A-PG_FUNCTION_INFO_V1(bt_index_check);=0A= -PG_FUNCTION_INFO_V1(bt_index_parent_check);=0A-=0A=20static=20void=20= bt_index_check_internal(Oid=20indrelid,=20bool=20parentcheck,=0A-=09=09=09= =09=09=09=09=09=09bool=20heapallindexed,=20bool=20rootdescend);=0A+=09=09= =09=09=09=09=09=09=09bool=20heapallindexed,=20bool=20rootdescend,=0A+=09=09= =09=09=09=09=09=09=09BtreeCheckContext=20*=20ctx);=0A=20static=20inline=20= void=20btree_index_checkable(Relation=20rel);=0A=20static=20inline=20= bool=20btree_index_mainfork_expected(Relation=20rel);=0A=20static=20void=20= bt_check_every_level(Relation=20rel,=20Relation=20heaprel,=0A=20=09=09=09= =09=09=09=09=09=20bool=20heapkeyspace,=20bool=20readonly,=20bool=20= heapallindexed,=0A-=09=09=09=09=09=09=09=09=20bool=20rootdescend);=0A+=09= =09=09=09=09=09=09=09=20bool=20rootdescend,=20BtreeCheckContext=20*=20= ctx);=0A=20static=20BtreeLevel=20= bt_check_level_from_leftmost(BtreeCheckState=20*state,=0A-=09=09=09=09=09= =09=09=09=09=09=09=20=20=20BtreeLevel=20level);=0A-static=20void=20= bt_target_page_check(BtreeCheckState=20*state);=0A-static=20BTScanInsert=20= bt_right_page_check_scankey(BtreeCheckState=20*state);=0A+=09=09=09=09=09= =09=09=09=09=09=09=20=20=20BtreeLevel=20level,=20BtreeCheckContext=20*=20= ctx);=0A+static=20void=20bt_target_page_check(BtreeCheckState=20*state,=20= BtreeCheckContext=20*=20ctx);=0A+static=20BTScanInsert=20= bt_right_page_check_scankey(BtreeCheckState=20*state,=20= BtreeCheckContext=20*=20ctx);=0A=20static=20void=20= bt_child_check(BtreeCheckState=20*state,=20BTScanInsert=20targetkey,=0A-=09= =09=09=09=09=09=20=20=20OffsetNumber=20downlinkoffnum);=0A+=09=09=09=09=09= =09=20=20=20OffsetNumber=20downlinkoffnum,=20BtreeCheckContext=20*=20= ctx);=0A=20static=20void=20bt_child_highkey_check(BtreeCheckState=20= *state,=0A=20=09=09=09=09=09=09=09=09=20=20=20OffsetNumber=20= target_downlinkoffnum,=0A=20=09=09=09=09=09=09=09=09=20=20=20Page=20= loaded_child,=0A-=09=09=09=09=09=09=09=09=20=20=20uint32=20= target_level);=0A+=09=09=09=09=09=09=09=09=20=20=20uint32=20= target_level,=0A+=09=09=09=09=09=09=09=09=20=20=20BtreeCheckContext=20*=20= ctx);=0A=20static=20void=20bt_downlink_missing_check(BtreeCheckState=20= *state,=20bool=20rightsplit,=0A-=09=09=09=09=09=09=09=09=09=20=20= BlockNumber=20targetblock,=20Page=20target);=0A+=09=09=09=09=09=09=09=09=09= =20=20BlockNumber=20targetblock,=20Page=20target,=0A+=09=09=09=09=09=09=09= =09=09=20=20BtreeCheckContext=20*=20ctx);=0A=20static=20void=20= bt_tuple_present_callback(Relation=20index,=20ItemPointer=20tid,=0A=20=09= =09=09=09=09=09=09=09=09=20=20Datum=20*values,=20bool=20*isnull,=0A=20=09= =09=09=09=09=09=09=09=09=20=20bool=20tupleIsAlive,=20void=20= *checkstate);=0A@@=20-176,7=20+199,7=20@@=20static=20inline=20bool=20= invariant_l_nontarget_offset(BtreeCheckState=20*state,=0A=20=09=09=09=09=09= =09=09=09=09=09=09=09BlockNumber=20nontargetblock,=0A=20=09=09=09=09=09=09= =09=09=09=09=09=09Page=20nontarget,=0A=20=09=09=09=09=09=09=09=09=09=09=09= =09OffsetNumber=20upperbound);=0A-static=20Page=20= palloc_btree_page(BtreeCheckState=20*state,=20BlockNumber=20blocknum);=0A= +static=20Page=20palloc_btree_page(BtreeCheckState=20*state,=20= BlockNumber=20blocknum,=20BtreeCheckContext=20*=20ctx);=0A=20static=20= inline=20BTScanInsert=20bt_mkscankey_pivotsearch(Relation=20rel,=0A=20=09= =09=09=09=09=09=09=09=09=09=09=09=09IndexTuple=20itup);=0A=20static=20= ItemId=20PageGetItemIdCareful(BtreeCheckState=20*state,=20BlockNumber=20= block,=0A@@=20-185,6=20+208,26=20@@=20static=20inline=20ItemPointer=20= BTreeTupleGetHeapTIDCareful(BtreeCheckState=20*state,=0A=20=09=09=09=09=09= =09=09=09=09=09=09=09=09=20=20IndexTuple=20itup,=20bool=20nonpivot);=0A=20= static=20inline=20ItemPointer=20BTreeTupleGetPointsToTID(IndexTuple=20= itup);=0A=20=0A+static=20TupleDesc=20verify_btreeam_tupdesc(void);=0A= +static=20void=20confess(BtreeCheckContext=20*=20ctx,=20BlockNumber=20= blkno,=20char=20*msg);=0A+=0A+/*=0A+=20*=20Macro=20for=20either=20= calling=20ereport(...)=20or=20confess(...)=20depending=20on=20whether=0A= +=20*=20a=20context=20for=20returning=20the=20error=20message=20exists.=20= =20Prior=20to=20version=201.3,=0A+=20*=20all=20functions=20reported=20= any=20detected=20corruption=20via=20ereport,=20but=20starting=20in=0A+=20= *=201.3,=20the=20new=20function=20verify_btreeam=20reports=20detected=20= corruption=20back=20to=0A+=20*=20the=20caller=20as=20a=20set=20of=20= rows,=20and=20pre-existing=20functions=20continue=20to=20report=0A+=20*=20= corruption=20via=20ereport.=20=20This=20macro=20allows=20the=20shared=20= implementation=20to=0A+=20*=20to=20do=20the=20right=20thing=20depending=20= on=20context.=0A+=20*/=0A+#define=20econfess(ctx,=20blkno,=20code,=20= ...)=20\=0A+=09do=20{=20\=0A+=09=09if=20(ctx)=20\=0A+=09=09=09= confess(ctx,=20blkno,=20psprintf(__VA_ARGS__));=20\=0A+=09=09else=20\=0A= +=09=09=09ereport(ERROR,=20(errcode(code),=20errmsg(__VA_ARGS__)));=20\=0A= +=09}=20while(0)=0A+=0A=20/*=0A=20=20*=20bt_index_check(index=20= regclass,=20heapallindexed=20boolean)=0A=20=20*=0A@@=20-203,7=20+246,7=20= @@=20bt_index_check(PG_FUNCTION_ARGS)=0A=20=09if=20(PG_NARGS()=20=3D=3D=20= 2)=0A=20=09=09heapallindexed=20=3D=20PG_GETARG_BOOL(1);=0A=20=0A-=09= bt_index_check_internal(indrelid,=20false,=20heapallindexed,=20false);=0A= +=09bt_index_check_internal(indrelid,=20false,=20heapallindexed,=20= false,=20NULL);=0A=20=0A=20=09PG_RETURN_VOID();=0A=20}=0A@@=20-229,17=20= +272,64=20@@=20bt_index_parent_check(PG_FUNCTION_ARGS)=0A=20=09if=20= (PG_NARGS()=20=3D=3D=203)=0A=20=09=09rootdescend=20=3D=20= PG_GETARG_BOOL(2);=0A=20=0A-=09bt_index_check_internal(indrelid,=20true,=20= heapallindexed,=20rootdescend);=0A+=09bt_index_check_internal(indrelid,=20= true,=20heapallindexed,=20rootdescend,=20NULL);=0A=20=0A=20=09= PG_RETURN_VOID();=0A=20}=0A=20=0A+Datum=0A= +verify_btreeam(PG_FUNCTION_ARGS)=0A+{=0A+#define=20= BTREECHECK_RELATION_COLS=202=0A+=09ReturnSetInfo=20*rsinfo=20=3D=20= (ReturnSetInfo=20*)=20fcinfo->resultinfo;=0A+=09MemoryContext=20= oldcontext;=0A+=09BtreeCheckContext=20ctx;=0A+=09bool=09=09randomAccess;=0A= +=09Oid=09=09=09indrelid;=0A+=0A+=09/*=20check=20to=20see=20if=20caller=20= supports=20us=20returning=20a=20tuplestore=20*/=0A+=09if=20(rsinfo=20=3D=3D= =20NULL=20||=20!IsA(rsinfo,=20ReturnSetInfo))=0A+=09=09ereport(ERROR,=0A= +=09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),=0A+=09=09=09=09=20= errmsg("set-valued=20function=20called=20in=20context=20that=20cannot=20= accept=20a=20set")));=0A+=09if=20(!(rsinfo->allowedModes=20&=20= SFRM_Materialize))=0A+=09=09ereport(ERROR,=0A+=09=09=09=09= (errcode(ERRCODE_SYNTAX_ERROR),=0A+=09=09=09=09=20errmsg("materialize=20= mode=20required,=20but=20it=20is=20not=20allowed=20in=20this=20= context")));=0A+=0A+=09/*=20check=20supplied=20arguments=20*/=0A+=09if=20= (PG_ARGISNULL(0))=0A+=09=09ereport(ERROR,=0A+=09=09=09=09= (errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=20= errmsg("missing=20required=20parameter=20for=20'rel'")));=0A+=09indrelid=20= =3D=20PG_GETARG_OID(0);=0A+=0A+=09memset(&ctx,=200,=20= sizeof(BtreeCheckContext));=0A+=0A+=09ctx.on_error_stop=20=3D=20= PG_ARGISNULL(1)=20?=20false=20:=20PG_GETARG_BOOL(1);=0A+=0A+=09/*=20The=20= tupdesc=20and=20tuplestore=20must=20be=20created=20in=20= ecxt_per_query_memory=20*/=0A+=09oldcontext=20=3D=20= MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);=0A+=09= randomAccess=20=3D=20(rsinfo->allowedModes=20&=20= SFRM_Materialize_Random)=20!=3D=200;=0A+=09ctx.tupdesc=20=3D=20= verify_btreeam_tupdesc();=0A+=09ctx.tupstore=20=3D=20= tuplestore_begin_heap(randomAccess,=20false,=20work_mem);=0A+=09= rsinfo->returnMode=20=3D=20SFRM_Materialize;=0A+=09rsinfo->setResult=20=3D= =20ctx.tupstore;=0A+=09rsinfo->setDesc=20=3D=20ctx.tupdesc;=0A+=0A+=09= MemoryContextSwitchTo(oldcontext);=0A+=0A+=09= bt_index_check_internal(indrelid,=20true,=20true,=20true,=20&ctx);=0A+=0A= +=09PG_RETURN_NULL();=0A+}=0A+=0A=20/*=0A=20=20*=20Helper=20for=20= bt_index_[parent_]check,=20coordinating=20the=20bulk=20of=20the=20work.=0A= =20=20*/=0A=20static=20void=0A=20bt_index_check_internal(Oid=20indrelid,=20= bool=20parentcheck,=20bool=20heapallindexed,=0A-=09=09=09=09=09=09bool=20= rootdescend)=0A+=09=09=09=09=09=09bool=20rootdescend,=20= BtreeCheckContext=20*=20ctx)=0A=20{=0A=20=09Oid=09=09=09heapid;=0A=20=09= Relation=09indrel;=0A@@=20-300,15=20+390,16=20@@=20= bt_index_check_internal(Oid=20indrelid,=20bool=20parentcheck,=20bool=20= heapallindexed,=0A=20=0A=20=09=09RelationOpenSmgr(indrel);=0A=20=09=09if=20= (!smgrexists(indrel->rd_smgr,=20MAIN_FORKNUM))=0A-=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=09=20errmsg("index=20\"%s\"=20lacks=20a=20main=20relation=20= fork",=0A-=09=09=09=09=09=09=09RelationGetRelationName(indrel))));=0A+=09= =09=09econfess(ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A+=09= =09=09=09=09=20"index=20\"%s\"=20lacks=20a=20main=20relation=20fork",=0A= +=09=09=09=09=09=20RelationGetRelationName(indrel));=0A=20=0A=20=09=09/*=20= Check=20index,=20possibly=20against=20table=20it=20is=20an=20index=20on=20= */=0A-=09=09_bt_metaversion(indrel,=20&heapkeyspace,=20&allequalimage);=0A= -=09=09bt_check_every_level(indrel,=20heaprel,=20heapkeyspace,=20= parentcheck,=0A-=09=09=09=09=09=09=09=20heapallindexed,=20rootdescend);=0A= +=09=09if=20(CONTINUE_CHECKING(ctx))=0A+=09=09=09_bt_metaversion(indrel,=20= &heapkeyspace,=20&allequalimage);=0A+=09=09if=20(CONTINUE_CHECKING(ctx))=0A= +=09=09=09bt_check_every_level(indrel,=20heaprel,=20heapkeyspace,=20= parentcheck,=0A+=09=09=09=09=09=09=09=09=20heapallindexed,=20= rootdescend,=20ctx);=0A=20=09}=0A=20=0A=20=09/*=0A@@=20-402,7=20+493,8=20= @@=20btree_index_mainfork_expected(Relation=20rel)=0A=20=20*/=0A=20= static=20void=0A=20bt_check_every_level(Relation=20rel,=20Relation=20= heaprel,=20bool=20heapkeyspace,=0A-=09=09=09=09=09=20bool=20readonly,=20= bool=20heapallindexed,=20bool=20rootdescend)=0A+=09=09=09=09=09=20bool=20= readonly,=20bool=20heapallindexed,=20bool=20rootdescend,=0A+=09=09=09=09=09= =20BtreeCheckContext=20*=20ctx)=0A=20{=0A=20=09BtreeCheckState=20*state;=0A= =20=09Page=09=09metapage;=0A@@=20-434,6=20+526,7=20@@=20= bt_check_every_level(Relation=20rel,=20Relation=20heaprel,=20bool=20= heapkeyspace,=0A=20=09state->readonly=20=3D=20readonly;=0A=20=09= state->heapallindexed=20=3D=20heapallindexed;=0A=20=09state->rootdescend=20= =3D=20rootdescend;=0A+=09state->ctx=20=3D=20ctx;=0A=20=0A=20=09if=20= (state->heapallindexed)=0A=20=09{=0A@@=20-506,7=20+599,7=20@@=20= bt_check_every_level(Relation=20rel,=20Relation=20heaprel,=20bool=20= heapkeyspace,=0A=20=09state->checkstrategy=20=3D=20= GetAccessStrategy(BAS_BULKREAD);=0A=20=0A=20=09/*=20Get=20true=20root=20= block=20from=20meta-page=20*/=0A-=09metapage=20=3D=20= palloc_btree_page(state,=20BTREE_METAPAGE);=0A+=09metapage=20=3D=20= palloc_btree_page(state,=20BTREE_METAPAGE,=20ctx);=0A=20=09metad=20=3D=20= BTPageGetMeta(metapage);=0A=20=0A=20=09/*=0A@@=20-535,19=20+628,18=20@@=20= bt_check_every_level(Relation=20rel,=20Relation=20heaprel,=20bool=20= heapkeyspace,=0A=20=09current.level=20=3D=20metad->btm_level;=0A=20=09= current.leftmost=20=3D=20metad->btm_root;=0A=20=09= current.istruerootlevel=20=3D=20true;=0A-=09while=20(current.leftmost=20= !=3D=20P_NONE)=0A+=09while=20(CONTINUE_CHECKING(state->ctx)=20&&=20= current.leftmost=20!=3D=20P_NONE)=0A=20=09{=0A=20=09=09/*=0A=20=09=09=20= *=20Verify=20this=20level,=20and=20get=20left=20most=20page=20for=20next=20= level=20down,=20if=0A=20=09=09=20*=20not=20at=20leaf=20level=0A=20=09=09=20= */=0A-=09=09current=20=3D=20bt_check_level_from_leftmost(state,=20= current);=0A+=09=09current=20=3D=20bt_check_level_from_leftmost(state,=20= current,=20ctx);=0A=20=0A=20=09=09if=20(current.leftmost=20=3D=3D=20= InvalidBlockNumber)=0A-=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("index=20= \"%s\"=20has=20no=20valid=20pages=20on=20level=20below=20%u=20or=20first=20= level",=0A-=09=09=09=09=09=09=09RelationGetRelationName(rel),=20= previouslevel)));=0A+=09=09=09econfess(state->ctx,=20InvalidBlockNumber,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20"index=20\"%s\"=20has=20no=20= valid=20pages=20on=20level=20below=20%u=20or=20first=20level",=0A+=09=09=09= =09=09=20RelationGetRelationName(rel),=20previouslevel);=0A=20=0A=20=09=09= previouslevel=20=3D=20current.level;=0A=20=09}=0A@@=20-555,7=20+647,7=20= @@=20bt_check_every_level(Relation=20rel,=20Relation=20heaprel,=20bool=20= heapkeyspace,=0A=20=09/*=0A=20=09=20*=20*=20Check=20whether=20heap=20= contains=20unindexed/malformed=20tuples=20*=0A=20=09=20*/=0A-=09if=20= (state->heapallindexed)=0A+=09if=20(CONTINUE_CHECKING(state->ctx)=20&&=20= state->heapallindexed)=0A=20=09{=0A=20=09=09IndexInfo=20=20*indexinfo=20= =3D=20BuildIndexInfo(state->rel);=0A=20=09=09TableScanDesc=20scan;=0A@@=20= -639,7=20+731,7=20@@=20bt_check_every_level(Relation=20rel,=20Relation=20= heaprel,=20bool=20heapkeyspace,=0A=20=20*=20each=20call=20to=20= bt_target_page_check().=0A=20=20*/=0A=20static=20BtreeLevel=0A= -bt_check_level_from_leftmost(BtreeCheckState=20*state,=20BtreeLevel=20= level)=0A+bt_check_level_from_leftmost(BtreeCheckState=20*state,=20= BtreeLevel=20level,=20BtreeCheckContext=20*=20ctx)=0A=20{=0A=20=09/*=20= State=20to=20establish=20early,=20concerning=20entire=20level=20*/=0A=20=09= BTPageOpaque=20opaque;=0A@@=20-672,7=20+764,7=20@@=20= bt_check_level_from_leftmost(BtreeCheckState=20*state,=20BtreeLevel=20= level)=0A=20=0A=20=09=09/*=20Initialize=20state=20for=20this=20iteration=20= */=0A=20=09=09state->targetblock=20=3D=20current;=0A-=09=09state->target=20= =3D=20palloc_btree_page(state,=20state->targetblock);=0A+=09=09= state->target=20=3D=20palloc_btree_page(state,=20state->targetblock,=20= ctx);=0A=20=09=09state->targetlsn=20=3D=20PageGetLSN(state->target);=0A=20= =0A=20=09=09opaque=20=3D=20(BTPageOpaque)=20= PageGetSpecialPointer(state->target);=0A@@=20-691,18=20+783,16=20@@=20= bt_check_level_from_leftmost(BtreeCheckState=20*state,=20BtreeLevel=20= level)=0A=20=09=09=09=20*=20checked.=0A=20=09=09=09=20*/=0A=20=09=09=09= if=20(state->readonly=20&&=20P_ISDELETED(opaque))=0A-=09=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A= -=09=09=09=09=09=09=20errmsg("downlink=20or=20sibling=20link=20points=20= to=20deleted=20block=20in=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=09=20= errdetail_internal("Block=3D%u=20left=20block=3D%u=20left=20link=20from=20= block=3D%u.",=0A-=09=09=09=09=09=09=09=09=09=09=09current,=20= leftcurrent,=20opaque->btpo_prev)));=0A+=09=09=09=09econfess(state->ctx,=20= current,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=09=20"downlink=20= or=20sibling=20link=20points=20to=20deleted=20block=20in=20index=20= \"%s\"=20"=0A+=09=09=09=09=09=09=20"(Block=3D%u=20left=20block=3D%u=20= left=20link=20from=20block=3D%u)",=0A+=09=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=09=20current,=20= leftcurrent,=20opaque->btpo_prev);=0A=20=0A=20=09=09=09if=20= (P_RIGHTMOST(opaque))=0A-=09=09=09=09ereport(ERROR,=0A-=09=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=09=20errmsg("block=20= %u=20fell=20off=20the=20end=20of=20index=20\"%s\"",=0A-=09=09=09=09=09=09= =09=09current,=20RelationGetRelationName(state->rel))));=0A+=09=09=09=09= econfess(state->ctx,=20current,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09= =09=09=20"block=20%u=20fell=20off=20the=20end=20of=20index=20\"%s\"",=0A= +=09=09=09=09=09=09=20current,=20RelationGetRelationName(state->rel));=0A= =20=09=09=09else=0A=20=09=09=09=09ereport(DEBUG1,=0A=20=09=09=09=09=09=09= (errcode(ERRCODE_NO_DATA),=0A@@=20-722,16=20+812,14=20@@=20= bt_check_level_from_leftmost(BtreeCheckState=20*state,=20BtreeLevel=20= level)=0A=20=09=09=09if=20(state->readonly)=0A=20=09=09=09{=0A=20=09=09=09= =09if=20(!P_LEFTMOST(opaque))=0A-=09=09=09=09=09ereport(ERROR,=0A-=09=09=09= =09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=09=09=20= errmsg("block=20%u=20is=20not=20leftmost=20in=20index=20\"%s\"",=0A-=09=09= =09=09=09=09=09=09=09current,=20RelationGetRelationName(state->rel))));=0A= +=09=09=09=09=09econfess(state->ctx,=20current,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=09=09=20"block=20%u=20is=20= not=20leftmost=20in=20index=20\"%s\"",=0A+=09=09=09=09=09=09=09=20= current,=20RelationGetRelationName(state->rel));=0A=20=0A=20=09=09=09=09= if=20(level.istruerootlevel=20&&=20!P_ISROOT(opaque))=0A-=09=09=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=09=09=20= errmsg("block=20%u=20is=20not=20true=20root=20in=20index=20\"%s\"",=0A-=09= =09=09=09=09=09=09=09=09current,=20= RelationGetRelationName(state->rel))));=0A+=09=09=09=09=09= econfess(state->ctx,=20current,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09= =09=09=09=20"block=20%u=20is=20not=20true=20root=20in=20index=20\"%s\"",=0A= +=09=09=09=09=09=09=09=20current,=20= RelationGetRelationName(state->rel));=0A=20=09=09=09}=0A=20=0A=20=09=09=09= /*=0A@@=20-780,33=20+868,30=20@@=20= bt_check_level_from_leftmost(BtreeCheckState=20*state,=20BtreeLevel=20= level)=0A=20=09=09=20*=20so=20sibling=20pointers=20should=20always=20be=20= in=20mutual=20agreement=0A=20=09=09=20*/=0A=20=09=09if=20= (state->readonly=20&&=20opaque->btpo_prev=20!=3D=20leftcurrent)=0A-=09=09= =09ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A= -=09=09=09=09=09=20errmsg("left=20link/right=20link=20pair=20in=20index=20= \"%s\"=20not=20in=20agreement",=0A-=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Block=3D%u=20left=20block=3D%u=20left=20link=20from=20= block=3D%u.",=0A-=09=09=09=09=09=09=09=09=09=09current,=20leftcurrent,=20= opaque->btpo_prev)));=0A+=09=09=09econfess(state->ctx,=20current,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20"left=20link/right=20link=20= pair=20in=20index=20\"%s\"=20not=20in=20agreement=20"=0A+=09=09=09=09=09=20= "(Block=3D%u=20left=20block=3D%u=20left=20link=20from=20block=3D%u)",=0A= +=09=09=09=09=09=20RelationGetRelationName(state->rel),=0A+=09=09=09=09=09= =20current,=20leftcurrent,=20opaque->btpo_prev);=0A=20=0A=20=09=09/*=20= Check=20level,=20which=20must=20be=20valid=20for=20non-ignorable=20page=20= */=0A=20=09=09if=20(level.level=20!=3D=20opaque->btpo.level)=0A-=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=09=20errmsg("leftmost=20down=20link=20for=20level=20points=20= to=20block=20in=20index=20\"%s\"=20whose=20level=20is=20not=20one=20= level=20down",=0A-=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Block=20pointed=20to=3D%u=20expected=20level=3D%u=20= level=20in=20pointed=20to=20block=3D%u.",=0A-=09=09=09=09=09=09=09=09=09=09= current,=20level.level,=20opaque->btpo.level)));=0A+=09=09=09= econfess(state->ctx,=20current,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09= =09=20"leftmost=20down=20link=20for=20level=20points=20to=20block=20in=20= index=20\"%s\"=20whose=20level=20is=20not=20one=20level=20down=20"=0A+=09= =09=09=09=09=20"(Block=20pointed=20to=3D%u=20expected=20level=3D%u=20= level=20in=20pointed=20to=20block=3D%u)",=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20current,=20= level.level,=20opaque->btpo.level);=0A=20=0A=20=09=09/*=20Verify=20= invariants=20for=20page=20*/=0A-=09=09bt_target_page_check(state);=0A+=09= =09bt_target_page_check(state,=20ctx);=0A=20=0A=20nextpage:=0A=20=0A=20=09= =09/*=20Try=20to=20detect=20circular=20links=20*/=0A=20=09=09if=20= (current=20=3D=3D=20leftcurrent=20||=20current=20=3D=3D=20= opaque->btpo_prev)=0A-=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("circular=20= link=20chain=20found=20in=20block=20%u=20of=20index=20\"%s\"",=0A-=09=09=09= =09=09=09=09current,=20RelationGetRelationName(state->rel))));=0A+=09=09=09= econfess(state->ctx,=20current,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09= =09=20"circular=20link=20chain=20found=20in=20block=20%u=20of=20index=20= \"%s\"",=0A+=09=09=09=09=09=20current,=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09=09leftcurrent=20=3D=20= current;=0A=20=09=09current=20=3D=20opaque->btpo_next;=0A@@=20-850,7=20= +935,7=20@@=20nextpage:=0A=20=09=09/*=20Free=20page=20and=20associated=20= memory=20for=20this=20iteration=20*/=0A=20=09=09= MemoryContextReset(state->targetcontext);=0A=20=09}=0A-=09while=20= (current=20!=3D=20P_NONE);=0A+=09while=20(CONTINUE_CHECKING(state->ctx)=20= &&=20current=20!=3D=20P_NONE);=0A=20=0A=20=09if=20(state->lowkey)=0A=20=09= {=0A@@=20-902,7=20+987,7=20@@=20nextpage:=0A=20=20*=20resetting=20= state->targetcontext.=0A=20=20*/=0A=20static=20void=0A= -bt_target_page_check(BtreeCheckState=20*state)=0A= +bt_target_page_check(BtreeCheckState=20*state,=20BtreeCheckContext=20*=20= ctx)=0A=20{=0A=20=09OffsetNumber=20offset;=0A=20=09OffsetNumber=20max;=0A= @@=20-930,16=20+1015,15=20@@=20bt_target_page_check(BtreeCheckState=20= *state)=0A=20=09=09=09=09=09=09=09=20P_HIKEY))=0A=20=09=09{=0A=20=09=09=09= itup=20=3D=20(IndexTuple)=20PageGetItem(state->target,=20itemid);=0A-=09=09= =09ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A= -=09=09=09=09=09=20errmsg("wrong=20number=20of=20high=20key=20index=20= tuple=20attributes=20in=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Index=20block=3D%u=20natts=3D%u=20block=20type=3D%s=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09= state->targetblock,=0A-=09=09=09=09=09=09=09=09=09=09= BTreeTupleGetNAtts(itup,=20state->rel),=0A-=09=09=09=09=09=09=09=09=09=09= P_ISLEAF(topaque)=20?=20"heap"=20:=20"index",=0A-=09=09=09=09=09=09=09=09= =09=09(uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09= =09=09(uint32)=20state->targetlsn)));=0A+=09=09=09econfess(state->ctx,=20= state->targetblock,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20= "wrong=20number=20of=20high=20key=20index=20tuple=20attributes=20in=20= index=20\"%s\"=20"=0A+=09=09=09=09=09=20"(Index=20block=3D%u=20natts=3D%u=20= block=20type=3D%s=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20= state->targetblock,=0A+=09=09=09=09=09=20BTreeTupleGetNAtts(itup,=20= state->rel),=0A+=09=09=09=09=09=20P_ISLEAF(topaque)=20?=20"heap"=20:=20= "index",=0A+=09=09=09=09=09=20(uint32)=20(state->targetlsn=20>>=2032),=0A= +=09=09=09=09=09=20(uint32)=20state->targetlsn);=0A=20=09=09}=0A=20=09}=0A= =20=0A@@=20-949,7=20+1033,7=20@@=20bt_target_page_check(BtreeCheckState=20= *state)=0A=20=09=20*=20real=20item=20(if=20any).=0A=20=09=20*/=0A=20=09= for=20(offset=20=3D=20P_FIRSTDATAKEY(topaque);=0A-=09=09=20offset=20<=3D=20= max;=0A+=09=09=20offset=20<=3D=20max=20&&=20= CONTINUE_CHECKING(state->ctx);=0A=20=09=09=20offset=20=3D=20= OffsetNumberNext(offset))=0A=20=09{=0A=20=09=09ItemId=09=09itemid;=0A@@=20= -973,16=20+1057,15=20@@=20bt_target_page_check(BtreeCheckState=20*state)=0A= =20=09=09=20*=20frequently,=20and=20is=20surprisingly=20tolerant=20of=20= corrupt=20lp_len=20fields.=0A=20=09=09=20*/=0A=20=09=09if=20(tupsize=20= !=3D=20ItemIdGetLength(itemid))=0A-=09=09=09ereport(ERROR,=0A-=09=09=09=09= =09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("index=20= tuple=20size=20does=20not=20equal=20lp_len=20in=20index=20\"%s\"",=0A-=09= =09=09=09=09=09=09RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09= =20errdetail_internal("Index=20tid=3D(%u,%u)=20tuple=20size=3D%zu=20= lp_len=3D%u=20page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09= state->targetblock,=20offset,=0A-=09=09=09=09=09=09=09=09=09=09tupsize,=20= ItemIdGetLength(itemid),=0A-=09=09=09=09=09=09=09=09=09=09(uint32)=20= (state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09=09=09(uint32)=20= state->targetlsn),=0A-=09=09=09=09=09=20errhint("This=20could=20be=20a=20= torn=20page=20problem.")));=0A+=09=09=09econfess(state->ctx,=20= state->targetblock,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20= "index=20tuple=20size=20does=20not=20equal=20lp_len=20in=20index=20= \"%s\"=20"=0A+=09=09=09=09=09=20"(Index=20tid=3D(%u,%u)=20tuple=20= size=3D%zu=20lp_len=3D%u=20page=20lsn=3D%X/%X)=20"=0A+=09=09=09=09=09=20= "(This=20could=20be=20a=20torn=20page=20problem)",=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20= state->targetblock,=20offset,=0A+=09=09=09=09=09=20tupsize,=20= ItemIdGetLength(itemid),=0A+=09=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=0A=20=09=09/*=20Check=20the=20number=20of=20= index=20tuple=20attributes=20*/=0A=20=09=09if=20= (!_bt_check_natts(state->rel,=20state->heapkeyspace,=20state->target,=0A= @@=20-998,17=20+1081,16=20@@=20bt_target_page_check(BtreeCheckState=20= *state)=0A=20=09=09=09=09=09=09=09ItemPointerGetBlockNumberNoCheck(tid),=0A= =20=09=09=09=09=09=09=09ItemPointerGetOffsetNumberNoCheck(tid));=0A=20=0A= -=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("wrong=20= number=20of=20index=20tuple=20attributes=20in=20index=20\"%s\"",=0A-=09=09= =09=09=09=09=09RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Index=20tid=3D%s=20natts=3D%u=20points=20to=20%s=20= tid=3D%s=20page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09itid,=0A= -=09=09=09=09=09=09=09=09=09=09BTreeTupleGetNAtts(itup,=20state->rel),=0A= -=09=09=09=09=09=09=09=09=09=09P_ISLEAF(topaque)=20?=20"heap"=20:=20= "index",=0A-=09=09=09=09=09=09=09=09=09=09htid,=0A-=09=09=09=09=09=09=09=09= =09=09(uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09= =09=09(uint32)=20state->targetlsn)));=0A+=09=09=09econfess(state->ctx,=20= InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20= "wrong=20number=20of=20index=20tuple=20attributes=20in=20index=20\"%s\"=20= "=0A+=09=09=09=09=09=20"(Index=20tid=3D%s=20natts=3D%u=20points=20to=20= %s=20tid=3D%s=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20itid,=0A+=09=09= =09=09=09=20BTreeTupleGetNAtts(itup,=20state->rel),=0A+=09=09=09=09=09=20= P_ISLEAF(topaque)=20?=20"heap"=20:=20"index",=0A+=09=09=09=09=09=20htid,=0A= +=09=09=09=09=09=20(uint32)=20(state->targetlsn=20>>=2032),=0A+=09=09=09=09= =09=20(uint32)=20state->targetlsn);=0A=20=09=09}=0A=20=0A=20=09=09/*=0A= @@=20-1027,7=20+1109,8=20@@=20bt_target_page_check(BtreeCheckState=20= *state)=0A=20=09=09=09=09bt_child_highkey_check(state,=0A=20=09=09=09=09=09= =09=09=09=09=20=20=20offset,=0A=20=09=09=09=09=09=09=09=09=09=20=20=20= NULL,=0A-=09=09=09=09=09=09=09=09=09=20=20=20topaque->btpo.level);=0A+=09= =09=09=09=09=09=09=09=09=20=20=20topaque->btpo.level,=0A+=09=09=09=09=09=09= =09=09=09=20=20=20ctx);=0A=20=09=09=09}=0A=20=09=09=09continue;=0A=20=09=09= }=0A@@=20-1049,14=20+1132,13=20@@=20bt_target_page_check(BtreeCheckState=20= *state)=0A=20=09=09=09htid=20=3D=20psprintf("(%u,%u)",=20= ItemPointerGetBlockNumber(tid),=0A=20=09=09=09=09=09=09=09= ItemPointerGetOffsetNumber(tid));=0A=20=0A-=09=09=09ereport(ERROR,=0A-=09= =09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20= errmsg("could=20not=20find=20tuple=20using=20search=20from=20root=20page=20= in=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Index=20tid=3D%s=20points=20to=20heap=20tid=3D%s=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09itid,=20htid,=0A-=09= =09=09=09=09=09=09=09=09=09(uint32)=20(state->targetlsn=20>>=2032),=0A-=09= =09=09=09=09=09=09=09=09=09(uint32)=20state->targetlsn)));=0A+=09=09=09= econfess(state->ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=09=20"could=20not=20find=20tuple=20using=20search=20from=20= root=20page=20in=20index=20\"%s\"=20"=0A+=09=09=09=09=09=20"(Index=20= tid=3D%s=20points=20to=20heap=20tid=3D%s=20page=20lsn=3D%X/%X)",=0A+=09=09= =09=09=09=20RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20= itid,=20htid,=0A+=09=09=09=09=09=20(uint32)=20(state->targetlsn=20>>=20= 32),=0A+=09=09=09=09=09=20(uint32)=20state->targetlsn);=0A=20=09=09}=0A=20= =0A=20=09=09/*=0A@@=20-1079,14=20+1161,13=20@@=20= bt_target_page_check(BtreeCheckState=20*state)=0A=20=09=09=09=09{=0A=20=09= =09=09=09=09char=09=20=20=20*itid=20=3D=20psprintf("(%u,%u)",=20= state->targetblock,=20offset);=0A=20=0A-=09=09=09=09=09ereport(ERROR,=0A= -=09=09=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09= =09=09=20errmsg_internal("posting=20list=20contains=20misplaced=20TID=20= in=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09=09=09=09=09=20= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=09=09=20= errdetail_internal("Index=20tid=3D%s=20posting=20list=20offset=3D%d=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09=09=09itid,=20i,=0A= -=09=09=09=09=09=09=09=09=09=09=09=09(uint32)=20(state->targetlsn=20>>=20= 32),=0A-=09=09=09=09=09=09=09=09=09=09=09=09(uint32)=20= state->targetlsn)));=0A+=09=09=09=09=09econfess(state->ctx,=20= InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=09=09=20= "posting=20list=20contains=20misplaced=20TID=20in=20index=20\"%s\"=20"=0A= +=09=09=09=09=09=09=09=20"(Index=20tid=3D%s=20posting=20list=20offset=3D%d= =20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=09=09=20itid,=20= i,=0A+=09=09=09=09=09=09=09=20(uint32)=20(state->targetlsn=20>>=2032),=0A= +=09=09=09=09=09=09=09=20(uint32)=20state->targetlsn);=0A=20=09=09=09=09= }=0A=20=0A=20=09=09=09=09ItemPointerCopy(current,=20&last);=0A@@=20= -1134,16=20+1215,15=20@@=20bt_target_page_check(BtreeCheckState=20= *state)=0A=20=09=09=09=09=09=09=09ItemPointerGetBlockNumberNoCheck(tid),=0A= =20=09=09=09=09=09=09=09ItemPointerGetOffsetNumberNoCheck(tid));=0A=20=0A= -=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("index=20= row=20size=20%zu=20exceeds=20maximum=20for=20index=20\"%s\"",=0A-=09=09=09= =09=09=09=09tupsize,=20RelationGetRelationName(state->rel)),=0A-=09=09=09= =09=09=20errdetail_internal("Index=20tid=3D%s=20points=20to=20%s=20= tid=3D%s=20page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09itid,=0A= -=09=09=09=09=09=09=09=09=09=09P_ISLEAF(topaque)=20?=20"heap"=20:=20= "index",=0A-=09=09=09=09=09=09=09=09=09=09htid,=0A-=09=09=09=09=09=09=09=09= =09=09(uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09= =09=09(uint32)=20state->targetlsn)));=0A+=09=09=09econfess(state->ctx,=20= InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20= "index=20row=20size=20%zu=20exceeds=20maximum=20for=20index=20\"%s\"=20"=0A= +=09=09=09=09=09=20"(Index=20tid=3D%s=20points=20to=20%s=20tid=3D%s=20= page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=20tupsize,=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20itid,=0A+=09=09= =09=09=09=20P_ISLEAF(topaque)=20?=20"heap"=20:=20"index",=0A+=09=09=09=09= =09=20htid,=0A+=09=09=09=09=09=20(uint32)=20(state->targetlsn=20>>=20= 32),=0A+=09=09=09=09=09=20(uint32)=20state->targetlsn);=0A=20=09=09}=0A=20= =0A=20=09=09/*=20Fingerprint=20leaf=20page=20tuples=20(those=20that=20= point=20to=20the=20heap)=20*/=0A@@=20-1242,16=20+1322,15=20@@=20= bt_target_page_check(BtreeCheckState=20*state)=0A=20=09=09=09=09=09=09=09= ItemPointerGetBlockNumberNoCheck(tid),=0A=20=09=09=09=09=09=09=09= ItemPointerGetOffsetNumberNoCheck(tid));=0A=20=0A-=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=09=20errmsg("high=20key=20invariant=20violated=20for=20index=20= \"%s\"",=0A-=09=09=09=09=09=09=09RelationGetRelationName(state->rel)),=0A= -=09=09=09=09=09=20errdetail_internal("Index=20tid=3D%s=20points=20to=20= %s=20tid=3D%s=20page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09= itid,=0A-=09=09=09=09=09=09=09=09=09=09P_ISLEAF(topaque)=20?=20"heap"=20= :=20"index",=0A-=09=09=09=09=09=09=09=09=09=09htid,=0A-=09=09=09=09=09=09= =09=09=09=09(uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09= =09=09=09=09(uint32)=20state->targetlsn)));=0A+=09=09=09= econfess(state->ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=09=20"high=20key=20invariant=20violated=20for=20index=20= \"%s\"=20"=0A+=09=09=09=09=09=20"(Index=20tid=3D%s=20points=20to=20%s=20= tid=3D%s=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20itid,=0A+=09=09= =09=09=09=20P_ISLEAF(topaque)=20?=20"heap"=20:=20"index",=0A+=09=09=09=09= =09=20htid,=0A+=09=09=09=09=09=20(uint32)=20(state->targetlsn=20>>=20= 32),=0A+=09=09=09=09=09=20(uint32)=20state->targetlsn);=0A=20=09=09}=0A=20= =09=09/*=20Reset,=20in=20case=20scantid=20was=20set=20to=20(itup)=20= posting=20tuple's=20max=20TID=20*/=0A=20=09=09skey->scantid=20=3D=20= scantid;=0A@@=20-1289,21=20+1368,20=20@@=20= bt_target_page_check(BtreeCheckState=20*state)=0A=20=09=09=09=09=09=09=09= =20ItemPointerGetBlockNumberNoCheck(tid),=0A=20=09=09=09=09=09=09=09=20= ItemPointerGetOffsetNumberNoCheck(tid));=0A=20=0A-=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=09=20errmsg("item=20order=20invariant=20violated=20for=20index=20= \"%s\"",=0A-=09=09=09=09=09=09=09RelationGetRelationName(state->rel)),=0A= -=09=09=09=09=09=20errdetail_internal("Lower=20index=20tid=3D%s=20= (points=20to=20%s=20tid=3D%s)=20"=0A-=09=09=09=09=09=09=09=09=09=09= "higher=20index=20tid=3D%s=20(points=20to=20%s=20tid=3D%s)=20"=0A-=09=09=09= =09=09=09=09=09=09=09"page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09= =09itid,=0A-=09=09=09=09=09=09=09=09=09=09P_ISLEAF(topaque)=20?=20"heap"=20= :=20"index",=0A-=09=09=09=09=09=09=09=09=09=09htid,=0A-=09=09=09=09=09=09= =09=09=09=09nitid,=0A-=09=09=09=09=09=09=09=09=09=09P_ISLEAF(topaque)=20= ?=20"heap"=20:=20"index",=0A-=09=09=09=09=09=09=09=09=09=09nhtid,=0A-=09=09= =09=09=09=09=09=09=09=09(uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09= =09=09=09=09=09=09=09=09(uint32)=20state->targetlsn)));=0A+=09=09=09= econfess(state->ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=09=20"item=20order=20invariant=20violated=20for=20index=20= \"%s\"=20"=0A+=09=09=09=09=09=20"(Lower=20index=20tid=3D%s=20(points=20= to=20%s=20tid=3D%s)=20"=0A+=09=09=09=09=09=20"higher=20index=20tid=3D%s=20= (points=20to=20%s=20tid=3D%s)=20"=0A+=09=09=09=09=09=20"page=20= lsn=3D%X/%X)",=0A+=09=09=09=09=09=20RelationGetRelationName(state->rel),=0A= +=09=09=09=09=09=20itid,=0A+=09=09=09=09=09=20P_ISLEAF(topaque)=20?=20= "heap"=20:=20"index",=0A+=09=09=09=09=09=20htid,=0A+=09=09=09=09=09=20= nitid,=0A+=09=09=09=09=09=20P_ISLEAF(topaque)=20?=20"heap"=20:=20= "index",=0A+=09=09=09=09=09=20nhtid,=0A+=09=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=09=09}=0A=20=0A=20=09=09/*=0A@@=20-1328,7=20= +1406,7=20@@=20bt_target_page_check(BtreeCheckState=20*state)=0A=20=09=09= =09BTScanInsert=20rightkey;=0A=20=0A=20=09=09=09/*=20Get=20item=20in=20= next/right=20page=20*/=0A-=09=09=09rightkey=20=3D=20= bt_right_page_check_scankey(state);=0A+=09=09=09rightkey=20=3D=20= bt_right_page_check_scankey(state,=20ctx);=0A=20=0A=20=09=09=09if=20= (rightkey=20&&=0A=20=09=09=09=09!invariant_g_offset(state,=20rightkey,=20= max))=0A@@=20-1343,7=20+1421,7=20@@=20= bt_target_page_check(BtreeCheckState=20*state)=0A=20=09=09=09=09if=20= (!state->readonly)=0A=20=09=09=09=09{=0A=20=09=09=09=09=09/*=20Get=20= fresh=20copy=20of=20target=20page=20*/=0A-=09=09=09=09=09state->target=20= =3D=20palloc_btree_page(state,=20state->targetblock);=0A+=09=09=09=09=09= state->target=20=3D=20palloc_btree_page(state,=20state->targetblock,=20= ctx);=0A=20=09=09=09=09=09/*=20Note=20that=20we=20deliberately=20do=20= not=20update=20target=20LSN=20*/=0A=20=09=09=09=09=09topaque=20=3D=20= (BTPageOpaque)=20PageGetSpecialPointer(state->target);=0A=20=0A@@=20= -1354,14=20+1432,13=20@@=20bt_target_page_check(BtreeCheckState=20= *state)=0A=20=09=09=09=09=09=09return;=0A=20=09=09=09=09}=0A=20=0A-=09=09= =09=09ereport(ERROR,=0A-=09=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=09=20errmsg("cross=20= page=20item=20order=20invariant=20violated=20for=20index=20\"%s\"",=0A-=09= =09=09=09=09=09=09=09RelationGetRelationName(state->rel)),=0A-=09=09=09=09= =09=09=20errdetail_internal("Last=20item=20on=20page=20tid=3D(%u,%u)=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09=09= state->targetblock,=20offset,=0A-=09=09=09=09=09=09=09=09=09=09=09= (uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09=09=09= =09(uint32)=20state->targetlsn)));=0A+=09=09=09=09econfess(state->ctx,=20= state->targetblock,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=09=20= "cross=20page=20item=20order=20invariant=20violated=20for=20index=20= \"%s\"=20"=0A+=09=09=09=09=09=09=20"(Last=20item=20on=20page=20= tid=3D(%u,%u)=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=09=20= state->targetblock,=20offset,=0A+=09=09=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=09=09=09}=0A=20=09=09}=0A=20=0A@@=20-1374,7=20= +1451,7=20@@=20bt_target_page_check(BtreeCheckState=20*state)=0A=20=09=09= =20*=20because=20it=20has=20no=20useful=20value=20to=20compare).=0A=20=09= =09=20*/=0A=20=09=09if=20(!P_ISLEAF(topaque)=20&&=20state->readonly)=0A-=09= =09=09bt_child_check(state,=20skey,=20offset);=0A+=09=09=09= bt_child_check(state,=20skey,=20offset,=20ctx);=0A=20=09}=0A=20=0A=20=09= /*=0A@@=20-1386,10=20+1463,11=20@@=20= bt_target_page_check(BtreeCheckState=20*state)=0A=20=09=20*=20right=20of=20= the=20child=20page=20pointer=20to=20by=20our=20rightmost=20downlink.=20=20= And=20they=0A=20=09=20*=20might=20have=20missing=20downlinks.=20=20This=20= final=20call=20checks=20for=20them.=0A=20=09=20*/=0A-=09if=20= (!P_ISLEAF(topaque)=20&&=20P_RIGHTMOST(topaque)=20&&=20state->readonly)=0A= +=09if=20(CONTINUE_CHECKING(state->ctx)=20&&=0A+=09=09!P_ISLEAF(topaque)=20= &&=20P_RIGHTMOST(topaque)=20&&=20state->readonly)=0A=20=09{=0A=20=09=09= bt_child_highkey_check(state,=20InvalidOffsetNumber,=0A-=09=09=09=09=09=09= =09=20=20=20NULL,=20topaque->btpo.level);=0A+=09=09=09=09=09=09=09=20=20=20= NULL,=20topaque->btpo.level,=20ctx);=0A=20=09}=0A=20}=0A=20=0A@@=20= -1410,7=20+1488,7=20@@=20bt_target_page_check(BtreeCheckState=20*state)=0A= =20=20*=20been=20concurrently=20deleted.=0A=20=20*/=0A=20static=20= BTScanInsert=0A-bt_right_page_check_scankey(BtreeCheckState=20*state)=0A= +bt_right_page_check_scankey(BtreeCheckState=20*state,=20= BtreeCheckContext=20*=20ctx)=0A=20{=0A=20=09BTPageOpaque=20opaque;=0A=20=09= ItemId=09=09rightitem;=0A@@=20-1455,7=20+1533,7=20@@=20= bt_right_page_check_scankey(BtreeCheckState=20*state)=0A=20=09{=0A=20=09=09= CHECK_FOR_INTERRUPTS();=0A=20=0A-=09=09rightpage=20=3D=20= palloc_btree_page(state,=20targetnext);=0A+=09=09rightpage=20=3D=20= palloc_btree_page(state,=20targetnext,=20ctx);=0A=20=09=09opaque=20=3D=20= (BTPageOpaque)=20PageGetSpecialPointer(rightpage);=0A=20=0A=20=09=09if=20= (!P_IGNORE(opaque)=20||=20P_RIGHTMOST(opaque))=0A@@=20-1666,7=20+1744,8=20= @@=20static=20void=0A=20bt_child_highkey_check(BtreeCheckState=20*state,=0A= =20=09=09=09=09=09=20=20=20OffsetNumber=20target_downlinkoffnum,=0A=20=09= =09=09=09=09=20=20=20Page=20loaded_child,=0A-=09=09=09=09=09=20=20=20= uint32=20target_level)=0A+=09=09=09=09=09=20=20=20uint32=20target_level,=0A= +=09=09=09=09=09=20=20=20BtreeCheckContext=20*=20ctx)=0A=20{=0A=20=09= BlockNumber=20blkno=20=3D=20state->prevrightlink;=0A=20=09Page=09=09= page;=0A@@=20-1708,7=20+1787,7=20@@=20= bt_child_highkey_check(BtreeCheckState=20*state,=0A=20=09}=0A=20=0A=20=09= /*=20Move=20to=20the=20right=20on=20the=20child=20level=20*/=0A-=09while=20= (true)=0A+=09while=20(CONTINUE_CHECKING(state->ctx))=0A=20=09{=0A=20=09=09= /*=0A=20=09=09=20*=20Did=20we=20traverse=20the=20whole=20tree=20level=20= and=20this=20is=20check=20for=20pages=20to=0A@@=20-1723,51=20+1802,47=20= @@=20bt_child_highkey_check(BtreeCheckState=20*state,=0A=20=0A=20=09=09= /*=20Did=20we=20traverse=20the=20whole=20tree=20level=20and=20don't=20= find=20next=20downlink?=20*/=0A=20=09=09if=20(blkno=20=3D=3D=20P_NONE)=0A= -=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("can't=20= traverse=20from=20downlink=20%u=20to=20downlink=20%u=20of=20index=20= \"%s\"",=0A-=09=09=09=09=09=09=09state->prevrightlink,=20downlink,=0A-=09= =09=09=09=09=09=09RelationGetRelationName(state->rel))));=0A+=09=09=09= econfess(state->ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=09=20"can't=20traverse=20from=20downlink=20%u=20to=20= downlink=20%u=20of=20index=20\"%s\"",=0A+=09=09=09=09=09=20= state->prevrightlink,=20downlink,=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09=09/*=20Load=20page=20= contents=20*/=0A=20=09=09if=20(blkno=20=3D=3D=20downlink=20&&=20= loaded_child)=0A=20=09=09=09page=20=3D=20loaded_child;=0A=20=09=09else=0A= -=09=09=09page=20=3D=20palloc_btree_page(state,=20blkno);=0A+=09=09=09= page=20=3D=20palloc_btree_page(state,=20blkno,=20ctx);=0A=20=0A=20=09=09= opaque=20=3D=20(BTPageOpaque)=20PageGetSpecialPointer(page);=0A=20=0A=20=09= =09/*=20The=20first=20page=20we=20visit=20at=20the=20level=20should=20be=20= leftmost=20*/=0A=20=09=09if=20(first=20&&=20= !BlockNumberIsValid(state->prevrightlink)=20&&=20!P_LEFTMOST(opaque))=0A= -=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("the=20= first=20child=20of=20leftmost=20target=20page=20is=20not=20leftmost=20of=20= its=20level=20in=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Target=20block=3D%u=20child=20block=3D%u=20target=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09= state->targetblock,=20blkno,=0A-=09=09=09=09=09=09=09=09=09=09(uint32)=20= (state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09=09=09(uint32)=20= state->targetlsn)));=0A+=09=09=09econfess(state->ctx,=20blkno,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20"the=20first=20child=20of=20= leftmost=20target=20page=20is=20not=20leftmost=20of=20its=20level=20in=20= index=20\"%s\"=20"=0A+=09=09=09=09=09=20"(Target=20block=3D%u=20child=20= block=3D%u=20target=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20= state->targetblock,=20blkno,=0A+=09=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=0A=20=09=09/*=20Check=20level=20for=20= non-ignorable=20page=20*/=0A=20=09=09if=20(!P_IGNORE(opaque)=20&&=20= opaque->btpo.level=20!=3D=20target_level=20-=201)=0A-=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=09=20errmsg("block=20found=20while=20following=20rightlinks=20= from=20child=20of=20index=20\"%s\"=20has=20invalid=20level",=0A-=09=09=09= =09=09=09=09RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Block=20pointed=20to=3D%u=20expected=20level=3D%u=20= level=20in=20pointed=20to=20block=3D%u.",=0A-=09=09=09=09=09=09=09=09=09=09= blkno,=20target_level=20-=201,=20opaque->btpo.level)));=0A+=09=09=09= econfess(state->ctx,=20blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09= =20"block=20found=20while=20following=20rightlinks=20from=20child=20of=20= index=20\"%s\"=20has=20invalid=20level=20"=0A+=09=09=09=09=09=20"(Block=20= pointed=20to=3D%u=20expected=20level=3D%u=20level=20in=20pointed=20to=20= block=3D%u)",=0A+=09=09=09=09=09=20RelationGetRelationName(state->rel),=0A= +=09=09=09=09=09=20blkno,=20target_level=20-=201,=20opaque->btpo.level);=0A= =20=0A=20=09=09/*=20Try=20to=20detect=20circular=20links=20*/=0A=20=09=09= if=20((!first=20&&=20blkno=20=3D=3D=20state->prevrightlink)=20||=20blkno=20= =3D=3D=20opaque->btpo_prev)=0A-=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20errmsg("circular=20= link=20chain=20found=20in=20block=20%u=20of=20index=20\"%s\"",=0A-=09=09=09= =09=09=09=09blkno,=20RelationGetRelationName(state->rel))));=0A+=09=09=09= econfess(state->ctx,=20blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09= =20"circular=20link=20chain=20found=20in=20block=20%u=20of=20index=20= \"%s\"",=0A+=09=09=09=09=09=20blkno,=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09=09if=20(blkno=20!=3D=20= downlink=20&&=20!P_IGNORE(opaque))=0A=20=09=09{=0A=20=09=09=09/*=20blkno=20= probably=20has=20missing=20parent=20downlink=20*/=0A-=09=09=09= bt_downlink_missing_check(state,=20rightsplit,=20blkno,=20page);=0A+=09=09= =09bt_downlink_missing_check(state,=20rightsplit,=20blkno,=20page,=20= ctx);=0A=20=09=09}=0A=20=0A=20=09=09rightsplit=20=3D=20= P_INCOMPLETE_SPLIT(opaque);=0A@@=20-1825,14=20+1900,13=20@@=20= bt_child_highkey_check(BtreeCheckState=20*state,=0A=20=09=09=09=09if=20= (pivotkey_offset=20>=20PageGetMaxOffsetNumber(state->target))=0A=20=09=09= =09=09{=0A=20=09=09=09=09=09if=20(P_RIGHTMOST(topaque))=0A-=09=09=09=09=09= =09ereport(ERROR,=0A-=09=09=09=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=09=09=09=20= errmsg("child=20high=20key=20is=20greater=20than=20rightmost=20pivot=20= key=20on=20target=20level=20in=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09= =09=09=09RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=09=09=09= =20errdetail_internal("Target=20block=3D%u=20child=20block=3D%u=20target=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09=09=09=09= state->targetblock,=20blkno,=0A-=09=09=09=09=09=09=09=09=09=09=09=09=09= (uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09=09=09= =09=09=09(uint32)=20state->targetlsn)));=0A+=09=09=09=09=09=09= econfess(state->ctx,=20blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09= =09=09=09=20"child=20high=20key=20is=20greater=20than=20rightmost=20= pivot=20key=20on=20target=20level=20in=20index=20\"%s\"=20"=0A+=09=09=09=09= =09=09=09=09=20"(Target=20block=3D%u=20child=20block=3D%u=20target=20= page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=09=09=09=20= state->targetblock,=20blkno,=0A+=09=09=09=09=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=09=09=09=09=09pivotkey_offset=20=3D=20P_HIKEY;=0A= =20=09=09=09=09}=0A=20=09=09=09=09itemid=20=3D=20= PageGetItemIdCareful(state,=20state->targetblock,=0A@@=20-1856,27=20= +1930,25=20@@=20bt_child_highkey_check(BtreeCheckState=20*state,=0A=20=09= =09=09=09=20*=20page.=0A=20=09=09=09=09=20*/=0A=20=09=09=09=09if=20= (!state->lowkey)=0A-=09=09=09=09=09ereport(ERROR,=0A-=09=09=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=09=09=20= errmsg("can't=20find=20left=20sibling=20high=20key=20in=20index=20= \"%s\"",=0A-=09=09=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=09=09=20= errdetail_internal("Target=20block=3D%u=20child=20block=3D%u=20target=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09=09=09= state->targetblock,=20blkno,=0A-=09=09=09=09=09=09=09=09=09=09=09=09= (uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09=09=09= =09=09(uint32)=20state->targetlsn)));=0A+=09=09=09=09=09= econfess(state->ctx,=20blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09= =09=09=20"can't=20find=20left=20sibling=20high=20key=20in=20index=20= \"%s\"=20"=0A+=09=09=09=09=09=09=09=20"(Target=20block=3D%u=20child=20= block=3D%u=20target=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=09=09=20= state->targetblock,=20blkno,=0A+=09=09=09=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=09=09=09=09itup=20=3D=20state->lowkey;=0A=20=09=09= =09}=0A=20=0A=20=09=09=09if=20(!bt_pivot_tuple_identical(highkey,=20= itup))=0A=20=09=09=09{=0A-=09=09=09=09ereport(ERROR,=0A-=09=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=09=20= errmsg("mismatch=20between=20parent=20key=20and=20child=20high=20key=20= in=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=09=20= errdetail_internal("Target=20block=3D%u=20child=20block=3D%u=20target=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09=09= state->targetblock,=20blkno,=0A-=09=09=09=09=09=09=09=09=09=09=09= (uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09=09=09= =09(uint32)=20state->targetlsn)));=0A+=09=09=09=09econfess(state->ctx,=20= blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=09=20"mismatch=20= between=20parent=20key=20and=20child=20high=20key=20in=20index=20\"%s\"=20= "=0A+=09=09=09=09=09=09=20"(Target=20block=3D%u=20child=20block=3D%u=20= target=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=09=20= state->targetblock,=20blkno,=0A+=09=09=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=09=09=09}=0A=20=09=09}=0A=20=0A@@=20-1913,7=20= +1985,7=20@@=20bt_child_highkey_check(BtreeCheckState=20*state,=0A=20=20= */=0A=20static=20void=0A=20bt_child_check(BtreeCheckState=20*state,=20= BTScanInsert=20targetkey,=0A-=09=09=09=20=20=20OffsetNumber=20= downlinkoffnum)=0A+=09=09=09=20=20=20OffsetNumber=20downlinkoffnum,=20= BtreeCheckContext=20*=20ctx)=0A=20{=0A=20=09ItemId=09=09itemid;=0A=20=09= IndexTuple=09itup;=0A@@=20-1978,7=20+2050,7=20@@=20= bt_child_check(BtreeCheckState=20*state,=20BTScanInsert=20targetkey,=0A=20= =09=20*=20the=20operator=20class=20obeys=20the=20transitive=20law.=0A=20=09= =20*/=0A=20=09topaque=20=3D=20(BTPageOpaque)=20= PageGetSpecialPointer(state->target);=0A-=09child=20=3D=20= palloc_btree_page(state,=20childblock);=0A+=09child=20=3D=20= palloc_btree_page(state,=20childblock,=20ctx);=0A=20=09copaque=20=3D=20= (BTPageOpaque)=20PageGetSpecialPointer(child);=0A=20=09maxoffset=20=3D=20= PageGetMaxOffsetNumber(child);=0A=20=0A@@=20-1987,7=20+2059,7=20@@=20= bt_child_check(BtreeCheckState=20*state,=20BTScanInsert=20targetkey,=0A=20= =09=20*=20check=20for=20downlink=20connectivity.=0A=20=09=20*/=0A=20=09= bt_child_highkey_check(state,=20downlinkoffnum,=0A-=09=09=09=09=09=09=20=20= =20child,=20topaque->btpo.level);=0A+=09=09=09=09=09=09=20=20=20child,=20= topaque->btpo.level,=20ctx);=0A=20=0A=20=09/*=0A=20=09=20*=20Since=20= there=20cannot=20be=20a=20concurrent=20VACUUM=20operation=20in=20= readonly=20mode,=0A@@=20-2014,17=20+2086,16=20@@=20= bt_child_check(BtreeCheckState=20*state,=20BTScanInsert=20targetkey,=0A=20= =09=20*=20to=20test.=0A=20=09=20*/=0A=20=09if=20(P_ISDELETED(copaque))=0A= -=09=09ereport(ERROR,=0A-=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A= -=09=09=09=09=20errmsg("downlink=20to=20deleted=20page=20found=20in=20= index=20\"%s\"",=0A-=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=20= errdetail_internal("Parent=20block=3D%u=20child=20block=3D%u=20parent=20= page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09state->targetblock,=20= childblock,=0A-=09=09=09=09=09=09=09=09=09(uint32)=20(state->targetlsn=20= >>=2032),=0A-=09=09=09=09=09=09=09=09=09(uint32)=20state->targetlsn)));=0A= +=09=09econfess(state->ctx,=20state->targetblock,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"downlink=20to=20deleted=20= page=20found=20in=20index=20\"%s\"=20"=0A+=09=09=09=09=20"(Parent=20= block=3D%u=20child=20block=3D%u=20parent=20page=20lsn=3D%X/%X)",=0A+=09=09= =09=09=20RelationGetRelationName(state->rel),=0A+=09=09=09=09=20= state->targetblock,=20childblock,=0A+=09=09=09=09=20(uint32)=20= (state->targetlsn=20>>=2032),=0A+=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=0A=20=09for=20(offset=20=3D=20= P_FIRSTDATAKEY(copaque);=0A-=09=09=20offset=20<=3D=20maxoffset;=0A+=09=09= =20offset=20<=3D=20maxoffset=20&&=20CONTINUE_CHECKING(state->ctx);=0A=20=09= =09=20offset=20=3D=20OffsetNumberNext(offset))=0A=20=09{=0A=20=09=09/*=0A= @@=20-2056,14=20+2127,13=20@@=20bt_child_check(BtreeCheckState=20*state,=20= BTScanInsert=20targetkey,=0A=20=0A=20=09=09if=20= (!invariant_l_nontarget_offset(state,=20targetkey,=20childblock,=20= child,=0A=20=09=09=09=09=09=09=09=09=09=09=20=20offset))=0A-=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=09=20errmsg("down-link=20lower=20bound=20invariant=20violated=20= for=20index=20\"%s\"",=0A-=09=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Parent=20block=3D%u=20child=20index=20tid=3D(%u,%u)=20= parent=20page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09=09= state->targetblock,=20childblock,=20offset,=0A-=09=09=09=09=09=09=09=09=09= =09(uint32)=20(state->targetlsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09=09= =09(uint32)=20state->targetlsn)));=0A+=09=09=09econfess(state->ctx,=20= state->targetblock,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20= "down-link=20lower=20bound=20invariant=20violated=20for=20index=20\"%s\"=20= "=0A+=09=09=09=09=09=20"(Parent=20block=3D%u=20child=20index=20= tid=3D(%u,%u)=20parent=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20= state->targetblock,=20childblock,=20offset,=0A+=09=09=09=09=09=20= (uint32)=20(state->targetlsn=20>>=2032),=0A+=09=09=09=09=09=20(uint32)=20= state->targetlsn);=0A=20=09}=0A=20=0A=20=09pfree(child);=0A@@=20-2084,7=20= +2154,7=20@@=20bt_child_check(BtreeCheckState=20*state,=20BTScanInsert=20= targetkey,=0A=20=20*/=0A=20static=20void=0A=20= bt_downlink_missing_check(BtreeCheckState=20*state,=20bool=20rightsplit,=0A= -=09=09=09=09=09=09=20=20BlockNumber=20blkno,=20Page=20page)=0A+=09=09=09= =09=09=09=20=20BlockNumber=20blkno,=20Page=20page,=20BtreeCheckContext=20= *=20ctx)=0A=20{=0A=20=09BTPageOpaque=20opaque=20=3D=20(BTPageOpaque)=20= PageGetSpecialPointer(page);=0A=20=09ItemId=09=09itemid;=0A@@=20-2150,14=20= +2220,13=20@@=20bt_downlink_missing_check(BtreeCheckState=20*state,=20= bool=20rightsplit,=0A=20=09=20*=20inconsistencies=20anywhere=20else.=0A=20= =09=20*/=0A=20=09if=20(P_ISLEAF(opaque))=0A-=09=09ereport(ERROR,=0A-=09=09= =09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20errmsg("leaf=20= index=20block=20lacks=20downlink=20in=20index=20\"%s\"",=0A-=09=09=09=09=09= =09RelationGetRelationName(state->rel)),=0A-=09=09=09=09=20= errdetail_internal("Block=3D%u=20page=20lsn=3D%X/%X.",=0A-=09=09=09=09=09= =09=09=09=09blkno,=0A-=09=09=09=09=09=09=09=09=09(uint32)=20(pagelsn=20= >>=2032),=0A-=09=09=09=09=09=09=09=09=09(uint32)=20pagelsn)));=0A+=09=09= econfess(state->ctx,=20blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20= "leaf=20index=20block=20lacks=20downlink=20in=20index=20\"%s\"=20"=0A+=09= =09=09=09=20"(Block=3D%u=20page=20lsn=3D%X/%X)",=0A+=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=20blkno,=0A+=09=09=09= =09=20(uint32)=20(pagelsn=20>>=2032),=0A+=09=09=09=09=20(uint32)=20= pagelsn);=0A=20=0A=20=09/*=20Descend=20from=20the=20given=20page,=20= which=20is=20an=20internal=20page=20*/=0A=20=09elog(DEBUG1,=20"checking=20= for=20interrupted=20multi-level=20deletion=20due=20to=20missing=20= downlink=20in=20index=20\"%s\"",=0A@@=20-2167,11=20+2236,11=20@@=20= bt_downlink_missing_check(BtreeCheckState=20*state,=20bool=20rightsplit,=0A= =20=09itemid=20=3D=20PageGetItemIdCareful(state,=20blkno,=20page,=20= P_FIRSTDATAKEY(opaque));=0A=20=09itup=20=3D=20(IndexTuple)=20= PageGetItem(page,=20itemid);=0A=20=09childblk=20=3D=20= BTreeTupleGetDownLink(itup);=0A-=09for=20(;;)=0A+=09while=20= (CONTINUE_CHECKING(state->ctx))=0A=20=09{=0A=20=09=09= CHECK_FOR_INTERRUPTS();=0A=20=0A-=09=09child=20=3D=20= palloc_btree_page(state,=20childblk);=0A+=09=09child=20=3D=20= palloc_btree_page(state,=20childblk,=20ctx);=0A=20=09=09copaque=20=3D=20= (BTPageOpaque)=20PageGetSpecialPointer(child);=0A=20=0A=20=09=09if=20= (P_ISLEAF(copaque))=0A@@=20-2179,13=20+2248,12=20@@=20= bt_downlink_missing_check(BtreeCheckState=20*state,=20bool=20rightsplit,=0A= =20=0A=20=09=09/*=20Do=20an=20extra=20sanity=20check=20in=20passing=20on=20= internal=20pages=20*/=0A=20=09=09if=20(copaque->btpo.level=20!=3D=20= level=20-=201)=0A-=09=09=09ereport(ERROR,=0A-=09=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20= errmsg_internal("downlink=20points=20to=20block=20in=20index=20\"%s\"=20= whose=20level=20is=20not=20one=20level=20down",=0A-=09=09=09=09=09=09=09=09= =09=20RelationGetRelationName(state->rel)),=0A-=09=09=09=09=09=20= errdetail_internal("Top=20parent/under=20check=20block=3D%u=20block=20= pointed=20to=3D%u=20expected=20level=3D%u=20level=20in=20pointed=20to=20= block=3D%u.",=0A-=09=09=09=09=09=09=09=09=09=09blkno,=20childblk,=0A-=09=09= =09=09=09=09=09=09=09=09level=20-=201,=20copaque->btpo.level)));=0A+=09=09= =09econfess(state->ctx,=20blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09= =09=20"downlink=20points=20to=20block=20in=20index=20\"%s\"=20whose=20= level=20is=20not=20one=20level=20down=20"=0A+=09=09=09=09=09=20"(Top=20= parent/under=20check=20block=3D%u=20block=20pointed=20to=3D%u=20expected=20= level=3D%u=20level=20in=20pointed=20to=20block=3D%u)",=0A+=09=09=09=09=09= =20RelationGetRelationName(state->rel),=0A+=09=09=09=09=09=20blkno,=20= childblk,=0A+=09=09=09=09=09=20level=20-=201,=20copaque->btpo.level);=0A=20= =0A=20=09=09level=20=3D=20copaque->btpo.level;=0A=20=09=09itemid=20=3D=20= PageGetItemIdCareful(state,=20childblk,=20child,=0A@@=20-2217,14=20= +2285,13=20@@=20bt_downlink_missing_check(BtreeCheckState=20*state,=20= bool=20rightsplit,=0A=20=09=20*=20parent/ancestor=20page)=20lacked=20a=20= downlink=20is=20incidental.=0A=20=09=20*/=0A=20=09if=20= (P_ISDELETED(copaque))=0A-=09=09ereport(ERROR,=0A-=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20= errmsg_internal("downlink=20to=20deleted=20leaf=20page=20found=20in=20= index=20\"%s\"",=0A-=09=09=09=09=09=09=09=09=20= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=20= errdetail_internal("Top=20parent/target=20block=3D%u=20leaf=20block=3D%u=20= top=20parent/under=20check=20lsn=3D%X/%X.",=0A-=09=09=09=09=09=09=09=09=09= blkno,=20childblk,=0A-=09=09=09=09=09=09=09=09=09(uint32)=20(pagelsn=20= >>=2032),=0A-=09=09=09=09=09=09=09=09=09(uint32)=20pagelsn)));=0A+=09=09= econfess(state->ctx,=20blkno,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20= "downlink=20to=20deleted=20leaf=20page=20found=20in=20index=20\"%s\"=20"=0A= +=09=09=09=09=20"(Top=20parent/target=20block=3D%u=20leaf=20block=3D%u=20= top=20parent/under=20check=20lsn=3D%X/%X)",=0A+=09=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=20blkno,=20= childblk,=0A+=09=09=09=09=20(uint32)=20(pagelsn=20>>=2032),=0A+=09=09=09=09= =20(uint32)=20pagelsn);=0A=20=0A=20=09/*=0A=20=09=20*=20Iff=20leaf=20= page=20is=20half-dead,=20its=20high=20key=20top=20parent=20link=20should=20= point=0A@@=20-2244,14=20+2311,13=20@@=20= bt_downlink_missing_check(BtreeCheckState=20*state,=20bool=20rightsplit,=0A= =20=09=09=09return;=0A=20=09}=0A=20=0A-=09ereport(ERROR,=0A-=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=20errmsg("internal=20= index=20block=20lacks=20downlink=20in=20index=20\"%s\"",=0A-=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=20= errdetail_internal("Block=3D%u=20level=3D%u=20page=20lsn=3D%X/%X.",=0A-=09= =09=09=09=09=09=09=09blkno,=20opaque->btpo.level,=0A-=09=09=09=09=09=09=09= =09(uint32)=20(pagelsn=20>>=2032),=0A-=09=09=09=09=09=09=09=09(uint32)=20= pagelsn)));=0A+=09econfess(state->ctx,=20blkno,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=20"internal=20index=20block=20= lacks=20downlink=20in=20index=20\"%s\"=20"=0A+=09=09=09=20"(Block=3D%u=20= level=3D%u=20page=20lsn=3D%X/%X)",=0A+=09=09=09=20= RelationGetRelationName(state->rel),=0A+=09=09=09=20blkno,=20= opaque->btpo.level,=0A+=09=09=09=20(uint32)=20(pagelsn=20>>=2032),=0A+=09= =09=09=20(uint32)=20pagelsn);=0A=20}=0A=20=0A=20/*=0A@@=20-2327,16=20= +2393,12=20@@=20bt_tuple_present_callback(Relation=20index,=20= ItemPointer=20tid,=20Datum=20*values,=0A=20=09/*=20Probe=20Bloom=20= filter=20--=20tuple=20should=20be=20present=20*/=0A=20=09if=20= (bloom_lacks_element(state->filter,=20(unsigned=20char=20*)=20norm,=0A=20= =09=09=09=09=09=09=09IndexTupleSize(norm)))=0A-=09=09ereport(ERROR,=0A-=09= =09=09=09(errcode(ERRCODE_DATA_CORRUPTED),=0A-=09=09=09=09=20= errmsg("heap=20tuple=20(%u,%u)=20from=20table=20\"%s\"=20lacks=20= matching=20index=20tuple=20within=20index=20\"%s\"",=0A-=09=09=09=09=09=09= ItemPointerGetBlockNumber(&(itup->t_tid)),=0A-=09=09=09=09=09=09= ItemPointerGetOffsetNumber(&(itup->t_tid)),=0A-=09=09=09=09=09=09= RelationGetRelationName(state->heaprel),=0A-=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=20!state->readonly=0A= -=09=09=09=09=20?=20errhint("Retrying=20verification=20using=20the=20= function=20bt_index_parent_check()=20might=20provide=20a=20more=20= specific=20error.")=0A-=09=09=09=09=20:=200));=0A+=09=09= econfess(state->ctx,=20ItemPointerGetBlockNumber(&(itup->t_tid)),=20= ERRCODE_DATA_CORRUPTED,=0A+=09=09=09=09=20"heap=20tuple=20(%u,%u)=20from=20= table=20\"%s\"=20lacks=20matching=20index=20tuple=20within=20index=20= \"%s\"",=0A+=09=09=09=09=20ItemPointerGetBlockNumber(&(itup->t_tid)),=0A= +=09=09=09=09=20ItemPointerGetOffsetNumber(&(itup->t_tid)),=0A+=09=09=09=09= =20RelationGetRelationName(state->heaprel),=0A+=09=09=09=09=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09= state->heaptuplespresent++;=0A=20=09pfree(itup);=0A@@=20-2395,7=20= +2457,7=20@@=20bt_normalize_tuple(BtreeCheckState=20*state,=20IndexTuple=20= itup)=0A=20=09if=20(!IndexTupleHasVarwidths(itup))=0A=20=09=09return=20= itup;=0A=20=0A-=09for=20(i=20=3D=200;=20i=20<=20tupleDescriptor->natts;=20= i++)=0A+=09for=20(i=20=3D=200;=20CONTINUE_CHECKING(state->ctx)=20&&=20i=20= <=20tupleDescriptor->natts;=20i++)=0A=20=09{=0A=20=09=09= Form_pg_attribute=20att;=0A=20=0A@@=20-2415,12=20+2477,11=20@@=20= bt_normalize_tuple(BtreeCheckState=20*state,=20IndexTuple=20itup)=0A=20=09= =09=20*=20should=20never=20be=20encountered=20here=0A=20=09=09=20*/=0A=20= =09=09if=20(VARATT_IS_EXTERNAL(DatumGetPointer(normalized[i])))=0A-=09=09= =09ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A= -=09=09=09=09=09=20errmsg("external=20varlena=20datum=20in=20tuple=20= that=20references=20heap=20row=20(%u,%u)=20in=20index=20\"%s\"",=0A-=09=09= =09=09=09=09=09ItemPointerGetBlockNumber(&(itup->t_tid)),=0A-=09=09=09=09= =09=09=09ItemPointerGetOffsetNumber(&(itup->t_tid)),=0A-=09=09=09=09=09=09= =09RelationGetRelationName(state->rel))));=0A+=09=09=09= econfess(state->ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=09=20"external=20varlena=20datum=20in=20tuple=20that=20= references=20heap=20row=20(%u,%u)=20in=20index=20\"%s\"",=0A+=09=09=09=09= =09=20ItemPointerGetBlockNumber(&(itup->t_tid)),=0A+=09=09=09=09=09=20= ItemPointerGetOffsetNumber(&(itup->t_tid)),=0A+=09=09=09=09=09=20= RelationGetRelationName(state->rel));=0A=20=09=09else=20if=20= (VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])))=0A=20=09=09{=0A=20= =09=09=09formnewtup=20=3D=20true;=0A@@=20-2780,7=20+2841,7=20@@=20= invariant_l_nontarget_offset(BtreeCheckState=20*state,=20BTScanInsert=20= key,=0A=20=20*=20misbehaves.=0A=20=20*/=0A=20static=20Page=0A= -palloc_btree_page(BtreeCheckState=20*state,=20BlockNumber=20blocknum)=0A= +palloc_btree_page(BtreeCheckState=20*state,=20BlockNumber=20blocknum,=20= BtreeCheckContext=20*=20ctx)=0A=20{=0A=20=09Buffer=09=09buffer;=0A=20=09= Page=09=09page;=0A@@=20-2810,10=20+2871,9=20@@=20= palloc_btree_page(BtreeCheckState=20*state,=20BlockNumber=20blocknum)=0A=20= =09opaque=20=3D=20(BTPageOpaque)=20PageGetSpecialPointer(page);=0A=20=0A=20= =09if=20(P_ISMETA(opaque)=20&&=20blocknum=20!=3D=20BTREE_METAPAGE)=0A-=09= =09ereport(ERROR,=0A-=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=20errmsg("invalid=20meta=20page=20found=20at=20block=20%u=20in=20= index=20\"%s\"",=0A-=09=09=09=09=09=09blocknum,=20= RelationGetRelationName(state->rel))));=0A+=09=09econfess(state->ctx,=20= blocknum,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"invalid=20meta=20= page=20found=20at=20block=20%u=20in=20index=20\"%s\"",=0A+=09=09=09=09=20= blocknum,=20RelationGetRelationName(state->rel));=0A=20=0A=20=09/*=20= Check=20page=20from=20block=20that=20ought=20to=20be=20meta=20page=20*/=0A= =20=09if=20(blocknum=20=3D=3D=20BTREE_METAPAGE)=0A@@=20-2822,20=20= +2882,18=20@@=20palloc_btree_page(BtreeCheckState=20*state,=20= BlockNumber=20blocknum)=0A=20=0A=20=09=09if=20(!P_ISMETA(opaque)=20||=0A=20= =09=09=09metad->btm_magic=20!=3D=20BTREE_MAGIC)=0A-=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=09=20errmsg("index=20\"%s\"=20meta=20page=20is=20corrupt",=0A-=09= =09=09=09=09=09=09RelationGetRelationName(state->rel))));=0A+=09=09=09= econfess(state->ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=09=20"index=20\"%s\"=20meta=20page=20is=20corrupt",=0A+=09=09= =09=09=09=20RelationGetRelationName(state->rel));=0A=20=0A=20=09=09if=20= (metad->btm_version=20<=20BTREE_MIN_VERSION=20||=0A=20=09=09=09= metad->btm_version=20>=20BTREE_VERSION)=0A-=09=09=09ereport(ERROR,=0A-=09= =09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=09=20= errmsg("version=20mismatch=20in=20index=20\"%s\":=20file=20version=20%d,=20= "=0A-=09=09=09=09=09=09=09"current=20version=20%d,=20minimum=20supported=20= version=20%d",=0A-=09=09=09=09=09=09=09= RelationGetRelationName(state->rel),=0A-=09=09=09=09=09=09=09= metad->btm_version,=20BTREE_VERSION,=0A-=09=09=09=09=09=09=09= BTREE_MIN_VERSION)));=0A+=09=09=09econfess(state->ctx,=20= InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=09=20= "version=20mismatch=20in=20index=20\"%s\":=20file=20version=20%d,=20"=0A= +=09=09=09=09=09=20"current=20version=20%d,=20minimum=20supported=20= version=20%d",=0A+=09=09=09=09=09=20RelationGetRelationName(state->rel),=0A= +=09=09=09=09=09=20metad->btm_version,=20BTREE_VERSION,=0A+=09=09=09=09=09= =20BTREE_MIN_VERSION);=0A=20=0A=20=09=09/*=20Finished=20with=20metapage=20= checks=20*/=0A=20=09=09return=20page;=0A@@=20-2846,17=20+2904,15=20@@=20= palloc_btree_page(BtreeCheckState=20*state,=20BlockNumber=20blocknum)=0A=20= =09=20*=20page=20level=0A=20=09=20*/=0A=20=09if=20(P_ISLEAF(opaque)=20&&=20= !P_ISDELETED(opaque)=20&&=20opaque->btpo.level=20!=3D=200)=0A-=09=09= ereport(ERROR,=0A-=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09= =09=09=20errmsg("invalid=20leaf=20page=20level=20%u=20for=20block=20%u=20= in=20index=20\"%s\"",=0A-=09=09=09=09=09=09opaque->btpo.level,=20= blocknum,=20RelationGetRelationName(state->rel))));=0A+=09=09= econfess(state->ctx,=20InvalidBlockNumber,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=20"invalid=20leaf=20page=20level=20%u=20for=20block=20%u=20= in=20index=20\"%s\"",=0A+=09=09=09=09=20opaque->btpo.level,=20blocknum,=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09if=20= (!P_ISLEAF(opaque)=20&&=20!P_ISDELETED(opaque)=20&&=0A=20=09=09= opaque->btpo.level=20=3D=3D=200)=0A-=09=09ereport(ERROR,=0A-=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20errmsg("invalid=20= internal=20page=20level=200=20for=20block=20%u=20in=20index=20\"%s\"",=0A= -=09=09=09=09=09=09blocknum,=20RelationGetRelationName(state->rel))));=0A= +=09=09econfess(state->ctx,=20blocknum,=20ERRCODE_INDEX_CORRUPTED,=0A+=09= =09=09=09=20"invalid=20internal=20page=20level=200=20for=20block=20%u=20= in=20index=20\"%s\"",=0A+=09=09=09=09=20blocknum,=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09/*=0A=20=09=20*=20= Sanity=20checks=20for=20number=20of=20items=20on=20page.=0A@@=20-2882,23=20= +2938,20=20@@=20palloc_btree_page(BtreeCheckState=20*state,=20= BlockNumber=20blocknum)=0A=20=09=20*/=0A=20=09maxoffset=20=3D=20= PageGetMaxOffsetNumber(page);=0A=20=09if=20(maxoffset=20>=20= MaxIndexTuplesPerPage)=0A-=09=09ereport(ERROR,=0A-=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20errmsg("Number=20of=20= items=20on=20block=20%u=20of=20index=20\"%s\"=20exceeds=20= MaxIndexTuplesPerPage=20(%u)",=0A-=09=09=09=09=09=09blocknum,=20= RelationGetRelationName(state->rel),=0A-=09=09=09=09=09=09= MaxIndexTuplesPerPage)));=0A+=09=09econfess(ctx,=20blocknum,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"Number=20of=20items=20on=20= block=20%u=20of=20index=20\"%s\"=20exceeds=20MaxIndexTuplesPerPage=20= (%u)",=0A+=09=09=09=09=20blocknum,=20= RelationGetRelationName(state->rel),=0A+=09=09=09=09=20= MaxIndexTuplesPerPage);=0A=20=0A=20=09if=20(!P_ISLEAF(opaque)=20&&=20= !P_ISDELETED(opaque)=20&&=20maxoffset=20<=20P_FIRSTDATAKEY(opaque))=0A-=09= =09ereport(ERROR,=0A-=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09= =09=09=09=20errmsg("internal=20block=20%u=20in=20index=20\"%s\"=20lacks=20= high=20key=20and/or=20at=20least=20one=20downlink",=0A-=09=09=09=09=09=09= blocknum,=20RelationGetRelationName(state->rel))));=0A+=09=09= econfess(ctx,=20blocknum,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20= "internal=20block=20%u=20in=20index=20\"%s\"=20lacks=20high=20key=20= and/or=20at=20least=20one=20downlink",=0A+=09=09=09=09=20blocknum,=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09if=20= (P_ISLEAF(opaque)=20&&=20!P_ISDELETED(opaque)=20&&=20= !P_RIGHTMOST(opaque)=20&&=20maxoffset=20<=20P_HIKEY)=0A-=09=09= ereport(ERROR,=0A-=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09= =09=09=20errmsg("non-rightmost=20leaf=20block=20%u=20in=20index=20\"%s\"=20= lacks=20high=20key=20item",=0A-=09=09=09=09=09=09blocknum,=20= RelationGetRelationName(state->rel))));=0A+=09=09econfess(ctx,=20= blocknum,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"non-rightmost=20= leaf=20block=20%u=20in=20index=20\"%s\"=20lacks=20high=20key=20item",=0A= +=09=09=09=09=20blocknum,=20RelationGetRelationName(state->rel));=0A=20=0A= =20=09/*=0A=20=09=20*=20In=20general,=20internal=20pages=20are=20never=20= marked=20half-dead,=20except=20on=0A@@=20-2910,17=20+2963,15=20@@=20= palloc_btree_page(BtreeCheckState=20*state,=20BlockNumber=20blocknum)=0A=20= =09=20*=20Internal=20pages=20should=20never=20have=20garbage=20items,=20= either.=0A=20=09=20*/=0A=20=09if=20(!P_ISLEAF(opaque)=20&&=20= P_ISHALFDEAD(opaque))=0A-=09=09ereport(ERROR,=0A-=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20errmsg("internal=20= page=20block=20%u=20in=20index=20\"%s\"=20is=20half-dead",=0A-=09=09=09=09= =09=09blocknum,=20RelationGetRelationName(state->rel)),=0A-=09=09=09=09=20= errhint("This=20can=20be=20caused=20by=20an=20interrupted=20VACUUM=20in=20= version=209.3=20or=20older,=20before=20upgrade.=20Please=20REINDEX=20= it.")));=0A+=09=09econfess(state->ctx,=20blocknum,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"internal=20page=20block=20%u=20= in=20index=20\"%s\"=20is=20half-dead=20"=0A+=09=09=09=09=20"(This=20can=20= be=20caused=20by=20an=20interrupted=20VACUUM=20in=20version=209.3=20or=20= older,=20before=20upgrade.=20Please=20REINDEX=20it)",=0A+=09=09=09=09=20= blocknum,=20RelationGetRelationName(state->rel));=0A=20=0A=20=09if=20= (!P_ISLEAF(opaque)=20&&=20P_HAS_GARBAGE(opaque))=0A-=09=09ereport(ERROR,=0A= -=09=09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20= errmsg("internal=20page=20block=20%u=20in=20index=20\"%s\"=20has=20= garbage=20items",=0A-=09=09=09=09=09=09blocknum,=20= RelationGetRelationName(state->rel))));=0A+=09=09econfess(state->ctx,=20= blocknum,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"internal=20page=20= block=20%u=20in=20index=20\"%s\"=20has=20garbage=20items",=0A+=09=09=09=09= =20blocknum,=20RelationGetRelationName(state->rel));=0A=20=0A=20=09= return=20page;=0A=20}=0A@@=20-2971,14=20+3022,13=20@@=20= PageGetItemIdCareful(BtreeCheckState=20*state,=20BlockNumber=20block,=20= Page=20page,=0A=20=0A=20=09if=20(ItemIdGetOffset(itemid)=20+=20= ItemIdGetLength(itemid)=20>=0A=20=09=09BLCKSZ=20-=20= sizeof(BTPageOpaqueData))=0A-=09=09ereport(ERROR,=0A-=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20errmsg("line=20= pointer=20points=20past=20end=20of=20tuple=20space=20in=20index=20= \"%s\"",=0A-=09=09=09=09=09=09RelationGetRelationName(state->rel)),=0A-=09= =09=09=09=20errdetail_internal("Index=20tid=3D(%u,%u)=20lp_off=3D%u,=20= lp_len=3D%u=20lp_flags=3D%u.",=0A-=09=09=09=09=09=09=09=09=09block,=20= offset,=20ItemIdGetOffset(itemid),=0A-=09=09=09=09=09=09=09=09=09= ItemIdGetLength(itemid),=0A-=09=09=09=09=09=09=09=09=09= ItemIdGetFlags(itemid))));=0A+=09=09econfess(state->ctx,=20block,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"line=20pointer=20points=20= past=20end=20of=20tuple=20space=20in=20index=20\"%s\"=20"=0A+=09=09=09=09= =20"(Index=20tid=3D(%u,%u)=20lp_off=3D%u,=20lp_len=3D%u=20lp_flags=3D%u)",= =0A+=09=09=09=09=20RelationGetRelationName(state->rel),=0A+=09=09=09=09=20= block,=20offset,=20ItemIdGetOffset(itemid),=0A+=09=09=09=09=20= ItemIdGetLength(itemid),=0A+=09=09=09=09=20ItemIdGetFlags(itemid));=0A=20= =0A=20=09/*=0A=20=09=20*=20Verify=20that=20line=20pointer=20isn't=20= LP_REDIRECT=20or=20LP_UNUSED,=20since=20nbtree=0A@@=20-2987,14=20= +3037,13=20@@=20PageGetItemIdCareful(BtreeCheckState=20*state,=20= BlockNumber=20block,=20Page=20page,=0A=20=09=20*/=0A=20=09if=20= (ItemIdIsRedirected(itemid)=20||=20!ItemIdIsUsed(itemid)=20||=0A=20=09=09= ItemIdGetLength(itemid)=20=3D=3D=200)=0A-=09=09ereport(ERROR,=0A-=09=09=09= =09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20errmsg("invalid=20= line=20pointer=20storage=20in=20index=20\"%s\"",=0A-=09=09=09=09=09=09= RelationGetRelationName(state->rel)),=0A-=09=09=09=09=20= errdetail_internal("Index=20tid=3D(%u,%u)=20lp_off=3D%u,=20lp_len=3D%u=20= lp_flags=3D%u.",=0A-=09=09=09=09=09=09=09=09=09block,=20offset,=20= ItemIdGetOffset(itemid),=0A-=09=09=09=09=09=09=09=09=09= ItemIdGetLength(itemid),=0A-=09=09=09=09=09=09=09=09=09= ItemIdGetFlags(itemid))));=0A+=09=09econfess(state->ctx,=20block,=20= ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"invalid=20line=20pointer=20= storage=20in=20index=20\"%s\"=20"=0A+=09=09=09=09=20"(Index=20= tid=3D(%u,%u)=20lp_off=3D%u,=20lp_len=3D%u=20lp_flags=3D%u)",=0A+=09=09=09= =09=20RelationGetRelationName(state->rel),=0A+=09=09=09=09=20block,=20= offset,=20ItemIdGetOffset(itemid),=0A+=09=09=09=09=20= ItemIdGetLength(itemid),=0A+=09=09=09=09=20ItemIdGetFlags(itemid));=0A=20= =0A=20=09return=20itemid;=0A=20}=0A@@=20-3016,26=20+3065,23=20@@=20= BTreeTupleGetHeapTIDCareful(BtreeCheckState=20*state,=20IndexTuple=20= itup,=0A=20=09=20*/=0A=20=09Assert(state->heapkeyspace);=0A=20=09if=20= (BTreeTupleIsPivot(itup)=20&&=20nonpivot)=0A-=09=09ereport(ERROR,=0A-=09=09= =09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20= errmsg_internal("block=20%u=20or=20its=20right=20sibling=20block=20or=20= child=20block=20in=20index=20\"%s\"=20has=20unexpected=20pivot=20tuple",=0A= -=09=09=09=09=09=09=09=09=20state->targetblock,=0A-=09=09=09=09=09=09=09=09= =20RelationGetRelationName(state->rel))));=0A+=09=09econfess(state->ctx,=20= state->targetblock,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"block=20= %u=20or=20its=20right=20sibling=20block=20or=20child=20block=20in=20= index=20\"%s\"=20has=20unexpected=20pivot=20tuple",=0A+=09=09=09=09=20= state->targetblock,=0A+=09=09=09=09=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09if=20= (!BTreeTupleIsPivot(itup)=20&&=20!nonpivot)=0A-=09=09ereport(ERROR,=0A-=09= =09=09=09(errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20= errmsg_internal("block=20%u=20or=20its=20right=20sibling=20block=20or=20= child=20block=20in=20index=20\"%s\"=20has=20unexpected=20non-pivot=20= tuple",=0A-=09=09=09=09=09=09=09=09=20state->targetblock,=0A-=09=09=09=09= =09=09=09=09=20RelationGetRelationName(state->rel))));=0A+=09=09= econfess(state->ctx,=20state->targetblock,=20ERRCODE_INDEX_CORRUPTED,=0A= +=09=09=09=09=20"block=20%u=20or=20its=20right=20sibling=20block=20or=20= child=20block=20in=20index=20\"%s\"=20has=20unexpected=20non-pivot=20= tuple",=0A+=09=09=09=09=20state->targetblock,=0A+=09=09=09=09=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09htid=20=3D=20= BTreeTupleGetHeapTID(itup);=0A=20=09if=20(!ItemPointerIsValid(htid)=20&&=20= nonpivot)=0A-=09=09ereport(ERROR,=0A-=09=09=09=09= (errcode(ERRCODE_INDEX_CORRUPTED),=0A-=09=09=09=09=20errmsg("block=20%u=20= or=20its=20right=20sibling=20block=20or=20child=20block=20in=20index=20= \"%s\"=20contains=20non-pivot=20tuple=20that=20lacks=20a=20heap=20TID",=0A= -=09=09=09=09=09=09state->targetblock,=0A-=09=09=09=09=09=09= RelationGetRelationName(state->rel))));=0A+=09=09econfess(state->ctx,=20= state->targetblock,=20ERRCODE_INDEX_CORRUPTED,=0A+=09=09=09=09=20"block=20= %u=20or=20its=20right=20sibling=20block=20or=20child=20block=20in=20= index=20\"%s\"=20contains=20non-pivot=20tuple=20that=20lacks=20a=20heap=20= TID",=0A+=09=09=09=09=20state->targetblock,=0A+=09=09=09=09=20= RelationGetRelationName(state->rel));=0A=20=0A=20=09return=20htid;=0A=20= }=0A@@=20-3066,3=20+3112,53=20@@=20BTreeTupleGetPointsToTID(IndexTuple=20= itup)=0A=20=09/*=20Pivot=20tuple=20returns=20TID=20with=20downlink=20= block=20(heapkeyspace=20variant)=20*/=0A=20=09return=20&itup->t_tid;=0A=20= }=0A+=0A+/*=0A+=20*=20Helper=20function=20to=20construct=20the=20= TupleDesc=20needed=20by=20verify_heapam.=0A+=20*/=0A+static=20TupleDesc=0A= +verify_btreeam_tupdesc(void)=0A+{=0A+=09TupleDesc=09tupdesc;=0A+=09= AttrNumber=09a=20=3D=200;=0A+=0A+=09tupdesc=20=3D=20= CreateTemplateTupleDesc(BTREECHECK_RELATION_COLS);=0A+=09= TupleDescInitEntry(tupdesc,=20++a,=20"blkno",=20INT8OID,=20-1,=200);=0A+=09= TupleDescInitEntry(tupdesc,=20++a,=20"msg",=20TEXTOID,=20-1,=200);=0A+=09= Assert(a=20=3D=3D=20BTREECHECK_RELATION_COLS);=0A+=0A+=09return=20= BlessTupleDesc(tupdesc);=0A+}=0A+=0A+/*=0A+=20*=20confess=0A+=20*=0A+=20= *=20=20=20Return=20a=20message=20about=20index=20corruption=0A+=20*=0A+=20= *=20=20=20The=20msg=20argument=20is=20pfree'd=20by=20this=20function.=0A= +=20*/=0A+static=20void=0A+confess(BtreeCheckContext=20*=20ctx,=20= BlockNumber=20blkno,=20char=20*msg)=0A+{=0A+=09Datum=09=09= values[BTREECHECK_RELATION_COLS];=0A+=09bool=09=09= nulls[BTREECHECK_RELATION_COLS];=0A+=09HeapTuple=09tuple;=0A+=0A+=09= MemSet(values,=200,=20sizeof(values));=0A+=09MemSet(nulls,=200,=20= sizeof(nulls));=0A+=09values[0]=20=3D=20Int64GetDatum(blkno);=0A+=09= nulls[0]=20=3D=20(blkno=20=3D=3D=20InvalidBlockNumber);=0A+=09values[1]=20= =3D=20CStringGetTextDatum(msg);=0A+=0A+=09/*=0A+=09=20*=20In=20= principle,=20there=20is=20nothing=20to=20prevent=20a=20scan=20over=20a=20= large,=20highly=0A+=09=20*=20corrupted=20table=20from=20using=20workmem=20= worth=20of=20memory=20building=20up=20the=0A+=09=20*=20tuplestore.=20=20= That=20is=20OK,=20but=20leaves=20no=20room=20for=20leaking=20all=20the=20= msg=0A+=09=20*=20arguments=20that=20are=20allocated=20during=20the=20= scan.=0A+=09=20*/=0A+=09pfree(msg);=0A+=0A+=09tuple=20=3D=20= heap_form_tuple(ctx->tupdesc,=20values,=20nulls);=0A+=09= tuplestore_puttuple(ctx->tupstore,=20tuple);=0A+=09ctx->is_corrupt=20=3D=20= true;=0A+}=0Adiff=20--git=20a/src/tools/pgindent/typedefs.list=20= b/src/tools/pgindent/typedefs.list=0Aindex=207eaaad1e14..467120f1d0=20= 100644=0A---=20a/src/tools/pgindent/typedefs.list=0A+++=20= b/src/tools/pgindent/typedefs.list=0A@@=20-275,6=20+275,7=20@@=20= BrinSpecialSpace=0A=20BrinStatsData=0A=20BrinTuple=0A=20BrinValues=0A= +BtreeCheckContext=0A=20BtreeCheckState=0A=20BtreeLevel=0A=20Bucket=0A--=20= =0A2.21.1=20(Apple=20Git-122.3)=0A=0A= --Apple-Mail=_268BCDFB-5F95-4F65-8E72-69AFC6227897 Content-Disposition: attachment; filename=v13-0002-Adding-function-verify_heapam-to-amcheck-module.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v13-0002-Adding-function-verify_heapam-to-amcheck-module.patch" Content-Transfer-Encoding: quoted-printable =46rom=206846abccc7c87b9377dc2f12bb6d8321f445d118=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Mark=20Dilger=20=0A= Date:=20Mon,=2027=20Jul=202020=2008:03:53=20-0700=0ASubject:=20[PATCH=20= v13=202/3]=20Adding=20function=20verify_heapam=20to=20amcheck=20module=0A= =0AAdding=20new=20function=20verify_heapam=20for=20checking=20a=20heap=20= relation=20and=0Aassociated=20toast=20relation,=20if=20any,=20to=20= contrib/amcheck.=0A---=0A=20contrib/amcheck/Makefile=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=20=205=20+-=0A=20= contrib/amcheck/amcheck--1.2--1.3.sql=20=20=20=20=20=20=20=20=20|=20=20=20= 54=20+=0A=20contrib/amcheck/amcheck.h=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20|=20=20=20=205=20+=0A=20= contrib/amcheck/expected/check_heap.out=20=20=20=20=20=20=20|=20=20=2067=20= +=0A=20.../amcheck/expected/disallowed_reltypes.out=20=20|=20=20=2048=20= +=0A=20contrib/amcheck/sql/check_heap.sql=20=20=20=20=20=20=20=20=20=20=20= =20|=20=20=2019=20+=0A=20contrib/amcheck/sql/disallowed_reltypes.sql=20=20= =20|=20=20=2048=20+=0A=20contrib/amcheck/t/001_verify_heapam.pl=20=20=20=20= =20=20=20=20|=20=20=2094=20++=0A=20contrib/amcheck/verify_heapam.c=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20|=201139=20+++++++++++++++++=0A=20= doc/src/sgml/amcheck.sgml=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20|=20=20106=20+-=0A=20src/backend/access/heap/hio.c=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=2011=20+=0A=20= src/tools/pgindent/typedefs.list=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=20=20=202=20+=0A=2012=20files=20changed,=201596=20insertions(+),=20= 2=20deletions(-)=0A=20create=20mode=20100644=20= contrib/amcheck/amcheck--1.2--1.3.sql=0A=20create=20mode=20100644=20= contrib/amcheck/amcheck.h=0A=20create=20mode=20100644=20= contrib/amcheck/expected/check_heap.out=0A=20create=20mode=20100644=20= contrib/amcheck/expected/disallowed_reltypes.out=0A=20create=20mode=20= 100644=20contrib/amcheck/sql/check_heap.sql=0A=20create=20mode=20100644=20= contrib/amcheck/sql/disallowed_reltypes.sql=0A=20create=20mode=20100644=20= contrib/amcheck/t/001_verify_heapam.pl=0A=20create=20mode=20100644=20= contrib/amcheck/verify_heapam.c=0A=0Adiff=20--git=20= a/contrib/amcheck/Makefile=20b/contrib/amcheck/Makefile=0Aindex=20= b288c28fa0..27d38b2e86=20100644=0A---=20a/contrib/amcheck/Makefile=0A+++=20= b/contrib/amcheck/Makefile=0A@@=20-3,13=20+3,16=20@@=0A=20MODULE_big=09=3D= =20amcheck=0A=20OBJS=20=3D=20\=0A=20=09$(WIN32RES)=20\=0A+=09= verify_heapam.o=20\=0A=20=09verify_nbtree.o=0A=20=0A=20EXTENSION=20=3D=20= amcheck=0A=20DATA=20=3D=20amcheck--1.2--1.3.sql=20amcheck--1.1--1.2.sql=20= amcheck--1.0--1.1.sql=20amcheck--1.0.sql=0A=20PGFILEDESC=20=3D=20= "amcheck=20-=20function=20for=20verifying=20relation=20integrity"=0A=20=0A= -REGRESS=20=3D=20check=20check_btree=0A+REGRESS=20=3D=20check=20= check_btree=20check_heap=20disallowed_reltypes=0A+=0A+TAP_TESTS=20=3D=20= 1=0A=20=0A=20ifdef=20USE_PGXS=0A=20PG_CONFIG=20=3D=20pg_config=0Adiff=20= --git=20a/contrib/amcheck/amcheck--1.2--1.3.sql=20= b/contrib/amcheck/amcheck--1.2--1.3.sql=0Anew=20file=20mode=20100644=0A= index=200000000000..df418a850b=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/amcheck--1.2--1.3.sql=0A@@=20-0,0=20+1,54=20@@=0A+/*=20= contrib/amcheck/amcheck--1.2--1.3.sql=20*/=0A+=0A+--=20complain=20if=20= script=20is=20sourced=20in=20psql,=20rather=20than=20via=20CREATE=20= EXTENSION=0A+\echo=20Use=20"ALTER=20EXTENSION=20amcheck=20UPDATE=20TO=20= '1.3'"=20to=20load=20this=20file.=20\quit=0A+=0A+--=20In=20order=20to=20= avoid=20issues=20with=20dependencies=20when=20updating=20amcheck=20to=20= 1.3,=0A+--=20create=20new,=20overloaded=20version=20of=20the=201.2=20= function=20signature=0A+=0A+--=0A+--=20verify_heapam()=0A+--=0A+CREATE=20= FUNCTION=20verify_heapam(rel=20regclass,=0A+=09=09=09=09=09=09=09=20=20= on_error_stop=20boolean=20default=20false,=0A+=09=09=09=09=09=09=09=20=20= skip=20cstring=20default=20'none',=0A+=09=09=09=09=09=09=09=20=20= startblock=20bigint=20default=20null,=0A+=09=09=09=09=09=09=09=20=20= endblock=20bigint=20default=20null,=0A+=09=09=09=09=09=09=09=20=20blkno=20= OUT=20bigint,=0A+=09=09=09=09=09=09=09=20=20offnum=20OUT=20integer,=0A+=09= =09=09=09=09=09=09=20=20lp_off=20OUT=20smallint,=0A+=09=09=09=09=09=09=09= =20=20lp_flags=20OUT=20smallint,=0A+=09=09=09=09=09=09=09=20=20lp_len=20= OUT=20smallint,=0A+=09=09=09=09=09=09=09=20=20attnum=20OUT=20integer,=0A= +=09=09=09=09=09=09=09=20=20chunk=20OUT=20integer,=0A+=09=09=09=09=09=09=09= =20=20msg=20OUT=20text=0A+=09=09=09=09=09=09=09=20=20)=0A+RETURNS=20= SETOF=20record=0A+AS=20'MODULE_PATHNAME',=20'verify_heapam'=0A+LANGUAGE=20= C;=0A+=0A+--=20Don't=20want=20this=20to=20be=20available=20to=20public=0A= +REVOKE=20ALL=20ON=20FUNCTION=20verify_heapam(regclass,=20boolean,=20= cstring,=20bigint,=20bigint)=0A+FROM=20PUBLIC;=0A+=0A+--=0A+--=20= verify_btreeam()=0A+--=0A+CREATE=20FUNCTION=20verify_btreeam(rel=20= regclass,=0A+=09=09=09=09=09=09=09=20=20=20blkno=20OUT=20bigint,=0A+=09=09= =09=09=09=09=09=20=20=20msg=20OUT=20text)=0A+RETURNS=20SETOF=20record=0A= +AS=20'MODULE_PATHNAME',=20'verify_btreeam'=0A+LANGUAGE=20C;=0A+=0A= +CREATE=20FUNCTION=20verify_btreeam(rel=20regclass,=0A+=09=09=09=09=09=09= =09=20=20=20on_error_stop=20boolean,=0A+=09=09=09=09=09=09=09=20=20=20= blkno=20OUT=20bigint,=0A+=09=09=09=09=09=09=09=20=20=20msg=20OUT=20text)=0A= +RETURNS=20SETOF=20record=0A+AS=20'MODULE_PATHNAME',=20'verify_btreeam'=0A= +LANGUAGE=20C;=0A+=0A+--=20Don't=20want=20this=20to=20be=20available=20= to=20public=0A+REVOKE=20ALL=20ON=20FUNCTION=20verify_btreeam(regclass)=20= FROM=20PUBLIC;=0A+REVOKE=20ALL=20ON=20FUNCTION=20= verify_btreeam(regclass,=20boolean)=20FROM=20PUBLIC;=0Adiff=20--git=20= a/contrib/amcheck/amcheck.h=20b/contrib/amcheck/amcheck.h=0Anew=20file=20= mode=20100644=0Aindex=200000000000..74edfc2f65=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/amcheck.h=0A@@=20-0,0=20+1,5=20@@=0A+#include=20= "postgres.h"=0A+=0A+Datum=09=09verify_heapam(PG_FUNCTION_ARGS);=0A+Datum=09= =09bt_index_check(PG_FUNCTION_ARGS);=0A+Datum=09=09= bt_index_parent_check(PG_FUNCTION_ARGS);=0Adiff=20--git=20= a/contrib/amcheck/expected/check_heap.out=20= b/contrib/amcheck/expected/check_heap.out=0Anew=20file=20mode=20100644=0A= index=200000000000..4175bb2d37=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/expected/check_heap.out=0A@@=20-0,0=20+1,67=20@@=0A= +CREATE=20TABLE=20heaptest=20(a=20integer,=20b=20text);=0A+SELECT=20*=20= FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20skip=20:=3D=20'rope');=0A= +ERROR:=20=20unrecognized=20parameter=20for=20'skip':=20rope=0A+HINT:=20=20= please=20choose=20from=20'all-visible',=20'all-frozen',=20or=20'none'=0A= +SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20skip=20:=3D=20= 'none');=0A+=20blkno=20|=20offnum=20|=20lp_off=20|=20lp_flags=20|=20= lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'all-frozen');=0A+=20blkno=20|=20offnum=20|=20= lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'all-visible');=0A+=20blkno=20|=20offnum=20|=20= lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20startblock=20:=3D=200,=20endblock=20:=3D=200);=0A+ERROR:=20= =20starting=20block=200=20is=20out=20of=20bounds=20for=20relation=20with=20= no=20blocks=0A+INSERT=20INTO=20heaptest=20(a,=20b)=0A+=09(SELECT=20gs,=20= repeat('x',=20gs)=0A+=09=09FROM=20generate_series(1,10000)=20gs);=0A= +SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20startblock=20= :=3D=20100000,=20endblock=20:=3D=20200000);=0A+ERROR:=20=20block=20range=20= 100000=20..=20200000=20is=20out=20of=20bounds=20for=20relation=20with=20= block=20count=20370=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'none');=0A+=20blkno=20|=20offnum=20|=20= lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'all-frozen');=0A+=20blkno=20|=20offnum=20|=20= lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'all-visible');=0A+=20blkno=20|=20offnum=20|=20= lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20startblock=20:=3D=200,=20endblock=20:=3D=200);=0A+=20blkno=20= |=20offnum=20|=20lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20= chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+VACUUM=20FREEZE=20heaptest;=0A+SELECT=20*=20FROM=20= verify_heapam(rel=20:=3D=20'heaptest',=20skip=20:=3D=20'none');=0A+=20= blkno=20|=20offnum=20|=20lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20= |=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'all-frozen');=0A+=20blkno=20|=20offnum=20|=20= lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'all-visible');=0A+=20blkno=20|=20offnum=20|=20= lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20startblock=20:=3D=200,=20endblock=20:=3D=200);=0A+=20blkno=20= |=20offnum=20|=20lp_off=20|=20lp_flags=20|=20lp_len=20|=20attnum=20|=20= chunk=20|=20msg=20=0A= +-------+--------+--------+----------+--------+--------+-------+-----=0A= +(0=20rows)=0A+=0Adiff=20--git=20= a/contrib/amcheck/expected/disallowed_reltypes.out=20= b/contrib/amcheck/expected/disallowed_reltypes.out=0Anew=20file=20mode=20= 100644=0Aindex=200000000000..892ae89652=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/expected/disallowed_reltypes.out=0A@@=20-0,0=20+1,48=20= @@=0A+--=0A+--=20check=20that=20using=20the=20module's=20functions=20= with=20unsupported=20relations=20will=20fail=0A+--=0A+--=20partitioned=20= tables=20(the=20parent=20ones)=20don't=20have=20visibility=20maps=0A= +create=20table=20test_partitioned=20(a=20int,=20b=20text=20default=20= repeat('x',=205000))=0A+=09=09=09=20partition=20by=20list=20(a);=0A+--=20= these=20should=20all=20fail=0A+select=20*=20from=20= verify_heapam('test_partitioned',=0A+=09=09=09=09=09=09=09on_error_stop=20= :=3D=20false,=0A+=09=09=09=09=09=09=09skip=20:=3D=20NULL,=0A+=09=09=09=09= =09=09=09startblock=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09endblock=20:=3D= =20NULL);=0A+ERROR:=20=20"test_partitioned"=20is=20not=20a=20table,=20= materialized=20view,=20or=20TOAST=20table=0A+create=20table=20= test_partition=20partition=20of=20test_partitioned=20for=20values=20in=20= (1);=0A+create=20index=20test_index=20on=20test_partition=20(a);=0A+--=20= indexes=20do=20not,=20so=20these=20all=20fail=0A+select=20*=20from=20= verify_heapam('test_index',=0A+=09=09=09=09=09=09=09on_error_stop=20:=3D=20= false,=0A+=09=09=09=09=09=09=09skip=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09= startblock=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09endblock=20:=3D=20= NULL);=0A+ERROR:=20=20"test_index"=20is=20not=20a=20table,=20= materialized=20view,=20or=20TOAST=20table=0A+create=20view=20test_view=20= as=20select=201;=0A+--=20views=20do=20not=20have=20vms,=20so=20these=20= all=20fail=0A+select=20*=20from=20verify_heapam('test_view',=0A+=09=09=09= =09=09=09=09on_error_stop=20:=3D=20false,=0A+=09=09=09=09=09=09=09skip=20= :=3D=20NULL,=0A+=09=09=09=09=09=09=09startblock=20:=3D=20NULL,=0A+=09=09=09= =09=09=09=09endblock=20:=3D=20NULL);=0A+ERROR:=20=20"test_view"=20is=20= not=20a=20table,=20materialized=20view,=20or=20TOAST=20table=0A+create=20= sequence=20test_sequence;=0A+--=20sequences=20do=20not=20have=20vms,=20= so=20these=20all=20fail=0A+select=20*=20from=20= verify_heapam('test_sequence',=0A+=09=09=09=09=09=09=09on_error_stop=20= :=3D=20false,=0A+=09=09=09=09=09=09=09skip=20:=3D=20NULL,=0A+=09=09=09=09= =09=09=09startblock=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09endblock=20:=3D= =20NULL);=0A+ERROR:=20=20"test_sequence"=20is=20not=20a=20table,=20= materialized=20view,=20or=20TOAST=20table=0A+create=20foreign=20data=20= wrapper=20dummy;=0A+create=20server=20dummy_server=20foreign=20data=20= wrapper=20dummy;=0A+create=20foreign=20table=20test_foreign_table=20()=20= server=20dummy_server;=0A+--=20foreign=20tables=20do=20not=20have=20vms,=20= so=20these=20all=20fail=0A+select=20*=20from=20= verify_heapam('test_foreign_table',=0A+=09=09=09=09=09=09=09= on_error_stop=20:=3D=20false,=0A+=09=09=09=09=09=09=09skip=20:=3D=20= NULL,=0A+=09=09=09=09=09=09=09startblock=20:=3D=20NULL,=0A+=09=09=09=09=09= =09=09endblock=20:=3D=20NULL);=0A+ERROR:=20=20"test_foreign_table"=20is=20= not=20a=20table,=20materialized=20view,=20or=20TOAST=20table=0Adiff=20= --git=20a/contrib/amcheck/sql/check_heap.sql=20= b/contrib/amcheck/sql/check_heap.sql=0Anew=20file=20mode=20100644=0A= index=200000000000..c75f5ff869=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/sql/check_heap.sql=0A@@=20-0,0=20+1,19=20@@=0A+CREATE=20= TABLE=20heaptest=20(a=20integer,=20b=20text);=0A+SELECT=20*=20FROM=20= verify_heapam(rel=20:=3D=20'heaptest',=20skip=20:=3D=20'rope');=0A= +SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20skip=20:=3D=20= 'none');=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20= skip=20:=3D=20'all-frozen');=0A+SELECT=20*=20FROM=20verify_heapam(rel=20= :=3D=20'heaptest',=20skip=20:=3D=20'all-visible');=0A+SELECT=20*=20FROM=20= verify_heapam(rel=20:=3D=20'heaptest',=20startblock=20:=3D=200,=20= endblock=20:=3D=200);=0A+INSERT=20INTO=20heaptest=20(a,=20b)=0A+=09= (SELECT=20gs,=20repeat('x',=20gs)=0A+=09=09FROM=20= generate_series(1,10000)=20gs);=0A+SELECT=20*=20FROM=20verify_heapam(rel=20= :=3D=20'heaptest',=20startblock=20:=3D=20100000,=20endblock=20:=3D=20= 200000);=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20= skip=20:=3D=20'none');=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20= 'heaptest',=20skip=20:=3D=20'all-frozen');=0A+SELECT=20*=20FROM=20= verify_heapam(rel=20:=3D=20'heaptest',=20skip=20:=3D=20'all-visible');=0A= +SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20startblock=20= :=3D=200,=20endblock=20:=3D=200);=0A+VACUUM=20FREEZE=20heaptest;=0A= +SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20skip=20:=3D=20= 'none');=0A+SELECT=20*=20FROM=20verify_heapam(rel=20:=3D=20'heaptest',=20= skip=20:=3D=20'all-frozen');=0A+SELECT=20*=20FROM=20verify_heapam(rel=20= :=3D=20'heaptest',=20skip=20:=3D=20'all-visible');=0A+SELECT=20*=20FROM=20= verify_heapam(rel=20:=3D=20'heaptest',=20startblock=20:=3D=200,=20= endblock=20:=3D=200);=0Adiff=20--git=20= a/contrib/amcheck/sql/disallowed_reltypes.sql=20= b/contrib/amcheck/sql/disallowed_reltypes.sql=0Anew=20file=20mode=20= 100644=0Aindex=200000000000..fc90e6ca33=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/sql/disallowed_reltypes.sql=0A@@=20-0,0=20+1,48=20@@=0A= +--=0A+--=20check=20that=20using=20the=20module's=20functions=20with=20= unsupported=20relations=20will=20fail=0A+--=0A+=0A+--=20partitioned=20= tables=20(the=20parent=20ones)=20don't=20have=20visibility=20maps=0A= +create=20table=20test_partitioned=20(a=20int,=20b=20text=20default=20= repeat('x',=205000))=0A+=09=09=09=20partition=20by=20list=20(a);=0A+--=20= these=20should=20all=20fail=0A+select=20*=20from=20= verify_heapam('test_partitioned',=0A+=09=09=09=09=09=09=09on_error_stop=20= :=3D=20false,=0A+=09=09=09=09=09=09=09skip=20:=3D=20NULL,=0A+=09=09=09=09= =09=09=09startblock=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09endblock=20:=3D= =20NULL);=0A+=0A+create=20table=20test_partition=20partition=20of=20= test_partitioned=20for=20values=20in=20(1);=0A+create=20index=20= test_index=20on=20test_partition=20(a);=0A+--=20indexes=20do=20not,=20so=20= these=20all=20fail=0A+select=20*=20from=20verify_heapam('test_index',=0A= +=09=09=09=09=09=09=09on_error_stop=20:=3D=20false,=0A+=09=09=09=09=09=09= =09skip=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09startblock=20:=3D=20NULL,=0A= +=09=09=09=09=09=09=09endblock=20:=3D=20NULL);=0A+=0A+create=20view=20= test_view=20as=20select=201;=0A+--=20views=20do=20not=20have=20vms,=20so=20= these=20all=20fail=0A+select=20*=20from=20verify_heapam('test_view',=0A+=09= =09=09=09=09=09=09on_error_stop=20:=3D=20false,=0A+=09=09=09=09=09=09=09= skip=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09startblock=20:=3D=20NULL,=0A= +=09=09=09=09=09=09=09endblock=20:=3D=20NULL);=0A+=0A+create=20sequence=20= test_sequence;=0A+--=20sequences=20do=20not=20have=20vms,=20so=20these=20= all=20fail=0A+select=20*=20from=20verify_heapam('test_sequence',=0A+=09=09= =09=09=09=09=09on_error_stop=20:=3D=20false,=0A+=09=09=09=09=09=09=09= skip=20:=3D=20NULL,=0A+=09=09=09=09=09=09=09startblock=20:=3D=20NULL,=0A= +=09=09=09=09=09=09=09endblock=20:=3D=20NULL);=0A+=0A+create=20foreign=20= data=20wrapper=20dummy;=0A+create=20server=20dummy_server=20foreign=20= data=20wrapper=20dummy;=0A+create=20foreign=20table=20test_foreign_table=20= ()=20server=20dummy_server;=0A+--=20foreign=20tables=20do=20not=20have=20= vms,=20so=20these=20all=20fail=0A+select=20*=20from=20= verify_heapam('test_foreign_table',=0A+=09=09=09=09=09=09=09= on_error_stop=20:=3D=20false,=0A+=09=09=09=09=09=09=09skip=20:=3D=20= NULL,=0A+=09=09=09=09=09=09=09startblock=20:=3D=20NULL,=0A+=09=09=09=09=09= =09=09endblock=20:=3D=20NULL);=0Adiff=20--git=20= a/contrib/amcheck/t/001_verify_heapam.pl=20= b/contrib/amcheck/t/001_verify_heapam.pl=0Anew=20file=20mode=20100644=0A= index=200000000000..c2d890bcd9=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/t/001_verify_heapam.pl=0A@@=20-0,0=20+1,94=20@@=0A+use=20= strict;=0A+use=20warnings;=0A+=0A+use=20PostgresNode;=0A+use=20TestLib;=0A= +=0A+use=20Test::More=20tests=20=3D>=2048;=0A+=0A+my=20($node,=20= $result);=0A+=0A+#=20Check=20various=20options=20are=20stable=20(don't=20= abort)=20when=20running=20verify_heapam=20on=0A+#=20the=20test=20table.=20= =20For=20uncorrupted=20tables,=20there=20isn't=20anything=20to=20check=20= except=0A+#=20that=20it=20runs=20without=20crashing.=0A+sub=20= check_all_options=0A+{=0A+=09for=20my=20$stop=20(qw(NULL=20true=20= false))=0A+=09{=0A+=09=09for=20my=20$skip=20("'none'",=20"'all-frozen'",=20= "'all-visible'")=0A+=09=09{=0A+=09=09=09my=20$check=20=3D=20"SELECT=20= verify_heapam('test',=20$stop,=20$skip)";=0A+=09=09=09$result=20=3D=20= $node->safe_psql('postgres',=20"$check;=20SELECT=201");=0A+=09=09=09is=20= ($result,=201,=20"checked:=20$check");=0A+=09=09}=0A+=09}=0A+}=0A+=0A+#=20= Stops=20the=20server=20and=20writes=20nulls=20in=20the=20first=20page=20= of=20the=20table,=0A+#=20assuming=20page=20size=20is=20large=20enough=20= for=20offset=201000..1016=20to=20be=0A+#=20in=20the=20midst=20of=20the=20= first=20page=20of=20data.=0A+sub=20corrupt_first_page=0A+{=0A+=09my=20= $pgdata=20=3D=20$node->data_dir;=0A+=09my=20$rel=20=3D=20= $node->safe_psql('postgres',=0A+=09=09=09=09=09=09=09=20=20=20qq(SELECT=20= pg_relation_filepath('test')));=0A+=09my=20$relpath=20=3D=20= "$pgdata/$rel";=0A+=09$node->stop;=0A+=0A+=09my=20$fh;=0A+=09open($fh,=20= '+<',=20$relpath);=0A+=09binmode=20$fh;=0A+=09seek($fh,=201000,=200);=0A= +=09syswrite($fh,=20'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',=2016);=0A+=09= close($fh);=0A+=0A+=09$node->start;=0A+}=0A+=0A+#=20Test=20set-up=0A= +$node=20=3D=20get_new_node('test');=0A+$node->init;=0A= +$node->append_conf('postgresql.conf',=20'autovacuum=3Doff');=0A= +$node->start;=0A+$node->safe_psql('postgres',=20q(CREATE=20EXTENSION=20= amcheck));=0A+=0A+#=20Check=20empty=20table=0A= +$node->safe_psql('postgres',=20q(=0A+=09CREATE=20TABLE=20test=20(a=20= integer);=0A+=09ALTER=20TABLE=20public.test=20SET=20= (autovacuum_enabled=3Dfalse);=0A+));=0A+check_all_options();=0A+=0A+#=20= Check=20table=20with=20trivial=20data=0A+$node->safe_psql('postgres',=20= q(INSERT=20INTO=20test=20VALUES=20(0)));=0A+check_all_options();=0A+=0A= +#=20Check=20table=20with=20non-trivial=20data=20(more=20than=20a=20page=20= worth)=20but=0A+#=20without=20any=20all-frozen=20or=20all-visible=0A= +$node->safe_psql('postgres',=20q(=0A+INSERT=20INTO=20test=20SELECT=20= generate_series(1,10000)));=0A+check_all_options();=0A+=0A+#=20Check=20= table=20with=20all-visible=20data=0A+$node->safe_psql('postgres',=20= q(VACUUM=20test));=0A+check_all_options();=0A+=0A+#=20Check=20table=20= with=20all-frozen=20data=0A+$node->safe_psql('postgres',=20q(VACUUM=20= FREEZE=20test));=0A+check_all_options();=0A+=0A+#=20Check=20table=20with=20= corruption,=20no=20skipping=0A+corrupt_first_page();=0A+$result=20=3D=20= $node->safe_psql('postgres',=20q(=0A+SELECT=20COUNT(*)=20>=200=20FROM=20= verify_heapam('test',=20on_error_stop=20:=3D=20false,=20skip=20:=3D=20= NULL,=20startblock=20:=3D=20NULL,=20endblock=20:=3D=20NULL)));=0A= +is($result,=20't',=20'corruption=20detected=20on=20first=20page');=0A+=0A= +#=20Check=20table=20with=20corruption,=20skipping=20all-visible=20= blocks=0A+$result=20=3D=20$node->safe_psql('postgres',=20q(=0A+SELECT=20= COUNT(*)=20>=200=20FROM=20verify_heapam('test',=20on_error_stop=20:=3D=20= false,=20skip=20:=3D=20'all-visible',=20startblock=20:=3D=20NULL,=20= endblock=20:=3D=20NULL)));=0A+is($result,=20'f',=20'skipping=20= all-visible=20first=20page');=0A+=0A+#=20Check=20table=20with=20= corruption,=20skipping=20all-frozen=20blocks=0A+$result=20=3D=20= $node->safe_psql('postgres',=20q(=0A+SELECT=20COUNT(*)=20>=200=20FROM=20= verify_heapam('test',=20on_error_stop=20:=3D=20false,=20skip=20:=3D=20= 'all-frozen',=20startblock=20:=3D=20NULL,=20endblock=20:=3D=20NULL)));=0A= +is($result,=20'f',=20'skipping=20all-frozen=20first=20page');=0Adiff=20= --git=20a/contrib/amcheck/verify_heapam.c=20= b/contrib/amcheck/verify_heapam.c=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..8bf6b891ff=0A---=20/dev/null=0A+++=20= b/contrib/amcheck/verify_heapam.c=0A@@=20-0,0=20+1,1139=20@@=0A= +/*-----------------------------------------------------------------------= --=0A+=20*=0A+=20*=20verify_heapam.c=0A+=20*=09=20=20Functions=20to=20= check=20postgresql=20heap=20relations=20for=20corruption=0A+=20*=0A+=20*=20= Copyright=20(c)=202016-2020,=20PostgreSQL=20Global=20Development=20Group=0A= +=20*=0A+=20*=09=20=20contrib/amcheck/verify_heapam.c=0A+=20= *-------------------------------------------------------------------------= =0A+=20*/=0A+#include=20"postgres.h"=0A+=0A+#include=20= "access/detoast.h"=0A+#include=20"access/genam.h"=0A+#include=20= "access/heapam.h"=0A+#include=20"access/heaptoast.h"=0A+#include=20= "access/htup_details.h"=0A+#include=20"access/multixact.h"=0A+#include=20= "access/toast_internals.h"=0A+#include=20"access/visibilitymap.h"=0A= +#include=20"access/xact.h"=0A+#include=20"amcheck.h"=0A+#include=20= "catalog/pg_am.h"=0A+#include=20"catalog/pg_type.h"=0A+#include=20= "catalog/storage_xlog.h"=0A+#include=20"funcapi.h"=0A+#include=20= "miscadmin.h"=0A+#include=20"storage/bufmgr.h"=0A+#include=20= "storage/procarray.h"=0A+#include=20"storage/smgr.h"=0A+#include=20= "utils/builtins.h"=0A+#include=20"utils/fmgroids.h"=0A+#include=20= "utils/lsyscache.h"=0A+#include=20"utils/rel.h"=0A+#include=20= "utils/snapmgr.h"=0A+#include=20"utils/syscache.h"=0A+=0A= +PG_FUNCTION_INFO_V1(verify_heapam);=0A+=0A+/*=20The=20number=20of=20= columns=20in=20tuples=20returned=20by=20verify_heapam=20*/=0A+#define=20= HEAPCHECK_RELATION_COLS=208=0A+=0A+/*=0A+=20*=20Struct=20holding=20the=20= running=20context=20information=20during=0A+=20*=20a=20lifetime=20of=20a=20= verify_heapam=20execution.=0A+=20*/=0A+typedef=20struct=20= HeapCheckContext=0A+{=0A+=09/*=0A+=09=20*=20While=20verifying=20a=20= table,=20we=20check=20whether=20any=20xid=20we=20encounter=20is=0A+=09=20= *=20either=20too=20old=20or=20too=20new.=20=20We=20could=20naively=20= check=20that=20by=20taking=20the=0A+=09=20*=20XidGenLock=20each=20time=20= and=20reading=20ShmemVariableCache.=20=20We=20instead=20cache=0A+=09=20*=20= the=20values=20and=20rely=20on=20the=20fact=20that=20we=20have=20the=20= table=20locked=0A+=09=20*=20sufficiently=20that=20the=20oldest=20xid=20= in=20the=20table=20cannot=20change=0A+=09=20*=20mid-verification,=20and=20= although=20the=20newest=20xid=20in=20the=20table=20may=20advance,=0A+=09=20= *=20it=20cannot=20retreat.=20=20As=20such,=20whenever=20we=20encounter=20= an=20xid=20older=20than=0A+=09=20*=20our=20cached=20oldest=20xid,=20we=20= know=20it=20is=20invalid,=20and=20when=20we=20encounter=20an=0A+=09=20*=20= xid=20newer=20than=20our=20cached=20newest=20xid,=20we=20recheck=20the=0A= +=09=20*=20ShmemVariableCache.=0A+=09=20*/=0A+=09TransactionId=20= next_valid_xid;=0A+=09TransactionId=20oldest_valid_xid;=0A+=0A+=09/*=20= Values=20concerning=20the=20heap=20relation=20being=20checked=20*/=0A+=09= Relation=09rel;=0A+=09TransactionId=20relfrozenxid;=0A+=09TransactionId=20= relminmxid;=0A+=09Relation=09toast_rel;=0A+=09Relation=20=20=20= *toast_indexes;=0A+=09Relation=09valid_toast_index;=0A+=09int=09=09=09= num_toast_indexes;=0A+=0A+=09/*=20Values=20for=20iterating=20over=20= pages=20in=20the=20relation=20*/=0A+=09BlockNumber=20nblocks;=0A+=09= BlockNumber=20blkno;=0A+=09BufferAccessStrategy=20bstrategy;=0A+=09= Buffer=09=09buffer;=0A+=09Page=09=09page;=0A+=0A+=09/*=20Values=20for=20= iterating=20over=20tuples=20within=20a=20page=20*/=0A+=09OffsetNumber=20= offnum;=0A+=09ItemId=09=09itemid;=0A+=09uint16=09=09lp_len;=0A+=09= HeapTupleHeader=20tuphdr;=0A+=09int=09=09=09natts;=0A+=0A+=09/*=20Values=20= for=20iterating=20over=20attributes=20within=20the=20tuple=20*/=0A+=09= uint32=09=09offset;=09=09=09/*=20offset=20in=20tuple=20data=20*/=0A+=09= AttrNumber=09attnum;=0A+=0A+=09/*=20Values=20for=20iterating=20over=20= toast=20for=20the=20attribute=20*/=0A+=09int32=09=09chunkno;=0A+=09int32=09= =09attrsize;=0A+=09int32=09=09endchunk;=0A+=09int32=09=09totalchunks;=0A= +=0A+=09/*=20Whether=20verify_heapam=20has=20yet=20encountered=20any=20= corrupt=20tuples=20*/=0A+=09bool=09=09is_corrupt;=0A+=0A+=09/*=20The=20= descriptor=20and=20tuplestore=20for=20verify_heapam's=20result=20tuples=20= */=0A+=09TupleDesc=09tupdesc;=0A+=09Tuplestorestate=20*tupstore;=0A+}=20= HeapCheckContext;=0A+=0A+/*=20Internal=20implementation=20*/=0A+static=20= void=20check_relation_relkind_and_relam(Relation=20rel);=0A+=0A+static=20= void=20confess(HeapCheckContext=20*ctx,=20char=20*msg);=0A+static=20= TupleDesc=20verify_heapam_tupdesc(void);=0A+=0A+static=20bool=20= xid_valid_in_rel(TransactionId=20xid,=20HeapCheckContext=20*ctx);=0A= +static=20bool=20tuple_is_visible(HeapTupleHeader=20tuphdr,=20= HeapCheckContext=20*ctx);=0A+static=20void=20check_toast_tuple(HeapTuple=20= toasttup,=20HeapCheckContext=20*ctx);=0A+static=20bool=20= check_tuple_attribute(HeapCheckContext=20*ctx);=0A+static=20void=20= check_tuple(HeapCheckContext=20*ctx);=0A+=0A+typedef=20enum=20SkipPages=0A= +{=0A+=09SKIP_ALL_FROZEN_PAGES,=0A+=09SKIP_ALL_VISIBLE_PAGES,=0A+=09= SKIP_PAGES_NONE=0A+}=20SkipPages;=0A+=0A+/*=0A+=20*=20verify_heapam=0A+=20= *=0A+=20*=20=20=20Scan=20and=20report=20corruption=20in=20heap=20pages=20= or=20in=20associated=20toast=20relation.=0A+=20*/=0A+Datum=0A= +verify_heapam(PG_FUNCTION_ARGS)=0A+{=0A+=09ReturnSetInfo=20*rsinfo=20=3D=20= (ReturnSetInfo=20*)=20fcinfo->resultinfo;=0A+=09MemoryContext=20= old_context;=0A+=09bool=09=09random_access;=0A+=09HeapCheckContext=20= ctx;=0A+=09FullTransactionId=20next_full_xid;=0A+=09Buffer=09=09vmbuffer=20= =3D=20InvalidBuffer;=0A+=09Oid=09=09=09relid;=0A+=09bool=09=09fatal=20=3D=20= false;=0A+=09bool=09=09on_error_stop;=0A+=09SkipPages=09skip_option=20=3D=20= SKIP_PAGES_NONE;=0A+=09int64=09=09start_block;=0A+=09int64=09=09= end_block;=0A+=0A+=09/*=20check=20to=20see=20if=20caller=20supports=20us=20= returning=20a=20tuplestore=20*/=0A+=09if=20(rsinfo=20=3D=3D=20NULL=20||=20= !IsA(rsinfo,=20ReturnSetInfo))=0A+=09=09ereport(ERROR,=0A+=09=09=09=09= (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),=0A+=09=09=09=09=20= errmsg("set-valued=20function=20called=20in=20context=20that=20cannot=20= accept=20a=20set")));=0A+=09if=20(!(rsinfo->allowedModes=20&=20= SFRM_Materialize))=0A+=09=09ereport(ERROR,=0A+=09=09=09=09= (errcode(ERRCODE_SYNTAX_ERROR),=0A+=09=09=09=09=20errmsg("materialize=20= mode=20required,=20but=20it=20is=20not=20allowed=20in=20this=20= context")));=0A+=0A+=09/*=20check=20supplied=20arguments=20*/=0A+=09if=20= (PG_ARGISNULL(0))=0A+=09=09ereport(ERROR,=0A+=09=09=09=09= (errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=20= errmsg("missing=20required=20parameter=20for=20'rel'")));=0A+=09relid=20= =3D=20PG_GETARG_OID(0);=0A+=09on_error_stop=20=3D=20PG_ARGISNULL(1)=20?=20= false=20:=20PG_GETARG_BOOL(1);=0A+=09if=20(!PG_ARGISNULL(2))=0A+=09{=0A+=09= =09const=20char=20*skip=20=3D=20PG_GETARG_CSTRING(2);=0A+=0A+=09=09if=20= (pg_strcasecmp(skip,=20"all-visible")=20=3D=3D=200)=0A+=09=09=09= skip_option=20=3D=20SKIP_ALL_VISIBLE_PAGES;=0A+=09=09else=20if=20= (pg_strcasecmp(skip,=20"all-frozen")=20=3D=3D=200)=0A+=09=09=09= skip_option=20=3D=20SKIP_ALL_FROZEN_PAGES;=0A+=09=09else=20if=20= (pg_strcasecmp(skip,=20"none")=20=3D=3D=200)=0A+=09=09=09skip_option=20=3D= =20SKIP_PAGES_NONE;=0A+=09=09else=0A+=09=09=09ereport(ERROR,=0A+=09=09=09= =09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=09=20= errmsg("unrecognized=20parameter=20for=20'skip':=20%s",=20skip),=0A+=09=09= =09=09=09=20errhint("please=20choose=20from=20'all-visible',=20= 'all-frozen',=20or=20'none'")));=0A+=09}=0A+=0A+=09memset(&ctx,=200,=20= sizeof(HeapCheckContext));=0A+=0A+=09/*=20The=20tupdesc=20and=20= tuplestore=20must=20be=20created=20in=20ecxt_per_query_memory=20*/=0A+=09= old_context=20=3D=20= MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);=0A+=09= random_access=20=3D=20(rsinfo->allowedModes=20&=20= SFRM_Materialize_Random)=20!=3D=200;=0A+=09ctx.tupdesc=20=3D=20= verify_heapam_tupdesc();=0A+=09ctx.tupstore=20=3D=20= tuplestore_begin_heap(random_access,=20false,=20work_mem);=0A+=09= rsinfo->returnMode=20=3D=20SFRM_Materialize;=0A+=09rsinfo->setResult=20=3D= =20ctx.tupstore;=0A+=09rsinfo->setDesc=20=3D=20ctx.tupdesc;=0A+=0A+=09= MemoryContextSwitchTo(old_context);=0A+=0A+=09/*=0A+=09=20*=20Open=20the=20= relation.=20=20We=20use=20ShareUpdateExclusive=20to=20prevent=20= concurrent=0A+=09=20*=20vacuums=20from=20changing=20the=20relfrozenxid,=20= relminmxid,=20or=20advancing=20the=0A+=09=20*=20global=20oldestXid=20to=20= be=20newer=20than=20those.=0A+=09=20*/=0A+=09ctx.rel=20=3D=20= relation_open(relid,=20ShareUpdateExclusiveLock);=0A+=09= check_relation_relkind_and_relam(ctx.rel);=0A+=09ctx.nblocks=20=3D=20= RelationGetNumberOfBlocks(ctx.rel);=0A+=0A+=09/*=20Early=20exit=20if=20= the=20relation=20is=20empty=20*/=0A+=09if=20(!ctx.nblocks)=0A+=09{=0A+=09= =09/*=0A+=09=09=20*=20For=20consistency,=20we=20need=20to=20enforce=20= that=20the=20start_block=20and=0A+=09=09=20*=20end_block=20are=20within=20= the=20valid=20range=20if=20the=20user=20specified=20them.=0A+=09=09=20*=20= Yet,=20for=20an=20empty=20table=20with=20no=20blocks,=20no=20specified=20= block=20can=20be=0A+=09=09=20*=20in=20range.=0A+=09=09=20*/=0A+=09=09if=20= (!PG_ARGISNULL(3))=0A+=09=09=09ereport(ERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=09=20= errmsg("starting=20block=20"=20INT64_FORMAT=20"=20is=20out=20of=20bounds=20= for=20relation=20with=20no=20blocks",=0A+=09=09=09=09=09=09=09= PG_GETARG_INT64(3))));=0A+=09=09if=20(!PG_ARGISNULL(4))=0A+=09=09=09= ereport(ERROR,=0A+=09=09=09=09=09= (errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=09=20= errmsg("ending=20block=20"=20INT64_FORMAT=20"=20is=20out=20of=20bounds=20= for=20relation=20with=20no=20blocks",=0A+=09=09=09=09=09=09=09= PG_GETARG_INT64(4))));=0A+=09=09relation_close(ctx.rel,=20= ShareUpdateExclusiveLock);=0A+=09=09PG_RETURN_NULL();=0A+=09}=0A+=0A+=09= ctx.bstrategy=20=3D=20GetAccessStrategy(BAS_BULKREAD);=0A+=09ctx.buffer=20= =3D=20InvalidBuffer;=0A+=09ctx.page=20=3D=20NULL;=0A+=0A+=09/*=20If=20we=20= get=20this=20far,=20we=20know=20the=20relation=20has=20at=20least=20one=20= block=20*/=0A+=09start_block=20=3D=20PG_ARGISNULL(3)=20?=200=20:=20= PG_GETARG_INT64(3);=0A+=09end_block=20=3D=20PG_ARGISNULL(4)=20?=20= ((int64)=20ctx.nblocks)=20-=201=20:=20PG_GETARG_INT64(4);=0A+=09if=20= (start_block=20<=200=20||=20end_block=20>=3D=20ctx.nblocks=20||=20= start_block=20>=20end_block)=0A+=09=09ereport(ERROR,=0A+=09=09=09=09= (errcode(ERRCODE_INVALID_PARAMETER_VALUE),=0A+=09=09=09=09=20= errmsg("block=20range=20"=20INT64_FORMAT=20"=20..=20"=20INT64_FORMAT=20"=20= is=20out=20of=20bounds=20for=20relation=20with=20block=20count=20%u",=0A= +=09=09=09=09=09=09start_block,=20end_block,=20ctx.nblocks)));=0A+=0A+=09= /*=0A+=09=20*=20Open=20the=20toast=20relation,=20if=20any,=20also=20= protected=20from=20concurrent=0A+=09=20*=20vacuums.=0A+=09=20*/=0A+=09if=20= (ctx.rel->rd_rel->reltoastrelid)=0A+=09{=0A+=09=09int=09=09=09offset;=0A= +=0A+=09=09/*=20Main=20relation=20has=20associated=20toast=20relation=20= */=0A+=09=09ctx.toast_rel=20=3D=20= table_open(ctx.rel->rd_rel->reltoastrelid,=0A+=09=09=09=09=09=09=09=09=20= =20=20ShareUpdateExclusiveLock);=0A+=09=09offset=20=3D=20= toast_open_indexes(ctx.toast_rel,=0A+=09=09=09=09=09=09=09=09=09= ShareUpdateExclusiveLock,=0A+=09=09=09=09=09=09=09=09=09= &(ctx.toast_indexes),=0A+=09=09=09=09=09=09=09=09=09= &(ctx.num_toast_indexes));=0A+=09=09ctx.valid_toast_index=20=3D=20= ctx.toast_indexes[offset];=0A+=09}=0A+=09else=0A+=09{=0A+=09=09/*=20Main=20= relation=20has=20no=20associated=20toast=20relation=20*/=0A+=09=09= ctx.toast_rel=20=3D=20NULL;=0A+=09=09ctx.toast_indexes=20=3D=20NULL;=0A+=09= =09ctx.num_toast_indexes=20=3D=200;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20= Now=20that=20we=20have=20our=20relation(s)=20locked,=20oldestXid=20= cannot=20advance=0A+=09=20*=20beyond=20the=20oldest=20valid=20xid=20in=20= our=20table,=20nor=20can=20our=20relfrozenxid=0A+=09=20*=20advance.=0A+=09= =20*=0A+=09=20*=20If=20relfrozenxid=20is=20normal,=20it=20contains=20the=20= oldest=20valid=20xid=20we=20may=0A+=09=20*=20encounter=20in=20the=20= table.=20=20If=20not,=20the=20oldest=20xid=20for=20our=20database=20is=20= the=0A+=09=20*=20oldest=20we=20should=20encounter.=0A+=09=20*=0A+=09=20*=20= Bugs=20in=20pg_upgrade=20are=20reported=20(see=20commands/vacuum.c=20= circa=20line=201572)=0A+=09=20*=20to=20have=20sometimes=20rendered=20the=20= oldest=20xid=20value=20for=20a=20database=20invalid.=0A+=09=20*=20It=20= seems=20unwise=20to=20report=20rows=20as=20corrupt=20for=20failing=20to=20= be=20newer=20than=0A+=09=20*=20a=20value=20which=20itself=20may=20be=20= corrupt.=20=20We=20instead=20use=20the=20oldest=20xid=20for=0A+=09=20*=20= the=20entire=20cluster,=20which=20must=20be=20at=20least=20as=20old=20as=20= the=20oldest=20xid=20for=0A+=09=20*=20our=20database.=0A+=09=20*=0A+=09=20= *=20If=20neither=20the=20value=20for=20the=20database=20nor=20the=20xids=20= for=20any=20row=20are=0A+=09=20*=20corrupt,=20then=20this=20gives=20the=20= right=20answer.=20=20If=20the=20rows=20disagree=20with=0A+=09=20*=20the=20= value=20for=20the=20database,=20how=20can=20we=20know=20which=20one=20is=20= wrong?=0A+=09=20*/=0A+=09ctx.relfrozenxid=20=3D=20= ctx.rel->rd_rel->relfrozenxid;=0A+=09ctx.relminmxid=20=3D=20= ctx.rel->rd_rel->relminmxid;=0A+=0A+=09LWLockAcquire(XidGenLock,=20= LW_SHARED);=0A+=09next_full_xid=20=3D=20ShmemVariableCache->nextFullXid;=0A= +=09ctx.oldest_valid_xid=20=3D=20ShmemVariableCache->oldestXid;=0A+=09= LWLockRelease(XidGenLock);=0A+=09ctx.next_valid_xid=20=3D=20= XidFromFullTransactionId(next_full_xid);=0A+=0A+=09if=20= (TransactionIdIsNormal(ctx.relfrozenxid)=20&&=0A+=09=09= TransactionIdPrecedes(ctx.relfrozenxid,=20ctx.oldest_valid_xid))=0A+=09{=0A= +=09=09confess(&ctx,=0A+=09=09=09=09psprintf("relfrozenxid=20%u=20= precedes=20global=20oldest=20valid=20xid=20%u",=0A+=09=09=09=09=09=09=20= ctx.relfrozenxid,=20ctx.oldest_valid_xid));=0A+=09=09fatal=20=3D=20true;=0A= +=09}=0A+=09else=20if=20(TransactionIdIsNormal(ctx.relminmxid)=20&&=0A+=09= =09=09=20TransactionIdPrecedes(ctx.relminmxid,=20ctx.oldest_valid_xid))=0A= +=09{=0A+=09=09confess(&ctx,=0A+=09=09=09=09psprintf("relminmxid=20%u=20= precedes=20global=20oldest=20valid=20xid=20%u",=0A+=09=09=09=09=09=09=20= ctx.relminmxid,=20ctx.oldest_valid_xid));=0A+=09=09fatal=20=3D=20true;=0A= +=09}=0A+=0A+=09if=20(fatal)=0A+=09{=0A+=09=09/*=20Close=20the=20= associated=20toast=20table=20and=20indexes,=20if=20any.=20*/=0A+=09=09if=20= (ctx.toast_indexes)=0A+=09=09=09toast_close_indexes(ctx.toast_indexes,=20= ctx.num_toast_indexes,=0A+=09=09=09=09=09=09=09=09= ShareUpdateExclusiveLock);=0A+=09=09if=20(ctx.toast_rel)=0A+=09=09=09= table_close(ctx.toast_rel,=20ShareUpdateExclusiveLock);=0A+=0A+=09=09/*=20= Close=20the=20main=20relation=20*/=0A+=09=09relation_close(ctx.rel,=20= ShareUpdateExclusiveLock);=0A+=0A+=09=09PG_RETURN_NULL();=0A+=09}=0A+=0A= +=09if=20(TransactionIdIsNormal(ctx.relfrozenxid))=0A+=09=09= ctx.oldest_valid_xid=20=3D=20ctx.relfrozenxid;=0A+=0A+=09for=20= (ctx.blkno=20=3D=20start_block;=20ctx.blkno=20<=3D=20end_block;=20= ctx.blkno++)=0A+=09{=0A+=09=09int32=09=09mapbits;=0A+=09=09OffsetNumber=20= maxoff;=0A+=09=09PageHeader=09ph;=0A+=0A+=09=09/*=20Optionally=20skip=20= over=20all-frozen=20or=20all-visible=20blocks=20*/=0A+=09=09if=20= (skip_option=20!=3D=20SKIP_PAGES_NONE)=0A+=09=09{=0A+=09=09=09bool=09=09= all_frozen,=0A+=09=09=09=09=09=09all_visible;=0A+=0A+=09=09=09mapbits=20= =3D=20(int32)=20visibilitymap_get_status(ctx.rel,=20ctx.blkno,=0A+=09=09=09= =09=09=09=09=09=09=09=09=09=09=20=20=20&vmbuffer);=0A+=09=09=09= all_frozen=20=3D=20mapbits=20&=20VISIBILITYMAP_ALL_VISIBLE;=0A+=09=09=09= all_visible=20=3D=20mapbits=20&=20VISIBILITYMAP_ALL_FROZEN;=0A+=0A+=09=09= =09if=20((all_frozen=20&&=20skip_option=20=3D=3D=20= SKIP_ALL_FROZEN_PAGES)=20||=0A+=09=09=09=09(all_visible=20&&=20= skip_option=20=3D=3D=20SKIP_ALL_VISIBLE_PAGES))=0A+=09=09=09{=0A+=09=09=09= =09continue;=0A+=09=09=09}=0A+=09=09}=0A+=0A+=09=09/*=20Read=20and=20= lock=20the=20next=20page.=20*/=0A+=09=09ctx.buffer=20=3D=20= ReadBufferExtended(ctx.rel,=20MAIN_FORKNUM,=20ctx.blkno,=0A+=09=09=09=09=09= =09=09=09=09=09RBM_NORMAL,=20ctx.bstrategy);=0A+=09=09= LockBuffer(ctx.buffer,=20BUFFER_LOCK_SHARE);=0A+=09=09ctx.page=20=3D=20= BufferGetPage(ctx.buffer);=0A+=09=09ph=20=3D=20(PageHeader)=20ctx.page;=0A= +=0A+=09=09/*=20Perform=20tuple=20checks=20*/=0A+=09=09maxoff=20=3D=20= PageGetMaxOffsetNumber(ctx.page);=0A+=09=09for=20(ctx.offnum=20=3D=20= FirstOffsetNumber;=20ctx.offnum=20<=3D=20maxoff;=0A+=09=09=09=20= ctx.offnum=20=3D=20OffsetNumberNext(ctx.offnum))=0A+=09=09{=0A+=09=09=09= ctx.itemid=20=3D=20PageGetItemId(ctx.page,=20ctx.offnum);=0A+=0A+=09=09=09= /*=20Skip=20over=20unused/dead=20line=20pointers=20*/=0A+=09=09=09if=20= (!ItemIdIsUsed(ctx.itemid)=20||=20ItemIdIsDead(ctx.itemid))=0A+=09=09=09=09= continue;=0A+=0A+=09=09=09/*=0A+=09=09=09=20*=20If=20this=20line=20= pointer=20has=20been=20redirected,=20check=20that=20it=0A+=09=09=09=20*=20= redirects=20to=20a=20valid=20offset=20within=20the=20line=20pointer=20= array.=0A+=09=09=09=20*/=0A+=09=09=09if=20= (ItemIdIsRedirected(ctx.itemid))=0A+=09=09=09{=0A+=09=09=09=09= OffsetNumber=20rdoffnum=20=3D=20ItemIdGetRedirect(ctx.itemid);=0A+=09=09=09= =09ItemId=09=09rditem;=0A+=0A+=09=09=09=09if=20(rdoffnum=20<=20= FirstOffsetNumber=20||=20rdoffnum=20>=20maxoff)=0A+=09=09=09=09{=0A+=09=09= =09=09=09confess(&ctx,=0A+=09=09=09=09=09=09=09psprintf("line=20pointer=20= redirection=20to=20item=20at=20offset=20number=20%u=20is=20outside=20= valid=20bounds=20%u=20..=20%u",=0A+=09=09=09=09=09=09=09=09=09=20= (unsigned)=20rdoffnum,=20(unsigned)=20FirstOffsetNumber,=0A+=09=09=09=09=09= =09=09=09=09=20(unsigned)=20maxoff));=0A+=09=09=09=09=09continue;=0A+=09=09= =09=09}=0A+=09=09=09=09rditem=20=3D=20PageGetItemId(ctx.page,=20= rdoffnum);=0A+=09=09=09=09if=20(!ItemIdIsUsed(rditem))=0A+=09=09=09=09=09= confess(&ctx,=0A+=09=09=09=09=09=09=09psprintf("line=20pointer=20= redirection=20to=20unused=20item=20at=20offset=20%u",=0A+=09=09=09=09=09=09= =09=09=09=20(unsigned)=20rdoffnum));=0A+=09=09=09=09continue;=0A+=09=09=09= }=0A+=0A+=09=09=09/*=20Set=20up=20context=20information=20about=20this=20= next=20tuple=20*/=0A+=09=09=09ctx.lp_len=20=3D=20= ItemIdGetLength(ctx.itemid);=0A+=09=09=09ctx.tuphdr=20=3D=20= (HeapTupleHeader)=20PageGetItem(ctx.page,=20ctx.itemid);=0A+=09=09=09= ctx.natts=20=3D=20HeapTupleHeaderGetNatts(ctx.tuphdr);=0A+=0A+=09=09=09= /*=0A+=09=09=09=20*=20Reset=20information=20about=20individual=20= attributes=20and=20related=20toast=0A+=09=09=09=20*=20values,=20so=20= they=20show=20as=20NULL=20in=20the=20corruption=20report=20if=20we=0A+=09= =09=09=20*=20record=20a=20corruption=20before=20beginning=20to=20iterate=20= over=20the=0A+=09=09=09=20*=20attributes.=0A+=09=09=09=20*/=0A+=09=09=09= ctx.attnum=20=3D=20-1;=0A+=09=09=09ctx.chunkno=20=3D=20-1;=0A+=0A+=09=09=09= /*=20Ok,=20ready=20to=20check=20this=20next=20tuple=20*/=0A+=09=09=09= check_tuple(&ctx);=0A+=09=09}=0A+=0A+=09=09/*=20clean=20up=20*/=0A+=09=09= UnlockReleaseBuffer(ctx.buffer);=0A+=0A+=09=09if=20(on_error_stop=20&&=20= ctx.is_corrupt)=0A+=09=09=09break;=0A+=09}=0A+=0A+=09if=20(vmbuffer=20!=3D= =20InvalidBuffer)=0A+=09=09ReleaseBuffer(vmbuffer);=0A+=0A+=09/*=20Close=20= the=20associated=20toast=20table=20and=20indexes,=20if=20any.=20*/=0A+=09= if=20(ctx.toast_indexes)=0A+=09=09toast_close_indexes(ctx.toast_indexes,=20= ctx.num_toast_indexes,=0A+=09=09=09=09=09=09=09= ShareUpdateExclusiveLock);=0A+=09if=20(ctx.toast_rel)=0A+=09=09= table_close(ctx.toast_rel,=20ShareUpdateExclusiveLock);=0A+=0A+=09/*=20= Close=20the=20main=20relation=20*/=0A+=09relation_close(ctx.rel,=20= ShareUpdateExclusiveLock);=0A+=0A+=09PG_RETURN_NULL();=0A+}=0A+=0A+/*=0A= +=20*=20check_relation_relkind_and_relam=0A+=20*=0A+=20*=20=20=20= convenience=20routine=20to=20check=20that=20relation=20is=20of=20a=20= supported=20relkind.=0A+=20*/=0A+static=20void=0A= +check_relation_relkind_and_relam(Relation=20rel)=0A+{=0A+=09if=20= (rel->rd_rel->relkind=20!=3D=20RELKIND_RELATION=20&&=0A+=09=09= rel->rd_rel->relkind=20!=3D=20RELKIND_MATVIEW=20&&=0A+=09=09= rel->rd_rel->relkind=20!=3D=20RELKIND_TOASTVALUE)=0A+=09=09= ereport(ERROR,=0A+=09=09=09=09(errcode(ERRCODE_WRONG_OBJECT_TYPE),=0A+=09= =09=09=09=20errmsg("\"%s\"=20is=20not=20a=20table,=20materialized=20= view,=20or=20TOAST=20table",=0A+=09=09=09=09=09=09= RelationGetRelationName(rel))));=0A+=09if=20(rel->rd_rel->relam=20!=3D=20= HEAP_TABLE_AM_OID)=0A+=09=09ereport(ERROR,=0A+=09=09=09=09= (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),=0A+=09=09=09=09=20errmsg("only=20= heap=20AM=20is=20supported")));=0A+}=0A+=0A+/*=0A+=20*=20confess=0A+=20*=0A= +=20*=20=20=20Return=20a=20message=20about=20corruption,=20including=20= information=0A+=20*=20=20=20about=20where=20in=20the=20relation=20the=20= corruption=20was=20found.=0A+=20*=0A+=20*=20=20=20The=20msg=20argument=20= is=20pfree'd=20by=20this=20function.=0A+=20*/=0A+static=20void=0A= +confess(HeapCheckContext=20*ctx,=20char=20*msg)=0A+{=0A+=09Datum=09=09= values[HEAPCHECK_RELATION_COLS];=0A+=09bool=09=09= nulls[HEAPCHECK_RELATION_COLS];=0A+=09HeapTuple=09tuple;=0A+=09int16=09=09= lp_off=20=3D=20ItemIdGetOffset(ctx->itemid);=0A+=09int16=09=09lp_flags=20= =3D=20ItemIdGetFlags(ctx->itemid);=0A+=09int16=09=09lp_len=20=3D=20= ItemIdGetLength(ctx->itemid);=0A+=0A+=09MemSet(values,=200,=20= sizeof(values));=0A+=09MemSet(nulls,=200,=20sizeof(nulls));=0A+=09= values[0]=20=3D=20Int64GetDatum(ctx->blkno);=0A+=09values[1]=20=3D=20= Int32GetDatum(ctx->offnum);=0A+=09nulls[1]=20=3D=20(ctx->offnum=20<=20= 0);=0A+=09values[2]=20=3D=20Int16GetDatum(lp_off);=0A+=09nulls[2]=20=3D=20= (lp_off=20<=200);=0A+=09values[3]=20=3D=20Int16GetDatum(lp_flags);=0A+=09= nulls[3]=20=3D=20(lp_flags=20<=200);=0A+=09values[4]=20=3D=20= Int16GetDatum(lp_len);=0A+=09nulls[4]=20=3D=20(lp_len=20<=200);=0A+=09= values[5]=20=3D=20Int32GetDatum(ctx->attnum);=0A+=09nulls[5]=20=3D=20= (ctx->attnum=20<=200);=0A+=09values[6]=20=3D=20= Int32GetDatum(ctx->chunkno);=0A+=09nulls[6]=20=3D=20(ctx->chunkno=20<=20= 0);=0A+=09values[7]=20=3D=20CStringGetTextDatum(msg);=0A+=0A+=09/*=0A+=09= =20*=20In=20principle,=20there=20is=20nothing=20to=20prevent=20a=20scan=20= over=20a=20large,=20highly=0A+=09=20*=20corrupted=20table=20from=20using=20= workmem=20worth=20of=20memory=20building=20up=20the=0A+=09=20*=20= tuplestore.=20=20That's=20ok,=20but=20if=20we=20also=20leak=20the=20msg=20= argument=20memory=0A+=09=20*=20until=20the=20end=20of=20the=20query,=20= we=20could=20exceed=20workmem=20by=20more=20than=20a=0A+=09=20*=20= trivial=20amount.=20=20Therefore,=20free=20the=20msg=20argument=20each=20= time=20we=20are=0A+=09=20*=20called=20rather=20than=20waiting=20for=20= our=20current=20memory=20context=20to=20be=20freed.=0A+=09=20*/=0A+=09= pfree(msg);=0A+=0A+=09tuple=20=3D=20heap_form_tuple(ctx->tupdesc,=20= values,=20nulls);=0A+=09tuplestore_puttuple(ctx->tupstore,=20tuple);=0A+=09= ctx->is_corrupt=20=3D=20true;=0A+}=0A+=0A+/*=0A+=20*=20Helper=20function=20= to=20construct=20the=20TupleDesc=20needed=20by=20verify_heapam.=0A+=20*/=0A= +static=20TupleDesc=0A+verify_heapam_tupdesc(void)=0A+{=0A+=09TupleDesc=09= tupdesc;=0A+=09AttrNumber=09a=20=3D=200;=0A+=0A+=09tupdesc=20=3D=20= CreateTemplateTupleDesc(HEAPCHECK_RELATION_COLS);=0A+=09= TupleDescInitEntry(tupdesc,=20++a,=20"blkno",=20INT8OID,=20-1,=200);=0A+=09= TupleDescInitEntry(tupdesc,=20++a,=20"offnum",=20INT4OID,=20-1,=200);=0A= +=09TupleDescInitEntry(tupdesc,=20++a,=20"lp_off",=20INT2OID,=20-1,=20= 0);=0A+=09TupleDescInitEntry(tupdesc,=20++a,=20"lp_flags",=20INT2OID,=20= -1,=200);=0A+=09TupleDescInitEntry(tupdesc,=20++a,=20"lp_len",=20= INT2OID,=20-1,=200);=0A+=09TupleDescInitEntry(tupdesc,=20++a,=20= "attnum",=20INT4OID,=20-1,=200);=0A+=09TupleDescInitEntry(tupdesc,=20= ++a,=20"chunk",=20INT4OID,=20-1,=200);=0A+=09TupleDescInitEntry(tupdesc,=20= ++a,=20"msg",=20TEXTOID,=20-1,=200);=0A+=09Assert(a=20=3D=3D=20= HEAPCHECK_RELATION_COLS);=0A+=0A+=09return=20BlessTupleDesc(tupdesc);=0A= +}=0A+=0A+static=20inline=20bool=0A+XidInValidRange(TransactionId=20xid,=20= HeapCheckContext=20*ctx)=0A+{=0A+=09return=20= (TransactionIdPrecedesOrEquals(ctx->oldest_valid_xid,=20xid)=20&&=0A+=09=09= =09TransactionIdPrecedes(xid,=20ctx->next_valid_xid));=0A+}=0A+=0A+/*=0A= +=20*=20Given=20a=20TransactionId,=20attempt=20to=20interpret=20it=20as=20= a=20valid=0A+=20*=20FullTransactionId,=20neither=20in=20the=20future=20= nor=20overlong=20in=0A+=20*=20the=20past.=20=20Stores=20the=20inferred=20= FullTransactionId=20in=20*fxid.=0A+=20*=0A+=20*=20Returns=20whether=20= the=20xid=20is=20newer=20than=20the=20oldest=20clog=20xid.=0A+=20*/=0A= +static=20bool=0A+xid_valid_in_rel(TransactionId=20xid,=20= HeapCheckContext=20*ctx)=0A+{=0A+=09/*=20Quick=20return=20for=20special=20= oids=20*/=0A+=09switch=20(xid)=0A+=09{=0A+=09=09case=20= InvalidTransactionId:=0A+=09=09=09return=20false;=0A+=09=09case=20= BootstrapTransactionId:=0A+=09=09case=20FrozenTransactionId:=0A+=09=09=09= return=20true;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20If=20this=20xid=20is=20= within=20the=20last=20known=20valid=20range=20of=20xids,=20then=20it=20= has=0A+=09=20*=20to=20be=20ok.=20=20The=20oldest=20valid=20xid=20cannot=20= advance,=20because=20we=20have=20too=0A+=09=20*=20strong=20a=20lock=20on=20= the=20relation=20for=20that,=20and=20although=20the=20newest=20valid=0A+=09= =20*=20xid=20may=20advance,=20that=20doesn't=20invalidate=20anything=20= from=20the=20range=20we've=0A+=09=20*=20already=20identified.=0A+=09=20= */=0A+=09if=20(XidInValidRange(xid,=20ctx))=0A+=09=09return=20true;=0A+=0A= +=09/*=20The=20latest=20valid=20xid=20may=20have=20advanced.=20=20= Recheck.=20*/=0A+=09ctx->next_valid_xid=20=3D=0A+=09=09= XidFromFullTransactionId(ReadNextFullTransactionId());=0A+=09if=20= (XidInValidRange(xid,=20ctx))=0A+=09=09return=20true;=0A+=0A+=09/*=20No=20= good.=20=20This=20xid=20is=20invalid.=20*/=0A+=09return=20false;=0A+}=0A= +=0A+/*=0A+=20*=20tuple_is_visible=0A+=20*=0A+=20*=09Determine=20whether=20= tuples=20are=20visible=20for=20verification.=20=20Similar=20to=0A+=20*=20= =20HeapTupleSatisfiesVacuum,=20but=20with=20critical=20differences.=0A+=20= *=0A+=20*=20=201)=20Does=20not=20touch=20hint=20bits.=20=20It=20seems=20= imprudent=20to=20write=20hint=20bits=0A+=20*=20=20=20=20=20to=20a=20= table=20during=20a=20corruption=20check.=0A+=20*=20=202)=20Only=20makes=20= a=20boolean=20determination=20of=20whether=20verification=20should=0A+=20= *=20=20=20=20=20see=20the=20tuple,=20rather=20than=20doing=20extra=20= work=20for=20vacuum-related=0A+=20*=20=20=20=20=20categorization.=0A+=20= *=0A+=20*=20=20The=20caller=20should=20already=20have=20checked=20that=20= xmin=20and=20xmax=20are=20not=20out=20of=0A+=20*=20=20bounds=20for=20the=20= relation.=0A+=20*/=0A+static=20bool=0A+tuple_is_visible(HeapTupleHeader=20= tuphdr,=20HeapCheckContext=20*ctx)=0A+{=0A+=09uint16=09=09infomask=20=3D=20= tuphdr->t_infomask;=0A+=0A+=09if=20= (!HeapTupleHeaderXminCommitted(tuphdr))=0A+=09{=0A+=09=09TransactionId=20= raw_xmin=20=3D=20HeapTupleHeaderGetRawXmin(tuphdr);=0A+=0A+=09=09if=20= (HeapTupleHeaderXminInvalid(tuphdr))=0A+=09=09{=0A+=09=09=09return=20= false;=09=09/*=20HEAPTUPLE_DEAD=20*/=0A+=09=09}=0A+=09=09/*=20Used=20by=20= pre-9.0=20binary=20upgrades=20*/=0A+=09=09else=20if=20(infomask=20&=20= HEAP_MOVED_OFF=20||=0A+=09=09=09=09=20infomask=20&=20HEAP_MOVED_IN)=0A+=09= =09{=0A+=09=09=09TransactionId=20xvac=20=3D=20= HeapTupleHeaderGetXvac(tuphdr);=0A+=0A+=09=09=09if=20= (TransactionIdIsCurrentTransactionId(xvac))=0A+=09=09=09=09return=20= true;=09/*=20HEAPTUPLE_DELETE_IN_PROGRESS=20*/=0A+=09=09=09if=20= (TransactionIdIsInProgress(xvac))=0A+=09=09=09=09return=20true;=09/*=20= HEAPTUPLE_DELETE_IN_PROGRESS=20*/=0A+=0A+=09=09=09if=20= (!xid_valid_in_rel(xvac,=20ctx))=0A+=09=09=09{=0A+=09=09=09=09= confess(ctx,=0A+=09=09=09=09=09=09psprintf("old-style=20VACUUM=20FULL=20= transaction=20ID=20%u=20is=20invalid=20in=20this=20relation",=0A+=09=09=09= =09=09=09=09=09=20xvac));=0A+=09=09=09=09return=20false;=0A+=09=09=09}=0A= +=09=09=09else=20if=20(TransactionIdDidCommit(xvac))=0A+=09=09=09=09= return=20false;=09/*=20HEAPTUPLE_DEAD=20*/=0A+=09=09}=0A+=09=09else=20if=20= (TransactionIdIsCurrentTransactionId(raw_xmin))=0A+=09=09=09return=20= true;=09=09/*=20insert=20or=20delete=20in=20progress=20*/=0A+=09=09else=20= if=20(TransactionIdIsInProgress(raw_xmin))=0A+=09=09=09return=20true;=09=09= /*=20HEAPTUPLE_INSERT_IN_PROGRESS=20*/=0A+=09=09else=20if=20= (!TransactionIdDidCommit(raw_xmin))=0A+=09=09{=0A+=09=09=09return=20= false;=09=09/*=20HEAPTUPLE_DEAD=20*/=0A+=09=09}=0A+=09}=0A+=0A+=09if=20= (!(infomask=20&=20HEAP_XMAX_INVALID)=20&&=20= !HEAP_XMAX_IS_LOCKED_ONLY(infomask))=0A+=09{=0A+=09=09if=20(infomask=20&=20= HEAP_XMAX_IS_MULTI)=0A+=09=09{=0A+=09=09=09TransactionId=20xmax=20=3D=20= HeapTupleGetUpdateXid(tuphdr);=0A+=0A+=09=09=09/*=20not=20LOCKED_ONLY,=20= so=20it=20has=20to=20have=20an=20xmax=20*/=0A+=09=09=09if=20= (!TransactionIdIsValid(xmax))=0A+=09=09=09{=0A+=09=09=09=09confess(ctx,=0A= +=09=09=09=09=09=09pstrdup("heap=20tuple=20with=20XMAX_IS_MULTI=20is=20= neither=20LOCKED_ONLY=20nor=20has=20a=20valid=20xmax"));=0A+=09=09=09=09= return=20false;=0A+=09=09=09}=0A+=09=09=09if=20= (TransactionIdIsInProgress(xmax))=0A+=09=09=09=09return=20true;=09/*=20= HEAPTUPLE_DELETE_IN_PROGRESS=20*/=0A+=0A+=09=09=09else=20if=20= (TransactionIdDidCommit(xmax))=0A+=09=09=09{=0A+=09=09=09=09return=20= false;=09/*=20HEAPTUPLE_RECENTLY_DEAD=20or=20HEAPTUPLE_DEAD=20*/=0A+=09=09= =09}=0A+=09=09=09/*=20Ok,=20the=20tuple=20is=20live=20*/=0A+=09=09}=0A+=09= =09else=20if=20(!(infomask=20&=20HEAP_XMAX_COMMITTED))=0A+=09=09{=0A+=09=09= =09if=20(TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuphdr)))=0A= +=09=09=09=09return=20true;=09/*=20HEAPTUPLE_DELETE_IN_PROGRESS=20*/=0A+=09= =09=09/*=20Ok,=20the=20tuple=20is=20live=20*/=0A+=09=09}=0A+=09=09else=0A= +=09=09=09return=20false;=09=09/*=20HEAPTUPLE_RECENTLY_DEAD=20or=20= HEAPTUPLE_DEAD=20*/=0A+=09}=0A+=09return=20true;=0A+}=0A+=0A+/*=0A+=20*=20= check_toast_tuple=0A+=20*=0A+=20*=20=20=20Checks=20the=20current=20toast=20= tuple=20as=20tracked=20in=20ctx=20for=20corruption.=20=20Records=0A+=20*=20= =20=20any=20corruption=20found=20in=20ctx->corruption.=0A+=20*/=0A= +static=20void=0A+check_toast_tuple(HeapTuple=20toasttup,=20= HeapCheckContext=20*ctx)=0A+{=0A+=09int32=09=09curchunk;=0A+=09Pointer=09= =09chunk;=0A+=09bool=09=09isnull;=0A+=09char=09=20=20=20*chunkdata;=0A+=09= int32=09=09chunksize;=0A+=09int32=09=09expected_size;=0A+=0A+=09/*=0A+=09= =20*=20Have=20a=20chunk,=20extract=20the=20sequence=20number=20and=20the=20= data=0A+=09=20*/=0A+=09curchunk=20=3D=20= DatumGetInt32(fastgetattr(toasttup,=202,=0A+=09=09=09=09=09=09=09=09=09=09= =20ctx->toast_rel->rd_att,=20&isnull));=0A+=09if=20(isnull)=0A+=09{=0A+=09= =09confess(ctx,=0A+=09=09=09=09pstrdup("toast=20chunk=20sequence=20= number=20is=20null"));=0A+=09=09return;=0A+=09}=0A+=09chunk=20=3D=20= DatumGetPointer(fastgetattr(toasttup,=203,=0A+=09=09=09=09=09=09=09=09=09= =09ctx->toast_rel->rd_att,=20&isnull));=0A+=09if=20(isnull)=0A+=09{=0A+=09= =09confess(ctx,=0A+=09=09=09=09pstrdup("toast=20chunk=20data=20is=20= null"));=0A+=09=09return;=0A+=09}=0A+=09if=20= (!VARATT_IS_EXTENDED(chunk))=0A+=09{=0A+=09=09chunksize=20=3D=20= VARSIZE(chunk)=20-=20VARHDRSZ;=0A+=09=09chunkdata=20=3D=20= VARDATA(chunk);=0A+=09}=0A+=09else=20if=20(VARATT_IS_SHORT(chunk))=0A+=09= {=0A+=09=09/*=0A+=09=09=20*=20could=20happen=20due=20to=20= heap_form_tuple=20doing=20its=20thing=0A+=09=09=20*/=0A+=09=09chunksize=20= =3D=20VARSIZE_SHORT(chunk)=20-=20VARHDRSZ_SHORT;=0A+=09=09chunkdata=20=3D=20= VARDATA_SHORT(chunk);=0A+=09}=0A+=09else=0A+=09{=0A+=09=09/*=20should=20= never=20happen=20*/=0A+=09=09uint32=09=09header=20=3D=20((varattrib_4b=20= *)=20chunk)->va_4byte.va_header;=0A+=0A+=09=09confess(ctx,=0A+=09=09=09=09= psprintf("corrupt=20extended=20toast=20chunk=20with=20sequence=20number=20= %d=20has=20invalid=20varlena=20header=20%0x",=0A+=09=09=09=09=09=09=20= curchunk,=20header));=0A+=09=09return;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20= Some=20checks=20on=20the=20data=20we've=20found=0A+=09=20*/=0A+=09if=20= (curchunk=20!=3D=20ctx->chunkno)=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09=09= =09psprintf("toast=20chunk=20sequence=20number=20%u=20not=20the=20= expected=20sequence=20number=20%u",=0A+=09=09=09=09=09=09=20curchunk,=20= ctx->chunkno));=0A+=09=09return;=0A+=09}=0A+=09if=20(curchunk=20>=20= ctx->endchunk)=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09=09=09= psprintf("toast=20chunk=20sequence=20number=20%u=20exceeds=20the=20end=20= chunk=20sequence=20number=20%u",=0A+=09=09=09=09=09=09=20curchunk,=20= ctx->endchunk));=0A+=09=09return;=0A+=09}=0A+=0A+=09expected_size=20=3D=20= curchunk=20<=20ctx->totalchunks=20-=201=20?=20TOAST_MAX_CHUNK_SIZE=0A+=09= =09:=20ctx->attrsize=20-=20((ctx->totalchunks=20-=201)=20*=20= TOAST_MAX_CHUNK_SIZE);=0A+=09if=20(chunksize=20!=3D=20expected_size)=0A+=09= {=0A+=09=09confess(ctx,=0A+=09=09=09=09psprintf("toast=20chunk=20size=20= %u=20differs=20from=20expected=20size=20%u",=0A+=09=09=09=09=09=09=20= chunksize,=20expected_size));=0A+=09=09return;=0A+=09}=0A+=0A+=09= ctx->chunkno++;=0A+}=0A+=0A+/*=0A+=20*=20check_tuple_attribute=0A+=20*=0A= +=20*=20=20=20Checks=20the=20current=20attribute=20as=20tracked=20in=20= ctx=20for=20corruption.=20=20Records=0A+=20*=20=20=20any=20corruption=20= found=20in=20ctx->corruption.=0A+=20*=0A+=20*=20=20=20This=20function=20= follows=20the=20logic=20performed=20by=20heap_deform_tuple(),=20and=20in=0A= +=20*=20=20=20the=20case=20of=20a=20toasted=20value,=20continues=20along=20= the=20logic=20of=0A+=20*=20=20=20detoast_external_attr(),=20checking=20= for=20any=20conditions=20that=20would=20result=20in=0A+=20*=20=20=20= either=20of=20those=20functions=20Asserting=20or=20crashing=20the=20= backend.=20=20The=20checks=0A+=20*=20=20=20performed=20by=20Asserts=20= present=20in=20those=20two=20functions=20are=20also=20performed=0A+=20*=20= =20=20here.=20=20In=20cases=20where=20those=20two=20functions=20are=20a=20= bit=20cavalier=20in=20their=0A+=20*=20=20=20assumptions=20about=20data=20= being=20correct,=20we=20perform=20additional=20checks=20not=0A+=20*=20=20= =20present=20in=20either=20of=20those=20two=20functions.=20=20Where=20= some=20condition=20is=20checked=0A+=20*=20=20=20in=20both=20of=20those=20= functions,=20we=20perform=20it=20here=20twice,=20as=20we=20parallel=20= the=0A+=20*=20=20=20logical=20flow=20of=20those=20two=20functions.=20=20= The=20presence=20of=20duplicate=20checks=0A+=20*=20=20=20seems=20a=20= reasonable=20price=20to=20pay=20for=20keeping=20this=20code=20tightly=20= coupled=20with=0A+=20*=20=20=20the=20code=20it=20protects.=0A+=20*=0A+=20= *=20=20=20Returns=20true=20if=20the=20tuple=20attribute=20is=20sane=20= enough=20for=20processing=20to=0A+=20*=20=20=20continue=20on=20to=20the=20= next=20attribute,=20false=20otherwise.=0A+=20*/=0A+static=20bool=0A= +check_tuple_attribute(HeapCheckContext=20*ctx)=0A+{=0A+=09struct=20= varatt_external=20toast_pointer;=0A+=09ScanKeyData=20toastkey;=0A+=09= SysScanDesc=20toastscan;=0A+=09SnapshotData=20SnapshotToast;=0A+=09= HeapTuple=09toasttup;=0A+=09bool=09=09found_toasttup;=0A+=09Datum=09=09= attdatum;=0A+=09struct=20varlena=20*attr;=0A+=09char=09=20=20=20*tp;=09=09= =09=09/*=20pointer=20to=20the=20tuple=20data=20*/=0A+=09uint16=09=09= infomask;=0A+=09Form_pg_attribute=20thisatt;=0A+=0A+=09infomask=20=3D=20= ctx->tuphdr->t_infomask;=0A+=09thisatt=20=3D=20= TupleDescAttr(RelationGetDescr(ctx->rel),=20ctx->attnum);=0A+=0A+=09tp=20= =3D=20(char=20*)=20ctx->tuphdr=20+=20ctx->tuphdr->t_hoff;=0A+=0A+=09if=20= (ctx->tuphdr->t_hoff=20+=20ctx->offset=20>=20ctx->lp_len)=0A+=09{=0A+=09=09= confess(ctx,=0A+=09=09=09=09psprintf("tuple=20attribute=20should=20start=20= at=20offset=20%u,=20but=20tuple=20length=20is=20only=20%u",=0A+=09=09=09=09= =09=09=20ctx->tuphdr->t_hoff=20+=20ctx->offset,=20ctx->lp_len));=0A+=09=09= return=20false;=0A+=09}=0A+=0A+=09/*=20Skip=20null=20values=20*/=0A+=09= if=20(infomask=20&=20HEAP_HASNULL=20&&=20att_isnull(ctx->attnum,=20= ctx->tuphdr->t_bits))=0A+=09=09return=20true;=0A+=0A+=09/*=20Skip=20= non-varlena=20values,=20but=20update=20offset=20first=20*/=0A+=09if=20= (thisatt->attlen=20!=3D=20-1)=0A+=09{=0A+=09=09ctx->offset=20=3D=20= att_align_nominal(ctx->offset,=20thisatt->attalign);=0A+=09=09= ctx->offset=20=3D=20att_addlength_pointer(ctx->offset,=20= thisatt->attlen,=0A+=09=09=09=09=09=09=09=09=09=09=09tp=20+=20= ctx->offset);=0A+=09=09return=20true;=0A+=09}=0A+=0A+=09/*=20Ok,=20we're=20= looking=20at=20a=20varlena=20attribute.=20*/=0A+=09ctx->offset=20=3D=20= att_align_pointer(ctx->offset,=20thisatt->attalign,=20-1,=0A+=09=09=09=09= =09=09=09=09=09tp=20+=20ctx->offset);=0A+=0A+=09/*=20Get=20the=20= (possibly=20corrupt)=20varlena=20datum=20*/=0A+=09attdatum=20=3D=20= fetchatt(thisatt,=20tp=20+=20ctx->offset);=0A+=0A+=09/*=0A+=09=20*=20We=20= have=20the=20datum,=20but=20we=20cannot=20decode=20it=20carelessly,=20as=20= it=20may=20still=0A+=09=20*=20be=20corrupt.=0A+=09=20*/=0A+=0A+=09/*=0A+=09= =20*=20Check=20that=20VARTAG_SIZE=20won't=20hit=20a=20TrapMacro=20on=20a=20= corrupt=20va_tag=20before=0A+=09=20*=20risking=20a=20call=20into=20= att_addlength_pointer=0A+=09=20*/=0A+=09if=20(VARATT_IS_EXTERNAL(tp=20+=20= ctx->offset))=0A+=09{=0A+=09=09uint8=09=09va_tag=20=3D=20= VARTAG_EXTERNAL(tp=20+=20ctx->offset);=0A+=0A+=09=09if=20(va_tag=20!=3D=20= VARTAG_ONDISK)=0A+=09=09{=0A+=09=09=09confess(ctx,=0A+=09=09=09=09=09= psprintf("%s=20toast=20at=20offset=20%u=20is=20unexpected",=0A+=09=09=09=09= =09=09=09=20va_tag=20=3D=3D=20VARTAG_INDIRECT=20?=20"indirect"=20:=0A+=09= =09=09=09=09=09=09=20va_tag=20=3D=3D=20VARTAG_EXPANDED_RO=20?=20= "expanded"=20:=0A+=09=09=09=09=09=09=09=20va_tag=20=3D=3D=20= VARTAG_EXPANDED_RW=20?=20"expanded"=20:=0A+=09=09=09=09=09=09=09=20= "unexpected",=0A+=09=09=09=09=09=09=09=20ctx->tuphdr->t_hoff=20+=20= ctx->offset));=0A+=09=09=09/*=20We=20can't=20know=20where=20the=20next=20= attribute=20begins=20*/=0A+=09=09=09return=20false;=0A+=09=09}=0A+=09}=0A= +=0A+=09/*=20Ok,=20should=20be=20safe=20now=20*/=0A+=09ctx->offset=20=3D=20= att_addlength_pointer(ctx->offset,=20thisatt->attlen,=0A+=09=09=09=09=09=09= =09=09=09=09tp=20+=20ctx->offset);=0A+=0A+=09if=20(ctx->tuphdr->t_hoff=20= +=20ctx->offset=20>=20ctx->lp_len)=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09= =09=09psprintf("tuple=20attribute=20of=20length=20%u=20ends=20at=20= offset=20%u,=20but=20tuple=20length=20is=20only=20%u",=0A+=09=09=09=09=09= =09=20thisatt->attlen,=20ctx->tuphdr->t_hoff=20+=20ctx->offset,=0A+=09=09= =09=09=09=09=20ctx->lp_len));=0A+=09=09return=20false;=0A+=09}=0A+=0A+=09= /*=0A+=09=20*=20heap_deform_tuple=20would=20be=20done=20with=20this=20= attribute=20at=20this=20point,=0A+=09=20*=20having=20stored=20it=20in=20= values[],=20and=20would=20continue=20to=20the=20next=20attribute.=0A+=09=20= *=20We=20go=20further,=20because=20we=20need=20to=20check=20if=20the=20= toast=20datum=20is=20corrupt.=0A+=09=20*/=0A+=0A+=09attr=20=3D=20(struct=20= varlena=20*)=20DatumGetPointer(attdatum);=0A+=0A+=09/*=0A+=09=20*=20Now=20= we=20follow=20the=20logic=20of=20detoast_external_attr(),=20with=20the=20= same=0A+=09=20*=20caveats=20about=20being=20paranoid=20about=20= corruption.=0A+=09=20*/=0A+=0A+=09/*=20Skip=20values=20that=20are=20not=20= external=20*/=0A+=09if=20(!VARATT_IS_EXTERNAL(attr))=0A+=09=09return=20= true;=0A+=0A+=09/*=20It=20is=20external,=20and=20we're=20looking=20at=20= a=20page=20on=20disk=20*/=0A+=0A+=09/*=20The=20tuple=20header=20better=20= claim=20to=20contain=20toasted=20values=20*/=0A+=09if=20(!(infomask=20&=20= HEAP_HASEXTERNAL))=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09=09=09= pstrdup("attribute=20is=20external=20but=20tuple=20header=20flag=20= HEAP_HASEXTERNAL=20not=20set"));=0A+=09=09return=20true;=0A+=09}=0A+=0A+=09= /*=20The=20relation=20better=20have=20a=20toast=20table=20*/=0A+=09if=20= (!ctx->rel->rd_rel->reltoastrelid)=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09= =09=09pstrdup("attribute=20is=20external=20but=20relation=20has=20no=20= toast=20relation"));=0A+=09=09return=20true;=0A+=09}=0A+=0A+=09/*=0A+=09=20= *=20Must=20copy=20attr=20into=20toast_pointer=20for=20alignment=20= considerations=0A+=09=20*/=0A+=09= VARATT_EXTERNAL_GET_POINTER(toast_pointer,=20attr);=0A+=0A+=09= ctx->attrsize=20=3D=20toast_pointer.va_extsize;=0A+=09ctx->endchunk=20=3D=20= (ctx->attrsize=20-=201)=20/=20TOAST_MAX_CHUNK_SIZE;=0A+=09= ctx->totalchunks=20=3D=20ctx->endchunk=20+=201;=0A+=0A+=09/*=0A+=09=20*=20= Setup=20a=20scan=20key=20to=20find=20chunks=20in=20toast=20table=20with=20= matching=20va_valueid=0A+=09=20*/=0A+=09ScanKeyInit(&toastkey,=0A+=09=09=09= =09(AttrNumber)=201,=0A+=09=09=09=09BTEqualStrategyNumber,=20F_OIDEQ,=0A= +=09=09=09=09ObjectIdGetDatum(toast_pointer.va_valueid));=0A+=0A+=09/*=0A= +=09=20*=20Check=20if=20any=20chunks=20for=20this=20toasted=20object=20= exist=20in=20the=20toast=20table,=0A+=09=20*=20accessible=20via=20the=20= index.=0A+=09=20*/=0A+=09init_toast_snapshot(&SnapshotToast);=0A+=09= toastscan=20=3D=20systable_beginscan_ordered(ctx->toast_rel,=0A+=09=09=09= =09=09=09=09=09=09=09=20=20=20ctx->valid_toast_index,=0A+=09=09=09=09=09=09= =09=09=09=09=20=20=20&SnapshotToast,=201,=0A+=09=09=09=09=09=09=09=09=09=09= =20=20=20&toastkey);=0A+=09ctx->chunkno=20=3D=200;=0A+=0A+=09= found_toasttup=20=3D=20false;=0A+=09while=20((toasttup=20=3D=0A+=09=09=09= systable_getnext_ordered(toastscan,=0A+=09=09=09=09=09=09=09=09=09=20= ForwardScanDirection))=20!=3D=20NULL)=0A+=09{=0A+=09=09found_toasttup=20= =3D=20true;=0A+=09=09check_toast_tuple(toasttup,=20ctx);=0A+=09}=0A+=09= if=20(ctx->chunkno=20!=3D=20(ctx->endchunk=20+=201))=0A+=09=09= confess(ctx,=0A+=09=09=09=09psprintf("final=20chunk=20number=20%u=20= differs=20from=20expected=20value=20%u",=0A+=09=09=09=09=09=09=20= ctx->chunkno,=20(ctx->endchunk=20+=201)));=0A+=09if=20(!found_toasttup)=0A= +=09=09confess(ctx,=0A+=09=09=09=09pstrdup("toasted=20value=20missing=20= from=20toast=20table"));=0A+=09systable_endscan_ordered(toastscan);=0A+=0A= +=09return=20true;=0A+}=0A+=0A+/*=0A+=20*=20check_tuple=0A+=20*=0A+=20*=20= =20=20Checks=20the=20current=20tuple=20as=20tracked=20in=20ctx=20for=20= corruption.=20=20Records=20any=0A+=20*=20=20=20corruption=20found=20in=20= ctx->corruption.=0A+=20*/=0A+static=20void=0A= +check_tuple(HeapCheckContext=20*ctx)=0A+{=0A+=09TransactionId=20xmin;=0A= +=09TransactionId=20xmax;=0A+=09bool=09=09fatal=20=3D=20false;=0A+=09= uint16=09=09infomask=20=3D=20ctx->tuphdr->t_infomask;=0A+=0A+=09/*=0A+=09= =20*=20If=20the=20line=20pointer=20for=20this=20tuple=20does=20not=20= reserve=20enough=20space=20for=20a=0A+=09=20*=20complete=20tuple=20= header,=20we=20dare=20not=20read=20the=20tuple=20header.=0A+=09=20*/=0A+=09= if=20(ctx->lp_len=20<=20MAXALIGN(SizeofHeapTupleHeader))=0A+=09{=0A+=09=09= confess(ctx,=0A+=09=09=09=09psprintf("tuple's=20%u=20byte=20line=20= pointer=20length=20is=20less=20than=20the=20%u=20byte=20minimum=20tuple=20= header=20size",=0A+=09=09=09=09=09=09=20ctx->lp_len,=20(uint32)=20= MAXALIGN(SizeofHeapTupleHeader)));=0A+=09=09return;=0A+=09}=0A+=0A+=09/*=20= Check=20relminmxid=20against=20mxid,=20if=20any=20*/=0A+=09xmax=20=3D=20= HeapTupleHeaderGetRawXmax(ctx->tuphdr);=0A+=09if=20(infomask=20&=20= HEAP_XMAX_IS_MULTI=20&&=0A+=09=09MultiXactIdPrecedes(xmax,=20= ctx->relminmxid))=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09=09=09= psprintf("tuple=20xmax=20%u=20precedes=20relminmxid=20%u",=0A+=09=09=09=09= =09=09=20xmax,=20ctx->relminmxid));=0A+=09=09fatal=20=3D=20true;=0A+=09}=0A= +=0A+=09/*=20Check=20xmin=20against=20relfrozenxid=20*/=0A+=09xmin=20=3D=20= HeapTupleHeaderGetXmin(ctx->tuphdr);=0A+=09if=20= (TransactionIdIsNormal(ctx->relfrozenxid)=20&&=0A+=09=09= TransactionIdIsNormal(xmin))=0A+=09{=0A+=09=09if=20= (TransactionIdPrecedes(xmin,=20ctx->relfrozenxid))=0A+=09=09{=0A+=09=09=09= confess(ctx,=0A+=09=09=09=09=09psprintf("tuple=20xmin=20%u=20precedes=20= relfrozenxid=20%u",=0A+=09=09=09=09=09=09=09=20xmin,=20= ctx->relfrozenxid));=0A+=09=09=09fatal=20=3D=20true;=0A+=09=09}=0A+=09=09= else=20if=20(!xid_valid_in_rel(xmin,=20ctx))=0A+=09=09{=0A+=09=09=09= confess(ctx,=0A+=09=09=09=09=09psprintf("tuple=20xmin=20%u=20follows=20= last=20assigned=20xid=20%u",=0A+=09=09=09=09=09=09=09=20xmin,=20= ctx->next_valid_xid));=0A+=09=09=09fatal=20=3D=20true;=0A+=09=09}=0A+=09= }=0A+=0A+=09/*=20Check=20xmax=20against=20relfrozenxid=20*/=0A+=09if=20= (TransactionIdIsNormal(ctx->relfrozenxid)=20&&=0A+=09=09= TransactionIdIsNormal(xmax))=0A+=09{=0A+=09=09if=20= (TransactionIdPrecedes(xmax,=20ctx->relfrozenxid))=0A+=09=09{=0A+=09=09=09= confess(ctx,=0A+=09=09=09=09=09psprintf("tuple=20xmax=20%u=20precedes=20= relfrozenxid=20%u",=0A+=09=09=09=09=09=09=09=20xmax,=20= ctx->relfrozenxid));=0A+=09=09=09fatal=20=3D=20true;=0A+=09=09}=0A+=09=09= else=20if=20(!xid_valid_in_rel(xmax,=20ctx))=0A+=09=09{=0A+=09=09=09= confess(ctx,=0A+=09=09=09=09=09psprintf("tuple=20xmax=20%u=20follows=20= last=20assigned=20xid=20%u",=0A+=09=09=09=09=09=09=09=20xmax,=20= ctx->next_valid_xid));=0A+=09=09=09fatal=20=3D=20true;=0A+=09=09}=0A+=09= }=0A+=0A+=09/*=20Check=20for=20tuple=20header=20corruption=20*/=0A+=09if=20= (ctx->tuphdr->t_hoff=20<=20SizeofHeapTupleHeader)=0A+=09{=0A+=09=09= confess(ctx,=0A+=09=09=09=09psprintf("tuple's=20header=20size=20is=20%u=20= bytes=20which=20is=20less=20than=20the=20%u=20byte=20minimum=20valid=20= header=20size",=0A+=09=09=09=09=09=09=20ctx->tuphdr->t_hoff,=0A+=09=09=09= =09=09=09=20(unsigned)=20SizeofHeapTupleHeader));=0A+=09=09fatal=20=3D=20= true;=0A+=09}=0A+=09if=20(ctx->tuphdr->t_hoff=20>=20ctx->lp_len)=0A+=09{=0A= +=09=09confess(ctx,=0A+=09=09=09=09psprintf("tuple's=20%u=20byte=20= header=20size=20exceeds=20the=20%u=20byte=20length=20of=20the=20entire=20= tuple",=0A+=09=09=09=09=09=09=20ctx->tuphdr->t_hoff,=20ctx->lp_len));=0A= +=09=09fatal=20=3D=20true;=0A+=09}=0A+=09if=20(ctx->tuphdr->t_hoff=20!=3D=20= MAXALIGN(ctx->tuphdr->t_hoff))=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09=09= =09psprintf("tuple's=20user=20data=20offset=20%u=20not=20maximally=20= aligned=20to=20%u",=0A+=09=09=09=09=09=09=20ctx->tuphdr->t_hoff,=20= (uint32)=20MAXALIGN(ctx->tuphdr->t_hoff)));=0A+=09=09fatal=20=3D=20true;=0A= +=09}=0A+=09if=20((ctx->tuphdr->t_infomask=20&=20HEAP_XMAX_LOCK_ONLY)=20= &&=0A+=09=09(ctx->tuphdr->t_infomask2=20&=20HEAP_KEYS_UPDATED))=0A+=09{=0A= +=09=09confess(ctx,=0A+=09=09=09=09psprintf("tuple=20xmax=20marked=20= incompatibly=20as=20keys=20updated=20and=20locked=20only"));=0A+=09}=0A+=09= if=20((ctx->tuphdr->t_infomask=20&=20HEAP_XMAX_COMMITTED)=20&&=0A+=09=09= (ctx->tuphdr->t_infomask=20&=20HEAP_XMAX_IS_MULTI))=0A+=09{=0A+=09=09= confess(ctx,=0A+=09=09=09=09psprintf("tuple=20xmax=20marked=20= incompatibly=20as=20committed=20and=20as=20a=20multitransaction=20ID"));=0A= +=09}=0A+=0A+=09/*=0A+=09=20*=20If=20the=20tuple=20has=20nulls,=20check=20= that=20the=20implied=20length=20of=20the=20variable=0A+=09=20*=20length=20= nulls=20bitmap=20field=20t_bits=20does=20not=20overflow=20the=20allowed=20= space.=0A+=09=20*=20We=20don't=20know=20if=20the=20corruption=20is=20in=20= the=20t_hoff=20field=20or=20the=20infomask=0A+=09=20*=20bit=20= HEAP_HASNULL.=0A+=09=20*=0A+=09=20*=20If=20the=20tuple=20does=20not=20= have=20nulls,=20check=20that=20no=20space=20has=20been=20reserved=0A+=09=20= *=20for=20the=20null=20bitmap.=0A+=09=20*/=0A+=09if=20((infomask=20&=20= HEAP_HASNULL)=20&&=0A+=09=09(ctx->tuphdr->t_hoff=20!=3D=20= MAXALIGN(SizeofHeapTupleHeader=20+=20BITMAPLEN(ctx->natts))))=0A+=09{=0A= +=09=09confess(ctx,=0A+=09=09=09=09psprintf("tuple=20with=20null=20= values=20has=20user=20data=20offset=20%u=20rather=20than=20the=20= expected=20offset=20%u",=0A+=09=09=09=09=09=09=20ctx->tuphdr->t_hoff,=0A= +=09=09=09=09=09=09=20(uint32)=20MAXALIGN(SizeofHeapTupleHeader=20+=20= BITMAPLEN(ctx->natts))));=0A+=09=09fatal=20=3D=20true;=0A+=09}=0A+=09= else=20if=20(!(infomask=20&=20HEAP_HASNULL)=20&&=0A+=09=09=09=20= (ctx->tuphdr->t_hoff=20!=3D=20MAXALIGN(SizeofHeapTupleHeader)))=0A+=09{=0A= +=09=09confess(ctx,=0A+=09=09=09=09psprintf("tuple=20without=20null=20= values=20has=20user=20data=20offset=20%u=20rather=20than=20the=20= expected=20offset=20%u",=0A+=09=09=09=09=09=09=20ctx->tuphdr->t_hoff,=0A= +=09=09=09=09=09=09=20(uint32)=20MAXALIGN(SizeofHeapTupleHeader)));=0A+=09= =09fatal=20=3D=20true;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Cannot=20= process=20tuple=20data=20if=20tuple=20header=20was=20corrupt,=20as=20the=20= offsets=0A+=09=20*=20within=20the=20page=20cannot=20be=20trusted,=20= leaving=20too=20much=20risk=20of=20reading=0A+=09=20*=20garbage=20if=20= we=20continue.=0A+=09=20*=0A+=09=20*=20We=20also=20cannot=20process=20= the=20tuple=20if=20the=20xmin=20or=20xmax=20were=20invalid=0A+=09=20*=20= relative=20to=20relfrozenxid=20or=20relminmxid,=20as=20clog=20entries=20= for=20the=20xids=0A+=09=20*=20may=20already=20be=20gone.=0A+=09=20*/=0A+=09= if=20(fatal)=0A+=09=09return;=0A+=0A+=09/*=0A+=09=20*=20Skip=20tuples=20= that=20are=20invisible,=20as=20we=20cannot=20assume=20the=20TupleDesc=20= we=0A+=09=20*=20are=20using=20is=20appropriate.=0A+=09=20*/=0A+=09if=20= (!tuple_is_visible(ctx->tuphdr,=20ctx))=0A+=09=09return;=0A+=0A+=09/*=0A= +=09=20*=20If=20we=20get=20this=20far,=20the=20tuple=20is=20visible=20to=20= us,=20so=20it=20must=20not=20be=0A+=09=20*=20incompatible=20with=20our=20= relDesc.=20=20The=20natts=20field=20could=20be=20legitimately=0A+=09=20*=20= shorter=20than=20rel's=20natts,=20but=20it=20cannot=20be=20longer=20than=20= rel's=20natts.=0A+=09=20*/=0A+=09if=20(RelationGetDescr(ctx->rel)->natts=20= <=20ctx->natts)=0A+=09{=0A+=09=09confess(ctx,=0A+=09=09=09=09= psprintf("tuple=20has=20%u=20attributes=20in=20relation=20with=20only=20= %u=20attributes",=0A+=09=09=09=09=09=09=20ctx->natts,=0A+=09=09=09=09=09=09= =20RelationGetDescr(ctx->rel)->natts));=0A+=09=09return;=0A+=09}=0A+=0A+=09= /*=0A+=09=20*=20Iterate=20over=20the=20attributes=20looking=20for=20= broken=20toast=20values.=20This=0A+=09=20*=20roughly=20follows=20the=20= logic=20of=20heap_deform_tuple,=20except=20that=20it=20doesn't=0A+=09=20= *=20bother=20building=20up=20isnull[]=20and=20values[]=20arrays,=20since=20= nobody=20wants=0A+=09=20*=20them,=20and=20it=20unrolls=20anything=20that=20= might=20trip=20over=20an=20Assert=20when=0A+=09=20*=20processing=20= corrupt=20data.=0A+=09=20*/=0A+=09ctx->offset=20=3D=200;=0A+=09for=20= (ctx->attnum=20=3D=200;=20ctx->attnum=20<=20ctx->natts;=20ctx->attnum++)=0A= +=09{=0A+=09=09if=20(!check_tuple_attribute(ctx))=0A+=09=09=09break;=0A+=09= }=0A+}=0Adiff=20--git=20a/doc/src/sgml/amcheck.sgml=20= b/doc/src/sgml/amcheck.sgml=0Aindex=20a9df2c1a9d..b8170bbfdf=20100644=0A= ---=20a/doc/src/sgml/amcheck.sgml=0A+++=20b/doc/src/sgml/amcheck.sgml=0A= @@=20-69,7=20+69,7=20@@=20AND=20c.relpersistence=20!=3D=20't'=0A=20--=20= Function=20may=20throw=20an=20error=20when=20this=20is=20omitted:=0A=20= AND=20c.relkind=20=3D=20'i'=20AND=20i.indisready=20AND=20i.indisvalid=0A=20= ORDER=20BY=20c.relpages=20DESC=20LIMIT=2010;=0A-=20bt_index_check=20|=20=20= =20=20=20=20=20=20=20=20=20=20=20relname=20=20=20=20=20=20=20=20=20=20=20= =20=20|=20relpages=20=0A+=20bt_index_check=20|=20=20=20=20=20=20=20=20=20= =20=20=20=20relname=20=20=20=20=20=20=20=20=20=20=20=20=20|=20relpages=0A= =20----------------+---------------------------------+----------=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20= pg_depend_reference_index=20=20=20=20=20=20=20|=20=20=20=20=20=20=2043=0A= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20= pg_depend_depender_index=20=20=20=20=20=20=20=20|=20=20=20=20=20=20=2040=0A= @@=20-165,6=20+165,110=20@@=20ORDER=20BY=20c.relpages=20DESC=20LIMIT=20= 10;=0A=20=20=20=20=20=20=0A=20=20=20=20=20=0A=20=20=20=20= =0A+=0A+=20=20=20=0A+=20=20=20=20=0A+=20= =20=20=20=20=0A+=20=20=20=20=20=20verify_heapam(relation=20= regclass,=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= on_error_stop=20boolean,=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20skip_all_frozen=20boolean,=0A+=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20skip_all_visible=20boolean,=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20blkno=20OUT=20bigint,=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20offnum=20= OUT=20integer,=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20lp_off=20OUT=20smallint,=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20lp_flags=20OUT=20smallint,=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20lp_len=20OUT=20smallint,=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20attnum=20OUT=20integer,=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20chunk=20OUT=20= integer,=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= msg=20OUT=20text)=0A+=20=20=20=20=20=20returns=20record=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=20=20=20=20=0A+=20=20=20= =20=20=0A+=20=20=20=20=20=20Checks=20for=20"logical"=20corruption,=20= where=20the=20page=20is=20valid=20but=20inconsistent=0A+=20=20=20=20=20=20= with=20the=20rest=20of=20the=20database=20cluster.=20This=20can=20happen=20= due=20to=20faulty=20or=0A+=20=20=20=20=20=20ill-conceived=20backup=20and=20= restore=20tools,=20or=20bad=20storage,=20or=20user=20error,=20or=0A+=20=20= =20=20=20=20bugs=20in=20the=20server=20itself.=20=20It=20checks=20xmin=20= and=20xmax=20values=20against=0A+=20=20=20=20=20=20relfrozenxid=20and=20= relminmxid,=20and=20also=20validates=20TOAST=20pointers.=0A+=20=20=20=20=20= =0A+=0A+=20=20=20=20=20=0A+=20=20=20=20=20=20For=20each=20= block=20in=20the=20relation=20where=20corruption=20is=20detected,=20or=20= for=20just=0A+=20=20=20=20=20=20the=20first=20block=20if=20on_error_stop=20= is=20true,=20for=20each=20corruption=20detected,=0A+=20=20=20=20=20=20= returns=20one=20row=20containing=20the=20following=20fields:=0A+=20=20=20= =20=20=0A+=20=20=20=20=20=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20blkno=0A+=20=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=20=20The=20number=20of=20the=20block=20containing=20the=20corrupt=20= page.=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20offnum=0A+=20=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=20=20The=20OffsetNumber=20of=20the=20corrupt=20tuple.=0A+=20=20=20=20= =20=20=20=20=0A+=20=20=20=20=20=20=20=0A+=20=20=20=20=20= =20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =20=20lp_off=0A+=20=20=20=20=20=20=20=0A+=20=20=20= =20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20The=20offset=20into=20= the=20page=20of=20the=20line=20pointer=20for=20the=20corrupt=20tuple.=0A= +=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=0A+=20= =20=20=20=20=20=0A+=20=20=20=20=20=20=0A+=20= =20=20=20=20=20=20lp_flags=0A+=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20= The=20flags=20in=20the=20line=20pointer=20for=20the=20corrupt=20tuple.=0A= +=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=0A+=20= =20=20=20=20=20=0A+=20=20=20=20=20=20=0A+=20= =20=20=20=20=20=20lp_len=0A+=20=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20The=20= length=20of=20the=20corrupt=20tuple=20as=20recorded=20in=20the=20line=20= pointer.=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20attnum=0A+=20=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=20=20The=20attribute=20number=20of=20the=20corrupt=20column=20in=20= the=20tuple,=20if=20the=0A+=20=20=20=20=20=20=20=20=20corruption=20is=20= specific=20to=20a=20column=20and=20not=20the=20tuple=20as=20a=20whole.=0A= +=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=0A+=20= =20=20=20=20=20=0A+=20=20=20=20=20=20=0A+=20= =20=20=20=20=20=20chunk=0A+=20=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20=20=20The=20= chunk=20number=20of=20the=20corrupt=20toasted=20attribute,=20if=20the=20= corruption=0A+=20=20=20=20=20=20=20=20=20is=20specific=20to=20a=20= toasted=20value.=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20msg=0A+=20=20=20=20=20= =20=20=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= =20=20A=20human=20readable=20message=20describing=20the=20corruption=20= in=20the=20page.=0A+=20=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20= =20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=20=20=20=0A= +=0A=20=20=20=0A=20=20=20=0A=20=20=20=20=0A= diff=20--git=20a/src/backend/access/heap/hio.c=20= b/src/backend/access/heap/hio.c=0Aindex=20aa3f14c019..00de10b7c9=20= 100644=0A---=20a/src/backend/access/heap/hio.c=0A+++=20= b/src/backend/access/heap/hio.c=0A@@=20-47,6=20+47,17=20@@=20= RelationPutHeapTuple(Relation=20relation,=0A=20=09=20*/=0A=20=09= Assert(!token=20||=20HeapTupleHeaderIsSpeculative(tuple->t_data));=0A=20=0A= +=09/*=0A+=09=20*=20Do=20not=20allow=20tuples=20with=20invalid=20= combinations=20of=20hint=20bits=20to=20be=20placed=0A+=09=20*=20on=20a=20= page.=20=20These=20combinations=20are=20detected=20as=20corruption=20by=20= the=0A+=09=20*=20contrib/amcheck=20logic,=20so=20if=20you=20decide=20to=20= disable=20one=20or=20more=20of=20these=0A+=09=20*=20assertions,=20make=20= corresponding=20changes=20to=20contrib/amcheck.=0A+=09=20*/=0A+=09= Assert(!((tuple->t_data->t_infomask=20&=20HEAP_XMAX_LOCK_ONLY)=20&&=0A+=09= =09=09=20(tuple->t_data->t_infomask2=20&=20HEAP_KEYS_UPDATED)));=0A+=09= Assert(!((tuple->t_data->t_infomask=20&=20HEAP_XMAX_COMMITTED)=20&&=0A+=09= =09=09=20(tuple->t_data->t_infomask=20&=20HEAP_XMAX_IS_MULTI)));=0A+=0A=20= =09/*=20Add=20the=20tuple=20to=20the=20page=20*/=0A=20=09pageHeader=20=3D=20= BufferGetPage(buffer);=0A=20=0Adiff=20--git=20= a/src/tools/pgindent/typedefs.list=20b/src/tools/pgindent/typedefs.list=0A= index=20467120f1d0..c58d025902=20100644=0A---=20= a/src/tools/pgindent/typedefs.list=0A+++=20= b/src/tools/pgindent/typedefs.list=0A@@=20-1018,6=20+1018,7=20@@=20= HbaToken=0A=20HeadlineJsonState=0A=20HeadlineParsedText=0A=20= HeadlineWordEntry=0A+HeapCheckContext=0A=20HeapScanDesc=0A=20HeapTuple=0A= =20HeapTupleData=0A@@=20-3321,6=20+3322,7=20@@=20sigjmp_buf=0A=20= signedbitmapword=0A=20sigset_t=0A=20size_t=0A+SkipPages=0A=20slist_head=0A= =20slist_iter=0A=20slist_mutable_iter=0A--=20=0A2.21.1=20(Apple=20= Git-122.3)=0A=0A= --Apple-Mail=_268BCDFB-5F95-4F65-8E72-69AFC6227897 Content-Disposition: attachment; filename=v13-0003-Adding-contrib-module-pg_amcheck.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v13-0003-Adding-contrib-module-pg_amcheck.patch" Content-Transfer-Encoding: quoted-printable =46rom=202cc3a67a3faa87625268796a4e903312556f0409=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Mark=20Dilger=20=0A= Date:=20Mon,=2027=20Jul=202020=2008:04:49=20-0700=0ASubject:=20[PATCH=20= v13=203/3]=20Adding=20contrib=20module=20pg_amcheck=0A=0AAdding=20new=20= contrib=20module=20pg_amcheck,=20which=20is=20a=20command=20line=0A= interface=20for=20running=20amcheck's=20verifications=20against=20tables=20= and=0Aindexes.=0A---=0A=20contrib/Makefile=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=201=20+=0A=20= contrib/pg_amcheck/.gitignore=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20= =203=20+=0A=20contrib/pg_amcheck/Makefile=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20|=20=2028=20+=0A=20contrib/pg_amcheck/pg_amcheck.c=20=20=20=20= =20=20=20=20=20=20=20|=20900=20++++++++++++++++++++++=0A=20= contrib/pg_amcheck/t/001_basic.pl=20=20=20=20=20=20=20=20=20|=20=20=209=20= +=0A=20contrib/pg_amcheck/t/002_nonesuch.pl=20=20=20=20=20=20|=20=2055=20= ++=0A=20contrib/pg_amcheck/t/003_check.pl=20=20=20=20=20=20=20=20=20|=20=20= 85=20++=0A=20contrib/pg_amcheck/t/004_verify_heapam.pl=20|=20434=20= +++++++++++=0A=20doc/src/sgml/contrib.sgml=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=20=201=20+=0A=20doc/src/sgml/filelist.sgml=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=201=20+=0A=20= doc/src/sgml/pg_amcheck.sgml=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20= 136=20++++=0A=20src/tools/pgindent/typedefs.list=20=20=20=20=20=20=20=20=20= =20|=20=20=202=20+=0A=2012=20files=20changed,=201655=20insertions(+)=0A=20= create=20mode=20100644=20contrib/pg_amcheck/.gitignore=0A=20create=20= mode=20100644=20contrib/pg_amcheck/Makefile=0A=20create=20mode=20100644=20= contrib/pg_amcheck/pg_amcheck.c=0A=20create=20mode=20100644=20= contrib/pg_amcheck/t/001_basic.pl=0A=20create=20mode=20100644=20= contrib/pg_amcheck/t/002_nonesuch.pl=0A=20create=20mode=20100644=20= contrib/pg_amcheck/t/003_check.pl=0A=20create=20mode=20100644=20= contrib/pg_amcheck/t/004_verify_heapam.pl=0A=20create=20mode=20100644=20= doc/src/sgml/pg_amcheck.sgml=0A=0Adiff=20--git=20a/contrib/Makefile=20= b/contrib/Makefile=0Aindex=201846d415b6..c21c27cbeb=20100644=0A---=20= a/contrib/Makefile=0A+++=20b/contrib/Makefile=0A@@=20-29,6=20+29,7=20@@=20= SUBDIRS=20=3D=20\=0A=20=09=09oid2name=09\=0A=20=09=09pageinspect=09\=0A=20= =09=09passwordcheck=09\=0A+=09=09pg_amcheck=09=09\=0A=20=09=09= pg_buffercache=09\=0A=20=09=09pg_freespacemap=20\=0A=20=09=09pg_prewarm=09= \=0Adiff=20--git=20a/contrib/pg_amcheck/.gitignore=20= b/contrib/pg_amcheck/.gitignore=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..07ad380105=0A---=20/dev/null=0A+++=20= b/contrib/pg_amcheck/.gitignore=0A@@=20-0,0=20+1,3=20@@=0A+/pg_amcheck=0A= +=0A+/tmp_check/=0Adiff=20--git=20a/contrib/pg_amcheck/Makefile=20= b/contrib/pg_amcheck/Makefile=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..74554b9e8d=0A---=20/dev/null=0A+++=20= b/contrib/pg_amcheck/Makefile=0A@@=20-0,0=20+1,28=20@@=0A+#=20= contrib/pg_amcheck/Makefile=0A+=0A+PGFILEDESC=20=3D=20"pg_amcheck=20-=20= detects=20corruption=20within=20database=20relations"=0A+PGAPPICON=20=3D=20= win32=0A+=0A+PROGRAM=20=3D=20pg_amcheck=0A+OBJS=20=3D=20\=0A+=09= $(WIN32RES)=20\=0A+=09pg_amcheck.o=0A+=0A+REGRESS_OPTS=20+=3D=20= --load-extension=3Damcheck=20--load-extension=3Dpageinspect=0A= +EXTRA_INSTALL=20+=3D=20contrib/amcheck=20contrib/pageinspect=0A+=0A= +TAP_TESTS=20=3D=201=0A+=0A+PG_CPPFLAGS=20=3D=20-I$(libpq_srcdir)=0A= +PG_LIBS_INTERNAL=20=3D=20-L$(top_builddir)/src/fe_utils=20-lpgfeutils=20= $(libpq_pgport)=0A+=0A+ifdef=20USE_PGXS=0A+PG_CONFIG=20=3D=20pg_config=0A= +PGXS=20:=3D=20$(shell=20$(PG_CONFIG)=20--pgxs)=0A+include=20$(PGXS)=0A= +else=0A+subdir=20=3D=20contrib/pg_amcheck=0A+top_builddir=20=3D=20../..=0A= +include=20$(top_builddir)/src/Makefile.global=0A+include=20= $(top_srcdir)/contrib/contrib-global.mk=0A+endif=0Adiff=20--git=20= a/contrib/pg_amcheck/pg_amcheck.c=20b/contrib/pg_amcheck/pg_amcheck.c=0A= new=20file=20mode=20100644=0Aindex=200000000000..2bd98076eb=0A---=20= /dev/null=0A+++=20b/contrib/pg_amcheck/pg_amcheck.c=0A@@=20-0,0=20+1,900=20= @@=0A= +/*-----------------------------------------------------------------------= --=0A+=20*=0A+=20*=20pg_amcheck.c=0A+=20*=09=09Detects=20corruption=20= within=20database=20relations.=0A+=20*=0A+=20*=20Copyright=20(c)=20= 2017-2020,=20PostgreSQL=20Global=20Development=20Group=0A+=20*=0A+=20*=20= IDENTIFICATION=0A+=20*=09=20=20contrib/pg_amcheck/pg_amcheck.c=0A+=20*=0A= +=20= *-------------------------------------------------------------------------= =0A+=20*/=0A+#include=20"postgres_fe.h"=0A+=0A+#include=20= "catalog/pg_am.h"=0A+#include=20"catalog/pg_class.h"=0A+#include=20= "common/logging.h"=0A+#include=20"common/username.h"=0A+#include=20= "fe_utils/connect.h"=0A+#include=20"fe_utils/print.h"=0A+#include=20= "fe_utils/simple_list.h"=0A+#include=20"fe_utils/string_utils.h"=0A= +#include=20"pg_getopt.h"=0A+=0A+const=20char=20*usage_text[]=20=3D=20{=0A= +=09"pg_amcheck=20is=20the=20PostgreSQL=20command=20line=20database=20= corruption=20checker.",=0A+=09"",=0A+=09"Usage:",=0A+=09"=20=20= pg_amcheck=20[OPTION]...=20[DBNAME=20[USERNAME]]",=0A+=09"",=0A+=09= "General=20options:",=0A+=09"=20=20-V,=20--version=20=20=20=20=20=20=20=20= =20=20=20=20output=20version=20information,=20then=20exit",=0A+=09"=20=20= -?,=20--help=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20show=20this=20= help,=20then=20exit",=0A+=09"=20=20-s,=20--schema=3DPATTERN=20=20=20=20=20= check=20all=20relations=20in=20the=20specified=20schema(s)",=0A+=09"=20=20= -N,=20--exclude-schema=3DPATTERN=20do=20NOT=20check=20relations=20in=20= the=20specified=20"=0A+=09"schema(s)",=0A+=09"=20=20-t,=20= --table=3DPATTERN=20=20=20=20=20=20check=20the=20specified=20table(s)=20= only",=0A+=09"=20=20-T,=20--exclude-table=3DPATTERN=20=20=20=20do=20NOT=20= check=20the=20specified=20table(s)",=0A+=09"=20=20-i,=20--check-indexes=20= =20=20=20=20=20check=20associated=20btree=20indexes,=20if=20any",=0A+=09= "=20=20-I,=20--exclude-indexes=20=20=20=20do=20NOT=20check=20associated=20= btree=20indexes",=0A+=09"=20=20--strict-names=20=20=20=20=20=20=20=20=20=20= =20require=20table=20and/or=20schema=20include=20patterns=20"=0A+=09"to=20= match=20at=20least=20one=20entity=20each",=0A+=09"=20=20-b,=20= --startblock=20=20=20=20=20=20=20=20=20check=20relations=20beginning=20= at=20the=20given=20"=0A+=09"starting=20block=20number",=0A+=09"=20=20-e,=20= --endblock=20=20=20=20=20=20=20=20=20=20=20check=20relations=20only=20up=20= to=20the=20given=20ending=20"=0A+=09"block=20number",=0A+=09"=20=20-f,=20= --skip-all-frozen=20=20=20=20do=20not=20check=20blocks=20marked=20as=20= all=20frozen",=0A+=09"=20=20-v,=20--skip-all-visible=20=20=20do=20not=20= check=20blocks=20marked=20as=20all=20visible",=0A+=09"",=0A+=09= "Connection=20options:",=0A+=09"=20=20-d,=20--dbname=3DDBNAME=20=20=20=20= =20=20database=20name=20to=20connect=20to",=0A+=09"=20=20-h,=20= --host=3DHOSTNAME=20=20=20=20=20=20database=20server=20host=20or=20= socket=20directory",=0A+=09"=20=20-p,=20--port=3DPORT=20=20=20=20=20=20=20= =20=20=20database=20server=20port",=0A+=09"=20=20-U,=20= --username=3DUSERNAME=20=20database=20user=20name",=0A+=09"=20=20-w,=20= --no-password=20=20=20=20=20=20=20=20never=20prompt=20for=20password",=0A= +=09"=20=20-W,=20--password=20=20=20=20=20=20=20=20=20=20=20force=20= password=20prompt=20(should=20happen=20"=0A+=09"automatically)",=0A+=09= "",=0A+=09NULL=09=09=09=09=09=09/*=20sentinel=20*/=0A+};=0A+=0A+typedef=20= struct=0A+{=0A+=09char=09=20=20=20*dbname;=0A+=09char=09=20=20=20*host;=0A= +=09char=09=20=20=20*port;=0A+=09char=09=20=20=20*username;=0A+}=20= ConnectOptions;=0A+=0A+typedef=20enum=20trivalue=0A+{=0A+=09TRI_DEFAULT,=0A= +=09TRI_NO,=0A+=09TRI_YES=0A+}=20trivalue;=0A+=0A+typedef=20struct=0A+{=0A= +=09PGconn=09=20=20=20*db;=09=09=09=09/*=20connection=20to=20backend=20= */=0A+=09bool=09=09notty;=09=09=09/*=20stdin=20or=20stdout=20is=20not=20= a=20tty=20(as=20determined=0A+=09=09=09=09=09=09=09=09=20*=20on=20= startup)=20*/=0A+=09trivalue=09getPassword;=09/*=20prompt=20for=20a=20= username=20and=20password=20*/=0A+=09const=20char=20*progname;=09=09/*=20= in=20case=20you=20renamed=20pg_amcheck=20*/=0A+=09bool=09=09= strict_names;=09/*=20The=20specified=20names/patterns=20should=20to=0A+=09= =09=09=09=09=09=09=09=20*=20match=20at=20least=20one=20entity=20*/=0A+=09= bool=09=09on_error_stop;=09/*=20The=20checking=20of=20each=20table=20= should=20stop=0A+=09=09=09=09=09=09=09=09=20*=20after=20the=20first=20= corrupt=20page=20is=20found.=20*/=0A+=09bool=09=09skip_frozen;=09/*=20Do=20= not=20check=20pages=20marked=20all=20frozen=20*/=0A+=09bool=09=09= skip_visible;=09/*=20Do=20not=20check=20pages=20marked=20all=20visible=20= */=0A+=09bool=09=09check_indexes;=09/*=20Check=20btree=20indexes=20for=20= tables=20*/=0A+=09char=09=20=20=20*startblock;=09=09/*=20Block=20number=20= where=20checking=20begins=20*/=0A+=09char=09=20=20=20*endblock;=09=09/*=20= Block=20number=20where=20checking=20ends=20*/=0A+}=20AmCheckSettings;=0A= +=0A+static=20AmCheckSettings=20settings;=0A+=0A+/*=0A+=20*=20Object=20= inclusion/exclusion=20lists=0A+=20*=0A+=20*=20The=20string=20lists=20= record=20the=20patterns=20given=20by=20command-line=20switches,=0A+=20*=20= which=20we=20then=20convert=20to=20lists=20of=20OIDs=20of=20matching=20= objects.=0A+=20*/=0A+static=20SimpleStringList=20schema_include_patterns=20= =3D=20{NULL,=20NULL};=0A+static=20SimpleOidList=20schema_include_oids=20= =3D=20{NULL,=20NULL};=0A+static=20SimpleStringList=20= schema_exclude_patterns=20=3D=20{NULL,=20NULL};=0A+static=20= SimpleOidList=20schema_exclude_oids=20=3D=20{NULL,=20NULL};=0A+=0A= +static=20SimpleStringList=20table_include_patterns=20=3D=20{NULL,=20= NULL};=0A+static=20SimpleOidList=20table_include_oids=20=3D=20{NULL,=20= NULL};=0A+static=20SimpleStringList=20table_exclude_patterns=20=3D=20= {NULL,=20NULL};=0A+static=20SimpleOidList=20table_exclude_oids=20=3D=20= {NULL,=20NULL};=0A+=0A+/*=0A+=20*=20List=20of=20tables=20to=20be=20= checked,=20compiled=20from=20above=20lists.=0A+=20*/=0A+static=20= SimpleOidList=20checklist=20=3D=20{NULL,=20NULL};=0A+=0A+=0A+static=20= void=20check_tables(SimpleOidList=20*checklist);=0A+static=20void=20= check_table(Oid=20tbloid);=0A+static=20void=20check_indexes(Oid=20= tbloid);=0A+static=20void=20check_index(Oid=20tbloid,=20Oid=20idxoid);=0A= +=0A+static=20void=20parse_cli_options(int=20argc,=20char=20*argv[],=0A+=09= =09=09=09=09=09=09=20=20ConnectOptions=20*connOpts);=0A+static=20void=20= usage(void);=0A+static=20void=20showVersion(void);=0A+=0A+static=20void=20= NoticeProcessor(void=20*arg,=20const=20char=20*message);=0A+=0A+static=20= void=20expand_schema_name_patterns(SimpleStringList=20*patterns,=0A+=09=09= =09=09=09=09=09=09=09=09SimpleOidList=20*oids,=0A+=09=09=09=09=09=09=09=09= =09=09bool=20strict_names);=0A+static=20void=20= expand_table_name_patterns(SimpleStringList=20*patterns,=0A+=09=09=09=09=09= =09=09=09=09=20=20=20SimpleOidList=20*oids,=0A+=09=09=09=09=09=09=09=09=09= =20=20=20bool=20strict_names);=0A+static=20void=20= get_table_check_list(SimpleOidList=20*include_nsp,=0A+=09=09=09=09=09=09=09= =09=20SimpleOidList=20*exclude_nsp,=0A+=09=09=09=09=09=09=09=09=20= SimpleOidList=20*include_tbl,=0A+=09=09=09=09=09=09=09=09=20= SimpleOidList=20*exclude_tbl,=0A+=09=09=09=09=09=09=09=09=20= SimpleOidList=20*checklist);=0A+=0A+static=20void=20= die_on_query_failure(const=20char=20*query);=0A+static=20void=20= ExecuteSqlStatement(const=20char=20*query);=0A+static=20PGresult=20= *ExecuteSqlQuery(const=20char=20*query,=20ExecStatusType=20status);=0A= +static=20PGresult=20*ExecuteSqlQueryForSingleRow(const=20char=20= *query);=0A+=0A+#define=20fatal(...)=20do=20{=20= pg_log_error(__VA_ARGS__);=20exit(1);=20}=20while(0)=0A+=0A+#define=20= NOPAGER=09=090=0A+#define=20EXIT_BADCONN=202=0A+=0A+int=0A+main(int=20= argc,=20char=20**argv)=0A+{=0A+=09ConnectOptions=20connOpts;=0A+=09bool=09= =09have_password=20=3D=20false;=0A+=09char=09=09password[100];=0A+=09= bool=09=09new_pass;=0A+=0A+=09pg_logging_init(argv[0]);=0A+=09= set_pglocale_pgservice(argv[0],=20PG_TEXTDOMAIN("pg_amcheck"));=0A+=0A+=09= if=20(argc=20>=201)=0A+=09{=0A+=09=09if=20((strcmp(argv[1],=20"-?")=20=3D=3D= =200)=20||=0A+=09=09=09(argc=20=3D=3D=202=20&&=20(strcmp(argv[1],=20= "--help")=20=3D=3D=200)))=0A+=09=09{=0A+=09=09=09usage();=0A+=09=09=09= exit(EXIT_SUCCESS);=0A+=09=09}=0A+=09=09if=20(strcmp(argv[1],=20= "--version")=20=3D=3D=200=20||=20strcmp(argv[1],=20"-V")=20=3D=3D=200)=0A= +=09=09{=0A+=09=09=09showVersion();=0A+=09=09=09exit(EXIT_SUCCESS);=0A+=09= =09}=0A+=09}=0A+=0A+=09memset(&settings,=200,=20sizeof(settings));=0A+=09= settings.progname=20=3D=20get_progname(argv[0]);=0A+=0A+=09settings.db=20= =3D=20NULL;=0A+=09setDecimalLocale();=0A+=0A+=09settings.notty=20=3D=20= (!isatty(fileno(stdin))=20||=20!isatty(fileno(stdout)));=0A+=0A+=09= settings.getPassword=20=3D=20TRI_DEFAULT;=0A+=0A+=09/*=20Default=20= behaviors=20*/=0A+=09settings.on_error_stop=20=3D=20false;=0A+=09= settings.skip_frozen=20=3D=20false;=0A+=09settings.skip_visible=20=3D=20= false;=0A+=09settings.check_indexes=20=3D=20true;=0A+=0A+=09= parse_cli_options(argc,=20argv,=20&connOpts);=0A+=0A+=09if=20= (settings.getPassword=20=3D=3D=20TRI_YES)=0A+=09{=0A+=09=09/*=0A+=09=09=20= *=20We=20can't=20be=20sure=20yet=20of=20the=20username=20that=20will=20= be=20used,=20so=20don't=0A+=09=09=20*=20offer=20a=20potentially=20wrong=20= one.=20=20Typical=20uses=20of=20this=20option=20are=0A+=09=09=20*=20= noninteractive=20anyway.=0A+=09=09=20*/=0A+=09=09= simple_prompt("Password:=20",=20password,=20sizeof(password),=20false);=0A= +=09=09have_password=20=3D=20true;=0A+=09}=0A+=0A+=09/*=20loop=20until=20= we=20have=20a=20password=20if=20requested=20by=20backend=20*/=0A+=09do=0A= +=09{=0A+#define=20ARRAY_SIZE=098=0A+=09=09const=20char=20**keywords=20=3D= =20pg_malloc(ARRAY_SIZE=20*=20sizeof(*keywords));=0A+=09=09const=20char=20= **values=20=3D=20pg_malloc(ARRAY_SIZE=20*=20sizeof(*values));=0A+=0A+=09=09= keywords[0]=20=3D=20"host";=0A+=09=09values[0]=20=3D=20connOpts.host;=0A= +=09=09keywords[1]=20=3D=20"port";=0A+=09=09values[1]=20=3D=20= connOpts.port;=0A+=09=09keywords[2]=20=3D=20"user";=0A+=09=09values[2]=20= =3D=20connOpts.username;=0A+=09=09keywords[3]=20=3D=20"password";=0A+=09=09= values[3]=20=3D=20have_password=20?=20password=20:=20NULL;=0A+=09=09= keywords[4]=20=3D=20"dbname";=20/*=20see=20do_connect()=20*/=0A+=09=09if=20= (connOpts.dbname=20=3D=3D=20NULL)=0A+=09=09{=0A+=09=09=09if=20= (getenv("PGDATABASE"))=0A+=09=09=09=09values[4]=20=3D=20= getenv("PGDATABASE");=0A+=09=09=09else=20if=20(getenv("PGUSER"))=0A+=09=09= =09=09values[4]=20=3D=20getenv("PGUSER");=0A+=09=09=09else=0A+=09=09=09=09= values[4]=20=3D=20"postgres";=0A+=09=09}=0A+=09=09else=0A+=09=09=09= values[4]=20=3D=20connOpts.dbname;=0A+=09=09keywords[5]=20=3D=20= "fallback_application_name";=0A+=09=09values[5]=20=3D=20= settings.progname;=0A+=09=09keywords[6]=20=3D=20"client_encoding";=0A+=09= =09values[6]=20=3D=20(settings.notty=20||=0A+=09=09=09=09=09=20= getenv("PGCLIENTENCODING"))=20?=20NULL=20:=20"auto";=0A+=09=09= keywords[7]=20=3D=20NULL;=0A+=09=09values[7]=20=3D=20NULL;=0A+=0A+=09=09= new_pass=20=3D=20false;=0A+=09=09settings.db=20=3D=20= PQconnectdbParams(keywords,=20values,=20true);=0A+=09=09if=20= (settings.db=20=3D=3D=20NULL)=0A+=09=09{=0A+=09=09=09pg_log_error("no=20= connection=20to=20server=20after=20initial=20attempt");=0A+=09=09=09= exit(EXIT_BADCONN);=0A+=09=09}=0A+=0A+=09=09free(keywords);=0A+=09=09= free(values);=0A+=0A+=09=09if=20(PQstatus(settings.db)=20=3D=3D=20= CONNECTION_BAD=20&&=0A+=09=09=09PQconnectionNeedsPassword(settings.db)=20= &&=0A+=09=09=09!have_password=20&&=0A+=09=09=09settings.getPassword=20!=3D= =20TRI_NO)=0A+=09=09{=0A+=09=09=09/*=0A+=09=09=09=20*=20Before=20closing=20= the=20old=20PGconn,=20extract=20the=20user=20name=20that=20was=0A+=09=09=09= =20*=20actually=20connected=20with.=0A+=09=09=09=20*/=0A+=09=09=09const=20= char=20*realusername=20=3D=20PQuser(settings.db);=0A+=09=09=09char=09=20=20= =20*password_prompt;=0A+=0A+=09=09=09if=20(realusername=20&&=20= realusername[0])=0A+=09=09=09=09password_prompt=20=3D=20= psprintf(_("Password=20for=20user=20%s:=20"),=0A+=09=09=09=09=09=09=09=09= =09=09=20=20=20realusername);=0A+=09=09=09else=0A+=09=09=09=09= password_prompt=20=3D=20pg_strdup(_("Password:=20"));=0A+=09=09=09= PQfinish(settings.db);=0A+=0A+=09=09=09simple_prompt(password_prompt,=20= password,=20sizeof(password),=20false);=0A+=09=09=09= free(password_prompt);=0A+=09=09=09have_password=20=3D=20true;=0A+=09=09=09= new_pass=20=3D=20true;=0A+=09=09}=0A+=09}=20while=20(new_pass);=0A+=0A+=09= if=20(!settings.db)=0A+=09{=0A+=09=09pg_log_error("no=20connection=20to=20= server");=0A+=09=09exit(EXIT_BADCONN);=0A+=09}=0A+=0A+=09if=20= (PQstatus(settings.db)=20=3D=3D=20CONNECTION_BAD)=0A+=09{=0A+=09=09= pg_log_error("could=20not=20connect=20to=20server:=20%s",=0A+=09=09=09=09= =09=20PQerrorMessage(settings.db));=0A+=09=09PQfinish(settings.db);=0A+=09= =09exit(EXIT_BADCONN);=0A+=09}=0A+=0A+=09/*=20Expand=20schema=20= selection=20patterns=20into=20OID=20lists=20*/=0A+=09if=20= (schema_include_patterns.head=20!=3D=20NULL)=0A+=09{=0A+=09=09= expand_schema_name_patterns(&schema_include_patterns,=0A+=09=09=09=09=09=09= =09=09=09&schema_include_oids,=0A+=09=09=09=09=09=09=09=09=09= settings.strict_names);=0A+=09=09if=20(schema_include_oids.head=20=3D=3D=20= NULL)=0A+=09=09=09fatal("no=20matching=20schemas=20were=20found");=0A+=09= }=0A+=09expand_schema_name_patterns(&schema_exclude_patterns,=0A+=09=09=09= =09=09=09=09=09&schema_exclude_oids,=0A+=09=09=09=09=09=09=09=09false);=0A= +=09/*=20non-matching=20exclusion=20patterns=20aren't=20an=20error=20*/=0A= +=0A+=09/*=20Expand=20table=20selection=20patterns=20into=20OID=20lists=20= */=0A+=09if=20(table_include_patterns.head=20!=3D=20NULL)=0A+=09{=0A+=09=09= expand_table_name_patterns(&table_include_patterns,=0A+=09=09=09=09=09=09= =09=09=20=20=20&table_include_oids,=0A+=09=09=09=09=09=09=09=09=20=20=20= settings.strict_names);=0A+=09=09if=20(table_include_oids.head=20=3D=3D=20= NULL)=0A+=09=09=09fatal("no=20matching=20tables=20were=20found");=0A+=09= }=0A+=09expand_table_name_patterns(&table_exclude_patterns,=0A+=09=09=09=09= =09=09=09=20=20=20&table_exclude_oids,=0A+=09=09=09=09=09=09=09=20=20=20= false);=0A+=0A+=09/*=0A+=09=20*=20Compile=20list=20of=20all=20tables=20= to=20be=20checked=20based=20on=20namespace=20and=20table=0A+=09=20*=20= includes=20and=20excludes.=0A+=09=20*/=0A+=09= get_table_check_list(&schema_include_oids,=20&schema_exclude_oids,=0A+=09= =09=09=09=09=09=20&table_include_oids,=20&table_exclude_oids,=20= &checklist);=0A+=0A+=09PQsetNoticeProcessor(settings.db,=20= NoticeProcessor,=20NULL);=0A+=0A+=09check_tables(&checklist);=0A+=0A+=09= return=200;=0A+}=0A+=0A+static=20void=0A+check_tables(SimpleOidList=20= *checklist)=0A+{=0A+=09const=20SimpleOidListCell=20*cell;=0A+=0A+=09for=20= (cell=20=3D=20checklist->head;=20cell;=20cell=20=3D=20cell->next)=0A+=09= {=0A+=09=09check_table(cell->val);=0A+=09=09if=20= (settings.check_indexes)=0A+=09=09=09check_indexes(cell->val);=0A+=09}=0A= +}=0A+=0A+static=20void=0A+check_table(Oid=20tbloid)=0A+{=0A+=09= PQExpBuffer=20query;=0A+=09PGresult=20=20=20*res;=0A+=09int=09=09=09i;=0A= +=09char=09=20=20=20*skip;=0A+=09const=20char=20*stop;=0A+=0A+=09if=20= (settings.db=20=3D=3D=20NULL)=0A+=09=09fatal("no=20connection=20on=20= entry=20to=20expand_table_name_patterns");=0A+=0A+=09if=20= (settings.startblock=20=3D=3D=20NULL)=0A+=09=09settings.startblock=20=3D=20= pg_strdup("NULL");=0A+=09if=20(settings.endblock=20=3D=3D=20NULL)=0A+=09=09= settings.endblock=20=3D=20pg_strdup("NULL");=0A+=09if=20= (settings.skip_frozen)=0A+=09=09skip=20=3D=20pg_strdup("'all=20= frozen'");=0A+=09else=20if=20(settings.skip_visible)=0A+=09=09skip=20=3D=20= pg_strdup("'all=20visible'");=0A+=09else=0A+=09=09skip=20=3D=20= pg_strdup("NULL");=0A+=09stop=20=3D=20(settings.on_error_stop)=20?=20= "true"=20:=20"false";=0A+=0A+=09query=20=3D=20createPQExpBuffer();=0A+=0A= +=09appendPQExpBuffer(query,=0A+=09=09=09=09=09=20=20"SELECT=20= c.relname,=20v.blkno,=20v.offnum,=20v.lp_off,=20"=0A+=09=09=09=09=09=20=20= "v.lp_flags,=20v.lp_len,=20v.attnum,=20v.chunk,=20v.msg"=0A+=09=09=09=09=09= =20=20"\nFROM=20verify_heapam(rel=20:=3D=20%u,=20on_error_stop=20:=3D=20= %s,=20"=0A+=09=09=09=09=09=20=20"skip=20:=3D=20%s,=20startblock=20:=3D=20= %s,=20endblock=20:=3D=20%s)=20v,=20"=0A+=09=09=09=09=09=20=20"pg_class=20= c"=0A+=09=09=09=09=09=20=20"\nWHERE=20c.oid=20=3D=20%u",=0A+=09=09=09=09=09= =20=20tbloid,=20stop,=20skip,=20settings.startblock,=0A+=09=09=09=09=09=20= =20settings.endblock,=20tbloid);=0A+=0A+=09ExecuteSqlStatement("RESET=20= search_path");=0A+=09res=20=3D=20ExecuteSqlQuery(query->data,=20= PGRES_TUPLES_OK);=0A+=09= PQclear(ExecuteSqlQueryForSingleRow(ALWAYS_SECURE_SEARCH_PATH_SQL));=0A+=0A= +=09if=20(PQntuples(res)=20>=200)=0A+=09{=0A+=09=09int=09=09=09lines=20=3D= =20PQntuples(res)=20*=202;=0A+=09=09FILE=09=20=20=20*output=20=3D=20= PageOutput(lines,=20NULL);=0A+=0A+=09=09for=20(i=20=3D=200;=20i=20<=20= PQntuples(res);=20i++)=0A+=09=09{=0A+=09=09=09fprintf(output,=0A+=09=09=09= =09=09"(relname=3D%s,blkno=3D%s,offnum=3D%s,lp_off=3D%s,lp_flags=3D%s,"=0A= +=09=09=09=09=09"lp_len=3D%s,attnum=3D%s,chunk=3D%s)\n%s\n",=0A+=09=09=09= =09=09PQgetvalue(res,=20i,=200),=09/*=20relname=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=201),=09/*=20blkno=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=202),=09/*=20offnum=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=203),=09/*=20lp_off=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=204),=09/*=20lp_flags=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=205),=09/*=20lp_len=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=206),=09/*=20attnum=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=207),=09/*=20chunk=20*/=0A+=09=09=09=09=09= PQgetvalue(res,=20i,=208));=20/*=20msg=20*/=0A+=09=09}=0A+=09}=0A+=0A+=09= PQclear(res);=0A+=09resetPQExpBuffer(query);=0A+=09= destroyPQExpBuffer(query);=0A+}=0A+=0A+static=20void=0A= +check_indexes(Oid=20tbloid)=0A+{=0A+=09PQExpBuffer=20query;=0A+=09= PGresult=20=20=20*res;=0A+=09int=09=09=09i;=0A+=0A+=09query=20=3D=20= createPQExpBuffer();=0A+=09appendPQExpBuffer(query,=0A+=09=09=09=09=09=20= =20"SELECT=20i.indexrelid"=0A+=09=09=09=09=09=20=20"\nFROM=20= pg_catalog.pg_index=20i,=20pg_catalog.pg_class=20c"=0A+=09=09=09=09=09=20= =20"\nWHERE=20i.indexrelid=20=3D=20c.oid"=0A+=09=09=09=09=09=20=20"\n=20=20= =20=20AND=20c.relam=20=3D=20%u"=0A+=09=09=09=09=09=20=20"\n=20=20=20=20= AND=20i.indrelid=20=3D=20%u",=0A+=09=09=09=09=09=20=20BTREE_AM_OID,=20= tbloid);=0A+=0A+=09ExecuteSqlStatement("RESET=20search_path");=0A+=09res=20= =3D=20ExecuteSqlQuery(query->data,=20PGRES_TUPLES_OK);=0A+=09= PQclear(ExecuteSqlQueryForSingleRow(ALWAYS_SECURE_SEARCH_PATH_SQL));=0A+=0A= +=09for=20(i=20=3D=200;=20i=20<=20PQntuples(res);=20i++)=0A+=09=09= check_index(tbloid,=20atooid(PQgetvalue(res,=20i,=200)));=0A+=0A+=09= PQclear(res);=0A+=09resetPQExpBuffer(query);=0A+=09= destroyPQExpBuffer(query);=0A+}=0A+=0A+static=20void=0A+check_index(Oid=20= tbloid,=20Oid=20idxoid)=0A+{=0A+=09PQExpBuffer=20query;=0A+=09PGresult=20= =20=20*res;=0A+=09int=09=09=09i;=0A+=0A+=09query=20=3D=20= createPQExpBuffer();=0A+=0A+=09appendPQExpBuffer(query,=0A+=09=09=09=09=09= =20=20"SELECT=20ct.relname,=20ci.relname,=20blkno,=20msg"=0A+=09=09=09=09= =09=20=20"\nFROM=20verify_btreeam(%u,%s),"=0A+=09=09=09=09=09=20=20"\n=20= =20=20=20=20pg_catalog.pg_class=20ci,"=0A+=09=09=09=09=09=20=20"\n=20=20=20= =20=20pg_catalog.pg_class=20ct"=0A+=09=09=09=09=09=20=20"\nWHERE=20= ci.oid=20=3D=20%u"=0A+=09=09=09=09=09=20=20"\n=20=20AND=20ct.oid=20=3D=20= %u",=0A+=09=09=09=09=09=20=20idxoid,=0A+=09=09=09=09=09=20=20= settings.on_error_stop=20?=20"true"=20:=20"false",=0A+=09=09=09=09=09=20=20= idxoid,=20tbloid);=0A+=0A+=09ExecuteSqlStatement("RESET=20search_path");=0A= +=09res=20=3D=20ExecuteSqlQuery(query->data,=20PGRES_TUPLES_OK);=0A+=09= PQclear(ExecuteSqlQueryForSingleRow(ALWAYS_SECURE_SEARCH_PATH_SQL));=0A+=0A= +=09if=20(PQntuples(res)=20>=200)=0A+=09{=0A+=09=09int=09=09=09lines=20=3D= =20PQntuples(res)=20*=202;=0A+=09=09FILE=09=20=20=20*output=20=3D=20= PageOutput(lines,=20NULL);=0A+=0A+=09=09for=20(i=20=3D=200;=20i=20<=20= PQntuples(res);=20i++)=0A+=09=09{=0A+=09=09=09fprintf(output,=0A+=09=09=09= =09=09"(table=3D%s,index=3D%s,blkno=3D%s)"=0A+=09=09=09=09=09"\n%s\n",=0A= +=09=09=09=09=09PQgetvalue(res,=20i,=200),=09/*=20table=20relname=20*/=0A= +=09=09=09=09=09PQgetvalue(res,=20i,=201),=09/*=20index=20relname=20*/=0A= +=09=09=09=09=09PQgetvalue(res,=20i,=202),=09/*=20index=20blkno=20*/=0A+=09= =09=09=09=09PQgetvalue(res,=20i,=203));=20/*=20msg=20*/=0A+=09=09}=0A+=09= }=0A+=0A+=09PQclear(res);=0A+=09resetPQExpBuffer(query);=0A+=09= destroyPQExpBuffer(query);=0A+}=0A+=0A+static=20void=0A= +parse_cli_options(int=20argc,=20char=20*argv[],=20ConnectOptions=20= *connOpts)=0A+{=0A+=09static=20struct=20option=20long_options[]=20=3D=0A= +=09{=0A+=09=09{"startblock",=20required_argument,=20NULL,=20'b'},=0A+=09= =09{"dbname",=20required_argument,=20NULL,=20'd'},=0A+=09=09{"endblock",=20= required_argument,=20NULL,=20'e'},=0A+=09=09{"host",=20= required_argument,=20NULL,=20'h'},=0A+=09=09{"check-indexes",=20= no_argument,=20NULL,=20'i'},=0A+=09=09{"exclude-indexes",=20no_argument,=20= NULL,=20'I'},=0A+=09=09{"skip-all-visible",=20no_argument,=20NULL,=20= 'v'},=0A+=09=09{"skip-all-frozen",=20no_argument,=20NULL,=20'f'},=0A+=09=09= {"schema",=20required_argument,=20NULL,=20'n'},=0A+=09=09= {"exclude-schema",=20required_argument,=20NULL,=20'N'},=0A+=09=09= {"on-error-stop",=20no_argument,=20NULL,=20'o'},=0A+=09=09{"port",=20= required_argument,=20NULL,=20'p'},=0A+=09=09{"strict-names",=20= no_argument,=20NULL,=20's'},=0A+=09=09{"table",=20required_argument,=20= NULL,=20't'},=0A+=09=09{"exclude-table",=20required_argument,=20NULL,=20= 'T'},=0A+=09=09{"username",=20required_argument,=20NULL,=20'U'},=0A+=09=09= {"version",=20no_argument,=20NULL,=20'V'},=0A+=09=09{"no-password",=20= no_argument,=20NULL,=20'w'},=0A+=09=09{"password",=20no_argument,=20= NULL,=20'W'},=0A+=09=09{"help",=20optional_argument,=20NULL,=20'?'},=0A+=09= =09{NULL,=200,=20NULL,=200}=0A+=09};=0A+=0A+=09int=09=09=09optindex;=0A+=09= int=09=09=09c;=0A+=0A+=09memset(connOpts,=200,=20sizeof=20*connOpts);=0A= +=0A+=09while=20((c=20=3D=20getopt_long(argc,=20argv,=20= "b:d:e:fh:iIn:N:op:st:T:U:vVwW?1",=0A+=09=09=09=09=09=09=09long_options,=20= &optindex))=20!=3D=20-1)=0A+=09{=0A+=09=09switch=20(c)=0A+=09=09{=0A+=09=09= =09case=20'b':=0A+=09=09=09=09settings.startblock=20=3D=20= pg_strdup(optarg);=0A+=09=09=09=09break;=0A+=09=09=09case=20'd':=0A+=09=09= =09=09connOpts->dbname=20=3D=20pg_strdup(optarg);=0A+=09=09=09=09break;=0A= +=09=09=09case=20'e':=0A+=09=09=09=09settings.endblock=20=3D=20= pg_strdup(optarg);=0A+=09=09=09=09break;=0A+=09=09=09case=20'f':=0A+=09=09= =09=09settings.skip_frozen=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09= case=20'h':=0A+=09=09=09=09connOpts->host=20=3D=20pg_strdup(optarg);=0A+=09= =09=09=09break;=0A+=09=09=09case=20'i':=0A+=09=09=09=09= settings.check_indexes=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09= case=20'I':=0A+=09=09=09=09settings.check_indexes=20=3D=20false;=0A+=09=09= =09=09break;=0A+=09=09=09case=20'n':=09=09=09/*=20include=20schema(s)=20= */=0A+=09=09=09=09simple_string_list_append(&schema_include_patterns,=20= optarg);=0A+=09=09=09=09break;=0A+=09=09=09case=20'N':=09=09=09/*=20= exclude=20schema(s)=20*/=0A+=09=09=09=09= simple_string_list_append(&schema_exclude_patterns,=20optarg);=0A+=09=09=09= =09break;=0A+=09=09=09case=20'o':=0A+=09=09=09=09settings.on_error_stop=20= =3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09case=20'p':=0A+=09=09=09=09= connOpts->port=20=3D=20pg_strdup(optarg);=0A+=09=09=09=09break;=0A+=09=09= =09case=20's':=0A+=09=09=09=09settings.strict_names=20=3D=20true;=0A+=09=09= =09=09break;=0A+=09=09=09case=20't':=09=09=09/*=20include=20table(s)=20= */=0A+=09=09=09=09simple_string_list_append(&table_include_patterns,=20= optarg);=0A+=09=09=09=09break;=0A+=09=09=09case=20'T':=09=09=09/*=20= exclude=20table(s)=20*/=0A+=09=09=09=09= simple_string_list_append(&table_exclude_patterns,=20optarg);=0A+=09=09=09= =09break;=0A+=09=09=09case=20'U':=0A+=09=09=09=09connOpts->username=20=3D=20= pg_strdup(optarg);=0A+=09=09=09=09break;=0A+=09=09=09case=20'v':=0A+=09=09= =09=09settings.skip_visible=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09= =09case=20'V':=0A+=09=09=09=09showVersion();=0A+=09=09=09=09= exit(EXIT_SUCCESS);=0A+=09=09=09case=20'w':=0A+=09=09=09=09= settings.getPassword=20=3D=20TRI_NO;=0A+=09=09=09=09break;=0A+=09=09=09= case=20'W':=0A+=09=09=09=09settings.getPassword=20=3D=20TRI_YES;=0A+=09=09= =09=09break;=0A+=09=09=09case=20'?':=0A+=09=09=09=09if=20(optind=20<=3D=20= argc=20&&=0A+=09=09=09=09=09strcmp(argv[optind=20-=201],=20"-?")=20=3D=3D=20= 0)=0A+=09=09=09=09{=0A+=09=09=09=09=09/*=20actual=20help=20option=20= given=20*/=0A+=09=09=09=09=09usage();=0A+=09=09=09=09=09= exit(EXIT_SUCCESS);=0A+=09=09=09=09}=0A+=09=09=09=09else=0A+=09=09=09=09= {=0A+=09=09=09=09=09/*=20getopt=20error=20(unknown=20option=20or=20= missing=20argument)=20*/=0A+=09=09=09=09=09goto=20unknown_option;=0A+=09=09= =09=09}=0A+=09=09=09=09break;=0A+=09=09=09case=201:=0A+=09=09=09=09{=0A+=09= =09=09=09=09if=20(!optarg=20||=20strcmp(optarg,=20"options")=20=3D=3D=20= 0)=0A+=09=09=09=09=09=09usage();=0A+=09=09=09=09=09else=0A+=09=09=09=09=09= =09goto=20unknown_option;=0A+=0A+=09=09=09=09=09exit(EXIT_SUCCESS);=0A+=09= =09=09=09}=0A+=09=09=09=09break;=0A+=09=09=09default:=0A+=09=09= unknown_option:=0A+=09=09=09=09fprintf(stderr,=20_("Try=20\"%s=20= --help\"=20for=20more=20information.\n"),=0A+=09=09=09=09=09=09= settings.progname);=0A+=09=09=09=09exit(EXIT_FAILURE);=0A+=09=09=09=09= break;=0A+=09=09}=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20if=20we=20still=20= have=20arguments,=20use=20it=20as=20the=20database=20name=20and=20= username=0A+=09=20*/=0A+=09while=20(argc=20-=20optind=20>=3D=201)=0A+=09= {=0A+=09=09if=20(!connOpts->dbname)=0A+=09=09=09connOpts->dbname=20=3D=20= argv[optind];=0A+=09=09else=20if=20(!connOpts->username)=0A+=09=09=09= connOpts->username=20=3D=20argv[optind];=0A+=09=09else=0A+=09=09=09= pg_log_warning("extra=20command-line=20argument=20\"%s\"=20ignored",=0A+=09= =09=09=09=09=09=20=20=20argv[optind]);=0A+=0A+=09=09optind++;=0A+=09}=0A= +=0A+}=0A+=0A+/*=0A+=20*=20usage=0A+=20*=0A+=20*=20print=20out=20command=20= line=20arguments=0A+=20*/=0A+static=20void=0A+usage(void)=0A+{=0A+=09= FILE=09=20=20=20*output;=0A+=09int=09=09=09lines;=0A+=09int=09=09=09= lineno;=0A+=0A+=09for=20(lines=20=3D=200;=20usage_text[lines];=20= lines++)=0A+=09=09;=0A+=09output=20=3D=20PageOutput(lines=20+=202,=20= NULL);=0A+=09for=20(lineno=20=3D=200;=20usage_text[lineno];=20lineno++)=0A= +=09=09fprintf(output,=20"%s\n",=20usage_text[lineno]);=0A+=09= fprintf(output,=20"Report=20bugs=20to=20<%s>.\n",=20PACKAGE_BUGREPORT);=0A= +=09fprintf(output,=20"%s=20home=20page:=20<%s>\n",=20PACKAGE_NAME,=20= PACKAGE_URL);=0A+=0A+=09ClosePager(output);=0A+}=0A+=0A+static=20void=0A= +showVersion(void)=0A+{=0A+=09puts("pg_amcheck=20(PostgreSQL)=20"=20= PG_VERSION);=0A+}=0A+=0A+/*=0A+=20*=20for=20backend=20Notice=20messages=20= (INFO,=20WARNING,=20etc)=0A+=20*/=0A+static=20void=0A= +NoticeProcessor(void=20*arg,=20const=20char=20*message)=0A+{=0A+=09= (void)=20arg;=09=09=09=09=09/*=20not=20used=20*/=0A+=09pg_log_info("%s",=20= message);=0A+}=0A+=0A+/*=0A+=20*=20Find=20the=20OIDs=20of=20all=20= schemas=20matching=20the=20given=20list=20of=20patterns,=0A+=20*=20and=20= append=20them=20to=20the=20given=20OID=20list.=0A+=20*/=0A+static=20void=0A= +expand_schema_name_patterns(SimpleStringList=20*patterns,=0A+=09=09=09=09= =09=09=09SimpleOidList=20*oids,=0A+=09=09=09=09=09=09=09bool=20= strict_names)=0A+{=0A+=09PQExpBuffer=20query;=0A+=09PGresult=20=20=20= *res;=0A+=09SimpleStringListCell=20*cell;=0A+=09int=09=09=09i;=0A+=0A+=09= if=20(settings.db=20=3D=3D=20NULL)=0A+=09=09fatal("no=20connection=20on=20= entry=20to=20expand_schema_name_patterns");=0A+=0A+=09if=20= (patterns->head=20=3D=3D=20NULL)=0A+=09=09return;=09=09=09=09=09/*=20= nothing=20to=20do=20*/=0A+=0A+=09query=20=3D=20createPQExpBuffer();=0A+=0A= +=09/*=0A+=09=20*=20The=20loop=20below=20runs=20multiple=20SELECTs=20= might=20sometimes=20result=20in=0A+=09=20*=20duplicate=20entries=20in=20= the=20OID=20list,=20but=20we=20don't=20care.=0A+=09=20*/=0A+=0A+=09for=20= (cell=20=3D=20patterns->head;=20cell;=20cell=20=3D=20cell->next)=0A+=09{=0A= +=09=09appendPQExpBufferStr(query,=0A+=09=09=09=09=09=09=09=20"SELECT=20= oid=20FROM=20pg_catalog.pg_namespace=20n\n");=0A+=09=09= processSQLNamePattern(settings.db,=20query,=20cell->val,=20false,=0A+=09=09= =09=09=09=09=09=20=20false,=20NULL,=20"n.nspname",=20NULL,=20NULL);=0A+=0A= +=09=09res=20=3D=20ExecuteSqlQuery(query->data,=20PGRES_TUPLES_OK);=0A+=09= =09if=20(strict_names=20&&=20PQntuples(res)=20=3D=3D=200)=0A+=09=09=09= fatal("no=20matching=20schemas=20were=20found=20for=20pattern=20\"%s\"",=0A= +=09=09=09=09=20=20cell->val);=0A+=0A+=09=09for=20(i=20=3D=200;=20i=20<=20= PQntuples(res);=20i++)=0A+=09=09{=0A+=09=09=09= simple_oid_list_append(oids,=20atooid(PQgetvalue(res,=20i,=200)));=0A+=09= =09}=0A+=0A+=09=09PQclear(res);=0A+=09=09resetPQExpBuffer(query);=0A+=09= }=0A+=0A+=09destroyPQExpBuffer(query);=0A+}=0A+=0A+/*=0A+=20*=20Find=20= the=20OIDs=20of=20all=20tables=20matching=20the=20given=20list=20of=20= patterns,=0A+=20*=20and=20append=20them=20to=20the=20given=20OID=20list.=20= See=20also=20expand_dbname_patterns()=0A+=20*=20in=20pg_dumpall.c=0A+=20= */=0A+static=20void=0A+expand_table_name_patterns(SimpleStringList=20= *patterns,=20SimpleOidList=20*oids,=0A+=09=09=09=09=09=09=20=20=20bool=20= strict_names)=0A+{=0A+=09PQExpBuffer=20query;=0A+=09PGresult=20=20=20= *res;=0A+=09SimpleStringListCell=20*cell;=0A+=09int=09=09=09i;=0A+=0A+=09= if=20(settings.db=20=3D=3D=20NULL)=0A+=09=09fatal("no=20connection=20on=20= entry=20to=20expand_table_name_patterns");=0A+=0A+=09if=20= (patterns->head=20=3D=3D=20NULL)=0A+=09=09return;=09=09=09=09=09/*=20= nothing=20to=20do=20*/=0A+=0A+=09query=20=3D=20createPQExpBuffer();=0A+=0A= +=09/*=0A+=09=20*=20this=20might=20sometimes=20result=20in=20duplicate=20= entries=20in=20the=20OID=20list,=20but=0A+=09=20*=20we=20don't=20care.=0A= +=09=20*/=0A+=0A+=09for=20(cell=20=3D=20patterns->head;=20cell;=20cell=20= =3D=20cell->next)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20Query=20must=20= remain=20ABSOLUTELY=20devoid=20of=20unqualified=20names.=20=20This=0A+=09= =09=20*=20would=20be=20unnecessary=20given=20a=20pg_table_is_visible()=20= variant=20taking=20a=0A+=09=09=20*=20search_path=20argument.=0A+=09=09=20= */=0A+=09=09appendPQExpBuffer(query,=0A+=09=09=09=09=09=09=20=20"SELECT=20= c.oid"=0A+=09=09=09=09=09=09=20=20"\nFROM=20pg_catalog.pg_class=20c"=0A+=09= =09=09=09=09=09=20=20"\n=20=20=20=20LEFT=20JOIN=20= pg_catalog.pg_namespace=20n"=0A+=09=09=09=09=09=09=20=20"\n=20=20=20=20= ON=20n.oid=20OPERATOR(pg_catalog.=3D)=20c.relnamespace"=0A+=09=09=09=09=09= =09=20=20"\nWHERE=20c.relkind=20OPERATOR(pg_catalog.=3D)=20ANY"=0A+=09=09= =09=09=09=09=20=20"\n=20=20=20=20(array['%c',=20'%c',=20'%c'])\n",=0A+=09= =09=09=09=09=09=20=20RELKIND_RELATION,=20RELKIND_MATVIEW,=0A+=09=09=09=09= =09=09=20=20RELKIND_PARTITIONED_TABLE);=0A+=09=09= processSQLNamePattern(settings.db,=20query,=20cell->val,=20true,=0A+=09=09= =09=09=09=09=09=20=20false,=20"n.nspname",=20"c.relname",=20NULL,=20= NULL);=0A+=09=09ExecuteSqlStatement("RESET=20search_path");=0A+=09=09res=20= =3D=20ExecuteSqlQuery(query->data,=20PGRES_TUPLES_OK);=0A+=09=09= PQclear(ExecuteSqlQueryForSingleRow(ALWAYS_SECURE_SEARCH_PATH_SQL));=0A+=09= =09if=20(strict_names=20&&=20PQntuples(res)=20=3D=3D=200)=0A+=09=09=09= fatal("no=20matching=20tables=20were=20found=20for=20pattern=20\"%s\"",=0A= +=09=09=09=09=20=20cell->val);=0A+=0A+=09=09for=20(i=20=3D=200;=20i=20<=20= PQntuples(res);=20i++)=0A+=09=09=09simple_oid_list_append(oids,=20= atooid(PQgetvalue(res,=20i,=200)));=0A+=0A+=09=09PQclear(res);=0A+=09=09= resetPQExpBuffer(query);=0A+=09}=0A+=0A+=09destroyPQExpBuffer(query);=0A= +}=0A+=0A+static=20void=0A+append_csv_oids(PQExpBuffer=20query,=20const=20= SimpleOidList=20*oids)=0A+{=0A+=09const=20SimpleOidListCell=20*cell;=0A+=09= const=20char=20*comma;=0A+=0A+=09for=20(comma=20=3D=20"",=20cell=20=3D=20= oids->head;=20cell;=20comma=20=3D=20",=20",=20cell=20=3D=20cell->next)=0A= +=09=09appendPQExpBuffer(query,=20"%s%u",=20comma,=20cell->val);=0A+}=0A= +=0A+static=20bool=0A+append_filter(PQExpBuffer=20query,=20const=20char=20= *lval,=20const=20char=20*operator,=0A+=09=09=09=20=20const=20= SimpleOidList=20*oids)=0A+{=0A+=09if=20(!oids->head)=0A+=09=09return=20= false;=0A+=09appendPQExpBuffer(query,=20"\nAND=20%s=20%s=20= ANY(array[\n",=20lval,=20operator);=0A+=09append_csv_oids(query,=20= oids);=0A+=09appendPQExpBuffer(query,=20"\n])");=0A+=09return=20true;=0A= +}=0A+=0A+static=20void=0A+get_table_check_list(SimpleOidList=20= *include_nsp,=20SimpleOidList=20*exclude_nsp,=0A+=09=09=09=09=09=20= SimpleOidList=20*include_tbl,=20SimpleOidList=20*exclude_tbl,=0A+=09=09=09= =09=09=20SimpleOidList=20*checklist)=0A+{=0A+=09PQExpBuffer=20query;=0A+=09= PGresult=20=20=20*res;=0A+=09int=09=09=09i;=0A+=0A+=09if=20(settings.db=20= =3D=3D=20NULL)=0A+=09=09fatal("no=20connection=20on=20entry=20to=20= expand_table_name_patterns");=0A+=0A+=09query=20=3D=20= createPQExpBuffer();=0A+=0A+=09appendPQExpBuffer(query,=0A+=09=09=09=09=09= =20=20"SELECT=20c.oid"=0A+=09=09=09=09=09=20=20"\nFROM=20= pg_catalog.pg_class=20c"=0A+=09=09=09=09=09=20=20"\n=20=20=20=20LEFT=20= JOIN=20pg_catalog.pg_namespace=20n"=0A+=09=09=09=09=09=20=20"\n=20=20=20=20= ON=20n.oid=20OPERATOR(pg_catalog.=3D)=20c.relnamespace"=0A+=09=09=09=09=09= =20=20"\nWHERE=20c.relkind=20OPERATOR(pg_catalog.=3D)=20ANY"=0A+=09=09=09= =09=09=20=20"\n=20=20=20=20(array['%c',=20'%c',=20'%c'])\n",=0A+=09=09=09= =09=09=20=20RELKIND_RELATION,=20RELKIND_MATVIEW,=0A+=09=09=09=09=09=20=20= RELKIND_PARTITIONED_TABLE);=0A+=09append_filter(query,=20"n.oid",=20= "OPERATOR(pg_catalog.=3D)",=20include_nsp);=0A+=09append_filter(query,=20= "n.oid",=20"OPERATOR(pg_catalog.!=3D)",=20exclude_nsp);=0A+=09= append_filter(query,=20"c.oid",=20"OPERATOR(pg_catalog.=3D)",=20= include_tbl);=0A+=09append_filter(query,=20"c.oid",=20= "OPERATOR(pg_catalog.!=3D)",=20exclude_tbl);=0A+=0A+=09= ExecuteSqlStatement("RESET=20search_path");=0A+=09res=20=3D=20= ExecuteSqlQuery(query->data,=20PGRES_TUPLES_OK);=0A+=09= PQclear(ExecuteSqlQueryForSingleRow(ALWAYS_SECURE_SEARCH_PATH_SQL));=0A+=0A= +=09for=20(i=20=3D=200;=20i=20<=20PQntuples(res);=20i++)=0A+=09=09= simple_oid_list_append(checklist,=20atooid(PQgetvalue(res,=20i,=200)));=0A= +=0A+=09PQclear(res);=0A+=09resetPQExpBuffer(query);=0A+=09= destroyPQExpBuffer(query);=0A+}=0A+=0A+/*=20Like=20fatal(),=20but=20with=20= a=20complaint=20about=20a=20particular=20query.=20*/=0A+static=20void=0A= +die_on_query_failure(const=20char=20*query)=0A+{=0A+=09= pg_log_error("query=20failed:=20%s",=0A+=09=09=09=09=20= PQerrorMessage(settings.db));=0A+=09fatal("query=20was:=20%s",=20query);=0A= +}=0A+=0A+static=20void=0A+ExecuteSqlStatement(const=20char=20*query)=0A= +{=0A+=09PGresult=20=20=20*res;=0A+=0A+=09res=20=3D=20= PQexec(settings.db,=20query);=0A+=09if=20(PQresultStatus(res)=20!=3D=20= PGRES_COMMAND_OK)=0A+=09=09die_on_query_failure(query);=0A+=09= PQclear(res);=0A+}=0A+=0A+static=20PGresult=20*=0A+ExecuteSqlQuery(const=20= char=20*query,=20ExecStatusType=20status)=0A+{=0A+=09PGresult=20=20=20= *res;=0A+=0A+=09res=20=3D=20PQexec(settings.db,=20query);=0A+=09if=20= (PQresultStatus(res)=20!=3D=20status)=0A+=09=09= die_on_query_failure(query);=0A+=09return=20res;=0A+}=0A+=0A+/*=0A+=20*=20= Execute=20an=20SQL=20query=20and=20verify=20that=20we=20got=20exactly=20= one=20row=20back.=0A+=20*/=0A+static=20PGresult=20*=0A= +ExecuteSqlQueryForSingleRow(const=20char=20*query)=0A+{=0A+=09PGresult=20= =20=20*res;=0A+=09int=09=09=09ntups;=0A+=0A+=09res=20=3D=20= ExecuteSqlQuery(query,=20PGRES_TUPLES_OK);=0A+=0A+=09/*=20Expecting=20a=20= single=20result=20only=20*/=0A+=09ntups=20=3D=20PQntuples(res);=0A+=09if=20= (ntups=20!=3D=201)=0A+=09=09fatal(ngettext("query=20returned=20%d=20row=20= instead=20of=20one:=20%s",=0A+=09=09=09=09=09=20=20=20"query=20returned=20= %d=20rows=20instead=20of=20one:=20%s",=0A+=09=09=09=09=09=20=20=20= ntups),=0A+=09=09=09=20=20ntups,=20query);=0A+=0A+=09return=20res;=0A+}=0A= diff=20--git=20a/contrib/pg_amcheck/t/001_basic.pl=20= b/contrib/pg_amcheck/t/001_basic.pl=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..dfa0ae9e06=0A---=20/dev/null=0A+++=20= b/contrib/pg_amcheck/t/001_basic.pl=0A@@=20-0,0=20+1,9=20@@=0A+use=20= strict;=0A+use=20warnings;=0A+=0A+use=20TestLib;=0A+use=20Test::More=20= tests=20=3D>=208;=0A+=0A+program_help_ok('pg_amcheck');=0A= +program_version_ok('pg_amcheck');=0A= +program_options_handling_ok('pg_amcheck');=0Adiff=20--git=20= a/contrib/pg_amcheck/t/002_nonesuch.pl=20= b/contrib/pg_amcheck/t/002_nonesuch.pl=0Anew=20file=20mode=20100644=0A= index=200000000000..c63ba4452e=0A---=20/dev/null=0A+++=20= b/contrib/pg_amcheck/t/002_nonesuch.pl=0A@@=20-0,0=20+1,55=20@@=0A+use=20= strict;=0A+use=20warnings;=0A+=0A+use=20PostgresNode;=0A+use=20TestLib;=0A= +use=20Test::More=20tests=20=3D>=2012;=0A+=0A+#=20Test=20set-up=0A+my=20= ($node,=20$port);=0A+$node=20=3D=20get_new_node('test');=0A+$node->init;=0A= +$node->start;=0A+$port=20=3D=20$node->port;=0A+=0A+#=20Load=20the=20= amcheck=20extension,=20upon=20which=20pg_amcheck=20depends=0A= +$node->safe_psql('postgres',=20q(CREATE=20EXTENSION=20amcheck));=0A+=0A= +#########################################=0A+#=20Test=20connecting=20to=20= a=20non-existent=20database=0A+=0A+command_fails_like(=0A+=09[=20= 'pg_amcheck',=20'-p',=20"$port",=20'qqq'=20],=0A+=09qr/\Qpg_amcheck:=20= error:=20could=20not=20connect=20to=20server:=20FATAL:=20=20database=20= "qqq"=20does=20not=20exist\E/,=0A+=09'connecting=20to=20a=20non-existent=20= database');=0A+=0A+#########################################=0A+#=20Test=20= connecting=20with=20a=20non-existent=20user=0A+=0A+command_fails_like(=0A= +=09[=20'pg_amcheck',=20'-p',=20"$port",=20'-U=3Dno_such_user'=20],=0A+=09= qr/\Qpg_amcheck:=20error:=20could=20not=20connect=20to=20server:=20= FATAL:=20=20role=20"=3Dno_such_user"=20does=20not=20exist\E/,=0A+=09= 'connecting=20with=20a=20non-existent=20user');=0A+=0A= +#########################################=0A+#=20Test=20checking=20a=20= non-existent=20schema,=20table,=20and=20patterns=20with=20--strict-names=0A= +=0A+command_fails_like(=0A+=09[=20'pg_amcheck',=20'-p',=20"$port",=20= '-n',=20'nonexistent'=20],=0A+=09qr/\Qpg_amcheck:=20error:=20no=20= matching=20schemas=20were=20found\E/,=0A+=09'checking=20a=20non-existent=20= schema');=0A+=0A+command_fails_like(=0A+=09[=20'pg_amcheck',=20'-p',=20= "$port",=20'-t',=20'nonexistent'=20],=0A+=09qr/\Qpg_amcheck:=20error:=20= no=20matching=20tables=20were=20found\E/,=0A+=09'checking=20a=20= non-existent=20table');=0A+=0A+command_fails_like(=0A+=09[=20= 'pg_amcheck',=20'-p',=20"$port",=20'--strict-names',=20'-n',=20= 'nonexistent*'=20],=0A+=09qr/\Qpg_amcheck:=20error:=20no=20matching=20= schemas=20were=20found=20for=20pattern\E/,=0A+=09'no=20matching=20= schemas');=0A+=0A+command_fails_like(=0A+=09[=20'pg_amcheck',=20'-p',=20= "$port",=20'--strict-names',=20'-t',=20'nonexistent*'=20],=0A+=09= qr/\Qpg_amcheck:=20error:=20no=20matching=20tables=20were=20found=20for=20= pattern\E/,=0A+=09'no=20matching=20tables');=0Adiff=20--git=20= a/contrib/pg_amcheck/t/003_check.pl=20= b/contrib/pg_amcheck/t/003_check.pl=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..01531e5c77=0A---=20/dev/null=0A+++=20= b/contrib/pg_amcheck/t/003_check.pl=0A@@=20-0,0=20+1,85=20@@=0A+use=20= strict;=0A+use=20warnings;=0A+=0A+use=20PostgresNode;=0A+use=20TestLib;=0A= +use=20Test::More=20tests=20=3D>=207;=0A+=0A+#=20Test=20set-up=0A+my=20= ($node,=20$port);=0A+$node=20=3D=20get_new_node('test');=0A+$node->init;=0A= +$node->start;=0A+$port=20=3D=20$node->port;=0A+=0A+#=20Load=20the=20= amcheck=20extension,=20upon=20which=20pg_amcheck=20depends=0A= +$node->safe_psql('postgres',=20q(CREATE=20EXTENSION=20amcheck));=0A+=0A= +#=20Create=20schemas=20and=20tables=20for=20checking=20pg_amcheck's=20= include=0A+#=20and=20exclude=20schema=20and=20table=20command=20line=20= options=0A+$node->safe_psql('postgres',=20q(=0A+CREATE=20SCHEMA=20s1;=0A= +CREATE=20SCHEMA=20s2;=0A+CREATE=20SCHEMA=20s3;=0A+CREATE=20TABLE=20= s1.t1=20(a=20TEXT);=0A+CREATE=20TABLE=20s1.t2=20(a=20TEXT);=0A+CREATE=20= TABLE=20s1.t3=20(a=20TEXT);=0A+CREATE=20TABLE=20s2.t1=20(a=20TEXT);=0A= +CREATE=20TABLE=20s2.t2=20(a=20TEXT);=0A+CREATE=20TABLE=20s2.t3=20(a=20= TEXT);=0A+CREATE=20TABLE=20s3.t1=20(a=20TEXT);=0A+CREATE=20TABLE=20s3.t2=20= (a=20TEXT);=0A+CREATE=20TABLE=20s3.t3=20(a=20TEXT);=0A+CREATE=20INDEX=20= i1=20ON=20s1.t1(a);=0A+CREATE=20INDEX=20i2=20ON=20s1.t2(a);=0A+CREATE=20= INDEX=20i3=20ON=20s1.t3(a);=0A+CREATE=20INDEX=20i1=20ON=20s2.t1(a);=0A= +CREATE=20INDEX=20i2=20ON=20s2.t2(a);=0A+CREATE=20INDEX=20i3=20ON=20= s2.t3(a);=0A+CREATE=20INDEX=20i1=20ON=20s3.t1(a);=0A+CREATE=20INDEX=20i2=20= ON=20s3.t2(a);=0A+CREATE=20INDEX=20i3=20ON=20s3.t3(a);=0A+INSERT=20INTO=20= s1.t1=20(a)=20(SELECT=20gs::TEXT=20FROM=20generate_series(1,10000)=20AS=20= gs);=0A+));=0A+=0A+$node->command_ok(=0A+=09[=0A+=09=09'pg_amcheck',=20= '-I',=20'-p',=20$port,=20'postgres'=0A+=09],=0A+=09'pg_amcheck=20all=20= schemas=20and=20tables');=0A+=0A+$node->command_ok(=0A+=09[=0A+=09=09= 'pg_amcheck',=20'-i',=20'-p',=20$port,=20'postgres'=0A+=09],=0A+=09= 'pg_amcheck=20all=20schemas,=20tables=20and=20indexes');=0A+=0A= +;$node->command_ok(=0A+=09[=0A+=09=09'pg_amcheck',=20'-p',=20$port,=20= 'postgres',=20'-n',=20's1'=0A+=09],=0A+=09'pg_amcheck=20all=20objects=20= in=20schema=20s1');=0A+=0A+$node->command_ok(=0A+=09[=0A+=09=09= 'pg_amcheck',=20'-p',=20$port,=20'postgres',=20'-N',=20's1'=0A+=09],=0A+=09= 'pg_amcheck=20all=20objects=20not=20in=20schema=20s1');=0A+=0A= +$node->command_ok(=0A+=09[=0A+=09=09'pg_amcheck',=20'-p',=20$port,=20= 'postgres',=20'-i',=20'-n',=20's*',=20'-t',=20't1'=0A+=09],=0A+=09= 'pg_amcheck=20all=20tables=20named=20t1=20and=20their=20indexes');=0A+=0A= +$node->command_ok(=0A+=09[=0A+=09=09'pg_amcheck',=20'-I',=20'-p',=20= $port,=20'postgres',=20'-T',=20't1'=0A+=09],=0A+=09'pg_amcheck=20all=20= tables=20not=20named=20t1');=0A+=0A+$node->command_ok(=0A+=09[=0A+=09=09= 'pg_amcheck',=20'-I',=20'-p',=20$port,=20'postgres',=20'-N',=20's1',=20= '-T',=20't1'=0A+=09],=0A+=09'pg_amcheck=20all=20tables=20not=20named=20= t1=20nor=20in=20schema=20s1');=0Adiff=20--git=20= a/contrib/pg_amcheck/t/004_verify_heapam.pl=20= b/contrib/pg_amcheck/t/004_verify_heapam.pl=0Anew=20file=20mode=20100644=0A= index=200000000000..58d5ab88cb=0A---=20/dev/null=0A+++=20= b/contrib/pg_amcheck/t/004_verify_heapam.pl=0A@@=20-0,0=20+1,434=20@@=0A= +use=20strict;=0A+use=20warnings;=0A+=0A+use=20PostgresNode;=0A+use=20= TestLib;=0A+=0A+use=20Test::More=20tests=20=3D>=2048;=0A+=0A+#=20This=20= regression=20test=20demonstrates=20that=20the=20verify_heapam()=20= function=20supplied=0A+#=20with=20the=20amcheck=20contrib=20module=20and=20= depended=20upon=20by=20this=20pg_amcheck=20contrib=0A+#=20module=20= correctly=20identifies=20specific=20kinds=20of=20corruption=20within=20= pages.=20=20To=0A+#=20test=20this,=20we=20need=20a=20mechanism=20to=20= create=20corrupt=20pages=20with=20predictable,=0A+#=20repeatable=20= corruption.=20=20The=20postgres=20backend=20cannot=20be=20expected=20to=20= help=20us=0A+#=20with=20this,=20as=20its=20design=20is=20not=20= consistent=20with=20the=20goal=20of=20intentionally=0A+#=20corrupting=20= pages.=0A+#=0A+#=20Instead,=20we=20create=20a=20table=20to=20corrupt,=20= and=20with=20careful=20consideration=20of=20how=0A+#=20postgresql=20lays=20= out=20heap=20pages,=20we=20seek=20to=20offsets=20within=20the=20page=20= and=0A+#=20overwrite=20deliberately=20chosen=20bytes=20with=20specific=20= values=20calculated=20to=0A+#=20corrupt=20the=20page=20in=20expected=20= ways.=20=20We=20then=20verify=20that=20verify_heapam=0A+#=20reports=20= the=20corruption,=20and=20that=20it=20runs=20without=20crashing.=20=20= Note=20that=20the=0A+#=20backend=20cannot=20simply=20be=20started=20to=20= run=20queries=20against=20the=20corrupt=20table,=20as=0A+#=20the=20= backend=20will=20crash,=20at=20least=20for=20some=20of=20the=20= corruption=20types=20we=0A+#=20generate.=0A+#=0A+#=20Autovacuum=20= potentially=20touching=20the=20table=20in=20the=20background=20makes=20= the=20exact=0A+#=20behavior=20of=20this=20test=20harder=20to=20reason=20= about.=20=20We=20turn=20it=20off=20to=20keep=20things=0A+#=20simpler.=20=20= We=20use=20a=20"belt=20and=20suspenders"=20approach,=20turning=20it=20= off=20for=20the=0A+#=20system=20generally=20in=20postgresql.conf,=20and=20= turning=20it=20off=20specifically=20for=20the=0A+#=20test=20table.=0A+#=0A= +#=20This=20test=20depends=20on=20the=20table=20being=20written=20to=20= the=20heap=20file=20exactly=20as=20we=0A+#=20expect=20it=20to=20be,=20so=20= we=20take=20care=20to=20arrange=20the=20columns=20of=20the=20table,=20= and=0A+#=20insert=20rows=20of=20the=20table,=20that=20give=20predictable=20= sizes=20and=20locations=20within=0A+#=20the=20table=20page.=0A+#=0A+#=20= The=20HeapTupleHeaderData=20has=2023=20bytes=20of=20fixed=20size=20= fields=20before=20the=20variable=0A+#=20length=20t_bits[]=20array.=20=20= We=20have=20exactly=203=20columns=20in=20the=20table,=20so=20natts=20=3D=20= 3,=0A+#=20t_bits=20is=201=20byte=20long,=20and=20t_hoff=20=3D=20= MAXALIGN(23=20+=201)=20=3D=2024.=0A+#=0A+#=20We're=20not=20too=20fussy=20= about=20which=20datatypes=20we=20use=20for=20the=20test,=20but=20we=20do=20= care=0A+#=20about=20some=20specific=20properties.=20=20We'd=20like=20to=20= test=20both=20fixed=20size=20and=0A+#=20varlena=20types.=20=20We'd=20= like=20some=20varlena=20data=20inline=20and=20some=20toasted.=20=20And=0A= +#=20we'd=20like=20the=20layout=20of=20the=20table=20such=20that=20the=20= datums=20land=20at=20predictable=0A+#=20offsets=20within=20the=20tuple.=20= =20We=20choose=20a=20structure=20without=20padding=20on=20all=0A+#=20= supported=20architectures:=0A+#=0A+#=20=09a=20BIGINT=0A+#=09b=20TEXT=0A= +#=09c=20TEXT=0A+#=0A+#=20We=20always=20insert=20a=207-ascii=20character=20= string=20into=20field=20'b',=20which=20with=20a=0A+#=201-byte=20varlena=20= header=20gives=20an=208=20byte=20inline=20value.=20=20We=20always=20= insert=20a=20long=0A+#=20text=20string=20in=20field=20'c',=20long=20= enough=20to=20force=20toast=20storage.=0A+#=0A+#=0A+#=20We=20choose=20to=20= read=20and=20write=20binary=20copies=20of=20our=20table's=20tuples,=20= using=20perl's=0A+#=20pack()=20and=20unpack()=20functions.=20=20Perl=20= uses=20a=20packing=20code=20system=20in=20which:=0A+#=0A+#=09L=20=3D=20= "Unsigned=2032-bit=20Long",=0A+#=09S=20=3D=20"Unsigned=2016-bit=20= Short",=0A+#=09C=20=3D=20"Unsigned=208-bit=20Octet",=0A+#=09c=20=3D=20= "signed=208-bit=20octet",=0A+#=09q=20=3D=20"signed=2064-bit=20quadword"=0A= +#=0A+#=20Each=20tuple=20in=20our=20table=20has=20a=20layout=20as=20= follows:=0A+#=0A+#=20=20=20=20xx=20xx=20xx=20xx=20=20=20=20=20=20=20=20=20= =20=20=20t_xmin:=20xxxx=09=09offset=20=3D=200=09=09L=0A+#=20=20=20=20xx=20= xx=20xx=20xx=20=20=20=20=20=20=20=20=20=20=20=20t_xmax:=20xxxx=09=09= offset=20=3D=204=09=09L=0A+#=20=20=20=20xx=20xx=20xx=20xx=20=20=20=20=20=20= =20=20=20=20t_field3:=20xxxx=09=09offset=20=3D=208=09=09L=0A+#=20=20=20=20= xx=20xx=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20bi_hi:=20= xx=09=09=09offset=20=3D=2012=09=09S=0A+#=20=20=20=20xx=20xx=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20bi_lo:=20xx=09=09=09offset=20=3D= =2014=09=09S=0A+#=20=20=20=20xx=20xx=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20ip_posid:=20xx=09=09=09offset=20=3D=2016=09=09S=0A+#=20=20=20=20= xx=20xx=20=20=20=20=20=20=20=20=20=20=20=20=20t_infomask2:=20xx=09=09=09= offset=20=3D=2018=09=09S=0A+#=20=20=20=20xx=20xx=20=20=20=20=20=20=20=20=20= =20=20=20=20=20t_infomask:=20xx=09=09=09offset=20=3D=2020=09=09S=0A+#=20=20= =20=20xx=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= t_hoff:=20x=09=09=09offset=20=3D=2022=09=09C=0A+#=20=20=20=20xx=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20t_bits:=20x=09=09=09= offset=20=3D=2023=09=09C=0A+#=20=20=20=20xx=20xx=20xx=20xx=20xx=20xx=20= xx=20xx=20=20=20'a':=20xxxxxxxx=09offset=20=3D=2024=09=09q=0A+#=20=20=20=20= xx=20xx=20xx=20xx=20xx=20xx=20xx=20xx=20=20=20'b':=20xxxxxxxx=09offset=20= =3D=2032=09=09Cccccccc=0A+#=20=20=20=20xx=20xx=20xx=20xx=20xx=20xx=20xx=20= xx=20=20=20'c':=20xxxxxxxx=09offset=20=3D=2040=09=09SSSS=0A+#=20=20=20=20= xx=20xx=20xx=20xx=20xx=20xx=20xx=20xx=20=20=20=20=20=20:=20xxxxxxxx=09=20= ...continued=09SSSS=0A+#=20=20=20=20xx=20xx=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20:=20xx=20=20=20=20=20=20=09=20= ...continued=09S=0A+#=0A+#=20We=20could=20choose=20to=20read=20and=20= write=20columns=20'b'=20and=20'c'=20in=20other=20ways,=20but=0A+#=20it=20= is=20convenient=20enough=20to=20do=20it=20this=20way.=20=20We=20define=20= packing=20code=0A+#=20constants=20here,=20where=20they=20can=20be=20= compared=20easily=20against=20the=20layout.=0A+=0A+use=20constant=20= HEAPTUPLE_PACK_CODE=20=3D>=20'LLLSSSSSCCqCcccccccSSSSSSSSS';=0A+use=20= constant=20HEAPTUPLE_PACK_LENGTH=20=3D>=2058;=20=20=20=20=20#=20Total=20= size=0A+=0A+#=20Read=20a=20tuple=20of=20our=20table=20from=20a=20heap=20= page.=0A+#=0A+#=20Takes=20an=20open=20filehandle=20to=20the=20heap=20= file,=20and=20the=20offset=20of=20the=20tuple.=0A+#=0A+#=20Rather=20than=20= returning=20the=20binary=20data=20from=20the=20file,=20unpacks=20the=20= data=20into=20a=0A+#=20perl=20hash=20with=20named=20fields.=20=20These=20= fields=20exactly=20match=20the=20ones=20understood=0A+#=20by=20= write_tuple(),=20below.=20=20Returns=20a=20reference=20to=20this=20hash.=0A= +#=0A+sub=20read_tuple=20($$)=0A+{=0A+=09my=20($fh,=20$offset)=20=3D=20= @_;=0A+=09my=20($buffer,=20%tup);=0A+=09seek($fh,=20$offset,=200);=0A+=09= sysread($fh,=20$buffer,=20HEAPTUPLE_PACK_LENGTH);=0A+=0A+=09@_=20=3D=20= unpack(HEAPTUPLE_PACK_CODE,=20$buffer);=0A+=09%tup=20=3D=20(t_xmin=20=3D>=20= shift,=0A+=09=09=09t_xmax=20=3D>=20shift,=0A+=09=09=09t_field3=20=3D>=20= shift,=0A+=09=09=09bi_hi=20=3D>=20shift,=0A+=09=09=09bi_lo=20=3D>=20= shift,=0A+=09=09=09ip_posid=20=3D>=20shift,=0A+=09=09=09t_infomask2=20=3D>= =20shift,=0A+=09=09=09t_infomask=20=3D>=20shift,=0A+=09=09=09t_hoff=20=3D>= =20shift,=0A+=09=09=09t_bits=20=3D>=20shift,=0A+=09=09=09a=20=3D>=20= shift,=0A+=09=09=09b_header=20=3D>=20shift,=0A+=09=09=09b_body1=20=3D>=20= shift,=0A+=09=09=09b_body2=20=3D>=20shift,=0A+=09=09=09b_body3=20=3D>=20= shift,=0A+=09=09=09b_body4=20=3D>=20shift,=0A+=09=09=09b_body5=20=3D>=20= shift,=0A+=09=09=09b_body6=20=3D>=20shift,=0A+=09=09=09b_body7=20=3D>=20= shift,=0A+=09=09=09c1=20=3D>=20shift,=0A+=09=09=09c2=20=3D>=20shift,=0A+=09= =09=09c3=20=3D>=20shift,=0A+=09=09=09c4=20=3D>=20shift,=0A+=09=09=09c5=20= =3D>=20shift,=0A+=09=09=09c6=20=3D>=20shift,=0A+=09=09=09c7=20=3D>=20= shift,=0A+=09=09=09c8=20=3D>=20shift,=0A+=09=09=09c9=20=3D>=20shift);=0A= +=09#=20Stitch=20together=20the=20text=20for=20column=20'b'=0A+=09= $tup{b}=20=3D=20join('',=20map=20{=20chr($tup{"b_body$_"})=20}=20= (1..7));=0A+=09return=20\%tup;=0A+}=0A+=0A+#=20Write=20a=20tuple=20of=20= our=20table=20to=20a=20heap=20page.=0A+#=0A+#=20Takes=20an=20open=20= filehandle=20to=20the=20heap=20file,=20the=20offset=20of=20the=20tuple,=20= and=20a=0A+#=20reference=20to=20a=20hash=20with=20the=20tuple=20values,=20= as=20returned=20by=20read_tuple().=0A+#=20Writes=20the=20tuple=20fields=20= from=20the=20hash=20into=20the=20heap=20file.=0A+#=0A+#=20The=20purpose=20= of=20this=20function=20is=20to=20write=20a=20tuple=20back=20to=20disk=20= with=20some=0A+#=20subset=20of=20fields=20modified.=20=20The=20function=20= does=20no=20error=20checking.=20=20Use=0A+#=20cautiously.=0A+#=0A+sub=20= write_tuple($$$)=0A+{=0A+=09my=20($fh,=20$offset,=20$tup)=20=3D=20@_;=0A= +=09my=20$buffer=20=3D=20pack(HEAPTUPLE_PACK_CODE,=0A+=09=09=09=09=09= $tup->{t_xmin},=0A+=09=09=09=09=09$tup->{t_xmax},=0A+=09=09=09=09=09= $tup->{t_field3},=0A+=09=09=09=09=09$tup->{bi_hi},=0A+=09=09=09=09=09= $tup->{bi_lo},=0A+=09=09=09=09=09$tup->{ip_posid},=0A+=09=09=09=09=09= $tup->{t_infomask2},=0A+=09=09=09=09=09$tup->{t_infomask},=0A+=09=09=09=09= =09$tup->{t_hoff},=0A+=09=09=09=09=09$tup->{t_bits},=0A+=09=09=09=09=09= $tup->{a},=0A+=09=09=09=09=09$tup->{b_header},=0A+=09=09=09=09=09= $tup->{b_body1},=0A+=09=09=09=09=09$tup->{b_body2},=0A+=09=09=09=09=09= $tup->{b_body3},=0A+=09=09=09=09=09$tup->{b_body4},=0A+=09=09=09=09=09= $tup->{b_body5},=0A+=09=09=09=09=09$tup->{b_body6},=0A+=09=09=09=09=09= $tup->{b_body7},=0A+=09=09=09=09=09$tup->{c1},=0A+=09=09=09=09=09= $tup->{c2},=0A+=09=09=09=09=09$tup->{c3},=0A+=09=09=09=09=09$tup->{c4},=0A= +=09=09=09=09=09$tup->{c5},=0A+=09=09=09=09=09$tup->{c6},=0A+=09=09=09=09= =09$tup->{c7},=0A+=09=09=09=09=09$tup->{c8},=0A+=09=09=09=09=09= $tup->{c9});=0A+=09seek($fh,=20$offset,=200);=0A+=09syswrite($fh,=20= $buffer,=20HEAPTUPLE_PACK_LENGTH);=0A+=09return;=0A+}=0A+=0A+#=20Set=20= umask=20so=20test=20directories=20and=20files=20are=20created=20with=20= default=20permissions=0A+umask(0077);=0A+=0A+#=20Set=20up=20the=20node.=20= =20Once=20we=20create=20and=20corrupt=20the=20table,=0A+#=20autovacuum=20= workers=20visiting=20the=20table=20could=20crash=20the=20backend.=0A+#=20= Disable=20autovacuum=20so=20that=20won't=20happen.=0A+my=20$node=20=3D=20= get_new_node('test');=0A+$node->init;=0A= +$node->append_conf('postgresql.conf',=20'autovacuum=3Doff');=0A+=0A+#=20= Start=20the=20node=20and=20load=20the=20extensions.=20=20We=20depend=20= on=20both=0A+#=20amcheck=20and=20pageinspect=20for=20this=20test.=0A= +$node->start;=0A+my=20$port=20=3D=20$node->port;=0A+my=20$pgdata=20=3D=20= $node->data_dir;=0A+$node->safe_psql('postgres',=20"CREATE=20EXTENSION=20= amcheck");=0A+$node->safe_psql('postgres',=20"CREATE=20EXTENSION=20= pageinspect");=0A+=0A+#=20Create=20the=20test=20table=20with=20precisely=20= the=20schema=20that=20our=0A+#=20corruption=20function=20expects.=0A= +$node->safe_psql(=0A+=09'postgres',=20qq(=0A+=09=09CREATE=20TABLE=20= public.test=20(a=20BIGINT,=20b=20TEXT,=20c=20TEXT);=0A+=09=09ALTER=20= TABLE=20public.test=20SET=20(autovacuum_enabled=3Dfalse);=0A+=09=09ALTER=20= TABLE=20public.test=20ALTER=20COLUMN=20c=20SET=20STORAGE=20EXTERNAL;=0A+=09= =09CREATE=20INDEX=20test_idx=20ON=20public.test(a,=20b);=0A+=09));=0A+=0A= +my=20$rel=20=3D=20$node->safe_psql('postgres',=20qq(SELECT=20= pg_relation_filepath('public.test')));=0A+my=20$relpath=20=3D=20= "$pgdata/$rel";=0A+=0A+use=20constant=20ROWCOUNT=20=3D>=2014;=0A= +$node->safe_psql('postgres',=20qq(=0A+=09INSERT=20INTO=20public.test=20= (a,=20b,=20c)=0A+=09=09VALUES=20(=0A+=09=09=0912345678,=0A+=09=09=09= 'abcdefg',=0A+=09=09=09repeat('w',=2010000)=0A+=09=09);=0A+=09VACUUM=20= FREEZE=20public.test=0A+=09))=20for=20(1..ROWCOUNT);=0A+=0A+my=20= $relfrozenxid=20=3D=20$node->safe_psql('postgres',=0A+=09q(select=20= relfrozenxid=20from=20pg_class=20where=20relname=20=3D=20'test'));=0A+=0A= +#=20Find=20where=20each=20of=20the=20tuples=20is=20located=20on=20the=20= page.=0A+my=20@lp_off;=0A+for=20my=20$tup=20(0..ROWCOUNT-1)=0A+{=0A+=09= push=20(@lp_off,=20$node->safe_psql('postgres',=20qq(=0A+select=20lp_off=20= from=20heap_page_items(get_raw_page('test',=20'main',=200))=0A+=09offset=20= $tup=20limit=201)));=0A+}=0A+=0A+#=20Check=20that=20pg_amcheck=20runs=20= against=20the=20uncorrupted=20table=20without=20error.=0A= +$node->command_ok(['pg_amcheck',=20'-p',=20$port,=20'postgres'],=0A+=09=09= =09=09=20=20'pg_amcheck=20test=20table,=20prior=20to=20corruption');=0A+=0A= +#=20Check=20that=20pg_amcheck=20runs=20against=20the=20uncorrupted=20= table=20and=20index=20without=20error.=0A= +$node->command_ok(['pg_amcheck',=20'--check-indexes',=20'-p',=20$port,=20= 'postgres'],=0A+=09=09=09=09=20=20'pg_amcheck=20test=20table=20and=20= index,=20prior=20to=20corruption');=0A+=0A+$node->stop;=0A+=0A+#=20Some=20= #define=20constants=20from=20access/htup_details.h=20for=20use=20while=20= corrupting.=0A+use=20constant=20HEAP_HASNULL=20=20=20=20=20=20=20=20=20=20= =20=20=3D>=200x0001;=0A+use=20constant=20HEAP_XMAX_LOCK_ONLY=20=20=20=20=20= =3D>=200x0080;=0A+use=20constant=20HEAP_XMIN_COMMITTED=20=20=20=20=20=3D>=20= 0x0100;=0A+use=20constant=20HEAP_XMIN_INVALID=20=20=20=20=20=20=20=3D>=20= 0x0200;=0A+use=20constant=20HEAP_XMAX_COMMITTED=20=20=20=20=20=3D>=20= 0x0400;=0A+use=20constant=20HEAP_XMAX_INVALID=20=20=20=20=20=20=20=3D>=20= 0x0800;=0A+use=20constant=20HEAP_NATTS_MASK=20=20=20=20=20=20=20=20=20=3D>= =200x07FF;=0A+use=20constant=20HEAP_XMAX_IS_MULTI=20=20=20=20=20=20=3D>=20= 0x1000;=0A+use=20constant=20HEAP_KEYS_UPDATED=20=20=20=20=20=20=20=3D>=20= 0x2000;=0A+=0A+#=20Corrupt=20the=20tuples,=20one=20type=20of=20= corruption=20per=20tuple.=20=20Some=20types=20of=0A+#=20corruption=20= cause=20verify_heapam=20to=20skip=20to=20the=20next=20tuple=20without=0A= +#=20performing=20any=20remaining=20checks,=20so=20we=20can't=20exercise=20= the=20system=20properly=20if=0A+#=20we=20focus=20all=20our=20corruption=20= on=20a=20single=20tuple.=0A+#=0A+my=20$file;=0A+open($file,=20'+<',=20= $relpath);=0A+binmode=20$file;=0A+=0A+for=20(my=20$tupidx=20=3D=200;=20= $tupidx=20<=20ROWCOUNT;=20$tupidx++)=0A+{=0A+=09my=20$offset=20=3D=20= $lp_off[$tupidx];=0A+=09my=20$tup=20=3D=20read_tuple($file,=20$offset);=0A= +=0A+=09#=20Sanity-check=20that=20the=20data=20appears=20on=20the=20page=20= where=20we=20expect.=0A+=09if=20($tup->{a}=20ne=20'12345678'=20||=20= $tup->{b}=20ne=20'abcdefg')=0A+=09{=0A+=09=09fail('Page=20layout=20= differs=20from=20our=20expectations');=0A+=09=09$node->clean_node;=0A+=09= =09exit;=0A+=09}=0A+=0A+=09if=20($tupidx=20=3D=3D=200)=0A+=09{=0A+=09=09= #=20Corruptly=20set=20xmin=20<=20relfrozenxid=0A+=09=09$tup->{t_xmin}=20= =3D=203;=0A+=09=09$tup->{t_infomask}=20&=3D=20~HEAP_XMIN_COMMITTED;=0A+=09= =09$tup->{t_infomask}=20&=3D=20~HEAP_XMIN_INVALID;=0A+=09}=0A+=09elsif=20= ($tupidx=20=3D=3D=201)=0A+=09{=0A+=09=09#=20Corruptly=20set=20xmin=20<=20= relfrozenxid,=20further=20back=0A+=09=09$tup->{t_xmin}=20=3D=20= 4026531839;=09=09#=20Note=20circularity=20of=20xid=20comparison=0A+=09=09= $tup->{t_infomask}=20&=3D=20~HEAP_XMIN_COMMITTED;=0A+=09=09= $tup->{t_infomask}=20&=3D=20~HEAP_XMIN_INVALID;=0A+=09}=0A+=09elsif=20= ($tupidx=20=3D=3D=202)=0A+=09{=0A+=09=09#=20Corruptly=20set=20xmax=20<=20= relminmxid;=0A+=09=09$tup->{t_xmax}=20=3D=204026531839;=09=09#=20Note=20= circularity=20of=20xid=20comparison=0A+=09=09$tup->{t_infomask}=20&=3D=20= ~HEAP_XMAX_INVALID;=0A+=09}=0A+=09elsif=20($tupidx=20=3D=3D=203)=0A+=09{=0A= +=09=09#=20Corrupt=20the=20tuple=20t_hoff,=20but=20keep=20it=20aligned=20= properly=0A+=09=09$tup->{t_hoff}=20+=3D=20128;=0A+=09}=0A+=09elsif=20= ($tupidx=20=3D=3D=204)=0A+=09{=0A+=09=09#=20Corrupt=20the=20tuple=20= t_hoff,=20wrong=20alignment=0A+=09=09$tup->{t_hoff}=20+=3D=203;=0A+=09}=0A= +=09elsif=20($tupidx=20=3D=3D=205)=0A+=09{=0A+=09=09#=20Corrupt=20the=20= tuple=20t_hoff,=20underflow=20but=20correct=20alignment=0A+=09=09= $tup->{t_hoff}=20-=3D=208;=0A+=09}=0A+=09elsif=20($tupidx=20=3D=3D=206)=0A= +=09{=0A+=09=09#=20Corrupt=20the=20tuple=20t_hoff,=20underflow=20and=20= wrong=20alignment=0A+=09=09$tup->{t_hoff}=20-=3D=203;=0A+=09}=0A+=09= elsif=20($tupidx=20=3D=3D=207)=0A+=09{=0A+=09=09#=20Corrupt=20the=20= tuple=20to=20look=20like=20it=20has=20lots=20of=20attributes,=20not=20= just=203=0A+=09=09$tup->{t_infomask2}=20|=3D=20HEAP_NATTS_MASK;=0A+=09}=0A= +=09elsif=20($tupidx=20=3D=3D=208)=0A+=09{=0A+=09=09#=20Corrupt=20the=20= tuple=20to=20look=20like=20it=20has=20lots=20of=20attributes,=20some=20= of=0A+=09=09#=20them=20null.=20=20This=20falsely=20creates=20the=20= impression=20that=20the=20t_bits=0A+=09=09#=20array=20is=20longer=20than=20= just=20one=20byte,=20but=20t_hoff=20still=20says=20otherwise.=0A+=09=09= $tup->{t_infomask}=20|=3D=20HEAP_HASNULL;=0A+=09=09$tup->{t_infomask2}=20= |=3D=20HEAP_NATTS_MASK;=0A+=09=09$tup->{t_bits}=20=3D=200xAA;=0A+=09}=0A= +=09elsif=20($tupidx=20=3D=3D=209)=0A+=09{=0A+=09=09#=20Same=20as=20= above,=20but=20this=20time=20t_hoff=20plays=20along=0A+=09=09= $tup->{t_infomask}=20|=3D=20HEAP_HASNULL;=0A+=09=09$tup->{t_infomask2}=20= |=3D=20(HEAP_NATTS_MASK=20&=200x40);=0A+=09=09$tup->{t_bits}=20=3D=20= 0xAA;=0A+=09=09$tup->{t_hoff}=20=3D=2032;=0A+=09}=0A+=09elsif=20($tupidx=20= =3D=3D=2010)=0A+=09{=0A+=09=09#=20Corrupt=20the=20bits=20in=20column=20= 'b'=201-byte=20varlena=20header=0A+=09=09$tup->{b_header}=20=3D=200x80;=0A= +=09}=0A+=09elsif=20($tupidx=20=3D=3D=2011)=0A+=09{=0A+=09=09#=20Corrupt=20= the=20bits=20in=20column=20'c'=20toast=20pointer=0A+=09=09$tup->{c6}=20=3D= =2041;=0A+=09=09$tup->{c7}=20=3D=2041;=0A+=09}=0A+=09elsif=20($tupidx=20= =3D=3D=2012)=0A+=09{=0A+=09=09#=20Set=20both=20HEAP_XMAX_LOCK_ONLY=20and=20= HEAP_KEYS_UPDATED=0A+=09=09$tup->{t_infomask}=20|=3D=20= HEAP_XMAX_LOCK_ONLY;=0A+=09=09$tup->{t_infomask2}=20|=3D=20= HEAP_KEYS_UPDATED;=0A+=09}=0A+=09elsif=20($tupidx=20=3D=3D=2013)=0A+=09{=0A= +=09=09#=20Set=20both=20HEAP_XMAX_COMMITTED=20and=20HEAP_XMAX_IS_MULTI=0A= +=09=09$tup->{t_infomask}=20|=3D=20HEAP_XMAX_COMMITTED;=0A+=09=09= $tup->{t_infomask}=20|=3D=20HEAP_XMAX_IS_MULTI;=0A+=09}=0A+=09= write_tuple($file,=20$offset,=20$tup);=0A+}=0A+close($file);=0A+=0A+#=20= Run=20verify_heapam=20on=20the=20corrupted=20file=0A+$node->start;=0A+=0A= +my=20$result=20=3D=20$node->safe_psql(=0A+=09=09=09'postgres',=0A+=09=09= =09q(SELECT=20*=20FROM=20verify_heapam('test',=20on_error_stop=20:=3D=20= false,=20skip=20:=3D=20NULL,=20startblock=20:=3D=20NULL,=20endblock=20:=3D= =20NULL)));=0A+is=20($result,=0A+"0|1|8128|1|58|||tuple=20xmin=203=20= precedes=20relfrozenxid=20$relfrozenxid=0A+0|2|8064|1|58|||tuple=20xmin=20= 4026531839=20precedes=20relfrozenxid=20$relfrozenxid=0A= +0|3|8000|1|58|||tuple=20xmax=204026531839=20precedes=20relfrozenxid=20= $relfrozenxid=0A+0|4|7936|1|58|||tuple's=20152=20byte=20header=20size=20= exceeds=20the=2058=20byte=20length=20of=20the=20entire=20tuple=0A= +0|4|7936|1|58|||tuple=20without=20null=20values=20has=20user=20data=20= offset=20152=20rather=20than=20the=20expected=20offset=2024=0A= +0|5|7872|1|58|||tuple's=20user=20data=20offset=2027=20not=20maximally=20= aligned=20to=2032=0A+0|5|7872|1|58|||tuple=20without=20null=20values=20= has=20user=20data=20offset=2027=20rather=20than=20the=20expected=20= offset=2024=0A+0|6|7808|1|58|||tuple's=20header=20size=20is=2016=20bytes=20= which=20is=20less=20than=20the=2023=20byte=20minimum=20valid=20header=20= size=0A+0|6|7808|1|58|||tuple=20without=20null=20values=20has=20user=20= data=20offset=2016=20rather=20than=20the=20expected=20offset=2024=0A= +0|7|7744|1|58|||tuple's=20header=20size=20is=2021=20bytes=20which=20is=20= less=20than=20the=2023=20byte=20minimum=20valid=20header=20size=0A= +0|7|7744|1|58|||tuple's=20user=20data=20offset=2021=20not=20maximally=20= aligned=20to=2024=0A+0|7|7744|1|58|||tuple=20without=20null=20values=20= has=20user=20data=20offset=2021=20rather=20than=20the=20expected=20= offset=2024=0A+0|8|7680|1|58|||tuple=20has=202047=20attributes=20in=20= relation=20with=20only=203=20attributes=0A+0|9|7616|1|58|||tuple=20with=20= null=20values=20has=20user=20data=20offset=2024=20rather=20than=20the=20= expected=20offset=20280=0A+0|10|7552|1|58|||tuple=20has=2067=20= attributes=20in=20relation=20with=20only=203=20attributes=0A= +0|11|7488|1|58|1||tuple=20attribute=20of=20length=204294967295=20ends=20= at=20offset=20416848000,=20but=20tuple=20length=20is=20only=2058=0A= +0|12|7424|1|58|2|0|final=20chunk=20number=200=20differs=20from=20= expected=20value=206=0A+0|12|7424|1|58|2|0|toasted=20value=20missing=20= from=20toast=20table=0A+0|13|7360|1|58|||tuple=20xmax=20marked=20= incompatibly=20as=20keys=20updated=20and=20locked=20only=0A= +0|14|7296|1|58|||tuple=20xmax=200=20precedes=20relminmxid=201=0A= +0|14|7296|1|58|||tuple=20xmax=20marked=20incompatibly=20as=20committed=20= and=20as=20a=20multitransaction=20ID",=0A+"Expected=20verify_heapam=20= output");=0A+=0A+#=20Each=20table=20corruption=20message=20is=20returned=20= with=20a=20standard=20header,=20and=20we=20can=0A+#=20check=20for=20= those=20headers=20to=20verify=20that=20corruption=20is=20being=20= reported.=20=20We=20can=0A+#=20also=20check=20for=20each=20individual=20= corruption=20that=20we=20would=20expect=20to=20see.=0A+my=20= @corruption_re=20=3D=20(=0A+=0A+=09#=20standard=20header=0A+=09= qr/relname=3Dtest,blkno=3D\d*,offnum=3D\d*,lp_off=3D\d*,lp_flags=3D\d*,lp_= len=3D\d*,attnum=3D\d*,chunk=3D\d*/,=0A+=0A+=09#=20individual=20detected=20= corruptions=0A+=09qr/final=20chunk=20number=20\d+=20differs=20from=20= expected=20value=20\d+/,=0A+=09qr/toasted=20value=20missing=20from=20= toast=20table/,=0A+=09qr/tuple=20attribute=20of=20length=20\d+=20ends=20= at=20offset=20\d+,=20but=20tuple=20length=20is=20only=20\d+/,=0A+=09= qr/tuple=20has=20\d+=20attributes=20in=20relation=20with=20only=20\d+=20= attributes/,=0A+=09qr/tuple=20with=20null=20values=20has=20user=20data=20= offset=20\d+=20rather=20than=20the=20expected=20offset=20\d+/,=0A+=09= qr/tuple=20without=20null=20values=20has=20user=20data=20offset=20\d+=20= rather=20than=20the=20expected=20offset=20\d+/,=0A+=09qr/tuple=20xmax=20= \d+=20precedes=20relfrozenxid=20\d+/,=0A+=09qr/tuple=20xmax=20\d+=20= precedes=20relminmxid=20\d+/,=0A+=09qr/tuple=20xmax=20marked=20= incompatibly=20as=20committed=20and=20as=20a=20multitransaction=20ID/,=0A= +=09qr/tuple=20xmax=20marked=20incompatibly=20as=20keys=20updated=20and=20= locked=20only/,=0A+=09qr/tuple=20xmin=20\d+=20precedes=20relfrozenxid=20= \d+/,=0A+=09qr/tuple's=20\d+=20byte=20header=20size=20exceeds=20the=20= \d+=20byte=20length=20of=20the=20entire=20tuple/,=0A+=09qr/tuple's=20= header=20size=20is=20\d+=20bytes=20which=20is=20less=20than=20the=20\d+=20= byte=20minimum=20valid=20header=20size/,=0A+=09qr/tuple's=20user=20data=20= offset=20\d+=20not=20maximally=20aligned=20to=20\d+/,=0A+);=0A+=0A= +$node->command_like(=0A+=09['pg_amcheck',=20'--exclude-indexes',=20= '-p',=20$port,=20'postgres'],=20$_,=0A+=09"pg_amcheck=20reports:=20$_"=0A= +=09)=20for(@corruption_re);=0A+=0A+$node->teardown_node;=0A= +$node->clean_node;=0Adiff=20--git=20a/doc/src/sgml/contrib.sgml=20= b/doc/src/sgml/contrib.sgml=0Aindex=20261a559e81..f606e42fb9=20100644=0A= ---=20a/doc/src/sgml/contrib.sgml=0A+++=20b/doc/src/sgml/contrib.sgml=0A= @@=20-118,6=20+118,7=20@@=20CREATE=20EXTENSION=20= module_name;=0A=20=20<ree;=0A=20=20= &pageinspect;=0A=20=20&passwordcheck;=0A+=20&pg_amcheck;=0A=20=20= &pgbuffercache;=0A=20=20&pgcrypto;=0A=20=20&pgfreespacemap;=0Adiff=20= --git=20a/doc/src/sgml/filelist.sgml=20b/doc/src/sgml/filelist.sgml=0A= index=2064b5da0070..10e1ca9663=20100644=0A---=20= a/doc/src/sgml/filelist.sgml=0A+++=20b/doc/src/sgml/filelist.sgml=0A@@=20= -131,6=20+131,7=20@@=0A=20=0A=20=0A=20=0A+=0A=20=0A=20=0A=20=0Adiff=20--git=20a/doc/src/sgml/pg_amcheck.sgml=20= b/doc/src/sgml/pg_amcheck.sgml=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..a0b9c9d19b=0A---=20/dev/null=0A+++=20= b/doc/src/sgml/pg_amcheck.sgml=0A@@=20-0,0=20+1,136=20@@=0A+=0A+=0A+=0A+=20pg_amcheck=0A+=0A+=20= =0A+=20=20pg_amcheck=0A= +=20=0A+=0A+=20=0A+=20=20The=20= pg_amcheck=20module=20provides=20a=20command=20line=20= interface=0A+=20=20to=20the=20=20corruption=20= checking=20functionality.=0A+=20=0A+=0A+=20=0A+=20=20= pg_amcheck=20is=20a=20regular=0A+=20=20= PostgreSQL=20client=20application.=20You=20= can=20perform=0A+=20=20corruption=20checks=20from=20any=20remote=20host=20= that=20has=20access=20to=20the=20database=0A+=20=20connecting=20as=20a=20= user=20with=20sufficient=20privileges=20to=20check=20tables=20and=20= indexes.=0A+=20=20Currently,=20this=20requires=20superuser=20privileges.=0A= +=20=0A+=0A+=20=0A+=20=20Options=0A+=0A+=20=20= =0A+=20=20=20To=20specify=20which=20database=20server=20= pg_amcheck=20should=0A+=20=20=20contact,=20= use=20the=20command=20line=20options=20=20or=0A+=20=20= =20=20and=20=20or=0A+=20=20=20= .=20The=20default=20host=20is=20the=20local=20host=0A= +=20=20=20or=20whatever=20your=20PGHOST=20environment=20= variable=20specifies.=0A+=20=20=20Similarly,=20the=20default=20port=20is=20= indicated=20by=20the=20PGPORT=0A+=20=20=20environment=20= variable=20or,=20failing=20that,=20by=20the=20compiled-in=20default.=0A+=20= =20=0A+=20=0A+=20=20=0A+=20=20=20Like=20any=20other=20= PostgreSQL=20client=20application,=0A+=20=20=20= pg_amcheck=20will=20by=20default=20connect=20= with=20the=0A+=20=20=20database=20user=20name=20that=20is=20equal=20to=20= the=20current=20operating=20system=20user=20name.=0A+=20=20=20To=20= override=20this,=20either=20specify=20the=20=20option=20= or=20set=20the=0A+=20=20=20environment=20variable=20= PGUSER.=20Remember=20that=0A+=20=20=20= pg_amcheck=20connections=20are=20subject=20to=20= the=20normal=0A+=20=20=20client=20authentication=20mechanisms=20(which=20= are=20described=20in=20).=0A+=20=20=0A+=20=0A+=20=20= =0A+=20=20=20To=20restrict=20checking=20of=20tables=20and=20= indexes=20to=20specific=20schemas,=20specify=20the=0A+=20=20=20= =20or=20=20option=20with=20a=20= pattern.=0A+=20=20=20To=20exclude=20checking=20of=20tables=20and=20= indexes=20within=20specific=20schemas,=20specify=0A+=20=20=20the=20= =20or=20=20option=20= with=0A+=20=20=20a=20pattern.=0A+=20=20=0A+=20=0A+=20=20=0A= +=20=20=20To=20specify=20which=20tables=20are=20checked,=20specify=20the=0A= +=20=20=20=20or=20=20option=20= with=20a=20pattern.=0A+=20=20=20To=20exclude=20checking=20of=20tables,=20= specify=20the=0A+=20=20=20=20or=20= =20option=20with=20a=0A+=20=20=20= pattern.=0A+=20=20=0A+=20=0A+=20=20=0A+=20=20=20To=20check=20= indexes=20associated=20with=20checked=20tables,=20specify=20the=0A+=20=20= =20=20or=20=20option.=20= =20Only=0A+=20=20=20indexes=20on=20tables=20which=20are=20being=20= checked=20will=20themselves=20be=20checked.=20=20To=0A+=20=20=20check=20= all=20indexes=20in=20a=20database,=20all=20tables=20on=20which=20the=20= indexes=20exist=20must=0A+=20=20=20also=20be=20checked.=20=20This=20= restriction=20may=20be=20relaxed=20in=20the=20future.=0A+=20=20=0A= +=20=0A+=20=20=0A+=20=20=20To=20restrict=20the=20range=20of=20= blocks=20within=20a=20table=20that=20are=20checked,=20specify=20the=0A+=20= =20=20=20or=20=20and/or=0A= +=20=20=20=20or=20=20= options=20with=20numeric=0A+=20=20=20values=20for=20the=20starting=20and=20= ending=20block=20numbers.=20=20Although=20these=20options=0A+=20=20=20= make=20the=20most=20sense=20when=20applied=20to=20a=20single=20table,=20= if=20specified=20along=20with=0A+=20=20=20options=20that=20select=20= multiple=20tables,=20each=20table=20check=20will=20be=20restricted=20to=0A= +=20=20=20the=20specified=20blocks.=20=20If=20= =20is=20omitted,=20checking=0A+=20=20=20= begins=20with=20the=20first=20block.=20If=20=20= is=20omitted,=0A+=20=20=20checking=20continues=20to=20the=20end=20of=20= the=20relation.=0A+=20=20=0A+=20=0A+=20=20=0A+=20=20=20Some=20= users=20may=20wish=20to=20periodically=20check=20tables=20without=20= incurring=20the=20cost=0A+=20=20=20of=20rechecking=20older=20table=20= blocks,=20presumably=20because=20those=20blocks=20have=0A+=20=20=20= already=20been=20checked=20in=20the=20past.=20=20There=20is=20at=20= present=20no=20perfect=20way=20to=20do=0A+=20=20=20this.=20=20Although=20= the=20=20and=20=0A= +=20=20=20options=20can=20be=20used=20to=20restrict=20blocks,=20the=20= user=20is=20not=20expected=20to=20have=0A+=20=20=20perfect=20knowledge=20= of=20which=20blocks=20have=20already=20been=20checked,=20and=20in=20any=0A= +=20=20=20event,=20some=20blocks=20that=20were=20previously=20checked=20= may=20have=20been=20subject=20to=0A+=20=20=20modification=20since=20the=20= last=20check.=20=20As=20an=20approximation=20to=20the=20desired=0A+=20=20= =20functionality,=20one=20can=20specify=20the=0A+=20=20=20= =20or=20=20option,=20= or=0A+=20=20=20alternatively=20the=0A+=20=20=20=20or=20= =20option=20to=20skip=0A+=20=20=20= blocks=20marked=20all=20frozen=20or=20all=20visible,=20respectively.=0A+=20= =20=0A+=20=0A+=0A+=20=0A+=20=20Example=20= Usage=0A+=0A+=20=20=0A+=20=20=20Checking=20an=20entire=20= database=20which=20contains=20one=20corrupt=20table,=20"corrupted",=0A+=20= =20=20along=20with=20the=20output:=0A+=20=20=0A+=0A+=0A+%=20= pg_amcheck=20-i=20test=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=0A= +(relname=3Dcorrupted,blkno=3D0,offnum=3D16,lp_off=3D7680,lp_flags=3D1,lp_= len=3D31,attnum=3D,chunk=3D)=0A+tuple=20xmin=20=3D=203289393=20is=20in=20= the=20future=0A= +(relname=3Dcorrupted,blkno=3D0,offnum=3D17,lp_off=3D7648,lp_flags=3D1,lp_= len=3D31,attnum=3D,chunk=3D)=0A+tuple=20xmax=20=3D=200=20precedes=20= relation=20relminmxid=20=3D=201=0A= +(relname=3Dcorrupted,blkno=3D0,offnum=3D17,lp_off=3D7648,lp_flags=3D1,lp_= len=3D31,attnum=3D,chunk=3D)=0A+tuple=20xmin=20=3D=2012593=20is=20in=20= the=20future=0A+=0A+=0A+=20=20=0A+=20=20=20....=20many=20= pages=20of=20output=20removed=20for=20brevity=20....=0A+=20=20=0A= +=0A+=0A= +(relname=3Dcorrupted,blkno=3D107,offnum=3D22,lp_off=3D7312,lp_flags=3D1,l= p_len=3D34,attnum=3D,chunk=3D)=0A+tuple=20xmin=20=3D=20305=20precedes=20= relation=20relfrozenxid=20=3D=20487=0A= +(relname=3Dcorrupted,blkno=3D107,offnum=3D22,lp_off=3D7312,lp_flags=3D1,l= p_len=3D34,attnum=3D,chunk=3D)=0A+t_hoff=20>=20lp_len=20(54=20>=2034)=0A= +(relname=3Dcorrupted,blkno=3D107,offnum=3D22,lp_off=3D7312,lp_flags=3D1,l= p_len=3D34,attnum=3D,chunk=3D)=0A+t_hoff=20not=20max-aligned=20(54)=0A= +=0A+=0A+=20=20=0A+=20=20=20Each=20detected=20corruption=20= is=20reported=20on=20two=20lines,=20the=20first=20line=20shows=20the=0A+=20= =20=20location=20and=20the=20second=20line=20shows=20a=20message=20= describing=20the=20problem.=0A+=20=20=0A+=20=0A+=0A= diff=20--git=20a/src/tools/pgindent/typedefs.list=20= b/src/tools/pgindent/typedefs.list=0Aindex=20c58d025902..6fb42e0897=20= 100644=0A---=20a/src/tools/pgindent/typedefs.list=0A+++=20= b/src/tools/pgindent/typedefs.list=0A@@=20-102,6=20+102,7=20@@=20= AlterUserMappingStmt=0A=20AlteredTableInfo=0A=20AlternativeSubPlan=0A=20= AlternativeSubPlanState=0A+AmCheckSettings=0A=20= AnalyzeAttrComputeStatsFunc=0A=20AnalyzeAttrFetchFunc=0A=20= AnalyzeForeignTable_function=0A@@=20-404,6=20+405,7=20@@=20= ConnCacheEntry=0A=20ConnCacheKey=0A=20ConnStatusType=0A=20ConnType=0A= +ConnectOptions=0A=20ConnectionStateEnum=0A=20ConsiderSplitContext=0A=20= Const=0A--=20=0A2.21.1=20(Apple=20Git-122.3)=0A=0A= --Apple-Mail=_268BCDFB-5F95-4F65-8E72-69AFC6227897 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 =E2=80=94 Mark Dilger EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company --Apple-Mail=_268BCDFB-5F95-4F65-8E72-69AFC6227897--