Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wSsmg-000AXk-1F for pgsql-hackers@arkaria.postgresql.org; Fri, 29 May 2026 08:41:38 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wSsmd-0021Yx-2T for pgsql-hackers@arkaria.postgresql.org; Fri, 29 May 2026 08:41:36 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wSsmd-0021Yp-0g for pgsql-hackers@lists.postgresql.org; Fri, 29 May 2026 08:41:35 +0000 Received: from mail-pl1-x62d.google.com ([2607:f8b0:4864:20::62d]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wSsma-000000005vP-3ffH for pgsql-hackers@postgresql.org; Fri, 29 May 2026 08:41:34 +0000 Received: by mail-pl1-x62d.google.com with SMTP id d9443c01a7336-2bf1f074a12so5828915ad.0 for ; Fri, 29 May 2026 01:41:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780044092; x=1780648892; darn=postgresql.org; h=to:cc:date:message-id:subject:mime-version:from:from:to:cc:subject :date:message-id:reply-to; bh=KtZ+AhL5N5hkg5Ah1AM0cn9AviC2vCAWR2QDT9EVAaI=; b=V203e8c7bmgw/gdSTw1R2TvZ1N6iMFeH9JvoVsLuZdOst6QjDLJmYBOu2DwMXkXS9p 3ZXd679404if71EVItL/xjsBVVNuT8H4UcvKioQb+y/JROlnNqRPUE8IWm/JtEkKWA9Q 2sKDb4xW7N7E0UssdVo7KvcE6OeOlfwpb6WYzalU09PGeYGuJuNCqGyk9Sw1sQgirsTV 8hsx4tCP8UM/hJR3s9DAn5OWC6I2UDd/8zaa1nuHf+bzTkTipC0ZVhgp8CRQZ6j4KyXd rSJy3GnKW4pmDXQJE8poN3Awfa0fP+GhFvx/Gh3Kr1VqixwysINKlb6aBwoCM2jysUYo C9GA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780044092; x=1780648892; h=to:cc:date:message-id:subject:mime-version:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=KtZ+AhL5N5hkg5Ah1AM0cn9AviC2vCAWR2QDT9EVAaI=; b=qyKX1AU7htQ/JHjdRr2/MKQRJHHlss0qWHptaCFOOlcbAstOgQ4ijswu5CT3a85PUH ZX2Yyim3ckMmxCBe13HBxMNAr5ie9izBwlTxIjZpoeP/aKujee54I+aUpAbGFwOV4Jea v+GfzwWrtHc3ted29VcSs/6rorOQGsF7DW/ixvNoUSsMpsUfqZtMzsrIAn3J6YLIhtSo K48rJULX9PUh3Efg2mzNMo4fkgUwZDoW/aCOABfK7gxeh+9HhSKVtiPdI8MirpQPp2P+ Ph4ePxX5b9Cf2kYQRMlTKF2Qx+b5sYtB/iGJ8/DjTI10qgirBulEaLh8ir0HBnF8+u+h 8XQQ== X-Gm-Message-State: AOJu0Yw/3o78g61YPXKlCfGuGFRnHCfNFATRmL/evrM0o38+0UHYb5wQ aKgv+DjfjKbS/oAl0biGoGcukqDrTd25DW6K5awuX9Ge0W6WamRMbfk6I9JE3vL8rI8= X-Gm-Gg: Acq92OE+bFP6XS5keMDz/mACUELPegnvmjC2eX32N3jskJ+Wki5Q7oXvbGp19VuC4OH UlQJsFTMxcPDCogtVCY+MuTtpiS1VI09Q8KFkDLF76tMzykyw6v9mFqydx3aMfT2XuDoqTja9tv 62HFStfibHyu+fn1lhZVdIx7ntxaykXqtqhel6JRMXPEXiv9qUMIEckeLZN1+bORJXv09EeTJDE r+5TWH3EhWnVjmR1MdZ1OE3aLrcqK2jybGYjLU4x4lmZjR6QqzmMFg+iYkQbdxgh3e7PWXMlNkx Ro/AzacbF8vLSv2nN0RGO2oCO7vlCXLgl3mUEqmerJJK2ae3/vsP3c39bynNztEIuirSoOdfVP8 jlcOFJ6TMt+8buYVeM/JJPUZzckNmy7z+HxHcIOJCRBlTtpbnROX9922UVxehyWCmxKJWfbo+jv BUQ3Faj77tfCiWeNmcqcqlvx5yzMzNY/FuERC52bvNjQ== X-Received: by 2002:a17:902:e54d:b0:2bf:66c:d9e6 with SMTP id d9443c01a7336-2bf20bd837dmr26662495ad.37.1780044092116; Fri, 29 May 2026 01:41:32 -0700 (PDT) Received: from smtpclient.apple ([203.76.245.26]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bf239fd6edsm14992715ad.18.2026.05.29.01.41.29 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 29 May 2026 01:41:31 -0700 (PDT) From: Chao Li Content-Type: multipart/mixed; boundary="Apple-Mail=_FB9ED315-AE8A-4596-807E-516E85613369" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3864.600.51.1.1\)) Subject: Fix regression in vacuumdb --analyze-in-stages for partitioned tables Message-Id: Date: Fri, 29 May 2026 16:40:56 +0800 Cc: Fujii Masao , =?utf-8?Q?=C3=81lvaro_Herrera?= , Antonin Houska , Laurenz Albe To: PostgreSQL-development X-Mailer: Apple Mail (2.3864.600.51.1.1) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_FB9ED315-AE8A-4596-807E-516E85613369 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hi, While testing "vacuumdb: Make vacuumdb --analyze-only process = partitioned tables=E2=80=9D, I found a regression from later commit = c4067383cb2. The original feature commit 6429e5b77 made "--analyze-in-stages" work = for partitioned tables, as the doc change states: ``` --- a/doc/src/sgml/ref/vacuumdb.sgml +++ b/doc/src/sgml/ref/vacuumdb.sgml @@ -397,6 +397,15 @@ PostgreSQL documentation Multiple tables can be vacuumed by writing multiple switches. + + If no tables are specified with the = option, + vacuumdb will clean all regular = tables + and materialized views in the connected database. + If or + is also specified, + it will analyze all regular tables, partitioned tables, + and materialized views (but not foreign tables). + ``` The corresponding code was: ``` + /* + * vacuumdb should generally follow the behavior of the = underlying + * VACUUM and ANALYZE commands. If analyze_only is true, = process + * regular tables, materialized views, and partitioned = tables, just + * like ANALYZE (with no specific target tables) does. = Otherwise, + * process only regular tables and materialized views, = since VACUUM + * skips partitioned tables when no target tables are = specified. + */ + if (vacopts->analyze_only) + appendPQExpBufferStr(&catalog_query, + " AND = c.relkind OPERATOR(pg_catalog.=3D) ANY (array[" + = CppAsString2(RELKIND_RELATION) ", " + = CppAsString2(RELKIND_MATVIEW) ", " + = CppAsString2(RELKIND_PARTITIONED_TABLE) "])\n"); ``` However, the refactoring commit c4067383cb2 removed the `analyze_only` = field from `vacuumingOptions` and switched to a new `mode` field. The = new code is: ``` + /* + * vacuumdb should generally follow the behavior of the = underlying + * VACUUM and ANALYZE commands. In MODE_ANALYZE mode, = process regular + * tables, materialized views, and partitioned tables, = just like + * ANALYZE (with no specific target tables) does. = Otherwise, process + * only regular tables and materialized views, since = VACUUM skips + * partitioned tables when no target tables are = specified. + */ + if (vacopts->mode =3D=3D MODE_ANALYZE) + appendPQExpBufferStr(&catalog_query, + " AND = c.relkind OPERATOR(pg_catalog.=3D) ANY (array[" + = CppAsString2(RELKIND_RELATION) ", " + = CppAsString2(RELKIND_MATVIEW) ", " + = CppAsString2(RELKIND_PARTITIONED_TABLE) "])\n"); ``` analyze_only used to be true when "--analyze-in-stages" was specified, = but that meaning was lost in c4067383cb2: ``` case 3: - analyze_in_stages =3D = vacopts.analyze_only =3D true; + vacopts.mode =3D MODE_ANALYZE_IN_STAGES; break; ``` The fix is very straightforward, just add check for vacopts->mode =3D=3D = MODE_ANALYZE_IN_STAGES. I also added a test. If we had had this test = earlier, the regression should have been caught. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ --Apple-Mail=_FB9ED315-AE8A-4596-807E-516E85613369 Content-Disposition: attachment; filename=v1-0001-vacuumdb-Analyze-partitioned-tables-with-analyze-.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v1-0001-vacuumdb-Analyze-partitioned-tables-with-analyze-.patch" Content-Transfer-Encoding: quoted-printable =46rom=20bcef4667b618de169d17838f3edabdb90bc5f639=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Fri,=2029=20May=202026=2016:36:05=20+0800=0ASubject:=20[PATCH=20= v1]=20vacuumdb:=20Analyze=20partitioned=20tables=20with=0A=20= --analyze-in-stages=0A=0ACommit=206429e5b77=20made=20vacuumdb=20process=20= partitioned=20tables=20when=20running=20in=0Aanalyze-only=20mode,=20= including=20both=20--analyze-only=20and=20--analyze-in-stages.=0AThis=20= matched=20the=20documented=20behavior=20that,=20when=20no=20target=20= tables=20are=20specified,=0Athese=20options=20analyze=20regular=20= tables,=20partitioned=20tables,=20and=20materialized=0Aviews.=0A=0A= Later,=20commit=20c4067383cb2=20refactored=20vacuumingOptions=20by=20= replacing=20the=0Aanalyze_only=20flag=20with=20a=20mode=20field.=20=20= During=20that=20refactoring,=20the=20object=0Aselection=20logic=20was=20= changed=20to=20check=20only=20MODE_ANALYZE,=20so=0A= MODE_ANALYZE_IN_STAGES=20no=20longer=20included=20partitioned=20tables.=0A= =0AFix=20this=20by=20treating=20MODE_ANALYZE_IN_STAGES=20the=20same=20as=20= MODE_ANALYZE=20when=0Aselecting=20objects=20to=20process,=20and=20add=20= a=20regression=20test=20to=20cover=20the=20case.=0A=0AAuthor:=20Chao=20= Li=20=0A---=0A=20src/bin/scripts/t/100_vacuumdb.pl=20|=20= =204=20++++=0A=20src/bin/scripts/vacuuming.c=20=20=20=20=20=20=20|=2014=20= ++++++++------=0A=202=20files=20changed,=2012=20insertions(+),=206=20= deletions(-)=0A=0Adiff=20--git=20a/src/bin/scripts/t/100_vacuumdb.pl=20= b/src/bin/scripts/t/100_vacuumdb.pl=0Aindex=2084fcacd57fa..5fd55628507=20= 100644=0A---=20a/src/bin/scripts/t/100_vacuumdb.pl=0A+++=20= b/src/bin/scripts/t/100_vacuumdb.pl=0A@@=20-363,6=20+363,10=20@@=20= $node->issues_sql_like(=0A=20=09[=20'vacuumdb',=20'--analyze-only',=20= 'postgres'=20],=0A=20=09qr/statement:=20ANALYZE=20public.parent_table/s,=0A= =20=09'--analyze-only=20updates=20statistics=20for=20partitioned=20= tables');=0A+$node->issues_sql_like(=0A+=09[=20'vacuumdb',=20= '--analyze-in-stages',=20'postgres'=20],=0A+=09qr/statement:=20ANALYZE=20= public.parent_table/s,=0A+=09'--analyze-in-stages=20updates=20statistics=20= for=20partitioned=20tables');=0A=20$node->issues_sql_unlike(=0A=20=09[=20= 'vacuumdb',=20'--analyze-only',=20'postgres'=20],=0A=20=09qr/statement:\=20= VACUUM/sx,=0Adiff=20--git=20a/src/bin/scripts/vacuuming.c=20= b/src/bin/scripts/vacuuming.c=0Aindex=20faac9089a01..37608806056=20= 100644=0A---=20a/src/bin/scripts/vacuuming.c=0A+++=20= b/src/bin/scripts/vacuuming.c=0A@@=20-650,13=20+650,15=20@@=20= retrieve_objects(PGconn=20*conn,=20vacuumingOptions=20*vacopts,=0A=20=09= {=0A=20=09=09/*=0A=20=09=09=20*=20vacuumdb=20should=20generally=20follow=20= the=20behavior=20of=20the=20underlying=0A-=09=09=20*=20VACUUM=20and=20= ANALYZE=20commands.=20=20In=20MODE_ANALYZE=20mode,=20process=20regular=0A= -=09=09=20*=20tables,=20materialized=20views,=20and=20partitioned=20= tables,=20just=20like=0A-=09=09=20*=20ANALYZE=20(with=20no=20specific=20= target=20tables)=20does.=20Otherwise,=20process=0A-=09=09=20*=20only=20= regular=20tables=20and=20materialized=20views,=20since=20VACUUM=20skips=0A= -=09=09=20*=20partitioned=20tables=20when=20no=20target=20tables=20are=20= specified.=0A+=09=09=20*=20VACUUM=20and=20ANALYZE=20commands.=20=20In=20= MODE_ANALYZE=20or=0A+=09=09=20*=20MODE_ANALYZE_IN_STAGES=20modes,=20= process=20regular=20tables,=20materialized=0A+=09=09=20*=20views,=20and=20= partitioned=20tables,=20just=20like=20ANALYZE=20(with=20no=20specific=0A= +=09=09=20*=20target=20tables)=20does.=20Otherwise,=20process=20only=20= regular=20tables=20and=0A+=09=09=20*=20materialized=20views,=20since=20= VACUUM=20skips=20partitioned=20tables=20when=20no=0A+=09=09=20*=20target=20= tables=20are=20specified.=0A=20=09=09=20*/=0A-=09=09if=20(vacopts->mode=20= =3D=3D=20MODE_ANALYZE)=0A+=09=09if=20(vacopts->mode=20=3D=3D=20= MODE_ANALYZE=20||=0A+=09=09=09vacopts->mode=20=3D=3D=20= MODE_ANALYZE_IN_STAGES)=0A=20=09=09=09= appendPQExpBufferStr(&catalog_query,=0A=20=09=09=09=09=09=09=09=09=20"=20= AND=20c.relkind=20OPERATOR(pg_catalog.=3D)=20ANY=20(array["=0A=20=09=09=09= =09=09=09=09=09=20CppAsString2(RELKIND_RELATION)=20",=20"=0A--=20=0A= 2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_FB9ED315-AE8A-4596-807E-516E85613369--