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 1x0gUg-004l9W-2y for pgsql-hackers@arkaria.postgresql.org; Sun, 30 Aug 2026 14:26:47 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1x0gUf-00DYa8-2K for pgsql-hackers@arkaria.postgresql.org; Sun, 30 Aug 2026 14:26:45 +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 1x0gUf-00DYZz-1L for pgsql-hackers@lists.postgresql.org; Sun, 30 Aug 2026 14:26:45 +0000 Received: from mail-qk1-x72d.google.com ([2607:f8b0:4864:20::72d]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1x0gUd-000000039ut-2nQQ for pgsql-hackers@lists.postgresql.org; Sun, 30 Aug 2026 14:26:44 +0000 Received: by mail-qk1-x72d.google.com with SMTP id af79cd13be357-9309d4ea213so255869785a.1 for ; Sun, 30 Aug 2026 07:26:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788100003; x=1788704803; darn=lists.postgresql.org; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:from:to:cc:subject :date:message-id:reply-to:content-type; bh=ymr4VH6RgQ9Edyjj2fVdQUO353PbVh8aipCEUMuBFk8=; b=LPT/hdEredhLtnj+jJHwY/asIl41awSttmcR2AOJJNuQmKiKiEvWiGV/fzfFy3VdXx P44htPCGhG0IhuYUCWEu1tXgAvE/PcqQwGp8Z4eclVxCxnjgMSyyugk/lnI/FCFPOW8V dr4hHVNXxZJW4YPXkM5kaxH/rEsdpWjHOyZJn1oJ0ZqHxYIih4/KGTQr0KtDtOOLBEqJ wygh9ehL9DEFbth4QPUfYKi73uFm75tOLf0FnbRA2BJT+WnzbM4sgSo5eg87m/Js0KsZ Vle0AEkShd+NfrZYEoLXPjGdo6nTqFGcvoidhImVLWdSqh1G7TpPsj8EGgookNqaEcIx TfPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788100003; x=1788704803; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=ymr4VH6RgQ9Edyjj2fVdQUO353PbVh8aipCEUMuBFk8=; b=ktrsnbFwEO6ihTAxyXSfNbIYuYdU8YGQBia33FEnPnwmgaEgtdpxfVprznWk2ugW7W sy8ZLwR2FVkMIiOELjYx9Ui4OKS1j6LPo8C2gUOKwH5DS8vYky4FcxkOIUXMTgVx2Qh7 P+XZ92yWNEcpSf5UT8kKockM5c6hXRuua8yjRLXd9i22K/5RjdQbVUlwjI4prVLhRoOX UwnqwhT2bLzkou3TIh4hISAyD2KqAQK0X0K1Ol4koj3hMX4MghxmgI+S+MoxRYPCeZFs ap2f7zMlfCBNOyfJ+8u0CUDsFgJzZwrc7iSy7kJt89+Cm1PBxC+lOb93NZ/VP2UhyY1i r3oQ== X-Forwarded-Encrypted: i=1; AHgh+Rrskn305SsCiNKTDEIRW18IF71FNL7dzz6/l/j4yo2Bu5winCwQJd39jJqBWRAMPTHV0HNgqX2poZCCmC1W@lists.postgresql.org X-Gm-Message-State: AFuF++lhrcvtw/WrhD5jtOBlVxnI325U2MYqIbMLSpmKLj34DbifTSow skIQikZKw6thnpm6Zbp4ssc86jAh/fAFAT1ZUd7PNWcP6hTaq9OgGIRx X-Gm-Gg: AR+sD13sQJyp511QxtbZ1xyG8zHXJoF4E1/R8wuHecp+DUvnX+7yuSECCpxXUl02V8V 4a43coX4qrXAeO2zhojWrYGBvQOEGGHIyHZL+8Gi49lLf/bP1vLAgDzNt2myQrhFBXgDBsnylL8 2Wb3ta5OrQH/c0S5UStD29sO1FXchhSfzM1MIaoIKnKG1yBlyOayAF6RmsP+gvf3K2f4IeKxI7R Q4BGKSAXuR3BnSQ6Acmd7VyRqpGU4b92KyXR4aIYPbtRv5XsuTg4mKwDVbp9ZESKwBzhh19on2l H7zX3ezJwSZB+iG3dAc6iNKAs1UmOxTI+Vgbeo19cRfkujlh0vBvhxwydJNopiuWdCi9pgkcpxv vDimZfN5j4QNQHKdXqnIrjXjsxV+bMCb387rfPSv84otFEzB0FaFHaQt+TTdhd21uWWkB7WpRo6 vXgyqWEHX/0nvHjX2Mo7m2HpgM0lXmGQEiRXZczgcf84OjTHTWlwt5bxgHcHLVGiLtRWdTlHF9z jNvkbGs7lD1e9Jt9pwHduyJ3OwMaIKEqtwJ6r37T+jV1u7/m536BTXtDnfNvk+aN6Zj5zWgVFnp QGPM8KQsPQONxg0= X-Received: by 2002:a05:620a:1b9a:b0:939:1830:af4e with SMTP id af79cd13be357-9391830b5c8mr2096536085a.16.1788100002761; Sun, 30 Aug 2026 07:26:42 -0700 (PDT) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id af79cd13be357-93918b0f825sm558627785a.40.2026.08.30.07.26.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 30 Aug 2026 07:26:40 -0700 (PDT) Date: Sun, 30 Aug 2026 09:26:38 -0500 From: Nathan Bossart To: Corey Huinker Cc: Michael Paquier , Sami Imseih , pgsql-hackers@lists.postgresql.org Subject: Re: Add starelid, attnum to pg_stats and leverage this in pg_dump Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="H90LzND/eZZd006U" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --H90LzND/eZZd006U Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Claude advises me that removing the redundant filter clause was a mistake. See the attached patch. -- nathan --H90LzND/eZZd006U Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v1-0001-pg_dump-Avoid-full-scans-of-pg_stats.patch From 42f7c17cbc530d78a3ddc94ba0193d3da3f487a1 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Sun, 30 Aug 2026 09:21:47 -0500 Subject: [PATCH v1 1/1] pg_dump: Avoid full scans of pg_stats. Commit 4b5ba0c4ca taught the attribute statistics query to join pg_stats on the new tableid column, and it dropped the redundant filter clause on s.tablename at the same time, on the theory that the clause was only compensating for the name-based lookup. That isn't what the clause was doing. pg_stats is a security barrier view, so the planner will not push a join clause down into it, whereas the redundant clause is a restriction clause on a leakproof operator, which it will push. Presently, we scan all of pg_statistic once per batch of 64 relations, which makes dumping statistics quadratic in the number of relations. With 8000 tables, pg_dump --statistics-only takes 27.6s instead of 1.5s, and pg_upgrade pays that cost during downtime. To fix, put the filter clause back, now on s.tableid, and correct the comment that claimed the OIDs had made it unnecessary. I've checked that the resulting plan holds up as a generic plan, which matters here because pg_dump prepares this query once and executes it once per batch. Oversight in commit 4b5ba0c4ca. Discussion: https://postgr.es/m/CADkLM%3DcoCVy92QkVUUTLdo5eO2bMDtwMrzRn_8miAhX%2BuPaqXg%40mail.gmail.com Backpatch-through: 19 --- src/bin/pg_dump/pg_dump.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index db14834e430..b4bb0ce7b22 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -11150,17 +11150,19 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te) * The results must be in the order of the relations supplied in the * parameters to ensure we remain in sync as we walk through the TOC. * - * For versions before 19, the redundant filter clause on s.tablename - * = ANY(...) seems sufficient to convince the planner to use - * pg_class_relname_nsp_index, which avoids a full scan of pg_stats. - * In newer versions, pg_stats returns the table OIDs, eliminating the - * need for that hack. + * pg_stats is a security barrier view, so the planner will not push + * the join clause down into it, and we would scan all of pg_statistic + * once per batch. The redundant filter clause is a restriction + * clause on a leakproof operator, which the planner is willing to + * push down, and that gets us an index scan. This may not work for + * all versions. */ if (fout->remoteVersion >= 190000) appendPQExpBufferStr(query, "FROM pg_catalog.pg_stats s " "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) " "ON s.tableid = u.tableid " + "WHERE s.tableid = ANY($1) " "ORDER BY u.ord, s.attname, s.inherited"); else appendPQExpBufferStr(query, -- 2.55.0 --H90LzND/eZZd006U--