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 1w2Vve-000MGv-1m for pgsql-hackers@arkaria.postgresql.org; Tue, 17 Mar 2026 15:01:54 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w2Vvd-002PTB-0S for pgsql-hackers@arkaria.postgresql.org; Tue, 17 Mar 2026 15:01:53 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w2Vvc-002PT2-2e for pgsql-hackers@lists.postgresql.org; Tue, 17 Mar 2026 15:01:52 +0000 Received: from mail-ot1-x32f.google.com ([2607:f8b0:4864:20::32f]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w2Vva-00000000cXW-0jhP for pgsql-hackers@lists.postgresql.org; Tue, 17 Mar 2026 15:01:52 +0000 Received: by mail-ot1-x32f.google.com with SMTP id 46e09a7af769-7d7c77fd31cso158265a34.3 for ; Tue, 17 Mar 2026 08:01:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773759709; x=1774364509; darn=lists.postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=Q49uWAdj4DG8W4l3ExnuJ6wOw6NqBXeJPoKQ0ZvIXmU=; b=EuhRVeVqaQ10pWLJ+nWrb2iBtZ0ira5zmCugO9Z15AB+4/nRGIquL2V9R6wqJHVNa6 Jd5ZCUXxkDC+wrEqcV8TVvGgC9cuyqKaIaSZv8sSJq6YsK92Y7qZ0AGjWLZ40l20j+Jx ppZOzyxD1OT2K/vx5HgznMMre2+HejTgY+1rk7oiUlmDgA1NAQ7fKJbGPdqQPISw36sq r40/m9KoJSCwN1m1ttisvp6KUWCG6yPFfmVOnguqekFV3HOtB+y0xcFa62HfJqJfagO5 T5+4W7xvQoJz78/skTx7sgHCB4VeFounhRnrDHyawCKHgIbynZFB7DeQTkQi8KmKsc6i g7dA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773759709; x=1774364509; h=in-reply-to:content-disposition: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; bh=Q49uWAdj4DG8W4l3ExnuJ6wOw6NqBXeJPoKQ0ZvIXmU=; b=pCcLn2eWDclDx/UIoigiFDR/za9Flkf20avnN13PIw5oOysSkj1s+AD5F3fzdDQP0b Rqhtq/BQz6V2AWRHX8wkiBzhSD8V/7MY6G2cV2Ksf+IzhawQq5ZCkUuX22LqATI4fW18 ztq29Hd8Kwj8QiP98oR/74lJ80U+Z+G3pBMw/TDGuw3b7GZTPghGKJIs2dIH87Out/NU +fbLRxZZVoSDWOfTQEBrNpM4Ue1RayAwFiTGlifeA7RxBeb8XJp2BWZGsIJM26Oz0uTn mleRLE+KMi9S5tpV1zzIO0XFo/PT3OXP3DvfO1G1ItdEJkzSbcBXCEiUvAE7t7VYxO2c amiQ== X-Forwarded-Encrypted: i=1; AJvYcCWSdLHAAPnZk0DwsWD8u/aTP09AlHOmc4NZe3UmYIgVT/zuw8+pzVGWC6aabPiy49U0k5zF+YPKLhtGq1/+@lists.postgresql.org X-Gm-Message-State: AOJu0YxGhGi8s0AJ/kpZRJAtZHHa7zg8YUxY3HqZ48EO1vrRTUijsH9T 4Qq4BJk1O7gvfqA+2LJE2rmPsFiK0RUPfKkTXHGfjk5NvMPD9Pfqd/St X-Gm-Gg: ATEYQzw0CvKTnLe53iKpXmnQZaMo704IpMEr6ao6Ms/SzqiZAv7ZJJNpWyWZy+femxa xdQ1PU8Ae6id30vOeWscHxoKqCXPmHc0ya1NnjOcN777Mh4qcDqF+s0WRAxj46cn3vEFx8cwQ/r M+LPp0sCbfI/l/zo125TEuk2Q5LIxjhKiBCrG5PpJSANbVh9UPYVDYagY2fhgiqg7OxpmuFcv9m u07irBPSUi6WyhWpClRnLfmnaaifgyWErxjUy8zN9+Yad+nu8HH4jw9CkvVLw0viCsz7GKQ7JnI DTj3pz7pG5i5q1XEmtUdw6/1PYflo20v7j/oJNy8x+7eV5fo2nrnoDyQGcYuISucakGHro5Yj0g IN1JJvy8ymBv2BNCB5ALG0/zA45JftCl+Qj7qUdWlC2TOMR2mKLmb/adTIOOvxc5JkYelBApcN5 zOwYiYjrPOpOmWtPa2xjM5/YtQAwVtfLSDWM6pu0ON7MJu0ZICDxkhJyRJ0cqyBc6WsExgaFM+C 25aZr666mZN5/pcaIZLkA== X-Received: by 2002:a05:6808:1a21:b0:464:3d5d:d9d4 with SMTP id 5614622812f47-467572d38bcmr9052849b6e.39.1773759706951; Tue, 17 Mar 2026 08:01:46 -0700 (PDT) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 5614622812f47-467342fb528sm11524062b6e.17.2026.03.17.08.01.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 17 Mar 2026 08:01:45 -0700 (PDT) Date: Tue, 17 Mar 2026 10:01:42 -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="vdBCrePG3NhnK/Ex" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --vdBCrePG3NhnK/Ex Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Tue, Mar 17, 2026 at 09:26:57AM -0500, Nathan Bossart wrote: > Committed the next patch in the series. I'll have a rebased version of the > last one ready to share soon. As promised... -- nathan --vdBCrePG3NhnK/Ex Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v10-0001-pg_dump-Simplify-query-in-getAttributeStats.patch From 3d2e9ac40c3695ba60d50c66112d48423734c641 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Tue, 17 Mar 2026 09:35:32 -0500 Subject: [PATCH v10 1/1] pg_dump: Simplify query in getAttributeStats(). Presently, this query fetches information from pg_stats, which did not return table OIDs until recent commit 3b88e50d6c. Because of this, we had to cart around arrays of schema and table names, and we needed an extra filter clause to hopefully convince the planner to use the correct index. With the introduction of pg_stats.tableid, we can instead just use an array of OIDs without the extra filter clause hack. Author: Corey Huinker Reviewed-by: Sami Imseih Discussion: https://postgr.es/m/CADkLM%3DcoCVy92QkVUUTLdo5eO2bMDtwMrzRn_8miAhX%2BuPaqXg%40mail.gmail.com --- src/bin/pg_dump/pg_dump.c | 65 +++++++++++++++++++++++++++++++-------- src/bin/pg_dump/pg_dump.h | 1 + 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 23af95027e6..ad09677c336 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7227,6 +7227,7 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages, dobj->components |= DUMP_COMPONENT_STATISTICS; dobj->name = pg_strdup(rel->name); dobj->namespace = rel->namespace; + info->relid = rel->catId.oid; info->relpages = relpages; info->reltuples = pstrdup(reltuples); info->relallvisible = relallvisible; @@ -11122,6 +11123,7 @@ static PGresult * fetchAttributeStats(Archive *fout) { ArchiveHandle *AH = (ArchiveHandle *) fout; + PQExpBuffer relids = createPQExpBuffer(); PQExpBuffer nspnames = createPQExpBuffer(); PQExpBuffer relnames = createPQExpBuffer(); int count = 0; @@ -11157,6 +11159,7 @@ fetchAttributeStats(Archive *fout) restarted = true; } + appendPQExpBufferChar(relids, '{'); appendPQExpBufferChar(nspnames, '{'); appendPQExpBufferChar(relnames, '{'); @@ -11168,15 +11171,28 @@ fetchAttributeStats(Archive *fout) */ for (; te != AH->toc && count < max_rels; te = te->next) { - if ((te->reqs & REQ_STATS) != 0 && - strcmp(te->desc, "STATISTICS DATA") == 0) + if ((te->reqs & REQ_STATS) == 0 || + strcmp(te->desc, "STATISTICS DATA") != 0) + continue; + + if (fout->remoteVersion >= 190000) + { + RelStatsInfo *rsinfo = (RelStatsInfo *) te->defnDumperArg; + char relid[32]; + + sprintf(relid, "%u", rsinfo->relid); + appendPGArray(relids, relid); + } + else { appendPGArray(nspnames, te->namespace); appendPGArray(relnames, te->tag); - count++; } + + count++; } + appendPQExpBufferChar(relids, '}'); appendPQExpBufferChar(nspnames, '}'); appendPQExpBufferChar(relnames, '}'); @@ -11186,14 +11202,25 @@ fetchAttributeStats(Archive *fout) PQExpBuffer query = createPQExpBuffer(); appendPQExpBufferStr(query, "EXECUTE getAttributeStats("); - appendStringLiteralAH(query, nspnames->data, fout); - appendPQExpBufferStr(query, "::pg_catalog.name[],"); - appendStringLiteralAH(query, relnames->data, fout); - appendPQExpBufferStr(query, "::pg_catalog.name[])"); + + if (fout->remoteVersion >= 190000) + { + appendStringLiteralAH(query, relids->data, fout); + appendPQExpBufferStr(query, "::pg_catalog.oid[])"); + } + else + { + appendStringLiteralAH(query, nspnames->data, fout); + appendPQExpBufferStr(query, "::pg_catalog.name[],"); + appendStringLiteralAH(query, relnames->data, fout); + appendPQExpBufferStr(query, "::pg_catalog.name[])"); + } + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); destroyPQExpBuffer(query); } + destroyPQExpBuffer(relids); destroyPQExpBuffer(nspnames); destroyPQExpBuffer(relnames); return res; @@ -11254,8 +11281,14 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te) query = createPQExpBuffer(); if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS]) { + if (fout->remoteVersion >= 190000) + appendPQExpBufferStr(query, + "PREPARE getAttributeStats(pg_catalog.oid[]) AS\n"); + else + appendPQExpBufferStr(query, + "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"); + appendPQExpBufferStr(query, - "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n" "SELECT s.schemaname, s.tablename, s.attname, s.inherited, " "s.null_frac, s.avg_width, s.n_distinct, " "s.most_common_vals, s.most_common_freqs, " @@ -11277,17 +11310,25 @@ 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. - * The redundant filter clause on s.tablename = ANY(...) seems - * sufficient to convince the planner to use + * + * For v9.4 through v18, 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. - * This may not work for all versions. + * In newer versions, pg_stats returns the table OIDs, eliminating the + * need for that hack. * * Our query for retrieving statistics for multiple relations uses * WITH ORDINALITY and multi-argument UNNEST(), both of which were * introduced in v9.4. For older versions, we resort to gathering * statistics for a single relation at a time. */ - if (fout->remoteVersion >= 90400) + 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 " + "ORDER BY u.ord, s.attname, s.inherited"); + else if (fout->remoteVersion >= 90400) appendPQExpBufferStr(query, "FROM pg_catalog.pg_stats s " "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) " diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 1c11a79083f..2b9c01b2c0a 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -448,6 +448,7 @@ typedef struct _indexAttachInfo typedef struct _relStatsInfo { DumpableObject dobj; + Oid relid; int32 relpages; char *reltuples; int32 relallvisible; -- 2.50.1 (Apple Git-155) --vdBCrePG3NhnK/Ex--