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 1lKkrT-0007iB-9Z for pgsql-hackers@arkaria.postgresql.org; Fri, 12 Mar 2021 16:42:04 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1lKkrR-0006uB-8c for pgsql-hackers@arkaria.postgresql.org; Fri, 12 Mar 2021 16:42:01 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lKkrQ-0006mW-Lg for pgsql-hackers@lists.postgresql.org; Fri, 12 Mar 2021 16:42:00 +0000 Received: from mail-pf1-x42c.google.com ([2607:f8b0:4864:20::42c]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1lKkrG-0004vJ-Cd for pgsql-hackers@postgresql.org; Fri, 12 Mar 2021 16:41:58 +0000 Received: by mail-pf1-x42c.google.com with SMTP id y67so2240013pfb.2 for ; Fri, 12 Mar 2021 08:41:49 -0800 (PST) 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=nGNSwR9lXzWCOlZzO4TAYP7aVH53/nfVhy726nYS22Y=; b=JnS0YZG3rYJ1paENTK8VyGEAEOapbGbY41sCWxQk5e1eoImHj3wD3txU083kX/nVP8 s1qXRH5MNORpMhlj7ccvEF298q9k6GJ80Eq96vPFyju1A+5JVT/piL1dC2B0yPeEWGRM 1gGIsfspJgUU7CM0khH9js4nW2L/1Q6r66uWrD/JG9HEUUr0nHjhwETXt2dG3DXISlaI /xoe11CLBfxHKeZsZe4bzJnJFAgYdtHwMeQ04MFdbabB9yTvogCarYoiZJV/HIvCvsDb /ETzlfPrbGfy2lTaxtuXzgF3W2jDhgo2nSxCzb1m5CxckI3ZL3eOal8XkwFcFzCxaCrr Ekfg== 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=nGNSwR9lXzWCOlZzO4TAYP7aVH53/nfVhy726nYS22Y=; b=OHK/suqUyXzY0xGxgC1HiXJ2EUnSWPV5CmAkc+bbzpaglBvotsU/xM94lCbOsWu+Tp ul/eBVqBwlbRyaSulfdUk0No5nzwa7fIUoZibwuTvkPubLJT7y8d6bNoauQowKb/BfDW nXY2qL2Wawfy7FRJo8CZxQaEDolH1PHulRS7hucXu+f53xrVpaf/0hhF9UvIoYBtlM8d gEQECFVPjx5UQA6uxACQap45kssgosbQegKcq12uXCkWj3gnUZyWKier19B+0NwmPjmF 0zFvjJRo04pPJL09dxEmmBxK9NHOImwMZW859W1I7LAGFDbCGkIaRWbYPdX5cymVMZU6 8C9Q== X-Gm-Message-State: AOAM530Nh5RjX12YG0BQz1OdPJRT868x4ArxSpNnc0aaH7XnFu7CqTNB aPvnM507fkwGk1aSzqPE+J4ZzApp/hZHSM+gIkuo7I+zztDaIpmkcpTup8lp1VqlRng6b+FRReU oxcwNG2eBlrFi9TaEsSnCUWU7Cj5+p3U7cVoXf9tN1VO5zPrpC9Mi/7x9jv6QVeiUCzaCGPPG5p eeqHSovntrLSyByJ53lAQaRwQcTt41mRGxpoo0 X-Google-Smtp-Source: ABdhPJwh2+nBiEpV7qrSvGu7LH+b/2Qk+5w8l43WZ3rrBcAIpwsVKQFrHyvt8Iz/zbueThiVh018xQ== X-Received: by 2002:a63:f912:: with SMTP id h18mr12625174pgi.287.1615567307408; Fri, 12 Mar 2021 08:41:47 -0800 (PST) Received: from laptop280-ma-us.hitronhub.home (24-113-193-150.wavecable.com. [24.113.193.150]) by smtp.gmail.com with ESMTPSA id x190sm5808656pfx.60.2021.03.12.08.41.45 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 12 Mar 2021 08:41:46 -0800 (PST) From: Mark Dilger Message-Id: Content-Type: multipart/mixed; boundary="Apple-Mail=_B8784A81-E7AA-433D-A050-825D9AC0B82D" Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.1\)) Subject: Re: pg_amcheck contrib application Date: Fri, 12 Mar 2021 08:41:44 -0800 In-Reply-To: Cc: Peter Geoghegan , Thomas Munro , Tom Lane , "Andrey M. Borodin" , Stephen Frost , Michael Paquier , Amul Sul , Dilip Kumar , Andres Freund , PostgreSQL-development To: Robert Haas References: <130B2AED-8322-47E6-AD3F-1263E5E18C80@enterprisedb.com> <53F089E8-F0E5-4FC9-A9DE-3D6377FA39C4@enterprisedb.com> X-Mailer: Apple Mail (2.3608.120.23.2.1) 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: Archived-At: Precedence: bulk --Apple-Mail=_B8784A81-E7AA-433D-A050-825D9AC0B82D Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii > On Mar 12, 2021, at 5:33 AM, Robert Haas = wrote: >=20 > Well, OK. In that case I guess we need to patch the docs a little > more. Here's a patch documentation that revised behavior, and also > tidying up a few other things I noticed along the way. >=20 > Since nobody is saying we *shouldn't* move this to src/bin, I think > you may as well go put it there per Peter's suggestion. >=20 > Then I think it's time to get this committed and move on to the next = thing. In this next patch, your documentation patch has been applied, and the = whole project has been relocated from contrib/pg_amcheck to = src/bin/pg_amcheck. --Apple-Mail=_B8784A81-E7AA-433D-A050-825D9AC0B82D Content-Disposition: attachment; filename=v47-0001-Adding-frontend-utility-program-pg_amcheck.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v47-0001-Adding-frontend-utility-program-pg_amcheck.patch" Content-Transfer-Encoding: quoted-printable =46rom=20bf0f4ae87ccbdff8d7e268a62547cb3d50870240=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20Mark=20Dilger=20=0A= Date:=20Fri,=2012=20Mar=202021=2008:24:49=20-0800=0ASubject:=20[PATCH=20= v47]=20Adding=20frontend=20utility=20program=20pg_amcheck=0A=0AAdding=20= new=20utility=20pg_amcheck,=20which=20is=20a=20command=20line=20= interface=20for=0Arunning=20contrib/amcheck's=20verifications=20against=20= tables=20and=20indexes.=0A---=0A=20doc/src/sgml/ref/allfiles.sgml=20=20=20= =20=20=20=20=20=20=20=20=20=20|=20=20=20=201=20+=0A=20= doc/src/sgml/ref/pg_amcheck.sgml=20=20=20=20=20=20=20=20=20=20=20|=20=20= 609=20++++++=0A=20doc/src/sgml/reference.sgml=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=20=20=201=20+=0A=20src/bin/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=20= =20=201=20+=0A=20src/bin/pg_amcheck/.gitignore=20=20=20=20=20=20=20=20=20= =20=20=20=20=20|=20=20=20=203=20+=0A=20src/bin/pg_amcheck/Makefile=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=2051=20+=0A=20= src/bin/pg_amcheck/pg_amcheck.c=20=20=20=20=20=20=20=20=20=20=20=20|=20= 2134=20++++++++++++++++++++=0A=20src/bin/pg_amcheck/t/001_basic.pl=20=20=20= =20=20=20=20=20=20=20|=20=20=20=209=20+=0A=20= src/bin/pg_amcheck/t/002_nonesuch.pl=20=20=20=20=20=20=20|=20=20248=20= +++=0A=20src/bin/pg_amcheck/t/003_check.pl=20=20=20=20=20=20=20=20=20=20= |=20=20504=20+++++=0A=20src/bin/pg_amcheck/t/004_verify_heapam.pl=20=20|=20= =20516=20+++++=0A=20src/bin/pg_amcheck/t/005_opclass_damage.pl=20|=20=20=20= 54=20+=0A=20src/tools/msvc/Install.pm=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=20=2012=20+-=0A=20src/tools/msvc/Mkvcbuild.pm=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20=20=2026=20+-=0A=2014=20= files=20changed,=204155=20insertions(+),=2014=20deletions(-)=0A=20create=20= mode=20100644=20doc/src/sgml/ref/pg_amcheck.sgml=0A=20create=20mode=20= 100644=20src/bin/pg_amcheck/.gitignore=0A=20create=20mode=20100644=20= src/bin/pg_amcheck/Makefile=0A=20create=20mode=20100644=20= src/bin/pg_amcheck/pg_amcheck.c=0A=20create=20mode=20100644=20= src/bin/pg_amcheck/t/001_basic.pl=0A=20create=20mode=20100644=20= src/bin/pg_amcheck/t/002_nonesuch.pl=0A=20create=20mode=20100644=20= src/bin/pg_amcheck/t/003_check.pl=0A=20create=20mode=20100644=20= src/bin/pg_amcheck/t/004_verify_heapam.pl=0A=20create=20mode=20100644=20= src/bin/pg_amcheck/t/005_opclass_damage.pl=0A=0Adiff=20--git=20= a/doc/src/sgml/ref/allfiles.sgml=20b/doc/src/sgml/ref/allfiles.sgml=0A= index=20bee7d28928..202711c005=20100644=0A---=20= a/doc/src/sgml/ref/allfiles.sgml=0A+++=20= b/doc/src/sgml/ref/allfiles.sgml=0A@@=20-196,6=20+196,7=20@@=20Complete=20= list=20of=20usable=20sgml=20source=20files=20in=20this=20directory.=0A=20= =0A=20=0A=20=0A+=0A=20=0A=20=0A=20= =0Adiff=20--git=20a/doc/src/sgml/ref/pg_amcheck.sgml=20= b/doc/src/sgml/ref/pg_amcheck.sgml=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..fcc96b430a=0A---=20/dev/null=0A+++=20= b/doc/src/sgml/ref/pg_amcheck.sgml=0A@@=20-0,0=20+1,609=20@@=0A+=0A= +=0A+=0A+=20=0A+=20=20pg_amcheck=0A+=20= =0A+=0A+=20=0A+=20=20= pg_amcheck=0A+=20= =201=0A+=20=20= Application=0A+=20=0A+=0A+=20= =0A+=20=20pg_amcheck=0A+=20=20= checks=20for=20corruption=20in=20one=20or=20more=0A+=20=20= PostgreSQL=20databases=0A+=20= =0A+=0A+=20=0A+=20=20=0A+=20=20= =20pg_amcheck=0A+=20=20=20option=0A+=20=20=20= dbname=0A+=20=20=0A+=20= =0A+=0A+=20=0A+=20=20= Description=0A+=0A+=20=20=0A+=20=20=20= pg_amcheck=20supports=20running=0A+=20=20=20= 's=20corruption=20checking=20functions=20= against=20one=20or=0A+=20=20=20more=20databases,=20with=20options=20to=20= select=20which=20schemas,=20tables=20and=20indexes=20to=0A+=20=20=20= check,=20which=20kinds=20of=20checking=20to=20perform,=20and=20whether=20= to=20perform=20the=20checks=0A+=20=20=20in=20parallel,=20and=20if=20so,=20= the=20number=20of=20parallel=20connections=20to=20establish=20and=0A+=20=20= =20use.=0A+=20=20=0A+=0A+=20=20=0A+=20=20=20Only=20table=20= relations=20and=20btree=20indexes=20are=20currently=20supported.=20=20= Other=0A+=20=20=20relation=20types=20are=20silently=20skipped.=0A+=20=20= =0A+=0A+=20=20=0A+=20=20=20If=20dbname=20= is=20specified,=20it=20should=20be=20the=20name=20of=20a=0A+=20=20=20= single=20database=20to=20check,=20and=20no=20other=20database=20= selection=20options=20should=0A+=20=20=20be=20present.=20Otherwise,=20if=20= any=20database=20selection=20options=20are=20present,=0A+=20=20=20all=20= matching=20databases=20will=20be=20checked.=20If=20no=20such=20options=20= are=20present,=0A+=20=20=20the=20default=20database=20will=20be=20= checked.=20Database=20selection=20options=20include=0A+=20=20=20= ,=20=20and=0A+=20=20=20= .=20They=20also=20include=0A+=20=20=20= ,=20,=0A+=20= =20=20,=20,=0A+=20= =20=20,=20and=20= ,=0A+=20=20=20but=20only=20when=20such=20= options=20are=20used=20with=20a=20three-part=20pattern=0A+=20=20=20(e.g.=20= ).=20=20Finally,=20they=20include=0A= +=20=20=20=20and=20= =0A+=20=20=20when=20such=20options=20= are=20used=20with=20a=20two-part=20pattern=0A+=20=20=20(e.g.=20= ).=0A+=20=20=0A+=0A+=20=20=0A= +=20=20=20dbname=20can=20also=20be=20a=0A+=20=20= =20connection=20string.=0A+=20= =20=0A+=20=0A+=0A+=20=0A+=20=20= Options=0A+=0A+=20=20=0A+=20=20=20pg_amcheck=20= accepts=20the=20following=20command-line=20arguments:=0A+=0A+=20=20=20= =0A+=20=20=20=20=0A+=20=20=20=20=20= =0A+=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=20Check=20all=20= databases,=20except=20for=20any=20excluded=20via=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+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20Check=20= databases=20matching=20the=20specified=0A+=20=20=20=20=20=20=20pattern,=0A+=20=20=20=20=20=20=20= except=20for=20any=20excluded=20by=20= .=0A+=20=20=20=20=20=20=20This=20= option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=0A+=20=20=20=20=0A+=0A= +=20=20=20=20=0A+=20=20=20=20=20=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=20= Exclude=20databases=20matching=20the=20given=0A+=20=20=20=20=20=20=20= pattern.=0A+=20=20=20=20=20=20=20= This=20option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20= =20=20=0A+=20=20=20=20=20=0A+=20=20=20=20= =0A+=0A+=20=20=20=20=0A+=20=20=20=20=20= =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=20Echo=20to=20stdout=20all=20SQL=20= sent=20to=20the=20server.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =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=20End=20= checking=20at=20the=20specified=20block=20number.=20=20An=20error=20will=20= occur=20if=20the=0A+=20=20=20=20=20=20=20table=20relation=20being=20= checked=20has=20fewer=20than=20this=20number=20of=20blocks.=0A+=20=20=20=20= =20=20=20This=20option=20does=20not=20apply=20to=20indexes,=20and=20is=20= probably=20only=20useful=20when=0A+=20=20=20=20=20=20=20checking=20a=20= single=20table=20relation.=20If=20both=20a=20regular=20table=20and=20a=20= toast=0A+=20=20=20=20=20=20=20table=20are=20checked,=20this=20option=20= will=20apply=20to=20both,=20but=20higher-numbered=0A+=20=20=20=20=20=20=20= toast=20blocks=20may=20still=20be=20accessed=20while=20validating=20= toast=20pointers,=0A+=20=20=20=20=20=20=20unless=20that=20is=20= suppressed=20using=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+=0A+=20=20=20= =20=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=20By=20= default,=20whenever=20a=20toast=20pointer=20is=20encountered=20in=20a=20= table,=0A+=20=20=20=20=20=20=20a=20lookup=20is=20performed=20to=20ensure=20= that=20it=20references=20apparently-valid=0A+=20=20=20=20=20=20=20= entries=20in=20the=20toast=20table.=20These=20checks=20can=20be=20quite=20= slow,=20and=20this=0A+=20=20=20=20=20=20=20option=20can=20be=20used=20to=20= skip=20them.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=0A= +=20=20=20=20=0A+=0A+=20=20=20=20=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=20For=20= each=20index=20checked,=20verify=20the=20presence=20of=20all=20heap=20= tuples=20as=20index=0A+=20=20=20=20=20=20=20tuples=20in=20the=20index=20= using=20's=0A+=20=20=20=20=20=20=20= =20option.=0A+=20=20=20=20=20=20=0A= +=20=20=20=20=20=0A+=20=20=20=20=0A+=0A+=20=20=20= =20=0A+=20=20=20=20=20=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=20Show=20= help=20about=20pg_amcheck=20command=20line=0A= +=20=20=20=20=20=20=20arguments,=20and=20exit.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=0A+=20=20=20=20=0A+=0A= +=20=20=20=20=0A+=20=20=20=20=20=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=20= Specifies=20the=20host=20name=20of=20the=20machine=20on=20which=20the=20= server=20is=20running.=0A+=20=20=20=20=20=20=20If=20the=20value=20begins=20= with=20a=20slash,=20it=20is=20used=20as=20the=20directory=20for=20the=0A= +=20=20=20=20=20=20=20Unix=20domain=20socket.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=0A+=20=20=20=20=0A+=0A= +=20=20=20=20=0A+=20=20=20=20=20=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=20Check=20= indexes=20matching=20the=20specified=0A+=20=20=20=20=20=20=20pattern,=0A+=20=20=20=20=20=20=20= unless=20they=20are=20otherwise=20excluded.=0A+=20=20=20=20=20=20=20This=20= option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20This=20is=20= similar=20to=20the=20=20option,=20except=20= that=0A+=20=20=20=20=20=20=20it=20applies=20only=20to=20indexes,=20not=20= tables.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=0A+=20= =20=20=20=0A+=0A+=20=20=20=20=0A+=20=20=20=20= =20=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=20= Exclude=20indexes=20matching=20the=20specified=0A+=20=20=20=20=20=20=20= pattern.=0A+=20=20=20=20=20=20=20= This=20option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20= =20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20This=20= is=20similar=20to=20the=20=20option,=0A= +=20=20=20=20=20=20=20except=20that=20it=20applies=20only=20to=20= indexes,=20not=20tables.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20Use=20= num=20concurrent=20connections=20to=20the=20= server,=0A+=20=20=20=20=20=20=20or=20one=20per=20object=20to=20be=20= checked,=20whichever=20is=20less.=0A+=20=20=20=20=20=20=0A+=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=20The=20default=20is=20to=20use=20= a=20single=20connection.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =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=20Specifies=20= a=20database=20or=0A+=20=20=20=20=20=20=20connection=20string=20to=20be=0A+=20=20= =20=20=20=20=20used=20to=20discover=20the=20list=20of=20databases=20to=20= be=20checked.=20If=20neither=0A+=20=20=20=20=20=20=20= =20nor=20any=20option=20including=20a=20database=20= pattern=20is=0A+=20=20=20=20=20=20=20used,=20no=20such=20connection=20is=20= required=20and=20this=20option=20does=20nothing.=0A+=20=20=20=20=20=20=20= Otherwise,=20any=20connection=20string=20parameters=20other=20than=0A+=20= =20=20=20=20=20=20the=20database=20name=20which=20are=20included=20in=20= the=20value=20for=20this=20option=0A+=20=20=20=20=20=20=20will=20also=20= be=20used=20when=20connecting=20to=20the=20databases=0A+=20=20=20=20=20=20= =20being=20checked.=20If=20this=20option=20is=20omitted,=20the=20default=20= is=0A+=20=20=20=20=20=20=20postgres=20or,=20if=20that=20= fails,=0A+=20=20=20=20=20=20=20template1.=0A+=20=20=20= =20=20=20=0A+=20=20=20=20=20=0A+=20=20=20=20= =0A+=0A+=20=20=20=20=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=20By=20= default,=20if=20a=20table=20is=20checked,=20any=20btree=20indexes=20of=20= that=20table=0A+=20=20=20=20=20=20=20will=20also=20be=20checked,=20even=20= if=20they=20are=20not=20explicitly=20selected=20by=0A+=20=20=20=20=20=20=20= an=20option=20such=20as=20--index=20or=0A+=20=20=20=20= =20=20=20--relation.=20This=20option=20suppresses=20= that=20behavior.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =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=20By=20= default,=20if=20an=20argument=20to=20--database,=0A+=20= =20=20=20=20=20=20--table,=20= --index,=0A+=20=20=20=20=20=20=20or=20= --relation=20matches=20no=20objects,=20it=20is=20a=20= fatal=0A+=20=20=20=20=20=20=20error.=20This=20option=20downgrades=20that=20= error=20to=20a=20warning.=0A+=20=20=20=20=20=20=20If=20this=20option=20= is=20used=20with=20--quiet,=20the=20warning=0A+=20=20=20= =20=20=20=20will=20be=20supressed=20as=20well.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=0A+=20=20=20=20=0A+=0A= +=20=20=20=20=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=20By=20= default,=20if=20a=20table=20is=20checked,=20its=20toast=20table,=20if=20= any,=20will=20also=0A+=20=20=20=20=20=20=20be=20checked,=20even=20if=20= it=20is=20not=20explicitly=20selected=20by=20an=20option=0A+=20=20=20=20=20= =20=20such=20as=20--table=20or=20= --relation.=0A+=20=20=20=20=20=20=20This=20option=20= suppresses=20that=20behavior.=0A+=20=20=20=20=20=20=0A+=20=20=20=20= =20=0A+=20=20=20=20=0A+=0A+=20=20=20=20= =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=20After=20= reporting=20all=20corruptions=20on=20the=20first=20page=20of=20a=20table=20= where=0A+=20=20=20=20=20=20=20corruption=20is=20found,=20stop=20= processing=20that=20table=20relation=20and=20move=20on=0A+=20=20=20=20=20= =20=20to=20the=20next=20table=20or=20index.=0A+=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20Note=20that=20index=20= checking=20always=20stops=20after=20the=20first=20corrupt=20page.=0A+=20=20= =20=20=20=20=20This=20option=20only=20has=20meaning=20relative=20to=20= table=20relations.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =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=20For=20= each=20btree=20index=20checked,=20use=20's=0A= +=20=20=20=20=20=20=20bt_index_parent_check=20= function,=20which=20performs=0A+=20=20=20=20=20=20=20additional=20checks=20= of=20parent/child=20relationships=20during=20index=20checking.=0A+=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20The=20= default=20is=20to=20use=20amcheck's=0A+=20=20=20= =20=20=20=20bt_index_check=20function,=20but=20note=20= that=20use=20of=20the=0A+=20=20=20=20=20=20=20= =20option=20implicitly=20selects=0A+=20=20=20= =20=20=20=20bt_index_parent_check.=0A+=20=20=20=20=20= =20=0A+=20=20=20=20=20=0A+=20=20=20=20=0A= +=0A+=20=20=20=20=0A+=20=20=20=20=20=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=20Specifies=20= the=20TCP=20port=20or=20local=20Unix=20domain=20socket=20file=20= extension=20on=0A+=20=20=20=20=20=20=20which=20the=20server=20is=20= listening=20for=20connections.=0A+=20=20=20=20=20=20=0A+=20=20=20=20= =20=0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20Show=20= progress=20information.=20Progress=20information=20includes=20the=20= number=0A+=20=20=20=20=20=20=20of=20relations=20for=20which=20checking=20= has=20been=20completed,=20and=20the=20total=0A+=20=20=20=20=20=20=20size=20= of=20those=20relations.=20It=20also=20includes=20the=20total=20number=20= of=20relations=0A+=20=20=20=20=20=20=20that=20will=20eventually=20be=20= checked,=20and=20the=20estimated=20size=20of=20those=0A+=20=20=20=20=20=20= =20relations.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=0A= +=20=20=20=20=0A+=0A+=20=20=20=20=0A+=20=20=20= =20=20=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=20Print=20fewer=20messages,=20= and=20less=20detail=20regarding=20any=20server=20errors.=0A+=20=20=20=20=20= =20=0A+=20=20=20=20=20=0A+=20=20=20=20=0A= +=0A+=20=20=20=20=0A+=20=20=20=20=20=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=20Check=20= relations=20matching=20the=20specified=0A+=20=20=20=20=20=20=20pattern,=0A+=20=20=20=20=20=20=20= unless=20they=20are=20otherwise=20excluded.=0A+=20=20=20=20=20=20=20This=20= option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20Patterns=20= may=20be=20unqualified,=20e.g.=20myrel*,=20or=20they=0A= +=20=20=20=20=20=20=20may=20be=20schema-qualified,=20e.g.=20= myschema*.myrel*=20or=0A+=20=20=20=20=20=20=20= database-qualified=20and=20schema-qualified,=20e.g.=0A+=20=20=20=20=20=20= =20mydb*.myscheam*.myrel*.=20A=20database-qualified=0A= +=20=20=20=20=20=20=20pattern=20will=20add=20matching=20databases=20to=20= the=20list=20of=20databases=20to=20be=0A+=20=20=20=20=20=20=20checked.=0A= +=20=20=20=20=20=20=0A+=20=20=20=20=20=0A+=20=20=20=20= =0A+=0A+=20=20=20=20=0A+=20=20=20=20=20= =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=20= Exclude=20relations=20matching=20the=20specified=0A+=20=20=20=20=20=20=20= pattern.=0A+=20=20=20=20=20=20=20= This=20option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20= =20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20As=20= with=20,=20the=0A+=20=20=20=20=20=20=20pattern=20may=20be=20= unqualified,=20schema-qualified,=0A+=20=20=20=20=20=20=20or=20database-=20= and=20schema-qualified.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =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=20For=20each=20index=20= checked,=20re-find=20tuples=20on=20the=20leaf=20level=20by=20performing=20= a=0A+=20=20=20=20=20=20=20new=20search=20from=20the=20root=20page=20for=20= each=20tuple=20using=0A+=20=20=20=20=20=20=20's=20=20option.=0A+=20=20= =20=20=20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= Use=20of=20this=20option=20implicitly=20also=20selects=20the=0A+=20=20=20= =20=20=20=20=20option.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20This=20form=20= of=20verification=20was=20originally=20written=20to=20help=20in=20the=0A= +=20=20=20=20=20=20=20development=20of=20btree=20index=20features.=20=20= It=20may=20be=20of=20limited=20use=20or=20even=0A+=20=20=20=20=20=20=20= of=20no=20use=20in=20helping=20detect=20the=20kinds=20of=20corruption=20= that=20occur=20in=0A+=20=20=20=20=20=20=20practice.=20=20It=20may=20also=20= cause=20corruption=20checking=20to=20take=20considerably=0A+=20=20=20=20=20= =20=20longer=20and=20consume=20considerably=20more=20resources=20on=20= the=20server.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=0A= +=20=20=20=20=0A+=0A+=20=20=20=20=0A+=20=20=20= =20=20=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=20Check=20= tables=20and=20indexes=20in=20schemas=20matching=20the=20specified=0A+=20= =20=20=20=20=20=20pattern,=20unless=20they=20are=20= otherwise=20excluded.=0A+=20=20=20=20=20=20=20This=20option=20can=20be=20= specified=20more=20than=20once.=0A+=20=20=20=20=20=20=0A+=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=20To=20select=20only=20tables=20in=20= schemas=20matching=20a=20particular=20pattern,=0A+=20=20=20=20=20=20=20= consider=20using=20something=20like=0A+=20=20=20=20=20=20=20= --table=3DSCHEMAPAT.*=20--no-dependent-indexes.=0A+=20= =20=20=20=20=20=20To=20select=20only=20indexes,=20consider=20using=20= something=20like=0A+=20=20=20=20=20=20=20= --index=3DSCHEMAPAT.*.=0A+=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20A=20schema=20pattern=20= may=20be=20database-qualified.=20For=20example,=20you=20may=0A+=20=20=20=20= =20=20=20write=20--schema=3Dmydb*.myschema*=20to=20= select=0A+=20=20=20=20=20=20=20schemas=20matching=20= myschema*=20in=20databases=20matching=0A+=20=20=20=20=20= =20=20mydb*.=0A+=20=20=20=20=20=20=0A+=20=20=20= =20=20=0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20= Exclude=20tables=20and=20indexes=20in=20schemas=20matching=20the=20= specified=0A+=20=20=20=20=20=20=20pattern.=0A+=20=20=20=20=20=20=20= This=20option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20= =20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20As=20= with=20,=20the=20pattern=20may=20be=0A+=20=20=20= =20=20=20=20database-qualified.=0A+=20=20=20=20=20=20=0A+=20=20=20= =20=20=0A+=20=20=20=20=0A+=0A+=20=20=20=20= =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=20If=20= "all-frozen"=20is=20given,=20table=20corruption=20= checks=0A+=20=20=20=20=20=20=20will=20skip=20over=20pages=20in=20all=20= tables=20that=20are=20marked=20as=20all=20frozen.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20If=20= all-visible=20is=20given,=20table=20corruption=20= checks=0A+=20=20=20=20=20=20=20will=20skip=20over=20pages=20in=20all=20= tables=20that=20are=20marked=20as=20all=20visible.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20By=20= default,=20no=20pages=20are=20skipped.=20=20This=20can=20be=20specified=20= as=0A+=20=20=20=20=20=20=20none,=20but=20since=20this=20= is=20the=20default,=20it=20need=20not=20be=0A+=20=20=20=20=20=20=20= mentioned.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=0A= +=20=20=20=20=0A+=0A+=20=20=20=20=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=20Start=20= checking=20at=20the=20specified=20block=20number.=20An=20error=20will=20= occur=20if=0A+=20=20=20=20=20=20=20the=20table=20relation=20being=20= checked=20has=20fewer=20than=20this=20number=20of=20blocks.=0A+=20=20=20=20= =20=20=20This=20option=20does=20not=20apply=20to=20indexes,=20and=20is=20= probably=20only=20useful=0A+=20=20=20=20=20=20=20when=20checking=20a=20= single=20table=20relation.=20See=20--endblock=0A+=20=20= =20=20=20=20=20for=20further=20caveats.=0A+=20=20=20=20=20=20=0A+=20= =20=20=20=20=0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20Check=20= tables=20matching=20the=20specified=0A+=20=20=20=20=20=20=20pattern,=0A+=20=20=20=20=20=20=20= unless=20they=20are=20otherwise=20excluded.=0A+=20=20=20=20=20=20=20This=20= option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20This=20is=20= similar=20to=20the=20=20option,=20except=20= that=0A+=20=20=20=20=20=20=20it=20applies=20only=20to=20tables,=20not=20= indexes.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=0A+=20= =20=20=20=0A+=0A+=20=20=20=20=0A+=20=20=20=20= =20=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=20= Exclude=20tables=20matching=20the=20specified=0A+=20=20=20=20=20=20=20= pattern.=0A+=20=20=20=20=20=20=20= This=20option=20can=20be=20specified=20more=20than=20once.=0A+=20=20=20=20= =20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20This=20= is=20similar=20to=20the=20=20option,=0A= +=20=20=20=20=20=20=20except=20that=20it=20applies=20only=20to=20tables,=20= not=20indexes.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20User=20= name=20to=20connect=20as.=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20= =0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20Print=20= more=20messages.=20In=20particular,=20this=20will=20print=20a=20message=20= for=0A+=20=20=20=20=20=20=20each=20relation=20being=20checked,=20and=20= will=20increase=20the=20level=20of=20detail=0A+=20=20=20=20=20=20=20= shown=20for=20server=20errors.=0A+=20=20=20=20=20=20=0A+=20=20=20=20= =20=0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20Print=20= the=20pg_amcheck=20version=20and=20exit.=0A+=20= =20=20=20=20=20=0A+=20=20=20=20=20=0A+=20=20=20=20= =0A+=0A+=20=20=20=20=0A+=20=20=20=20=20= =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=20Never=20issue=20a=20= password=20prompt.=20=20If=20the=20server=20requires=20password=0A+=20=20= =20=20=20=20=20authentication=20and=20a=20password=20is=20not=20= available=20by=20other=20means=20such=20as=0A+=20=20=20=20=20=20=20a=20= .pgpass=20file,=20the=20connection=20attempt=20will=20= fail.=0A+=20=20=20=20=20=20=20This=20option=20can=20be=20useful=20in=20= batch=20jobs=20and=20scripts=20where=20no=20user=20is=0A+=20=20=20=20=20=20= =20present=20to=20enter=20a=20password.=0A+=20=20=20=20=20=20=0A+=20= =20=20=20=20=0A+=20=20=20=20=0A+=0A+=20=20=20=20= =0A+=20=20=20=20=20=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=20Force=20= pg_amcheck=20to=20prompt=20for=20a=20password=0A= +=20=20=20=20=20=20=20before=20connecting=20to=20a=20database.=0A+=20=20=20= =20=20=20=0A+=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= This=20option=20is=20never=20essential,=20since=0A+=20=20=20=20=20=20=20= pg_amcheck=20will=20automatically=20prompt=20= for=20a=0A+=20=20=20=20=20=20=20password=20if=20the=20server=20demands=20= password=20authentication.=20=20However,=0A+=20=20=20=20=20=20=20= pg_amcheck=20will=20waste=20a=20connection=20= attempt=0A+=20=20=20=20=20=20=20finding=20out=20that=20the=20server=20= wants=20a=20password.=20=20In=20some=20cases=20it=20is=0A+=20=20=20=20=20= =20=20worth=20typing=20=20to=20avoid=20the=20extra=20= connection=20attempt.=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=0A+=20=0A+=0A+=20=0A= +=20=20Notes=0A+=0A+=20=20=0A+=20=20=20= pg_amcheck=20is=20designed=20to=20work=20with=0A= +=20=20=20PostgreSQL=2014.0=20and=20later.=0A= +=20=20=0A+=20=0A+=0A+=20=0A+=20=20= See=20Also=0A+=0A+=20=20=0A= +=20=20=20=0A+=20=20= =0A+=20=0A+=0Adiff=20--git=20= a/doc/src/sgml/reference.sgml=20b/doc/src/sgml/reference.sgml=0Aindex=20= dd2bddab8c..40b0406f1c=20100644=0A---=20a/doc/src/sgml/reference.sgml=0A= +++=20b/doc/src/sgml/reference.sgml=0A@@=20-246,6=20+246,7=20@@=0A=20=20=20= =20&dropdb;=0A=20=20=20=20&dropuser;=0A=20=20=20=20&ecpgRef;=0A+=20=20=20= &pgAMCheck;=0A=20=20=20=20&pgBasebackup;=0A=20=20=20=20&pgbench;=0A=20=20= =20=20&pgConfig;=0Adiff=20--git=20a/src/bin/Makefile=20= b/src/bin/Makefile=0Aindex=20f7573efcd3..2fe0ae6652=20100644=0A---=20= a/src/bin/Makefile=0A+++=20b/src/bin/Makefile=0A@@=20-15,6=20+15,7=20@@=20= include=20$(top_builddir)/src/Makefile.global=0A=20=0A=20SUBDIRS=20=3D=20= \=0A=20=09initdb=20\=0A+=09pg_amcheck=20\=0A=20=09pg_archivecleanup=20\=0A= =20=09pg_basebackup=20\=0A=20=09pg_checksums=20\=0Adiff=20--git=20= a/src/bin/pg_amcheck/.gitignore=20b/src/bin/pg_amcheck/.gitignore=0Anew=20= file=20mode=20100644=0Aindex=200000000000..c21a14de31=0A---=20/dev/null=0A= +++=20b/src/bin/pg_amcheck/.gitignore=0A@@=20-0,0=20+1,3=20@@=0A= +pg_amcheck=0A+=0A+/tmp_check/=0Adiff=20--git=20= a/src/bin/pg_amcheck/Makefile=20b/src/bin/pg_amcheck/Makefile=0Anew=20= file=20mode=20100644=0Aindex=200000000000..6192523f10=0A---=20/dev/null=0A= +++=20b/src/bin/pg_amcheck/Makefile=0A@@=20-0,0=20+1,51=20@@=0A= +#------------------------------------------------------------------------= -=0A+#=0A+#=20Makefile=20for=20src/bin/pg_amcheck=0A+#=0A+#=20Portions=20= Copyright=20(c)=201996-2021,=20PostgreSQL=20Global=20Development=20Group=0A= +#=20Portions=20Copyright=20(c)=201994,=20Regents=20of=20the=20= University=20of=20California=0A+#=0A+#=20src/bin/pg_amcheck/Makefile=0A= +#=0A= +#------------------------------------------------------------------------= -=0A+=0A+PGFILEDESC=20=3D=20"pg_amcheck=20-=20detect=20corruption=20= within=20database=20relations"=0A+PGAPPICON=3Dwin32=0A+=0A= +EXTRA_INSTALL=3Dcontrib/amcheck=20contrib/pageinspect=0A+=0A+subdir=20=3D= =20src/bin/pg_amcheck=0A+top_builddir=20=3D=20../../..=0A+include=20= $(top_builddir)/src/Makefile.global=0A+=0A+override=20CPPFLAGS=20:=3D=20= -I$(libpq_srcdir)=20$(CPPFLAGS)=0A+LDFLAGS_INTERNAL=20+=3D=20= -L$(top_builddir)/src/fe_utils=20-lpgfeutils=20$(libpq_pgport)=0A+=0A= +OBJS=20=3D=20\=0A+=09$(WIN32RES)=20\=0A+=09pg_amcheck.o=0A+=0A+all:=20= pg_amcheck=0A+=0A+pg_amcheck:=20$(OBJS)=20|=20submake-libpq=20= submake-libpgport=20submake-libpgfeutils=0A+=09$(CC)=20$(CFLAGS)=20$^=20= $(LDFLAGS)=20$(LDFLAGS_EX)=20$(LIBS)=20-o=20$@$(X)=0A+=0A+=0A+install:=20= all=20installdirs=0A+=09$(INSTALL_PROGRAM)=20pg_amcheck$(X)=20= '$(DESTDIR)$(bindir)/pg_amcheck$(X)'=0A+=0A+installdirs:=0A+=09= $(MKDIR_P)=20'$(DESTDIR)$(bindir)'=0A+=0A+uninstall:=0A+=09rm=20-f=20= '$(DESTDIR)$(bindir)/pg_amcheck$(X)'=0A+=0A+clean=20distclean=20= maintainer-clean:=0A+=09rm=20-f=20pg_amcheck$(X)=20$(OBJS)=0A+=09rm=20= -rf=20tmp_check=0A+=0A+check:=0A+=09$(prove_check)=0A+=0A+installcheck:=0A= +=09$(prove_installcheck)=0Adiff=20--git=20= a/src/bin/pg_amcheck/pg_amcheck.c=20b/src/bin/pg_amcheck/pg_amcheck.c=0A= new=20file=20mode=20100644=0Aindex=200000000000..59dbf9e9a0=0A---=20= /dev/null=0A+++=20b/src/bin/pg_amcheck/pg_amcheck.c=0A@@=20-0,0=20= +1,2134=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-2021,=20PostgreSQL=20Global=20Development=20Group=0A+=20*=0A+=20*=20= IDENTIFICATION=0A+=20*=09=20=20src/bin/pg_amcheck/pg_amcheck.c=0A+=20*=0A= +=20= *-------------------------------------------------------------------------= =0A+=20*/=0A+#include=20"postgres_fe.h"=0A+=0A+#include=20=0A+=0A= +#include=20"catalog/pg_am_d.h"=0A+#include=20"catalog/pg_namespace_d.h"=0A= +#include=20"common/logging.h"=0A+#include=20"common/username.h"=0A= +#include=20"fe_utils/cancel.h"=0A+#include=20"fe_utils/option_utils.h"=0A= +#include=20"fe_utils/parallel_slot.h"=0A+#include=20= "fe_utils/query_utils.h"=0A+#include=20"fe_utils/simple_list.h"=0A= +#include=20"fe_utils/string_utils.h"=0A+#include=20"getopt_long.h"=09=09= /*=20pgrminclude=20ignore=20*/=0A+#include=20"pgtime.h"=0A+#include=20= "storage/block.h"=0A+=0A+typedef=20struct=20PatternInfo=0A+{=0A+=09const=20= char=20*pattern;=09=09/*=20Unaltered=20pattern=20from=20the=20command=20= line=20*/=0A+=09char=09=20=20=20*db_regex;=09=09/*=20Database=20regexp=20= parsed=20from=20pattern,=20or=0A+=09=09=09=09=09=09=09=09=20*=20NULL=20= */=0A+=09char=09=20=20=20*nsp_regex;=09=09/*=20Schema=20regexp=20parsed=20= from=20pattern,=20or=20NULL=20*/=0A+=09char=09=20=20=20*rel_regex;=09=09= /*=20Relation=20regexp=20parsed=20from=20pattern,=20or=0A+=09=09=09=09=09= =09=09=09=20*=20NULL=20*/=0A+=09bool=09=09heap_only;=09=09/*=20true=20if=20= rel_regex=20should=20only=20match=20heap=0A+=09=09=09=09=09=09=09=09=20*=20= tables=20*/=0A+=09bool=09=09btree_only;=09=09/*=20true=20if=20rel_regex=20= should=20only=20match=20btree=0A+=09=09=09=09=09=09=09=09=20*=20indexes=20= */=0A+=09bool=09=09matched;=09=09/*=20true=20if=20the=20pattern=20= matched=20in=20any=20database=20*/=0A+}=20PatternInfo;=0A+=0A+typedef=20= struct=20PatternInfoArray=0A+{=0A+=09PatternInfo=20*data;=0A+=09size_t=09= =09len;=0A+}=20PatternInfoArray;=0A+=0A+/*=20pg_amcheck=20command=20line=20= options=20controlled=20by=20user=20flags=20*/=0A+typedef=20struct=20= AmcheckOptions=0A+{=0A+=09bool=09=09dbpattern;=0A+=09bool=09=09alldb;=0A= +=09bool=09=09echo;=0A+=09bool=09=09quiet;=0A+=09bool=09=09verbose;=0A+=09= bool=09=09strict_names;=0A+=09bool=09=09show_progress;=0A+=09int=09=09=09= jobs;=0A+=0A+=09/*=20Objects=20to=20check=20or=20not=20to=20check,=20as=20= lists=20of=20PatternInfo=20structs.=20*/=0A+=09PatternInfoArray=20= include;=0A+=09PatternInfoArray=20exclude;=0A+=0A+=09/*=0A+=09=20*=20As=20= an=20optimization,=20if=20any=20pattern=20in=20the=20exclude=20list=20= applies=20to=20heap=0A+=09=20*=20tables,=20or=20similarly=20if=20any=20= such=20pattern=20applies=20to=20btree=20indexes,=20or=0A+=09=20*=20to=20= schemas,=20then=20these=20will=20be=20true,=20otherwise=20false.=20=20= These=20should=0A+=09=20*=20always=20agree=20with=20what=20you'd=20= conclude=20by=20grep'ing=20through=20the=20exclude=0A+=09=20*=20list.=0A= +=09=20*/=0A+=09bool=09=09excludetbl;=0A+=09bool=09=09excludeidx;=0A+=09= bool=09=09excludensp;=0A+=0A+=09/*=0A+=09=20*=20If=20any=20inclusion=20= pattern=20exists,=20then=20we=20should=20only=20be=20checking=0A+=09=20*=20= matching=20relations=20rather=20than=20all=20relations,=20so=20this=20is=20= true=20iff=0A+=09=20*=20include=20is=20empty.=0A+=09=20*/=0A+=09bool=09=09= allrel;=0A+=0A+=09/*=20heap=20table=20checking=20options=20*/=0A+=09bool=09= =09no_toast_expansion;=0A+=09bool=09=09reconcile_toast;=0A+=09bool=09=09= on_error_stop;=0A+=09int64=09=09startblock;=0A+=09int64=09=09endblock;=0A= +=09const=20char=20*skip;=0A+=0A+=09/*=20btree=20index=20checking=20= options=20*/=0A+=09bool=09=09parent_check;=0A+=09bool=09=09rootdescend;=0A= +=09bool=09=09heapallindexed;=0A+=0A+=09/*=20heap=20and=20btree=20hybrid=20= option=20*/=0A+=09bool=09=09no_btree_expansion;=0A+}=20AmcheckOptions;=0A= +=0A+static=20AmcheckOptions=20opts=20=3D=20{=0A+=09.dbpattern=20=3D=20= false,=0A+=09.alldb=20=3D=20false,=0A+=09.echo=20=3D=20false,=0A+=09= .quiet=20=3D=20false,=0A+=09.verbose=20=3D=20false,=0A+=09.strict_names=20= =3D=20true,=0A+=09.show_progress=20=3D=20false,=0A+=09.jobs=20=3D=201,=0A= +=09.include=20=3D=20{NULL,=200},=0A+=09.exclude=20=3D=20{NULL,=200},=0A= +=09.excludetbl=20=3D=20false,=0A+=09.excludeidx=20=3D=20false,=0A+=09= .excludensp=20=3D=20false,=0A+=09.allrel=20=3D=20true,=0A+=09= .no_toast_expansion=20=3D=20false,=0A+=09.reconcile_toast=20=3D=20true,=0A= +=09.on_error_stop=20=3D=20false,=0A+=09.startblock=20=3D=20-1,=0A+=09= .endblock=20=3D=20-1,=0A+=09.skip=20=3D=20"none",=0A+=09.parent_check=20= =3D=20false,=0A+=09.rootdescend=20=3D=20false,=0A+=09.heapallindexed=20=3D= =20false,=0A+=09.no_btree_expansion=20=3D=20false=0A+};=0A+=0A+static=20= const=20char=20*progname=20=3D=20NULL;=0A+=0A+/*=20Whether=20all=20= relations=20have=20so=20far=20passed=20their=20corruption=20checks=20*/=0A= +static=20bool=20all_checks_pass=20=3D=20true;=0A+=0A+/*=20Time=20last=20= progress=20report=20was=20displayed=20*/=0A+static=20pg_time_t=20= last_progress_report=20=3D=200;=0A+static=20bool=20= progress_since_last_stderr=20=3D=20false;=0A+=0A+typedef=20struct=20= DatabaseInfo=0A+{=0A+=09char=09=20=20=20*datname;=0A+=09char=09=20=20=20= *amcheck_schema;=20/*=20escaped,=20quoted=20literal=20*/=0A+}=20= DatabaseInfo;=0A+=0A+typedef=20struct=20RelationInfo=0A+{=0A+=09const=20= DatabaseInfo=20*datinfo;=09/*=20shared=20by=20other=20relinfos=20*/=0A+=09= Oid=09=09=09reloid;=0A+=09bool=09=09is_heap;=09=09/*=20true=20if=20heap,=20= false=20if=20btree=20*/=0A+=09char=09=20=20=20*nspname;=0A+=09char=09=20=20= =20*relname;=0A+=09int=09=09=09relpages;=0A+=09int=09=09=09= blocks_to_check;=0A+=09char=09=20=20=20*sql;=09=09=09/*=20set=20during=20= query=20run,=20pg_free'd=20after=20*/=0A+}=20RelationInfo;=0A+=0A+/*=0A+=20= *=20Query=20for=20determining=20if=20contrib's=20amcheck=20is=20= installed.=20=20If=20so,=20selects=20the=0A+=20*=20namespace=20name=20= where=20amcheck's=20functions=20can=20be=20found.=0A+=20*/=0A+static=20= const=20char=20*amcheck_sql=20=3D=0A+"SELECT=20n.nspname,=20x.extversion=20= FROM=20pg_catalog.pg_extension=20x"=0A+"\nJOIN=20pg_catalog.pg_namespace=20= n=20ON=20x.extnamespace=20=3D=20n.oid"=0A+"\nWHERE=20x.extname=20=3D=20= 'amcheck'";=0A+=0A+static=20void=20prepare_heap_command(PQExpBuffer=20= sql,=20RelationInfo=20*rel,=0A+=09=09=09=09=09=09=09=09=20PGconn=20= *conn);=0A+static=20void=20prepare_btree_command(PQExpBuffer=20sql,=20= RelationInfo=20*rel,=0A+=09=09=09=09=09=09=09=09=20=20PGconn=20*conn);=0A= +static=20void=20run_command(ParallelSlot=20*slot,=20const=20char=20= *sql);=0A+static=20bool=20verify_heap_slot_handler(PGresult=20*res,=20= PGconn=20*conn,=0A+=09=09=09=09=09=09=09=09=09=20void=20*context);=0A= +static=20bool=20verify_btree_slot_handler(PGresult=20*res,=20PGconn=20= *conn,=20void=20*context);=0A+static=20void=20help(const=20char=20= *progname);=0A+static=20void=20progress_report(uint64=20relations_total,=20= uint64=20relations_checked,=0A+=09=09=09=09=09=09=09uint64=20= relpages_total,=20uint64=20relpages_checked,=0A+=09=09=09=09=09=09=09= const=20char=20*datname,=20bool=20force,=20bool=20finished);=0A+=0A= +static=20void=20append_database_pattern(PatternInfoArray=20*pia,=20= const=20char=20*pattern,=0A+=09=09=09=09=09=09=09=09=09int=20encoding);=0A= +static=20void=20append_schema_pattern(PatternInfoArray=20*pia,=20const=20= char=20*pattern,=0A+=09=09=09=09=09=09=09=09=20=20int=20encoding);=0A= +static=20void=20append_relation_pattern(PatternInfoArray=20*pia,=20= const=20char=20*pattern,=0A+=09=09=09=09=09=09=09=09=09int=20encoding);=0A= +static=20void=20append_heap_pattern(PatternInfoArray=20*pia,=20const=20= char=20*pattern,=0A+=09=09=09=09=09=09=09=09int=20encoding);=0A+static=20= void=20append_btree_pattern(PatternInfoArray=20*pia,=20const=20char=20= *pattern,=0A+=09=09=09=09=09=09=09=09=20int=20encoding);=0A+static=20= void=20compile_database_list(PGconn=20*conn,=20SimplePtrList=20= *databases,=0A+=09=09=09=09=09=09=09=09=20=20const=20char=20= *initial_dbname);=0A+static=20void=20compile_relation_list_one_db(PGconn=20= *conn,=20SimplePtrList=20*relations,=0A+=09=09=09=09=09=09=09=09=09=09=20= const=20DatabaseInfo=20*datinfo,=0A+=09=09=09=09=09=09=09=09=09=09=20= uint64=20*pagecount);=0A+=0A+#define=20log_no_match(...)=20do=20{=20\=0A= +=09=09if=20(opts.strict_names)=20\=0A+=09=09=09= pg_log_generic(PG_LOG_ERROR,=20__VA_ARGS__);=20\=0A+=09=09else=20\=0A+=09= =09=09pg_log_generic(PG_LOG_WARNING,=20__VA_ARGS__);=20\=0A+=09}=20= while(0)=0A+=0A+#define=20FREE_AND_SET_NULL(x)=20do=20{=20\=0A+=09= pg_free(x);=20\=0A+=09(x)=20=3D=20NULL;=20\=0A+=09}=20while=20(0)=0A+=0A= +int=0A+main(int=20argc,=20char=20*argv[])=0A+{=0A+=09PGconn=09=20=20=20= *conn=20=3D=20NULL;=0A+=09SimplePtrListCell=20*cell;=0A+=09SimplePtrList=20= databases=20=3D=20{NULL,=20NULL};=0A+=09SimplePtrList=20relations=20=3D=20= {NULL,=20NULL};=0A+=09bool=09=09failed=20=3D=20false;=0A+=09const=20char=20= *latest_datname;=0A+=09int=09=09=09parallel_workers;=0A+=09= ParallelSlotArray=20*sa;=0A+=09PQExpBufferData=20sql;=0A+=09uint64=09=09= reltotal=20=3D=200;=0A+=09uint64=09=09pageschecked=20=3D=200;=0A+=09= uint64=09=09pagestotal=20=3D=200;=0A+=09uint64=09=09relprogress=20=3D=20= 0;=0A+=09int=09=09=09pattern_id;=0A+=0A+=09static=20struct=20option=20= long_options[]=20=3D=20{=0A+=09=09/*=20Connection=20options=20*/=0A+=09=09= {"host",=20required_argument,=20NULL,=20'h'},=0A+=09=09{"port",=20= required_argument,=20NULL,=20'p'},=0A+=09=09{"username",=20= required_argument,=20NULL,=20'U'},=0A+=09=09{"no-password",=20= no_argument,=20NULL,=20'w'},=0A+=09=09{"password",=20no_argument,=20= NULL,=20'W'},=0A+=09=09{"maintenance-db",=20required_argument,=20NULL,=20= 1},=0A+=0A+=09=09/*=20check=20options=20*/=0A+=09=09{"all",=20= no_argument,=20NULL,=20'a'},=0A+=09=09{"database",=20required_argument,=20= NULL,=20'd'},=0A+=09=09{"exclude-database",=20required_argument,=20NULL,=20= 'D'},=0A+=09=09{"echo",=20no_argument,=20NULL,=20'e'},=0A+=09=09= {"index",=20required_argument,=20NULL,=20'i'},=0A+=09=09= {"exclude-index",=20required_argument,=20NULL,=20'I'},=0A+=09=09{"jobs",=20= required_argument,=20NULL,=20'j'},=0A+=09=09{"progress",=20no_argument,=20= NULL,=20'P'},=0A+=09=09{"quiet",=20no_argument,=20NULL,=20'q'},=0A+=09=09= {"relation",=20required_argument,=20NULL,=20'r'},=0A+=09=09= {"exclude-relation",=20required_argument,=20NULL,=20'R'},=0A+=09=09= {"schema",=20required_argument,=20NULL,=20's'},=0A+=09=09= {"exclude-schema",=20required_argument,=20NULL,=20'S'},=0A+=09=09= {"table",=20required_argument,=20NULL,=20't'},=0A+=09=09= {"exclude-table",=20required_argument,=20NULL,=20'T'},=0A+=09=09= {"verbose",=20no_argument,=20NULL,=20'v'},=0A+=09=09= {"no-dependent-indexes",=20no_argument,=20NULL,=202},=0A+=09=09= {"no-dependent-toast",=20no_argument,=20NULL,=203},=0A+=09=09= {"exclude-toast-pointers",=20no_argument,=20NULL,=204},=0A+=09=09= {"on-error-stop",=20no_argument,=20NULL,=205},=0A+=09=09{"skip",=20= required_argument,=20NULL,=206},=0A+=09=09{"startblock",=20= required_argument,=20NULL,=207},=0A+=09=09{"endblock",=20= required_argument,=20NULL,=208},=0A+=09=09{"rootdescend",=20no_argument,=20= NULL,=209},=0A+=09=09{"no-strict-names",=20no_argument,=20NULL,=2010},=0A= +=09=09{"heapallindexed",=20no_argument,=20NULL,=2011},=0A+=09=09= {"parent-check",=20no_argument,=20NULL,=2012},=0A+=0A+=09=09{NULL,=200,=20= NULL,=200}=0A+=09};=0A+=0A+=09int=09=09=09optindex;=0A+=09int=09=09=09c;=0A= +=0A+=09const=20char=20*db=20=3D=20NULL;=0A+=09const=20char=20= *maintenance_db=20=3D=20NULL;=0A+=0A+=09const=20char=20*host=20=3D=20= NULL;=0A+=09const=20char=20*port=20=3D=20NULL;=0A+=09const=20char=20= *username=20=3D=20NULL;=0A+=09enum=20trivalue=20prompt_password=20=3D=20= TRI_DEFAULT;=0A+=09int=09=09=09encoding=20=3D=20= pg_get_encoding_from_locale(NULL,=20false);=0A+=09ConnParams=09cparams;=0A= +=0A+=09pg_logging_init(argv[0]);=0A+=09progname=20=3D=20= get_progname(argv[0]);=0A+=09set_pglocale_pgservice(argv[0],=20= PG_TEXTDOMAIN("pg_amcheck"));=0A+=0A+=09handle_help_version_opts(argc,=20= argv,=20progname,=20help);=0A+=0A+=09/*=20process=20command-line=20= options=20*/=0A+=09while=20((c=20=3D=20getopt_long(argc,=20argv,=20= "ad:D:eh:Hi:I:j:p:Pqr:R:s:S:t:T:U:wWv",=0A+=09=09=09=09=09=09=09= long_options,=20&optindex))=20!=3D=20-1)=0A+=09{=0A+=09=09char=09=20=20=20= *endptr;=0A+=0A+=09=09switch=20(c)=0A+=09=09{=0A+=09=09=09case=20'a':=0A= +=09=09=09=09opts.alldb=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09= case=20'd':=0A+=09=09=09=09opts.dbpattern=20=3D=20true;=0A+=09=09=09=09= append_database_pattern(&opts.include,=20optarg,=20encoding);=0A+=09=09=09= =09break;=0A+=09=09=09case=20'D':=0A+=09=09=09=09opts.dbpattern=20=3D=20= true;=0A+=09=09=09=09append_database_pattern(&opts.exclude,=20optarg,=20= encoding);=0A+=09=09=09=09break;=0A+=09=09=09case=20'e':=0A+=09=09=09=09= opts.echo=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09case=20'h':=0A= +=09=09=09=09host=20=3D=20pg_strdup(optarg);=0A+=09=09=09=09break;=0A+=09= =09=09case=20'i':=0A+=09=09=09=09opts.allrel=20=3D=20false;=0A+=09=09=09=09= append_btree_pattern(&opts.include,=20optarg,=20encoding);=0A+=09=09=09=09= break;=0A+=09=09=09case=20'I':=0A+=09=09=09=09opts.excludeidx=20=3D=20= true;=0A+=09=09=09=09append_btree_pattern(&opts.exclude,=20optarg,=20= encoding);=0A+=09=09=09=09break;=0A+=09=09=09case=20'j':=0A+=09=09=09=09= opts.jobs=20=3D=20atoi(optarg);=0A+=09=09=09=09if=20(opts.jobs=20<=201)=0A= +=09=09=09=09{=0A+=09=09=09=09=09fprintf(stderr,=0A+=09=09=09=09=09=09=09= "number=20of=20parallel=20jobs=20must=20be=20at=20least=201\n");=0A+=09=09= =09=09=09exit(1);=0A+=09=09=09=09}=0A+=09=09=09=09break;=0A+=09=09=09= case=20'p':=0A+=09=09=09=09port=20=3D=20pg_strdup(optarg);=0A+=09=09=09=09= break;=0A+=09=09=09case=20'P':=0A+=09=09=09=09opts.show_progress=20=3D=20= true;=0A+=09=09=09=09break;=0A+=09=09=09case=20'q':=0A+=09=09=09=09= opts.quiet=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09case=20'r':=0A= +=09=09=09=09opts.allrel=20=3D=20false;=0A+=09=09=09=09= append_relation_pattern(&opts.include,=20optarg,=20encoding);=0A+=09=09=09= =09break;=0A+=09=09=09case=20'R':=0A+=09=09=09=09opts.excludeidx=20=3D=20= true;=0A+=09=09=09=09opts.excludetbl=20=3D=20true;=0A+=09=09=09=09= append_relation_pattern(&opts.exclude,=20optarg,=20encoding);=0A+=09=09=09= =09break;=0A+=09=09=09case=20's':=0A+=09=09=09=09opts.allrel=20=3D=20= false;=0A+=09=09=09=09append_schema_pattern(&opts.include,=20optarg,=20= encoding);=0A+=09=09=09=09break;=0A+=09=09=09case=20'S':=0A+=09=09=09=09= opts.excludensp=20=3D=20true;=0A+=09=09=09=09= append_schema_pattern(&opts.exclude,=20optarg,=20encoding);=0A+=09=09=09=09= break;=0A+=09=09=09case=20't':=0A+=09=09=09=09opts.allrel=20=3D=20false;=0A= +=09=09=09=09append_heap_pattern(&opts.include,=20optarg,=20encoding);=0A= +=09=09=09=09break;=0A+=09=09=09case=20'T':=0A+=09=09=09=09= opts.excludetbl=20=3D=20true;=0A+=09=09=09=09= append_heap_pattern(&opts.exclude,=20optarg,=20encoding);=0A+=09=09=09=09= break;=0A+=09=09=09case=20'U':=0A+=09=09=09=09username=20=3D=20= pg_strdup(optarg);=0A+=09=09=09=09break;=0A+=09=09=09case=20'w':=0A+=09=09= =09=09prompt_password=20=3D=20TRI_NO;=0A+=09=09=09=09break;=0A+=09=09=09= case=20'W':=0A+=09=09=09=09prompt_password=20=3D=20TRI_YES;=0A+=09=09=09=09= break;=0A+=09=09=09case=20'v':=0A+=09=09=09=09opts.verbose=20=3D=20true;=0A= +=09=09=09=09pg_logging_increase_verbosity();=0A+=09=09=09=09break;=0A+=09= =09=09case=201:=0A+=09=09=09=09maintenance_db=20=3D=20pg_strdup(optarg);=0A= +=09=09=09=09break;=0A+=09=09=09case=202:=0A+=09=09=09=09= opts.no_btree_expansion=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09= case=203:=0A+=09=09=09=09opts.no_toast_expansion=20=3D=20true;=0A+=09=09=09= =09break;=0A+=09=09=09case=204:=0A+=09=09=09=09opts.reconcile_toast=20=3D=20= false;=0A+=09=09=09=09break;=0A+=09=09=09case=205:=0A+=09=09=09=09= opts.on_error_stop=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09case=20= 6:=0A+=09=09=09=09if=20(pg_strcasecmp(optarg,=20"all-visible")=20=3D=3D=20= 0)=0A+=09=09=09=09=09opts.skip=20=3D=20"all=20visible";=0A+=09=09=09=09= else=20if=20(pg_strcasecmp(optarg,=20"all-frozen")=20=3D=3D=200)=0A+=09=09= =09=09=09opts.skip=20=3D=20"all=20frozen";=0A+=09=09=09=09else=0A+=09=09=09= =09{=0A+=09=09=09=09=09fprintf(stderr,=20"invalid=20skip=20option\n");=0A= +=09=09=09=09=09exit(1);=0A+=09=09=09=09}=0A+=09=09=09=09break;=0A+=09=09= =09case=207:=0A+=09=09=09=09opts.startblock=20=3D=20strtol(optarg,=20= &endptr,=2010);=0A+=09=09=09=09if=20(*endptr=20!=3D=20'\0')=0A+=09=09=09=09= {=0A+=09=09=09=09=09fprintf(stderr,=0A+=09=09=09=09=09=09=09"invalid=20= start=20block\n");=0A+=09=09=09=09=09exit(1);=0A+=09=09=09=09}=0A+=09=09=09= =09if=20(opts.startblock=20>=20MaxBlockNumber=20||=20opts.startblock=20<=20= 0)=0A+=09=09=09=09{=0A+=09=09=09=09=09fprintf(stderr,=0A+=09=09=09=09=09=09= =09"start=20block=20out=20of=20bounds\n");=0A+=09=09=09=09=09exit(1);=0A= +=09=09=09=09}=0A+=09=09=09=09break;=0A+=09=09=09case=208:=0A+=09=09=09=09= opts.endblock=20=3D=20strtol(optarg,=20&endptr,=2010);=0A+=09=09=09=09if=20= (*endptr=20!=3D=20'\0')=0A+=09=09=09=09{=0A+=09=09=09=09=09= fprintf(stderr,=0A+=09=09=09=09=09=09=09"invalid=20end=20block\n");=0A+=09= =09=09=09=09exit(1);=0A+=09=09=09=09}=0A+=09=09=09=09if=20(opts.endblock=20= >=20MaxBlockNumber=20||=20opts.endblock=20<=200)=0A+=09=09=09=09{=0A+=09=09= =09=09=09fprintf(stderr,=0A+=09=09=09=09=09=09=09"end=20block=20out=20of=20= bounds\n");=0A+=09=09=09=09=09exit(1);=0A+=09=09=09=09}=0A+=09=09=09=09= break;=0A+=09=09=09case=209:=0A+=09=09=09=09opts.rootdescend=20=3D=20= true;=0A+=09=09=09=09opts.parent_check=20=3D=20true;=0A+=09=09=09=09= break;=0A+=09=09=09case=2010:=0A+=09=09=09=09opts.strict_names=20=3D=20= false;=0A+=09=09=09=09break;=0A+=09=09=09case=2011:=0A+=09=09=09=09= opts.heapallindexed=20=3D=20true;=0A+=09=09=09=09break;=0A+=09=09=09case=20= 12:=0A+=09=09=09=09opts.parent_check=20=3D=20true;=0A+=09=09=09=09break;=0A= +=09=09=09default:=0A+=09=09=09=09fprintf(stderr,=0A+=09=09=09=09=09=09= "Try=20\"%s=20--help\"=20for=20more=20information.\n",=0A+=09=09=09=09=09= =09progname);=0A+=09=09=09=09exit(1);=0A+=09=09}=0A+=09}=0A+=0A+=09if=20= (opts.endblock=20>=3D=200=20&&=20opts.endblock=20<=20opts.startblock)=0A= +=09{=0A+=09=09fprintf(stderr,=0A+=09=09=09=09"end=20block=20precedes=20= start=20block\n");=0A+=09=09exit(1);=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20A=20= single=20non-option=20arguments=20specifies=20a=20database=20name=20or=20= connection=0A+=09=20*=20string.=0A+=09=20*/=0A+=09if=20(optind=20<=20= argc)=0A+=09{=0A+=09=09db=20=3D=20argv[optind];=0A+=09=09optind++;=0A+=09= }=0A+=0A+=09if=20(optind=20<=20argc)=0A+=09{=0A+=09=09pg_log_error("too=20= many=20command-line=20arguments=20(first=20is=20\"%s\")",=0A+=09=09=09=09= =09=20argv[optind]);=0A+=09=09fprintf(stderr,=20_("Try=20\"%s=20--help\"=20= for=20more=20information.\n"),=20progname);=0A+=09=09exit(1);=0A+=09}=0A= +=0A+=09/*=20fill=20cparams=20except=20for=20dbname,=20which=20is=20set=20= below=20*/=0A+=09cparams.pghost=20=3D=20host;=0A+=09cparams.pgport=20=3D=20= port;=0A+=09cparams.pguser=20=3D=20username;=0A+=09= cparams.prompt_password=20=3D=20prompt_password;=0A+=09cparams.dbname=20= =3D=20NULL;=0A+=09cparams.override_dbname=20=3D=20NULL;=0A+=0A+=09= setup_cancel_handler(NULL);=0A+=0A+=09/*=20choose=20the=20database=20for=20= our=20initial=20connection=20*/=0A+=09if=20(opts.alldb)=0A+=09{=0A+=09=09= if=20(db=20!=3D=20NULL)=0A+=09=09{=0A+=09=09=09pg_log_error("Cannot=20= check=20all=20databases=20and=20a=20specific=20one=20at=20the=20same=20= time");=0A+=09=09=09exit(1);=0A+=09=09}=0A+=09=09cparams.dbname=20=3D=20= maintenance_db;=0A+=09}=0A+=09else=20if=20(db=20!=3D=20NULL)=0A+=09{=0A+=09= =09if=20(opts.dbpattern)=0A+=09=09{=0A+=09=09=09pg_log_error("Cannot=20= check=20a=20specific=20database=20and=20specify=20database=20patterns=20= at=20the=20same=20time");=0A+=09=09=09exit(1);=0A+=09=09}=0A+=09=09= cparams.dbname=20=3D=20db;=0A+=09}=0A+=0A+=09if=20(opts.alldb=20||=20= opts.dbpattern)=0A+=09{=0A+=09=09conn=20=3D=20= connectMaintenanceDatabase(&cparams,=20progname,=20opts.echo);=0A+=09=09= compile_database_list(conn,=20&databases,=20NULL);=0A+=09}=0A+=09else=0A= +=09{=0A+=09=09if=20(cparams.dbname=20=3D=3D=20NULL)=0A+=09=09{=0A+=09=09= =09if=20(getenv("PGDATABASE"))=0A+=09=09=09=09cparams.dbname=20=3D=20= getenv("PGDATABASE");=0A+=09=09=09else=20if=20(getenv("PGUSER"))=0A+=09=09= =09=09cparams.dbname=20=3D=20getenv("PGUSER");=0A+=09=09=09else=0A+=09=09= =09=09cparams.dbname=20=3D=20get_user_name_or_exit(progname);=0A+=09=09}=0A= +=09=09conn=20=3D=20connectDatabase(&cparams,=20progname,=20opts.echo,=20= false,=20true);=0A+=09=09compile_database_list(conn,=20&databases,=20= PQdb(conn));=0A+=09}=0A+=0A+=09if=20(databases.head=20=3D=3D=20NULL)=0A+=09= {=0A+=09=09if=20(conn=20!=3D=20NULL)=0A+=09=09=09= disconnectDatabase(conn);=0A+=09=09pg_log_error("no=20databases=20to=20= check");=0A+=09=09exit(0);=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Compile=20a=20= list=20of=20all=20relations=20spanning=20all=20databases=20to=20be=20= checked.=0A+=09=20*/=0A+=09for=20(cell=20=3D=20databases.head;=20cell;=20= cell=20=3D=20cell->next)=0A+=09{=0A+=09=09PGresult=20=20=20*result;=0A+=09= =09int=09=09=09ntups;=0A+=09=09const=20char=20*amcheck_schema=20=3D=20= NULL;=0A+=09=09DatabaseInfo=20*dat=20=3D=20(DatabaseInfo=20*)=20= cell->ptr;=0A+=0A+=09=09cparams.override_dbname=20=3D=20dat->datname;=0A= +=09=09if=20(conn=20=3D=3D=20NULL=20||=20strcmp(PQdb(conn),=20= dat->datname)=20!=3D=200)=0A+=09=09{=0A+=09=09=09if=20(conn=20!=3D=20= NULL)=0A+=09=09=09=09disconnectDatabase(conn);=0A+=09=09=09conn=20=3D=20= connectDatabase(&cparams,=20progname,=20opts.echo,=20false,=20true);=0A+=09= =09}=0A+=0A+=09=09/*=0A+=09=09=20*=20Verify=20that=20amcheck=20is=20= installed=20for=20this=20next=20database.=20=20User=0A+=09=09=20*=20= error=20could=20result=20in=20a=20database=20not=20having=20amcheck=20= that=20should=0A+=09=09=20*=20have=20it,=20but=20we=20also=20could=20be=20= iterating=20over=20multiple=20databases=0A+=09=09=20*=20where=20not=20= all=20of=20them=20have=20amcheck=20installed=20(for=20example,=0A+=09=09=20= *=20'template1').=0A+=09=09=20*/=0A+=09=09result=20=3D=20= executeQuery(conn,=20amcheck_sql,=20opts.echo);=0A+=09=09if=20= (PQresultStatus(result)=20!=3D=20PGRES_TUPLES_OK)=0A+=09=09{=0A+=09=09=09= /*=20Querying=20the=20catalog=20failed.=20*/=0A+=09=09=09= pg_log_error("database=20\"%s\":=20%s",=0A+=09=09=09=09=09=09=20= PQdb(conn),=20PQerrorMessage(conn));=0A+=09=09=09pg_log_info("query=20= was:=20%s",=20amcheck_sql);=0A+=09=09=09PQclear(result);=0A+=09=09=09= disconnectDatabase(conn);=0A+=09=09=09exit(1);=0A+=09=09}=0A+=09=09ntups=20= =3D=20PQntuples(result);=0A+=09=09if=20(ntups=20=3D=3D=200)=0A+=09=09{=0A= +=09=09=09/*=20Querying=20the=20catalog=20succeeded,=20but=20amcheck=20= is=20missing.=20*/=0A+=09=09=09pg_log_warning("skipping=20database=20= \"%s\":=20amcheck=20is=20not=20installed",=0A+=09=09=09=09=09=09=20=20=20= PQdb(conn));=0A+=09=09=09disconnectDatabase(conn);=0A+=09=09=09conn=20=3D=20= NULL;=0A+=09=09=09continue;=0A+=09=09}=0A+=09=09amcheck_schema=20=3D=20= PQgetvalue(result,=200,=200);=0A+=09=09if=20(opts.verbose)=0A+=09=09=09= pg_log_info("in=20database=20\"%s\":=20using=20amcheck=20version=20= \"%s\"=20in=20schema=20\"%s\"",=0A+=09=09=09=09=09=09PQdb(conn),=20= PQgetvalue(result,=200,=201),=20amcheck_schema);=0A+=09=09= dat->amcheck_schema=20=3D=20PQescapeIdentifier(conn,=20amcheck_schema,=0A= +=09=09=09=09=09=09=09=09=09=09=09=09=20strlen(amcheck_schema));=0A+=09=09= PQclear(result);=0A+=0A+=09=09compile_relation_list_one_db(conn,=20= &relations,=20dat,=20&pagestotal);=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20= Check=20that=20all=20inclusion=20patterns=20matched=20at=20least=20one=20= schema=20or=0A+=09=20*=20relation=20that=20we=20can=20check.=0A+=09=20*/=0A= +=09for=20(pattern_id=20=3D=200;=20pattern_id=20<=20opts.include.len;=20= pattern_id++)=0A+=09{=0A+=09=09PatternInfo=20*pat=20=3D=20= &opts.include.data[pattern_id];=0A+=0A+=09=09if=20(!pat->matched=20&&=20= (pat->nsp_regex=20!=3D=20NULL=20||=20pat->rel_regex=20!=3D=20NULL))=0A+=09= =09{=0A+=09=09=09failed=20=3D=20opts.strict_names;=0A+=0A+=09=09=09if=20= (!opts.quiet=20||=20failed)=0A+=09=09=09{=0A+=09=09=09=09if=20= (pat->heap_only)=0A+=09=09=09=09=09log_no_match("no=20heap=20tables=20to=20= check=20matching=20\"%s\"",=0A+=09=09=09=09=09=09=09=09=20pat->pattern);=0A= +=09=09=09=09else=20if=20(pat->btree_only)=0A+=09=09=09=09=09= log_no_match("no=20btree=20indexes=20to=20check=20matching=20\"%s\"",=0A= +=09=09=09=09=09=09=09=09=20pat->pattern);=0A+=09=09=09=09else=20if=20= (pat->rel_regex=20=3D=3D=20NULL)=0A+=09=09=09=09=09log_no_match("no=20= relations=20to=20check=20in=20schemas=20matching=20\"%s\"",=0A+=09=09=09=09= =09=09=09=09=20pat->pattern);=0A+=09=09=09=09else=0A+=09=09=09=09=09= log_no_match("no=20relations=20to=20check=20matching=20\"%s\"",=0A+=09=09= =09=09=09=09=09=09=20pat->pattern);=0A+=09=09=09}=0A+=09=09}=0A+=09}=0A+=0A= +=09if=20(failed)=0A+=09{=0A+=09=09if=20(conn=20!=3D=20NULL)=0A+=09=09=09= disconnectDatabase(conn);=0A+=09=09exit(1);=0A+=09}=0A+=0A+=09/*=0A+=09=20= *=20Set=20parallel_workers=20to=20the=20lesser=20of=20opts.jobs=20and=20= the=20number=20of=0A+=09=20*=20relations.=0A+=09=20*/=0A+=09= parallel_workers=20=3D=200;=0A+=09for=20(cell=20=3D=20relations.head;=20= cell;=20cell=20=3D=20cell->next)=0A+=09{=0A+=09=09reltotal++;=0A+=09=09= if=20(parallel_workers=20<=20opts.jobs)=0A+=09=09=09parallel_workers++;=0A= +=09}=0A+=0A+=09if=20(reltotal=20=3D=3D=200)=0A+=09{=0A+=09=09if=20(conn=20= !=3D=20NULL)=0A+=09=09=09disconnectDatabase(conn);=0A+=09=09= pg_log_error("no=20relations=20to=20check");=0A+=09=09exit(1);=0A+=09}=0A= +=09progress_report(reltotal,=20relprogress,=20pagestotal,=20= pageschecked,=20NULL,=20true,=20false);=0A+=0A+=09/*=0A+=09=20*=20Main=20= event=20loop.=0A+=09=20*=0A+=09=20*=20We=20use=20server-side=20= parallelism=20to=20check=20up=20to=20parallel_workers=0A+=09=20*=20= relations=20in=20parallel.=20=20The=20list=20of=20relations=20was=20= computed=20in=20database=0A+=09=20*=20order,=20which=20minimizes=20the=20= number=20of=20connects=20and=20disconnects=20as=20we=0A+=09=20*=20= process=20the=20list.=0A+=09=20*/=0A+=09latest_datname=20=3D=20NULL;=0A+=09= sa=20=3D=20ParallelSlotsSetup(parallel_workers,=20&cparams,=20progname,=20= opts.echo,=0A+=09=09=09=09=09=09=09NULL);=0A+=09if=20(conn=20!=3D=20= NULL)=0A+=09{=0A+=09=09ParallelSlotsAdoptConn(sa,=20conn);=0A+=09=09conn=20= =3D=20NULL;=0A+=09}=0A+=0A+=09initPQExpBuffer(&sql);=0A+=09for=20= (relprogress=20=3D=200,=20cell=20=3D=20relations.head;=20cell;=20cell=20= =3D=20cell->next)=0A+=09{=0A+=09=09ParallelSlot=20*free_slot;=0A+=09=09= RelationInfo=20*rel;=0A+=0A+=09=09rel=20=3D=20(RelationInfo=20*)=20= cell->ptr;=0A+=0A+=09=09if=20(CancelRequested)=0A+=09=09{=0A+=09=09=09= failed=20=3D=20true;=0A+=09=09=09break;=0A+=09=09}=0A+=0A+=09=09/*=0A+=09= =09=20*=20The=20list=20of=20relations=20is=20in=20database=20sorted=20= order.=20=20If=20this=20next=0A+=09=09=20*=20relation=20is=20in=20a=20= different=20database=20than=20the=20last=20one=20seen,=20we=20are=0A+=09=09= =20*=20about=20to=20start=20checking=20this=20database.=20=20Note=20that=20= other=20slots=20may=0A+=09=09=20*=20still=20be=20working=20on=20= relations=20from=20prior=20databases.=0A+=09=09=20*/=0A+=09=09= latest_datname=20=3D=20rel->datinfo->datname;=0A+=0A+=09=09= progress_report(reltotal,=20relprogress,=20pagestotal,=20pageschecked,=20= latest_datname,=20false,=20false);=0A+=0A+=09=09relprogress++;=0A+=09=09= pageschecked=20+=3D=20rel->blocks_to_check;=0A+=0A+=09=09/*=0A+=09=09=20= *=20Get=20a=20parallel=20slot=20for=20the=20next=20amcheck=20command,=20= blocking=20if=0A+=09=09=20*=20necessary=20until=20one=20is=20available,=20= or=20until=20a=20previously=20issued=20slot=0A+=09=09=20*=20command=20= fails,=20indicating=20that=20we=20should=20abort=20checking=20the=0A+=09=09= =20*=20remaining=20objects.=0A+=09=09=20*/=0A+=09=09free_slot=20=3D=20= ParallelSlotsGetIdle(sa,=20rel->datinfo->datname);=0A+=09=09if=20= (!free_slot)=0A+=09=09{=0A+=09=09=09/*=0A+=09=09=09=20*=20Something=20= failed.=20=20We=20don't=20need=20to=20know=20what=20it=20was,=20because=0A= +=09=09=09=20*=20the=20handler=20should=20already=20have=20emitted=20the=20= necessary=20error=0A+=09=09=09=20*=20messages.=0A+=09=09=09=20*/=0A+=09=09= =09failed=20=3D=20true;=0A+=09=09=09break;=0A+=09=09}=0A+=0A+=09=09if=20= (opts.verbose)=0A+=09=09=09PQsetErrorVerbosity(free_slot->connection,=20= PQERRORS_VERBOSE);=0A+=09=09else=20if=20(opts.quiet)=0A+=09=09=09= PQsetErrorVerbosity(free_slot->connection,=20PQERRORS_TERSE);=0A+=0A+=09=09= /*=0A+=09=09=20*=20Execute=20the=20appropriate=20amcheck=20command=20for=20= this=20relation=20using=20our=0A+=09=09=20*=20slot's=20database=20= connection.=20=20We=20do=20not=20wait=20for=20the=20command=20to=0A+=09=09= =20*=20complete,=20nor=20do=20we=20perform=20any=20error=20checking,=20= as=20that=20is=20done=20by=0A+=09=09=20*=20the=20parallel=20slots=20and=20= our=20handler=20callback=20functions.=0A+=09=09=20*/=0A+=09=09if=20= (rel->is_heap)=0A+=09=09{=0A+=09=09=09if=20(opts.verbose)=0A+=09=09=09{=0A= +=09=09=09=09if=20(opts.show_progress=20&&=20progress_since_last_stderr)=0A= +=09=09=09=09=09fprintf(stderr,=20"\n");=0A+=09=09=09=09= pg_log_info("checking=20heap=20table=20\"%s\".\"%s\".\"%s\"",=0A+=09=09=09= =09=09=09=09rel->datinfo->datname,=20rel->nspname,=20rel->relname);=0A+=09= =09=09=09progress_since_last_stderr=20=3D=20false;=0A+=09=09=09}=0A+=09=09= =09prepare_heap_command(&sql,=20rel,=20free_slot->connection);=0A+=09=09=09= rel->sql=20=3D=20pstrdup(sql.data);=09/*=20pg_free'd=20after=20command=20= */=0A+=09=09=09ParallelSlotSetHandler(free_slot,=20= verify_heap_slot_handler,=20rel);=0A+=09=09=09run_command(free_slot,=20= rel->sql);=0A+=09=09}=0A+=09=09else=0A+=09=09{=0A+=09=09=09if=20= (opts.verbose)=0A+=09=09=09{=0A+=09=09=09=09if=20(opts.show_progress=20= &&=20progress_since_last_stderr)=0A+=09=09=09=09=09fprintf(stderr,=20= "\n");=0A+=0A+=09=09=09=09pg_log_info("checking=20btree=20index=20= \"%s\".\"%s\".\"%s\"",=0A+=09=09=09=09=09=09=09rel->datinfo->datname,=20= rel->nspname,=20rel->relname);=0A+=09=09=09=09progress_since_last_stderr=20= =3D=20false;=0A+=09=09=09}=0A+=09=09=09prepare_btree_command(&sql,=20= rel,=20free_slot->connection);=0A+=09=09=09rel->sql=20=3D=20= pstrdup(sql.data);=09/*=20pg_free'd=20after=20command=20*/=0A+=09=09=09= ParallelSlotSetHandler(free_slot,=20verify_btree_slot_handler,=20rel);=0A= +=09=09=09run_command(free_slot,=20rel->sql);=0A+=09=09}=0A+=09}=0A+=09= termPQExpBuffer(&sql);=0A+=0A+=09if=20(!failed)=0A+=09{=0A+=0A+=09=09/*=0A= +=09=09=20*=20Wait=20for=20all=20slots=20to=20complete,=20or=20for=20one=20= to=20indicate=20that=20an=0A+=09=09=20*=20error=20occurred.=20=20Like=20= above,=20we=20rely=20on=20the=20handler=20emitting=20the=0A+=09=09=20*=20= necessary=20error=20messages.=0A+=09=09=20*/=0A+=09=09if=20(sa=20&&=20= !ParallelSlotsWaitCompletion(sa))=0A+=09=09=09failed=20=3D=20true;=0A+=0A= +=09=09progress_report(reltotal,=20relprogress,=20pagestotal,=20= pageschecked,=20NULL,=20true,=20true);=0A+=09}=0A+=0A+=09if=20(sa)=0A+=09= {=0A+=09=09ParallelSlotsTerminate(sa);=0A+=09=09FREE_AND_SET_NULL(sa);=0A= +=09}=0A+=0A+=09if=20(failed)=0A+=09=09exit(1);=0A+=0A+=09if=20= (!all_checks_pass)=0A+=09=09exit(2);=0A+}=0A+=0A+/*=0A+=20*=20= prepare_heap_command=0A+=20*=0A+=20*=20Creates=20a=20SQL=20command=20for=20= running=20amcheck=20checking=20on=20the=20given=20heap=0A+=20*=20= relation.=20=20The=20command=20is=20phrased=20as=20a=20SQL=20query,=20= with=20column=20order=20and=0A+=20*=20names=20matching=20the=20= expectations=20of=20verify_heap_slot_handler,=20which=20will=0A+=20*=20= receive=20and=20handle=20each=20row=20returned=20from=20the=20= verify_heapam()=20function.=0A+=20*=0A+=20*=20sql:=20buffer=20into=20= which=20the=20heap=20table=20checking=20command=20will=20be=20written=0A= +=20*=20rel:=20relation=20information=20for=20the=20heap=20table=20to=20= be=20checked=0A+=20*=20conn:=20the=20connection=20to=20be=20used,=20for=20= string=20escaping=20purposes=0A+=20*/=0A+static=20void=0A= +prepare_heap_command(PQExpBuffer=20sql,=20RelationInfo=20*rel,=20PGconn=20= *conn)=0A+{=0A+=09resetPQExpBuffer(sql);=0A+=09appendPQExpBuffer(sql,=0A= +=09=09=09=09=09=20=20"SELECT=20blkno,=20offnum,=20attnum,=20msg=20FROM=20= %s.verify_heapam("=0A+=09=09=09=09=09=20=20"\nrelation=20:=3D=20%u,=20= on_error_stop=20:=3D=20%s,=20check_toast=20:=3D=20%s,=20skip=20:=3D=20= '%s'",=0A+=09=09=09=09=09=20=20rel->datinfo->amcheck_schema,=0A+=09=09=09= =09=09=20=20rel->reloid,=0A+=09=09=09=09=09=20=20opts.on_error_stop=20?=20= "true"=20:=20"false",=0A+=09=09=09=09=09=20=20opts.reconcile_toast=20?=20= "true"=20:=20"false",=0A+=09=09=09=09=09=20=20opts.skip);=0A+=0A+=09if=20= (opts.startblock=20>=3D=200)=0A+=09=09appendPQExpBuffer(sql,=20",=20= startblock=20:=3D=20"=20INT64_FORMAT,=20opts.startblock);=0A+=09if=20= (opts.endblock=20>=3D=200)=0A+=09=09appendPQExpBuffer(sql,=20",=20= endblock=20:=3D=20"=20INT64_FORMAT,=20opts.endblock);=0A+=0A+=09= appendPQExpBuffer(sql,=20")");=0A+}=0A+=0A+/*=0A+=20*=20= prepare_btree_command=0A+=20*=0A+=20*=20Creates=20a=20SQL=20command=20= for=20running=20amcheck=20checking=20on=20the=20given=20btree=20index=0A= +=20*=20relation.=20=20The=20command=20does=20not=20select=20any=20= columns,=20as=20btree=20checking=0A+=20*=20functions=20do=20not=20return=20= any,=20but=20rather=20return=20corruption=20information=20by=0A+=20*=20= raising=20errors,=20which=20verify_btree_slot_handler=20expects.=0A+=20*=0A= +=20*=20sql:=20buffer=20into=20which=20the=20heap=20table=20checking=20= command=20will=20be=20written=0A+=20*=20rel:=20relation=20information=20= for=20the=20index=20to=20be=20checked=0A+=20*=20conn:=20the=20connection=20= to=20be=20used,=20for=20string=20escaping=20purposes=0A+=20*/=0A+static=20= void=0A+prepare_btree_command(PQExpBuffer=20sql,=20RelationInfo=20*rel,=20= PGconn=20*conn)=0A+{=0A+=09resetPQExpBuffer(sql);=0A+=0A+=09/*=0A+=09=20= *=20Embed=20the=20database,=20schema,=20and=20relation=20name=20in=20the=20= query,=20so=20if=20the=0A+=09=20*=20check=20throws=20an=20error,=20the=20= user=20knows=20which=20relation=20the=20error=20came=0A+=09=20*=20from.=0A= +=09=20*/=0A+=09if=20(opts.parent_check)=0A+=09=09appendPQExpBuffer(sql,=0A= +=09=09=09=09=09=09=20=20"SELECT=20*=20FROM=20%s.bt_index_parent_check("=0A= +=09=09=09=09=09=09=20=20"index=20:=3D=20'%u'::regclass,=20= heapallindexed=20:=3D=20%s,=20"=0A+=09=09=09=09=09=09=20=20"rootdescend=20= :=3D=20%s)",=0A+=09=09=09=09=09=09=20=20rel->datinfo->amcheck_schema,=0A= +=09=09=09=09=09=09=20=20rel->reloid,=0A+=09=09=09=09=09=09=20=20= (opts.heapallindexed=20?=20"true"=20:=20"false"),=0A+=09=09=09=09=09=09=20= =20(opts.rootdescend=20?=20"true"=20:=20"false"));=0A+=09else=0A+=09=09= appendPQExpBuffer(sql,=0A+=09=09=09=09=09=09=20=20"SELECT=20*=20FROM=20= %s.bt_index_check("=0A+=09=09=09=09=09=09=20=20"index=20:=3D=20= '%u'::regclass,=20heapallindexed=20:=3D=20%s)",=0A+=09=09=09=09=09=09=20=20= rel->datinfo->amcheck_schema,=0A+=09=09=09=09=09=09=20=20rel->reloid,=0A= +=09=09=09=09=09=09=20=20(opts.heapallindexed=20?=20"true"=20:=20= "false"));=0A+}=0A+=0A+/*=0A+=20*=20run_command=0A+=20*=0A+=20*=20Sends=20= a=20command=20to=20the=20server=20without=20waiting=20for=20the=20= command=20to=20complete.=0A+=20*=20Logs=20an=20error=20if=20the=20= command=20cannot=20be=20sent,=20but=20otherwise=20any=20errors=20are=0A+=20= *=20expected=20to=20be=20handled=20by=20a=20ParallelSlotHandler.=0A+=20*=0A= +=20*=20If=20reconnecting=20to=20the=20database=20is=20necessary,=20the=20= cparams=20argument=20may=20be=0A+=20*=20modified.=0A+=20*=0A+=20*=20= slot:=20slot=20with=20connection=20to=20the=20server=20we=20should=20use=20= for=20the=20command=0A+=20*=20sql:=20query=20to=20send=0A+=20*/=0A= +static=20void=0A+run_command(ParallelSlot=20*slot,=20const=20char=20= *sql)=0A+{=0A+=09if=20(opts.echo)=0A+=09=09printf("%s\n",=20sql);=0A+=0A= +=09if=20(PQsendQuery(slot->connection,=20sql)=20=3D=3D=200)=0A+=09{=0A+=09= =09pg_log_error("error=20sending=20command=20to=20database=20\"%s\":=20= %s",=0A+=09=09=09=09=09=20PQdb(slot->connection),=0A+=09=09=09=09=09=20= PQerrorMessage(slot->connection));=0A+=09=09pg_log_error("command=20was:=20= %s",=20sql);=0A+=09=09exit(1);=0A+=09}=0A+}=0A+=0A+/*=0A+=20*=20= should_processing_continue=0A+=20*=0A+=20*=20Checks=20a=20query=20result=20= returned=20from=20a=20query=20(presumably=20issued=20on=20a=20slot's=0A+=20= *=20connection)=20to=20determine=20if=20parallel=20slots=20should=20= continue=20issuing=20further=0A+=20*=20commands.=0A+=20*=0A+=20*=20Note:=20= Heap=20relation=20corruption=20is=20reported=20by=20verify_heapam()=20= via=20the=20result=0A+=20*=20set,=20rather=20than=20an=20ERROR,=20but=20= running=20verify_heapam()=20on=20a=20corrupted=20heap=0A+=20*=20table=20= may=20still=20result=20in=20an=20error=20being=20returned=20from=20the=20= server=20due=20to=0A+=20*=20missing=20relation=20files,=20bad=20= checksums,=20etc.=20=20The=20btree=20corruption=20checking=0A+=20*=20= functions=20always=20use=20errors=20to=20communicate=20corruption=20= messages.=20=20We=20can't=0A+=20*=20just=20abort=20processing=20because=20= we=20got=20a=20mere=20ERROR.=0A+=20*=0A+=20*=20res:=20result=20from=20an=20= executed=20sql=20query=0A+=20*/=0A+static=20bool=0A= +should_processing_continue(PGresult=20*res)=0A+{=0A+=09const=20char=20= *severity;=0A+=0A+=09switch=20(PQresultStatus(res))=0A+=09{=0A+=09=09=09= /*=20These=20are=20expected=20and=20ok=20*/=0A+=09=09case=20= PGRES_COMMAND_OK:=0A+=09=09case=20PGRES_TUPLES_OK:=0A+=09=09case=20= PGRES_NONFATAL_ERROR:=0A+=09=09=09break;=0A+=0A+=09=09=09/*=20This=20is=20= expected=20but=20requires=20closer=20scrutiny=20*/=0A+=09=09case=20= PGRES_FATAL_ERROR:=0A+=09=09=09severity=20=3D=20PQresultErrorField(res,=20= PG_DIAG_SEVERITY_NONLOCALIZED);=0A+=09=09=09if=20(strcmp(severity,=20= "FATAL")=20=3D=3D=200)=0A+=09=09=09=09return=20false;=0A+=09=09=09if=20= (strcmp(severity,=20"PANIC")=20=3D=3D=200)=0A+=09=09=09=09return=20= false;=0A+=09=09=09break;=0A+=0A+=09=09=09/*=20These=20are=20unexpected=20= */=0A+=09=09case=20PGRES_BAD_RESPONSE:=0A+=09=09case=20= PGRES_EMPTY_QUERY:=0A+=09=09case=20PGRES_COPY_OUT:=0A+=09=09case=20= PGRES_COPY_IN:=0A+=09=09case=20PGRES_COPY_BOTH:=0A+=09=09case=20= PGRES_SINGLE_TUPLE:=0A+=09=09=09return=20false;=0A+=09}=0A+=09return=20= true;=0A+}=0A+=0A+/*=0A+=20*=20Returns=20a=20copy=20of=20the=20argument=20= string=20with=20all=20lines=20indented=20four=20spaces.=0A+=20*=0A+=20*=20= The=20caller=20should=20pg_free=20the=20result=20when=20finished=20with=20= it.=0A+=20*/=0A+static=20char=20*=0A+indent_lines(const=20char=20*str)=0A= +{=0A+=09PQExpBufferData=20buf;=0A+=09const=20char=20*c;=0A+=09char=09=20= =20=20*result;=0A+=0A+=09initPQExpBuffer(&buf);=0A+=09= appendPQExpBufferStr(&buf,=20"=20=20=20=20");=0A+=09for=20(c=20=3D=20= str;=20*c;=20c++)=0A+=09{=0A+=09=09appendPQExpBufferChar(&buf,=20*c);=0A= +=09=09if=20(c[0]=20=3D=3D=20'\n'=20&&=20c[1]=20!=3D=20'\0')=0A+=09=09=09= appendPQExpBufferStr(&buf,=20"=20=20=20=20");=0A+=09}=0A+=09result=20=3D=20= pstrdup(buf.data);=0A+=09termPQExpBuffer(&buf);=0A+=0A+=09return=20= result;=0A+}=0A+=0A+/*=0A+=20*=20verify_heap_slot_handler=0A+=20*=0A+=20= *=20ParallelSlotHandler=20that=20receives=20results=20from=20a=20heap=20= table=20checking=20command=0A+=20*=20created=20by=20prepare_heap_command=20= and=20outputs=20the=20results=20for=20the=20user.=0A+=20*=0A+=20*=20res:=20= result=20from=20an=20executed=20sql=20query=0A+=20*=20conn:=20connection=20= on=20which=20the=20sql=20query=20was=20executed=0A+=20*=20context:=20the=20= sql=20query=20being=20handled,=20as=20a=20cstring=0A+=20*/=0A+static=20= bool=0A+verify_heap_slot_handler(PGresult=20*res,=20PGconn=20*conn,=20= void=20*context)=0A+{=0A+=09RelationInfo=20*rel=20=3D=20(RelationInfo=20= *)=20context;=0A+=0A+=09if=20(PQresultStatus(res)=20=3D=3D=20= PGRES_TUPLES_OK)=0A+=09{=0A+=09=09int=09=09=09i;=0A+=09=09int=09=09=09= ntups=20=3D=20PQntuples(res);=0A+=0A+=09=09if=20(ntups=20>=200)=0A+=09=09= =09all_checks_pass=20=3D=20false;=0A+=0A+=09=09for=20(i=20=3D=200;=20i=20= <=20ntups;=20i++)=0A+=09=09{=0A+=09=09=09const=20char=20*msg;=0A+=0A+=09=09= =09/*=20The=20message=20string=20should=20never=20be=20null,=20but=20= check=20*/=0A+=09=09=09if=20(PQgetisnull(res,=20i,=203))=0A+=09=09=09=09= msg=20=3D=20"NO=20MESSAGE";=0A+=09=09=09else=0A+=09=09=09=09msg=20=3D=20= PQgetvalue(res,=20i,=203);=0A+=0A+=09=09=09if=20(!PQgetisnull(res,=20i,=20= 2))=0A+=09=09=09=09printf("heap=20table=20\"%s\".\"%s\".\"%s\",=20block=20= %s,=20offset=20%s,=20attribute=20%s:\n=20=20=20=20%s\n",=0A+=09=09=09=09=09= =20=20=20rel->datinfo->datname,=20rel->nspname,=20rel->relname,=0A+=09=09= =09=09=09=20=20=20PQgetvalue(res,=20i,=200),=09/*=20blkno=20*/=0A+=09=09=09= =09=09=20=20=20PQgetvalue(res,=20i,=201),=09/*=20offnum=20*/=0A+=09=09=09= =09=09=20=20=20PQgetvalue(res,=20i,=202),=09/*=20attnum=20*/=0A+=09=09=09= =09=09=20=20=20msg);=0A+=0A+=09=09=09else=20if=20(!PQgetisnull(res,=20i,=20= 1))=0A+=09=09=09=09printf("heap=20table=20\"%s\".\"%s\".\"%s\",=20block=20= %s,=20offset=20%s:\n=20=20=20=20%s\n",=0A+=09=09=09=09=09=20=20=20= rel->datinfo->datname,=20rel->nspname,=20rel->relname,=0A+=09=09=09=09=09= =20=20=20PQgetvalue(res,=20i,=200),=09/*=20blkno=20*/=0A+=09=09=09=09=09=20= =20=20PQgetvalue(res,=20i,=201),=09/*=20offnum=20*/=0A+=09=09=09=09=09=20= =20=20msg);=0A+=0A+=09=09=09else=20if=20(!PQgetisnull(res,=20i,=200))=0A= +=09=09=09=09printf("heap=20table=20\"%s\".\"%s\".\"%s\",=20block=20= %s:\n=20=20=20=20%s\n",=0A+=09=09=09=09=09=20=20=20= rel->datinfo->datname,=20rel->nspname,=20rel->relname,=0A+=09=09=09=09=09= =20=20=20PQgetvalue(res,=20i,=200),=09/*=20blkno=20*/=0A+=09=09=09=09=09=20= =20=20msg);=0A+=0A+=09=09=09else=0A+=09=09=09=09printf("heap=20table=20= \"%s\".\"%s\".\"%s\":\n=20=20=20=20%s\n",=0A+=09=09=09=09=09=20=20=20= rel->datinfo->datname,=20rel->nspname,=20rel->relname,=20msg);=0A+=09=09= }=0A+=09}=0A+=09else=20if=20(PQresultStatus(res)=20!=3D=20= PGRES_TUPLES_OK)=0A+=09{=0A+=09=09char=09=20=20=20*msg=20=3D=20= indent_lines(PQerrorMessage(conn));=0A+=0A+=09=09all_checks_pass=20=3D=20= false;=0A+=09=09printf("heap=20table=20\"%s\".\"%s\".\"%s\":\n%s",=0A+=09= =09=09=20=20=20rel->datinfo->datname,=20rel->nspname,=20rel->relname,=20= msg);=0A+=09=09if=20(opts.verbose)=0A+=09=09=09printf("query=20was:=20= %s\n",=20rel->sql);=0A+=09=09FREE_AND_SET_NULL(msg);=0A+=09}=0A+=0A+=09= FREE_AND_SET_NULL(rel->sql);=0A+=09FREE_AND_SET_NULL(rel->nspname);=0A+=09= FREE_AND_SET_NULL(rel->relname);=0A+=0A+=09return=20= should_processing_continue(res);=0A+}=0A+=0A+/*=0A+=20*=20= verify_btree_slot_handler=0A+=20*=0A+=20*=20ParallelSlotHandler=20that=20= receives=20results=20from=20a=20btree=20checking=20command=0A+=20*=20= created=20by=20prepare_btree_command=20and=20outputs=20them=20for=20the=20= user.=20=20The=20results=0A+=20*=20from=20the=20btree=20checking=20= command=20is=20assumed=20to=20be=20empty,=20but=20when=20the=20results=0A= +=20*=20are=20an=20error=20code,=20the=20useful=20information=20about=20= the=20corruption=20is=20expected=0A+=20*=20in=20the=20connection's=20= error=20message.=0A+=20*=0A+=20*=20res:=20result=20from=20an=20executed=20= sql=20query=0A+=20*=20conn:=20connection=20on=20which=20the=20sql=20= query=20was=20executed=0A+=20*=20context:=20unused=0A+=20*/=0A+static=20= bool=0A+verify_btree_slot_handler(PGresult=20*res,=20PGconn=20*conn,=20= void=20*context)=0A+{=0A+=09RelationInfo=20*rel=20=3D=20(RelationInfo=20= *)=20context;=0A+=0A+=09if=20(PQresultStatus(res)=20=3D=3D=20= PGRES_TUPLES_OK)=0A+=09{=0A+=09=09int=09=09=09ntups=20=3D=20= PQntuples(res);=0A+=0A+=09=09if=20(ntups=20!=3D=201)=0A+=09=09{=0A+=09=09= =09/*=0A+=09=09=09=20*=20We=20expect=20the=20btree=20checking=20= functions=20to=20return=20one=20void=20row=0A+=09=09=09=20*=20each,=20so=20= we=20should=20output=20some=20sort=20of=20warning=20if=20we=20get=0A+=09=09= =09=20*=20anything=20else,=20not=20because=20it=20indicates=20= corruption,=20but=20because=0A+=09=09=09=20*=20it=20suggests=20a=20= mismatch=20between=20amcheck=20and=20pg_amcheck=20versions.=0A+=09=09=09=20= *=0A+=09=09=09=20*=20In=20conjunction=20with=20--progress,=20anything=20= written=20to=20stderr=20at=0A+=09=09=09=20*=20this=20time=20would=20= present=20strangely=20to=20the=20user=20without=20an=20extra=0A+=09=09=09= =20*=20newline,=20so=20we=20print=20one.=20=20If=20we=20were=20= multithreaded,=20we'd=20have=0A+=09=09=09=20*=20to=20avoid=20splitting=20= this=20across=20multiple=20calls,=20but=20we're=20in=20an=0A+=09=09=09=20= *=20event=20loop,=20so=20it=20doesn't=20matter.=0A+=09=09=09=20*/=0A+=09=09= =09if=20(opts.show_progress=20&&=20progress_since_last_stderr)=0A+=09=09=09= =09fprintf(stderr,=20"\n");=0A+=09=09=09pg_log_warning("btree=20index=20= \"%s\".\"%s\".\"%s\":=20btree=20checking=20function=20returned=20= unexpected=20number=20of=20rows:=20%d",=0A+=09=09=09=09=09=09=20=20=20= rel->datinfo->datname,=20rel->nspname,=20rel->relname,=20ntups);=0A+=09=09= =09if=20(opts.verbose)=0A+=09=09=09=09pg_log_info("query=20was:=20%s",=20= rel->sql);=0A+=09=09=09pg_log_warning("are=20%s's=20and=20amcheck's=20= versions=20compatible?",=0A+=09=09=09=09=09=09=20=20=20progname);=0A+=09=09= =09progress_since_last_stderr=20=3D=20false;=0A+=09=09}=0A+=09}=0A+=09= else=0A+=09{=0A+=09=09char=09=20=20=20*msg=20=3D=20= indent_lines(PQerrorMessage(conn));=0A+=0A+=09=09all_checks_pass=20=3D=20= false;=0A+=09=09printf("btree=20index=20\"%s\".\"%s\".\"%s\":\n%s",=0A+=09= =09=09=20=20=20rel->datinfo->datname,=20rel->nspname,=20rel->relname,=20= msg);=0A+=09=09if=20(opts.verbose)=0A+=09=09=09printf("query=20was:=20= %s\n",=20rel->sql);=0A+=09=09FREE_AND_SET_NULL(msg);=0A+=09}=0A+=0A+=09= FREE_AND_SET_NULL(rel->sql);=0A+=09FREE_AND_SET_NULL(rel->nspname);=0A+=09= FREE_AND_SET_NULL(rel->relname);=0A+=0A+=09return=20= should_processing_continue(res);=0A+}=0A+=0A+/*=0A+=20*=20help=0A+=20*=0A= +=20*=20Prints=20help=20page=20for=20the=20program=0A+=20*=0A+=20*=20= progname:=20the=20name=20of=20the=20executed=20program,=20such=20as=20= "pg_amcheck"=0A+=20*/=0A+static=20void=0A+help(const=20char=20*progname)=0A= +{=0A+=09printf("%s=20uses=20amcheck=20module=20to=20check=20objects=20= in=20a=20PostgreSQL=20database=20for=20corruption.\n\n",=20progname);=0A= +=09printf("Usage:\n");=0A+=09printf("=20=20%s=20[OPTION]...=20= [DBNAME]\n",=20progname);=0A+=09printf("\nTarget=20Options:\n");=0A+=09= printf("=20=20-a,=20--all=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20check=20all=20databases\n");=0A+=09printf("=20=20-d,=20= --database=3DPATTERN=20=20=20=20=20=20=20=20=20check=20matching=20= database(s)\n");=0A+=09printf("=20=20-D,=20--exclude-database=3DPATTERN=20= do=20NOT=20check=20matching=20database(s)\n");=0A+=09printf("=20=20-i,=20= --index=3DPATTERN=20=20=20=20=20=20=20=20=20=20=20=20check=20matching=20= index(es)\n");=0A+=09printf("=20=20-I,=20--exclude-index=3DPATTERN=20=20=20= =20do=20NOT=20check=20matching=20index(es)\n");=0A+=09printf("=20=20-r,=20= --relation=3DPATTERN=20=20=20=20=20=20=20=20=20check=20matching=20= relation(s)\n");=0A+=09printf("=20=20-R,=20--exclude-relation=3DPATTERN=20= do=20NOT=20check=20matching=20relation(s)\n");=0A+=09printf("=20=20-s,=20= --schema=3DPATTERN=20=20=20=20=20=20=20=20=20=20=20check=20matching=20= schema(s)\n");=0A+=09printf("=20=20-S,=20--exclude-schema=3DPATTERN=20=20= =20do=20NOT=20check=20matching=20schema(s)\n");=0A+=09printf("=20=20-t,=20= --table=3DPATTERN=20=20=20=20=20=20=20=20=20=20=20=20check=20matching=20= table(s)\n");=0A+=09printf("=20=20-T,=20--exclude-table=3DPATTERN=20=20=20= =20do=20NOT=20check=20matching=20table(s)\n");=0A+=09printf("=20=20=20=20= =20=20--no-dependent-indexes=20=20=20=20=20do=20NOT=20expand=20list=20of=20= relations=20to=20include=20indexes\n");=0A+=09printf("=20=20=20=20=20=20= --no-dependent-toast=20=20=20=20=20=20=20do=20NOT=20expand=20list=20of=20= relations=20to=20include=20toast\n");=0A+=09printf("=20=20=20=20=20=20= --no-strict-names=20=20=20=20=20=20=20=20=20=20do=20NOT=20require=20= patterns=20to=20match=20objects\n");=0A+=09printf("\nTable=20Checking=20= Options:\n");=0A+=09printf("=20=20=20=20=20=20--exclude-toast-pointers=20= =20=20do=20NOT=20follow=20relation=20toast=20pointers\n");=0A+=09= printf("=20=20=20=20=20=20--on-error-stop=20=20=20=20=20=20=20=20=20=20=20= =20stop=20checking=20at=20end=20of=20first=20corrupt=20page\n");=0A+=09= printf("=20=20=20=20=20=20--skip=3DOPTION=20=20=20=20=20=20=20=20=20=20=20= =20=20=20do=20NOT=20check=20\"all-frozen\"=20or=20\"all-visible\"=20= blocks\n");=0A+=09printf("=20=20=20=20=20=20--startblock=3DBLOCK=20=20=20= =20=20=20=20=20=20begin=20checking=20table(s)=20at=20the=20given=20block=20= number\n");=0A+=09printf("=20=20=20=20=20=20--endblock=3DBLOCK=20=20=20=20= =20=20=20=20=20=20=20check=20table(s)=20only=20up=20to=20the=20given=20= block=20number\n");=0A+=09printf("\nBtree=20Index=20Checking=20= Options:\n");=0A+=09printf("=20=20=20=20=20=20--heapallindexed=20=20=20=20= =20=20=20=20=20=20=20check=20all=20heap=20tuples=20are=20found=20within=20= indexes\n");=0A+=09printf("=20=20=20=20=20=20--parent-check=20=20=20=20=20= =20=20=20=20=20=20=20=20check=20index=20parent/child=20= relationships\n");=0A+=09printf("=20=20=20=20=20=20--rootdescend=20=20=20= =20=20=20=20=20=20=20=20=20=20=20search=20from=20root=20page=20to=20= refind=20tuples\n");=0A+=09printf("\nConnection=20options:\n");=0A+=09= printf("=20=20-h,=20--host=3DHOSTNAME=20=20=20=20=20=20=20=20=20=20=20=20= database=20server=20host=20or=20socket=20directory\n");=0A+=09printf("=20= =20-p,=20--port=3DPORT=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= database=20server=20port\n");=0A+=09printf("=20=20-U,=20= --username=3DUSERNAME=20=20=20=20=20=20=20=20user=20name=20to=20connect=20= as\n");=0A+=09printf("=20=20-w,=20--no-password=20=20=20=20=20=20=20=20=20= =20=20=20=20=20never=20prompt=20for=20password\n");=0A+=09printf("=20=20= -W,=20--password=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20force=20= password=20prompt\n");=0A+=09printf("=20=20=20=20=20=20= --maintenance-db=3DDBNAME=20=20=20=20alternate=20maintenance=20= database\n");=0A+=09printf("\nOther=20Options:\n");=0A+=09printf("=20=20= -e,=20--echo=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= show=20the=20commands=20being=20sent=20to=20the=20server\n");=0A+=09= printf("=20=20-j,=20--jobs=3DNUM=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20use=20this=20many=20concurrent=20connections=20to=20the=20= server\n");=0A+=09printf("=20=20-q,=20--quiet=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20don't=20write=20any=20messages\n");=0A+=09= printf("=20=20-v,=20--verbose=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20write=20a=20lot=20of=20output\n");=0A+=09printf("=20=20-V,=20= --version=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20output=20= version=20information,=20then=20exit\n");=0A+=09printf("=20=20-P,=20= --progress=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20show=20= progress=20information\n");=0A+=09printf("=20=20-?,=20--help=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20show=20this=20help,=20= then=20exit\n");=0A+=0A+=09printf("\nReport=20bugs=20to=20<%s>.\n",=20= PACKAGE_BUGREPORT);=0A+=09printf("%s=20home=20page:=20<%s>\n",=20= PACKAGE_NAME,=20PACKAGE_URL);=0A+}=0A+=0A+/*=0A+=20*=20Print=20a=20= progress=20report=20based=20on=20the=20global=20variables.=0A+=20*=0A+=20= *=20Progress=20report=20is=20written=20at=20maximum=20once=20per=20= second,=20unless=20the=20force=0A+=20*=20parameter=20is=20set=20to=20= true.=0A+=20*=0A+=20*=20If=20finished=20is=20set=20to=20true,=20this=20= is=20the=20last=20progress=20report.=20The=20cursor=0A+=20*=20is=20moved=20= to=20the=20next=20line.=0A+=20*/=0A+static=20void=0A= +progress_report(uint64=20relations_total,=20uint64=20relations_checked,=0A= +=09=09=09=09uint64=20relpages_total,=20uint64=20relpages_checked,=0A+=09= =09=09=09const=20char=20*datname,=20bool=20force,=20bool=20finished)=0A= +{=0A+=09int=09=09=09percent_rel=20=3D=200;=0A+=09int=09=09=09= percent_pages=20=3D=200;=0A+=09char=09=09checked_rel[32];=0A+=09char=09=09= total_rel[32];=0A+=09char=09=09checked_pages[32];=0A+=09char=09=09= total_pages[32];=0A+=09pg_time_t=09now;=0A+=0A+=09if=20= (!opts.show_progress)=0A+=09=09return;=0A+=0A+=09now=20=3D=20time(NULL);=0A= +=09if=20(now=20=3D=3D=20last_progress_report=20&&=20!force=20&&=20= !finished)=0A+=09=09return;=09=09=09=09=09/*=20Max=20once=20per=20second=20= */=0A+=0A+=09last_progress_report=20=3D=20now;=0A+=09if=20= (relations_total)=0A+=09=09percent_rel=20=3D=20(int)=20= (relations_checked=20*=20100=20/=20relations_total);=0A+=09if=20= (relpages_total)=0A+=09=09percent_pages=20=3D=20(int)=20= (relpages_checked=20*=20100=20/=20relpages_total);=0A+=0A+=09/*=0A+=09=20= *=20Separate=20step=20to=20keep=20platform-dependent=20format=20code=20= out=20of=20fprintf=0A+=09=20*=20calls.=20=20We=20only=20test=20for=20= INT64_FORMAT=20availability=20in=20snprintf,=20not=0A+=09=20*=20fprintf.=0A= +=09=20*/=0A+=09snprintf(checked_rel,=20sizeof(checked_rel),=20= INT64_FORMAT,=20relations_checked);=0A+=09snprintf(total_rel,=20= sizeof(total_rel),=20INT64_FORMAT,=20relations_total);=0A+=09= snprintf(checked_pages,=20sizeof(checked_pages),=20INT64_FORMAT,=20= relpages_checked);=0A+=09snprintf(total_pages,=20sizeof(total_pages),=20= INT64_FORMAT,=20relpages_total);=0A+=0A+#define=20VERBOSE_DATNAME_LENGTH=20= 35=0A+=09if=20(opts.verbose)=0A+=09{=0A+=09=09if=20(!datname)=0A+=0A+=09=09= =09/*=0A+=09=09=09=20*=20No=20datname=20given,=20so=20clear=20the=20= status=20line=20(used=20for=20first=20and=0A+=09=09=09=20*=20last=20= call)=0A+=09=09=09=20*/=0A+=09=09=09fprintf(stderr,=0A+=09=09=09=09=09= "%*s/%s=20relations=20(%d%%)=20%*s/%s=20pages=20(%d%%)=20%*s",=0A+=09=09=09= =09=09(int)=20strlen(total_rel),=0A+=09=09=09=09=09checked_rel,=20= total_rel,=20percent_rel,=0A+=09=09=09=09=09(int)=20strlen(total_pages),=0A= +=09=09=09=09=09checked_pages,=20total_pages,=20percent_pages,=0A+=09=09=09= =09=09VERBOSE_DATNAME_LENGTH=20+=202,=20"");=0A+=09=09else=0A+=09=09{=0A= +=09=09=09bool=09=09truncate=20=3D=20(strlen(datname)=20>=20= VERBOSE_DATNAME_LENGTH);=0A+=0A+=09=09=09fprintf(stderr,=0A+=09=09=09=09=09= "%*s/%s=20relations=20(%d%%)=20%*s/%s=20pages=20(%d%%),=20(%s%-*.*s)",=0A= +=09=09=09=09=09(int)=20strlen(total_rel),=0A+=09=09=09=09=09= checked_rel,=20total_rel,=20percent_rel,=0A+=09=09=09=09=09(int)=20= strlen(total_pages),=0A+=09=09=09=09=09checked_pages,=20total_pages,=20= percent_pages,=0A+=09=09=09/*=20Prefix=20with=20"..."=20if=20we=20do=20= leading=20truncation=20*/=0A+=09=09=09=09=09truncate=20?=20"..."=20:=20= "",=0A+=09=09=09=09=09truncate=20?=20VERBOSE_DATNAME_LENGTH=20-=203=20:=20= VERBOSE_DATNAME_LENGTH,=0A+=09=09=09=09=09truncate=20?=20= VERBOSE_DATNAME_LENGTH=20-=203=20:=20VERBOSE_DATNAME_LENGTH,=0A+=09=09=09= /*=20Truncate=20datname=20at=20beginning=20if=20it's=20too=20long=20*/=0A= +=09=09=09=09=09truncate=20?=20datname=20+=20strlen(datname)=20-=20= VERBOSE_DATNAME_LENGTH=20+=203=20:=20datname);=0A+=09=09}=0A+=09}=0A+=09= else=0A+=09=09fprintf(stderr,=0A+=09=09=09=09"%*s/%s=20relations=20= (%d%%)=20%*s/%s=20pages=20(%d%%)",=0A+=09=09=09=09(int)=20= strlen(total_rel),=0A+=09=09=09=09checked_rel,=20total_rel,=20= percent_rel,=0A+=09=09=09=09(int)=20strlen(total_pages),=0A+=09=09=09=09= checked_pages,=20total_pages,=20percent_pages);=0A+=0A+=09/*=0A+=09=20*=20= Stay=20on=20the=20same=20line=20if=20reporting=20to=20a=20terminal=20and=20= we're=20not=20done=0A+=09=20*=20yet.=0A+=09=20*/=0A+=09if=20(!finished=20= &&=20isatty(fileno(stderr)))=0A+=09{=0A+=09=09fputc('\r',=20stderr);=0A+=09= =09progress_since_last_stderr=20=3D=20true;=0A+=09}=0A+=09else=0A+=09=09= fputc('\n',=20stderr);=0A+}=0A+=0A+/*=0A+=20*=20Extend=20the=20pattern=20= info=20array=20to=20hold=20one=20additional=20initialized=20pattern=0A+=20= *=20info=20entry.=0A+=20*=0A+=20*=20Returns=20a=20pointer=20to=20the=20= new=20entry.=0A+=20*/=0A+static=20PatternInfo=20*=0A= +extend_pattern_info_array(PatternInfoArray=20*pia)=0A+{=0A+=09= PatternInfo=20*result;=0A+=0A+=09pia->len++;=0A+=09pia->data=20=3D=20= (PatternInfo=20*)=20pg_realloc(pia->data,=20pia->len=20*=20= sizeof(PatternInfo));=0A+=09result=20=3D=20&pia->data[pia->len=20-=201];=0A= +=09memset(result,=200,=20sizeof(*result));=0A+=0A+=09return=20result;=0A= +}=0A+=0A+/*=0A+=20*=20append_database_pattern=0A+=20*=0A+=20*=20Adds=20= the=20given=20pattern=20interpreted=20as=20a=20database=20name=20= pattern.=0A+=20*=0A+=20*=20pia:=20the=20pattern=20info=20array=20to=20be=20= appended=0A+=20*=20pattern:=20the=20database=20name=20pattern=0A+=20*=20= encoding:=20client=20encoding=20for=20parsing=20the=20pattern=0A+=20*/=0A= +static=20void=0A+append_database_pattern(PatternInfoArray=20*pia,=20= const=20char=20*pattern,=20int=20encoding)=0A+{=0A+=09PQExpBufferData=20= buf;=0A+=09PatternInfo=20*info=20=3D=20extend_pattern_info_array(pia);=0A= +=0A+=09initPQExpBuffer(&buf);=0A+=09patternToSQLRegex(encoding,=20NULL,=20= NULL,=20&buf,=20pattern,=20false);=0A+=09info->pattern=20=3D=20pattern;=0A= +=09info->db_regex=20=3D=20pstrdup(buf.data);=0A+=0A+=09= termPQExpBuffer(&buf);=0A+}=0A+=0A+/*=0A+=20*=20append_schema_pattern=0A= +=20*=0A+=20*=20Adds=20the=20given=20pattern=20interpreted=20as=20a=20= schema=20name=20pattern.=0A+=20*=0A+=20*=20pia:=20the=20pattern=20info=20= array=20to=20be=20appended=0A+=20*=20pattern:=20the=20schema=20name=20= pattern=0A+=20*=20encoding:=20client=20encoding=20for=20parsing=20the=20= pattern=0A+=20*/=0A+static=20void=0A= +append_schema_pattern(PatternInfoArray=20*pia,=20const=20char=20= *pattern,=20int=20encoding)=0A+{=0A+=09PQExpBufferData=20dbbuf;=0A+=09= PQExpBufferData=20nspbuf;=0A+=09PatternInfo=20*info=20=3D=20= extend_pattern_info_array(pia);=0A+=0A+=09initPQExpBuffer(&dbbuf);=0A+=09= initPQExpBuffer(&nspbuf);=0A+=0A+=09patternToSQLRegex(encoding,=20NULL,=20= &dbbuf,=20&nspbuf,=20pattern,=20false);=0A+=09info->pattern=20=3D=20= pattern;=0A+=09if=20(dbbuf.data[0])=0A+=09{=0A+=09=09opts.dbpattern=20=3D=20= true;=0A+=09=09info->db_regex=20=3D=20pstrdup(dbbuf.data);=0A+=09}=0A+=09= if=20(nspbuf.data[0])=0A+=09=09info->nsp_regex=20=3D=20= pstrdup(nspbuf.data);=0A+=0A+=09termPQExpBuffer(&dbbuf);=0A+=09= termPQExpBuffer(&nspbuf);=0A+}=0A+=0A+/*=0A+=20*=20= append_relation_pattern_helper=0A+=20*=0A+=20*=20Adds=20to=20a=20list=20= the=20given=20pattern=20interpreted=20as=20a=20relation=20pattern.=0A+=20= *=0A+=20*=20pia:=20the=20pattern=20info=20array=20to=20be=20appended=0A+=20= *=20pattern:=20the=20relation=20name=20pattern=0A+=20*=20encoding:=20= client=20encoding=20for=20parsing=20the=20pattern=0A+=20*=20heap_only:=20= whether=20the=20pattern=20should=20only=20be=20matched=20against=20heap=20= tables=0A+=20*=20btree_only:=20whether=20the=20pattern=20should=20only=20= be=20matched=20against=20btree=20indexes=0A+=20*/=0A+static=20void=0A= +append_relation_pattern_helper(PatternInfoArray=20*pia,=20const=20char=20= *pattern,=0A+=09=09=09=09=09=09=09=20=20=20int=20encoding,=20bool=20= heap_only,=20bool=20btree_only)=0A+{=0A+=09PQExpBufferData=20dbbuf;=0A+=09= PQExpBufferData=20nspbuf;=0A+=09PQExpBufferData=20relbuf;=0A+=09= PatternInfo=20*info=20=3D=20extend_pattern_info_array(pia);=0A+=0A+=09= initPQExpBuffer(&dbbuf);=0A+=09initPQExpBuffer(&nspbuf);=0A+=09= initPQExpBuffer(&relbuf);=0A+=0A+=09patternToSQLRegex(encoding,=20= &dbbuf,=20&nspbuf,=20&relbuf,=20pattern,=20false);=0A+=09info->pattern=20= =3D=20pattern;=0A+=09if=20(dbbuf.data[0])=0A+=09{=0A+=09=09= opts.dbpattern=20=3D=20true;=0A+=09=09info->db_regex=20=3D=20= pstrdup(dbbuf.data);=0A+=09}=0A+=09if=20(nspbuf.data[0])=0A+=09=09= info->nsp_regex=20=3D=20pstrdup(nspbuf.data);=0A+=09if=20= (relbuf.data[0])=0A+=09=09info->rel_regex=20=3D=20pstrdup(relbuf.data);=0A= +=0A+=09termPQExpBuffer(&dbbuf);=0A+=09termPQExpBuffer(&nspbuf);=0A+=09= termPQExpBuffer(&relbuf);=0A+=0A+=09info->heap_only=20=3D=20heap_only;=0A= +=09info->btree_only=20=3D=20btree_only;=0A+}=0A+=0A+/*=0A+=20*=20= append_relation_pattern=0A+=20*=0A+=20*=20Adds=20the=20given=20pattern=20= interpreted=20as=20a=20relation=20pattern,=20to=20be=20matched=0A+=20*=20= against=20both=20heap=20tables=20and=20btree=20indexes.=0A+=20*=0A+=20*=20= pia:=20the=20pattern=20info=20array=20to=20be=20appended=0A+=20*=20= pattern:=20the=20relation=20name=20pattern=0A+=20*=20encoding:=20client=20= encoding=20for=20parsing=20the=20pattern=0A+=20*/=0A+static=20void=0A= +append_relation_pattern(PatternInfoArray=20*pia,=20const=20char=20= *pattern,=20int=20encoding)=0A+{=0A+=09= append_relation_pattern_helper(pia,=20pattern,=20encoding,=20false,=20= false);=0A+}=0A+=0A+/*=0A+=20*=20append_heap_pattern=0A+=20*=0A+=20*=20= Adds=20the=20given=20pattern=20interpreted=20as=20a=20relation=20= pattern,=20to=20be=20matched=20only=0A+=20*=20against=20heap=20tables.=0A= +=20*=0A+=20*=20pia:=20the=20pattern=20info=20array=20to=20be=20appended=0A= +=20*=20pattern:=20the=20relation=20name=20pattern=0A+=20*=20encoding:=20= client=20encoding=20for=20parsing=20the=20pattern=0A+=20*/=0A+static=20= void=0A+append_heap_pattern(PatternInfoArray=20*pia,=20const=20char=20= *pattern,=20int=20encoding)=0A+{=0A+=09= append_relation_pattern_helper(pia,=20pattern,=20encoding,=20true,=20= false);=0A+}=0A+=0A+/*=0A+=20*=20append_btree_pattern=0A+=20*=0A+=20*=20= Adds=20the=20given=20pattern=20interpreted=20as=20a=20relation=20= pattern,=20to=20be=20matched=20only=0A+=20*=20against=20btree=20indexes.=0A= +=20*=0A+=20*=20pia:=20the=20pattern=20info=20array=20to=20be=20appended=0A= +=20*=20pattern:=20the=20relation=20name=20pattern=0A+=20*=20encoding:=20= client=20encoding=20for=20parsing=20the=20pattern=0A+=20*/=0A+static=20= void=0A+append_btree_pattern(PatternInfoArray=20*pia,=20const=20char=20= *pattern,=20int=20encoding)=0A+{=0A+=09= append_relation_pattern_helper(pia,=20pattern,=20encoding,=20false,=20= true);=0A+}=0A+=0A+/*=0A+=20*=20append_db_pattern_cte=0A+=20*=0A+=20*=20= Appends=20to=20the=20buffer=20the=20body=20of=20a=20Common=20Table=20= Expression=20(CTE)=20containing=0A+=20*=20the=20database=20portions=20= filtered=20from=20the=20list=20of=20patterns=20expressed=20as=20two=0A+=20= *=20columns:=0A+=20*=0A+=20*=20=20=20=20=20pattern_id:=20the=20index=20= of=20this=20pattern=20in=20pia->data[]=0A+=20*=20=20=20=20=20rgx:=20the=20= database=20regular=20expression=20parsed=20from=20the=20pattern=0A+=20*=0A= +=20*=20Patterns=20without=20a=20database=20portion=20are=20skipped.=20=20= Patterns=20with=20more=20than=0A+=20*=20just=20a=20database=20portion=20= are=20optionally=20skipped,=20depending=20on=20argument=0A+=20*=20= 'inclusive'.=0A+=20*=0A+=20*=20buf:=20the=20buffer=20to=20be=20appended=0A= +=20*=20pia:=20the=20array=20of=20patterns=20to=20be=20inserted=20into=20= the=20CTE=0A+=20*=20conn:=20the=20database=20connection=0A+=20*=20= inclusive:=20whether=20to=20include=20patterns=20with=20schema=20and/or=20= relation=20parts=0A+=20*=0A+=20*=20Returns=20whether=20any=20database=20= patterns=20were=20appended.=0A+=20*/=0A+static=20bool=0A= +append_db_pattern_cte(PQExpBuffer=20buf,=20const=20PatternInfoArray=20= *pia,=0A+=09=09=09=09=09=20=20PGconn=20*conn,=20bool=20inclusive)=0A+{=0A= +=09int=09=09=09pattern_id;=0A+=09const=20char=20*comma;=0A+=09bool=09=09= have_values;=0A+=0A+=09comma=20=3D=20"";=0A+=09have_values=20=3D=20= false;=0A+=09for=20(pattern_id=20=3D=200;=20pattern_id=20<=20pia->len;=20= pattern_id++)=0A+=09{=0A+=09=09PatternInfo=20*info=20=3D=20= &pia->data[pattern_id];=0A+=0A+=09=09if=20(info->db_regex=20!=3D=20NULL=20= &&=0A+=09=09=09(inclusive=20||=20(info->nsp_regex=20=3D=3D=20NULL=20&&=20= info->rel_regex=20=3D=3D=20NULL)))=0A+=09=09{=0A+=09=09=09if=20= (!have_values)=0A+=09=09=09=09appendPQExpBufferStr(buf,=20"\nVALUES");=0A= +=09=09=09have_values=20=3D=20true;=0A+=09=09=09appendPQExpBuffer(buf,=20= "%s\n(%d,=20",=20comma,=20pattern_id);=0A+=09=09=09= appendStringLiteralConn(buf,=20info->db_regex,=20conn);=0A+=09=09=09= appendPQExpBufferStr(buf,=20")");=0A+=09=09=09comma=20=3D=20",";=0A+=09=09= }=0A+=09}=0A+=0A+=09if=20(!have_values)=0A+=09=09= appendPQExpBufferStr(buf,=20"\nSELECT=20NULL,=20NULL,=20NULL=20WHERE=20= false");=0A+=0A+=09return=20have_values;=0A+}=0A+=0A+/*=0A+=20*=20= compile_database_list=0A+=20*=0A+=20*=20If=20any=20database=20patterns=20= exist,=20or=20if=20--all=20was=20given,=20compiles=20a=20distinct=0A+=20= *=20list=20of=20databases=20to=20check=20using=20a=20SQL=20query=20based=20= on=20the=20patterns=20plus=20the=0A+=20*=20literal=20initial=20database=20= name,=20if=20given.=20=20If=20no=20database=20patterns=20exist=20and=0A+=20= *=20--all=20was=20not=20given,=20the=20query=20is=20not=20necessary,=20= and=20only=20the=20initial=0A+=20*=20database=20name=20(if=20any)=20is=20= added=20to=20the=20list.=0A+=20*=0A+=20*=20conn:=20connection=20to=20the=20= initial=20database=0A+=20*=20databases:=20the=20list=20onto=20which=20= databases=20should=20be=20appended=0A+=20*=20initial_dbname:=20an=20= optional=20extra=20database=20name=20to=20include=20in=20the=20list=0A+=20= */=0A+static=20void=0A+compile_database_list(PGconn=20*conn,=20= SimplePtrList=20*databases,=0A+=09=09=09=09=09=20=20const=20char=20= *initial_dbname)=0A+{=0A+=09PGresult=20=20=20*res;=0A+=09PQExpBufferData=20= sql;=0A+=09int=09=09=09ntups;=0A+=09int=09=09=09i;=0A+=09bool=09=09= fatal;=0A+=0A+=09if=20(initial_dbname)=0A+=09{=0A+=09=09DatabaseInfo=20= *dat=20=3D=20(DatabaseInfo=20*)=20pg_malloc0(sizeof(DatabaseInfo));=0A+=0A= +=09=09/*=20This=20database=20is=20included.=20=20Add=20to=20list=20*/=0A= +=09=09if=20(opts.verbose)=0A+=09=09=09pg_log_info("including=20= database:=20\"%s\"",=20initial_dbname);=0A+=0A+=09=09dat->datname=20=3D=20= pstrdup(initial_dbname);=0A+=09=09simple_ptr_list_append(databases,=20= dat);=0A+=09}=0A+=0A+=09initPQExpBuffer(&sql);=0A+=0A+=09/*=20Append=20= the=20include=20patterns=20CTE.=20*/=0A+=09appendPQExpBufferStr(&sql,=20= "WITH=20include_raw=20(pattern_id,=20rgx)=20AS=20(");=0A+=09if=20= (!append_db_pattern_cte(&sql,=20&opts.include,=20conn,=20true)=20&&=0A+=09= =09!opts.alldb)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20None=20of=20the=20= inclusion=20patterns=20(if=20any)=20contain=20database=20portions,=0A+=09= =09=20*=20so=20there=20is=20no=20need=20to=20query=20the=20database=20to=20= resolve=20database=0A+=09=09=20*=20patterns.=0A+=09=09=20*=0A+=09=09=20*=20= Since=20we're=20also=20not=20operating=20under=20--all,=20we=20don't=20= need=20to=20query=0A+=09=09=20*=20the=20exhaustive=20list=20of=20= connectable=20databases,=20either.=0A+=09=09=20*/=0A+=09=09= termPQExpBuffer(&sql);=0A+=09=09return;=0A+=09}=0A+=0A+=09/*=20Append=20= the=20exclude=20patterns=20CTE.=20*/=0A+=09appendPQExpBufferStr(&sql,=20= "),\nexclude_raw=20(pattern_id,=20rgx)=20AS=20(");=0A+=09= append_db_pattern_cte(&sql,=20&opts.exclude,=20conn,=20false);=0A+=09= appendPQExpBufferStr(&sql,=20"),");=0A+=0A+=09/*=0A+=09=20*=20Append=20= the=20database=20CTE,=20which=20includes=20whether=20each=20database=20= is=0A+=09=20*=20connectable=20and=20also=20joins=20against=20exclude_raw=20= to=20determine=20whether=0A+=09=20*=20each=20database=20is=20excluded.=0A= +=09=20*/=0A+=09appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=20= "\ndatabase=20(datname)=20AS=20("=0A+=09=09=09=09=09=09=20"\nSELECT=20= d.datname=20"=0A+=09=09=09=09=09=09=20"FROM=20pg_catalog.pg_database=20d=20= "=0A+=09=09=09=09=09=09=20"LEFT=20OUTER=20JOIN=20exclude_raw=20e=20"=0A+=09= =09=09=09=09=09=20"ON=20d.datname=20~=20e.rgx=20"=0A+=09=09=09=09=09=09=20= "\nWHERE=20d.datallowconn=20"=0A+=09=09=09=09=09=09=20"AND=20= e.pattern_id=20IS=20NULL"=0A+=09=09=09=09=09=09=20"),"=0A+=0A+=09/*=0A+=09= =20*=20Append=20the=20include_pat=20CTE,=20which=20joins=20the=20= include_raw=20CTE=20against=20the=0A+=09=20*=20databases=20CTE=20to=20= determine=20if=20all=20the=20inclusion=20patterns=20had=20matches,=0A+=09= =20*=20and=20whether=20each=20matched=20pattern=20had=20the=20misfortune=20= of=20only=20matching=0A+=09=20*=20excluded=20or=20unconnectable=20= databases.=0A+=09=20*/=0A+=09=09=09=09=09=09=20"\ninclude_pat=20= (pattern_id,=20checkable)=20AS=20("=0A+=09=09=09=09=09=09=20"\nSELECT=20= i.pattern_id,=20"=0A+=09=09=09=09=09=09=20"COUNT(*)=20FILTER=20("=0A+=09=09= =09=09=09=09=20"WHERE=20d=20IS=20NOT=20NULL"=0A+=09=09=09=09=09=09=20")=20= AS=20checkable"=0A+=09=09=09=09=09=09=20"\nFROM=20include_raw=20i=20"=0A= +=09=09=09=09=09=09=20"LEFT=20OUTER=20JOIN=20database=20d=20"=0A+=09=09=09= =09=09=09=20"ON=20d.datname=20~=20i.rgx"=0A+=09=09=09=09=09=09=20= "\nGROUP=20BY=20i.pattern_id"=0A+=09=09=09=09=09=09=20"),"=0A+=0A+=09/*=0A= +=09=20*=20Append=20the=20filtered_databases=20CTE,=20which=20selects=20= from=20the=20database=20CTE=0A+=09=20*=20optionally=20joined=20against=20= the=20include_raw=20CTE=20to=20only=20select=20databases=0A+=09=20*=20= that=20match=20an=20inclusion=20pattern.=20=20This=20appears=20to=20= duplicate=20what=20the=0A+=09=20*=20include_pat=20CTE=20already=20did=20= above,=20but=20here=20we=20want=20only=20databases,=20and=0A+=09=20*=20= there=20we=20wanted=20patterns.=0A+=09=20*/=0A+=09=09=09=09=09=09=20= "\nfiltered_databases=20(datname)=20AS=20("=0A+=09=09=09=09=09=09=20= "\nSELECT=20DISTINCT=20d.datname=20"=0A+=09=09=09=09=09=09=20"FROM=20= database=20d");=0A+=09if=20(!opts.alldb)=0A+=09=09= appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=09=20"=20INNER=20JOIN=20= include_raw=20i=20"=0A+=09=09=09=09=09=09=09=20"ON=20d.datname=20~=20= i.rgx");=0A+=09appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=20")"=0A= +=0A+=09/*=0A+=09=20*=20Select=20the=20checkable=20databases=20and=20the=20= unmatched=20inclusion=20patterns.=0A+=09=20*/=0A+=09=09=09=09=09=09=20= "\nSELECT=20pattern_id,=20datname=20FROM=20("=0A+=09=09=09=09=09=09=20= "\nSELECT=20pattern_id,=20NULL::TEXT=20AS=20datname=20"=0A+=09=09=09=09=09= =09=20"FROM=20include_pat=20"=0A+=09=09=09=09=09=09=20"WHERE=20checkable=20= =3D=200=20"=0A+=09=09=09=09=09=09=20"UNION=20ALL"=0A+=09=09=09=09=09=09=20= "\nSELECT=20NULL,=20datname=20"=0A+=09=09=09=09=09=09=20"FROM=20= filtered_databases"=0A+=09=09=09=09=09=09=20")=20AS=20combined_records"=0A= +=09=09=09=09=09=09=20"\nORDER=20BY=20pattern_id=20NULLS=20LAST,=20= datname");=0A+=0A+=09res=20=3D=20executeQuery(conn,=20sql.data,=20= opts.echo);=0A+=09if=20(PQresultStatus(res)=20!=3D=20PGRES_TUPLES_OK)=0A= +=09{=0A+=09=09pg_log_error("query=20failed:=20%s",=20= PQerrorMessage(conn));=0A+=09=09pg_log_info("query=20was:=20%s",=20= sql.data);=0A+=09=09disconnectDatabase(conn);=0A+=09=09exit(1);=0A+=09}=0A= +=09termPQExpBuffer(&sql);=0A+=0A+=09ntups=20=3D=20PQntuples(res);=0A+=09= for=20(fatal=20=3D=20false,=20i=20=3D=200;=20i=20<=20ntups;=20i++)=0A+=09= {=0A+=09=09int=09=09=09pattern_id=20=3D=20-1;=0A+=09=09const=20char=20= *datname=20=3D=20NULL;=0A+=0A+=09=09if=20(!PQgetisnull(res,=20i,=200))=0A= +=09=09=09pattern_id=20=3D=20atoi(PQgetvalue(res,=20i,=200));=0A+=09=09= if=20(!PQgetisnull(res,=20i,=201))=0A+=09=09=09datname=20=3D=20= PQgetvalue(res,=20i,=201);=0A+=0A+=09=09if=20(pattern_id=20>=3D=200)=0A+=09= =09{=0A+=09=09=09/*=0A+=09=09=09=20*=20Current=20record=20pertains=20to=20= an=20inclusion=20pattern=20that=20matched=20no=0A+=09=09=09=20*=20= checkable=20databases.=0A+=09=09=09=20*/=0A+=09=09=09fatal=20=3D=20= opts.strict_names;=0A+=09=09=09if=20(pattern_id=20>=3D=20= opts.include.len)=0A+=09=09=09{=0A+=09=09=09=09pg_log_error("internal=20= error:=20received=20unexpected=20database=20pattern_id=20%d",=0A+=09=09=09= =09=09=09=09=20pattern_id);=0A+=09=09=09=09exit(1);=0A+=09=09=09}=0A+=09=09= =09log_no_match("no=20connectable=20databases=20to=20check=20matching=20= \"%s\"",=0A+=09=09=09=09=09=09=20opts.include.data[pattern_id].pattern);=0A= +=09=09}=0A+=09=09else=0A+=09=09{=0A+=09=09=09/*=20Current=20record=20= pertains=20to=20a=20database=20*/=0A+=09=09=09Assert(datname=20!=3D=20= NULL);=0A+=0A+=09=09=09/*=20Avoid=20entering=20a=20duplicate=20entry=20= matching=20the=20initial_dbname=20*/=0A+=09=09=09if=20(initial_dbname=20= !=3D=20NULL=20&&=20strcmp(initial_dbname,=20datname)=20=3D=3D=200)=0A+=09= =09=09=09continue;=0A+=0A+=09=09=09DatabaseInfo=20*dat=20=3D=20= (DatabaseInfo=20*)=20pg_malloc0(sizeof(DatabaseInfo));=0A+=0A+=09=09=09= /*=20This=20database=20is=20included.=20=20Add=20to=20list=20*/=0A+=09=09= =09if=20(opts.verbose)=0A+=09=09=09=09pg_log_info("including=20database:=20= \"%s\"",=20datname);=0A+=0A+=09=09=09dat->datname=20=3D=20= pstrdup(datname);=0A+=09=09=09simple_ptr_list_append(databases,=20dat);=0A= +=09=09}=0A+=09}=0A+=09PQclear(res);=0A+=0A+=09if=20(fatal)=0A+=09{=0A+=09= =09if=20(conn=20!=3D=20NULL)=0A+=09=09=09disconnectDatabase(conn);=0A+=09= =09exit(1);=0A+=09}=0A+}=0A+=0A+/*=0A+=20*=20append_rel_pattern_raw_cte=0A= +=20*=0A+=20*=20Appends=20to=20the=20buffer=20the=20body=20of=20a=20= Common=20Table=20Expression=20(CTE)=20containing=0A+=20*=20the=20given=20= patterns=20as=20six=20columns:=0A+=20*=0A+=20*=20=20=20=20=20pattern_id:=20= the=20index=20of=20this=20pattern=20in=20pia->data[]=0A+=20*=20=20=20=20=20= db_regex:=20the=20database=20regexp=20parsed=20from=20the=20pattern,=20= or=20NULL=20if=20the=0A+=20*=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= pattern=20had=20no=20database=20part=0A+=20*=20=20=20=20=20nsp_regex:=20= the=20namespace=20regexp=20parsed=20from=20the=20pattern,=20or=20NULL=20= if=20the=0A+=20*=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20pattern=20= had=20no=20namespace=20part=0A+=20*=20=20=20=20=20rel_regex:=20the=20= relname=20regexp=20parsed=20from=20the=20pattern,=20or=20NULL=20if=20the=0A= +=20*=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20pattern=20had=20no=20= relname=20part=0A+=20*=20=20=20=20=20heap_only:=20true=20if=20the=20= pattern=20applies=20only=20to=20heap=20tables=20(not=20indexes)=0A+=20*=20= =20=20=20=20btree_only:=20true=20if=20the=20pattern=20applies=20only=20= to=20btree=20indexes=20(not=20tables)=0A+=20*=0A+=20*=20buf:=20the=20= buffer=20to=20be=20appended=0A+=20*=20patterns:=20the=20array=20of=20= patterns=20to=20be=20inserted=20into=20the=20CTE=0A+=20*=20conn:=20the=20= database=20connection=0A+=20*/=0A+static=20void=0A= +append_rel_pattern_raw_cte(PQExpBuffer=20buf,=20const=20= PatternInfoArray=20*pia,=0A+=09=09=09=09=09=09=20=20=20PGconn=20*conn)=0A= +{=0A+=09int=09=09=09pattern_id;=0A+=09const=20char=20*comma;=0A+=09bool=09= =09have_values;=0A+=0A+=09comma=20=3D=20"";=0A+=09have_values=20=3D=20= false;=0A+=09for=20(pattern_id=20=3D=200;=20pattern_id=20<=20pia->len;=20= pattern_id++)=0A+=09{=0A+=09=09PatternInfo=20*info=20=3D=20= &pia->data[pattern_id];=0A+=0A+=09=09if=20(!have_values)=0A+=09=09=09= appendPQExpBufferStr(buf,=20"\nVALUES");=0A+=09=09have_values=20=3D=20= true;=0A+=09=09appendPQExpBuffer(buf,=20"%s\n(%d::INTEGER,=20",=20comma,=20= pattern_id);=0A+=09=09if=20(info->db_regex=20=3D=3D=20NULL)=0A+=09=09=09= appendPQExpBufferStr(buf,=20"NULL");=0A+=09=09else=0A+=09=09=09= appendStringLiteralConn(buf,=20info->db_regex,=20conn);=0A+=09=09= appendPQExpBufferStr(buf,=20"::TEXT,=20");=0A+=09=09if=20= (info->nsp_regex=20=3D=3D=20NULL)=0A+=09=09=09appendPQExpBufferStr(buf,=20= "NULL");=0A+=09=09else=0A+=09=09=09appendStringLiteralConn(buf,=20= info->nsp_regex,=20conn);=0A+=09=09appendPQExpBufferStr(buf,=20"::TEXT,=20= ");=0A+=09=09if=20(info->rel_regex=20=3D=3D=20NULL)=0A+=09=09=09= appendPQExpBufferStr(buf,=20"NULL");=0A+=09=09else=0A+=09=09=09= appendStringLiteralConn(buf,=20info->rel_regex,=20conn);=0A+=09=09if=20= (info->heap_only)=0A+=09=09=09appendPQExpBufferStr(buf,=20"::TEXT,=20= true::BOOLEAN");=0A+=09=09else=0A+=09=09=09appendPQExpBufferStr(buf,=20= "::TEXT,=20false::BOOLEAN");=0A+=09=09if=20(info->btree_only)=0A+=09=09=09= appendPQExpBufferStr(buf,=20",=20true::BOOLEAN");=0A+=09=09else=0A+=09=09= =09appendPQExpBufferStr(buf,=20",=20false::BOOLEAN");=0A+=09=09= appendPQExpBufferStr(buf,=20")");=0A+=09=09comma=20=3D=20",";=0A+=09}=0A= +=0A+=09if=20(!have_values)=0A+=09=09appendPQExpBufferStr(buf,=0A+=09=09=09= =09=09=09=09=20"\nSELECT=20NULL::INTEGER,=20NULL::TEXT,=20NULL::TEXT,=20= "=0A+=09=09=09=09=09=09=09=20"NULL::TEXT,=20NULL::BOOLEAN,=20= NULL::BOOLEAN=20"=0A+=09=09=09=09=09=09=09=20"WHERE=20false");=0A+}=0A+=0A= +/*=0A+=20*=20append_rel_pattern_filtered_cte=0A+=20*=0A+=20*=20Appends=20= to=20the=20buffer=20a=20Common=20Table=20Expression=20(CTE)=20which=20= selects=0A+=20*=20all=20patterns=20from=20the=20named=20raw=20CTE,=20= filtered=20by=20database.=20=20All=20patterns=0A+=20*=20which=20have=20= no=20database=20portion=20or=20whose=20database=20portion=20matches=20= our=0A+=20*=20connection's=20database=20name=20are=20selected,=20with=20= other=20patterns=20excluded.=0A+=20*=0A+=20*=20The=20basic=20idea=20here=20= is=20that=20if=20we're=20connected=20to=20database=20"foo"=20and=20we=20= have=0A+=20*=20patterns=20"foo.bar.baz",=20"alpha.beta"=20and=20= "one.two.three",=20we=20only=20want=20to=0A+=20*=20use=20the=20first=20= two=20while=20processing=20relations=20in=20this=20database,=20as=20the=20= third=0A+=20*=20one=20is=20not=20relevant.=0A+=20*=0A+=20*=20buf:=20the=20= buffer=20to=20be=20appended=0A+=20*=20raw:=20the=20name=20of=20the=20CTE=20= to=20select=20from=0A+=20*=20filtered:=20the=20name=20of=20the=20CTE=20= to=20create=0A+=20*=20conn:=20the=20database=20connection=0A+=20*/=0A= +static=20void=0A+append_rel_pattern_filtered_cte(PQExpBuffer=20buf,=20= const=20char=20*raw,=0A+=09=09=09=09=09=09=09=09const=20char=20= *filtered,=20PGconn=20*conn)=0A+{=0A+=09appendPQExpBuffer(buf,=0A+=09=09=09= =09=09=20=20"\n%s=20(pattern_id,=20nsp_regex,=20rel_regex,=20heap_only,=20= btree_only)=20AS=20("=0A+=09=09=09=09=09=20=20"\nSELECT=20pattern_id,=20= nsp_regex,=20rel_regex,=20heap_only,=20btree_only=20"=0A+=09=09=09=09=09=20= =20"FROM=20%s=20r"=0A+=09=09=09=09=09=20=20"\nWHERE=20(r.db_regex=20IS=20= NULL=20"=0A+=09=09=09=09=09=20=20"OR=20",=0A+=09=09=09=09=09=20=20= filtered,=20raw);=0A+=09appendStringLiteralConn(buf,=20PQdb(conn),=20= conn);=0A+=09appendPQExpBufferStr(buf,=20"=20~=20r.db_regex)");=0A+=09= appendPQExpBufferStr(buf,=0A+=09=09=09=09=09=09=20"=20AND=20(r.nsp_regex=20= IS=20NOT=20NULL"=0A+=09=09=09=09=09=09=20"=20OR=20r.rel_regex=20IS=20NOT=20= NULL)"=0A+=09=09=09=09=09=09=20"),");=0A+}=0A+=0A+/*=0A+=20*=20= compile_relation_list_one_db=0A+=20*=0A+=20*=20Compiles=20a=20list=20of=20= relations=20to=20check=20within=20the=20currently=20connected=0A+=20*=20= database=20based=20on=20the=20user=20supplied=20options,=20sorted=20by=20= descending=20size,=0A+=20*=20and=20appends=20them=20to=20the=20given=20= list=20of=20relations.=0A+=20*=0A+=20*=20The=20cells=20of=20the=20= constructed=20list=20contain=20all=20information=20about=20the=20= relation=0A+=20*=20necessary=20to=20connect=20to=20the=20database=20and=20= check=20the=20object,=20including=20which=0A+=20*=20database=20to=20= connect=20to,=20where=20contrib/amcheck=20is=20installed,=20and=20the=20= Oid=20and=0A+=20*=20type=20of=20object=20(heap=20table=20vs.=20btree=20= index).=20=20Rather=20than=20duplicating=20the=0A+=20*=20database=20= details=20per=20relation,=20the=20relation=20structs=20use=20references=20= to=20the=0A+=20*=20same=20database=20object,=20provided=20by=20the=20= caller.=0A+=20*=0A+=20*=20conn:=20connection=20to=20this=20next=20= database,=20which=20should=20be=20the=20same=20as=20in=20'dat'=0A+=20*=20= relations:=20list=20onto=20which=20the=20relations=20information=20= should=20be=20appended=0A+=20*=20dat:=20the=20database=20info=20struct=20= for=20use=20by=20each=20relation=0A+=20*=20pagecount:=20gets=20= incremented=20by=20the=20number=20of=20blocks=20to=20check=20in=20all=0A= +=20*=20relations=20added=0A+=20*/=0A+static=20void=0A= +compile_relation_list_one_db(PGconn=20*conn,=20SimplePtrList=20= *relations,=0A+=09=09=09=09=09=09=09=20const=20DatabaseInfo=20*dat,=0A+=09= =09=09=09=09=09=09=20uint64=20*pagecount)=0A+{=0A+=09PGresult=20=20=20= *res;=0A+=09PQExpBufferData=20sql;=0A+=09int=09=09=09ntups;=0A+=09int=09=09= =09i;=0A+=0A+=09initPQExpBuffer(&sql);=0A+=09appendPQExpBufferStr(&sql,=20= "WITH");=0A+=0A+=09/*=20Append=20CTEs=20for=20the=20relation=20inclusion=20= patterns,=20if=20any=20*/=0A+=09if=20(!opts.allrel)=0A+=09{=0A+=09=09= appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=09=20"=20include_raw=20= (pattern_id,=20db_regex,=20nsp_regex,=20rel_regex,=20heap_only,=20= btree_only)=20AS=20(");=0A+=09=09append_rel_pattern_raw_cte(&sql,=20= &opts.include,=20conn);=0A+=09=09appendPQExpBufferStr(&sql,=20"\n),");=0A= +=09=09append_rel_pattern_filtered_cte(&sql,=20"include_raw",=20= "include_pat",=20conn);=0A+=09}=0A+=0A+=09/*=20Append=20CTEs=20for=20the=20= relation=20exclusion=20patterns,=20if=20any=20*/=0A+=09if=20= (opts.excludetbl=20||=20opts.excludeidx=20||=20opts.excludensp)=0A+=09{=0A= +=09=09appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=09=20"=20= exclude_raw=20(pattern_id,=20db_regex,=20nsp_regex,=20rel_regex,=20= heap_only,=20btree_only)=20AS=20(");=0A+=09=09= append_rel_pattern_raw_cte(&sql,=20&opts.exclude,=20conn);=0A+=09=09= appendPQExpBufferStr(&sql,=20"\n),");=0A+=09=09= append_rel_pattern_filtered_cte(&sql,=20"exclude_raw",=20"exclude_pat",=20= conn);=0A+=09}=0A+=0A+=09/*=20Append=20the=20relation=20CTE.=20*/=0A+=09= appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=20"=20relation=20= (pattern_id,=20oid,=20nspname,=20relname,=20reltoastrelid,=20relpages,=20= is_heap,=20is_btree)=20AS=20("=0A+=09=09=09=09=09=09=20"\nSELECT=20= DISTINCT=20ON=20(c.oid");=0A+=09if=20(!opts.allrel)=0A+=09=09= appendPQExpBufferStr(&sql,=20",=20ip.pattern_id)=20ip.pattern_id,");=0A+=09= else=0A+=09=09appendPQExpBufferStr(&sql,=20")=20NULL::INTEGER=20AS=20= pattern_id,");=0A+=09appendPQExpBuffer(&sql,=0A+=09=09=09=09=09=20=20= "\nc.oid,=20n.nspname,=20c.relname,=20c.reltoastrelid,=20c.relpages,=20"=0A= +=09=09=09=09=09=20=20"c.relam=20=3D=20%u=20AS=20is_heap,=20"=0A+=09=09=09= =09=09=20=20"c.relam=20=3D=20%u=20AS=20is_btree"=0A+=09=09=09=09=09=20=20= "\nFROM=20pg_catalog.pg_class=20c=20"=0A+=09=09=09=09=09=20=20"INNER=20= JOIN=20pg_catalog.pg_namespace=20n=20"=0A+=09=09=09=09=09=20=20"ON=20= c.relnamespace=20=3D=20n.oid",=0A+=09=09=09=09=09=20=20= HEAP_TABLE_AM_OID,=20BTREE_AM_OID);=0A+=09if=20(!opts.allrel)=0A+=09=09= appendPQExpBuffer(&sql,=0A+=09=09=09=09=09=09=20=20"\nINNER=20JOIN=20= include_pat=20ip"=0A+=09=09=09=09=09=09=20=20"\nON=20(n.nspname=20~=20= ip.nsp_regex=20OR=20ip.nsp_regex=20IS=20NULL)"=0A+=09=09=09=09=09=09=20=20= "\nAND=20(c.relname=20~=20ip.rel_regex=20OR=20ip.rel_regex=20IS=20NULL)"=0A= +=09=09=09=09=09=09=20=20"\nAND=20(c.relam=20=3D=20%u=20OR=20NOT=20= ip.heap_only)"=0A+=09=09=09=09=09=09=20=20"\nAND=20(c.relam=20=3D=20%u=20= OR=20NOT=20ip.btree_only)",=0A+=09=09=09=09=09=09=20=20= HEAP_TABLE_AM_OID,=20BTREE_AM_OID);=0A+=09if=20(opts.excludetbl=20||=20= opts.excludeidx=20||=20opts.excludensp)=0A+=09=09appendPQExpBuffer(&sql,=0A= +=09=09=09=09=09=09=20=20"\nLEFT=20OUTER=20JOIN=20exclude_pat=20ep"=0A+=09= =09=09=09=09=09=20=20"\nON=20(n.nspname=20~=20ep.nsp_regex=20OR=20= ep.nsp_regex=20IS=20NULL)"=0A+=09=09=09=09=09=09=20=20"\nAND=20= (c.relname=20~=20ep.rel_regex=20OR=20ep.rel_regex=20IS=20NULL)"=0A+=09=09= =09=09=09=09=20=20"\nAND=20(c.relam=20=3D=20%u=20OR=20NOT=20ep.heap_only=20= OR=20ep.rel_regex=20IS=20NULL)"=0A+=09=09=09=09=09=09=20=20"\nAND=20= (c.relam=20=3D=20%u=20OR=20NOT=20ep.btree_only=20OR=20ep.rel_regex=20IS=20= NULL)",=0A+=09=09=09=09=09=09=20=20HEAP_TABLE_AM_OID,=20BTREE_AM_OID);=0A= +=0A+=09if=20(opts.excludetbl=20||=20opts.excludeidx=20||=20= opts.excludensp)=0A+=09=09appendPQExpBufferStr(&sql,=20"\nWHERE=20= ep.pattern_id=20IS=20NULL");=0A+=09else=0A+=09=09= appendPQExpBufferStr(&sql,=20"\nWHERE=20true");=0A+=0A+=09/*=0A+=09=20*=20= We=20need=20to=20be=20careful=20not=20to=20break=20the=20= --no-dependent-toast=20and=0A+=09=20*=20--no-dependent-indexes=20= options.=20=20By=20default,=20the=20btree=20indexes,=20toast=0A+=09=20*=20= tables,=20and=20toast=20table=20btree=20indexes=20associated=20with=20= primary=20heap=0A+=09=20*=20tables=20are=20included,=20using=20their=20= own=20CTEs=20below.=20=20We=20implement=20the=0A+=09=20*=20--exclude-*=20= options=20by=20not=20creating=20those=20CTEs,=20but=20that's=20no=20use=20= if=0A+=09=20*=20we've=20already=20selected=20the=20toast=20and=20indexes=20= here.=20=20On=20the=20other=20hand,=0A+=09=20*=20we=20want=20inclusion=20= patterns=20that=20match=20indexes=20or=20toast=20tables=20to=20be=0A+=09=20= *=20honored.=20=20So,=20if=20inclusion=20patterns=20were=20given,=20we=20= want=20to=20select=20all=0A+=09=20*=20tables,=20toast=20tables,=20or=20= indexes=20that=20match=20the=20patterns.=20=20But=20if=20no=0A+=09=20*=20= inclusion=20patterns=20were=20given,=20and=20we're=20simply=20matching=20= all=20relations,=0A+=09=20*=20then=20we=20only=20want=20to=20match=20the=20= primary=20tables=20here.=0A+=09=20*/=0A+=09if=20(opts.allrel)=0A+=09=09= appendPQExpBuffer(&sql,=0A+=09=09=09=09=09=09=20=20"=20AND=20c.relam=20=3D= =20%u=20"=0A+=09=09=09=09=09=09=20=20"AND=20c.relkind=20IN=20('r',=20= 'm',=20't')=20"=0A+=09=09=09=09=09=09=20=20"AND=20c.relnamespace=20!=3D=20= %u",=0A+=09=09=09=09=09=09=20=20HEAP_TABLE_AM_OID,=20= PG_TOAST_NAMESPACE);=0A+=09else=0A+=09=09appendPQExpBuffer(&sql,=0A+=09=09= =09=09=09=09=20=20"=20AND=20c.relam=20IN=20(%u,=20%u)"=0A+=09=09=09=09=09= =09=20=20"AND=20c.relkind=20IN=20('r',=20'm',=20't',=20'i')=20"=0A+=09=09= =09=09=09=09=20=20"AND=20((c.relam=20=3D=20%u=20AND=20c.relkind=20IN=20= ('r',=20'm',=20't'))=20OR=20"=0A+=09=09=09=09=09=09=20=20"(c.relam=20=3D=20= %u=20AND=20c.relkind=20=3D=20'i'))",=0A+=09=09=09=09=09=09=20=20= HEAP_TABLE_AM_OID,=20BTREE_AM_OID,=0A+=09=09=09=09=09=09=20=20= HEAP_TABLE_AM_OID,=20BTREE_AM_OID);=0A+=0A+=09appendPQExpBufferStr(&sql,=0A= +=09=09=09=09=09=09=20"\nORDER=20BY=20c.oid)");=0A+=0A+=09if=20= (!opts.no_toast_expansion)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20Include=20= a=20CTE=20for=20toast=20tables=20associated=20with=20primary=20heap=20= tables=0A+=09=09=20*=20selected=20above,=20filtering=20by=20exclusion=20= patterns=20(if=20any)=20that=20match=0A+=09=09=20*=20toast=20table=20= names.=0A+=09=09=20*/=0A+=09=09appendPQExpBufferStr(&sql,=0A+=09=09=09=09= =09=09=09=20",=20toast=20(oid,=20nspname,=20relname,=20relpages)=20AS=20= ("=0A+=09=09=09=09=09=09=09=20"\nSELECT=20t.oid,=20'pg_toast',=20= t.relname,=20t.relpages"=0A+=09=09=09=09=09=09=09=20"\nFROM=20= pg_catalog.pg_class=20t=20"=0A+=09=09=09=09=09=09=09=20"INNER=20JOIN=20= relation=20r=20"=0A+=09=09=09=09=09=09=09=20"ON=20r.reltoastrelid=20=3D=20= t.oid");=0A+=09=09if=20(opts.excludetbl=20||=20opts.excludensp)=0A+=09=09= =09appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=09=09=20"\nLEFT=20= OUTER=20JOIN=20exclude_pat=20ep"=0A+=09=09=09=09=09=09=09=09=20"\nON=20= ('pg_toast'=20~=20ep.nsp_regex=20OR=20ep.nsp_regex=20IS=20NULL)"=0A+=09=09= =09=09=09=09=09=09=20"\nAND=20(t.relname=20~=20ep.rel_regex=20OR=20= ep.rel_regex=20IS=20NULL)"=0A+=09=09=09=09=09=09=09=09=20"\nAND=20= ep.heap_only"=0A+=09=09=09=09=09=09=09=09=20"\nWHERE=20ep.pattern_id=20= IS=20NULL");=0A+=09=09appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=09= =20"\n)");=0A+=09}=0A+=09if=20(!opts.no_btree_expansion)=0A+=09{=0A+=09=09= /*=0A+=09=09=20*=20Include=20a=20CTE=20for=20btree=20indexes=20= associated=20with=20primary=20heap=20tables=0A+=09=09=20*=20selected=20= above,=20filtering=20by=20exclusion=20patterns=20(if=20any)=20that=20= match=0A+=09=09=20*=20btree=20index=20names.=0A+=09=09=20*/=0A+=09=09= appendPQExpBuffer(&sql,=0A+=09=09=09=09=09=09=20=20",=20index=20(oid,=20= nspname,=20relname,=20relpages)=20AS=20("=0A+=09=09=09=09=09=09=20=20= "\nSELECT=20c.oid,=20r.nspname,=20c.relname,=20c.relpages=20"=0A+=09=09=09= =09=09=09=20=20"FROM=20relation=20r"=0A+=09=09=09=09=09=09=20=20"\nINNER=20= JOIN=20pg_catalog.pg_index=20i=20"=0A+=09=09=09=09=09=09=20=20"ON=20= r.oid=20=3D=20i.indrelid=20"=0A+=09=09=09=09=09=09=20=20"INNER=20JOIN=20= pg_catalog.pg_class=20c=20"=0A+=09=09=09=09=09=09=20=20"ON=20= i.indexrelid=20=3D=20c.oid");=0A+=09=09if=20(opts.excludeidx=20||=20= opts.excludensp)=0A+=09=09=09appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09= =09=09=09=20"\nINNER=20JOIN=20pg_catalog.pg_namespace=20n=20"=0A+=09=09=09= =09=09=09=09=09=20"ON=20c.relnamespace=20=3D=20n.oid"=0A+=09=09=09=09=09=09= =09=09=20"\nLEFT=20OUTER=20JOIN=20exclude_pat=20ep=20"=0A+=09=09=09=09=09= =09=09=09=20"ON=20(n.nspname=20~=20ep.nsp_regex=20OR=20ep.nsp_regex=20IS=20= NULL)=20"=0A+=09=09=09=09=09=09=09=09=20"AND=20(c.relname=20~=20= ep.rel_regex=20OR=20ep.rel_regex=20IS=20NULL)=20"=0A+=09=09=09=09=09=09=09= =09=20"AND=20ep.btree_only"=0A+=09=09=09=09=09=09=09=09=20"\nWHERE=20= ep.pattern_id=20IS=20NULL");=0A+=09=09else=0A+=09=09=09= appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=09=09=20"\nWHERE=20= true");=0A+=09=09appendPQExpBuffer(&sql,=0A+=09=09=09=09=09=09=20=20"=20= AND=20c.relam=20=3D=20%u=20"=0A+=09=09=09=09=09=09=20=20"AND=20c.relkind=20= =3D=20'i'",=0A+=09=09=09=09=09=09=20=20BTREE_AM_OID);=0A+=09=09if=20= (opts.no_toast_expansion)=0A+=09=09=09appendPQExpBuffer(&sql,=0A+=09=09=09= =09=09=09=09=20=20"=20AND=20c.relnamespace=20!=3D=20%u",=0A+=09=09=09=09=09= =09=09=20=20PG_TOAST_NAMESPACE);=0A+=09=09appendPQExpBufferStr(&sql,=20= "\n)");=0A+=09}=0A+=0A+=09if=20(!opts.no_toast_expansion=20&&=20= !opts.no_btree_expansion)=0A+=09{=0A+=09=09/*=0A+=09=09=20*=20Include=20= a=20CTE=20for=20btree=20indexes=20associated=20with=20toast=20tables=20= of=0A+=09=09=20*=20primary=20heap=20tables=20selected=20above,=20= filtering=20by=20exclusion=20patterns=0A+=09=09=20*=20(if=20any)=20that=20= match=20the=20toast=20index=20names.=0A+=09=09=20*/=0A+=09=09= appendPQExpBuffer(&sql,=0A+=09=09=09=09=09=09=20=20",=20toast_index=20= (oid,=20nspname,=20relname,=20relpages)=20AS=20("=0A+=09=09=09=09=09=09=20= =20"\nSELECT=20c.oid,=20'pg_toast',=20c.relname,=20c.relpages=20"=0A+=09=09= =09=09=09=09=20=20"FROM=20toast=20t=20"=0A+=09=09=09=09=09=09=20=20= "INNER=20JOIN=20pg_catalog.pg_index=20i=20"=0A+=09=09=09=09=09=09=20=20= "ON=20t.oid=20=3D=20i.indrelid"=0A+=09=09=09=09=09=09=20=20"\nINNER=20= JOIN=20pg_catalog.pg_class=20c=20"=0A+=09=09=09=09=09=09=20=20"ON=20= i.indexrelid=20=3D=20c.oid");=0A+=09=09if=20(opts.excludeidx)=0A+=09=09=09= appendPQExpBufferStr(&sql,=0A+=09=09=09=09=09=09=09=09=20"\nLEFT=20OUTER=20= JOIN=20exclude_pat=20ep=20"=0A+=09=09=09=09=09=09=09=09=20"ON=20= ('pg_toast'=20~=20ep.nsp_regex=20OR=20ep.nsp_regex=20IS=20NULL)=20"=0A+=09= =09=09=09=09=09=09=09=20"AND=20(c.relname=20~=20ep.rel_regex=20OR=20= ep.rel_regex=20IS=20NULL)=20"=0A+=09=09=09=09=09=09=09=09=20"AND=20= ep.btree_only=20"=0A+=09=09=09=09=09=09=09=09=20"WHERE=20ep.pattern_id=20= IS=20NULL");=0A+=09=09else=0A+=09=09=09appendPQExpBufferStr(&sql,=0A+=09=09= =09=09=09=09=09=09=20"\nWHERE=20true");=0A+=09=09appendPQExpBuffer(&sql,=0A= +=09=09=09=09=09=09=20=20"=20AND=20c.relam=20=3D=20%u"=0A+=09=09=09=09=09= =09=20=20"=20AND=20c.relkind=20=3D=20'i')",=0A+=09=09=09=09=09=09=20=20= BTREE_AM_OID);=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Roll-up=20distinct=20= rows=20from=20CTEs.=0A+=09=20*=0A+=09=20*=20Relations=20that=20match=20= more=20than=20one=20pattern=20may=20occur=20more=20than=20once=20in=0A+=09= =20*=20the=20list,=20and=20indexes=20and=20toast=20for=20primary=20= relations=20may=20also=20have=0A+=09=20*=20matched=20in=20their=20own=20= right,=20so=20we=20rely=20on=20UNION=20to=20deduplicate=20the=0A+=09=20*=20= list.=0A+=09=20*/=0A+=09appendPQExpBuffer(&sql,=0A+=09=09=09=09=09=20=20= "\nSELECT=20pattern_id,=20is_heap,=20is_btree,=20oid,=20nspname,=20= relname,=20relpages=20"=0A+=09=09=09=09=09=20=20"FROM=20(");=0A+=09= appendPQExpBufferStr(&sql,=0A+=09/*=20Inclusion=20patterns=20that=20= failed=20to=20match=20*/=0A+=09=09=09=09=09=09=20"\nSELECT=20pattern_id,=20= is_heap,=20is_btree,=20"=0A+=09=09=09=09=09=09=20"NULL::OID=20AS=20oid,=20= "=0A+=09=09=09=09=09=09=20"NULL::TEXT=20AS=20nspname,=20"=0A+=09=09=09=09= =09=09=20"NULL::TEXT=20AS=20relname,=20"=0A+=09=09=09=09=09=09=20= "NULL::INTEGER=20AS=20relpages"=0A+=09=09=09=09=09=09=20"\nFROM=20= relation=20"=0A+=09=09=09=09=09=09=20"WHERE=20pattern_id=20IS=20NOT=20= NULL=20"=0A+=09=09=09=09=09=09=20"UNION"=0A+=09/*=20Primary=20relations=20= */=0A+=09=09=09=09=09=09=20"\nSELECT=20NULL::INTEGER=20AS=20pattern_id,=20= "=0A+=09=09=09=09=09=09=20"is_heap,=20is_btree,=20oid,=20nspname,=20= relname,=20relpages=20"=0A+=09=09=09=09=09=09=20"FROM=20relation");=0A+=09= if=20(!opts.no_toast_expansion)=0A+=09=09appendPQExpBufferStr(&sql,=0A+=09= =09=09=09=09=09=09=20"=20UNION"=0A+=09=09/*=20Toast=20tables=20for=20= primary=20relations=20*/=0A+=09=09=09=09=09=09=09=20"\nSELECT=20= NULL::INTEGER=20AS=20pattern_id,=20TRUE=20AS=20is_heap,=20"=0A+=09=09=09=09= =09=09=09=20"FALSE=20AS=20is_btree,=20oid,=20nspname,=20relname,=20= relpages=20"=0A+=09=09=09=09=09=09=09=20"FROM=20toast");=0A+=09if=20= (!opts.no_btree_expansion)=0A+=09=09appendPQExpBufferStr(&sql,=0A+=09=09=09= =09=09=09=09=20"=20UNION"=0A+=09=09/*=20Indexes=20for=20primary=20= relations=20*/=0A+=09=09=09=09=09=09=09=20"\nSELECT=20NULL::INTEGER=20AS=20= pattern_id,=20FALSE=20AS=20is_heap,=20"=0A+=09=09=09=09=09=09=09=20"TRUE=20= AS=20is_btree,=20oid,=20nspname,=20relname,=20relpages=20"=0A+=09=09=09=09= =09=09=09=20"FROM=20index");=0A+=09if=20(!opts.no_toast_expansion=20&&=20= !opts.no_btree_expansion)=0A+=09=09appendPQExpBufferStr(&sql,=0A+=09=09=09= =09=09=09=09=20"=20UNION"=0A+=09=09/*=20Indexes=20for=20toast=20= relations=20*/=0A+=09=09=09=09=09=09=09=20"\nSELECT=20NULL::INTEGER=20AS=20= pattern_id,=20FALSE=20AS=20is_heap,=20"=0A+=09=09=09=09=09=09=09=20"TRUE=20= AS=20is_btree,=20oid,=20nspname,=20relname,=20relpages=20"=0A+=09=09=09=09= =09=09=09=20"FROM=20toast_index");=0A+=09appendPQExpBufferStr(&sql,=0A+=09= =09=09=09=09=09=20"\n)=20AS=20combined_records=20"=0A+=09=09=09=09=09=09=20= "ORDER=20BY=20relpages=20DESC=20NULLS=20FIRST,=20oid");=0A+=0A+=09res=20= =3D=20executeQuery(conn,=20sql.data,=20opts.echo);=0A+=09if=20= (PQresultStatus(res)=20!=3D=20PGRES_TUPLES_OK)=0A+=09{=0A+=09=09= pg_log_error("query=20failed:=20%s",=20PQerrorMessage(conn));=0A+=09=09= pg_log_info("query=20was:=20%s",=20sql.data);=0A+=09=09= disconnectDatabase(conn);=0A+=09=09exit(1);=0A+=09}=0A+=09= termPQExpBuffer(&sql);=0A+=0A+=09ntups=20=3D=20PQntuples(res);=0A+=09for=20= (i=20=3D=200;=20i=20<=20ntups;=20i++)=0A+=09{=0A+=09=09int=09=09=09= pattern_id=20=3D=20-1;=0A+=09=09bool=09=09is_heap=20=3D=20false;=0A+=09=09= bool=09=09is_btree=20=3D=20false;=0A+=09=09Oid=09=09=09oid=20=3D=20= InvalidOid;=0A+=09=09const=20char=20*nspname=20=3D=20NULL;=0A+=09=09= const=20char=20*relname=20=3D=20NULL;=0A+=09=09int=09=09=09relpages=20=3D=20= 0;=0A+=0A+=09=09if=20(!PQgetisnull(res,=20i,=200))=0A+=09=09=09= pattern_id=20=3D=20atoi(PQgetvalue(res,=20i,=200));=0A+=09=09if=20= (!PQgetisnull(res,=20i,=201))=0A+=09=09=09is_heap=20=3D=20= (PQgetvalue(res,=20i,=201)[0]=20=3D=3D=20't');=0A+=09=09if=20= (!PQgetisnull(res,=20i,=202))=0A+=09=09=09is_btree=20=3D=20= (PQgetvalue(res,=20i,=202)[0]=20=3D=3D=20't');=0A+=09=09if=20= (!PQgetisnull(res,=20i,=203))=0A+=09=09=09oid=20=3D=20= atooid(PQgetvalue(res,=20i,=203));=0A+=09=09if=20(!PQgetisnull(res,=20i,=20= 4))=0A+=09=09=09nspname=20=3D=20PQgetvalue(res,=20i,=204);=0A+=09=09if=20= (!PQgetisnull(res,=20i,=205))=0A+=09=09=09relname=20=3D=20= PQgetvalue(res,=20i,=205);=0A+=09=09if=20(!PQgetisnull(res,=20i,=206))=0A= +=09=09=09relpages=20=3D=20atoi(PQgetvalue(res,=20i,=206));=0A+=0A+=09=09= if=20(pattern_id=20>=3D=200)=0A+=09=09{=0A+=09=09=09/*=0A+=09=09=09=20*=20= Current=20record=20pertains=20to=20an=20inclusion=20pattern.=20=20Record=20= that=0A+=09=09=09=20*=20it=20matched.=0A+=09=09=09=20*/=0A+=0A+=09=09=09= if=20(pattern_id=20>=3D=20opts.include.len)=0A+=09=09=09{=0A+=09=09=09=09= pg_log_error("internal=20error:=20received=20unexpected=20relation=20= pattern_id=20%d",=0A+=09=09=09=09=09=09=09=20pattern_id);=0A+=09=09=09=09= exit(1);=0A+=09=09=09}=0A+=0A+=09=09=09= opts.include.data[pattern_id].matched=20=3D=20true;=0A+=09=09}=0A+=09=09= else=0A+=09=09{=0A+=09=09=09/*=20Current=20record=20pertains=20to=20a=20= relation=20*/=0A+=0A+=09=09=09RelationInfo=20*rel=20=3D=20(RelationInfo=20= *)=20pg_malloc0(sizeof(RelationInfo));=0A+=0A+=09=09=09= Assert(OidIsValid(oid));=0A+=09=09=09Assert((is_heap=20&&=20!is_btree)=20= ||=20(is_btree=20&&=20!is_heap));=0A+=0A+=09=09=09rel->datinfo=20=3D=20= dat;=0A+=09=09=09rel->reloid=20=3D=20oid;=0A+=09=09=09rel->is_heap=20=3D=20= is_heap;=0A+=09=09=09rel->nspname=20=3D=20pstrdup(nspname);=0A+=09=09=09= rel->relname=20=3D=20pstrdup(relname);=0A+=09=09=09rel->relpages=20=3D=20= relpages;=0A+=09=09=09rel->blocks_to_check=20=3D=20relpages;=0A+=09=09=09= if=20(is_heap=20&&=20(opts.startblock=20>=3D=200=20||=20opts.endblock=20= >=3D=200))=0A+=09=09=09{=0A+=09=09=09=09/*=0A+=09=09=09=09=20*=20We=20= apply=20--startblock=20and=20--endblock=20to=20heap=20tables,=20but=0A+=09= =09=09=09=20*=20not=20btree=20indexes,=20and=20for=20progress=20purposes=20= we=20need=20to=0A+=09=09=09=09=20*=20track=20how=20many=20blocks=20we=20= expect=20to=20check.=0A+=09=09=09=09=20*/=0A+=09=09=09=09if=20= (opts.endblock=20>=3D=200=20&&=20rel->blocks_to_check=20>=20= opts.endblock)=0A+=09=09=09=09=09rel->blocks_to_check=20=3D=20= opts.endblock=20+=201;=0A+=09=09=09=09if=20(opts.startblock=20>=3D=200)=0A= +=09=09=09=09{=0A+=09=09=09=09=09if=20(rel->blocks_to_check=20>=20= opts.startblock)=0A+=09=09=09=09=09=09rel->blocks_to_check=20-=3D=20= opts.startblock;=0A+=09=09=09=09=09else=0A+=09=09=09=09=09=09= rel->blocks_to_check=20=3D=200;=0A+=09=09=09=09}=0A+=09=09=09}=0A+=09=09=09= *pagecount=20+=3D=20rel->blocks_to_check;=0A+=0A+=09=09=09= simple_ptr_list_append(relations,=20rel);=0A+=09=09}=0A+=09}=0A+=09= PQclear(res);=0A+}=0Adiff=20--git=20a/src/bin/pg_amcheck/t/001_basic.pl=20= b/src/bin/pg_amcheck/t/001_basic.pl=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..dfa0ae9e06=0A---=20/dev/null=0A+++=20= b/src/bin/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/src/bin/pg_amcheck/t/002_nonesuch.pl=20= b/src/bin/pg_amcheck/t/002_nonesuch.pl=0Anew=20file=20mode=20100644=0A= index=200000000000..b7d41c9b49=0A---=20/dev/null=0A+++=20= b/src/bin/pg_amcheck/t/002_nonesuch.pl=0A@@=20-0,0=20+1,248=20@@=0A+use=20= strict;=0A+use=20warnings;=0A+=0A+use=20PostgresNode;=0A+use=20TestLib;=0A= +use=20Test::More=20tests=20=3D>=2076;=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=20non-existent=20= databases=0A+=0A+#=20Failing=20to=20connect=20to=20the=20initial=20= database=20is=20an=20error.=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'qqq'=20],=0A+=091,=0A+=09[=20qr/^$/=20],=0A+=09[=20= qr/FATAL:=20=20database=20"qqq"=20does=20not=20exist/=20],=0A+=09= 'checking=20a=20non-existent=20database');=0A+=0A+#=20Failing=20to=20= resolve=20a=20database=20pattern=20is=20an=20error=20by=20default.=0A= +$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20'-d',=20'qqq',=20= '-d',=20'postgres'=20],=0A+=091,=0A+=09[=20qr/^$/=20],=0A+=09[=20= qr/pg_amcheck:=20error:=20no=20connectable=20databases=20to=20check=20= matching=20"qqq"/=20],=0A+=09'checking=20an=20unresolvable=20database=20= pattern');=0A+=0A+#=20But=20only=20a=20warning=20under=20= --no-strict-names=0A+$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20= '--no-strict-names',=20'-d',=20'qqq',=20'-d',=20'postgres'=20],=0A+=090,=0A= +=09[=20qr/^$/=20],=0A+=09[=20qr/pg_amcheck:=20warning:=20no=20= connectable=20databases=20to=20check=20matching=20"qqq"/=20],=0A+=09= 'checking=20an=20unresolvable=20database=20pattern=20under=20= --no-strict-names');=0A+=0A+#=20Check=20that=20a=20substring=20of=20an=20= existent=20database=20name=20does=20not=20get=20interpreted=0A+#=20as=20= a=20matching=20pattern.=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'-d',=20'post',=20'-d',=20'postgres'=20],=0A+=091,=0A+=09= [=20qr/^$/=20],=0A+=09[=20qr/pg_amcheck:=20error:=20no=20connectable=20= databases=20to=20check=20matching=20"post"/=20],=0A+=09'checking=20an=20= unresolvable=20database=20pattern=20(substring=20of=20existent=20= database)');=0A+=0A+#=20Check=20that=20a=20superstring=20of=20an=20= existent=20database=20name=20does=20not=20get=20interpreted=0A+#=20as=20= a=20matching=20pattern.=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'-d',=20'postgresql',=20'-d',=20'postgres'=20],=0A+=091,=0A= +=09[=20qr/^$/=20],=0A+=09[=20qr/pg_amcheck:=20error:=20no=20connectable=20= databases=20to=20check=20matching=20"postgresql"/=20],=0A+=09'checking=20= an=20unresolvable=20database=20pattern=20(superstring=20of=20existent=20= database)');=0A+=0A+#########################################=0A+#=20= Test=20connecting=20with=20a=20non-existent=20user=0A+=0A+#=20Failing=20= to=20connect=20to=20the=20initial=20database=20due=20to=20bad=20username=20= is=20an=20error.=0A+$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20= '-U',=20'no_such_user',=20'postgres'=20],=0A+=091,=0A+=09[=20qr/^$/=20],=0A= +=09[=20qr/role=20"no_such_user"=20does=20not=20exist/=20],=0A+=09= 'checking=20with=20a=20non-existent=20user');=0A+=0A+#=20Failing=20to=20= connect=20to=20the=20initial=20database=20due=20to=20bad=20username=20is=20= an=20still=20an=0A+#=20error=20under=20--no-strict-names.=0A= +$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20= '--no-strict-names',=20'-U',=20'no_such_user',=20'postgres'=20],=0A+=09= 1,=0A+=09[=20qr/^$/=20],=0A+=09[=20qr/role=20"no_such_user"=20does=20not=20= exist/=20],=0A+=09'checking=20with=20a=20non-existent=20user=20under=20= --no-strict-names');=0A+=0A+#########################################=0A= +#=20Test=20checking=20databases=20without=20amcheck=20installed=0A+=0A= +#=20Attempting=20to=20check=20a=20database=20by=20name=20where=20= amcheck=20is=20not=20installed=20should=0A+#=20raise=20a=20warning.=20=20= If=20all=20databases=20are=20skipped,=20having=20no=20relations=20to=20= check=0A+#=20raises=20an=20error.=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'template1'=20],=0A+=091,=0A+=09[=20qr/^$/=20],=0A+=09[=20= qr/pg_amcheck:=20warning:=20skipping=20database=20"template1":=20amcheck=20= is=20not=20installed/,=0A+=09=20=20qr/pg_amcheck:=20error:=20no=20= relations=20to=20check/=20],=0A+=09'checking=20a=20database=20by=20name=20= without=20amcheck=20installed,=20no=20other=20databases');=0A+=0A+#=20= Again,=20but=20this=20time=20with=20another=20database=20to=20check,=20= so=20no=20error=20is=20raised.=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'-d',=20'template1',=20'-d',=20'postgres'=20],=0A+=090,=0A= +=09[=20qr/^$/=20],=0A+=09[=20qr/pg_amcheck:=20warning:=20skipping=20= database=20"template1":=20amcheck=20is=20not=20installed/=20],=0A+=09= 'checking=20a=20database=20by=20name=20without=20amcheck=20installed,=20= with=20other=20databases');=0A+=0A+#=20Again,=20but=20by=20way=20of=20= checking=20all=20databases=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'--all'=20],=0A+=090,=0A+=09[=20qr/^$/=20],=0A+=09[=20= qr/pg_amcheck:=20warning:=20skipping=20database=20"template1":=20amcheck=20= is=20not=20installed/=20],=0A+=09'checking=20a=20database=20by=20pattern=20= without=20amcheck=20installed,=20with=20other=20databases');=0A+=0A= +#########################################=0A+#=20Test=20unreasonable=20= patterns=0A+=0A+#=20Check=20three-part=20unreasonable=20pattern=20that=20= has=20zero-length=20names=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'-d',=20'postgres',=20'-t',=20'..'=20],=0A+=091,=0A+=09[=20= qr/^$/=20],=0A+=09[=20qr/pg_amcheck:=20error:=20no=20connectable=20= databases=20to=20check=20matching=20"\.\."/=20],=0A+=09'checking=20table=20= pattern=20".."');=0A+=0A+#=20Again,=20but=20with=20non-trivial=20schema=20= and=20relation=20parts=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'-d',=20'postgres',=20'-t',=20'.foo.bar'=20],=0A+=091,=0A= +=09[=20qr/^$/=20],=0A+=09[=20qr/pg_amcheck:=20error:=20no=20connectable=20= databases=20to=20check=20matching=20"\.foo\.bar"/=20],=0A+=09'checking=20= table=20pattern=20".foo.bar"');=0A+=0A+#=20Check=20two-part=20= unreasonable=20pattern=20that=20has=20zero-length=20names=0A= +$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20'-d',=20= 'postgres',=20'-t',=20'.'=20],=0A+=091,=0A+=09[=20qr/^$/=20],=0A+=09[=20= qr/pg_amcheck:=20error:=20no=20heap=20tables=20to=20check=20matching=20= "\."/=20],=0A+=09'checking=20table=20pattern=20"."');=0A+=0A= +#########################################=0A+#=20Test=20checking=20= non-existent=20databases,=20schemas,=20tables,=20and=20indexes=0A+=0A+#=20= Use=20--no-strict-names=20and=20a=20single=20existent=20table=20so=20we=20= only=20get=20warnings=0A+#=20about=20the=20failed=20pattern=20matches=0A= +$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20= '--no-strict-names',=0A+=09=09'-t',=20'no_such_table',=0A+=09=09'-t',=20= 'no*such*table',=0A+=09=09'-i',=20'no_such_index',=0A+=09=09'-i',=20= 'no*such*index',=0A+=09=09'-r',=20'no_such_relation',=0A+=09=09'-r',=20= 'no*such*relation',=0A+=09=09'-d',=20'no_such_database',=0A+=09=09'-d',=20= 'no*such*database',=0A+=09=09'-r',=20'none.none',=0A+=09=09'-r',=20= 'none.none.none',=0A+=09=09'-r',=20= 'this.is.a.really.long.dotted.string',=0A+=09=09'-r',=20= 'postgres.none.none',=0A+=09=09'-r',=20'postgres.long.dotted.string',=0A= +=09=09'-r',=20'postgres.pg_catalog.none',=0A+=09=09'-r',=20= 'postgres.none.pg_class',=0A+=09=09'-t',=20= 'postgres.pg_catalog.pg_class',=09#=20This=20exists=0A+=09],=0A+=090,=0A= +=09[=20qr/^$/=20],=0A+=09[=20qr/pg_amcheck:=20warning:=20no=20heap=20= tables=20to=20check=20matching=20"no_such_table"/,=0A+=09=20=20= qr/pg_amcheck:=20warning:=20no=20heap=20tables=20to=20check=20matching=20= "no\*such\*table"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20no=20btree=20= indexes=20to=20check=20matching=20"no_such_index"/,=0A+=09=20=20= qr/pg_amcheck:=20warning:=20no=20btree=20indexes=20to=20check=20matching=20= "no\*such\*index"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20no=20= relations=20to=20check=20matching=20"no_such_relation"/,=0A+=09=20=20= qr/pg_amcheck:=20warning:=20no=20relations=20to=20check=20matching=20= "no\*such\*relation"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20no=20heap=20= tables=20to=20check=20matching=20"no\*such\*table"/,=0A+=09=20=20= qr/pg_amcheck:=20warning:=20no=20connectable=20databases=20to=20check=20= matching=20"no_such_database"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20= no=20connectable=20databases=20to=20check=20matching=20= "no\*such\*database"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20no=20= relations=20to=20check=20matching=20"none\.none"/,=0A+=09=20=20= qr/pg_amcheck:=20warning:=20no=20connectable=20databases=20to=20check=20= matching=20"none\.none\.none"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20= no=20connectable=20databases=20to=20check=20matching=20= "this\.is\.a\.really\.long\.dotted\.string"/,=0A+=09=20=20qr/pg_amcheck:=20= warning:=20no=20relations=20to=20check=20matching=20= "postgres\.none\.none"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20no=20= relations=20to=20check=20matching=20"postgres\.long\.dotted\.string"/,=0A= +=09=20=20qr/pg_amcheck:=20warning:=20no=20relations=20to=20check=20= matching=20"postgres\.pg_catalog\.none"/,=0A+=09=20=20qr/pg_amcheck:=20= warning:=20no=20relations=20to=20check=20matching=20= "postgres\.none\.pg_class"/,=0A+=09],=0A+=09'many=20unmatched=20patterns=20= and=20one=20matched=20pattern=20under=20--no-strict-names');=0A+=0A= +#########################################=0A+#=20Test=20checking=20= otherwise=20existent=20objects=20but=20in=20databases=20where=20they=20= do=20not=20exist=0A+=0A+$node->safe_psql('postgres',=20q(=0A+=09CREATE=20= TABLE=20public.foo=20(f=20integer);=0A+=09CREATE=20INDEX=20foo_idx=20ON=20= foo(f);=0A+));=0A+$node->safe_psql('postgres',=20q(CREATE=20DATABASE=20= another_db));=0A+=0A+$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20= '-d',=20'postgres',=20'--no-strict-names',=0A+=09=09'-t',=20= 'template1.public.foo',=0A+=09=09'-t',=20'another_db.public.foo',=0A+=09=09= '-t',=20'no_such_database.public.foo',=0A+=09=09'-i',=20= 'template1.public.foo_idx',=0A+=09=09'-i',=20= 'another_db.public.foo_idx',=0A+=09=09'-i',=20= 'no_such_database.public.foo_idx',=0A+=09],=0A+=091,=0A+=09[=20qr/^$/=20= ],=0A+=09[=20qr/pg_amcheck:=20warning:=20skipping=20database=20= "template1":=20amcheck=20is=20not=20installed/,=0A+=09=20=20= qr/pg_amcheck:=20warning:=20no=20heap=20tables=20to=20check=20matching=20= "template1\.public\.foo"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20no=20= heap=20tables=20to=20check=20matching=20"another_db\.public\.foo"/,=0A+=09= =20=20qr/pg_amcheck:=20warning:=20no=20connectable=20databases=20to=20= check=20matching=20"no_such_database\.public\.foo"/,=0A+=09=20=20= qr/pg_amcheck:=20warning:=20no=20btree=20indexes=20to=20check=20matching=20= "template1\.public\.foo_idx"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20= no=20btree=20indexes=20to=20check=20matching=20= "another_db\.public\.foo_idx"/,=0A+=09=20=20qr/pg_amcheck:=20warning:=20= no=20connectable=20databases=20to=20check=20matching=20= "no_such_database\.public\.foo_idx"/,=0A+=09=20=20qr/pg_amcheck:=20= error:=20no=20relations=20to=20check/,=0A+=09],=0A+=09'checking=20= otherwise=20existent=20objets=20in=20the=20wrong=20databases');=0A+=0A+=0A= +#########################################=0A+#=20Test=20schema=20= exclusion=20patterns=0A+=0A+#=20Check=20with=20only=20schema=20exclusion=20= patterns=0A+$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20= '--all',=20'--no-strict-names',=0A+=09=09'-S',=20'public',=0A+=09=09= '-S',=20'pg_catalog',=0A+=09=09'-S',=20'pg_toast',=0A+=09=09'-S',=20= 'information_schema',=0A+=09],=0A+=091,=0A+=09[=20qr/^$/=20],=0A+=09[=20= qr/pg_amcheck:=20warning:=20skipping=20database=20"template1":=20amcheck=20= is=20not=20installed/,=0A+=09=20=20qr/pg_amcheck:=20error:=20no=20= relations=20to=20check/=20],=0A+=09'schema=20exclusion=20patterns=20= exclude=20all=20relations');=0A+=0A+#=20Check=20with=20schema=20= exclusion=20patterns=20overriding=20relation=20and=20schema=20inclusion=20= patterns=0A+$node->command_checks_all(=0A+=09[=20'pg_amcheck',=20= '--all',=20'--no-strict-names',=0A+=09=09'-s',=20'public',=0A+=09=09= '-s',=20'pg_catalog',=0A+=09=09'-s',=20'pg_toast',=0A+=09=09'-s',=20= 'information_schema',=0A+=09=09'-t',=20'pg_catalog.pg_class',=0A+=09=09= '-S',=20'*'=0A+=09],=0A+=091,=0A+=09[=20qr/^$/=20],=0A+=09[=20= qr/pg_amcheck:=20warning:=20skipping=20database=20"template1":=20amcheck=20= is=20not=20installed/,=0A+=09=20=20qr/pg_amcheck:=20error:=20no=20= relations=20to=20check/=20],=0A+=09'schema=20exclusion=20pattern=20= overrides=20all=20inclusion=20patterns');=0Adiff=20--git=20= a/src/bin/pg_amcheck/t/003_check.pl=20= b/src/bin/pg_amcheck/t/003_check.pl=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..e43ffe7ed6=0A---=20/dev/null=0A+++=20= b/src/bin/pg_amcheck/t/003_check.pl=0A@@=20-0,0=20+1,504=20@@=0A+use=20= strict;=0A+use=20warnings;=0A+=0A+use=20PostgresNode;=0A+use=20TestLib;=0A= +use=20Test::More=20tests=20=3D>=2060;=0A+=0A+my=20($node,=20$port,=20= %corrupt_page,=20%remove_relation);=0A+=0A+#=20Returns=20the=20= filesystem=20path=20for=20the=20named=20relation.=0A+#=0A+#=20Assumes=20= the=20test=20node=20is=20running=0A+sub=20relation_filepath($$)=0A+{=0A+=09= my=20($dbname,=20$relname)=20=3D=20@_;=0A+=0A+=09my=20$pgdata=20=3D=20= $node->data_dir;=0A+=09my=20$rel=20=3D=20$node->safe_psql($dbname,=0A+=09= =09=09=09=09=09=09=20=20=20qq(SELECT=20= pg_relation_filepath('$relname')));=0A+=09die=20"path=20not=20found=20= for=20relation=20$relname"=20unless=20defined=20$rel;=0A+=09return=20= "$pgdata/$rel";=0A+}=0A+=0A+#=20Returns=20the=20name=20of=20the=20toast=20= relation=20associated=20with=20the=20named=20relation.=0A+#=0A+#=20= Assumes=20the=20test=20node=20is=20running=0A+sub=20relation_toast($$)=0A= +{=0A+=09my=20($dbname,=20$relname)=20=3D=20@_;=0A+=0A+=09my=20$rel=20=3D=20= $node->safe_psql($dbname,=20qq(=0A+=09=09SELECT=20= c.reltoastrelid::regclass=0A+=09=09=09FROM=20pg_catalog.pg_class=20c=0A+=09= =09=09WHERE=20c.oid=20=3D=20'$relname'::regclass=0A+=09=09=09=20=20AND=20= c.reltoastrelid=20!=3D=200=0A+=09=09=09));=0A+=09return=20undef=20unless=20= defined=20$rel;=0A+=09return=20$rel;=0A+}=0A+=0A+#=20Adds=20the=20= relation=20file=20for=20the=20given=20(dbname,=20relname)=20to=20the=20= list=0A+#=20to=20be=20corrupted=20by=20means=20of=20overwriting=20junk=20= in=20the=20first=20page.=0A+#=0A+#=20Assumes=20the=20test=20node=20is=20= running.=0A+sub=20plan_to_corrupt_first_page($$)=0A+{=0A+=09my=20= ($dbname,=20$relname)=20=3D=20@_;=0A+=09my=20$relpath=20=3D=20= relation_filepath($dbname,=20$relname);=0A+=09$corrupt_page{$relpath}=20= =3D=201;=0A+}=0A+=0A+#=20Adds=20the=20relation=20file=20for=20the=20= given=20(dbname,=20relname)=20to=20the=20list=0A+#=20to=20be=20corrupted=20= by=20means=20of=20removing=20the=20file..=0A+#=0A+#=20Assumes=20the=20= test=20node=20is=20running=0A+sub=20plan_to_remove_relation_file($$)=0A= +{=0A+=09my=20($dbname,=20$relname)=20=3D=20@_;=0A+=09my=20$relpath=20=3D=20= relation_filepath($dbname,=20$relname);=0A+=09$remove_relation{$relpath}=20= =3D=201;=0A+}=0A+=0A+#=20For=20the=20given=20(dbname,=20relname),=20if=20= a=20corresponding=20toast=20table=0A+#=20exists,=20adds=20that=20toast=20= table's=20relation=20file=20to=20the=20list=20to=20be=0A+#=20corrupted=20= by=20means=20of=20removing=20the=20file.=0A+#=0A+#=20Assumes=20the=20= test=20node=20is=20running.=0A+sub=20plan_to_remove_toast_file($$)=0A+{=0A= +=09my=20($dbname,=20$relname)=20=3D=20@_;=0A+=09my=20$toastname=20=3D=20= relation_toast($dbname,=20$relname);=0A+=09= plan_to_remove_relation_file($dbname,=20$toastname)=20if=20($toastname);=0A= +}=0A+=0A+#=20Corrupts=20the=20first=20page=20of=20the=20given=20file=20= path=0A+sub=20corrupt_first_page($)=0A+{=0A+=09my=20($relpath)=20=3D=20= @_;=0A+=0A+=09my=20$fh;=0A+=09open($fh,=20'+<',=20$relpath)=0A+=09=09or=20= BAIL_OUT("open=20failed:=20$!");=0A+=09binmode=20$fh;=0A+=0A+=09#=20= Corrupt=20some=20line=20pointers.=20=20The=20values=20are=20chosen=20to=20= hit=20the=0A+=09#=20various=20line-pointer-corruption=20checks=20in=20= verify_heapam.c=0A+=09#=20on=20both=20little-endian=20and=20big-endian=20= architectures.=0A+=09seek($fh,=2032,=200)=0A+=09=09or=20BAIL_OUT("seek=20= failed:=20$!");=0A+=09syswrite(=0A+=09=09$fh,=0A+=09=09pack("L*",=0A+=09=09= =090xAAA15550,=200xAAA0D550,=200x00010000,=0A+=09=09=090x00008000,=20= 0x0000800F,=200x001e8000,=0A+=09=09=090xFFFFFFFF)=0A+=09)=20or=20= BAIL_OUT("syswrite=20failed:=20$!");=0A+=09close($fh)=0A+=09=09or=20= BAIL_OUT("close=20failed:=20$!");=0A+}=0A+=0A+#=20Stops=20the=20node,=20= performs=20all=20the=20corruptions=20previously=20planned,=20and=0A+#=20= starts=20the=20node=20again.=0A+#=0A+sub=20perform_all_corruptions()=0A= +{=0A+=09$node->stop();=0A+=09for=20my=20$relpath=20(keys=20= %corrupt_page)=0A+=09{=0A+=09=09corrupt_first_page($relpath);=0A+=09}=0A= +=09for=20my=20$relpath=20(keys=20%remove_relation)=0A+=09{=0A+=09=09= unlink($relpath);=0A+=09}=0A+=09$node->start;=0A+}=0A+=0A+#=20Test=20= set-up=0A+$node=20=3D=20get_new_node('test');=0A+$node->init;=0A= +$node->start;=0A+$port=20=3D=20$node->port;=0A+=0A+for=20my=20$dbname=20= (qw(db1=20db2=20db3))=0A+{=0A+=09#=20Create=20the=20database=0A+=09= $node->safe_psql('postgres',=20qq(CREATE=20DATABASE=20$dbname));=0A+=0A+=09= #=20Load=20the=20amcheck=20extension,=20upon=20which=20pg_amcheck=20= depends.=20=20Put=20the=0A+=09#=20extension=20in=20an=20unexpected=20= location=20to=20test=20that=20pg_amcheck=20finds=20it=0A+=09#=20= correctly.=20=20Create=20tables=20with=20names=20that=20look=20like=20= pg_catalog=20names=20to=0A+=09#=20check=20that=20pg_amcheck=20does=20not=20= get=20confused=20by=20them.=20=20Create=20functions=20in=0A+=09#=20= schema=20public=20that=20look=20like=20amcheck=20functions=20to=20check=20= that=20pg_amcheck=0A+=09#=20does=20not=20use=20them.=0A+=09= $node->safe_psql($dbname,=20q(=0A+=09=09CREATE=20SCHEMA=20= amcheck_schema;=0A+=09=09CREATE=20EXTENSION=20amcheck=20WITH=20SCHEMA=20= amcheck_schema;=0A+=09=09CREATE=20TABLE=20amcheck_schema.pg_database=20= (junk=20text);=0A+=09=09CREATE=20TABLE=20amcheck_schema.pg_namespace=20= (junk=20text);=0A+=09=09CREATE=20TABLE=20amcheck_schema.pg_class=20(junk=20= text);=0A+=09=09CREATE=20TABLE=20amcheck_schema.pg_operator=20(junk=20= text);=0A+=09=09CREATE=20TABLE=20amcheck_schema.pg_proc=20(junk=20text);=0A= +=09=09CREATE=20TABLE=20amcheck_schema.pg_tablespace=20(junk=20text);=0A= +=0A+=09=09CREATE=20FUNCTION=20public.bt_index_check(index=20regclass,=0A= +=09=09=09=09=09=09=09=09=09=09=09=20=20heapallindexed=20boolean=20= default=20false)=0A+=09=09RETURNS=20VOID=20AS=20$$=0A+=09=09BEGIN=0A+=09=09= =09RAISE=20EXCEPTION=20'Invoked=20wrong=20bt_index_check!';=0A+=09=09= END;=0A+=09=09$$=20LANGUAGE=20plpgsql;=0A+=0A+=09=09CREATE=20FUNCTION=20= public.bt_index_parent_check(index=20regclass,=0A+=09=09=09=09=09=09=09=09= =09=09=09=09=09=20heapallindexed=20boolean=20default=20false,=0A+=09=09=09= =09=09=09=09=09=09=09=09=09=09=20rootdescend=20boolean=20default=20= false)=0A+=09=09RETURNS=20VOID=20AS=20$$=0A+=09=09BEGIN=0A+=09=09=09= RAISE=20EXCEPTION=20'Invoked=20wrong=20bt_index_parent_check!';=0A+=09=09= END;=0A+=09=09$$=20LANGUAGE=20plpgsql;=0A+=0A+=09=09CREATE=20FUNCTION=20= public.verify_heapam(relation=20regclass,=0A+=09=09=09=09=09=09=09=09=09=09= =09=20on_error_stop=20boolean=20default=20false,=0A+=09=09=09=09=09=09=09= =09=09=09=09=20check_toast=20boolean=20default=20false,=0A+=09=09=09=09=09= =09=09=09=09=09=09=20skip=20text=20default=20'none',=0A+=09=09=09=09=09=09= =09=09=09=09=09=20startblock=20bigint=20default=20null,=0A+=09=09=09=09=09= =09=09=09=09=09=09=20endblock=20bigint=20default=20null,=0A+=09=09=09=09=09= =09=09=09=09=09=09=20blkno=20OUT=20bigint,=0A+=09=09=09=09=09=09=09=09=09= =09=09=20offnum=20OUT=20integer,=0A+=09=09=09=09=09=09=09=09=09=09=09=20= attnum=20OUT=20integer,=0A+=09=09=09=09=09=09=09=09=09=09=09=20msg=20OUT=20= text)=0A+=09=09RETURNS=20SETOF=20record=20AS=20$$=0A+=09=09BEGIN=0A+=09=09= =09RAISE=20EXCEPTION=20'Invoked=20wrong=20verify_heapam!';=0A+=09=09END;=0A= +=09=09$$=20LANGUAGE=20plpgsql;=0A+=09));=0A+=0A+=09#=20Create=20= schemas,=20tables=20and=20indexes=20in=20five=20separate=0A+=09#=20= schemas.=20=20The=20schemas=20are=20all=20identical=20to=20start,=20but=0A= +=09#=20we=20will=20corrupt=20them=20differently=20later.=0A+=09#=0A+=09= for=20my=20$schema=20(qw(s1=20s2=20s3=20s4=20s5))=0A+=09{=0A+=09=09= $node->safe_psql($dbname,=20qq(=0A+=09=09=09CREATE=20SCHEMA=20$schema;=0A= +=09=09=09CREATE=20SEQUENCE=20$schema.seq1;=0A+=09=09=09CREATE=20= SEQUENCE=20$schema.seq2;=0A+=09=09=09CREATE=20TABLE=20$schema.t1=20(=0A+=09= =09=09=09i=20INTEGER,=0A+=09=09=09=09b=20BOX,=0A+=09=09=09=09ia=20= int4[],=0A+=09=09=09=09ir=20int4range,=0A+=09=09=09=09t=20TEXT=0A+=09=09=09= );=0A+=09=09=09CREATE=20TABLE=20$schema.t2=20(=0A+=09=09=09=09i=20= INTEGER,=0A+=09=09=09=09b=20BOX,=0A+=09=09=09=09ia=20int4[],=0A+=09=09=09= =09ir=20int4range,=0A+=09=09=09=09t=20TEXT=0A+=09=09=09);=0A+=09=09=09= CREATE=20VIEW=20$schema.t2_view=20AS=20(=0A+=09=09=09=09SELECT=20i*2,=20= t=20FROM=20$schema.t2=0A+=09=09=09);=0A+=09=09=09ALTER=20TABLE=20= $schema.t2=0A+=09=09=09=09ALTER=20COLUMN=20t=0A+=09=09=09=09SET=20= STORAGE=20EXTERNAL;=0A+=0A+=09=09=09INSERT=20INTO=20$schema.t1=20(i,=20= b,=20ia,=20ir,=20t)=0A+=09=09=09=09(SELECT=20gs::INTEGER=20AS=20i,=0A+=09= =09=09=09=09=09box(point(gs,gs+5),point(gs*2,gs*3))=20AS=20b,=0A+=09=09=09= =09=09=09array[gs,=20gs=20+=201]::int4[]=20AS=20ia,=0A+=09=09=09=09=09=09= int4range(gs,=20gs+100)=20AS=20ir,=0A+=09=09=09=09=09=09repeat('foo',=20= gs)=20AS=20t=0A+=09=09=09=09=09=20FROM=20generate_series(1,10000,3000)=20= AS=20gs);=0A+=0A+=09=09=09INSERT=20INTO=20$schema.t2=20(i,=20b,=20ia,=20= ir,=20t)=0A+=09=09=09=09(SELECT=20gs::INTEGER=20AS=20i,=0A+=09=09=09=09=09= =09box(point(gs,gs+5),point(gs*2,gs*3))=20AS=20b,=0A+=09=09=09=09=09=09= array[gs,=20gs=20+=201]::int4[]=20AS=20ia,=0A+=09=09=09=09=09=09= int4range(gs,=20gs+100)=20AS=20ir,=0A+=09=09=09=09=09=09repeat('foo',=20= gs)=20AS=20t=0A+=09=09=09=09=09=20FROM=20generate_series(1,10000,3000)=20= AS=20gs);=0A+=0A+=09=09=09CREATE=20MATERIALIZED=20VIEW=20$schema.t1_mv=20= AS=20SELECT=20*=20FROM=20$schema.t1;=0A+=09=09=09CREATE=20MATERIALIZED=20= VIEW=20$schema.t2_mv=20AS=20SELECT=20*=20FROM=20$schema.t2;=0A+=0A+=09=09= =09create=20table=20$schema.p1=20(a=20int,=20b=20int)=20PARTITION=20BY=20= list=20(a);=0A+=09=09=09create=20table=20$schema.p2=20(a=20int,=20b=20= int)=20PARTITION=20BY=20list=20(a);=0A+=0A+=09=09=09create=20table=20= $schema.p1_1=20partition=20of=20$schema.p1=20for=20values=20in=20(1,=20= 2,=203);=0A+=09=09=09create=20table=20$schema.p1_2=20partition=20of=20= $schema.p1=20for=20values=20in=20(4,=205,=206);=0A+=09=09=09create=20= table=20$schema.p2_1=20partition=20of=20$schema.p2=20for=20values=20in=20= (1,=202,=203);=0A+=09=09=09create=20table=20$schema.p2_2=20partition=20= of=20$schema.p2=20for=20values=20in=20(4,=205,=206);=0A+=0A+=09=09=09= CREATE=20INDEX=20t1_btree=20ON=20$schema.t1=20USING=20BTREE=20(i);=0A+=09= =09=09CREATE=20INDEX=20t2_btree=20ON=20$schema.t2=20USING=20BTREE=20(i);=0A= +=0A+=09=09=09CREATE=20INDEX=20t1_hash=20ON=20$schema.t1=20USING=20HASH=20= (i);=0A+=09=09=09CREATE=20INDEX=20t2_hash=20ON=20$schema.t2=20USING=20= HASH=20(i);=0A+=0A+=09=09=09CREATE=20INDEX=20t1_brin=20ON=20$schema.t1=20= USING=20BRIN=20(i);=0A+=09=09=09CREATE=20INDEX=20t2_brin=20ON=20= $schema.t2=20USING=20BRIN=20(i);=0A+=0A+=09=09=09CREATE=20INDEX=20= t1_gist=20ON=20$schema.t1=20USING=20GIST=20(b);=0A+=09=09=09CREATE=20= INDEX=20t2_gist=20ON=20$schema.t2=20USING=20GIST=20(b);=0A+=0A+=09=09=09= CREATE=20INDEX=20t1_gin=20ON=20$schema.t1=20USING=20GIN=20(ia);=0A+=09=09= =09CREATE=20INDEX=20t2_gin=20ON=20$schema.t2=20USING=20GIN=20(ia);=0A+=0A= +=09=09=09CREATE=20INDEX=20t1_spgist=20ON=20$schema.t1=20USING=20SPGIST=20= (ir);=0A+=09=09=09CREATE=20INDEX=20t2_spgist=20ON=20$schema.t2=20USING=20= SPGIST=20(ir);=0A+=09=09));=0A+=09}=0A+}=0A+=0A+#=20Database=20'db1'=20= corruptions=0A+#=0A+=0A+#=20Corrupt=20indexes=20in=20schema=20"s1"=0A= +plan_to_remove_relation_file('db1',=20's1.t1_btree');=0A= +plan_to_corrupt_first_page('db1',=20's1.t2_btree');=0A+=0A+#=20Corrupt=20= tables=20in=20schema=20"s2"=0A+plan_to_remove_relation_file('db1',=20= 's2.t1');=0A+plan_to_corrupt_first_page('db1',=20's2.t2');=0A+=0A+#=20= Corrupt=20tables,=20partitions,=20matviews,=20and=20btrees=20in=20schema=20= "s3"=0A+plan_to_remove_relation_file('db1',=20's3.t1');=0A= +plan_to_corrupt_first_page('db1',=20's3.t2');=0A+=0A= +plan_to_remove_relation_file('db1',=20's3.t1_mv');=0A= +plan_to_remove_relation_file('db1',=20's3.p1_1');=0A+=0A= +plan_to_corrupt_first_page('db1',=20's3.t2_mv');=0A= +plan_to_corrupt_first_page('db1',=20's3.p2_1');=0A+=0A= +plan_to_remove_relation_file('db1',=20's3.t1_btree');=0A= +plan_to_corrupt_first_page('db1',=20's3.t2_btree');=0A+=0A+#=20Corrupt=20= toast=20table,=20partitions,=20and=20materialized=20views=20in=20schema=20= "s4"=0A+plan_to_remove_toast_file('db1',=20's4.t2');=0A+=0A+#=20Corrupt=20= all=20other=20object=20types=20in=20schema=20"s5".=20=20We=20don't=20= have=20amcheck=20support=0A+#=20for=20these=20types,=20but=20we=20check=20= that=20their=20corruption=20does=20not=20trigger=20any=0A+#=20errors=20= in=20pg_amcheck=0A+plan_to_remove_relation_file('db1',=20's5.seq1');=0A= +plan_to_remove_relation_file('db1',=20's5.t1_hash');=0A= +plan_to_remove_relation_file('db1',=20's5.t1_gist');=0A= +plan_to_remove_relation_file('db1',=20's5.t1_gin');=0A= +plan_to_remove_relation_file('db1',=20's5.t1_brin');=0A= +plan_to_remove_relation_file('db1',=20's5.t1_spgist');=0A+=0A= +plan_to_corrupt_first_page('db1',=20's5.seq2');=0A= +plan_to_corrupt_first_page('db1',=20's5.t2_hash');=0A= +plan_to_corrupt_first_page('db1',=20's5.t2_gist');=0A= +plan_to_corrupt_first_page('db1',=20's5.t2_gin');=0A= +plan_to_corrupt_first_page('db1',=20's5.t2_brin');=0A= +plan_to_corrupt_first_page('db1',=20's5.t2_spgist');=0A+=0A+=0A+#=20= Database=20'db2'=20corruptions=0A+#=0A= +plan_to_remove_relation_file('db2',=20's1.t1');=0A= +plan_to_remove_relation_file('db2',=20's1.t1_btree');=0A+=0A+=0A+#=20= Leave=20'db3'=20uncorrupted=0A+#=0A+=0A+#=20Perform=20the=20corruptions=20= we=20planned=20above=20using=20only=20a=20single=20database=20restart.=0A= +#=0A+perform_all_corruptions();=0A+=0A+=0A+#=20Standard=20first=20= arguments=20to=20TestLib=20functions=0A+my=20@cmd=20=3D=20('pg_amcheck',=20= '--quiet',=20'-p',=20$port);=0A+=0A+#=20Regular=20expressions=20to=20= match=20various=20expected=20output=0A+my=20$no_output_re=20=3D=20= qr/^$/;=0A+my=20$line_pointer_corruption_re=20=3D=20qr/line=20pointer/;=0A= +my=20$missing_file_re=20=3D=20qr/could=20not=20open=20file=20".*":=20No=20= such=20file=20or=20directory/;=0A+my=20$index_missing_relation_fork_re=20= =3D=20qr/index=20".*"=20lacks=20a=20main=20relation=20fork/;=0A+=0A+#=20= Checking=20databases=20with=20amcheck=20installed=20and=20corrupt=20= relations,=20pg_amcheck=0A+#=20command=20itself=20should=20return=20exit=20= status=20=3D=202,=20because=20tables=20and=20indexes=20are=0A+#=20= corrupt,=20not=20exit=20status=20=3D=201,=20which=20would=20mean=20the=20= pg_amcheck=20command=20itself=0A+#=20failed.=20=20Corruption=20messages=20= should=20go=20to=20stdout,=20and=20nothing=20to=20stderr.=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1'=20],=0A+=092,=0A+=09= [=20$index_missing_relation_fork_re,=0A+=09=20=20= $line_pointer_corruption_re,=0A+=09=20=20$missing_file_re,=0A+=09],=0A+=09= [=20$no_output_re=20],=0A+=09'pg_amcheck=20all=20schemas,=20tables=20and=20= indexes=20in=20database=20db1');=0A+=0A+$node->command_checks_all(=0A+=09= [=20@cmd,=20'-d',=20'db1',=20'-d',=20'db2',=20'-d',=20'db3'=20],=0A+=09= 2,=0A+=09[=20$index_missing_relation_fork_re,=0A+=09=20=20= $line_pointer_corruption_re,=0A+=09=20=20$missing_file_re,=0A+=09],=0A+=09= [=20$no_output_re=20],=0A+=09'pg_amcheck=20all=20schemas,=20tables=20and=20= indexes=20in=20databases=20db1,=20db2,=20and=20db3');=0A+=0A+#=20Scans=20= of=20indexes=20in=20s1=20should=20detect=20the=20specific=20corruption=20= that=20we=20created=0A+#=20above.=20=20For=20missing=20relation=20forks,=20= we=20know=20what=20the=20error=20message=20looks=0A+#=20like.=20=20For=20= corrupted=20index=20pages,=20the=20error=20might=20vary=20depending=20on=20= how=20the=0A+#=20page=20was=20formatted=20on=20disk,=20including=20= variations=20due=20to=20alignment=20differences=0A+#=20between=20= platforms,=20so=20we=20accept=20any=20non-empty=20error=20message.=0A+#=0A= +#=20If=20we=20don't=20limit=20the=20check=20to=20databases=20with=20= amcheck=20installed,=20we=20expect=0A+#=20complaint=20on=20stderr,=20but=20= otherwise=20stderr=20should=20be=20quiet.=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'--all',=20'-s',=20's1',=20= '-i',=20't1_btree'=20],=0A+=092,=0A+=09[=20= $index_missing_relation_fork_re=20],=0A+=09[=20qr/pg_amcheck:=20warning:=20= skipping=20database=20"postgres":=20amcheck=20is=20not=20installed/=20],=0A= +=09'pg_amcheck=20index=20s1.t1_btree=20reports=20missing=20main=20= relation=20fork');=0A+=0A+$node->command_checks_all(=0A+=09[=20@cmd,=20= '-d',=20'db1',=20'-s',=20's1',=20'-i',=20't2_btree'=20],=0A+=092,=0A+=09= [=20qr/.+/=20],=09=09=09#=20Any=20non-empty=20error=20message=20is=20= acceptable=0A+=09[=20$no_output_re=20],=0A+=09'pg_amcheck=20index=20= s1.s2=20reports=20index=20corruption');=0A+=0A+#=20Checking=20db1.s1=20= with=20indexes=20excluded=20should=20show=20no=20corruptions=20because=20= we=0A+#=20did=20not=20corrupt=20any=20tables=20in=20db1.s1.=20=20Verify=20= that=20both=20stdout=20and=20stderr=0A+#=20are=20quiet.=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-t',=20's1.*',=20= '--no-dependent-indexes'=20],=0A+=090,=0A+=09[=20$no_output_re=20],=0A+=09= [=20$no_output_re=20],=0A+=09'pg_amcheck=20of=20db1.s1=20excluding=20= indexes');=0A+=0A+#=20Checking=20db2.s1=20should=20show=20table=20= corruptions=20if=20indexes=20are=20excluded=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db2',=20'-t',=20's1.*',=20= '--no-dependent-indexes'=20],=0A+=092,=0A+=09[=20$missing_file_re=20],=0A= +=09[=20$no_output_re=20],=0A+=09'pg_amcheck=20of=20db2.s1=20excluding=20= indexes');=0A+=0A+#=20In=20schema=20db1.s3,=20the=20tables=20and=20= indexes=20are=20both=20corrupt.=20=20We=20should=20see=0A+#=20corruption=20= messages=20on=20stdout,=20and=20nothing=20on=20stderr.=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's3'=20],=0A= +=092,=0A+=09[=20$index_missing_relation_fork_re,=0A+=09=20=20= $line_pointer_corruption_re,=0A+=09=20=20$missing_file_re,=0A+=09],=0A+=09= [=20$no_output_re=20],=0A+=09'pg_amcheck=20schema=20s3=20reports=20table=20= and=20index=20errors');=0A+=0A+#=20In=20schema=20db1.s4,=20only=20toast=20= tables=20are=20corrupt.=20=20Check=20that=20under=20default=0A+#=20= options=20the=20toast=20corruption=20is=20reported,=20but=20when=20= excluding=20toast=20we=20get=20no=0A+#=20error=20reports.=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's4'=20],=0A= +=092,=0A+=09[=20$missing_file_re=20],=0A+=09[=20$no_output_re=20],=0A+=09= 'pg_amcheck=20in=20schema=20s4=20reports=20toast=20corruption');=0A+=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'--no-dependent-toast',=20= '--exclude-toast-pointers',=20'db1',=20'-s',=20's4'=20],=0A+=090,=0A+=09= [=20$no_output_re=20],=0A+=09[=20$no_output_re=20],=0A+=09'pg_amcheck=20= in=20schema=20s4=20excluding=20toast=20reports=20no=20corruption');=0A+=0A= +#=20Check=20that=20no=20corruption=20is=20reported=20in=20schema=20= db1.s5=0A+$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-s',=20= 's5'=20],=0A+=090,=0A+=09[=20$no_output_re=20],=0A+=09[=20$no_output_re=20= ],=0A+=09'pg_amcheck=20over=20schema=20s5=20reports=20no=20corruption');=0A= +=0A+#=20In=20schema=20db1.s1,=20only=20indexes=20are=20corrupt.=20=20= Verify=20that=20when=20we=20exclude=0A+#=20the=20indexes,=20no=20= corruption=20is=20reported=20about=20the=20schema.=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's1',=20= '-I',=20't1_btree',=20'-I',=20't2_btree'=20],=0A+=090,=0A+=09[=20= $no_output_re=20],=0A+=09[=20$no_output_re=20],=0A+=09'pg_amcheck=20over=20= schema=20s1=20with=20corrupt=20indexes=20excluded=20reports=20no=20= corruption');=0A+=0A+#=20In=20schema=20db1.s1,=20only=20indexes=20are=20= corrupt.=20=20Verify=20that=20when=20we=20provide=20only=0A+#=20table=20= inclusions,=20and=20disable=20index=20expansion,=20no=20corruption=20is=20= reported=0A+#=20about=20the=20schema.=0A+#=0A+$node->command_checks_all(=0A= +=09[=20@cmd,=20'db1',=20'-t',=20's1.*',=20'--no-dependent-indexes'=20],=0A= +=090,=0A+=09[=20$no_output_re=20],=0A+=09[=20$no_output_re=20],=0A+=09= 'pg_amcheck=20over=20schema=20s1=20with=20all=20indexes=20excluded=20= reports=20no=20corruption');=0A+=0A+#=20In=20schema=20db1.s2,=20only=20= tables=20are=20corrupt.=20=20Verify=20that=20when=20we=20exclude=20those=0A= +#=20tables=20that=20no=20corruption=20is=20reported.=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's2',=20= '-T',=20't1',=20'-T',=20't2'=20],=0A+=090,=0A+=09[=20$no_output_re=20],=0A= +=09[=20$no_output_re=20],=0A+=09'pg_amcheck=20over=20schema=20s2=20with=20= corrupt=20tables=20excluded=20reports=20no=20corruption');=0A+=0A+#=20= Check=20errors=20about=20bad=20block=20range=20command=20line=20= arguments.=20=20We=20use=20schema=20s5=0A+#=20to=20avoid=20getting=20= messages=20about=20corrupt=20tables=20or=20indexes.=0A+#=0A= +command_fails_like(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's5',=20= '--startblock',=20'junk'=20],=0A+=09qr/invalid=20start=20block/,=0A+=09= 'pg_amcheck=20rejects=20garbage=20startblock');=0A+=0A= +command_fails_like(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's5',=20= '--endblock',=20'1234junk'=20],=0A+=09qr/invalid=20end=20block/,=0A+=09= 'pg_amcheck=20rejects=20garbage=20endblock');=0A+=0A+command_fails_like(=0A= +=09[=20@cmd,=20'db1',=20'-s',=20's5',=20'--startblock',=20'5',=20= '--endblock',=20'4'=20],=0A+=09qr/end=20block=20precedes=20start=20= block/,=0A+=09'pg_amcheck=20rejects=20invalid=20block=20range');=0A+=0A= +#=20Check=20bt_index_parent_check=20alternates.=20=20We=20don't=20= create=20any=20index=20corruption=0A+#=20that=20would=20behave=20= differently=20under=20these=20modes,=20so=20just=20smoke=20test=20that=20= the=0A+#=20arguments=20are=20handled=20sensibly.=0A+#=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's1',=20= '-i',=20't1_btree',=20'--parent-check'=20],=0A+=092,=0A+=09[=20= $index_missing_relation_fork_re=20],=0A+=09[=20$no_output_re=20],=0A+=09= 'pg_amcheck=20smoke=20test=20--parent-check');=0A+=0A= +$node->command_checks_all(=0A+=09[=20@cmd,=20'db1',=20'-s',=20's1',=20= '-i',=20't1_btree',=20'--heapallindexed',=20'--rootdescend'=20],=0A+=09= 2,=0A+=09[=20$index_missing_relation_fork_re=20],=0A+=09[=20= $no_output_re=20],=0A+=09'pg_amcheck=20smoke=20test=20--heapallindexed=20= --rootdescend');=0A+=0A+$node->command_checks_all(=0A+=09[=20@cmd,=20= '-d',=20'db1',=20'-d',=20'db2',=20'-d',=20'db3',=20'-S',=20's*'=20],=0A+=09= 0,=0A+=09[=20$no_output_re=20],=0A+=09[=20$no_output_re=20],=0A+=09= 'pg_amcheck=20excluding=20all=20corrupt=20schemas');=0Adiff=20--git=20= a/src/bin/pg_amcheck/t/004_verify_heapam.pl=20= b/src/bin/pg_amcheck/t/004_verify_heapam.pl=0Anew=20file=20mode=20100644=0A= index=200000000000..48dfbef145=0A---=20/dev/null=0A+++=20= b/src/bin/pg_amcheck/t/004_verify_heapam.pl=0A@@=20-0,0=20+1,516=20@@=0A= +use=20strict;=0A+use=20warnings;=0A+=0A+use=20PostgresNode;=0A+use=20= TestLib;=0A+=0A+use=20Test::More;=0A+=0A+#=20This=20regression=20test=20= demonstrates=20that=20the=20pg_amcheck=20binary=20correctly=0A+#=20= identifies=20specific=20kinds=20of=20corruption=20within=20pages.=20=20= To=20test=20this,=20we=20need=0A+#=20a=20mechanism=20to=20create=20= corrupt=20pages=20with=20predictable,=20repeatable=20corruption.=0A+#=20= The=20postgres=20backend=20cannot=20be=20expected=20to=20help=20us=20= with=20this,=20as=20its=20design=0A+#=20is=20not=20consistent=20with=20= the=20goal=20of=20intentionally=20corrupting=20pages.=0A+#=0A+#=20= Instead,=20we=20create=20a=20table=20to=20corrupt,=20and=20with=20= careful=20consideration=20of=20how=0A+#=20postgresql=20lays=20out=20heap=20= pages,=20we=20seek=20to=20offsets=20within=20the=20page=20and=0A+#=20= overwrite=20deliberately=20chosen=20bytes=20with=20specific=20values=20= calculated=20to=0A+#=20corrupt=20the=20page=20in=20expected=20ways.=20=20= We=20then=20verify=20that=20pg_amcheck=20reports=0A+#=20the=20= corruption,=20and=20that=20it=20runs=20without=20crashing.=20=20Note=20= that=20the=20backend=0A+#=20cannot=20simply=20be=20started=20to=20run=20= queries=20against=20the=20corrupt=20table,=20as=20the=0A+#=20backend=20= will=20crash,=20at=20least=20for=20some=20of=20the=20corruption=20types=20= we=20generate.=0A+#=0A+#=20Autovacuum=20potentially=20touching=20the=20= table=20in=20the=20background=20makes=20the=20exact=0A+#=20behavior=20of=20= this=20test=20harder=20to=20reason=20about.=20=20We=20turn=20it=20off=20= to=20keep=20things=0A+#=20simpler.=20=20We=20use=20a=20"belt=20and=20= suspenders"=20approach,=20turning=20it=20off=20for=20the=0A+#=20system=20= generally=20in=20postgresql.conf,=20and=20turning=20it=20off=20= specifically=20for=20the=0A+#=20test=20table.=0A+#=0A+#=20This=20test=20= depends=20on=20the=20table=20being=20written=20to=20the=20heap=20file=20= exactly=20as=20we=0A+#=20expect=20it=20to=20be,=20so=20we=20take=20care=20= to=20arrange=20the=20columns=20of=20the=20table,=20and=0A+#=20insert=20= rows=20of=20the=20table,=20that=20give=20predictable=20sizes=20and=20= locations=20within=0A+#=20the=20table=20page.=0A+#=0A+#=20The=20= HeapTupleHeaderData=20has=2023=20bytes=20of=20fixed=20size=20fields=20= before=20the=20variable=0A+#=20length=20t_bits[]=20array.=20=20We=20have=20= exactly=203=20columns=20in=20the=20table,=20so=20natts=20=3D=203,=0A+#=20= t_bits=20is=201=20byte=20long,=20and=20t_hoff=20=3D=20MAXALIGN(23=20+=20= 1)=20=3D=2024.=0A+#=0A+#=20We're=20not=20too=20fussy=20about=20which=20= datatypes=20we=20use=20for=20the=20test,=20but=20we=20do=20care=0A+#=20= about=20some=20specific=20properties.=20=20We'd=20like=20to=20test=20= both=20fixed=20size=20and=0A+#=20varlena=20types.=20=20We'd=20like=20= some=20varlena=20data=20inline=20and=20some=20toasted.=20=20And=0A+#=20= we'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+#=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=09= or=20BAIL_OUT("seek=20failed:=20$!");=0A+=09defined(sysread($fh,=20= $buffer,=20HEAPTUPLE_PACK_LENGTH))=0A+=09=09or=20BAIL_OUT("sysread=20= failed:=20$!");=0A+=0A+=09@_=20=3D=20unpack(HEAPTUPLE_PACK_CODE,=20= $buffer);=0A+=09%tup=20=3D=20(t_xmin=20=3D>=20shift,=0A+=09=09=09t_xmax=20= =3D>=20shift,=0A+=09=09=09t_field3=20=3D>=20shift,=0A+=09=09=09bi_hi=20= =3D>=20shift,=0A+=09=09=09bi_lo=20=3D>=20shift,=0A+=09=09=09ip_posid=20= =3D>=20shift,=0A+=09=09=09t_infomask2=20=3D>=20shift,=0A+=09=09=09= t_infomask=20=3D>=20shift,=0A+=09=09=09t_hoff=20=3D>=20shift,=0A+=09=09=09= t_bits=20=3D>=20shift,=0A+=09=09=09a=20=3D>=20shift,=0A+=09=09=09= b_header=20=3D>=20shift,=0A+=09=09=09b_body1=20=3D>=20shift,=0A+=09=09=09= b_body2=20=3D>=20shift,=0A+=09=09=09b_body3=20=3D>=20shift,=0A+=09=09=09= b_body4=20=3D>=20shift,=0A+=09=09=09b_body5=20=3D>=20shift,=0A+=09=09=09= b_body6=20=3D>=20shift,=0A+=09=09=09b_body7=20=3D>=20shift,=0A+=09=09=09= c1=20=3D>=20shift,=0A+=09=09=09c2=20=3D>=20shift,=0A+=09=09=09c3=20=3D>=20= shift,=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>=20shift,=0A+=09=09=09c8=20= =3D>=20shift,=0A+=09=09=09c9=20=3D>=20shift);=0A+=09#=20Stitch=20= together=20the=20text=20for=20column=20'b'=0A+=09$tup{b}=20=3D=20= join('',=20map=20{=20chr($tup{"b_body$_"})=20}=20(1..7));=0A+=09return=20= \%tup;=0A+}=0A+=0A+#=20Write=20a=20tuple=20of=20our=20table=20to=20a=20= heap=20page.=0A+#=0A+#=20Takes=20an=20open=20filehandle=20to=20the=20= heap=20file,=20the=20offset=20of=20the=20tuple,=20and=20a=0A+#=20= reference=20to=20a=20hash=20with=20the=20tuple=20values,=20as=20returned=20= by=20read_tuple().=0A+#=20Writes=20the=20tuple=20fields=20from=20the=20= hash=20into=20the=20heap=20file.=0A+#=0A+#=20The=20purpose=20of=20this=20= function=20is=20to=20write=20a=20tuple=20back=20to=20disk=20with=20some=0A= +#=20subset=20of=20fields=20modified.=20=20The=20function=20does=20no=20= error=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+=09=09or=20= BAIL_OUT("seek=20failed:=20$!");=0A+=09defined(syswrite($fh,=20$buffer,=20= HEAPTUPLE_PACK_LENGTH))=0A+=09=09or=20BAIL_OUT("syswrite=20failed:=20= $!");;=0A+=09return;=0A+}=0A+=0A+#=20Set=20umask=20so=20test=20= directories=20and=20files=20are=20created=20with=20default=20permissions=0A= +umask(0077);=0A+=0A+#=20Set=20up=20the=20node.=20=20Once=20we=20create=20= and=20corrupt=20the=20table,=0A+#=20autovacuum=20workers=20visiting=20= the=20table=20could=20crash=20the=20backend.=0A+#=20Disable=20autovacuum=20= so=20that=20won't=20happen.=0A+my=20$node=20=3D=20get_new_node('test');=0A= +$node->init;=0A+$node->append_conf('postgresql.conf',=20= 'autovacuum=3Doff');=0A+=0A+#=20Start=20the=20node=20and=20load=20the=20= extensions.=20=20We=20depend=20on=20both=0A+#=20amcheck=20and=20= pageinspect=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=20amcheck");=0A= +$node->safe_psql('postgres',=20"CREATE=20EXTENSION=20pageinspect");=0A+=0A= +#=20Get=20a=20non-zero=20datfrozenxid=0A+$node->safe_psql('postgres',=20= qq(VACUUM=20FREEZE));=0A+=0A+#=20Create=20the=20test=20table=20with=20= precisely=20the=20schema=20that=20our=20corruption=20function=0A+#=20= expects.=0A+$node->safe_psql(=0A+=09'postgres',=20qq(=0A+=09=09CREATE=20= TABLE=20public.test=20(a=20BIGINT,=20b=20TEXT,=20c=20TEXT);=0A+=09=09= ALTER=20TABLE=20public.test=20SET=20(autovacuum_enabled=3Dfalse);=0A+=09=09= ALTER=20TABLE=20public.test=20ALTER=20COLUMN=20c=20SET=20STORAGE=20= EXTERNAL;=0A+=09=09CREATE=20INDEX=20test_idx=20ON=20public.test(a,=20b);=0A= +=09));=0A+=0A+#=20We=20want=20(0=20<=20datfrozenxid=20<=20= test.relfrozenxid).=20=20To=20achieve=20this,=20we=20freeze=0A+#=20an=20= otherwise=20unused=20table,=20public.junk,=20prior=20to=20inserting=20= data=20and=20freezing=0A+#=20public.test=0A+$node->safe_psql(=0A+=09= 'postgres',=20qq(=0A+=09=09CREATE=20TABLE=20public.junk=20AS=20SELECT=20= 'junk'::TEXT=20AS=20junk_column;=0A+=09=09ALTER=20TABLE=20public.junk=20= SET=20(autovacuum_enabled=3Dfalse);=0A+=09=09VACUUM=20FREEZE=20= public.junk=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+#=20Insert=20data=20and=20freeze=20public.test=0A= +use=20constant=20ROWCOUNT=20=3D>=2016;=0A+$node->safe_psql('postgres',=20= qq(=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',=20= 10000)=0A+=09=09);=0A+=09VACUUM=20FREEZE=20public.test=0A+=09))=20for=20= (1..ROWCOUNT);=0A+=0A+my=20$relfrozenxid=20=3D=20= $node->safe_psql('postgres',=0A+=09q(select=20relfrozenxid=20from=20= pg_class=20where=20relname=20=3D=20'test'));=0A+my=20$datfrozenxid=20=3D=20= $node->safe_psql('postgres',=0A+=09q(select=20datfrozenxid=20from=20= pg_database=20where=20datname=20=3D=20'postgres'));=0A+=0A+#=20Sanity=20= check=20that=20our=20'test'=20table=20has=20a=20relfrozenxid=20newer=20= than=20the=0A+#=20datfrozenxid=20for=20the=20database,=20and=20that=20= the=20datfrozenxid=20is=20greater=20than=20the=0A+#=20first=20normal=20= xid.=20=20We=20rely=20on=20these=20invariants=20in=20some=20of=20our=20= tests.=0A+if=20($datfrozenxid=20<=3D=203=20||=20$datfrozenxid=20>=3D=20= $relfrozenxid)=0A+{=0A+=09$node->clean_node;=0A+=09plan=20skip_all=20=3D>=20= "Xid=20thresholds=20not=20as=20expected:=20got=20datfrozenxid=20=3D=20= $datfrozenxid,=20relfrozenxid=20=3D=20$relfrozenxid";=0A+=09exit;=0A+}=0A= +=0A+#=20Find=20where=20each=20of=20the=20tuples=20is=20located=20on=20= the=20page.=0A+my=20@lp_off;=0A+for=20my=20$tup=20(0..ROWCOUNT-1)=0A+{=0A= +=09push=20(@lp_off,=20$node->safe_psql('postgres',=20qq(=0A+select=20= lp_off=20from=20heap_page_items(get_raw_page('test',=20'main',=200))=0A+=09= offset=20$tup=20limit=201)));=0A+}=0A+=0A+#=20Sanity=20check=20that=20= our=20'test'=20table=20on=20disk=20layout=20matches=20expectations.=20=20= If=0A+#=20this=20is=20not=20so,=20we=20will=20have=20to=20skip=20the=20= test=20until=20somebody=20updates=20the=20test=0A+#=20to=20work=20on=20= this=20platform.=0A+$node->stop;=0A+my=20$file;=0A+open($file,=20'+<',=20= $relpath)=0A+=09or=20BAIL_OUT("open=20failed:=20$!");=0A+binmode=20= $file;=0A+=0A+for=20(my=20$tupidx=20=3D=200;=20$tupidx=20<=20ROWCOUNT;=20= $tupidx++)=0A+{=0A+=09my=20$offnum=20=3D=20$tupidx=20+=201;=20=20#=20= offnum=20is=201-based,=20not=20zero-based=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+=09my=20$a=20=3D=20$tup->{a};=0A+=09my=20$b=20=3D=20= $tup->{b};=0A+=09if=20($a=20ne=20'12345678'=20||=20$b=20ne=20'abcdefg')=0A= +=09{=0A+=09=09close($file);=20=20#=20ignore=20errors=20on=20close;=20= we're=20exiting=20anyway=0A+=09=09$node->clean_node;=0A+=09=09plan=20= skip_all=20=3D>=20qq(Page=20layout=20differs=20from=20our=20= expectations:=20expected=20(12345678,=20"abcdefg"),=20got=20($a,=20= "$b"));=0A+=09=09exit;=0A+=09}=0A+}=0A+close($file)=0A+=09or=20= BAIL_OUT("close=20failed:=20$!");=0A+$node->start;=0A+=0A+#=20Ok,=20Xids=20= and=20page=20layout=20look=20ok.=20=20We=20can=20run=20corruption=20= tests.=0A+plan=20tests=20=3D>=2020;=0A+=0A+#=20Check=20that=20pg_amcheck=20= runs=20against=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'-p',=20$port,=20'postgres'],=0A+=09=09= =09=09=20=20'pg_amcheck=20test=20table=20and=20index,=20prior=20to=20= corruption');=0A+=0A+$node->stop;=0A+=0A+#=20Some=20#define=20constants=20= from=20access/htup_details.h=20for=20use=20while=20corrupting.=0A+use=20= constant=20HEAP_HASNULL=20=20=20=20=20=20=20=20=20=20=20=20=3D>=20= 0x0001;=0A+use=20constant=20HEAP_XMAX_LOCK_ONLY=20=20=20=20=20=3D>=20= 0x0080;=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+#=20Helper=20function=20to=20generate=20a=20regular=20= expression=20matching=20the=20header=20we=0A+#=20expect=20= verify_heapam()=20to=20return=20given=20which=20fields=20we=20expect=20= to=20be=20non-null.=0A+sub=20header=0A+{=0A+=09my=20($blkno,=20$offnum,=20= $attnum)=20=3D=20@_;=0A+=09return=20qr/heap=20table=20= "postgres"\."public"\."test",=20block=20$blkno,=20offset=20$offnum,=20= attribute=20$attnum:\s+/ms=0A+=09=09if=20(defined=20$attnum);=0A+=09= return=20qr/heap=20table=20"postgres"\."public"\."test",=20block=20= $blkno,=20offset=20$offnum:\s+/ms=0A+=09=09if=20(defined=20$offnum);=0A+=09= return=20qr/heap=20table=20"postgres"\."public"\."test",=20block=20= $blkno:\s+/ms=0A+=09=09if=20(defined=20$blkno);=0A+=09return=20qr/heap=20= table=20"postgres"\."public"\."test":\s+/ms;=0A+}=0A+=0A+#=20Corrupt=20= the=20tuples,=20one=20type=20of=20corruption=20per=20tuple.=20=20Some=20= types=20of=0A+#=20corruption=20cause=20verify_heapam=20to=20skip=20to=20= the=20next=20tuple=20without=0A+#=20performing=20any=20remaining=20= checks,=20so=20we=20can't=20exercise=20the=20system=20properly=20if=0A+#=20= we=20focus=20all=20our=20corruption=20on=20a=20single=20tuple.=0A+#=0A= +my=20@expected;=0A+open($file,=20'+<',=20$relpath)=0A+=09or=20= BAIL_OUT("open=20failed:=20$!");=0A+binmode=20$file;=0A+=0A+for=20(my=20= $tupidx=20=3D=200;=20$tupidx=20<=20ROWCOUNT;=20$tupidx++)=0A+{=0A+=09my=20= $offnum=20=3D=20$tupidx=20+=201;=20=20#=20offnum=20is=201-based,=20not=20= zero-based=0A+=09my=20$offset=20=3D=20$lp_off[$tupidx];=0A+=09my=20$tup=20= =3D=20read_tuple($file,=20$offset);=0A+=0A+=09my=20$header=20=3D=20= header(0,=20$offnum,=20undef);=0A+=09if=20($offnum=20=3D=3D=201)=0A+=09{=0A= +=09=09#=20Corruptly=20set=20xmin=20<=20relfrozenxid=0A+=09=09my=20$xmin=20= =3D=20$relfrozenxid=20-=201;=0A+=09=09$tup->{t_xmin}=20=3D=20$xmin;=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+=0A+=09=09#=20= Expected=20corruption=20report=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${header}xmin=20$xmin=20precedes=20relation=20freeze=20threshold=20= 0:\d+/;=0A+=09}=0A+=09if=20($offnum=20=3D=3D=202)=0A+=09{=0A+=09=09#=20= Corruptly=20set=20xmin=20<=20datfrozenxid=0A+=09=09my=20$xmin=20=3D=203;=0A= +=09=09$tup->{t_xmin}=20=3D=20$xmin;=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+=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${$header}xmin=20$xmin=20precedes=20oldest=20valid=20transaction=20ID=20= 0:\d+/;=0A+=09}=0A+=09elsif=20($offnum=20=3D=3D=203)=0A+=09{=0A+=09=09#=20= Corruptly=20set=20xmin=20<=20datfrozenxid,=20further=20back,=20noting=20= circularity=0A+=09=09#=20of=20xid=20comparison.=20=20For=20a=20new=20= cluster=20with=20epoch=20=3D=200,=20the=20corrupt=0A+=09=09#=20xmin=20= will=20be=20interpreted=20as=20in=20the=20future=0A+=09=09$tup->{t_xmin}=20= =3D=204026531839;=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+=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${$header}xmin=204026531839=20equals=20or=20exceeds=20next=20valid=20= transaction=20ID=200:\d+/;=0A+=09}=0A+=09elsif=20($offnum=20=3D=3D=204)=0A= +=09{=0A+=09=09#=20Corruptly=20set=20xmax=20<=20relminmxid;=0A+=09=09= $tup->{t_xmax}=20=3D=204026531839;=0A+=09=09$tup->{t_infomask}=20&=3D=20= ~HEAP_XMAX_INVALID;=0A+=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${$header}xmax=204026531839=20equals=20or=20exceeds=20next=20valid=20= transaction=20ID=200:\d+/;=0A+=09}=0A+=09elsif=20($offnum=20=3D=3D=205)=0A= +=09{=0A+=09=09#=20Corrupt=20the=20tuple=20t_hoff,=20but=20keep=20it=20= aligned=20properly=0A+=09=09$tup->{t_hoff}=20+=3D=20128;=0A+=0A+=09=09= push=20@expected,=0A+=09=09=09qr/${$header}data=20begins=20at=20offset=20= 152=20beyond=20the=20tuple=20length=2058/,=0A+=09=09=09= qr/${$header}tuple=20data=20should=20begin=20at=20byte=2024,=20but=20= actually=20begins=20at=20byte=20152=20\(3=20attributes,=20no=20nulls\)/;=0A= +=09}=0A+=09elsif=20($offnum=20=3D=3D=206)=0A+=09{=0A+=09=09#=20Corrupt=20= the=20tuple=20t_hoff,=20wrong=20alignment=0A+=09=09$tup->{t_hoff}=20+=3D=20= 3;=0A+=0A+=09=09push=20@expected,=0A+=09=09=09qr/${$header}tuple=20data=20= should=20begin=20at=20byte=2024,=20but=20actually=20begins=20at=20byte=20= 27=20\(3=20attributes,=20no=20nulls\)/;=0A+=09}=0A+=09elsif=20($offnum=20= =3D=3D=207)=0A+=09{=0A+=09=09#=20Corrupt=20the=20tuple=20t_hoff,=20= underflow=20but=20correct=20alignment=0A+=09=09$tup->{t_hoff}=20-=3D=20= 8;=0A+=0A+=09=09push=20@expected,=0A+=09=09=09qr/${$header}tuple=20data=20= should=20begin=20at=20byte=2024,=20but=20actually=20begins=20at=20byte=20= 16=20\(3=20attributes,=20no=20nulls\)/;=0A+=09}=0A+=09elsif=20($offnum=20= =3D=3D=208)=0A+=09{=0A+=09=09#=20Corrupt=20the=20tuple=20t_hoff,=20= underflow=20and=20wrong=20alignment=0A+=09=09$tup->{t_hoff}=20-=3D=203;=0A= +=0A+=09=09push=20@expected,=0A+=09=09=09qr/${$header}tuple=20data=20= should=20begin=20at=20byte=2024,=20but=20actually=20begins=20at=20byte=20= 21=20\(3=20attributes,=20no=20nulls\)/;=0A+=09}=0A+=09elsif=20($offnum=20= =3D=3D=209)=0A+=09{=0A+=09=09#=20Corrupt=20the=20tuple=20to=20look=20= like=20it=20has=20lots=20of=20attributes,=20not=20just=203=0A+=09=09= $tup->{t_infomask2}=20|=3D=20HEAP_NATTS_MASK;=0A+=0A+=09=09push=20= @expected,=0A+=09=09=09qr/${$header}number=20of=20attributes=202047=20= exceeds=20maximum=20expected=20for=20table=203/;=0A+=09}=0A+=09elsif=20= ($offnum=20=3D=3D=2010)=0A+=09{=0A+=09=09#=20Corrupt=20the=20tuple=20to=20= look=20like=20it=20has=20lots=20of=20attributes,=20some=20of=0A+=09=09#=20= them=20null.=20=20This=20falsely=20creates=20the=20impression=20that=20= the=20t_bits=0A+=09=09#=20array=20is=20longer=20than=20just=20one=20= byte,=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+=0A+=09=09= push=20@expected,=0A+=09=09=09qr/${$header}tuple=20data=20should=20begin=20= at=20byte=20280,=20but=20actually=20begins=20at=20byte=2024=20\(2047=20= attributes,=20has=20nulls\)/;=0A+=09}=0A+=09elsif=20($offnum=20=3D=3D=20= 11)=0A+=09{=0A+=09=09#=20Same=20as=20above,=20but=20this=20time=20t_hoff=20= plays=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=200xAA;=0A+=09=09$tup->{t_hoff}=20=3D=2032;=0A+=0A+=09= =09push=20@expected,=0A+=09=09=09qr/${$header}number=20of=20attributes=20= 67=20exceeds=20maximum=20expected=20for=20table=203/;=0A+=09}=0A+=09= elsif=20($offnum=20=3D=3D=2012)=0A+=09{=0A+=09=09#=20Corrupt=20the=20= bits=20in=20column=20'b'=201-byte=20varlena=20header=0A+=09=09= $tup->{b_header}=20=3D=200x80;=0A+=0A+=09=09$header=20=3D=20header(0,=20= $offnum,=201);=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${header}attribute=201=20with=20length=204294967295=20ends=20at=20= offset=20416848000=20beyond=20total=20tuple=20length=2058/;=0A+=09}=0A+=09= elsif=20($offnum=20=3D=3D=2013)=0A+=09{=0A+=09=09#=20Corrupt=20the=20= bits=20in=20column=20'c'=20toast=20pointer=0A+=09=09$tup->{c6}=20=3D=20= 41;=0A+=09=09$tup->{c7}=20=3D=2041;=0A+=0A+=09=09$header=20=3D=20= header(0,=20$offnum,=202);=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${header}final=20toast=20chunk=20number=200=20differs=20from=20= expected=20value=206/,=0A+=09=09=09qr/${header}toasted=20value=20for=20= attribute=202=20missing=20from=20toast=20table/;=0A+=09}=0A+=09elsif=20= ($offnum=20=3D=3D=2014)=0A+=09{=0A+=09=09#=20Set=20both=20= HEAP_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=09$tup->{t_xmax}=20= =3D=204;=0A+=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${header}multitransaction=20ID=204=20equals=20or=20exceeds=20next=20= valid=20multitransaction=20ID=201/;=0A+=09}=0A+=09elsif=20($offnum=20=3D=3D= =2015)=09#=20Last=20offnum=20must=20equal=20ROWCOUNT=0A+=09{=0A+=09=09#=20= Set=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=09$tup->{t_xmax}=20= =3D=204000000000;=0A+=0A+=09=09push=20@expected,=0A+=09=09=09= qr/${header}multitransaction=20ID=204000000000=20precedes=20relation=20= minimum=20multitransaction=20ID=20threshold=201/;=0A+=09}=0A+=09= write_tuple($file,=20$offset,=20$tup);=0A+}=0A+close($file)=0A+=09or=20= BAIL_OUT("close=20failed:=20$!");=0A+$node->start;=0A+=0A+#=20Run=20= pg_amcheck=20against=20the=20corrupt=20table=20with=20epoch=3D0,=20= comparing=20actual=0A+#=20corruption=20messages=20against=20the=20= expected=20messages=0A+$node->command_checks_all(=0A+=09['pg_amcheck',=20= '--no-dependent-indexes',=20'-p',=20$port,=20'postgres'],=0A+=092,=0A+=09= [=20@expected=20],=0A+=09[=20],=0A+=09'Expected=20corruption=20message=20= output');=0A+=0A+$node->teardown_node;=0A+$node->clean_node;=0Adiff=20= --git=20a/src/bin/pg_amcheck/t/005_opclass_damage.pl=20= b/src/bin/pg_amcheck/t/005_opclass_damage.pl=0Anew=20file=20mode=20= 100644=0Aindex=200000000000..eba8ea9cae=0A---=20/dev/null=0A+++=20= b/src/bin/pg_amcheck/t/005_opclass_damage.pl=0A@@=20-0,0=20+1,54=20@@=0A= +#=20This=20regression=20test=20checks=20the=20behavior=20of=20the=20= btree=20validation=20in=20the=0A+#=20presence=20of=20breaking=20sort=20= order=20changes.=0A+#=0A+use=20strict;=0A+use=20warnings;=0A+use=20= PostgresNode;=0A+use=20TestLib;=0A+use=20Test::More=20tests=20=3D>=205;=0A= +=0A+my=20$node=20=3D=20get_new_node('test');=0A+$node->init;=0A= +$node->start;=0A+=0A+#=20Create=20a=20custom=20operator=20class=20and=20= an=20index=20which=20uses=20it.=0A+$node->safe_psql('postgres',=20q(=0A+=09= CREATE=20EXTENSION=20amcheck;=0A+=0A+=09CREATE=20FUNCTION=20int4_asc_cmp=20= (a=20int4,=20b=20int4)=20RETURNS=20int=20LANGUAGE=20sql=20AS=20$$=0A+=09=09= SELECT=20CASE=20WHEN=20$1=20=3D=20$2=20THEN=200=20WHEN=20$1=20>=20$2=20= THEN=201=20ELSE=20-1=20END;=20$$;=0A+=0A+=09CREATE=20OPERATOR=20CLASS=20= int4_fickle_ops=20FOR=20TYPE=20int4=20USING=20btree=20AS=0A+=09=20=20=20=20= OPERATOR=201=20<=20(int4,=20int4),=20OPERATOR=202=20<=3D=20(int4,=20= int4),=0A+=09=20=20=20=20OPERATOR=203=20=3D=20(int4,=20int4),=20OPERATOR=20= 4=20>=3D=20(int4,=20int4),=0A+=09=20=20=20=20OPERATOR=205=20>=20(int4,=20= int4),=20FUNCTION=201=20int4_asc_cmp(int4,=20int4);=0A+=0A+=09CREATE=20= TABLE=20int4tbl=20(i=20int4);=0A+=09INSERT=20INTO=20int4tbl=20(SELECT=20= *=20FROM=20generate_series(1,1000)=20gs);=0A+=09CREATE=20INDEX=20= fickleidx=20ON=20int4tbl=20USING=20btree=20(i=20int4_fickle_ops);=0A+));=0A= +=0A+#=20We=20have=20not=20yet=20broken=20the=20index,=20so=20we=20= should=20get=20no=20corruption=0A+$node->command_like(=0A+=09[=20= 'pg_amcheck',=20'--quiet',=20'-p',=20$node->port,=20'postgres'=20],=0A+=09= qr/^$/,=0A+=09'pg_amcheck=20all=20schemas,=20tables=20and=20indexes=20= reports=20no=20corruption');=0A+=0A+#=20Change=20the=20operator=20class=20= to=20use=20a=20function=20which=20sorts=20in=20a=20different=0A+#=20= order=20to=20corrupt=20the=20btree=20index=0A= +$node->safe_psql('postgres',=20q(=0A+=09CREATE=20FUNCTION=20= int4_desc_cmp=20(int4,=20int4)=20RETURNS=20int=20LANGUAGE=20sql=20AS=20= $$=0A+=09=09SELECT=20CASE=20WHEN=20$1=20=3D=20$2=20THEN=200=20WHEN=20$1=20= >=20$2=20THEN=20-1=20ELSE=201=20END;=20$$;=0A+=09UPDATE=20= pg_catalog.pg_amproc=0A+=09=09SET=20amproc=20=3D=20= 'int4_desc_cmp'::regproc=0A+=09=09WHERE=20amproc=20=3D=20= 'int4_asc_cmp'::regproc=0A+));=0A+=0A+#=20Index=20corruption=20should=20= now=20be=20reported=0A+$node->command_checks_all(=0A+=09[=20= 'pg_amcheck',=20'-p',=20$node->port,=20'postgres'=20],=0A+=092,=0A+=09[=20= qr/item=20order=20invariant=20violated=20for=20index=20"fickleidx"/=20],=0A= +=09[=20],=0A+=09'pg_amcheck=20all=20schemas,=20tables=20and=20indexes=20= reports=20fickleidx=20corruption'=0A+);=0Adiff=20--git=20= a/src/tools/msvc/Install.pm=20b/src/tools/msvc/Install.pm=0Aindex=20= ea3af48777..ffcd0e5095=20100644=0A---=20a/src/tools/msvc/Install.pm=0A= +++=20b/src/tools/msvc/Install.pm=0A@@=20-20,12=20+20,12=20@@=20our=20= (@ISA,=20@EXPORT_OK);=0A=20my=20$insttype;=0A=20my=20@client_contribs=20= =3D=20('oid2name',=20'pgbench',=20'vacuumlo');=0A=20my=20= @client_program_files=20=3D=20(=0A-=09'clusterdb',=20=20=20=20=20=20= 'createdb',=20=20=20'createuser',=20=20=20=20'dropdb',=0A-=09'dropuser',=20= =20=20=20=20=20=20'ecpg',=20=20=20=20=20=20=20'libecpg',=20=20=20=20=20=20= =20'libecpg_compat',=0A-=09'libpgtypes',=20=20=20=20=20'libpq',=20=20=20=20= =20=20'pg_basebackup',=20'pg_config',=0A-=09'pg_dump',=20=20=20=20=20=20=20= =20'pg_dumpall',=20'pg_isready',=20=20=20=20'pg_receivewal',=0A-=09= 'pg_recvlogical',=20'pg_restore',=20'psql',=20=20=20=20=20=20=20=20=20=20= 'reindexdb',=0A-=09'vacuumdb',=20=20=20=20=20=20=20@client_contribs);=0A= +=09'clusterdb',=20=20=20=20=20'createdb',=20=20=20=20=20=20=20= 'createuser',=20'dropdb',=0A+=09'dropuser',=20=20=20=20=20=20'ecpg',=20=20= =20=20=20=20=20=20=20=20=20'libecpg',=20=20=20=20'libecpg_compat',=0A+=09= 'libpgtypes',=20=20=20=20'libpq',=20=20=20=20=20=20=20=20=20=20= 'pg_amcheck',=20'pg_basebackup',=0A+=09'pg_config',=20=20=20=20=20= 'pg_dump',=20=20=20=20=20=20=20=20'pg_dumpall',=20'pg_isready',=0A+=09= 'pg_receivewal',=20'pg_recvlogical',=20'pg_restore',=20'psql',=0A+=09= 'reindexdb',=20=20=20=20=20'vacuumdb',=20=20=20=20=20=20=20= @client_contribs);=0A=20=0A=20sub=20lcopy=0A=20{=0Adiff=20--git=20= a/src/tools/msvc/Mkvcbuild.pm=20b/src/tools/msvc/Mkvcbuild.pm=0Aindex=20= 49614106dc..ead6765f4e=20100644=0A---=20a/src/tools/msvc/Mkvcbuild.pm=0A= +++=20b/src/tools/msvc/Mkvcbuild.pm=0A@@=20-54,17=20+54,21=20@@=20my=20= @contrib_excludes=20=3D=20(=0A=20=0A=20#=20Set=20of=20variables=20for=20= frontend=20modules=0A=20my=20$frontend_defines=20=3D=20{=20'initdb'=20=3D>= =20'FRONTEND'=20};=0A-my=20@frontend_uselibpq=20=3D=20('pg_ctl',=20= 'pg_upgrade',=20'pgbench',=20'psql',=20'initdb');=0A+my=20= @frontend_uselibpq=20=3D=20(=0A+=09'pg_amcheck',=20'pg_ctl',=20= 'pg_upgrade',=20'pgbench',=20'psql',=20'initdb');=0A=20my=20= @frontend_uselibpgport=20=3D=20(=0A-=09'pg_archivecleanup',=20= 'pg_test_fsync',=0A-=09'pg_test_timing',=20=20=20=20'pg_upgrade',=0A-=09= 'pg_waldump',=20=20=20=20=20=20=20=20'pgbench');=0A+=09'pg_amcheck',=20=20= =20=20'pg_archivecleanup',=0A+=09'pg_test_fsync',=20'pg_test_timing',=0A= +=09'pg_upgrade',=20=20=20=20'pg_waldump',=0A+=09'pgbench');=0A=20my=20= @frontend_uselibpgcommon=20=3D=20(=0A-=09'pg_archivecleanup',=20= 'pg_test_fsync',=0A-=09'pg_test_timing',=20=20=20=20'pg_upgrade',=0A-=09= 'pg_waldump',=20=20=20=20=20=20=20=20'pgbench');=0A+=09'pg_amcheck',=20=20= =20=20=20=20=20=20'pg_archivecleanup',=0A+=09'pg_test_fsync',=20=20=20=20= =20'pg_test_timing',=0A+=09'pg_upgrade',=20=20=20=20=20=20=20=20= 'pg_waldump',=0A+=09'pgbench');=0A=20my=20$frontend_extralibs=20=3D=20{=0A= =20=09'initdb'=20=20=20=20=20=3D>=20['ws2_32.lib'],=0A+=09'pg_amcheck'=20= =3D>=20['ws2_32.lib'],=0A=20=09'pg_restore'=20=3D>=20['ws2_32.lib'],=0A=20= =09'pgbench'=20=20=20=20=3D>=20['ws2_32.lib'],=0A=20=09'psql'=20=20=20=20= =20=20=20=3D>=20['ws2_32.lib']=0A@@=20-79,7=20+83,7=20@@=20my=20= $frontend_extrasource=20=3D=20{=0A=20=09=20=20[=20= 'src/bin/pgbench/exprscan.l',=20'src/bin/pgbench/exprparse.y'=20]=0A=20= };=0A=20my=20@frontend_excludes=20=3D=20(=0A-=09'pgevent',=20=20=20=20= 'pg_basebackup',=20'pg_rewind',=20'pg_dump',=0A+=09'pgevent',=20=20=20=20= 'pg_amcheck',=20'pg_basebackup',=20'pg_rewind',=20'pg_dump',=0A=20=09= 'pg_waldump',=20'scripts');=0A=20=0A=20sub=20mkvcbuild=0A@@=20-366,6=20= +370,12=20@@=20sub=20mkvcbuild=0A=20=09=09AddSimpleFrontend($d);=0A=20=09= }=0A=20=0A+=09my=20$pgamcheck=20=3D=20AddSimpleFrontend('pg_amcheck',=20= 1);=0A+=09$pgamcheck->{name}=20=3D=20'pg_amcheck';=0A+=09= $pgamcheck->AddFile('src/bin/pg_amcheck/pg_amcheck.c');=0A+=09= $pgamcheck->AddLibrary('ws2_32.lib');=0A+=09= $pgamcheck->AddDefine('FRONTEND');=0A+=0A=20=09my=20$pgbasebackup=20=3D=20= AddSimpleFrontend('pg_basebackup',=201);=0A=20=09= $pgbasebackup->AddFile('src/bin/pg_basebackup/pg_basebackup.c');=0A=20=09= $pgbasebackup->AddLibrary('ws2_32.lib');=0A--=20=0A2.21.1=20(Apple=20= Git-122.3)=0A=0A= --Apple-Mail=_B8784A81-E7AA-433D-A050-825D9AC0B82D 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=_B8784A81-E7AA-433D-A050-825D9AC0B82D--