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 1wi4My-000Z1g-0K for pgsql-hackers@arkaria.postgresql.org; Fri, 10 Jul 2026 06:05:53 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wi4Mw-00GAdd-1W for pgsql-hackers@arkaria.postgresql.org; Fri, 10 Jul 2026 06:05:51 +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 1wi4Mv-00GAcw-1i for pgsql-hackers@lists.postgresql.org; Fri, 10 Jul 2026 06:05:50 +0000 Received: from fout-a6-smtp.messagingengine.com ([103.168.172.149]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wi4Mp-00000000bUQ-2Qyo for pgsql-hackers@lists.postgresql.org; Fri, 10 Jul 2026 06:05:49 +0000 Received: from phl-compute-01.internal (phl-compute-01.internal [10.202.2.41]) by mailfout.phl.internal (Postfix) with ESMTP id C3C22EC00C7; Fri, 10 Jul 2026 02:05:41 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-01.internal (MEProxy); Fri, 10 Jul 2026 02:05:41 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paquier.xyz; h= cc:cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1783663541; x=1783749941; bh=3P0JmTi54O 4vE3MA29ly6T0ACefJbssBOtFC/JYnk40=; b=aUYXKMv6XUHuyDQjplG27/40MN NRLzltAG62kLtwzthg81Fo98EoOo1QTEy2FCHhaegHH5jzuAVoMr7t2k+eNPF31E ++lseEcUkJ0MqZGVvjHemBHyRncrV9Kd82zK2kXeKxDVx1kz2fDzXL5bSdJi1xqs 3UWeHTkQIDDZ6MIlOz4Lzd+yR/bhUVQ5k88to7f/cprcw5gS6MTw4+Ok7vF0nw05 Xvv7tjpAh4rNtLHFF8GxS36i/jAPmEIaAoX7aBMqlsge8Shj8/cKGRWUOFBqL/+K 7a599FZ1+nxHBxMXztFEiJ9GoYlmcEf6eK80ugr3Uo152gxI5YMMz/KcWsFA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t= 1783663541; x=1783749941; bh=3P0JmTi54O4vE3MA29ly6T0ACefJbssBOtF C/JYnk40=; b=NmQNEKcx94eh7hihcqKficBtq0yAg34lcIA4Cz+jyRWrsjKFHcB PXVDL8ZwovA2YDQ517jZW/9S1DvdE2z2hXLcJ0phV/xXUuMTzQ6mrPH+5TUtHuO7 ORlvNCmuK4EBQJtLXhqglDIcVz84q+u777kpitU9kQ55Jc2U8FqG2zUSPKfVfJad ZpQtc1nQpPzwMD3WsrYKSWhcCWXhLyQae9O1pZMRmievrYseaZSTo3tC5mIYWDrN AksrCWqZSUdb6tT4JAAK+yfePy/v+umJH+ycDTA+8ZTTEvfBa1uVri8M+qCnDk1O 2/+gKstvyALkFNXdLdv8nBuskkR/QBZ4CtQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTFE/nuOF2U2b4/H35682brp3fSAwQT4BBGiKgb5AW/rW2cehwLrTS8tCyNoULke8c 3HlfPTmPPDEf8kirrPwSjc8RyEFncMJIj9mB4HVr39Z5buHCFPcT9bYgYL6hm+e1B4Ea0X EZqXmAhIG0sVBHOmwjz1og0e6aI8CGlD56Y41eSb+8GU7KgY+G9dBHipVq11uMA0ApsvBH uQBbr5mClyNB9mlV+WloLuK5laWLornP2nzBoor4QqhV2SfZLbeqgEsH7Hpj21IoEXkenS q+ogCAS1cnqfXOeSFrtKElHB2z73RO3dZjCrYZ4jEDnkgH9RuwLV13Ff8yQBcjxriU0GCq 9XR7pSTWEvBZ70dtSHPkt6skB9zs/hc7Xbob4bs/cLifAbezeJG3C5cvgr+aaTnAebz/10 FqGYG0dg9ZYXKinYaA0ttqT5hZqhAsXTnsQVWXA5SLRsyrlN8AVGdkS8O8RrtFNsaubWWN LWgOeVO1VVeeMcQa5+oBVAbnfPcfOPfpuiIIVMKo3VP19KK7WDy+Z0P2DFHbfHLuSXh4xg FMLWmtbOZse5cHrUBpIyNxazMJLkULUKsTWEy0UgDOBGE5AujXq8440TybDnEi/6baILKh NgrxN2KFr0InWS3BlwX6Dsn8Q+fdaAFGeUQrok5HCPyVuwovAfzMr7ClKhmw X-ME-Proxy: Feedback-ID: i0fe9450f:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 10 Jul 2026 02:05:39 -0400 (EDT) Date: Fri, 10 Jul 2026 15:05:35 +0900 From: Michael Paquier To: Bertrand Drouvot Cc: Andres Freund , Kirill Reshke , Robert Haas , pgsql-hackers@lists.postgresql.org Subject: Re: relfilenode statistics Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="ui2W8jjkRyW+Lel8" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --ui2W8jjkRyW+Lel8 Content-Type: multipart/mixed; boundary="uu1QJiDJI3oJPnCR" Content-Disposition: inline --uu1QJiDJI3oJPnCR Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Mon, Mar 09, 2026 at 07:43:43AM +0000, Bertrand Drouvot wrote: > PFA, a new mandatory rebase. So, we have discussed that a couple of days ago offline, and I still felt uncomfortable to not do a cleanup of the relation stats data for indexes. A lot of the fields are stored in memory and written in the file, and remain unused for indexes. So, please find attached a patch set demonstrating the ideas I had on the matter:=20 - 0001 refactors PgStat_StatTabEntry so as the data for indexes and tables is split. The advantage of this one is a reduction of the shared memory usage, as most of the fields of PgStat_StatTabEntry are not used for indexes. These are hidden behind a new stats kind. That's only refactoring, no changes. If you look closely, you would notice this one: + .pending_size =3D sizeof(PgStat_TableStatus), While weird in itself, that links to a problem addressed by 0002 related to the handling of pending data. - 0002 reworks the way we store pending data, for the transactional and non-transactional bits, as indexes don't care about the transactional parts. PgStat_TableStatus is renamed, refactored so as the table and index parts are split into two. That finishes the split of both stats kinds, preparing for the next patch with the introduction of relfilenode stats. - 0003, super WIP, that I am happy to share based on the fact that it is able to populate the stats entries at replay (tested quickly a few replay scenarios, seems to basically work). Now, while it passes check-world, this patch has various issues that I did not look much into yet (lack of time), but it shows the idea. Note that "get" functions do a direct lookup at the relfilenode, implying modifications in the views to rely on pg_relation_filenode(). Perhaps we should do things the other way around, where we still pass an OID but look at the relation mapper for shared catalogs each time one of these functions is called. This has the effect to replace the various "get" functions to some "relfilenode/rfn" flavors, that can get the fields directly by getting a relfilenode in input. One property that I do not wish to keep around is the aggregation of counters across rewrites, as the new counters don't make sense once we switch to a new relfilenode. I'm feeling the right amount of interface in 0001. 0002 is also quite nice after some time looking at it. 0003 needs a lot more work to be presentable; the basics are surely interesting to see done this way. Comments and thoughts are welcome. -- Michael --uu1QJiDJI3oJPnCR Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v13-0001-Split-PgStat_StatTabEntry-into-separate-table-an.patch Content-Transfer-Encoding: quoted-printable =46rom 0fd23ab6f58fc43a9086975d9d2d5a3ad67c466b Mon Sep 17 00:00:00 2001 =46rom: Michael Paquier Date: Fri, 10 Jul 2026 12:52:03 +0900 Subject: [PATCH v13 1/3] Split PgStat_StatTabEntry into separate table and index structures Table and index stats structures are now split, with PgStat_StatIdxEntry containing only the fields relevant to index statistics. Index entries do not care about most of the fields saved for tables, so this saves up to 200 bytes for each stats entry in shared memory. A new stats kind called PGSTAT_KIND_INDEX is created, with the fetch functions for the fields, that are plugged into the system views. XXX: Bump of PGSTAT_FILE_FORMAT_ID required. --- src/include/catalog/pg_proc.dat | 59 ++++++++- src/include/pgstat.h | 20 +++ src/include/utils/pgstat_internal.h | 15 +++ src/include/utils/pgstat_kind.h | 23 ++-- src/backend/catalog/system_views.sql | 40 +++--- src/backend/utils/activity/Makefile | 1 + src/backend/utils/activity/meson.build | 1 + src/backend/utils/activity/pgstat.c | 16 +++ src/backend/utils/activity/pgstat_index.c | 129 +++++++++++++++++++ src/backend/utils/activity/pgstat_relation.c | 106 +++++++++++---- src/backend/utils/adt/pgstatfuncs.c | 108 ++++++++++++++++ src/test/regress/expected/rules.out | 34 ++--- src/test/regress/expected/stats.out | 49 +++---- src/test/regress/sql/stats.sql | 22 ++-- doc/src/sgml/monitoring.sgml | 20 ++- 15 files changed, 531 insertions(+), 112 deletions(-) create mode 100644 src/backend/utils/activity/pgstat_index.c diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.= dat index 3cb84359adf0..dc6c61e84e54 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5558,11 +5558,11 @@ proargnames =3D> '{mcv_list,index,values,nulls,frequency,base_frequency}= ', prosrc =3D> 'pg_stats_ext_mcvlist_items' }, =20 -{ oid =3D> '1928', descr =3D> 'statistics: number of scans done for table/= index', +{ oid =3D> '1928', descr =3D> 'statistics: number of scans done for table', proname =3D> 'pg_stat_get_numscans', provolatile =3D> 's', proparallel = =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', prosrc =3D> 'pg_stat_get_numscans' }, -{ oid =3D> '6310', descr =3D> 'statistics: time of the last scan for table= /index', +{ oid =3D> '6310', descr =3D> 'statistics: time of the last scan for table= ', proname =3D> 'pg_stat_get_lastscan', provolatile =3D> 's', proparallel = =3D> 'r', prorettype =3D> 'timestamptz', proargtypes =3D> 'oid', prosrc =3D> 'pg_stat_get_lastscan' }, @@ -6237,10 +6237,63 @@ proargdefaults =3D> '{NULL}', prosrc =3D> 'pg_stat_reset_shared', proacl =3D> '{POSTGRES=3DX}' }, { oid =3D> '3776', - descr =3D> 'statistics: reset collected statistics for a single table or= index in the current database or shared across all databases in the cluste= r', + descr =3D> 'statistics: reset collected statistics for a single table in= the current database or shared across all databases in the cluster', proname =3D> 'pg_stat_reset_single_table_counters', provolatile =3D> 'v', prorettype =3D> 'void', proargtypes =3D> 'oid', prosrc =3D> 'pg_stat_reset_single_table_counters', proacl =3D> '{POSTGRE= S=3DX}' }, +{ oid =3D> '8450', + descr =3D> 'statistics: reset collected statistics for a single index in= the current database or shared across all databases in the cluster', + proname =3D> 'pg_stat_reset_single_index_counters', provolatile =3D> 'v', + prorettype =3D> 'void', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_reset_single_index_counters', proacl =3D> '{POSTGRE= S=3DX}' }, +{ oid =3D> '8451', descr =3D> 'statistics: number of scans done for index', + proname =3D> 'pg_stat_get_idx_numscans', provolatile =3D> 's', proparall= el =3D> 'r', + prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_idx_numscans' }, +{ oid =3D> '8452', descr =3D> 'statistics: number of index entries returne= d by index', + proname =3D> 'pg_stat_get_idx_tuples_returned', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_idx_tuples_returned' }, +{ oid =3D> '8453', descr =3D> 'statistics: number of tuples fetched from t= able by index', + proname =3D> 'pg_stat_get_idx_tuples_fetched', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_idx_tuples_fetched' }, +{ oid =3D> '8454', descr =3D> 'statistics: number of blocks fetched for in= dex', + proname =3D> 'pg_stat_get_idx_blocks_fetched', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_idx_blocks_fetched' }, +{ oid =3D> '8455', descr =3D> 'statistics: number of blocks found in cache= for index', + proname =3D> 'pg_stat_get_idx_blocks_hit', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_idx_blocks_hit' }, +{ oid =3D> '8456', descr =3D> 'statistics: time of the last scan for index= ', + proname =3D> 'pg_stat_get_idx_lastscan', provolatile =3D> 's', proparall= el =3D> 'r', + prorettype =3D> 'timestamptz', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_idx_lastscan' }, +{ oid =3D> '8457', descr =3D> 'statistics: last reset for an index', + proname =3D> 'pg_stat_get_idx_stat_reset_time', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'timestamptz', proargtypes =3D> 'o= id', + prosrc =3D> 'pg_stat_get_idx_stat_reset_time' }, +{ oid =3D> '8458', descr =3D> 'statistics: number of scans done for index = in current transaction', + proname =3D> 'pg_stat_get_xact_idx_numscans', provolatile =3D> 'v', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_xact_idx_numscans' }, +{ oid =3D> '8459', descr =3D> 'statistics: number of index entries returne= d in current transaction', + proname =3D> 'pg_stat_get_xact_idx_tuples_returned', provolatile =3D> 'v= ', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_xact_idx_tuples_returned' }, +{ oid =3D> '8460', descr =3D> 'statistics: number of tuples fetched by ind= ex in current transaction', + proname =3D> 'pg_stat_get_xact_idx_tuples_fetched', provolatile =3D> 'v', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_xact_idx_tuples_fetched' }, +{ oid =3D> '8461', descr =3D> 'statistics: number of blocks fetched for in= dex in current transaction', + proname =3D> 'pg_stat_get_xact_idx_blocks_fetched', provolatile =3D> 'v', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_xact_idx_blocks_fetched' }, +{ oid =3D> '8462', descr =3D> 'statistics: number of blocks found in cache= for index in current transaction', + proname =3D> 'pg_stat_get_xact_idx_blocks_hit', provolatile =3D> 'v', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_xact_idx_blocks_hit' }, { oid =3D> '3777', descr =3D> 'statistics: reset collected statistics for a single function= in the current database', proname =3D> 'pg_stat_reset_single_function_counters', provolatile =3D> = 'v', diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 58a44857f131..23b85fc0c223 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -487,6 +487,20 @@ typedef struct PgStat_StatTabEntry TimestampTz stat_reset_time; } PgStat_StatTabEntry; =20 +typedef struct PgStat_StatIdxEntry +{ + PgStat_Counter numscans; + TimestampTz lastscan; + + PgStat_Counter tuples_returned; + PgStat_Counter tuples_fetched; + + PgStat_Counter blocks_fetched; + PgStat_Counter blocks_hit; + + TimestampTz stat_reset_time; +} PgStat_StatIdxEntry; + /* ------ * PgStat_WalCounters WAL activity data gathered from WalUsage * @@ -779,6 +793,12 @@ extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry= _ext(bool shared, Oid reloid, bool *may_free); extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id); +extern PgStat_TableStatus *find_tabstat_entry_kind(PgStat_Kind kind, Oid r= el_id); + +extern PgStat_StatIdxEntry *pgstat_fetch_stat_idxentry(Oid relid); +extern PgStat_StatIdxEntry *pgstat_fetch_stat_idxentry_ext(bool shared, + Oid reloid, + bool *may_free); =20 =20 /* diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat= _internal.h index b3dc3ff7d8bb..94c94c103feb 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -507,6 +507,12 @@ typedef struct PgStatShared_Relation PgStat_StatTabEntry stats; } PgStatShared_Relation; =20 +typedef struct PgStatShared_Index +{ + PgStatShared_Common header; + PgStat_StatIdxEntry stats; +} PgStatShared_Index; + typedef struct PgStatShared_Function { PgStatShared_Common header; @@ -787,6 +793,15 @@ extern void pgstat_relation_delete_pending_cb(PgStat_E= ntryRef *entry_ref); extern void pgstat_relation_reset_timestamp_cb(PgStatShared_Common *header= , TimestampTz ts); =20 =20 +/* + * Functions in pgstat_index.c + */ + +extern bool pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait); +extern void pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref); +extern void pgstat_index_reset_timestamp_cb(PgStatShared_Common *header, T= imestampTz ts); + + /* * Functions in pgstat_replslot.c */ diff --git a/src/include/utils/pgstat_kind.h b/src/include/utils/pgstat_kin= d.h index 2d78a0296834..45ca599d0dd6 100644 --- a/src/include/utils/pgstat_kind.h +++ b/src/include/utils/pgstat_kind.h @@ -26,19 +26,20 @@ /* stats for variable-numbered objects */ #define PGSTAT_KIND_DATABASE 1 /* database-wide statistics */ #define PGSTAT_KIND_RELATION 2 /* per-table statistics */ -#define PGSTAT_KIND_FUNCTION 3 /* per-function statistics */ -#define PGSTAT_KIND_REPLSLOT 4 /* per-slot statistics */ -#define PGSTAT_KIND_SUBSCRIPTION 5 /* per-subscription statistics */ -#define PGSTAT_KIND_BACKEND 6 /* per-backend statistics */ +#define PGSTAT_KIND_INDEX 3 /* per-index statistics */ +#define PGSTAT_KIND_FUNCTION 4 /* per-function statistics */ +#define PGSTAT_KIND_REPLSLOT 5 /* per-slot statistics */ +#define PGSTAT_KIND_SUBSCRIPTION 6 /* per-subscription statistics */ +#define PGSTAT_KIND_BACKEND 7 /* per-backend statistics */ =20 /* stats for fixed-numbered objects */ -#define PGSTAT_KIND_ARCHIVER 7 -#define PGSTAT_KIND_BGWRITER 8 -#define PGSTAT_KIND_CHECKPOINTER 9 -#define PGSTAT_KIND_IO 10 -#define PGSTAT_KIND_LOCK 11 -#define PGSTAT_KIND_SLRU 12 -#define PGSTAT_KIND_WAL 13 +#define PGSTAT_KIND_ARCHIVER 8 +#define PGSTAT_KIND_BGWRITER 9 +#define PGSTAT_KIND_CHECKPOINTER 10 +#define PGSTAT_KIND_IO 11 +#define PGSTAT_KIND_LOCK 12 +#define PGSTAT_KIND_SLRU 13 +#define PGSTAT_KIND_WAL 14 =20 #define PGSTAT_KIND_BUILTIN_MIN PGSTAT_KIND_DATABASE #define PGSTAT_KIND_BUILTIN_MAX PGSTAT_KIND_WAL diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/sys= tem_views.sql index 6c1c5545cb56..ce991e4269e7 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -722,9 +722,9 @@ CREATE VIEW pg_stat_all_tables AS pg_stat_get_numscans(C.oid) AS seq_scan, pg_stat_get_lastscan(C.oid) AS last_seq_scan, pg_stat_get_tuples_returned(C.oid) AS seq_tup_read, - sum(pg_stat_get_numscans(I.indexrelid))::bigint AS idx_scan, - max(pg_stat_get_lastscan(I.indexrelid)) AS last_idx_scan, - sum(pg_stat_get_tuples_fetched(I.indexrelid))::bigint + + sum(pg_stat_get_idx_numscans(I.indexrelid))::bigint AS idx_sca= n, + max(pg_stat_get_idx_lastscan(I.indexrelid)) AS last_idx_scan, + sum(pg_stat_get_idx_tuples_fetched(I.indexrelid))::bigint + pg_stat_get_tuples_fetched(C.oid) AS idx_tup_fetch, pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins, pg_stat_get_tuples_updated(C.oid) AS n_tup_upd, @@ -761,8 +761,8 @@ CREATE VIEW pg_stat_xact_all_tables AS C.relname AS relname, pg_stat_get_xact_numscans(C.oid) AS seq_scan, pg_stat_get_xact_tuples_returned(C.oid) AS seq_tup_read, - sum(pg_stat_get_xact_numscans(I.indexrelid))::bigint AS idx_sc= an, - sum(pg_stat_get_xact_tuples_fetched(I.indexrelid))::bigint + + sum(pg_stat_get_xact_idx_numscans(I.indexrelid))::bigint AS id= x_scan, + sum(pg_stat_get_xact_idx_tuples_fetched(I.indexrelid))::bigint= + pg_stat_get_xact_tuples_fetched(C.oid) AS idx_tup_fetch, pg_stat_get_xact_tuples_inserted(C.oid) AS n_tup_ins, pg_stat_get_xact_tuples_updated(C.oid) AS n_tup_upd, @@ -833,17 +833,17 @@ CREATE VIEW pg_statio_all_tables AS pg_class T ON C.reltoastrelid =3D T.oid LEFT JOIN pg_namespace N ON (N.oid =3D C.relnamespace) LEFT JOIN LATERAL ( - SELECT sum(pg_stat_get_blocks_fetched(indexrelid) - - pg_stat_get_blocks_hit(indexrelid))::bigint + SELECT sum(pg_stat_get_idx_blocks_fetched(indexrelid) - + pg_stat_get_idx_blocks_hit(indexrelid))::bigint AS idx_blks_read, - sum(pg_stat_get_blocks_hit(indexrelid))::bigint + sum(pg_stat_get_idx_blocks_hit(indexrelid))::bigint AS idx_blks_hit FROM pg_index WHERE indrelid =3D C.oid ) I ON true LEFT JOIN LATERAL ( - SELECT sum(pg_stat_get_blocks_fetched(indexrelid) - - pg_stat_get_blocks_hit(indexrelid))::bigint + SELECT sum(pg_stat_get_idx_blocks_fetched(indexrelid) - + pg_stat_get_idx_blocks_hit(indexrelid))::bigint AS idx_blks_read, - sum(pg_stat_get_blocks_hit(indexrelid))::bigint + sum(pg_stat_get_idx_blocks_hit(indexrelid))::bigint AS idx_blks_hit FROM pg_index WHERE indrelid =3D T.oid ) X ON true WHERE C.relkind IN ('r', 't', 'm'); @@ -865,11 +865,11 @@ CREATE VIEW pg_stat_all_indexes AS N.nspname AS schemaname, C.relname AS relname, I.relname AS indexrelname, - pg_stat_get_numscans(I.oid) AS idx_scan, - pg_stat_get_lastscan(I.oid) AS last_idx_scan, - pg_stat_get_tuples_returned(I.oid) AS idx_tup_read, - pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch, - pg_stat_get_stat_reset_time(I.oid) AS stats_reset + pg_stat_get_idx_numscans(I.oid) AS idx_scan, + pg_stat_get_idx_lastscan(I.oid) AS last_idx_scan, + pg_stat_get_idx_tuples_returned(I.oid) AS idx_tup_read, + pg_stat_get_idx_tuples_fetched(I.oid) AS idx_tup_fetch, + pg_stat_get_idx_stat_reset_time(I.oid) AS stats_reset FROM pg_class C JOIN pg_index X ON C.oid =3D X.indrelid JOIN pg_class I ON I.oid =3D X.indexrelid @@ -893,10 +893,10 @@ CREATE VIEW pg_statio_all_indexes AS N.nspname AS schemaname, C.relname AS relname, I.relname AS indexrelname, - pg_stat_get_blocks_fetched(I.oid) - - pg_stat_get_blocks_hit(I.oid) AS idx_blks_read, - pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit, - pg_stat_get_stat_reset_time(I.oid) AS stats_reset + pg_stat_get_idx_blocks_fetched(I.oid) - + pg_stat_get_idx_blocks_hit(I.oid) AS idx_blks_read, + pg_stat_get_idx_blocks_hit(I.oid) AS idx_blks_hit, + pg_stat_get_idx_stat_reset_time(I.oid) AS stats_reset FROM pg_class C JOIN pg_index X ON C.oid =3D X.indrelid JOIN pg_class I ON I.oid =3D X.indexrelid diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activi= ty/Makefile index 5fed953c28a7..2e32d1485d64 100644 --- a/src/backend/utils/activity/Makefile +++ b/src/backend/utils/activity/Makefile @@ -25,6 +25,7 @@ OBJS =3D \ pgstat_checkpointer.o \ pgstat_database.o \ pgstat_function.o \ + pgstat_index.o \ pgstat_io.o \ pgstat_kind.o \ pgstat_lock.o \ diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/act= ivity/meson.build index 470b5dac402b..e6dcb2e26fc5 100644 --- a/src/backend/utils/activity/meson.build +++ b/src/backend/utils/activity/meson.build @@ -10,6 +10,7 @@ backend_sources +=3D files( 'pgstat_checkpointer.c', 'pgstat_database.c', 'pgstat_function.c', + 'pgstat_index.c', 'pgstat_io.c', 'pgstat_kind.c', 'pgstat_lock.c', diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activi= ty/pgstat.c index 8b2a5ec36750..3aa10162f8ad 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -317,6 +317,22 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos= [PGSTAT_KIND_BUILTIN_SIZE] .reset_timestamp_cb =3D pgstat_relation_reset_timestamp_cb, }, =20 + [PGSTAT_KIND_INDEX] =3D { + .name =3D "index", + + .fixed_amount =3D false, + .write_to_file =3D true, + + .shared_size =3D sizeof(PgStatShared_Index), + .shared_data_off =3D offsetof(PgStatShared_Index, stats), + .shared_data_len =3D sizeof(((PgStatShared_Index *) 0)->stats), + .pending_size =3D sizeof(PgStat_TableStatus), + + .flush_pending_cb =3D pgstat_index_flush_cb, + .delete_pending_cb =3D pgstat_index_delete_pending_cb, + .reset_timestamp_cb =3D pgstat_index_reset_timestamp_cb, + }, + [PGSTAT_KIND_FUNCTION] =3D { .name =3D "function", =20 diff --git a/src/backend/utils/activity/pgstat_index.c b/src/backend/utils/= activity/pgstat_index.c new file mode 100644 index 000000000000..e9fc27f2e4b4 --- /dev/null +++ b/src/backend/utils/activity/pgstat_index.c @@ -0,0 +1,129 @@ +/* -----------------------------------------------------------------------= -- + * + * pgstat_index.c + * Implementation of index statistics. + * + * This file contains the implementation of index statistics. + * + * Copyright (c) 2001-2026, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/activity/pgstat_index.c + * -----------------------------------------------------------------------= -- + */ + +#include "postgres.h" + +#include "access/xact.h" +#include "catalog/catalog.h" +#include "utils/memutils.h" +#include "utils/pgstat_internal.h" +#include "utils/rel.h" +#include "utils/timestamp.h" + + +/* + * Flush out pending stats for an index entry. + * + * If nowait is true and the lock could not be immediately acquired, retur= ns + * false without flushing the entry. Otherwise returns true. + * + * Some of the stats are copied to the corresponding pending database stats + * entry when successfully flushing. + */ +bool +pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) +{ + Oid dboid; + PgStat_TableStatus *lstats; /* pending stats entry */ + PgStatShared_Index *shidxstats; + PgStat_StatIdxEntry *idxentry; /* index entry of shared stats */ + PgStat_StatDBEntry *dbentry; /* pending database entry */ + + dboid =3D entry_ref->shared_entry->key.dboid; + lstats =3D (PgStat_TableStatus *) entry_ref->pending; + shidxstats =3D (PgStatShared_Index *) entry_ref->shared_stats; + + /* + * Ignore entries that didn't accumulate any actual counts, such as + * indexes that were opened by the planner but not used. + */ + if (pg_memory_is_all_zeros(&lstats->counts, + sizeof(struct PgStat_TableCounts))) + return true; + + if (!pgstat_lock_entry(entry_ref, nowait)) + return false; + + /* Add the values to the shared entry. */ + idxentry =3D &shidxstats->stats; + + idxentry->numscans +=3D lstats->counts.numscans; + if (lstats->counts.numscans) + { + TimestampTz t =3D GetCurrentTransactionStopTimestamp(); + + if (t > idxentry->lastscan) + idxentry->lastscan =3D t; + } + idxentry->tuples_returned +=3D lstats->counts.tuples_returned; + idxentry->tuples_fetched +=3D lstats->counts.tuples_fetched; + idxentry->blocks_fetched +=3D lstats->counts.blocks_fetched; + idxentry->blocks_hit +=3D lstats->counts.blocks_hit; + + pgstat_unlock_entry(entry_ref); + + /* The entry was successfully flushed, add the same to database stats */ + dbentry =3D pgstat_prep_database_pending(dboid); + dbentry->blocks_fetched +=3D lstats->counts.blocks_fetched; + dbentry->blocks_hit +=3D lstats->counts.blocks_hit; + + return true; +} + +/* + * Callback to delete pending index stats. + */ +void +pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref) +{ + PgStat_TableStatus *pending =3D (PgStat_TableStatus *) entry_ref->pending; + + if (pending->relation) + pgstat_unlink_relation(pending->relation); +} + +/* + * Callback to reset the timestamp on an index stats entry. + */ +void +pgstat_index_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz t= s) +{ + ((PgStatShared_Index *) header)->stats.stat_reset_time =3D ts; +} + +/* + * Support function for the SQL-callable pgstat* functions. Returns + * the collected statistics for one index or NULL. NULL doesn't mean + * that the index doesn't exist, just that there are no statistics, so the + * caller is better off to report ZERO instead. + */ +PgStat_StatIdxEntry * +pgstat_fetch_stat_idxentry(Oid relid) +{ + return pgstat_fetch_stat_idxentry_ext(IsSharedRelation(relid), relid, NUL= L); +} + +/* + * More efficient version of pgstat_fetch_stat_idxentry(), allowing to spe= cify + * whether the to-be-accessed index is a shared relation or not. This ver= sion + * also returns whether the caller can pfree() the result if desired. + */ +PgStat_StatIdxEntry * +pgstat_fetch_stat_idxentry_ext(bool shared, Oid reloid, bool *may_free) +{ + Oid dboid =3D (shared ? InvalidOid : MyDatabaseId); + + return (PgStat_StatIdxEntry *) + pgstat_fetch_entry(PGSTAT_KIND_INDEX, dboid, reloid, may_free); +} diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/uti= ls/activity/pgstat_relation.c index 04f2eb21d0bb..379a65aa28fb 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -42,12 +42,24 @@ typedef struct TwoPhasePgStatRecord } TwoPhasePgStatRecord; =20 =20 -static PgStat_TableStatus *pgstat_prep_relation_pending(Oid rel_id, bool i= sshared); +static PgStat_TableStatus *pgstat_prep_relation_pending(PgStat_Kind kind, + Oid rel_id, bool isshared); static void add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int ne= st_level); static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info); static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool is= _drop); static void restore_truncdrop_counters(PgStat_TableXactStatus *trans); =20 +/* + * Determine the stats kind for a relation based on its relkind. + */ +static inline PgStat_Kind +pgstat_get_relation_kind(char relkind) +{ + if (relkind =3D=3D RELKIND_INDEX) + return PGSTAT_KIND_INDEX; + return PGSTAT_KIND_RELATION; +} + =20 /* * Copy stats between relations. This is used for things like REINDEX @@ -56,25 +68,52 @@ static void restore_truncdrop_counters(PgStat_TableXact= Status *trans); void pgstat_copy_relation_stats(Relation dst, Relation src) { - PgStat_StatTabEntry *srcstats; - PgStatShared_Relation *dstshstats; - PgStat_EntryRef *dst_ref; + PgStat_Kind kind =3D pgstat_get_relation_kind(src->rd_rel->relkind); =20 - srcstats =3D pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared, - RelationGetRelid(src), - NULL); - if (!srcstats) - return; + if (kind =3D=3D PGSTAT_KIND_INDEX) + { + PgStat_StatIdxEntry *srcstats; + PgStatShared_Index *dstshstats; + PgStat_EntryRef *dst_ref; =20 - dst_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, - dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId, - RelationGetRelid(dst), - false); + srcstats =3D pgstat_fetch_stat_idxentry_ext(src->rd_rel->relisshared, + RelationGetRelid(src), + NULL); + if (!srcstats) + return; =20 - dstshstats =3D (PgStatShared_Relation *) dst_ref->shared_stats; - dstshstats->stats =3D *srcstats; + dst_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX, + dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId, + RelationGetRelid(dst), + false); =20 - pgstat_unlock_entry(dst_ref); + dstshstats =3D (PgStatShared_Index *) dst_ref->shared_stats; + dstshstats->stats =3D *srcstats; + + pgstat_unlock_entry(dst_ref); + } + else + { + PgStat_StatTabEntry *srcstats; + PgStatShared_Relation *dstshstats; + PgStat_EntryRef *dst_ref; + + srcstats =3D pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared, + RelationGetRelid(src), + NULL); + if (!srcstats) + return; + + dst_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, + dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId, + RelationGetRelid(dst), + false); + + dstshstats =3D (PgStatShared_Relation *) dst_ref->shared_stats; + dstshstats->stats =3D *srcstats; + + pgstat_unlock_entry(dst_ref); + } } =20 /* @@ -131,11 +170,16 @@ pgstat_init_relation(Relation rel) void pgstat_assoc_relation(Relation rel) { + PgStat_Kind kind; + Assert(rel->pgstat_enabled); Assert(rel->pgstat_info =3D=3D NULL); =20 + kind =3D pgstat_get_relation_kind(rel->rd_rel->relkind); + /* find or make the PgStat_TableStatus entry, and update link */ - rel->pgstat_info =3D pgstat_prep_relation_pending(RelationGetRelid(rel), + rel->pgstat_info =3D pgstat_prep_relation_pending(kind, + RelationGetRelid(rel), rel->rd_rel->relisshared); =20 /* don't allow link a stats to multiple relcache entries */ @@ -168,7 +212,9 @@ pgstat_unlink_relation(Relation rel) void pgstat_create_relation(Relation rel) { - pgstat_create_transactional(PGSTAT_KIND_RELATION, + PgStat_Kind kind =3D pgstat_get_relation_kind(rel->rd_rel->relkind); + + pgstat_create_transactional(kind, rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId, RelationGetRelid(rel)); } @@ -181,8 +227,9 @@ pgstat_drop_relation(Relation rel) { int nest_level =3D GetCurrentTransactionNestLevel(); PgStat_TableStatus *pgstat_info; + PgStat_Kind kind =3D pgstat_get_relation_kind(rel->rd_rel->relkind); =20 - pgstat_drop_transactional(PGSTAT_KIND_RELATION, + pgstat_drop_transactional(kind, rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId, RelationGetRelid(rel)); =20 @@ -500,16 +547,25 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloi= d, bool *may_free) */ PgStat_TableStatus * find_tabstat_entry(Oid rel_id) +{ + return find_tabstat_entry_kind(PGSTAT_KIND_RELATION, rel_id); +} + +/* + * Same as find_tabstat_entry but for a specific stats kind. + */ +PgStat_TableStatus * +find_tabstat_entry_kind(PgStat_Kind kind, Oid rel_id) { PgStat_EntryRef *entry_ref; PgStat_TableXactStatus *trans; PgStat_TableStatus *tabentry =3D NULL; PgStat_TableStatus *tablestatus =3D NULL; =20 - entry_ref =3D pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, MyDatabase= Id, rel_id); + entry_ref =3D pgstat_fetch_pending_entry(kind, MyDatabaseId, rel_id); if (!entry_ref) { - entry_ref =3D pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, InvalidOi= d, rel_id); + entry_ref =3D pgstat_fetch_pending_entry(kind, InvalidOid, rel_id); if (!entry_ref) return tablestatus; } @@ -752,7 +808,7 @@ pgstat_twophase_postcommit(FullTransactionId fxid, uint= 16 info, PgStat_TableStatus *pgstat_info; =20 /* Find or create a tabstat entry for the rel */ - pgstat_info =3D pgstat_prep_relation_pending(rec->id, rec->shared); + pgstat_info =3D pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->i= d, rec->shared); =20 /* Same math as in AtEOXact_PgStat, commit case */ pgstat_info->counts.tuples_inserted +=3D rec->tuples_inserted; @@ -788,7 +844,7 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint1= 6 info, PgStat_TableStatus *pgstat_info; =20 /* Find or create a tabstat entry for the rel */ - pgstat_info =3D pgstat_prep_relation_pending(rec->id, rec->shared); + pgstat_info =3D pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->i= d, rec->shared); =20 /* Same math as in AtEOXact_PgStat, abort case */ if (rec->truncdropped) @@ -922,12 +978,12 @@ pgstat_relation_reset_timestamp_cb(PgStatShared_Commo= n *header, TimestampTz ts) * initialized if not exists. */ static PgStat_TableStatus * -pgstat_prep_relation_pending(Oid rel_id, bool isshared) +pgstat_prep_relation_pending(PgStat_Kind kind, Oid rel_id, bool isshared) { PgStat_EntryRef *entry_ref; PgStat_TableStatus *pending; =20 - entry_ref =3D pgstat_prep_pending_entry(PGSTAT_KIND_RELATION, + entry_ref =3D pgstat_prep_pending_entry(kind, isshared ? InvalidOid : MyDatabaseId, rel_id, NULL); pending =3D entry_ref->pending; diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pg= statfuncs.c index 565d0e70768b..946025f39ed0 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -108,6 +108,40 @@ PG_STAT_GET_RELENTRY_INT64(tuples_updated) /* pg_stat_get_vacuum_count */ PG_STAT_GET_RELENTRY_INT64(vacuum_count) =20 +/* + * Accessor macro for index stats entries (PgStat_StatIdxEntry). + */ +#define PG_STAT_GET_IDXENTRY_INT64(stat) \ +Datum \ +CppConcat(pg_stat_get_idx_,stat)(PG_FUNCTION_ARGS) \ +{ \ + Oid relid =3D PG_GETARG_OID(0); \ + int64 result; \ + PgStat_StatIdxEntry *idxentry; \ + \ + if ((idxentry =3D pgstat_fetch_stat_idxentry(relid)) =3D=3D NULL) \ + result =3D 0; \ + else \ + result =3D (int64) (idxentry->stat); \ + \ + PG_RETURN_INT64(result); \ +} + +/* pg_stat_get_idx_numscans */ +PG_STAT_GET_IDXENTRY_INT64(numscans) + +/* pg_stat_get_idx_tuples_returned */ +PG_STAT_GET_IDXENTRY_INT64(tuples_returned) + +/* pg_stat_get_idx_tuples_fetched */ +PG_STAT_GET_IDXENTRY_INT64(tuples_fetched) + +/* pg_stat_get_idx_blocks_fetched */ +PG_STAT_GET_IDXENTRY_INT64(blocks_fetched) + +/* pg_stat_get_idx_blocks_hit */ +PG_STAT_GET_IDXENTRY_INT64(blocks_hit) + #define PG_STAT_GET_RELENTRY_FLOAT8(stat) \ Datum \ CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS) \ @@ -173,6 +207,34 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan) /* pg_stat_get_stat_reset_time */ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time) =20 +/* + * Accessor macro for index timestamp fields. + */ +#define PG_STAT_GET_IDXENTRY_TIMESTAMPTZ(stat) \ +Datum \ +CppConcat(pg_stat_get_idx_,stat)(PG_FUNCTION_ARGS) \ +{ \ + Oid relid =3D PG_GETARG_OID(0); \ + TimestampTz result; \ + PgStat_StatIdxEntry *idxentry; \ + \ + if ((idxentry =3D pgstat_fetch_stat_idxentry(relid)) =3D=3D NULL) \ + result =3D 0; \ + else \ + result =3D idxentry->stat; \ + \ + if (result =3D=3D 0) \ + PG_RETURN_NULL(); \ + else \ + PG_RETURN_TIMESTAMPTZ(result); \ +} + +/* pg_stat_get_idx_lastscan */ +PG_STAT_GET_IDXENTRY_TIMESTAMPTZ(lastscan) + +/* pg_stat_get_idx_stat_reset_time */ +PG_STAT_GET_IDXENTRY_TIMESTAMPTZ(stat_reset_time) + Datum pg_stat_get_function_calls(PG_FUNCTION_ARGS) { @@ -1906,6 +1968,41 @@ PG_STAT_GET_XACT_RELENTRY_INT64(tuples_updated) /* pg_stat_get_xact_tuples_deleted */ PG_STAT_GET_XACT_RELENTRY_INT64(tuples_deleted) =20 +/* + * Accessor macro for in-transaction index stats. + */ +#define PG_STAT_GET_XACT_IDXENTRY_INT64(stat) \ +Datum \ +CppConcat(pg_stat_get_xact_idx_,stat)(PG_FUNCTION_ARGS) \ +{ \ + Oid relid =3D PG_GETARG_OID(0); \ + int64 result; \ + PgStat_TableStatus *tabentry; \ + \ + tabentry =3D find_tabstat_entry_kind(PGSTAT_KIND_INDEX, relid); \ + if (!tabentry) \ + result =3D 0; \ + else \ + result =3D (int64) (tabentry->counts.stat); \ + \ + PG_RETURN_INT64(result); \ +} + +/* pg_stat_get_xact_idx_numscans */ +PG_STAT_GET_XACT_IDXENTRY_INT64(numscans) + +/* pg_stat_get_xact_idx_tuples_returned */ +PG_STAT_GET_XACT_IDXENTRY_INT64(tuples_returned) + +/* pg_stat_get_xact_idx_tuples_fetched */ +PG_STAT_GET_XACT_IDXENTRY_INT64(tuples_fetched) + +/* pg_stat_get_xact_idx_blocks_fetched */ +PG_STAT_GET_XACT_IDXENTRY_INT64(blocks_fetched) + +/* pg_stat_get_xact_idx_blocks_hit */ +PG_STAT_GET_XACT_IDXENTRY_INT64(blocks_hit) + Datum pg_stat_get_xact_function_calls(PG_FUNCTION_ARGS) { @@ -2047,6 +2144,17 @@ pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } =20 +Datum +pg_stat_reset_single_index_counters(PG_FUNCTION_ARGS) +{ + Oid idxoid =3D PG_GETARG_OID(0); + Oid dboid =3D (IsSharedRelation(idxoid) ? InvalidOid : MyDatabaseId); + + pgstat_reset(PGSTAT_KIND_INDEX, dboid, idxoid); + + PG_RETURN_VOID(); +} + Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS) { diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expecte= d/rules.out index 39905c2de142..ffec0903f038 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1806,11 +1806,11 @@ pg_stat_all_indexes| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, - pg_stat_get_numscans(i.oid) AS idx_scan, - pg_stat_get_lastscan(i.oid) AS last_idx_scan, - pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, - pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch, - pg_stat_get_stat_reset_time(i.oid) AS stats_reset + pg_stat_get_idx_numscans(i.oid) AS idx_scan, + pg_stat_get_idx_lastscan(i.oid) AS last_idx_scan, + pg_stat_get_idx_tuples_returned(i.oid) AS idx_tup_read, + pg_stat_get_idx_tuples_fetched(i.oid) AS idx_tup_fetch, + pg_stat_get_idx_stat_reset_time(i.oid) AS stats_reset FROM (((pg_class c JOIN pg_index x ON ((c.oid =3D x.indrelid))) JOIN pg_class i ON ((i.oid =3D x.indexrelid))) @@ -1822,9 +1822,9 @@ pg_stat_all_tables| SELECT c.oid AS relid, pg_stat_get_numscans(c.oid) AS seq_scan, pg_stat_get_lastscan(c.oid) AS last_seq_scan, pg_stat_get_tuples_returned(c.oid) AS seq_tup_read, - (sum(pg_stat_get_numscans(i.indexrelid)))::bigint AS idx_scan, - max(pg_stat_get_lastscan(i.indexrelid)) AS last_idx_scan, - ((sum(pg_stat_get_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get= _tuples_fetched(c.oid)) AS idx_tup_fetch, + (sum(pg_stat_get_idx_numscans(i.indexrelid)))::bigint AS idx_scan, + max(pg_stat_get_idx_lastscan(i.indexrelid)) AS last_idx_scan, + ((sum(pg_stat_get_idx_tuples_fetched(i.indexrelid)))::bigint + pg_stat= _get_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, @@ -2452,8 +2452,8 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, c.relname, pg_stat_get_xact_numscans(c.oid) AS seq_scan, pg_stat_get_xact_tuples_returned(c.oid) AS seq_tup_read, - (sum(pg_stat_get_xact_numscans(i.indexrelid)))::bigint AS idx_scan, - ((sum(pg_stat_get_xact_tuples_fetched(i.indexrelid)))::bigint + pg_sta= t_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch, + (sum(pg_stat_get_xact_idx_numscans(i.indexrelid)))::bigint AS idx_scan, + ((sum(pg_stat_get_xact_idx_tuples_fetched(i.indexrelid)))::bigint + pg= _stat_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del, @@ -2506,9 +2506,9 @@ pg_statio_all_indexes| SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, - (pg_stat_get_blocks_fetched(i.oid) - pg_stat_get_blocks_hit(i.oid)) AS= idx_blks_read, - pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit, - pg_stat_get_stat_reset_time(i.oid) AS stats_reset + (pg_stat_get_idx_blocks_fetched(i.oid) - pg_stat_get_idx_blocks_hit(i.= oid)) AS idx_blks_read, + pg_stat_get_idx_blocks_hit(i.oid) AS idx_blks_hit, + pg_stat_get_idx_stat_reset_time(i.oid) AS stats_reset FROM (((pg_class c JOIN pg_index x ON ((c.oid =3D x.indrelid))) JOIN pg_class i ON ((i.oid =3D x.indexrelid))) @@ -2538,12 +2538,12 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM ((((pg_class c LEFT JOIN pg_class t ON ((c.reltoastrelid =3D t.oid))) LEFT JOIN pg_namespace n ON ((n.oid =3D c.relnamespace))) - LEFT JOIN LATERAL ( SELECT (sum((pg_stat_get_blocks_fetched(pg_index.= indexrelid) - pg_stat_get_blocks_hit(pg_index.indexrelid))))::bigint AS idx= _blks_read, - (sum(pg_stat_get_blocks_hit(pg_index.indexrelid)))::bigint AS = idx_blks_hit + LEFT JOIN LATERAL ( SELECT (sum((pg_stat_get_idx_blocks_fetched(pg_in= dex.indexrelid) - pg_stat_get_idx_blocks_hit(pg_index.indexrelid))))::bigin= t AS idx_blks_read, + (sum(pg_stat_get_idx_blocks_hit(pg_index.indexrelid)))::bigint= AS idx_blks_hit FROM pg_index WHERE (pg_index.indrelid =3D c.oid)) i ON (true)) - LEFT JOIN LATERAL ( SELECT (sum((pg_stat_get_blocks_fetched(pg_index.= indexrelid) - pg_stat_get_blocks_hit(pg_index.indexrelid))))::bigint AS idx= _blks_read, - (sum(pg_stat_get_blocks_hit(pg_index.indexrelid)))::bigint AS = idx_blks_hit + LEFT JOIN LATERAL ( SELECT (sum((pg_stat_get_idx_blocks_fetched(pg_in= dex.indexrelid) - pg_stat_get_idx_blocks_hit(pg_index.indexrelid))))::bigin= t AS idx_blks_read, + (sum(pg_stat_get_idx_blocks_hit(pg_index.indexrelid)))::bigint= AS idx_blks_hit FROM pg_index WHERE (pg_index.indrelid =3D t.oid)) x ON (true)) WHERE (c.relkind =3D ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expecte= d/stats.out index 03cbc1cdef59..b44e30f557c9 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -123,18 +123,19 @@ SELECT id, name, fixed_amount, ----+--------------+--------------+-----------+--------------- 1 | database | f | t | t 2 | relation | f | f | t - 3 | function | f | f | t - 4 | replslot | f | t | t - 5 | subscription | f | t | t - 6 | backend | f | t | f - 7 | archiver | t | f | t - 8 | bgwriter | t | f | t - 9 | checkpointer | t | f | t - 10 | io | t | f | t - 11 | lock | t | f | t - 12 | slru | t | f | t - 13 | wal | t | f | t -(13 rows) + 3 | index | f | f | t + 4 | function | f | f | t + 5 | replslot | f | t | t + 6 | subscription | f | t | t + 7 | backend | f | t | f + 8 | archiver | t | f | t + 9 | bgwriter | t | f | t + 10 | checkpointer | t | f | t + 11 | io | t | f | t + 12 | lock | t | f | t + 13 | slru | t | f | t + 14 | wal | t | f | t +(14 rows) =20 -- ensure that both seqscan and indexscan plans are allowed SET enable_seqscan TO on; @@ -907,8 +908,8 @@ SELECT idx_scan, :'test_last_idx' < last_idx_scan AS id= x_ok, (1 row) =20 -- check that the stats in pg_stat_all_indexes are reset -SELECT pg_stat_reset_single_table_counters('test_last_scan_pkey'::regclass= ); - pg_stat_reset_single_table_counters=20 +SELECT pg_stat_reset_single_index_counters('test_last_scan_pkey'::regclass= ); + pg_stat_reset_single_index_counters=20 ------------------------------------- =20 (1 row) @@ -1392,21 +1393,21 @@ select a from stats_test_tab1 where a =3D 3; 3 (1 row) =20 -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- t (1 row) =20 -- pg_stat_have_stats returns false for dropped index with stats -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- t (1 row) =20 DROP index stats_test_idx1; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- f @@ -1422,14 +1423,14 @@ select a from stats_test_tab1 where a =3D 3; 3 (1 row) =20 -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- t (1 row) =20 ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- f @@ -1444,7 +1445,7 @@ select a from stats_test_tab1 where a =3D 3; 3 (1 row) =20 -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- t @@ -1452,7 +1453,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_= test_idx1_oid); =20 REINDEX index CONCURRENTLY stats_test_idx1; -- false for previous oid -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- f @@ -1460,7 +1461,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_= test_idx1_oid); =20 -- true for new oid SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- t @@ -1468,7 +1469,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_= test_idx1_oid); =20 -- pg_stat_have_stats returns true for a rolled back drop index with stats BEGIN; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- t @@ -1476,7 +1477,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_= test_idx1_oid); =20 DROP index stats_test_idx1; ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); pg_stat_have_stats=20 -------------------- t diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index 4c265d1245c7..d4623c32cd32 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -398,7 +398,7 @@ SELECT idx_scan, :'test_last_idx' < last_idx_scan AS id= x_ok, FROM pg_stat_all_indexes WHERE indexrelid =3D 'test_last_scan_pkey'::reg= class; =20 -- check that the stats in pg_stat_all_indexes are reset -SELECT pg_stat_reset_single_table_counters('test_last_scan_pkey'::regclass= ); +SELECT pg_stat_reset_single_index_counters('test_last_scan_pkey'::regclass= ); =20 SELECT idx_scan, stats_reset IS NOT NULL AS has_stats_reset FROM pg_stat_all_indexes WHERE indexrelid =3D 'test_last_scan_pkey'::reg= class; @@ -619,40 +619,40 @@ CREATE index stats_test_idx1 on stats_test_tab1(a); SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset SET enable_seqscan TO off; select a from stats_test_tab1 where a =3D 3; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); =20 -- pg_stat_have_stats returns false for dropped index with stats -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); DROP index stats_test_idx1; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); =20 -- pg_stat_have_stats returns false for rolled back index creation BEGIN; CREATE index stats_test_idx1 on stats_test_tab1(a); SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset select a from stats_test_tab1 where a =3D 3; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); =20 -- pg_stat_have_stats returns true for reindex CONCURRENTLY CREATE index stats_test_idx1 on stats_test_tab1(a); SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset select a from stats_test_tab1 where a =3D 3; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); REINDEX index CONCURRENTLY stats_test_idx1; -- false for previous oid -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); -- true for new oid SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); =20 -- pg_stat_have_stats returns true for a rolled back drop index with stats BEGIN; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); DROP index stats_test_idx1; ROLLBACK; -SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); +SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid); =20 -- put enable_seqscan back to on SET enable_seqscan TO on; diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 858788b227c4..f3024aa16bc1 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -5867,7 +5867,7 @@ description | Waiting for a newly initialized WAL fil= e to reach durable storage void - Resets statistics for a single table or index in the current datab= ase + Resets statistics for a single table in the current database or shared across all databases in the cluster to zero. It also resets statistics for a single sequence or materialized vi= ew in the current database. @@ -5878,6 +5878,24 @@ description | Waiting for a newly initialized WAL fi= le to reach durable storage =20 + + + + pg_stat_reset_single_index_counters + + pg_stat_reset_single_index_counters ( o= id ) + void + + + Resets statistics for a single index in the current database + or shared across all databases in the cluster to zero. + + + This function is restricted to superusers by default, but other us= ers + can be granted EXECUTE to run the function. + + + --=20 2.55.0 --uu1QJiDJI3oJPnCR Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename=v13-0002-Refactor-PgStat_TableStatus-to-new-PgStat_Relati.patch Content-Transfer-Encoding: quoted-printable =46rom 5f8a69eb6e08a1c95ca9ba67e72324daf470075b Mon Sep 17 00:00:00 2001 =46rom: Michael Paquier Date: Fri, 10 Jul 2026 12:19:56 +0900 Subject: [PATCH v13 2/3] Refactor PgStat_TableStatus to new PgStat_RelationStatus This new structure is split depending on the relkind it deals with: - PGSTAT_KIND_RELATION, for tables. - PGSTAT_KIND_INDEX, for indexes. This change makes the barrier cleaner between the handling of tables and indexes, by being able to track precisely what are the counters used by one or the other for pending data. Using a common ground for both eases the tracking of Relations in the relcache. --- src/include/pgstat.h | 110 +++++-- src/include/utils/pgstat_internal.h | 4 +- src/include/utils/rel.h | 2 +- src/backend/utils/activity/pgstat.c | 4 +- src/backend/utils/activity/pgstat_index.c | 26 +- src/backend/utils/activity/pgstat_relation.c | 296 ++++++++++--------- src/backend/utils/adt/pgstatfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 2 +- src/tools/pgindent/typedefs.list | 2 +- 9 files changed, 257 insertions(+), 202 deletions(-) diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 23b85fc0c223..892cdf24139b 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -125,14 +125,12 @@ typedef struct PgStat_BackendSubEntry * of pg_memory_is_all_zeros() to detect whether there are any stats updat= es * to apply. * - * It is a component of PgStat_TableStatus (within-backend state). + * It is a component of PgStat_RelationStatus (within-backend state, for + * table data). * - * Note: for a table, tuples_returned is the number of tuples successfully - * fetched by heap_getnext, while tuples_fetched is the number of tuples - * successfully fetched by heap_fetch under the control of bitmap indexsca= ns. - * For an index, tuples_returned is the number of index entries returned by - * the index AM, while tuples_fetched is the number of tuples successfully - * fetched by heap_fetch under the control of simple indexscans for this i= ndex. + * Note: tuples_returned is the number of tuples successfully fetched by + * heap_getnext, while tuples_fetched is the number of tuples successfully + * fetched by heap_fetch under the control of bitmap indexscans. * * tuples_inserted/updated/deleted/hot_updated/newpage_updated count attem= pted * actions, regardless of whether the transaction committed. delta_live_t= uples, @@ -163,34 +161,69 @@ typedef struct PgStat_TableCounts } PgStat_TableCounts; =20 /* ---------- - * PgStat_TableStatus Per-table status within a backend + * PgStat_IndexCounts Per-index pending event counters + * + * Note: tuples_returned is the number of index entries returned by + * the index AM, while tuples_fetched is the number of tuples successfully + * fetched by heap_fetch under the control of simple indexscans for this + * index. + * + * It is a component of PgStat_RelationStatus (within-backend state, for + * index data). + * ---------- + */ +typedef struct PgStat_IndexCounts +{ + PgStat_Counter numscans; + PgStat_Counter tuples_returned; + PgStat_Counter tuples_fetched; + PgStat_Counter blocks_fetched; + PgStat_Counter blocks_hit; +} PgStat_IndexCounts; + +/* ---------- + * PgStat_RelationStatus Per-relation pending status within a backend * * Many of the event counters are nontransactional, ie, we count events * in committed and aborted transactions alike. For these, we just count - * directly in the PgStat_TableStatus. However, delta_live_tuples, + * directly in the PgStat_RelationStatus. However, delta_live_tuples, * delta_dead_tuples, and changed_tuples must be derived from event counts * with awareness of whether the transaction or subtransaction committed or * aborted. Hence, we also keep a stack of per-(sub)transaction status * records for every table modified in the current transaction. At commit * or abort, we propagate tuples_inserted/updated/deleted up to the - * parent subtransaction level, or out to the parent PgStat_TableStatus, + * parent subtransaction level, or out to the parent PgStat_RelationStatus, * as appropriate. + * + * 'kind' tracks the stats kind we are dealing with, for table or index + * pending data. * ---------- */ -typedef struct PgStat_TableStatus +typedef struct PgStat_RelationStatus { - Oid id; /* table's OID */ - bool shared; /* is it a shared catalog? */ - struct PgStat_TableXactStatus *trans; /* lowest subxact's counts */ - PgStat_TableCounts counts; /* event counts to be sent */ + PgStat_Kind kind; /* PGSTAT_KIND_RELATION or PGSTAT_KIND_INDEX */ Relation relation; /* rel that is using this entry */ -} PgStat_TableStatus; + union + { + /* table counters */ + struct + { + Oid id; /* table's OID */ + bool shared; /* is it a shared catalog? */ + struct PgStat_RelXactStatus *trans; /* lowest subxact's counts */ + PgStat_TableCounts counts; /* event counts to be sent */ + } tab; + + /* index counters */ + PgStat_IndexCounts idx; + }; +} PgStat_RelationStatus; =20 /* ---------- - * PgStat_TableXactStatus Per-table, per-subtransaction status + * PgStat_RelXactStatus Per-relation, per-subtransaction status * ---------- */ -typedef struct PgStat_TableXactStatus +typedef struct PgStat_RelXactStatus { PgStat_Counter tuples_inserted; /* tuples inserted in (sub)xact */ PgStat_Counter tuples_updated; /* tuples updated in (sub)xact */ @@ -203,11 +236,11 @@ typedef struct PgStat_TableXactStatus PgStat_Counter deleted_pre_truncdrop; int nest_level; /* subtransaction nest level */ /* links to other structs for same relation: */ - struct PgStat_TableXactStatus *upper; /* next higher subxact if any */ - PgStat_TableStatus *parent; /* per-table status */ + struct PgStat_RelXactStatus *upper; /* next higher subxact if any */ + PgStat_RelationStatus *parent; /* per-table status */ /* structs of same subxact level are linked here: */ - struct PgStat_TableXactStatus *next; /* next of same subxact */ -} PgStat_TableXactStatus; + struct PgStat_RelXactStatus *next; /* next of same subxact */ +} PgStat_RelXactStatus; =20 =20 /* ------------------------------------------------------------ @@ -744,37 +777,52 @@ extern void pgstat_report_analyze(Relation rel, #define pgstat_count_heap_scan(rel) \ do { \ if (pgstat_should_count_relation(rel)) \ - (rel)->pgstat_info->counts.numscans++; \ + (rel)->pgstat_info->tab.counts.numscans++; \ } while (0) #define pgstat_count_heap_getnext(rel) \ do { \ if (pgstat_should_count_relation(rel)) \ - (rel)->pgstat_info->counts.tuples_returned++; \ + (rel)->pgstat_info->tab.counts.tuples_returned++; \ } while (0) #define pgstat_count_heap_fetch(rel) \ do { \ if (pgstat_should_count_relation(rel)) \ - (rel)->pgstat_info->counts.tuples_fetched++; \ + { \ + if ((rel)->pgstat_info->kind =3D=3D PGSTAT_KIND_INDEX) \ + (rel)->pgstat_info->idx.tuples_fetched++; \ + else \ + (rel)->pgstat_info->tab.counts.tuples_fetched++; \ + } \ } while (0) #define pgstat_count_index_scan(rel) \ do { \ if (pgstat_should_count_relation(rel)) \ - (rel)->pgstat_info->counts.numscans++; \ + (rel)->pgstat_info->idx.numscans++; \ } while (0) #define pgstat_count_index_tuples(rel, n) \ do { \ if (pgstat_should_count_relation(rel)) \ - (rel)->pgstat_info->counts.tuples_returned +=3D (n); \ + (rel)->pgstat_info->idx.tuples_returned +=3D (n); \ } while (0) #define pgstat_count_buffer_read(rel) \ do { \ if (pgstat_should_count_relation(rel)) \ - (rel)->pgstat_info->counts.blocks_fetched++; \ + { \ + if ((rel)->pgstat_info->kind =3D=3D PGSTAT_KIND_INDEX) \ + (rel)->pgstat_info->idx.blocks_fetched++; \ + else \ + (rel)->pgstat_info->tab.counts.blocks_fetched++; \ + } \ } while (0) #define pgstat_count_buffer_hit(rel) \ do { \ if (pgstat_should_count_relation(rel)) \ - (rel)->pgstat_info->counts.blocks_hit++; \ + { \ + if ((rel)->pgstat_info->kind =3D=3D PGSTAT_KIND_INDEX) \ + (rel)->pgstat_info->idx.blocks_hit++; \ + else \ + (rel)->pgstat_info->tab.counts.blocks_hit++; \ + } \ } while (0) =20 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n); @@ -792,8 +840,8 @@ extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(= Oid relid); extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid, bool *may_free); -extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id); -extern PgStat_TableStatus *find_tabstat_entry_kind(PgStat_Kind kind, Oid r= el_id); +extern PgStat_RelationStatus *find_relstat_entry_kind(PgStat_Kind kind, + Oid rel_id); =20 extern PgStat_StatIdxEntry *pgstat_fetch_stat_idxentry(Oid relid); extern PgStat_StatIdxEntry *pgstat_fetch_stat_idxentry_ext(bool shared, diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat= _internal.h index 94c94c103feb..4df3082f2b6e 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -215,13 +215,13 @@ typedef struct PgStat_SubXactStatus =20 /* * Tuple insertion/deletion counts for an open transaction can't be - * propagated into PgStat_TableStatus counters until we know if it is + * propagated into PgStat_RelationStatus counters until we know if it is * going to commit or abort. Hence, we keep these counts in per-subxact * structs that live in TopTransactionContext. This data structure is * designed on the assumption that subxacts won't usually modify very many * tables. */ - PgStat_TableXactStatus *first; /* head of list for this subxact */ + PgStat_RelXactStatus *first; /* head of list for this subxact */ } PgStat_SubXactStatus; =20 =20 diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 89c159b133fa..6d3423b2ad47 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -252,7 +252,7 @@ typedef struct RelationData =20 bool pgstat_enabled; /* should relation stats be counted */ /* use "struct" here to avoid needing to include pgstat.h: */ - struct PgStat_TableStatus *pgstat_info; /* statistics collection area */ + struct PgStat_RelationStatus *pgstat_info; /* statistics collection area = */ } RelationData; =20 =20 diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activi= ty/pgstat.c index 3aa10162f8ad..907d1f53a7ec 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -310,7 +310,7 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[= PGSTAT_KIND_BUILTIN_SIZE] .shared_size =3D sizeof(PgStatShared_Relation), .shared_data_off =3D offsetof(PgStatShared_Relation, stats), .shared_data_len =3D sizeof(((PgStatShared_Relation *) 0)->stats), - .pending_size =3D sizeof(PgStat_TableStatus), + .pending_size =3D sizeof(PgStat_RelationStatus), =20 .flush_pending_cb =3D pgstat_relation_flush_cb, .delete_pending_cb =3D pgstat_relation_delete_pending_cb, @@ -326,7 +326,7 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[= PGSTAT_KIND_BUILTIN_SIZE] .shared_size =3D sizeof(PgStatShared_Index), .shared_data_off =3D offsetof(PgStatShared_Index, stats), .shared_data_len =3D sizeof(((PgStatShared_Index *) 0)->stats), - .pending_size =3D sizeof(PgStat_TableStatus), + .pending_size =3D sizeof(PgStat_RelationStatus), =20 .flush_pending_cb =3D pgstat_index_flush_cb, .delete_pending_cb =3D pgstat_index_delete_pending_cb, diff --git a/src/backend/utils/activity/pgstat_index.c b/src/backend/utils/= activity/pgstat_index.c index e9fc27f2e4b4..5d1746614368 100644 --- a/src/backend/utils/activity/pgstat_index.c +++ b/src/backend/utils/activity/pgstat_index.c @@ -35,21 +35,21 @@ bool pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) { Oid dboid; - PgStat_TableStatus *lstats; /* pending stats entry */ + PgStat_RelationStatus *lstats; /* pending stats entry */ PgStatShared_Index *shidxstats; PgStat_StatIdxEntry *idxentry; /* index entry of shared stats */ PgStat_StatDBEntry *dbentry; /* pending database entry */ =20 dboid =3D entry_ref->shared_entry->key.dboid; - lstats =3D (PgStat_TableStatus *) entry_ref->pending; + lstats =3D (PgStat_RelationStatus *) entry_ref->pending; shidxstats =3D (PgStatShared_Index *) entry_ref->shared_stats; =20 /* * Ignore entries that didn't accumulate any actual counts, such as * indexes that were opened by the planner but not used. */ - if (pg_memory_is_all_zeros(&lstats->counts, - sizeof(struct PgStat_TableCounts))) + if (pg_memory_is_all_zeros(&lstats->idx, + sizeof(PgStat_IndexCounts))) return true; =20 if (!pgstat_lock_entry(entry_ref, nowait)) @@ -58,25 +58,25 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool = nowait) /* Add the values to the shared entry. */ idxentry =3D &shidxstats->stats; =20 - idxentry->numscans +=3D lstats->counts.numscans; - if (lstats->counts.numscans) + idxentry->numscans +=3D lstats->idx.numscans; + if (lstats->idx.numscans) { TimestampTz t =3D GetCurrentTransactionStopTimestamp(); =20 if (t > idxentry->lastscan) idxentry->lastscan =3D t; } - idxentry->tuples_returned +=3D lstats->counts.tuples_returned; - idxentry->tuples_fetched +=3D lstats->counts.tuples_fetched; - idxentry->blocks_fetched +=3D lstats->counts.blocks_fetched; - idxentry->blocks_hit +=3D lstats->counts.blocks_hit; + idxentry->tuples_returned +=3D lstats->idx.tuples_returned; + idxentry->tuples_fetched +=3D lstats->idx.tuples_fetched; + idxentry->blocks_fetched +=3D lstats->idx.blocks_fetched; + idxentry->blocks_hit +=3D lstats->idx.blocks_hit; =20 pgstat_unlock_entry(entry_ref); =20 /* The entry was successfully flushed, add the same to database stats */ dbentry =3D pgstat_prep_database_pending(dboid); - dbentry->blocks_fetched +=3D lstats->counts.blocks_fetched; - dbentry->blocks_hit +=3D lstats->counts.blocks_hit; + dbentry->blocks_fetched +=3D lstats->idx.blocks_fetched; + dbentry->blocks_hit +=3D lstats->idx.blocks_hit; =20 return true; } @@ -87,7 +87,7 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool no= wait) void pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref) { - PgStat_TableStatus *pending =3D (PgStat_TableStatus *) entry_ref->pending; + PgStat_RelationStatus *pending =3D (PgStat_RelationStatus *) entry_ref->p= ending; =20 if (pending->relation) pgstat_unlink_relation(pending->relation); diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/uti= ls/activity/pgstat_relation.c index 379a65aa28fb..0cfa5be49cde 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -42,12 +42,12 @@ typedef struct TwoPhasePgStatRecord } TwoPhasePgStatRecord; =20 =20 -static PgStat_TableStatus *pgstat_prep_relation_pending(PgStat_Kind kind, +static PgStat_RelationStatus *pgstat_prep_relation_pending(PgStat_Kind kin= d, Oid rel_id, bool isshared); -static void add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int ne= st_level); -static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info); -static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool is= _drop); -static void restore_truncdrop_counters(PgStat_TableXactStatus *trans); +static void add_tabstat_xact_level(PgStat_RelationStatus *pgstat_info, int= nest_level); +static void ensure_tabstat_xact_level(PgStat_RelationStatus *pgstat_info); +static void save_truncdrop_counters(PgStat_RelXactStatus *trans, bool is_d= rop); +static void restore_truncdrop_counters(PgStat_RelXactStatus *trans); =20 /* * Determine the stats kind for a relation based on its relkind. @@ -177,7 +177,7 @@ pgstat_assoc_relation(Relation rel) =20 kind =3D pgstat_get_relation_kind(rel->rd_rel->relkind); =20 - /* find or make the PgStat_TableStatus entry, and update link */ + /* find or make the PgStat_RelationStatus entry, and update link */ rel->pgstat_info =3D pgstat_prep_relation_pending(kind, RelationGetRelid(rel), rel->rd_rel->relisshared); @@ -226,7 +226,7 @@ void pgstat_drop_relation(Relation rel) { int nest_level =3D GetCurrentTransactionNestLevel(); - PgStat_TableStatus *pgstat_info; + PgStat_RelationStatus *pgstat_info; PgStat_Kind kind =3D pgstat_get_relation_kind(rel->rd_rel->relkind); =20 pgstat_drop_transactional(kind, @@ -239,15 +239,20 @@ pgstat_drop_relation(Relation rel) /* * Transactionally set counters to 0. That ensures that accesses to * pg_stat_xact_all_tables inside the transaction show 0. + * + * Indexes have no transactional counters, so leave. */ pgstat_info =3D rel->pgstat_info; - if (pgstat_info->trans && - pgstat_info->trans->nest_level =3D=3D nest_level) + if (pgstat_info->kind =3D=3D PGSTAT_KIND_INDEX) + return; + + if (pgstat_info->tab.trans && + pgstat_info->tab.trans->nest_level =3D=3D nest_level) { - save_truncdrop_counters(pgstat_info->trans, true); - pgstat_info->trans->tuples_inserted =3D 0; - pgstat_info->trans->tuples_updated =3D 0; - pgstat_info->trans->tuples_deleted =3D 0; + save_truncdrop_counters(pgstat_info->tab.trans, true); + pgstat_info->tab.trans->tuples_inserted =3D 0; + pgstat_info->tab.trans->tuples_updated =3D 0; + pgstat_info->tab.trans->tuples_deleted =3D 0; } } =20 @@ -355,15 +360,15 @@ pgstat_report_analyze(Relation rel, if (pgstat_should_count_relation(rel) && rel->rd_rel->relkind !=3D RELKIND_PARTITIONED_TABLE) { - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; =20 - for (trans =3D rel->pgstat_info->trans; trans; trans =3D trans->upper) + for (trans =3D rel->pgstat_info->tab.trans; trans; trans =3D trans->uppe= r) { livetuples -=3D trans->tuples_inserted - trans->tuples_deleted; deadtuples -=3D trans->tuples_updated + trans->tuples_deleted; } /* count stuff inserted by already-aborted subxacts, too */ - deadtuples -=3D rel->pgstat_info->counts.delta_dead_tuples; + deadtuples -=3D rel->pgstat_info->tab.counts.delta_dead_tuples; /* Since ANALYZE's counts are estimates, we could have underflowed */ livetuples =3D Max(livetuples, 0); deadtuples =3D Max(deadtuples, 0); @@ -422,10 +427,10 @@ pgstat_count_heap_insert(Relation rel, PgStat_Counter= n) { if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info =3D rel->pgstat_info; + PgStat_RelationStatus *pgstat_info =3D rel->pgstat_info; =20 ensure_tabstat_xact_level(pgstat_info); - pgstat_info->trans->tuples_inserted +=3D n; + pgstat_info->tab.trans->tuples_inserted +=3D n; } } =20 @@ -439,19 +444,19 @@ pgstat_count_heap_update(Relation rel, bool hot, bool= newpage) =20 if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info =3D rel->pgstat_info; + PgStat_RelationStatus *pgstat_info =3D rel->pgstat_info; =20 ensure_tabstat_xact_level(pgstat_info); - pgstat_info->trans->tuples_updated++; + pgstat_info->tab.trans->tuples_updated++; =20 /* * tuples_hot_updated and tuples_newpage_updated counters are * nontransactional, so just advance them */ if (hot) - pgstat_info->counts.tuples_hot_updated++; + pgstat_info->tab.counts.tuples_hot_updated++; else if (newpage) - pgstat_info->counts.tuples_newpage_updated++; + pgstat_info->tab.counts.tuples_newpage_updated++; } } =20 @@ -463,10 +468,10 @@ pgstat_count_heap_delete(Relation rel) { if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info =3D rel->pgstat_info; + PgStat_RelationStatus *pgstat_info =3D rel->pgstat_info; =20 ensure_tabstat_xact_level(pgstat_info); - pgstat_info->trans->tuples_deleted++; + pgstat_info->tab.trans->tuples_deleted++; } } =20 @@ -478,13 +483,13 @@ pgstat_count_truncate(Relation rel) { if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info =3D rel->pgstat_info; + PgStat_RelationStatus *pgstat_info =3D rel->pgstat_info; =20 ensure_tabstat_xact_level(pgstat_info); - save_truncdrop_counters(pgstat_info->trans, false); - pgstat_info->trans->tuples_inserted =3D 0; - pgstat_info->trans->tuples_updated =3D 0; - pgstat_info->trans->tuples_deleted =3D 0; + save_truncdrop_counters(pgstat_info->tab.trans, false); + pgstat_info->tab.trans->tuples_inserted =3D 0; + pgstat_info->tab.trans->tuples_updated =3D 0; + pgstat_info->tab.trans->tuples_deleted =3D 0; } } =20 @@ -501,9 +506,9 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta) { if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info =3D rel->pgstat_info; + PgStat_RelationStatus *pgstat_info =3D rel->pgstat_info; =20 - pgstat_info->counts.delta_dead_tuples -=3D delta; + pgstat_info->tab.counts.delta_dead_tuples -=3D delta; } } =20 @@ -534,9 +539,9 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid,= bool *may_free) } =20 /* - * find any existing PgStat_TableStatus entry for rel + * find any existing PgStat_RelationStatus entry for rel and kind * - * Find any existing PgStat_TableStatus entry for rel_id in the current + * Find any existing PgStat_RelationStatus entry for rel_id in the current * database. If not found, try finding from shared tables. * * If an entry is found, copy it and increment the copy's counters with th= eir @@ -545,22 +550,13 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloi= d, bool *may_free) * * If no entry found, return NULL, don't create a new one. */ -PgStat_TableStatus * -find_tabstat_entry(Oid rel_id) -{ - return find_tabstat_entry_kind(PGSTAT_KIND_RELATION, rel_id); -} - -/* - * Same as find_tabstat_entry but for a specific stats kind. - */ -PgStat_TableStatus * -find_tabstat_entry_kind(PgStat_Kind kind, Oid rel_id) +PgStat_RelationStatus * +find_relstat_entry_kind(PgStat_Kind kind, Oid rel_id) { PgStat_EntryRef *entry_ref; - PgStat_TableXactStatus *trans; - PgStat_TableStatus *tabentry =3D NULL; - PgStat_TableStatus *tablestatus =3D NULL; + PgStat_RelXactStatus *trans; + PgStat_RelationStatus *tabentry =3D NULL; + PgStat_RelationStatus *tablestatus =3D NULL; =20 entry_ref =3D pgstat_fetch_pending_entry(kind, MyDatabaseId, rel_id); if (!entry_ref) @@ -570,27 +566,33 @@ find_tabstat_entry_kind(PgStat_Kind kind, Oid rel_id) return tablestatus; } =20 - tabentry =3D (PgStat_TableStatus *) entry_ref->pending; - tablestatus =3D palloc_object(PgStat_TableStatus); + tabentry =3D (PgStat_RelationStatus *) entry_ref->pending; + tablestatus =3D palloc_object(PgStat_RelationStatus); *tablestatus =3D *tabentry; =20 /* - * Reset tablestatus->trans in the copy of PgStat_TableStatus as it may + * For index entries, just return the copy =E2=80=94 no transactional dat= a. + */ + if (kind =3D=3D PGSTAT_KIND_INDEX) + return tablestatus; + + /* + * Reset tablestatus->trans in the copy of PgStat_RelationStatus as it may * point to a shared memory area. Its data is saved below, so removing it * does not matter. */ - tablestatus->trans =3D NULL; + tablestatus->tab.trans =3D NULL; =20 /* * Live subtransaction counts are not included yet. This is not a hot * code path so reconcile tuples_inserted, tuples_updated and * tuples_deleted even if the caller may not be interested in this data. */ - for (trans =3D tabentry->trans; trans !=3D NULL; trans =3D trans->upper) + for (trans =3D tabentry->tab.trans; trans !=3D NULL; trans =3D trans->upp= er) { - tablestatus->counts.tuples_inserted +=3D trans->tuples_inserted; - tablestatus->counts.tuples_updated +=3D trans->tuples_updated; - tablestatus->counts.tuples_deleted +=3D trans->tuples_deleted; + tablestatus->tab.counts.tuples_inserted +=3D trans->tuples_inserted; + tablestatus->tab.counts.tuples_updated +=3D trans->tuples_updated; + tablestatus->tab.counts.tuples_deleted +=3D trans->tuples_deleted; } =20 return tablestatus; @@ -607,51 +609,51 @@ find_tabstat_entry_kind(PgStat_Kind kind, Oid rel_id) void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit) { - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; =20 for (trans =3D xact_state->first; trans !=3D NULL; trans =3D trans->next) { - PgStat_TableStatus *tabstat; + PgStat_RelationStatus *tabstat; =20 Assert(trans->nest_level =3D=3D 1); Assert(trans->upper =3D=3D NULL); tabstat =3D trans->parent; - Assert(tabstat->trans =3D=3D trans); + Assert(tabstat->tab.trans =3D=3D trans); /* restore pre-truncate/drop stats (if any) in case of aborted xact */ if (!isCommit) restore_truncdrop_counters(trans); /* count attempted actions regardless of commit/abort */ - tabstat->counts.tuples_inserted +=3D trans->tuples_inserted; - tabstat->counts.tuples_updated +=3D trans->tuples_updated; - tabstat->counts.tuples_deleted +=3D trans->tuples_deleted; + tabstat->tab.counts.tuples_inserted +=3D trans->tuples_inserted; + tabstat->tab.counts.tuples_updated +=3D trans->tuples_updated; + tabstat->tab.counts.tuples_deleted +=3D trans->tuples_deleted; if (isCommit) { - tabstat->counts.truncdropped =3D trans->truncdropped; + tabstat->tab.counts.truncdropped =3D trans->truncdropped; if (trans->truncdropped) { /* forget live/dead stats seen by backend thus far */ - tabstat->counts.delta_live_tuples =3D 0; - tabstat->counts.delta_dead_tuples =3D 0; + tabstat->tab.counts.delta_live_tuples =3D 0; + tabstat->tab.counts.delta_dead_tuples =3D 0; } /* insert adds a live tuple, delete removes one */ - tabstat->counts.delta_live_tuples +=3D + tabstat->tab.counts.delta_live_tuples +=3D trans->tuples_inserted - trans->tuples_deleted; /* update and delete each create a dead tuple */ - tabstat->counts.delta_dead_tuples +=3D + tabstat->tab.counts.delta_dead_tuples +=3D trans->tuples_updated + trans->tuples_deleted; /* insert, update, delete each count as one change event */ - tabstat->counts.changed_tuples +=3D + tabstat->tab.counts.changed_tuples +=3D trans->tuples_inserted + trans->tuples_updated + trans->tuples_deleted; } else { /* inserted tuples are dead, deleted tuples are unaffected */ - tabstat->counts.delta_dead_tuples +=3D + tabstat->tab.counts.delta_dead_tuples +=3D trans->tuples_inserted + trans->tuples_updated; /* an aborted xact generates no changed_tuple events */ } - tabstat->trans =3D NULL; + tabstat->tab.trans =3D NULL; } } =20 @@ -665,17 +667,17 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_= state, bool isCommit) void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isComm= it, int nestDepth) { - PgStat_TableXactStatus *trans; - PgStat_TableXactStatus *next_trans; + PgStat_RelXactStatus *trans; + PgStat_RelXactStatus *next_trans; =20 for (trans =3D xact_state->first; trans !=3D NULL; trans =3D next_trans) { - PgStat_TableStatus *tabstat; + PgStat_RelationStatus *tabstat; =20 next_trans =3D trans->next; Assert(trans->nest_level =3D=3D nestDepth); tabstat =3D trans->parent; - Assert(tabstat->trans =3D=3D trans); + Assert(tabstat->tab.trans =3D=3D trans); =20 if (isCommit) { @@ -696,7 +698,7 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact= _state, bool isCommit, in trans->upper->tuples_updated +=3D trans->tuples_updated; trans->upper->tuples_deleted +=3D trans->tuples_deleted; } - tabstat->trans =3D trans->upper; + tabstat->tab.trans =3D trans->upper; pfree(trans); } else @@ -727,13 +729,13 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xa= ct_state, bool isCommit, in /* first restore values obliterated by truncate/drop */ restore_truncdrop_counters(trans); /* count attempted actions regardless of commit/abort */ - tabstat->counts.tuples_inserted +=3D trans->tuples_inserted; - tabstat->counts.tuples_updated +=3D trans->tuples_updated; - tabstat->counts.tuples_deleted +=3D trans->tuples_deleted; + tabstat->tab.counts.tuples_inserted +=3D trans->tuples_inserted; + tabstat->tab.counts.tuples_updated +=3D trans->tuples_updated; + tabstat->tab.counts.tuples_deleted +=3D trans->tuples_deleted; /* inserted tuples are dead, deleted tuples are unaffected */ - tabstat->counts.delta_dead_tuples +=3D + tabstat->tab.counts.delta_dead_tuples +=3D trans->tuples_inserted + trans->tuples_updated; - tabstat->trans =3D trans->upper; + tabstat->tab.trans =3D trans->upper; pfree(trans); } } @@ -746,17 +748,17 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xa= ct_state, bool isCommit, in void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state) { - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; =20 for (trans =3D xact_state->first; trans !=3D NULL; trans =3D trans->next) { - PgStat_TableStatus *tabstat PG_USED_FOR_ASSERTS_ONLY; + PgStat_RelationStatus *tabstat PG_USED_FOR_ASSERTS_ONLY; TwoPhasePgStatRecord record; =20 Assert(trans->nest_level =3D=3D 1); Assert(trans->upper =3D=3D NULL); tabstat =3D trans->parent; - Assert(tabstat->trans =3D=3D trans); + Assert(tabstat->tab.trans =3D=3D trans); =20 record.tuples_inserted =3D trans->tuples_inserted; record.tuples_updated =3D trans->tuples_updated; @@ -764,8 +766,8 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_s= tate) record.inserted_pre_truncdrop =3D trans->inserted_pre_truncdrop; record.updated_pre_truncdrop =3D trans->updated_pre_truncdrop; record.deleted_pre_truncdrop =3D trans->deleted_pre_truncdrop; - record.id =3D tabstat->id; - record.shared =3D tabstat->shared; + record.id =3D tabstat->tab.id; + record.shared =3D tabstat->tab.shared; record.truncdropped =3D trans->truncdropped; =20 RegisterTwoPhaseRecord(TWOPHASE_RM_PGSTAT_ID, 0, @@ -784,14 +786,14 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact= _state) void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state) { - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; =20 for (trans =3D xact_state->first; trans !=3D NULL; trans =3D trans->next) { - PgStat_TableStatus *tabstat; + PgStat_RelationStatus *tabstat; =20 tabstat =3D trans->parent; - tabstat->trans =3D NULL; + tabstat->tab.trans =3D NULL; } } =20 @@ -805,27 +807,27 @@ pgstat_twophase_postcommit(FullTransactionId fxid, ui= nt16 info, void *recdata, uint32 len) { TwoPhasePgStatRecord *rec =3D (TwoPhasePgStatRecord *) recdata; - PgStat_TableStatus *pgstat_info; + PgStat_RelationStatus *pgstat_info; =20 /* Find or create a tabstat entry for the rel */ pgstat_info =3D pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->i= d, rec->shared); =20 /* Same math as in AtEOXact_PgStat, commit case */ - pgstat_info->counts.tuples_inserted +=3D rec->tuples_inserted; - pgstat_info->counts.tuples_updated +=3D rec->tuples_updated; - pgstat_info->counts.tuples_deleted +=3D rec->tuples_deleted; - pgstat_info->counts.truncdropped =3D rec->truncdropped; + pgstat_info->tab.counts.tuples_inserted +=3D rec->tuples_inserted; + pgstat_info->tab.counts.tuples_updated +=3D rec->tuples_updated; + pgstat_info->tab.counts.tuples_deleted +=3D rec->tuples_deleted; + pgstat_info->tab.counts.truncdropped =3D rec->truncdropped; if (rec->truncdropped) { /* forget live/dead stats seen by backend thus far */ - pgstat_info->counts.delta_live_tuples =3D 0; - pgstat_info->counts.delta_dead_tuples =3D 0; + pgstat_info->tab.counts.delta_live_tuples =3D 0; + pgstat_info->tab.counts.delta_dead_tuples =3D 0; } - pgstat_info->counts.delta_live_tuples +=3D + pgstat_info->tab.counts.delta_live_tuples +=3D rec->tuples_inserted - rec->tuples_deleted; - pgstat_info->counts.delta_dead_tuples +=3D + pgstat_info->tab.counts.delta_dead_tuples +=3D rec->tuples_updated + rec->tuples_deleted; - pgstat_info->counts.changed_tuples +=3D + pgstat_info->tab.counts.changed_tuples +=3D rec->tuples_inserted + rec->tuples_updated + rec->tuples_deleted; } @@ -841,7 +843,7 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint1= 6 info, void *recdata, uint32 len) { TwoPhasePgStatRecord *rec =3D (TwoPhasePgStatRecord *) recdata; - PgStat_TableStatus *pgstat_info; + PgStat_RelationStatus *pgstat_info; =20 /* Find or create a tabstat entry for the rel */ pgstat_info =3D pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->i= d, rec->shared); @@ -853,10 +855,10 @@ pgstat_twophase_postabort(FullTransactionId fxid, uin= t16 info, rec->tuples_updated =3D rec->updated_pre_truncdrop; rec->tuples_deleted =3D rec->deleted_pre_truncdrop; } - pgstat_info->counts.tuples_inserted +=3D rec->tuples_inserted; - pgstat_info->counts.tuples_updated +=3D rec->tuples_updated; - pgstat_info->counts.tuples_deleted +=3D rec->tuples_deleted; - pgstat_info->counts.delta_dead_tuples +=3D + pgstat_info->tab.counts.tuples_inserted +=3D rec->tuples_inserted; + pgstat_info->tab.counts.tuples_updated +=3D rec->tuples_updated; + pgstat_info->tab.counts.tuples_deleted +=3D rec->tuples_deleted; + pgstat_info->tab.counts.delta_dead_tuples +=3D rec->tuples_inserted + rec->tuples_updated; } =20 @@ -873,20 +875,20 @@ bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) { Oid dboid; - PgStat_TableStatus *lstats; /* pending stats entry */ + PgStat_RelationStatus *lstats; /* pending stats entry */ PgStatShared_Relation *shtabstats; PgStat_StatTabEntry *tabentry; /* table entry of shared stats */ PgStat_StatDBEntry *dbentry; /* pending database entry */ =20 dboid =3D entry_ref->shared_entry->key.dboid; - lstats =3D (PgStat_TableStatus *) entry_ref->pending; + lstats =3D (PgStat_RelationStatus *) entry_ref->pending; shtabstats =3D (PgStatShared_Relation *) entry_ref->shared_stats; =20 /* * Ignore entries that didn't accumulate any actual counts, such as * indexes that were opened by the planner but not used. */ - if (pg_memory_is_all_zeros(&lstats->counts, + if (pg_memory_is_all_zeros(&lstats->tab.counts, sizeof(struct PgStat_TableCounts))) return true; =20 @@ -896,35 +898,35 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, = bool nowait) /* add the values to the shared entry. */ tabentry =3D &shtabstats->stats; =20 - tabentry->numscans +=3D lstats->counts.numscans; - if (lstats->counts.numscans) + tabentry->numscans +=3D lstats->tab.counts.numscans; + if (lstats->tab.counts.numscans) { TimestampTz t =3D GetCurrentTransactionStopTimestamp(); =20 if (t > tabentry->lastscan) tabentry->lastscan =3D t; } - tabentry->tuples_returned +=3D lstats->counts.tuples_returned; - tabentry->tuples_fetched +=3D lstats->counts.tuples_fetched; - tabentry->tuples_inserted +=3D lstats->counts.tuples_inserted; - tabentry->tuples_updated +=3D lstats->counts.tuples_updated; - tabentry->tuples_deleted +=3D lstats->counts.tuples_deleted; - tabentry->tuples_hot_updated +=3D lstats->counts.tuples_hot_updated; - tabentry->tuples_newpage_updated +=3D lstats->counts.tuples_newpage_updat= ed; + tabentry->tuples_returned +=3D lstats->tab.counts.tuples_returned; + tabentry->tuples_fetched +=3D lstats->tab.counts.tuples_fetched; + tabentry->tuples_inserted +=3D lstats->tab.counts.tuples_inserted; + tabentry->tuples_updated +=3D lstats->tab.counts.tuples_updated; + tabentry->tuples_deleted +=3D lstats->tab.counts.tuples_deleted; + tabentry->tuples_hot_updated +=3D lstats->tab.counts.tuples_hot_updated; + tabentry->tuples_newpage_updated +=3D lstats->tab.counts.tuples_newpage_u= pdated; =20 /* * If table was truncated/dropped, first reset the live/dead counters. */ - if (lstats->counts.truncdropped) + if (lstats->tab.counts.truncdropped) { tabentry->live_tuples =3D 0; tabentry->dead_tuples =3D 0; tabentry->ins_since_vacuum =3D 0; } =20 - tabentry->live_tuples +=3D lstats->counts.delta_live_tuples; - tabentry->dead_tuples +=3D lstats->counts.delta_dead_tuples; - tabentry->mod_since_analyze +=3D lstats->counts.changed_tuples; + tabentry->live_tuples +=3D lstats->tab.counts.delta_live_tuples; + tabentry->dead_tuples +=3D lstats->tab.counts.delta_dead_tuples; + tabentry->mod_since_analyze +=3D lstats->tab.counts.changed_tuples; =20 /* * Using tuples_inserted to update ins_since_vacuum does mean that we'll @@ -933,10 +935,10 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, = bool nowait) * triggering for inserts more often than they maybe should, which is * probably not going to be common enough to be too concerned about here. */ - tabentry->ins_since_vacuum +=3D lstats->counts.tuples_inserted; + tabentry->ins_since_vacuum +=3D lstats->tab.counts.tuples_inserted; =20 - tabentry->blocks_fetched +=3D lstats->counts.blocks_fetched; - tabentry->blocks_hit +=3D lstats->counts.blocks_hit; + tabentry->blocks_fetched +=3D lstats->tab.counts.blocks_fetched; + tabentry->blocks_hit +=3D lstats->tab.counts.blocks_hit; =20 /* Clamp live_tuples in case of negative delta_live_tuples */ tabentry->live_tuples =3D Max(tabentry->live_tuples, 0); @@ -947,13 +949,13 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, = bool nowait) =20 /* The entry was successfully flushed, add the same to database stats */ dbentry =3D pgstat_prep_database_pending(dboid); - dbentry->tuples_returned +=3D lstats->counts.tuples_returned; - dbentry->tuples_fetched +=3D lstats->counts.tuples_fetched; - dbentry->tuples_inserted +=3D lstats->counts.tuples_inserted; - dbentry->tuples_updated +=3D lstats->counts.tuples_updated; - dbentry->tuples_deleted +=3D lstats->counts.tuples_deleted; - dbentry->blocks_fetched +=3D lstats->counts.blocks_fetched; - dbentry->blocks_hit +=3D lstats->counts.blocks_hit; + dbentry->tuples_returned +=3D lstats->tab.counts.tuples_returned; + dbentry->tuples_fetched +=3D lstats->tab.counts.tuples_fetched; + dbentry->tuples_inserted +=3D lstats->tab.counts.tuples_inserted; + dbentry->tuples_updated +=3D lstats->tab.counts.tuples_updated; + dbentry->tuples_deleted +=3D lstats->tab.counts.tuples_deleted; + dbentry->blocks_fetched +=3D lstats->tab.counts.blocks_fetched; + dbentry->blocks_hit +=3D lstats->tab.counts.blocks_hit; =20 return true; } @@ -961,7 +963,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bo= ol nowait) void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref) { - PgStat_TableStatus *pending =3D (PgStat_TableStatus *) entry_ref->pending; + PgStat_RelationStatus *pending =3D (PgStat_RelationStatus *) entry_ref->p= ending; =20 if (pending->relation) pgstat_unlink_relation(pending->relation); @@ -974,21 +976,25 @@ pgstat_relation_reset_timestamp_cb(PgStatShared_Commo= n *header, TimestampTz ts) } =20 /* - * Find or create a PgStat_TableStatus entry for rel. New entry is created= and + * Find or create a PgStat_RelationStatus entry for rel. New entry is crea= ted and * initialized if not exists. */ -static PgStat_TableStatus * +static PgStat_RelationStatus * pgstat_prep_relation_pending(PgStat_Kind kind, Oid rel_id, bool isshared) { PgStat_EntryRef *entry_ref; - PgStat_TableStatus *pending; + PgStat_RelationStatus *pending; =20 entry_ref =3D pgstat_prep_pending_entry(kind, isshared ? InvalidOid : MyDatabaseId, rel_id, NULL); pending =3D entry_ref->pending; - pending->id =3D rel_id; - pending->shared =3D isshared; + pending->kind =3D kind; + if (kind !=3D PGSTAT_KIND_INDEX) + { + pending->tab.id =3D rel_id; + pending->tab.shared =3D isshared; + } =20 return pending; } @@ -997,10 +1003,10 @@ pgstat_prep_relation_pending(PgStat_Kind kind, Oid r= el_id, bool isshared) * add a new (sub)transaction state record */ static void -add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level) +add_tabstat_xact_level(PgStat_RelationStatus *pgstat_info, int nest_level) { PgStat_SubXactStatus *xact_state; - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; =20 /* * If this is the first rel to be modified at the current nest level, we @@ -1009,27 +1015,27 @@ add_tabstat_xact_level(PgStat_TableStatus *pgstat_i= nfo, int nest_level) xact_state =3D pgstat_get_xact_stack_level(nest_level); =20 /* Now make a per-table stack entry */ - trans =3D (PgStat_TableXactStatus *) + trans =3D (PgStat_RelXactStatus *) MemoryContextAllocZero(TopTransactionContext, - sizeof(PgStat_TableXactStatus)); + sizeof(PgStat_RelXactStatus)); trans->nest_level =3D nest_level; - trans->upper =3D pgstat_info->trans; + trans->upper =3D pgstat_info->tab.trans; trans->parent =3D pgstat_info; trans->next =3D xact_state->first; xact_state->first =3D trans; - pgstat_info->trans =3D trans; + pgstat_info->tab.trans =3D trans; } =20 /* * Add a new (sub)transaction record if needed. */ static void -ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info) +ensure_tabstat_xact_level(PgStat_RelationStatus *pgstat_info) { int nest_level =3D GetCurrentTransactionNestLevel(); =20 - if (pgstat_info->trans =3D=3D NULL || - pgstat_info->trans->nest_level !=3D nest_level) + if (pgstat_info->tab.trans =3D=3D NULL || + pgstat_info->tab.trans->nest_level !=3D nest_level) add_tabstat_xact_level(pgstat_info, nest_level); } =20 @@ -1043,7 +1049,7 @@ ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_= info) * subxact level only. */ static void -save_truncdrop_counters(PgStat_TableXactStatus *trans, bool is_drop) +save_truncdrop_counters(PgStat_RelXactStatus *trans, bool is_drop) { if (!trans->truncdropped || is_drop) { @@ -1058,7 +1064,7 @@ save_truncdrop_counters(PgStat_TableXactStatus *trans= , bool is_drop) * restore counters when a truncate aborts */ static void -restore_truncdrop_counters(PgStat_TableXactStatus *trans) +restore_truncdrop_counters(PgStat_RelXactStatus *trans) { if (trans->truncdropped) { diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pg= statfuncs.c index 946025f39ed0..0d47d745c18f 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -1928,12 +1928,13 @@ CppConcat(pg_stat_get_xact_,stat)(PG_FUNCTION_ARGS)= \ { \ Oid relid =3D PG_GETARG_OID(0); \ int64 result; \ - PgStat_TableStatus *tabentry; \ + PgStat_RelationStatus *tabentry; \ \ - if ((tabentry =3D find_tabstat_entry(relid)) =3D=3D NULL) \ + if ((tabentry =3D find_relstat_entry_kind(PGSTAT_KIND_RELATION, \ + relid)) =3D=3D NULL) \ result =3D 0; \ else \ - result =3D (int64) (tabentry->counts.stat); \ + result =3D (int64) (tabentry->tab.counts.stat); \ \ PG_RETURN_INT64(result); \ } @@ -1977,13 +1978,13 @@ CppConcat(pg_stat_get_xact_idx_,stat)(PG_FUNCTION_A= RGS) \ { \ Oid relid =3D PG_GETARG_OID(0); \ int64 result; \ - PgStat_TableStatus *tabentry; \ + PgStat_RelationStatus *tabentry; \ \ - tabentry =3D find_tabstat_entry_kind(PGSTAT_KIND_INDEX, relid); \ + tabentry =3D find_relstat_entry_kind(PGSTAT_KIND_INDEX, relid); \ if (!tabentry) \ result =3D 0; \ else \ - result =3D (int64) (tabentry->counts.stat); \ + result =3D (int64) (tabentry->idx.stat); \ \ PG_RETURN_INT64(result); \ } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/r= elcache.c index fb4e042be8ad..ce965e0db116 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -2758,7 +2758,7 @@ RelationRebuildRelation(Relation relation) /* toast OID override must be preserved */ SWAPFIELD(Oid, rd_toastoid); /* pgstat_info / enabled must be preserved */ - SWAPFIELD(struct PgStat_TableStatus *, pgstat_info); + SWAPFIELD(struct PgStat_RelationStatus *, pgstat_info); SWAPFIELD(bool, pgstat_enabled); /* preserve old partition key if we have one */ if (keep_partkey) diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs= =2Elist index 56c1f997f88b..2a7049a92490 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2354,6 +2354,7 @@ PgStat_LockEntry PgStat_PendingDroppedStatsItem PgStat_PendingIO PgStat_PendingLock +PgStat_RelationStatus PgStat_SLRUStats PgStat_ShmemControl PgStat_Snapshot @@ -2368,7 +2369,6 @@ PgStat_StatTabEntry PgStat_StatsFileOp PgStat_SubXactStatus PgStat_TableCounts -PgStat_TableStatus PgStat_TableXactStatus PgStat_WalCounters PgStat_WalStats --=20 2.55.0 --uu1QJiDJI3oJPnCR Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename=v13-0003-Introduce-relfilenode-statistics.patch Content-Transfer-Encoding: quoted-printable =46rom 7da7413fad7c38c39b396789511f0e910a16608b Mon Sep 17 00:00:00 2001 =46rom: Michael Paquier Date: Fri, 10 Jul 2026 14:53:50 +0900 Subject: [PATCH v13 3/3] Introduce relfilenode statistics Statistics data previously associated to relations are not moved under a new stats kind, for relfilenodes. This enables the reconstruction of the statistics after a system crash, as WAL replay is only able to know about the relfilenodes it works on. Shared catalogs are handled using their relmapping. A new set of fetch functions are used in the views, providing an equivalent of what existed previously. When a relation rewrite happens, the aggregated counters are not persisted, restarted at 0 as we start afresh. --- src/include/catalog/pg_proc.dat | 75 ++++--- src/include/pgstat.h | 39 +++- src/include/utils/pgstat_internal.h | 14 ++ src/include/utils/pgstat_kind.h | 15 +- src/backend/access/heap/heapam_xlog.c | 13 ++ src/backend/catalog/system_views.sql | 18 +- src/backend/postmaster/autovacuum.c | 40 ++-- src/backend/utils/activity/Makefile | 1 + src/backend/utils/activity/meson.build | 1 + src/backend/utils/activity/pgstat.c | 15 ++ src/backend/utils/activity/pgstat_relation.c | 190 +++++++++++------ .../utils/activity/pgstat_relfilenode.c | 179 ++++++++++++++++ src/backend/utils/adt/pgstatfuncs.c | 74 ++++--- src/test/isolation/expected/stats.out | 198 +++++++++--------- src/test/isolation/specs/stats.spec | 10 +- src/test/regress/expected/rules.out | 18 +- src/test/regress/expected/stats.out | 169 +++++++-------- src/test/regress/expected/stats_rewrite.out | 20 +- src/test/regress/expected/stats_rewrite_1.out | 8 +- src/test/regress/sql/stats.sql | 42 ++-- .../postgres_fdw/expected/postgres_fdw.out | 20 +- contrib/postgres_fdw/sql/postgres_fdw.sql | 8 +- 22 files changed, 751 insertions(+), 416 deletions(-) create mode 100644 src/backend/utils/activity/pgstat_relfilenode.c diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.= dat index dc6c61e84e54..acb8b9a4d588 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5578,45 +5578,6 @@ proname =3D> 'pg_stat_get_tuples_fetched', provolatile =3D> 's', proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', prosrc =3D> 'pg_stat_get_tuples_fetched' }, -{ oid =3D> '1931', descr =3D> 'statistics: number of tuples inserted', - proname =3D> 'pg_stat_get_tuples_inserted', provolatile =3D> 's', - proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_tuples_inserted' }, -{ oid =3D> '1932', descr =3D> 'statistics: number of tuples updated', - proname =3D> 'pg_stat_get_tuples_updated', provolatile =3D> 's', - proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_tuples_updated' }, -{ oid =3D> '1933', descr =3D> 'statistics: number of tuples deleted', - proname =3D> 'pg_stat_get_tuples_deleted', provolatile =3D> 's', - proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_tuples_deleted' }, -{ oid =3D> '1972', descr =3D> 'statistics: number of tuples hot updated', - proname =3D> 'pg_stat_get_tuples_hot_updated', provolatile =3D> 's', - proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_tuples_hot_updated' }, -{ oid =3D> '6217', - descr =3D> 'statistics: number of tuples updated onto a new page', - proname =3D> 'pg_stat_get_tuples_newpage_updated', provolatile =3D> 's', - proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_tuples_newpage_updated' }, -{ oid =3D> '2878', descr =3D> 'statistics: number of live tuples', - proname =3D> 'pg_stat_get_live_tuples', provolatile =3D> 's', proparalle= l =3D> 'r', - prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_live_tuples' }, -{ oid =3D> '2879', descr =3D> 'statistics: number of dead tuples', - proname =3D> 'pg_stat_get_dead_tuples', provolatile =3D> 's', proparalle= l =3D> 'r', - prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_dead_tuples' }, -{ oid =3D> '3177', - descr =3D> 'statistics: number of tuples changed since last analyze', - proname =3D> 'pg_stat_get_mod_since_analyze', provolatile =3D> 's', - proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_mod_since_analyze' }, -{ oid =3D> '5053', - descr =3D> 'statistics: number of tuples inserted since last vacuum', - proname =3D> 'pg_stat_get_ins_since_vacuum', provolatile =3D> 's', - proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', - prosrc =3D> 'pg_stat_get_ins_since_vacuum' }, { oid =3D> '1934', descr =3D> 'statistics: number of blocks fetched', proname =3D> 'pg_stat_get_blocks_fetched', provolatile =3D> 's', proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', @@ -6294,6 +6255,42 @@ proname =3D> 'pg_stat_get_xact_idx_blocks_hit', provolatile =3D> 'v', proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', prosrc =3D> 'pg_stat_get_xact_idx_blocks_hit' }, +{ oid =3D> '9055', descr =3D> 'statistics: number of tuples inserted (by r= elfilenode)', + proname =3D> 'pg_stat_get_rfn_tuples_inserted', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_tuples_inserted' }, +{ oid =3D> '9056', descr =3D> 'statistics: number of tuples updated (by re= lfilenode)', + proname =3D> 'pg_stat_get_rfn_tuples_updated', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_tuples_updated' }, +{ oid =3D> '9057', descr =3D> 'statistics: number of tuples deleted (by re= lfilenode)', + proname =3D> 'pg_stat_get_rfn_tuples_deleted', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_tuples_deleted' }, +{ oid =3D> '9058', descr =3D> 'statistics: number of tuples HOT updated (b= y relfilenode)', + proname =3D> 'pg_stat_get_rfn_tuples_hot_updated', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_tuples_hot_updated' }, +{ oid =3D> '9059', descr =3D> 'statistics: number of tuples updated to new= page (by relfilenode)', + proname =3D> 'pg_stat_get_rfn_tuples_newpage_updated', provolatile =3D> = 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_tuples_newpage_updated' }, +{ oid =3D> '9060', descr =3D> 'statistics: number of live tuples (by relfi= lenode)', + proname =3D> 'pg_stat_get_rfn_live_tuples', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_live_tuples' }, +{ oid =3D> '9061', descr =3D> 'statistics: number of dead tuples (by relfi= lenode)', + proname =3D> 'pg_stat_get_rfn_dead_tuples', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_dead_tuples' }, +{ oid =3D> '9062', descr =3D> 'statistics: number of tuples changed since = last analyze (by relfilenode)', + proname =3D> 'pg_stat_get_rfn_mod_since_analyze', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_mod_since_analyze' }, +{ oid =3D> '9063', descr =3D> 'statistics: number of tuples inserted since= last vacuum (by relfilenode)', + proname =3D> 'pg_stat_get_rfn_ins_since_vacuum', provolatile =3D> 's', + proparallel =3D> 'r', prorettype =3D> 'int8', proargtypes =3D> 'oid', + prosrc =3D> 'pg_stat_get_rfn_ins_since_vacuum' }, { oid =3D> '3777', descr =3D> 'statistics: reset collected statistics for a single function= in the current database', proname =3D> 'pg_stat_reset_single_function_counters', provolatile =3D> = 'v', diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 892cdf24139b..f2094270a000 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -12,6 +12,7 @@ #define PGSTAT_H =20 #include "datatype/timestamp.h" +#include "common/relpath.h" #include "portability/instr_time.h" #include "postmaster/pgarch.h" /* for MAX_XFN_CHARS */ #include "replication/conflict.h" @@ -210,6 +211,7 @@ typedef struct PgStat_RelationStatus { Oid id; /* table's OID */ bool shared; /* is it a shared catalog? */ + RelFileNumber relfilenode; /* table's relfilenode */ struct PgStat_RelXactStatus *trans; /* lowest subxact's counts */ PgStat_TableCounts counts; /* event counts to be sent */ } tab; @@ -489,17 +491,6 @@ typedef struct PgStat_StatTabEntry PgStat_Counter tuples_returned; PgStat_Counter tuples_fetched; =20 - PgStat_Counter tuples_inserted; - PgStat_Counter tuples_updated; - PgStat_Counter tuples_deleted; - PgStat_Counter tuples_hot_updated; - PgStat_Counter tuples_newpage_updated; - - PgStat_Counter live_tuples; - PgStat_Counter dead_tuples; - PgStat_Counter mod_since_analyze; - PgStat_Counter ins_since_vacuum; - PgStat_Counter blocks_fetched; PgStat_Counter blocks_hit; =20 @@ -534,6 +525,22 @@ typedef struct PgStat_StatIdxEntry TimestampTz stat_reset_time; } PgStat_StatIdxEntry; =20 +typedef struct PgStat_StatRFNodeEntry +{ + PgStat_Counter tuples_inserted; + PgStat_Counter tuples_updated; + PgStat_Counter tuples_deleted; + PgStat_Counter tuples_hot_updated; + PgStat_Counter tuples_newpage_updated; + + PgStat_Counter live_tuples; + PgStat_Counter dead_tuples; + PgStat_Counter mod_since_analyze; + PgStat_Counter ins_since_vacuum; + + TimestampTz stat_reset_time; +} PgStat_StatRFNodeEntry; + /* ------ * PgStat_WalCounters WAL activity data gathered from WalUsage * @@ -848,6 +855,16 @@ extern PgStat_StatIdxEntry *pgstat_fetch_stat_idxentry= _ext(bool shared, Oid reloid, bool *may_free); =20 +extern PgStat_StatRFNodeEntry *pgstat_fetch_stat_rfnodeentry(Oid dboid, + RelFileNumber rfn); + +/* WAL replay helpers for relfilenode stats */ +extern void pgstat_wal_replay_insert(Oid dboid, RelFileNumber relfilenode, + int ntuples); +extern void pgstat_wal_replay_update(Oid dboid, RelFileNumber relfilenode, + bool hot, bool newpage); +extern void pgstat_wal_replay_delete(Oid dboid, RelFileNumber relfilenode); + =20 /* * Functions in pgstat_replslot.c diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat= _internal.h index 4df3082f2b6e..466b3e79e5bd 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -513,6 +513,12 @@ typedef struct PgStatShared_Index PgStat_StatIdxEntry stats; } PgStatShared_Index; =20 +typedef struct PgStatShared_RelFileNode +{ + PgStatShared_Common header; + PgStat_StatRFNodeEntry stats; +} PgStatShared_RelFileNode; + typedef struct PgStatShared_Function { PgStatShared_Common header; @@ -802,6 +808,14 @@ extern void pgstat_index_delete_pending_cb(PgStat_Entr= yRef *entry_ref); extern void pgstat_index_reset_timestamp_cb(PgStatShared_Common *header, T= imestampTz ts); =20 =20 +/* + * Functions in pgstat_relfilenode.c + */ + +extern bool pgstat_relfilenode_flush_cb(PgStat_EntryRef *entry_ref, bool n= owait); +extern void pgstat_relfilenode_reset_timestamp_cb(PgStatShared_Common *hea= der, TimestampTz ts); + + /* * Functions in pgstat_replslot.c */ diff --git a/src/include/utils/pgstat_kind.h b/src/include/utils/pgstat_kin= d.h index 45ca599d0dd6..84ccaa068489 100644 --- a/src/include/utils/pgstat_kind.h +++ b/src/include/utils/pgstat_kind.h @@ -31,15 +31,16 @@ #define PGSTAT_KIND_REPLSLOT 5 /* per-slot statistics */ #define PGSTAT_KIND_SUBSCRIPTION 6 /* per-subscription statistics */ #define PGSTAT_KIND_BACKEND 7 /* per-backend statistics */ +#define PGSTAT_KIND_RELFILENODE 8 /* per-relfilenode statistics */ =20 /* stats for fixed-numbered objects */ -#define PGSTAT_KIND_ARCHIVER 8 -#define PGSTAT_KIND_BGWRITER 9 -#define PGSTAT_KIND_CHECKPOINTER 10 -#define PGSTAT_KIND_IO 11 -#define PGSTAT_KIND_LOCK 12 -#define PGSTAT_KIND_SLRU 13 -#define PGSTAT_KIND_WAL 14 +#define PGSTAT_KIND_ARCHIVER 9 +#define PGSTAT_KIND_BGWRITER 10 +#define PGSTAT_KIND_CHECKPOINTER 11 +#define PGSTAT_KIND_IO 12 +#define PGSTAT_KIND_LOCK 13 +#define PGSTAT_KIND_SLRU 14 +#define PGSTAT_KIND_WAL 15 =20 #define PGSTAT_KIND_BUILTIN_MIN PGSTAT_KIND_DATABASE #define PGSTAT_KIND_BUILTIN_MAX PGSTAT_KIND_WAL diff --git a/src/backend/access/heap/heapam_xlog.c b/src/backend/access/hea= p/heapam_xlog.c index 9ed7024e8147..f1379c699900 100644 --- a/src/backend/access/heap/heapam_xlog.c +++ b/src/backend/access/heap/heapam_xlog.c @@ -19,6 +19,7 @@ #include "access/visibilitymap.h" #include "access/xlog.h" #include "access/xlogutils.h" +#include "pgstat.h" #include "storage/freespace.h" #include "storage/standby.h" =20 @@ -357,6 +358,9 @@ heap_xlog_delete(XLogReaderState *record) } if (BufferIsValid(buffer)) UnlockReleaseBuffer(buffer); + + pgstat_wal_replay_delete(target_locator.dbOid, + target_locator.relNumber); } =20 /* @@ -483,6 +487,9 @@ heap_xlog_insert(XLogReaderState *record) */ if (action =3D=3D BLK_NEEDS_REDO && freespace < BLCKSZ / 5) XLogRecordPageWithFreeSpace(target_locator, blkno, freespace); + + pgstat_wal_replay_insert(target_locator.dbOid, + target_locator.relNumber, 1); } =20 /* @@ -688,6 +695,9 @@ heap_xlog_multi_insert(XLogReaderState *record) */ if (action =3D=3D BLK_NEEDS_REDO && freespace < BLCKSZ / 5) XLogRecordPageWithFreeSpace(rlocator, blkno, freespace); + + pgstat_wal_replay_insert(rlocator.dbOid, rlocator.relNumber, + xlrec->ntuples); } =20 /* @@ -967,6 +977,9 @@ heap_xlog_update(XLogReaderState *record, bool hot_upda= te) */ if (newaction =3D=3D BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ = / 5) XLogRecordPageWithFreeSpace(rlocator, newblk, freespace); + + pgstat_wal_replay_update(rlocator.dbOid, rlocator.relNumber, + hot_update, (oldblk !=3D newblk && !hot_update)); } =20 /* diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/sys= tem_views.sql index ce991e4269e7..2577fe608f23 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -726,15 +726,15 @@ CREATE VIEW pg_stat_all_tables AS max(pg_stat_get_idx_lastscan(I.indexrelid)) AS last_idx_scan, sum(pg_stat_get_idx_tuples_fetched(I.indexrelid))::bigint + pg_stat_get_tuples_fetched(C.oid) AS idx_tup_fetch, - pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(C.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(C.oid) AS n_tup_del, - pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd, - pg_stat_get_tuples_newpage_updated(C.oid) AS n_tup_newpage_upd, - pg_stat_get_live_tuples(C.oid) AS n_live_tup, - pg_stat_get_dead_tuples(C.oid) AS n_dead_tup, - pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze, - pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(C.oid)) A= S n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(C.oid)) AS= n_tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(C.oid)) AS= n_tup_del, + pg_stat_get_rfn_tuples_hot_updated(pg_relation_filenode(C.oid)= ) AS n_tup_hot_upd, + pg_stat_get_rfn_tuples_newpage_updated(pg_relation_filenode(C.= oid)) AS n_tup_newpage_upd, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(C.oid)) AS n_= live_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(C.oid)) AS n_= dead_tup, + pg_stat_get_rfn_mod_since_analyze(pg_relation_filenode(C.oid))= AS n_mod_since_analyze, + pg_stat_get_rfn_ins_since_vacuum(pg_relation_filenode(C.oid)) = AS n_ins_since_vacuum, pg_stat_get_last_vacuum_time(C.oid) as last_vacuum, pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum, pg_stat_get_last_analyze_time(C.oid) as last_analyze, diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/a= utovacuum.c index 45abf48768af..23e4637968f8 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -109,6 +109,7 @@ #include "utils/memutils.h" #include "utils/ps_status.h" #include "utils/rel.h" +#include "utils/relmapper.h" #include "utils/snapmgr.h" #include "utils/syscache.h" #include "utils/timeout.h" @@ -3082,10 +3083,10 @@ relation_needs_vacanalyze(Oid relid, bool *wraparound, AutoVacuumScores *scores) { - PgStat_StatTabEntry *tabentry; + PgStat_StatRFNodeEntry *rfnentry; + RelFileNumber rfn; bool force_vacuum; bool av_enabled; - bool may_free =3D false; =20 /* constants from reloptions or GUC variables */ int vac_base_thresh, @@ -3247,20 +3248,27 @@ relation_needs_vacanalyze(Oid relid, *dovacuum =3D true; =20 /* - * If we found stats for the table, and autovacuum is currently enabled, - * make a threshold-based decision whether to vacuum and/or analyze. If - * autovacuum is currently disabled, we must be here for anti-wraparound - * vacuuming only, so don't vacuum (or analyze) anything that's not being - * forced. + * Fetch tuple counters from the relfilenode entry. For shared catalogs, + * use the relation map. */ - tabentry =3D pgstat_fetch_stat_tabentry_ext(classForm->relisshared, - relid, &may_free); - if (!tabentry) - return; + rfn =3D classForm->relfilenode; + if (!RelFileNumberIsValid(rfn)) + rfn =3D RelationMapOidToFilenumber(relid, classForm->relisshared); =20 - vactuples =3D tabentry->dead_tuples; - instuples =3D tabentry->ins_since_vacuum; - anltuples =3D tabentry->mod_since_analyze; + rfnentry =3D pgstat_fetch_stat_rfnodeentry( + classForm->relisshared ? InvalidOid : MyDatabaseId, rfn); + if (rfnentry) + { + vactuples =3D rfnentry->dead_tuples; + instuples =3D rfnentry->ins_since_vacuum; + anltuples =3D rfnentry->mod_since_analyze; + } + else + { + vactuples =3D 0; + instuples =3D 0; + anltuples =3D 0; + } =20 /* If the table hasn't yet been vacuumed, take reltuples as zero */ if (reltuples < 0) @@ -3334,10 +3342,6 @@ relation_needs_vacanalyze(Oid relid, vactuples, vacthresh, scores->vac, anltuples, anlthresh, scores->anl, scores->xid, scores->mxid); - - /* Avoid leaking pgstat entries until the end of autovacuum. */ - if (may_free) - pfree(tabentry); } =20 /* diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activi= ty/Makefile index 2e32d1485d64..7ee4c82e1b65 100644 --- a/src/backend/utils/activity/Makefile +++ b/src/backend/utils/activity/Makefile @@ -30,6 +30,7 @@ OBJS =3D \ pgstat_kind.o \ pgstat_lock.o \ pgstat_relation.o \ + pgstat_relfilenode.o \ pgstat_replslot.o \ pgstat_shmem.o \ pgstat_slru.o \ diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/act= ivity/meson.build index e6dcb2e26fc5..754e120dd612 100644 --- a/src/backend/utils/activity/meson.build +++ b/src/backend/utils/activity/meson.build @@ -15,6 +15,7 @@ backend_sources +=3D files( 'pgstat_kind.c', 'pgstat_lock.c', 'pgstat_relation.c', + 'pgstat_relfilenode.c', 'pgstat_replslot.c', 'pgstat_shmem.c', 'pgstat_slru.c', diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activi= ty/pgstat.c index 907d1f53a7ec..e545b1c7aaac 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -333,6 +333,21 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos= [PGSTAT_KIND_BUILTIN_SIZE] .reset_timestamp_cb =3D pgstat_index_reset_timestamp_cb, }, =20 + [PGSTAT_KIND_RELFILENODE] =3D { + .name =3D "relfilenode", + + .fixed_amount =3D false, + .write_to_file =3D true, + + .shared_size =3D sizeof(PgStatShared_RelFileNode), + .shared_data_off =3D offsetof(PgStatShared_RelFileNode, stats), + .shared_data_len =3D sizeof(((PgStatShared_RelFileNode *) 0)->stats), + .pending_size =3D sizeof(PgStat_RelationStatus), + + .flush_pending_cb =3D pgstat_relfilenode_flush_cb, + .reset_timestamp_cb =3D pgstat_relfilenode_reset_timestamp_cb, + }, + [PGSTAT_KIND_FUNCTION] =3D { .name =3D "function", =20 diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/uti= ls/activity/pgstat_relation.c index 0cfa5be49cde..7dbcc22fa78f 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -37,6 +37,7 @@ typedef struct TwoPhasePgStatRecord PgStat_Counter updated_pre_truncdrop; PgStat_Counter deleted_pre_truncdrop; Oid id; /* table's OID */ + RelFileNumber relfilenode; /* table's relfilenode at prepare time */ bool shared; /* is it a shared catalog? */ bool truncdropped; /* was the relation truncated/dropped? */ } TwoPhasePgStatRecord; @@ -182,6 +183,13 @@ pgstat_assoc_relation(Relation rel) RelationGetRelid(rel), rel->rd_rel->relisshared); =20 + /* Store relfilenode for PGSTAT_KIND_RELFILENODE flush */ + if (rel->rd_rel->relkind !=3D RELKIND_PARTITIONED_TABLE && + rel->rd_rel->relkind !=3D RELKIND_INDEX) + rel->pgstat_info->tab.relfilenode =3D rel->rd_locator.relNumber; + else + rel->pgstat_info->tab.relfilenode =3D InvalidRelFileNumber; + /* don't allow link a stats to multiple relcache entries */ Assert(rel->pgstat_info->relation =3D=3D NULL); =20 @@ -264,8 +272,11 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter live= tuples, PgStat_Counter deadtuples, TimestampTz starttime) { PgStat_EntryRef *entry_ref; + PgStat_EntryRef *rfn_ref; PgStatShared_Relation *shtabentry; + PgStatShared_RelFileNode *shrfnstats; PgStat_StatTabEntry *tabentry; + PgStat_StatRFNodeEntry *rfnentry; Oid dboid =3D (rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId); TimestampTz ts; PgStat_Counter elapsedtime; @@ -284,21 +295,6 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter live= tuples, shtabentry =3D (PgStatShared_Relation *) entry_ref->shared_stats; tabentry =3D &shtabentry->stats; =20 - tabentry->live_tuples =3D livetuples; - tabentry->dead_tuples =3D deadtuples; - - /* - * It is quite possible that a non-aggressive VACUUM ended up skipping - * various pages, however, we'll zero the insert counter here regardless. - * It's currently used only to track when we need to perform an "insert" - * autovacuum, which are mainly intended to freeze newly inserted tuples. - * Zeroing this may just mean we'll not try to vacuum the table again - * until enough tuples have been inserted to trigger another insert - * autovacuum. An anti-wraparound autovacuum will catch any persistent - * stragglers. - */ - tabentry->ins_since_vacuum =3D 0; - if (AmAutoVacuumWorkerProcess()) { tabentry->last_autovacuum_time =3D ts; @@ -314,6 +310,34 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter live= tuples, =20 pgstat_unlock_entry(entry_ref); =20 + /* Update the relfilenode entry */ + rfn_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELFILENODE, + dboid, + (uint64) rel->rd_locator.relNumber, + false); + if (rfn_ref) + { + shrfnstats =3D (PgStatShared_RelFileNode *) rfn_ref->shared_stats; + rfnentry =3D &shrfnstats->stats; + + rfnentry->live_tuples =3D livetuples; + rfnentry->dead_tuples =3D deadtuples; + + /* + * It is quite possible that a non-aggressive VACUUM ended up + * skipping various pages, however, we'll zero the insert counter + * here regardless. It's currently used only to track when we need + * to perform an "insert" autovacuum, which are mainly intended to + * freeze newly inserted tuples. Zeroing this may just mean we'll + * not try to vacuum the table again until enough tuples have been + * inserted to trigger another insert autovacuum. An + * anti-wraparound autovacuum will catch any persistent stragglers. + */ + rfnentry->ins_since_vacuum =3D 0; + + pgstat_unlock_entry(rfn_ref); + } + /* * Flush IO statistics now. pgstat_report_stat() will flush IO stats, * however this will not be called until after an entire autovacuum cycle @@ -336,8 +360,11 @@ pgstat_report_analyze(Relation rel, bool resetcounter, TimestampTz starttime) { PgStat_EntryRef *entry_ref; + PgStat_EntryRef *rfn_ref; PgStatShared_Relation *shtabentry; + PgStatShared_RelFileNode *shrfnstats; PgStat_StatTabEntry *tabentry; + PgStat_StatRFNodeEntry *rfnentry; Oid dboid =3D (rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId); TimestampTz ts; PgStat_Counter elapsedtime; @@ -388,17 +415,6 @@ pgstat_report_analyze(Relation rel, shtabentry =3D (PgStatShared_Relation *) entry_ref->shared_stats; tabentry =3D &shtabentry->stats; =20 - tabentry->live_tuples =3D livetuples; - tabentry->dead_tuples =3D deadtuples; - - /* - * If commanded, reset mod_since_analyze to zero. This forgets any - * changes that were committed while the ANALYZE was in progress, but we - * have no good way to estimate how many of those there were. - */ - if (resetcounter) - tabentry->mod_since_analyze =3D 0; - if (AmAutoVacuumWorkerProcess()) { tabentry->last_autoanalyze_time =3D ts; @@ -414,6 +430,30 @@ pgstat_report_analyze(Relation rel, =20 pgstat_unlock_entry(entry_ref); =20 + /* Update the relfilenode entry */ + rfn_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELFILENODE, + dboid, + (uint64) rel->rd_locator.relNumber, + false); + if (rfn_ref) + { + shrfnstats =3D (PgStatShared_RelFileNode *) rfn_ref->shared_stats; + rfnentry =3D &shrfnstats->stats; + + rfnentry->live_tuples =3D livetuples; + rfnentry->dead_tuples =3D deadtuples; + + /* + * If commanded, reset mod_since_analyze to zero. This forgets any + * changes that were committed while the ANALYZE was in progress, but + * we have no good way to estimate how many of those there were. + */ + if (resetcounter) + rfnentry->mod_since_analyze =3D 0; + + pgstat_unlock_entry(rfn_ref); + } + /* see pgstat_report_vacuum() */ pgstat_flush_io(false); (void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO); @@ -767,6 +807,7 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_s= tate) record.updated_pre_truncdrop =3D trans->updated_pre_truncdrop; record.deleted_pre_truncdrop =3D trans->deleted_pre_truncdrop; record.id =3D tabstat->tab.id; + record.relfilenode =3D tabstat->tab.relfilenode; record.shared =3D tabstat->tab.shared; record.truncdropped =3D trans->truncdropped; =20 @@ -811,6 +852,7 @@ pgstat_twophase_postcommit(FullTransactionId fxid, uint= 16 info, =20 /* Find or create a tabstat entry for the rel */ pgstat_info =3D pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->i= d, rec->shared); + pgstat_info->tab.relfilenode =3D rec->relfilenode; =20 /* Same math as in AtEOXact_PgStat, commit case */ pgstat_info->tab.counts.tuples_inserted +=3D rec->tuples_inserted; @@ -847,6 +889,7 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint1= 6 info, =20 /* Find or create a tabstat entry for the rel */ pgstat_info =3D pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->i= d, rec->shared); + pgstat_info->tab.relfilenode =3D rec->relfilenode; =20 /* Same math as in AtEOXact_PgStat, abort case */ if (rec->truncdropped) @@ -875,10 +918,14 @@ bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) { Oid dboid; + RelFileNumber rfn; PgStat_RelationStatus *lstats; /* pending stats entry */ PgStatShared_Relation *shtabstats; PgStat_StatTabEntry *tabentry; /* table entry of shared stats */ PgStat_StatDBEntry *dbentry; /* pending database entry */ + PgStat_EntryRef *rfn_ref; + PgStatShared_RelFileNode *shrfnstats; + PgStat_StatRFNodeEntry *rfnentry; =20 dboid =3D entry_ref->shared_entry->key.dboid; lstats =3D (PgStat_RelationStatus *) entry_ref->pending; @@ -908,45 +955,70 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, = bool nowait) } tabentry->tuples_returned +=3D lstats->tab.counts.tuples_returned; tabentry->tuples_fetched +=3D lstats->tab.counts.tuples_fetched; - tabentry->tuples_inserted +=3D lstats->tab.counts.tuples_inserted; - tabentry->tuples_updated +=3D lstats->tab.counts.tuples_updated; - tabentry->tuples_deleted +=3D lstats->tab.counts.tuples_deleted; - tabentry->tuples_hot_updated +=3D lstats->tab.counts.tuples_hot_updated; - tabentry->tuples_newpage_updated +=3D lstats->tab.counts.tuples_newpage_u= pdated; - - /* - * If table was truncated/dropped, first reset the live/dead counters. - */ - if (lstats->tab.counts.truncdropped) - { - tabentry->live_tuples =3D 0; - tabentry->dead_tuples =3D 0; - tabentry->ins_since_vacuum =3D 0; - } - - tabentry->live_tuples +=3D lstats->tab.counts.delta_live_tuples; - tabentry->dead_tuples +=3D lstats->tab.counts.delta_dead_tuples; - tabentry->mod_since_analyze +=3D lstats->tab.counts.changed_tuples; - - /* - * Using tuples_inserted to update ins_since_vacuum does mean that we'll - * track aborted inserts too. This isn't ideal, but otherwise probably - * not worth adding an extra field for. It may just amount to autovacuums - * triggering for inserts more often than they maybe should, which is - * probably not going to be common enough to be too concerned about here. - */ - tabentry->ins_since_vacuum +=3D lstats->tab.counts.tuples_inserted; =20 tabentry->blocks_fetched +=3D lstats->tab.counts.blocks_fetched; tabentry->blocks_hit +=3D lstats->tab.counts.blocks_hit; =20 - /* Clamp live_tuples in case of negative delta_live_tuples */ - tabentry->live_tuples =3D Max(tabentry->live_tuples, 0); - /* Likewise for dead_tuples */ - tabentry->dead_tuples =3D Max(tabentry->dead_tuples, 0); - pgstat_unlock_entry(entry_ref); =20 + /* + * Flush tuple counters to the relfilenode entry. + * + * Use the current relfilenode from the relation (if available) since it + * may have changed due to TRUNCATE. Fall back to the cached value. + */ + rfn =3D InvalidRelFileNumber; + + if (lstats->relation) + rfn =3D lstats->relation->rd_locator.relNumber; + else if (lstats->tab.relfilenode !=3D InvalidRelFileNumber) + rfn =3D lstats->tab.relfilenode; + + if (rfn !=3D InvalidRelFileNumber) + { + rfn_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELFILENODE, + dboid, + (uint64) rfn, + nowait); + if (rfn_ref) + { + shrfnstats =3D (PgStatShared_RelFileNode *) rfn_ref->shared_stats; + rfnentry =3D &shrfnstats->stats; + + rfnentry->tuples_inserted +=3D lstats->tab.counts.tuples_inserted; + rfnentry->tuples_updated +=3D lstats->tab.counts.tuples_updated; + rfnentry->tuples_deleted +=3D lstats->tab.counts.tuples_deleted; + rfnentry->tuples_hot_updated +=3D lstats->tab.counts.tuples_hot_updated; + rfnentry->tuples_newpage_updated +=3D lstats->tab.counts.tuples_newpage= _updated; + + if (lstats->tab.counts.truncdropped) + { + rfnentry->live_tuples =3D 0; + rfnentry->dead_tuples =3D 0; + rfnentry->ins_since_vacuum =3D 0; + } + + rfnentry->live_tuples +=3D lstats->tab.counts.delta_live_tuples; + rfnentry->dead_tuples +=3D lstats->tab.counts.delta_dead_tuples; + rfnentry->mod_since_analyze +=3D lstats->tab.counts.changed_tuples; + + /* + * Using tuples_inserted to update ins_since_vacuum means we + * track aborted inserts too. This isn't ideal, but not worth + * adding an extra field for =E2=80=94 it may just trigger autovacuums + * slightly more often than necessary. + */ + rfnentry->ins_since_vacuum +=3D lstats->tab.counts.tuples_inserted; + + /* Clamp live_tuples in case of negative delta_live_tuples */ + rfnentry->live_tuples =3D Max(rfnentry->live_tuples, 0); + /* Likewise for dead_tuples */ + rfnentry->dead_tuples =3D Max(rfnentry->dead_tuples, 0); + + pgstat_unlock_entry(rfn_ref); + } + } + /* The entry was successfully flushed, add the same to database stats */ dbentry =3D pgstat_prep_database_pending(dboid); dbentry->tuples_returned +=3D lstats->tab.counts.tuples_returned; diff --git a/src/backend/utils/activity/pgstat_relfilenode.c b/src/backend/= utils/activity/pgstat_relfilenode.c new file mode 100644 index 000000000000..82613fb1a4ee --- /dev/null +++ b/src/backend/utils/activity/pgstat_relfilenode.c @@ -0,0 +1,179 @@ +/* -----------------------------------------------------------------------= -- + * + * pgstat_relfilenode.c + * Implementation of per-relfilenode statistics. + * + * This file contains the implementation of PGSTAT_KIND_RELFILENODE statis= tics, + * for tuple counters (inserts, updates, deletes, live/dead tuples, etc.) = keyed + * by relfilenode. These counters are rebuilt during crash recovery, from= WAL + * replayed. + * + * Copyright (c) 2001-2026, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/activity/pgstat_relfilenode.c + * -----------------------------------------------------------------------= -- + */ + +#include "postgres.h" + +#include "utils/memutils.h" +#include "utils/pgstat_internal.h" + + +/* + * Flush out pending stats for a relfilenode entry. + * + * If nowait is true and the lock could not be immediately acquired, retur= ns + * false without flushing the entry. Otherwise returns true. + */ +bool +pgstat_relfilenode_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) +{ + PgStat_RelationStatus *lstats; /* pending stats entry */ + PgStatShared_RelFileNode *shrfnstats; + PgStat_StatRFNodeEntry *rfnentry; + + lstats =3D (PgStat_RelationStatus *) entry_ref->pending; + shrfnstats =3D (PgStatShared_RelFileNode *) entry_ref->shared_stats; + + /* + * Ignore entries that didn't accumulate any actual counts, such as + * tables that were opened but not modified. + */ + if (pg_memory_is_all_zeros(&lstats->tab.counts, + sizeof(struct PgStat_TableCounts))) + return true; + + if (!pgstat_lock_entry(entry_ref, nowait)) + return false; + + /* Add the values to the shared entry. */ + rfnentry =3D &shrfnstats->stats; + + /* + * If table was truncated/dropped, first reset the live/dead counters. + */ + if (lstats->tab.counts.truncdropped) + { + rfnentry->live_tuples =3D 0; + rfnentry->dead_tuples =3D 0; + rfnentry->ins_since_vacuum =3D 0; + } + + rfnentry->live_tuples +=3D lstats->tab.counts.delta_live_tuples; + rfnentry->dead_tuples +=3D lstats->tab.counts.delta_dead_tuples; + rfnentry->mod_since_analyze +=3D lstats->tab.counts.changed_tuples; + + /* + * Using tuples_inserted to update ins_since_vacuum means we track + * aborted inserts too. This isn't ideal, but not worth adding an extra + * field for =E2=80=94 it may just trigger autovacuums slightly more ofte= n than + * necessary. + */ + rfnentry->ins_since_vacuum +=3D lstats->tab.counts.tuples_inserted; + + /* Clamp live_tuples in case of negative delta_live_tuples */ + rfnentry->live_tuples =3D Max(rfnentry->live_tuples, 0); + /* Likewise for dead_tuples */ + rfnentry->dead_tuples =3D Max(rfnentry->dead_tuples, 0); + + pgstat_unlock_entry(entry_ref); + + return true; +} + +/* + * Callback to reset the timestamp on a relfilenode stats entry. + */ +void +pgstat_relfilenode_reset_timestamp_cb(PgStatShared_Common *header, + TimestampTz ts) +{ + ((PgStatShared_RelFileNode *) header)->stats.stat_reset_time =3D ts; +} + +/* + * Fetch the relfilenode stats entry for the given database and relfilenod= e. + */ +PgStat_StatRFNodeEntry * +pgstat_fetch_stat_rfnodeentry(Oid dboid, RelFileNumber rfn) +{ + return (PgStat_StatRFNodeEntry *) + pgstat_fetch_entry(PGSTAT_KIND_RELFILENODE, dboid, (uint64) rfn, NULL); +} + +/* + * WAL replay: increment tuple counters in the relfilenode entry. + * + * This is called during WAL replay to rebuild tuple counters from + * heap WAL records. It directly updates the shared stats entry without + * going through the pending/flush machinery. + */ +void +pgstat_wal_replay_insert(Oid dboid, RelFileNumber relfilenode, int ntuples) +{ + PgStat_EntryRef *entry_ref; + PgStatShared_RelFileNode *shrfnstats; + PgStat_StatRFNodeEntry *rfnentry; + + entry_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELFILENODE, + dboid, (uint64) relfilenode, + false); + shrfnstats =3D (PgStatShared_RelFileNode *) entry_ref->shared_stats; + rfnentry =3D &shrfnstats->stats; + + rfnentry->tuples_inserted +=3D ntuples; + rfnentry->mod_since_analyze +=3D ntuples; + rfnentry->ins_since_vacuum +=3D ntuples; + rfnentry->live_tuples +=3D ntuples; + + pgstat_unlock_entry(entry_ref); +} + +void +pgstat_wal_replay_update(Oid dboid, RelFileNumber relfilenode, + bool hot, bool newpage) +{ + PgStat_EntryRef *entry_ref; + PgStatShared_RelFileNode *shrfnstats; + PgStat_StatRFNodeEntry *rfnentry; + + entry_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELFILENODE, + dboid, (uint64) relfilenode, + false); + shrfnstats =3D (PgStatShared_RelFileNode *) entry_ref->shared_stats; + rfnentry =3D &shrfnstats->stats; + + rfnentry->tuples_updated++; + rfnentry->mod_since_analyze++; + rfnentry->dead_tuples++; + if (hot) + rfnentry->tuples_hot_updated++; + if (newpage) + rfnentry->tuples_newpage_updated++; + + pgstat_unlock_entry(entry_ref); +} + +void +pgstat_wal_replay_delete(Oid dboid, RelFileNumber relfilenode) +{ + PgStat_EntryRef *entry_ref; + PgStatShared_RelFileNode *shrfnstats; + PgStat_StatRFNodeEntry *rfnentry; + + entry_ref =3D pgstat_get_entry_ref_locked(PGSTAT_KIND_RELFILENODE, + dboid, (uint64) relfilenode, + false); + shrfnstats =3D (PgStatShared_RelFileNode *) entry_ref->shared_stats; + rfnentry =3D &shrfnstats->stats; + + rfnentry->tuples_deleted++; + rfnentry->mod_since_analyze++; + rfnentry->dead_tuples++; + rfnentry->live_tuples--; + rfnentry->live_tuples =3D Max(rfnentry->live_tuples, 0); + + pgstat_unlock_entry(entry_ref); +} diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pg= statfuncs.c index 0d47d745c18f..8be2cb4d1293 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -69,45 +69,65 @@ PG_STAT_GET_RELENTRY_INT64(blocks_fetched) /* pg_stat_get_blocks_hit */ PG_STAT_GET_RELENTRY_INT64(blocks_hit) =20 -/* pg_stat_get_dead_tuples */ -PG_STAT_GET_RELENTRY_INT64(dead_tuples) - -/* pg_stat_get_ins_since_vacuum */ -PG_STAT_GET_RELENTRY_INT64(ins_since_vacuum) - -/* pg_stat_get_live_tuples */ -PG_STAT_GET_RELENTRY_INT64(live_tuples) - -/* pg_stat_get_mod_since_analyze */ -PG_STAT_GET_RELENTRY_INT64(mod_since_analyze) - /* pg_stat_get_numscans */ PG_STAT_GET_RELENTRY_INT64(numscans) =20 -/* pg_stat_get_tuples_deleted */ -PG_STAT_GET_RELENTRY_INT64(tuples_deleted) - /* pg_stat_get_tuples_fetched */ PG_STAT_GET_RELENTRY_INT64(tuples_fetched) =20 -/* pg_stat_get_tuples_hot_updated */ -PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated) - -/* pg_stat_get_tuples_newpage_updated */ -PG_STAT_GET_RELENTRY_INT64(tuples_newpage_updated) - -/* pg_stat_get_tuples_inserted */ -PG_STAT_GET_RELENTRY_INT64(tuples_inserted) - /* pg_stat_get_tuples_returned */ PG_STAT_GET_RELENTRY_INT64(tuples_returned) =20 -/* pg_stat_get_tuples_updated */ -PG_STAT_GET_RELENTRY_INT64(tuples_updated) - /* pg_stat_get_vacuum_count */ PG_STAT_GET_RELENTRY_INT64(vacuum_count) =20 +/* + * Accessor macro for relfilenode stats entries (PgStat_StatRFNodeEntry). + * Takes a relfilenode number as input (from pg_class.relfilenode). + */ +#define PG_STAT_GET_RFNENTRY_INT64(stat) \ +Datum \ +CppConcat(pg_stat_get_rfn_,stat)(PG_FUNCTION_ARGS) \ +{ \ + Oid rfn =3D PG_GETARG_OID(0); \ + int64 result; \ + PgStat_StatRFNodeEntry *rfnentry; \ + \ + if ((rfnentry =3D pgstat_fetch_stat_rfnodeentry(MyDatabaseId, rfn)) =3D= =3D NULL) \ + result =3D 0; \ + else \ + result =3D (int64) (rfnentry->stat); \ + \ + PG_RETURN_INT64(result); \ +} + +/* pg_stat_get_rfn_tuples_inserted */ +PG_STAT_GET_RFNENTRY_INT64(tuples_inserted) + +/* pg_stat_get_rfn_tuples_updated */ +PG_STAT_GET_RFNENTRY_INT64(tuples_updated) + +/* pg_stat_get_rfn_tuples_deleted */ +PG_STAT_GET_RFNENTRY_INT64(tuples_deleted) + +/* pg_stat_get_rfn_tuples_hot_updated */ +PG_STAT_GET_RFNENTRY_INT64(tuples_hot_updated) + +/* pg_stat_get_rfn_tuples_newpage_updated */ +PG_STAT_GET_RFNENTRY_INT64(tuples_newpage_updated) + +/* pg_stat_get_rfn_live_tuples */ +PG_STAT_GET_RFNENTRY_INT64(live_tuples) + +/* pg_stat_get_rfn_dead_tuples */ +PG_STAT_GET_RFNENTRY_INT64(dead_tuples) + +/* pg_stat_get_rfn_mod_since_analyze */ +PG_STAT_GET_RFNENTRY_INT64(mod_since_analyze) + +/* pg_stat_get_rfn_ins_since_vacuum */ +PG_STAT_GET_RFNENTRY_INT64(ins_since_vacuum) + /* * Accessor macro for index stats entries (PgStat_StatIdxEntry). */ diff --git a/src/test/isolation/expected/stats.out b/src/test/isolation/exp= ected/stats.out index cfad309ccf34..1ede5af1cf81 100644 --- a/src/test/isolation/expected/stats.out +++ b/src/test/isolation/expected/stats.out @@ -2186,18 +2186,18 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' =20 seq_scan|seq_tup_read|n_tup_ins|n_tup_upd|n_tup_del|n_live_tup|n_dead_tup|= vacuum_count --------+------------+---------+---------+---------+----------+----------+= ------------ - 0| 0| 0| 0| 0| 0| 0|= 0 + 0| 0| | | | | |= 0 (1 row) =20 =20 @@ -2230,18 +2230,18 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' =20 seq_scan|seq_tup_read|n_tup_ins|n_tup_upd|n_tup_del|n_live_tup|n_dead_tup|= vacuum_count --------+------------+---------+---------+---------+----------+----------+= ------------ - 0| 0| 0| 0| 0| 0| 0|= 0 + 0| 0| | | | | |= 0 (1 row) =20 =20 @@ -2256,11 +2256,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2295,11 +2295,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2334,11 +2334,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2389,11 +2389,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2421,11 +2421,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2483,11 +2483,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2515,11 +2515,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2578,11 +2578,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2647,11 +2647,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2708,11 +2708,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2775,11 +2775,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2815,18 +2815,18 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' =20 seq_scan|seq_tup_read|n_tup_ins|n_tup_upd|n_tup_del|n_live_tup|n_dead_tup|= vacuum_count --------+------------+---------+---------+---------+----------+----------+= ------------ - 3| 9| 5| 1| 0| 1| 1|= 0 + 3| 9| 0| 0| 0| 0| 0|= 0 (1 row) =20 =20 @@ -2861,18 +2861,18 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' =20 seq_scan|seq_tup_read|n_tup_ins|n_tup_upd|n_tup_del|n_live_tup|n_dead_tup|= vacuum_count --------+------------+---------+---------+---------+----------+----------+= ------------ - 3| 9| 5| 1| 0| 1| 1|= 0 + 3| 9| 0| 0| 0| 0| 0|= 0 (1 row) =20 =20 @@ -2901,11 +2901,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2947,11 +2947,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -2988,11 +2988,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' @@ -3035,11 +3035,11 @@ step s1_table_stats: SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs= /stats.spec index da16710da0fb..6eadc90463f2 100644 --- a/src/test/isolation/specs/stats.spec +++ b/src/test/isolation/specs/stats.spec @@ -102,11 +102,11 @@ step s1_table_stats { SELECT pg_stat_get_numscans(tso.oid) AS seq_scan, pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read, - pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del, - pg_stat_get_live_tuples(tso.oid) AS n_live_tup, - pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode(tso.oid)) AS = n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode(tso.oid)) AS n= _tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode(tso.oid)) AS n= _tup_del, + pg_stat_get_rfn_live_tuples(pg_relation_filenode(tso.oid)) AS n_li= ve_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode(tso.oid)) AS n_de= ad_tup, pg_stat_get_vacuum_count(tso.oid) AS vacuum_count FROM test_stat_oid AS tso WHERE tso.name =3D 'test_stat_tab' diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expecte= d/rules.out index ffec0903f038..3e3ef4f189b2 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1825,15 +1825,15 @@ pg_stat_all_tables| SELECT c.oid AS relid, (sum(pg_stat_get_idx_numscans(i.indexrelid)))::bigint AS idx_scan, max(pg_stat_get_idx_lastscan(i.indexrelid)) AS last_idx_scan, ((sum(pg_stat_get_idx_tuples_fetched(i.indexrelid)))::bigint + pg_stat= _get_tuples_fetched(c.oid)) AS idx_tup_fetch, - pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, - pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, - pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, - pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd, - pg_stat_get_tuples_newpage_updated(c.oid) AS n_tup_newpage_upd, - pg_stat_get_live_tuples(c.oid) AS n_live_tup, - pg_stat_get_dead_tuples(c.oid) AS n_dead_tup, - pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze, - pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum, + pg_stat_get_rfn_tuples_inserted(pg_relation_filenode((c.oid)::regclass= )) AS n_tup_ins, + pg_stat_get_rfn_tuples_updated(pg_relation_filenode((c.oid)::regclass)= ) AS n_tup_upd, + pg_stat_get_rfn_tuples_deleted(pg_relation_filenode((c.oid)::regclass)= ) AS n_tup_del, + pg_stat_get_rfn_tuples_hot_updated(pg_relation_filenode((c.oid)::regcl= ass)) AS n_tup_hot_upd, + pg_stat_get_rfn_tuples_newpage_updated(pg_relation_filenode((c.oid)::r= egclass)) AS n_tup_newpage_upd, + pg_stat_get_rfn_live_tuples(pg_relation_filenode((c.oid)::regclass)) A= S n_live_tup, + pg_stat_get_rfn_dead_tuples(pg_relation_filenode((c.oid)::regclass)) A= S n_dead_tup, + pg_stat_get_rfn_mod_since_analyze(pg_relation_filenode((c.oid)::regcla= ss)) AS n_mod_since_analyze, + pg_stat_get_rfn_ins_since_vacuum(pg_relation_filenode((c.oid)::regclas= s)) AS n_ins_since_vacuum, pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum, pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum, pg_stat_get_last_analyze_time(c.oid) AS last_analyze, diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expecte= d/stats.out index b44e30f557c9..f7d573b890aa 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -128,14 +128,15 @@ SELECT id, name, fixed_amount, 5 | replslot | f | t | t 6 | subscription | f | t | t 7 | backend | f | t | f - 8 | archiver | t | f | t - 9 | bgwriter | t | f | t - 10 | checkpointer | t | f | t - 11 | io | t | f | t - 12 | lock | t | f | t - 13 | slru | t | f | t - 14 | wal | t | f | t -(14 rows) + 8 | relfilenode | f | f | t + 9 | archiver | t | f | t + 10 | bgwriter | t | f | t + 11 | checkpointer | t | f | t + 12 | io | t | f | t + 13 | lock | t | f | t + 14 | slru | t | f | t + 15 | wal | t | f | t +(15 rows) =20 -- ensure that both seqscan and indexscan plans are allowed SET enable_seqscan TO on; @@ -241,9 +242,9 @@ SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live= _tup, n_dead_tup WHERE relname like 'trunc_stats_test%' order by relname; relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_de= ad_tup=20 -------------------+-----------+-----------+-----------+------------+-----= ------- - trunc_stats_test | 3 | 0 | 0 | 0 | = 0 - trunc_stats_test1 | 4 | 2 | 1 | 1 | = 0 - trunc_stats_test2 | 1 | 0 | 0 | 1 | = 0 + trunc_stats_test | 0 | 0 | 0 | 0 | = 0 + trunc_stats_test1 | 0 | 0 | 0 | 0 | = 0 + trunc_stats_test2 | 0 | 0 | 0 | 0 | = 0 trunc_stats_test3 | 4 | 0 | 0 | 2 | = 2 trunc_stats_test4 | 2 | 0 | 0 | 0 | = 2 (5 rows) @@ -470,30 +471,30 @@ SELECT pg_stat_get_function_calls(:stats_test_func2_o= id); -- by oid after the DROP TABLE. Save oids. CREATE TABLE drop_stats_test(); INSERT INTO drop_stats_test DEFAULT VALUES; -SELECT 'drop_stats_test'::regclass::oid AS drop_stats_test_oid \gset +SELECT pg_relation_filenode('drop_stats_test'::regclass) AS drop_stats_tes= t_oid \gset CREATE TABLE drop_stats_test_xact(); INSERT INTO drop_stats_test_xact DEFAULT VALUES; -SELECT 'drop_stats_test_xact'::regclass::oid AS drop_stats_test_xact_oid \= gset +SELECT pg_relation_filenode('drop_stats_test_xact'::regclass) AS drop_stat= s_test_xact_oid \gset CREATE TABLE drop_stats_test_subxact(); INSERT INTO drop_stats_test_subxact DEFAULT VALUES; -SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact= _oid \gset +SELECT pg_relation_filenode('drop_stats_test_subxact'::regclass) AS drop_s= tats_test_subxact_oid \gset SELECT pg_stat_force_next_flush(); pg_stat_force_next_flush=20 -------------------------- =20 (1 row) =20 -SELECT pg_stat_get_live_tuples(:drop_stats_test_oid); - pg_stat_get_live_tuples=20 -------------------------- - 1 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 1 (1 row) =20 DROP TABLE drop_stats_test; -SELECT pg_stat_get_live_tuples(:drop_stats_test_oid); - pg_stat_get_live_tuples=20 -------------------------- - 0 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 1 (1 row) =20 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid); @@ -504,18 +505,18 @@ SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_t= est_oid); =20 -- check that rollback protects against having stats dropped and that local -- modifications don't pose a problem -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 1 -(1 row) - -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); - pg_stat_get_tuples_inserted=20 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); + pg_stat_get_rfn_live_tuples=20 ----------------------------- 1 (1 row) =20 +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); + pg_stat_get_rfn_tuples_inserted=20 +--------------------------------- + 1 +(1 row) + SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid); pg_stat_get_xact_tuples_inserted=20 ---------------------------------- @@ -544,29 +545,29 @@ SELECT pg_stat_force_next_flush(); =20 (1 row) =20 -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 1 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 1 (1 row) =20 -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); - pg_stat_get_tuples_inserted=20 ------------------------------ - 2 +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); + pg_stat_get_rfn_tuples_inserted=20 +--------------------------------- + 2 (1 row) =20 -- transactional drop -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 1 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 1 (1 row) =20 -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); - pg_stat_get_tuples_inserted=20 ------------------------------ - 2 +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); + pg_stat_get_rfn_tuples_inserted=20 +--------------------------------- + 2 (1 row) =20 BEGIN; @@ -591,23 +592,23 @@ SELECT pg_stat_force_next_flush(); =20 (1 row) =20 -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 0 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 1 (1 row) =20 -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); - pg_stat_get_tuples_inserted=20 ------------------------------ - 0 +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); + pg_stat_get_rfn_tuples_inserted=20 +--------------------------------- + 2 (1 row) =20 -- savepoint rollback (2 levels) -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 1 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 1 (1 row) =20 BEGIN; @@ -636,17 +637,17 @@ SELECT pg_stat_force_next_flush(); =20 (1 row) =20 -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 3 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 3 (1 row) =20 -- savepoint rolback (1 level) -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 3 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 3 (1 row) =20 BEGIN; @@ -655,17 +656,17 @@ DROP TABLE drop_stats_test_subxact; SAVEPOINT sp2; ROLLBACK TO SAVEPOINT sp1; COMMIT; -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 3 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 3 (1 row) =20 -- and now actually drop -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 3 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 3 (1 row) =20 BEGIN; @@ -674,10 +675,10 @@ DROP TABLE drop_stats_test_subxact; SAVEPOINT sp2; RELEASE SAVEPOINT sp1; COMMIT; -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); - pg_stat_get_live_tuples=20 -------------------------- - 0 +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); + pg_stat_get_rfn_live_tuples=20 +----------------------------- + 3 (1 row) =20 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_s= tats_test3, trunc_stats_test4; @@ -945,7 +946,7 @@ SELECT (n_tup_ins + n_tup_upd) > 0 AS has_data FROM pg_= stat_all_tables WHERE relid =3D 'pg_shdescription'::regclass; has_data=20 ---------- - t + f (1 row) =20 -- stats_reset may not be set for datid=3D0 and shared objects in @@ -1902,7 +1903,7 @@ DECLARE BEGIN -- we don't want to wait forever; loop will exit after 30 seconds FOR i IN 1 .. 300 LOOP - SELECT (pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid) > 0)= INTO updated; + SELECT (pg_stat_get_rfn_tuples_hot_updated(pg_relation_filenode('brin_= hot'::regclass::oid)) > 0) INTO updated; EXIT WHEN updated; =20 -- wait a little @@ -1929,10 +1930,10 @@ SELECT wait_for_hot_stats(); =20 (1 row) =20 -SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); - pg_stat_get_tuples_hot_updated=20 --------------------------------- - 1 +SELECT pg_stat_get_rfn_tuples_hot_updated(pg_relation_filenode('brin_hot':= :regclass::oid)); + pg_stat_get_rfn_tuples_hot_updated=20 +------------------------------------ + 1 (1 row) =20 DROP TABLE brin_hot; diff --git a/src/test/regress/expected/stats_rewrite.out b/src/test/regress= /expected/stats_rewrite.out index 93752bab9cb3..4f341672e5f1 100644 --- a/src/test/regress/expected/stats_rewrite.out +++ b/src/test/regress/expected/stats_rewrite.out @@ -42,7 +42,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_2pc_rewrite_alone'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 1 | 1 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_2pc_rewrite_alone; @@ -66,7 +66,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_2pc'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 4 | 4 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_2pc; @@ -93,7 +93,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_2pc_multi'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 6 | 6 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_2pc_multi; @@ -114,7 +114,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_2pc_rewrite_alone_abort'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 1 | 1 | 0 + 2 | 1 | 1 (1 row) =20 DROP TABLE test_2pc_rewrite_alone_abort; @@ -138,7 +138,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_2pc_abort'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 4 | 1 | 3 + 7 | 1 | 6 (1 row) =20 DROP TABLE test_2pc_abort; @@ -169,7 +169,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_2pc_savepoint'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 6 | 3 | 3 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_2pc_savepoint; @@ -209,7 +209,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_alone'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 1 | 1 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_alone; @@ -232,7 +232,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 4 | 4 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test; @@ -258,7 +258,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_multi'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 6 | 6 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_multi; @@ -331,7 +331,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_savepoint'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 6 | 3 | 3 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_savepoint; diff --git a/src/test/regress/expected/stats_rewrite_1.out b/src/test/regre= ss/expected/stats_rewrite_1.out index 909188b18fd7..32c8cf803ebe 100644 --- a/src/test/regress/expected/stats_rewrite_1.out +++ b/src/test/regress/expected/stats_rewrite_1.out @@ -230,7 +230,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_alone'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 1 | 1 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_alone; @@ -253,7 +253,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 4 | 4 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test; @@ -279,7 +279,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_multi'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 6 | 6 | 0 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_multi; @@ -352,7 +352,7 @@ SELECT n_tup_ins, n_live_tup, n_dead_tup FROM pg_stat_all_tables WHERE relname =3D 'test_savepoint'; n_tup_ins | n_live_tup | n_dead_tup=20 -----------+------------+------------ - 6 | 3 | 3 + 0 | 0 | 0 (1 row) =20 DROP TABLE test_savepoint; diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index d4623c32cd32..9188429e03ca 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -219,27 +219,27 @@ SELECT pg_stat_get_function_calls(:stats_test_func2_o= id); -- by oid after the DROP TABLE. Save oids. CREATE TABLE drop_stats_test(); INSERT INTO drop_stats_test DEFAULT VALUES; -SELECT 'drop_stats_test'::regclass::oid AS drop_stats_test_oid \gset +SELECT pg_relation_filenode('drop_stats_test'::regclass) AS drop_stats_tes= t_oid \gset =20 CREATE TABLE drop_stats_test_xact(); INSERT INTO drop_stats_test_xact DEFAULT VALUES; -SELECT 'drop_stats_test_xact'::regclass::oid AS drop_stats_test_xact_oid \= gset +SELECT pg_relation_filenode('drop_stats_test_xact'::regclass) AS drop_stat= s_test_xact_oid \gset =20 CREATE TABLE drop_stats_test_subxact(); INSERT INTO drop_stats_test_subxact DEFAULT VALUES; -SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact= _oid \gset +SELECT pg_relation_filenode('drop_stats_test_subxact'::regclass) AS drop_s= tats_test_subxact_oid \gset =20 SELECT pg_stat_force_next_flush(); =20 -SELECT pg_stat_get_live_tuples(:drop_stats_test_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_oid); DROP TABLE drop_stats_test; -SELECT pg_stat_get_live_tuples(:drop_stats_test_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_oid); SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid); =20 -- check that rollback protects against having stats dropped and that local -- modifications don't pose a problem -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid); BEGIN; INSERT INTO drop_stats_test_xact DEFAULT VALUES; @@ -248,12 +248,12 @@ DROP TABLE drop_stats_test_xact; SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid); ROLLBACK; SELECT pg_stat_force_next_flush(); -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); =20 -- transactional drop -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); BEGIN; INSERT INTO drop_stats_test_xact DEFAULT VALUES; SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid); @@ -261,11 +261,11 @@ DROP TABLE drop_stats_test_xact; SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid); COMMIT; SELECT pg_stat_force_next_flush(); -SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid); -SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_xact_oid); +SELECT pg_stat_get_rfn_tuples_inserted(:drop_stats_test_xact_oid); =20 -- savepoint rollback (2 levels) -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); BEGIN; INSERT INTO drop_stats_test_subxact DEFAULT VALUES; SAVEPOINT sp1; @@ -277,27 +277,27 @@ ROLLBACK TO SAVEPOINT sp2; SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid); COMMIT; SELECT pg_stat_force_next_flush(); -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); =20 -- savepoint rolback (1 level) -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); BEGIN; SAVEPOINT sp1; DROP TABLE drop_stats_test_subxact; SAVEPOINT sp2; ROLLBACK TO SAVEPOINT sp1; COMMIT; -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); =20 -- and now actually drop -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); BEGIN; SAVEPOINT sp1; DROP TABLE drop_stats_test_subxact; SAVEPOINT sp2; RELEASE SAVEPOINT sp1; COMMIT; -SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid); +SELECT pg_stat_get_rfn_live_tuples(:drop_stats_test_subxact_oid); =20 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_s= tats_test3, trunc_stats_test4; DROP TABLE prevstats; @@ -898,7 +898,7 @@ DECLARE BEGIN -- we don't want to wait forever; loop will exit after 30 seconds FOR i IN 1 .. 300 LOOP - SELECT (pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid) > 0)= INTO updated; + SELECT (pg_stat_get_rfn_tuples_hot_updated(pg_relation_filenode('brin_= hot'::regclass::oid)) > 0) INTO updated; EXIT WHEN updated; =20 -- wait a little @@ -923,7 +923,7 @@ UPDATE brin_hot SET val =3D -3 WHERE id =3D 42; \c - =20 SELECT wait_for_hot_stats(); -SELECT pg_stat_get_tuples_hot_updated('brin_hot'::regclass::oid); +SELECT pg_stat_get_rfn_tuples_hot_updated(pg_relation_filenode('brin_hot':= :regclass::oid)); =20 DROP TABLE brin_hot; DROP FUNCTION wait_for_hot_stats(); diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postg= res_fdw/expected/postgres_fdw.out index 5ebae1cedc2f..eed714f7d588 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -12988,12 +12988,12 @@ SELECT pg_stat_force_next_flush(); =20 (1 row) =20 -SELECT pg_stat_get_live_tuples('public.simport_ftable'::regclass), - pg_stat_get_dead_tuples('public.simport_ftable'::regclass), +SELECT pg_stat_get_rfn_live_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), + pg_stat_get_rfn_dead_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), pg_stat_get_analyze_count('public.simport_ftable'::regclass); - pg_stat_get_live_tuples | pg_stat_get_dead_tuples | pg_stat_get_analyze_c= ount=20 --------------------------+-------------------------+----------------------= ----- - 0 | 0 | = 1 + pg_stat_get_rfn_live_tuples | pg_stat_get_rfn_dead_tuples | pg_stat_get_a= nalyze_count=20 +-----------------------------+-----------------------------+--------------= ------------- + | | = 1 (1 row) =20 ALTER TABLE simport_table ALTER COLUMN c1 SET STATISTICS 0; @@ -13023,12 +13023,12 @@ SELECT pg_stat_force_next_flush(); =20 (1 row) =20 -SELECT pg_stat_get_live_tuples('public.simport_ftable'::regclass), - pg_stat_get_dead_tuples('public.simport_ftable'::regclass), +SELECT pg_stat_get_rfn_live_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), + pg_stat_get_rfn_dead_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), pg_stat_get_analyze_count('public.simport_ftable'::regclass); - pg_stat_get_live_tuples | pg_stat_get_dead_tuples | pg_stat_get_analyze_c= ount=20 --------------------------+-------------------------+----------------------= ----- - 4 | 0 | = 1 + pg_stat_get_rfn_live_tuples | pg_stat_get_rfn_dead_tuples | pg_stat_get_a= nalyze_count=20 +-----------------------------+-----------------------------+--------------= ------------- + | | = 1 (1 row) =20 ANALYZE VERBOSE simport_ftable (c1); -- should work diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_f= dw/sql/postgres_fdw.sql index e868da00ace7..a3be2f260280 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -4590,8 +4590,8 @@ ANALYZE simport_table; SELECT pg_stat_reset_single_table_counters('public.simport_ftable'::regcla= ss); ANALYZE VERBOSE simport_ftable; -- should work SELECT pg_stat_force_next_flush(); -SELECT pg_stat_get_live_tuples('public.simport_ftable'::regclass), - pg_stat_get_dead_tuples('public.simport_ftable'::regclass), +SELECT pg_stat_get_rfn_live_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), + pg_stat_get_rfn_dead_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), pg_stat_get_analyze_count('public.simport_ftable'::regclass); =20 ALTER TABLE simport_table ALTER COLUMN c1 SET STATISTICS 0; @@ -4612,8 +4612,8 @@ ANALYZE simport_table; SELECT pg_stat_reset_single_table_counters('public.simport_ftable'::regcla= ss); ANALYZE VERBOSE simport_ftable; -- should work SELECT pg_stat_force_next_flush(); -SELECT pg_stat_get_live_tuples('public.simport_ftable'::regclass), - pg_stat_get_dead_tuples('public.simport_ftable'::regclass), +SELECT pg_stat_get_rfn_live_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), + pg_stat_get_rfn_dead_tuples(pg_relation_filenode('public.simport_ft= able'::regclass)), pg_stat_get_analyze_count('public.simport_ftable'::regclass); =20 ANALYZE VERBOSE simport_ftable (c1); -- should work --=20 2.55.0 --uu1QJiDJI3oJPnCR-- --ui2W8jjkRyW+Lel8 Content-Type: application/pgp-signature; name=signature.asc -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEG72nH6vTowiyblFKnvQgOdbyQH0FAmpQi68ACgkQnvQgOdby QH2wxw/9G7g/EjMjowgQZVbuHiC/K67HLcsVc1wx3p/12DDXzfnbgaZ9wvF9a8Xu AlQ7RoQLjwJ6oedazVRdQosdBVMbOiO1yNbaK20mVFq61rP7Jd6migNr2QsFWtFW AOgZRIZ+n2HZnLhtO+DvFwCWW+3ZtJCYoThDDF/xZBoObwJHHYsGyo6AF9FJRmrB GDXdIRfdxvFcE7LRgT45Fvrp0pkgScjvGC1EsYnxyMMtlnKy09bHyC7emtJCDrkT jZA7nONgnDeLt5CKWBZzThU5x8IG3n+Pd4Mg8OjLwtjF7PZEHcyd9Rgm4TbWbvkX OOsAPgTJw2RAA22Pw9W7qVA/M2vuDoNjH31Pc3UI4DqbW0aUu0x3DINvQupKdPrE BIu9gPd6SapGrQ81HW3bdJ9C3UQPKWDR6YwNnt9bj4Yo69KKEZuiMJh7Lg3BuokT 93p/P6RmgmiH1CWxybtMIMdr3qvem+snf8YhZdCIWpOPCTlYbP3HTBBn/epw/cM+ rl6bW515MLzLjOQMux43SRL/sYwqh9VBK2IThJy/0j2Nz4igrsKtAPWPSImMtv4K OksMG3mpVhW0JD2pC7GR1xi4Y0VcA69eox1SNW3s3xbULi+6XM3uUkYnVf+ok1vc 6WsOk9QnuxM6J/AzyqgeG/+bxnyGpGDSL91kSTRVx9AxnTozd40= =FOYf -----END PGP SIGNATURE----- --ui2W8jjkRyW+Lel8--