On Mon, Aug 10, 2026 at 04:40:54AM +0000, Bertrand Drouvot wrote: > I had a look at 0001 only, and I've a few comments.
Thanks for the review.
> === 1
>
> +pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
> +{
> [...]
> + /* The entry was successfully flushed, add the same to database stats */
> + dbentry = pgstat_prep_database_pending(dboid);
> + dbentry->blocks_fetched += lstats->counts.blocks_fetched;
> + dbentry->blocks_hit += lstats->counts.blocks_hit;
>
> Shouldn't we add tuples_returned and tuples_fetched here too?
Argh. I have missed these two. Nice catch.
> === 2
> But it looks like that pg_stat_get_xact_numscans() descr has not been updated
> in pg_proc.dat although it now returns 0 for indexes.
Indeed. This needs to be updated. Done now in the attached.
A test could be in place here, I guess, but we cannot make it relevant
except in a TAP test where the counter increments would be isolated.
Will look at how possible this could be later. For now, I will do an
extra pass later on this patch. Updated version is attached for now.
> There is no public doc for pg_stat_get_numscans() and
> pg_stat_get_xact_numscans(),
> so existing callers will get different behavior (that is not publicly
> documented).
>
> Should we preserve the old behavior while keeping the new functions as
> explicit
> alternatives? Or is it less of an issue given that those 2 don't have public
> doc?
pg_stat_get_xact_numscans() is only called for
pg_stat_xact_all_tables. pg_stat_get_numscans() is only called for
pg_stat_all_tables. Both apply to relkind IN ('r', 't', 'm', 'p').
So returning 0 is not a big deal IMO. We have never used that for
indexes, and the table behavior is untouched. So I think that the
patch as-is looks fine on this side.
--
Michael
From 7cd26a8e0874c77c18c6fb26f3c9ca169f22899e Mon Sep 17 00:00:00 2001 From: Michael Paquier <[email protected]> Date: Mon, 10 Aug 2026 16:19:56 +0900 Subject: [PATCH v15 1/2] 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 | 69 +++++++++- 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 | 131 +++++++++++++++++++ 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 ++- src/tools/pgindent/typedefs.list | 2 + 16 files changed, 544 insertions(+), 113 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 f8a021987b5e..66c3c9a04cfb 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5555,11 +5555,11 @@ proargnames => '{mcv_list,index,values,nulls,frequency,base_frequency}', prosrc => 'pg_stats_ext_mcvlist_items' }, -{ oid => '1928', descr => 'statistics: number of scans done for table/index', +{ oid => '1928', descr => 'statistics: number of scans done for table', proname => 'pg_stat_get_numscans', provolatile => 's', proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', prosrc => 'pg_stat_get_numscans' }, -{ oid => '6310', descr => 'statistics: time of the last scan for table/index', +{ oid => '6310', descr => 'statistics: time of the last scan for table', proname => 'pg_stat_get_lastscan', provolatile => 's', proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid', prosrc => 'pg_stat_get_lastscan' }, @@ -6142,7 +6142,7 @@ prosrc => 'pg_stat_get_function_stat_reset_time' }, { oid => '3037', - descr => 'statistics: number of scans done for table/index in current transaction', + descr => 'statistics: number of scans done for table in current transaction', proname => 'pg_stat_get_xact_numscans', provolatile => 'v', proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', prosrc => 'pg_stat_get_xact_numscans' }, @@ -6234,10 +6234,71 @@ proargdefaults => '{NULL}', prosrc => 'pg_stat_reset_shared', proacl => '{POSTGRES=X}' }, { oid => '3776', - descr => 'statistics: reset collected statistics for a single table or index in the current database or shared across all databases in the cluster', + descr => 'statistics: reset collected statistics for a single table in the current database or shared across all databases in the cluster', proname => 'pg_stat_reset_single_table_counters', provolatile => 'v', prorettype => 'void', proargtypes => 'oid', prosrc => 'pg_stat_reset_single_table_counters', proacl => '{POSTGRES=X}' }, +{ oid => '8450', + descr => 'statistics: reset collected statistics for a single index in the current database or shared across all databases in the cluster', + proname => 'pg_stat_reset_single_index_counters', provolatile => 'v', + prorettype => 'void', proargtypes => 'oid', + prosrc => 'pg_stat_reset_single_index_counters', proacl => '{POSTGRES=X}' }, +{ oid => '8451', descr => 'statistics: number of scans done for index', + proname => 'pg_stat_get_idx_numscans', provolatile => 's', proparallel => 'r', + prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_idx_numscans' }, +{ oid => '8452', + descr => 'statistics: number of index entries returned by index', + proname => 'pg_stat_get_idx_tuples_returned', provolatile => 's', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_idx_tuples_returned' }, +{ oid => '8453', + descr => 'statistics: number of tuples fetched from table by index', + proname => 'pg_stat_get_idx_tuples_fetched', provolatile => 's', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_idx_tuples_fetched' }, +{ oid => '8454', descr => 'statistics: number of blocks fetched for index', + proname => 'pg_stat_get_idx_blocks_fetched', provolatile => 's', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_idx_blocks_fetched' }, +{ oid => '8455', + descr => 'statistics: number of blocks found in cache for index', + proname => 'pg_stat_get_idx_blocks_hit', provolatile => 's', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_idx_blocks_hit' }, +{ oid => '8456', descr => 'statistics: time of the last scan for index', + proname => 'pg_stat_get_idx_lastscan', provolatile => 's', proparallel => 'r', + prorettype => 'timestamptz', proargtypes => 'oid', + prosrc => 'pg_stat_get_idx_lastscan' }, +{ oid => '8457', descr => 'statistics: last reset for an index', + proname => 'pg_stat_get_idx_stat_reset_time', provolatile => 's', + proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid', + prosrc => 'pg_stat_get_idx_stat_reset_time' }, +{ oid => '8458', + descr => 'statistics: number of scans done for index in current transaction', + proname => 'pg_stat_get_xact_idx_numscans', provolatile => 'v', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_xact_idx_numscans' }, +{ oid => '8459', + descr => 'statistics: number of index entries returned in current transaction', + proname => 'pg_stat_get_xact_idx_tuples_returned', provolatile => 'v', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_xact_idx_tuples_returned' }, +{ oid => '8460', + descr => 'statistics: number of tuples fetched by index in current transaction', + proname => 'pg_stat_get_xact_idx_tuples_fetched', provolatile => 'v', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_xact_idx_tuples_fetched' }, +{ oid => '8461', + descr => 'statistics: number of blocks fetched for index in current transaction', + proname => 'pg_stat_get_xact_idx_blocks_fetched', provolatile => 'v', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_xact_idx_blocks_fetched' }, +{ oid => '8462', + descr => 'statistics: number of blocks found in cache for index in current transaction', + proname => 'pg_stat_get_xact_idx_blocks_hit', provolatile => 'v', + proparallel => 'r', prorettype => 'int8', proargtypes => 'oid', + prosrc => 'pg_stat_get_xact_idx_blocks_hit' }, { oid => '3777', descr => 'statistics: reset collected statistics for a single function in the current database', proname => 'pg_stat_reset_single_function_counters', provolatile => '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; +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 rel_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); /* diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index b0a176919667..1ef98620e1c0 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -508,6 +508,12 @@ typedef struct PgStatShared_Relation PgStat_StatTabEntry stats; } PgStatShared_Relation; +typedef struct PgStatShared_Index +{ + PgStatShared_Common header; + PgStat_StatIdxEntry stats; +} PgStatShared_Index; + typedef struct PgStatShared_Function { PgStatShared_Common header; @@ -789,6 +795,15 @@ extern void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref); extern void pgstat_relation_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts); +/* + * 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, TimestampTz ts); + + /* * Functions in pgstat_replslot.c */ diff --git a/src/include/utils/pgstat_kind.h b/src/include/utils/pgstat_kind.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 */ /* 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 #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/system_views.sql index 090281a03ddf..8612d99a8909 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_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, @@ -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_scan, - sum(pg_stat_get_xact_tuples_fetched(I.indexrelid))::bigint + + 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, @@ -833,17 +833,17 @@ CREATE VIEW pg_statio_all_tables AS pg_class T ON C.reltoastrelid = T.oid LEFT JOIN pg_namespace N ON (N.oid = 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 = 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 = 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 = X.indrelid JOIN pg_class I ON I.oid = 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 = X.indrelid JOIN pg_class I ON I.oid = X.indexrelid diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile index 5fed953c28a7..2e32d1485d64 100644 --- a/src/backend/utils/activity/Makefile +++ b/src/backend/utils/activity/Makefile @@ -25,6 +25,7 @@ OBJS = \ 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/activity/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 += 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/activity/pgstat.c index 50cd07822b4a..fe1637e20956 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 = pgstat_relation_reset_timestamp_cb, }, + [PGSTAT_KIND_INDEX] = { + .name = "index", + + .fixed_amount = false, + .write_to_file = true, + + .shared_size = sizeof(PgStatShared_Index), + .shared_data_off = offsetof(PgStatShared_Index, stats), + .shared_data_len = sizeof(((PgStatShared_Index *) 0)->stats), + .pending_size = sizeof(PgStat_TableStatus), + + .flush_pending_cb = pgstat_index_flush_cb, + .delete_pending_cb = pgstat_index_delete_pending_cb, + .reset_timestamp_cb = pgstat_index_reset_timestamp_cb, + }, + [PGSTAT_KIND_FUNCTION] = { .name = "function", diff --git a/src/backend/utils/activity/pgstat_index.c b/src/backend/utils/activity/pgstat_index.c new file mode 100644 index 000000000000..ce7a7f0f56c7 --- /dev/null +++ b/src/backend/utils/activity/pgstat_index.c @@ -0,0 +1,131 @@ +/* ------------------------------------------------------------------------- + * + * 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, returns + * 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 = entry_ref->shared_entry->key.dboid; + lstats = (PgStat_TableStatus *) entry_ref->pending; + shidxstats = (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 = &shidxstats->stats; + + idxentry->numscans += lstats->counts.numscans; + if (lstats->counts.numscans) + { + TimestampTz t = GetCurrentTransactionStopTimestamp(); + + if (t > idxentry->lastscan) + idxentry->lastscan = t; + } + idxentry->tuples_returned += lstats->counts.tuples_returned; + idxentry->tuples_fetched += lstats->counts.tuples_fetched; + idxentry->blocks_fetched += lstats->counts.blocks_fetched; + idxentry->blocks_hit += lstats->counts.blocks_hit; + + pgstat_unlock_entry(entry_ref); + + /* The entry was successfully flushed, add the same to database stats */ + dbentry = pgstat_prep_database_pending(dboid); + dbentry->tuples_returned += lstats->counts.tuples_returned; + dbentry->tuples_fetched += lstats->counts.tuples_fetched; + dbentry->blocks_fetched += lstats->counts.blocks_fetched; + dbentry->blocks_hit += 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 = (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 ts) +{ + ((PgStatShared_Index *) header)->stats.stat_reset_time = 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, NULL); +} + +/* + * More efficient version of pgstat_fetch_stat_idxentry(), allowing to specify + * whether the to-be-accessed index is a shared relation or not. This version + * 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 = (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/utils/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; -static PgStat_TableStatus *pgstat_prep_relation_pending(Oid rel_id, bool isshared); +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 nest_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); +/* + * Determine the stats kind for a relation based on its relkind. + */ +static inline PgStat_Kind +pgstat_get_relation_kind(char relkind) +{ + if (relkind == RELKIND_INDEX) + return PGSTAT_KIND_INDEX; + return PGSTAT_KIND_RELATION; +} + /* * Copy stats between relations. This is used for things like REINDEX @@ -56,25 +68,52 @@ static void restore_truncdrop_counters(PgStat_TableXactStatus *trans); void pgstat_copy_relation_stats(Relation dst, Relation src) { - PgStat_StatTabEntry *srcstats; - PgStatShared_Relation *dstshstats; - PgStat_EntryRef *dst_ref; + PgStat_Kind kind = pgstat_get_relation_kind(src->rd_rel->relkind); - srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared, - RelationGetRelid(src), - NULL); - if (!srcstats) - return; + if (kind == PGSTAT_KIND_INDEX) + { + PgStat_StatIdxEntry *srcstats; + PgStatShared_Index *dstshstats; + PgStat_EntryRef *dst_ref; - dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, - dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId, - RelationGetRelid(dst), - false); + srcstats = pgstat_fetch_stat_idxentry_ext(src->rd_rel->relisshared, + RelationGetRelid(src), + NULL); + if (!srcstats) + return; - dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats; - dstshstats->stats = *srcstats; + dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX, + dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId, + RelationGetRelid(dst), + false); - pgstat_unlock_entry(dst_ref); + dstshstats = (PgStatShared_Index *) dst_ref->shared_stats; + dstshstats->stats = *srcstats; + + pgstat_unlock_entry(dst_ref); + } + else + { + PgStat_StatTabEntry *srcstats; + PgStatShared_Relation *dstshstats; + PgStat_EntryRef *dst_ref; + + srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared, + RelationGetRelid(src), + NULL); + if (!srcstats) + return; + + dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, + dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId, + RelationGetRelid(dst), + false); + + dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats; + dstshstats->stats = *srcstats; + + pgstat_unlock_entry(dst_ref); + } } /* @@ -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 == NULL); + kind = pgstat_get_relation_kind(rel->rd_rel->relkind); + /* find or make the PgStat_TableStatus entry, and update link */ - rel->pgstat_info = pgstat_prep_relation_pending(RelationGetRelid(rel), + rel->pgstat_info = pgstat_prep_relation_pending(kind, + RelationGetRelid(rel), rel->rd_rel->relisshared); /* 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 = 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 = GetCurrentTransactionNestLevel(); PgStat_TableStatus *pgstat_info; + PgStat_Kind kind = pgstat_get_relation_kind(rel->rd_rel->relkind); - pgstat_drop_transactional(PGSTAT_KIND_RELATION, + pgstat_drop_transactional(kind, rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId, RelationGetRelid(rel)); @@ -500,16 +547,25 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid, 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 = NULL; PgStat_TableStatus *tablestatus = NULL; - entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, MyDatabaseId, rel_id); + entry_ref = pgstat_fetch_pending_entry(kind, MyDatabaseId, rel_id); if (!entry_ref) { - entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, InvalidOid, rel_id); + entry_ref = pgstat_fetch_pending_entry(kind, InvalidOid, rel_id); if (!entry_ref) return tablestatus; } @@ -752,7 +808,7 @@ pgstat_twophase_postcommit(FullTransactionId fxid, uint16 info, PgStat_TableStatus *pgstat_info; /* Find or create a tabstat entry for the rel */ - pgstat_info = pgstat_prep_relation_pending(rec->id, rec->shared); + pgstat_info = pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->id, rec->shared); /* Same math as in AtEOXact_PgStat, commit case */ pgstat_info->counts.tuples_inserted += rec->tuples_inserted; @@ -788,7 +844,7 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint16 info, PgStat_TableStatus *pgstat_info; /* Find or create a tabstat entry for the rel */ - pgstat_info = pgstat_prep_relation_pending(rec->id, rec->shared); + pgstat_info = pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->id, rec->shared); /* Same math as in AtEOXact_PgStat, abort case */ if (rec->truncdropped) @@ -922,12 +978,12 @@ pgstat_relation_reset_timestamp_cb(PgStatShared_Common *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; - entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_RELATION, + entry_ref = pgstat_prep_pending_entry(kind, isshared ? InvalidOid : MyDatabaseId, rel_id, NULL); pending = entry_ref->pending; diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.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) +/* + * 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 = PG_GETARG_OID(0); \ + int64 result; \ + PgStat_StatIdxEntry *idxentry; \ + \ + if ((idxentry = pgstat_fetch_stat_idxentry(relid)) == NULL) \ + result = 0; \ + else \ + result = (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) +/* + * 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 = PG_GETARG_OID(0); \ + TimestampTz result; \ + PgStat_StatIdxEntry *idxentry; \ + \ + if ((idxentry = pgstat_fetch_stat_idxentry(relid)) == NULL) \ + result = 0; \ + else \ + result = idxentry->stat; \ + \ + if (result == 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) +/* + * 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 = PG_GETARG_OID(0); \ + int64 result; \ + PgStat_TableStatus *tabentry; \ + \ + tabentry = find_tabstat_entry_kind(PGSTAT_KIND_INDEX, relid); \ + if (!tabentry) \ + result = 0; \ + else \ + result = (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(); } +Datum +pg_stat_reset_single_index_counters(PG_FUNCTION_ARGS) +{ + Oid idxoid = PG_GETARG_OID(0); + Oid dboid = (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/expected/rules.out index 6a3341356da1..1a29d46213ea 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 = x.indrelid))) JOIN pg_class i ON ((i.oid = 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, @@ -2455,8 +2455,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_stat_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, @@ -2509,9 +2509,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 = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) @@ -2541,12 +2541,12 @@ pg_statio_all_tables| SELECT c.oid AS relid, FROM ((((pg_class c LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid))) LEFT JOIN pg_namespace n ON ((n.oid = 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_index.indexrelid) - pg_stat_get_idx_blocks_hit(pg_index.indexrelid))))::bigint 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 = 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_index.indexrelid) - pg_stat_get_idx_blocks_hit(pg_index.indexrelid))))::bigint 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 = t.oid)) x ON (true)) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index e230356de139..c682a9ed60a2 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -116,18 +116,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) -- ensure that both seqscan and indexscan plans are allowed SET enable_seqscan TO on; @@ -900,8 +901,8 @@ SELECT idx_scan, :'test_last_idx' < last_idx_scan AS idx_ok, (1 row) -- 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 +SELECT pg_stat_reset_single_index_counters('test_last_scan_pkey'::regclass); + pg_stat_reset_single_index_counters ------------------------------------- (1 row) @@ -1385,21 +1386,21 @@ select a from stats_test_tab1 where a = 3; 3 (1 row) -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 -------------------- t (1 row) -- 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 -------------------- t (1 row) 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 -------------------- f @@ -1415,14 +1416,14 @@ select a from stats_test_tab1 where a = 3; 3 (1 row) -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 -------------------- t (1 row) 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 -------------------- f @@ -1437,7 +1438,7 @@ select a from stats_test_tab1 where a = 3; 3 (1 row) -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 -------------------- t @@ -1445,7 +1446,7 @@ SELECT pg_stat_have_stats('relation', :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); pg_stat_have_stats -------------------- f @@ -1453,7 +1454,7 @@ SELECT pg_stat_have_stats('relation', :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); pg_stat_have_stats -------------------- t @@ -1461,7 +1462,7 @@ SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid); -- 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 -------------------- t @@ -1469,7 +1470,7 @@ SELECT pg_stat_have_stats('relation', :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); pg_stat_have_stats -------------------- 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 idx_ok, FROM pg_stat_all_indexes WHERE indexrelid = 'test_last_scan_pkey'::regclass; -- 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); SELECT idx_scan, stats_reset IS NOT NULL AS has_stats_reset FROM pg_stat_all_indexes WHERE indexrelid = 'test_last_scan_pkey'::regclass; @@ -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 = 3; -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 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); -- 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 = 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); -- 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 = 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); -- 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); -- 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 099e9b6f4e9a..32cb6fdbd764 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -5877,6 +5877,24 @@ description | Waiting for a newly initialized WAL file to reach durable storage </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>pg_stat_reset_single_index_counters</primary> + </indexterm> + <function>pg_stat_reset_single_index_counters</function> ( <type>oid</type> ) + <returnvalue>void</returnvalue> + </para> + <para> + Resets statistics for a single index in the current database + or shared across all databases in the cluster to zero. + </para> + <para> + This function is restricted to superusers by default, but other users + can be granted EXECUTE to run the function. + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -6022,7 +6040,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage <returnvalue>void</returnvalue> </para> <para> - Resets statistics for a single table or index in the current database + 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 view in the current database. diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 85d989f395d4..792e62a2518d 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2325,6 +2325,7 @@ PgStatShared_CustomVarEntry PgStatShared_Database PgStatShared_Function PgStatShared_HashEntry +PgStatShared_Index PgStatShared_IO PgStatShared_Lock PgStatShared_Relation @@ -2362,6 +2363,7 @@ PgStat_StatCustomFixedEntry PgStat_StatCustomVarEntry PgStat_StatDBEntry PgStat_StatFuncEntry +PgStat_StatIdxEntry PgStat_StatReplSlotEntry PgStat_StatSubEntry PgStat_StatTabEntry -- 2.55.0
From 6a626d34ff96f9d8cb64df3f1733d3b4bb5b23fd Mon Sep 17 00:00:00 2001 From: Michael Paquier <[email protected]> Date: Thu, 6 Aug 2026 11:02:16 +0900 Subject: [PATCH v15 2/2] 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 | 30 +- src/backend/utils/activity/pgstat_relation.c | 324 ++++++++++--------- src/backend/utils/adt/pgstatfuncs.c | 13 +- src/backend/utils/cache/relcache.c | 2 +- src/tools/pgindent/typedefs.list | 5 +- 9 files changed, 275 insertions(+), 219 deletions(-) diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 23b85fc0c223..1d99e5989596 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 updates * 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 indexscans. - * 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 index. + * 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 attempted * actions, regardless of whether the transaction committed. delta_live_tuples, @@ -163,34 +161,69 @@ typedef struct PgStat_TableCounts } PgStat_TableCounts; /* ---------- - * 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; /* ---------- - * 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; /* ------------------------------------------------------------ @@ -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 == 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 += (n); \ + (rel)->pgstat_info->idx.tuples_returned += (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 == 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 == PGSTAT_KIND_INDEX) \ + (rel)->pgstat_info->idx.blocks_hit++; \ + else \ + (rel)->pgstat_info->tab.counts.blocks_hit++; \ + } \ } while (0) 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 rel_id); +extern PgStat_RelationStatus *find_relstat_entry_kind(PgStat_Kind kind, + Oid rel_id); 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 1ef98620e1c0..59debdc8901b 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -215,13 +215,13 @@ typedef struct PgStat_SubXactStatus /* * 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; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 89c159b133fa..674ac5a1cf46 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -252,7 +252,7 @@ typedef struct RelationData 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; diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index fe1637e20956..4615f6101069 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 = sizeof(PgStatShared_Relation), .shared_data_off = offsetof(PgStatShared_Relation, stats), .shared_data_len = sizeof(((PgStatShared_Relation *) 0)->stats), - .pending_size = sizeof(PgStat_TableStatus), + .pending_size = sizeof(PgStat_RelationStatus), .flush_pending_cb = pgstat_relation_flush_cb, .delete_pending_cb = pgstat_relation_delete_pending_cb, @@ -326,7 +326,7 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE] .shared_size = sizeof(PgStatShared_Index), .shared_data_off = offsetof(PgStatShared_Index, stats), .shared_data_len = sizeof(((PgStatShared_Index *) 0)->stats), - .pending_size = sizeof(PgStat_TableStatus), + .pending_size = sizeof(PgStat_RelationStatus), .flush_pending_cb = pgstat_index_flush_cb, .delete_pending_cb = pgstat_index_delete_pending_cb, diff --git a/src/backend/utils/activity/pgstat_index.c b/src/backend/utils/activity/pgstat_index.c index ce7a7f0f56c7..a1f9a4c6ac13 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 */ dboid = entry_ref->shared_entry->key.dboid; - lstats = (PgStat_TableStatus *) entry_ref->pending; + lstats = (PgStat_RelationStatus *) entry_ref->pending; shidxstats = (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))) + if (pg_memory_is_all_zeros(&lstats->idx, + sizeof(struct PgStat_IndexCounts))) return true; if (!pgstat_lock_entry(entry_ref, nowait)) @@ -58,27 +58,27 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) /* Add the values to the shared entry. */ idxentry = &shidxstats->stats; - idxentry->numscans += lstats->counts.numscans; - if (lstats->counts.numscans) + idxentry->numscans += lstats->idx.numscans; + if (lstats->idx.numscans) { TimestampTz t = GetCurrentTransactionStopTimestamp(); if (t > idxentry->lastscan) idxentry->lastscan = t; } - idxentry->tuples_returned += lstats->counts.tuples_returned; - idxentry->tuples_fetched += lstats->counts.tuples_fetched; - idxentry->blocks_fetched += lstats->counts.blocks_fetched; - idxentry->blocks_hit += lstats->counts.blocks_hit; + idxentry->tuples_returned += lstats->idx.tuples_returned; + idxentry->tuples_fetched += lstats->idx.tuples_fetched; + idxentry->blocks_fetched += lstats->idx.blocks_fetched; + idxentry->blocks_hit += lstats->idx.blocks_hit; pgstat_unlock_entry(entry_ref); /* The entry was successfully flushed, add the same to database stats */ dbentry = pgstat_prep_database_pending(dboid); - dbentry->tuples_returned += lstats->counts.tuples_returned; - dbentry->tuples_fetched += lstats->counts.tuples_fetched; - dbentry->blocks_fetched += lstats->counts.blocks_fetched; - dbentry->blocks_hit += lstats->counts.blocks_hit; + dbentry->tuples_returned += lstats->idx.tuples_returned; + dbentry->tuples_fetched += lstats->idx.tuples_fetched; + dbentry->blocks_fetched += lstats->idx.blocks_fetched; + dbentry->blocks_hit += lstats->idx.blocks_hit; return true; } @@ -89,7 +89,7 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) void pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref) { - PgStat_TableStatus *pending = (PgStat_TableStatus *) entry_ref->pending; + PgStat_RelationStatus *pending = (PgStat_RelationStatus *) entry_ref->pending; if (pending->relation) pgstat_unlink_relation(pending->relation); diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 379a65aa28fb..9c8cb16c35df 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; -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 nest_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 PgStat_RelationStatus *pgstat_prep_relation_pending(PgStat_Kind kind, + Oid rel_id, bool isshared); +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_drop); +static void restore_truncdrop_counters(PgStat_RelXactStatus *trans); /* * Determine the stats kind for a relation based on its relkind. @@ -177,7 +177,7 @@ pgstat_assoc_relation(Relation rel) kind = pgstat_get_relation_kind(rel->rd_rel->relkind); - /* find or make the PgStat_TableStatus entry, and update link */ + /* find or make the PgStat_RelationStatus entry, and update link */ rel->pgstat_info = pgstat_prep_relation_pending(kind, RelationGetRelid(rel), rel->rd_rel->relisshared); @@ -226,7 +226,7 @@ void pgstat_drop_relation(Relation rel) { int nest_level = GetCurrentTransactionNestLevel(); - PgStat_TableStatus *pgstat_info; + PgStat_RelationStatus *pgstat_info; PgStat_Kind kind = pgstat_get_relation_kind(rel->rd_rel->relkind); 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 = rel->pgstat_info; - if (pgstat_info->trans && - pgstat_info->trans->nest_level == nest_level) + if (pgstat_info->kind == PGSTAT_KIND_INDEX) + return; + + if (pgstat_info->tab.trans && + pgstat_info->tab.trans->nest_level == nest_level) { - save_truncdrop_counters(pgstat_info->trans, true); - pgstat_info->trans->tuples_inserted = 0; - pgstat_info->trans->tuples_updated = 0; - pgstat_info->trans->tuples_deleted = 0; + save_truncdrop_counters(pgstat_info->tab.trans, true); + pgstat_info->tab.trans->tuples_inserted = 0; + pgstat_info->tab.trans->tuples_updated = 0; + pgstat_info->tab.trans->tuples_deleted = 0; } } @@ -355,15 +360,15 @@ pgstat_report_analyze(Relation rel, if (pgstat_should_count_relation(rel) && rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) { - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; - for (trans = rel->pgstat_info->trans; trans; trans = trans->upper) + for (trans = rel->pgstat_info->tab.trans; trans; trans = trans->upper) { livetuples -= trans->tuples_inserted - trans->tuples_deleted; deadtuples -= trans->tuples_updated + trans->tuples_deleted; } /* count stuff inserted by already-aborted subxacts, too */ - deadtuples -= rel->pgstat_info->counts.delta_dead_tuples; + deadtuples -= rel->pgstat_info->tab.counts.delta_dead_tuples; /* Since ANALYZE's counts are estimates, we could have underflowed */ livetuples = Max(livetuples, 0); deadtuples = 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 = rel->pgstat_info; + PgStat_RelationStatus *pgstat_info = rel->pgstat_info; ensure_tabstat_xact_level(pgstat_info); - pgstat_info->trans->tuples_inserted += n; + pgstat_info->tab.trans->tuples_inserted += n; } } @@ -439,19 +444,19 @@ pgstat_count_heap_update(Relation rel, bool hot, bool newpage) if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info = rel->pgstat_info; + PgStat_RelationStatus *pgstat_info = rel->pgstat_info; ensure_tabstat_xact_level(pgstat_info); - pgstat_info->trans->tuples_updated++; + pgstat_info->tab.trans->tuples_updated++; /* * 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++; } } @@ -463,10 +468,10 @@ pgstat_count_heap_delete(Relation rel) { if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info = rel->pgstat_info; + PgStat_RelationStatus *pgstat_info = rel->pgstat_info; ensure_tabstat_xact_level(pgstat_info); - pgstat_info->trans->tuples_deleted++; + pgstat_info->tab.trans->tuples_deleted++; } } @@ -478,13 +483,13 @@ pgstat_count_truncate(Relation rel) { if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info = rel->pgstat_info; + PgStat_RelationStatus *pgstat_info = rel->pgstat_info; ensure_tabstat_xact_level(pgstat_info); - save_truncdrop_counters(pgstat_info->trans, false); - pgstat_info->trans->tuples_inserted = 0; - pgstat_info->trans->tuples_updated = 0; - pgstat_info->trans->tuples_deleted = 0; + save_truncdrop_counters(pgstat_info->tab.trans, false); + pgstat_info->tab.trans->tuples_inserted = 0; + pgstat_info->tab.trans->tuples_updated = 0; + pgstat_info->tab.trans->tuples_deleted = 0; } } @@ -501,9 +506,9 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta) { if (pgstat_should_count_relation(rel)) { - PgStat_TableStatus *pgstat_info = rel->pgstat_info; + PgStat_RelationStatus *pgstat_info = rel->pgstat_info; - pgstat_info->counts.delta_dead_tuples -= delta; + pgstat_info->tab.counts.delta_dead_tuples -= delta; } } @@ -534,9 +539,9 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid, bool *may_free) } /* - * 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 their @@ -545,113 +550,110 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid, 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 = NULL; - PgStat_TableStatus *tablestatus = NULL; + PgStat_RelXactStatus *trans; + PgStat_RelationStatus *relentry = NULL; + PgStat_RelationStatus *relstatus = NULL; entry_ref = pgstat_fetch_pending_entry(kind, MyDatabaseId, rel_id); if (!entry_ref) { entry_ref = pgstat_fetch_pending_entry(kind, InvalidOid, rel_id); if (!entry_ref) - return tablestatus; + return relstatus; } - tabentry = (PgStat_TableStatus *) entry_ref->pending; - tablestatus = palloc_object(PgStat_TableStatus); - *tablestatus = *tabentry; + relentry = (PgStat_RelationStatus *) entry_ref->pending; + relstatus = palloc_object(PgStat_RelationStatus); + *relstatus = *relentry; /* - * Reset tablestatus->trans in the copy of PgStat_TableStatus as it may - * point to a shared memory area. Its data is saved below, so removing it - * does not matter. + * For index entries, just return the copy — no transactional data. */ - tablestatus->trans = NULL; + if (kind == PGSTAT_KIND_INDEX) + return relstatus; + + /* + * Reset relstatus->tab.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. + */ + relstatus->tab.trans = NULL; /* * 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 = tabentry->trans; trans != NULL; trans = trans->upper) + for (trans = relentry->tab.trans; trans != NULL; trans = trans->upper) { - tablestatus->counts.tuples_inserted += trans->tuples_inserted; - tablestatus->counts.tuples_updated += trans->tuples_updated; - tablestatus->counts.tuples_deleted += trans->tuples_deleted; + relstatus->tab.counts.tuples_inserted += trans->tuples_inserted; + relstatus->tab.counts.tuples_updated += trans->tuples_updated; + relstatus->tab.counts.tuples_deleted += trans->tuples_deleted; } - return tablestatus; + return relstatus; } /* * Perform relation stats specific end-of-transaction work. Helper for * AtEOXact_PgStat. * - * Transfer transactional insert/update counts into the base tabstat entries. + * Transfer transactional insert/update counts into the base relstat entries. * We don't bother to free any of the transactional state, since it's all in * TopTransactionContext and will go away anyway. */ void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit) { - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; for (trans = xact_state->first; trans != NULL; trans = trans->next) { - PgStat_TableStatus *tabstat; + PgStat_RelationStatus *relstat; Assert(trans->nest_level == 1); Assert(trans->upper == NULL); - tabstat = trans->parent; - Assert(tabstat->trans == trans); + relstat = trans->parent; + Assert(relstat->tab.trans == 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 += trans->tuples_inserted; - tabstat->counts.tuples_updated += trans->tuples_updated; - tabstat->counts.tuples_deleted += trans->tuples_deleted; + relstat->tab.counts.tuples_inserted += trans->tuples_inserted; + relstat->tab.counts.tuples_updated += trans->tuples_updated; + relstat->tab.counts.tuples_deleted += trans->tuples_deleted; if (isCommit) { - tabstat->counts.truncdropped = trans->truncdropped; + relstat->tab.counts.truncdropped = trans->truncdropped; if (trans->truncdropped) { /* forget live/dead stats seen by backend thus far */ - tabstat->counts.delta_live_tuples = 0; - tabstat->counts.delta_dead_tuples = 0; + relstat->tab.counts.delta_live_tuples = 0; + relstat->tab.counts.delta_dead_tuples = 0; } /* insert adds a live tuple, delete removes one */ - tabstat->counts.delta_live_tuples += + relstat->tab.counts.delta_live_tuples += trans->tuples_inserted - trans->tuples_deleted; /* update and delete each create a dead tuple */ - tabstat->counts.delta_dead_tuples += + relstat->tab.counts.delta_dead_tuples += trans->tuples_updated + trans->tuples_deleted; /* insert, update, delete each count as one change event */ - tabstat->counts.changed_tuples += + relstat->tab.counts.changed_tuples += trans->tuples_inserted + trans->tuples_updated + trans->tuples_deleted; } else { /* inserted tuples are dead, deleted tuples are unaffected */ - tabstat->counts.delta_dead_tuples += + relstat->tab.counts.delta_dead_tuples += trans->tuples_inserted + trans->tuples_updated; /* an aborted xact generates no changed_tuple events */ } - tabstat->trans = NULL; + relstat->tab.trans = NULL; } } @@ -665,17 +667,17 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit) void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth) { - PgStat_TableXactStatus *trans; - PgStat_TableXactStatus *next_trans; + PgStat_RelXactStatus *trans; + PgStat_RelXactStatus *next_trans; for (trans = xact_state->first; trans != NULL; trans = next_trans) { - PgStat_TableStatus *tabstat; + PgStat_RelationStatus *relstat; next_trans = trans->next; Assert(trans->nest_level == nestDepth); - tabstat = trans->parent; - Assert(tabstat->trans == trans); + relstat = trans->parent; + Assert(relstat->tab.trans == trans); if (isCommit) { @@ -696,7 +698,7 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in trans->upper->tuples_updated += trans->tuples_updated; trans->upper->tuples_deleted += trans->tuples_deleted; } - tabstat->trans = trans->upper; + relstat->tab.trans = trans->upper; pfree(trans); } else @@ -720,20 +722,20 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in else { /* - * On abort, update top-level tabstat counts, then forget the + * On abort, update top-level relstat counts, then forget the * subtransaction */ /* first restore values obliterated by truncate/drop */ restore_truncdrop_counters(trans); /* count attempted actions regardless of commit/abort */ - tabstat->counts.tuples_inserted += trans->tuples_inserted; - tabstat->counts.tuples_updated += trans->tuples_updated; - tabstat->counts.tuples_deleted += trans->tuples_deleted; + relstat->tab.counts.tuples_inserted += trans->tuples_inserted; + relstat->tab.counts.tuples_updated += trans->tuples_updated; + relstat->tab.counts.tuples_deleted += trans->tuples_deleted; /* inserted tuples are dead, deleted tuples are unaffected */ - tabstat->counts.delta_dead_tuples += + relstat->tab.counts.delta_dead_tuples += trans->tuples_inserted + trans->tuples_updated; - tabstat->trans = trans->upper; + relstat->tab.trans = trans->upper; pfree(trans); } } @@ -746,17 +748,17 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state) { - PgStat_TableXactStatus *trans; + PgStat_RelXactStatus *trans; for (trans = xact_state->first; trans != NULL; trans = trans->next) { - PgStat_TableStatus *tabstat PG_USED_FOR_ASSERTS_ONLY; + PgStat_RelationStatus *relstat PG_USED_FOR_ASSERTS_ONLY; TwoPhasePgStatRecord record; Assert(trans->nest_level == 1); Assert(trans->upper == NULL); - tabstat = trans->parent; - Assert(tabstat->trans == trans); + relstat = trans->parent; + Assert(relstat->tab.trans == trans); record.tuples_inserted = trans->tuples_inserted; record.tuples_updated = trans->tuples_updated; @@ -764,8 +766,8 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state) record.inserted_pre_truncdrop = trans->inserted_pre_truncdrop; record.updated_pre_truncdrop = trans->updated_pre_truncdrop; record.deleted_pre_truncdrop = trans->deleted_pre_truncdrop; - record.id = tabstat->id; - record.shared = tabstat->shared; + record.id = relstat->tab.id; + record.shared = relstat->tab.shared; record.truncdropped = trans->truncdropped; 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; for (trans = xact_state->first; trans != NULL; trans = trans->next) { - PgStat_TableStatus *tabstat; + PgStat_RelationStatus *relstat; - tabstat = trans->parent; - tabstat->trans = NULL; + relstat = trans->parent; + relstat->tab.trans = NULL; } } @@ -805,27 +807,27 @@ pgstat_twophase_postcommit(FullTransactionId fxid, uint16 info, void *recdata, uint32 len) { TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata; - PgStat_TableStatus *pgstat_info; + PgStat_RelationStatus *pgstat_info; - /* Find or create a tabstat entry for the rel */ + /* Find or create a relstat entry for the rel */ pgstat_info = pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->id, rec->shared); /* Same math as in AtEOXact_PgStat, commit case */ - pgstat_info->counts.tuples_inserted += rec->tuples_inserted; - pgstat_info->counts.tuples_updated += rec->tuples_updated; - pgstat_info->counts.tuples_deleted += rec->tuples_deleted; - pgstat_info->counts.truncdropped = rec->truncdropped; + pgstat_info->tab.counts.tuples_inserted += rec->tuples_inserted; + pgstat_info->tab.counts.tuples_updated += rec->tuples_updated; + pgstat_info->tab.counts.tuples_deleted += rec->tuples_deleted; + pgstat_info->tab.counts.truncdropped = rec->truncdropped; if (rec->truncdropped) { /* forget live/dead stats seen by backend thus far */ - pgstat_info->counts.delta_live_tuples = 0; - pgstat_info->counts.delta_dead_tuples = 0; + pgstat_info->tab.counts.delta_live_tuples = 0; + pgstat_info->tab.counts.delta_dead_tuples = 0; } - pgstat_info->counts.delta_live_tuples += + pgstat_info->tab.counts.delta_live_tuples += rec->tuples_inserted - rec->tuples_deleted; - pgstat_info->counts.delta_dead_tuples += + pgstat_info->tab.counts.delta_dead_tuples += rec->tuples_updated + rec->tuples_deleted; - pgstat_info->counts.changed_tuples += + pgstat_info->tab.counts.changed_tuples += rec->tuples_inserted + rec->tuples_updated + rec->tuples_deleted; } @@ -841,9 +843,9 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint16 info, void *recdata, uint32 len) { TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata; - PgStat_TableStatus *pgstat_info; + PgStat_RelationStatus *pgstat_info; - /* Find or create a tabstat entry for the rel */ + /* Find or create a relstat entry for the rel */ pgstat_info = pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, rec->id, rec->shared); /* Same math as in AtEOXact_PgStat, abort case */ @@ -853,10 +855,10 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint16 info, rec->tuples_updated = rec->updated_pre_truncdrop; rec->tuples_deleted = rec->deleted_pre_truncdrop; } - pgstat_info->counts.tuples_inserted += rec->tuples_inserted; - pgstat_info->counts.tuples_updated += rec->tuples_updated; - pgstat_info->counts.tuples_deleted += rec->tuples_deleted; - pgstat_info->counts.delta_dead_tuples += + pgstat_info->tab.counts.tuples_inserted += rec->tuples_inserted; + pgstat_info->tab.counts.tuples_updated += rec->tuples_updated; + pgstat_info->tab.counts.tuples_deleted += rec->tuples_deleted; + pgstat_info->tab.counts.delta_dead_tuples += rec->tuples_inserted + rec->tuples_updated; } @@ -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 */ dboid = entry_ref->shared_entry->key.dboid; - lstats = (PgStat_TableStatus *) entry_ref->pending; + lstats = (PgStat_RelationStatus *) entry_ref->pending; shtabstats = (PgStatShared_Relation *) 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, + if (pg_memory_is_all_zeros(&lstats->tab.counts, sizeof(struct PgStat_TableCounts))) return true; @@ -896,35 +898,35 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) /* add the values to the shared entry. */ tabentry = &shtabstats->stats; - tabentry->numscans += lstats->counts.numscans; - if (lstats->counts.numscans) + tabentry->numscans += lstats->tab.counts.numscans; + if (lstats->tab.counts.numscans) { TimestampTz t = GetCurrentTransactionStopTimestamp(); if (t > tabentry->lastscan) tabentry->lastscan = t; } - tabentry->tuples_returned += lstats->counts.tuples_returned; - tabentry->tuples_fetched += lstats->counts.tuples_fetched; - tabentry->tuples_inserted += lstats->counts.tuples_inserted; - tabentry->tuples_updated += lstats->counts.tuples_updated; - tabentry->tuples_deleted += lstats->counts.tuples_deleted; - tabentry->tuples_hot_updated += lstats->counts.tuples_hot_updated; - tabentry->tuples_newpage_updated += lstats->counts.tuples_newpage_updated; + tabentry->tuples_returned += lstats->tab.counts.tuples_returned; + tabentry->tuples_fetched += lstats->tab.counts.tuples_fetched; + tabentry->tuples_inserted += lstats->tab.counts.tuples_inserted; + tabentry->tuples_updated += lstats->tab.counts.tuples_updated; + tabentry->tuples_deleted += lstats->tab.counts.tuples_deleted; + tabentry->tuples_hot_updated += lstats->tab.counts.tuples_hot_updated; + tabentry->tuples_newpage_updated += lstats->tab.counts.tuples_newpage_updated; /* * If table was truncated/dropped, first reset the live/dead counters. */ - if (lstats->counts.truncdropped) + if (lstats->tab.counts.truncdropped) { tabentry->live_tuples = 0; tabentry->dead_tuples = 0; tabentry->ins_since_vacuum = 0; } - tabentry->live_tuples += lstats->counts.delta_live_tuples; - tabentry->dead_tuples += lstats->counts.delta_dead_tuples; - tabentry->mod_since_analyze += lstats->counts.changed_tuples; + tabentry->live_tuples += lstats->tab.counts.delta_live_tuples; + tabentry->dead_tuples += lstats->tab.counts.delta_dead_tuples; + tabentry->mod_since_analyze += lstats->tab.counts.changed_tuples; /* * 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 += lstats->counts.tuples_inserted; + tabentry->ins_since_vacuum += lstats->tab.counts.tuples_inserted; - tabentry->blocks_fetched += lstats->counts.blocks_fetched; - tabentry->blocks_hit += lstats->counts.blocks_hit; + tabentry->blocks_fetched += lstats->tab.counts.blocks_fetched; + tabentry->blocks_hit += lstats->tab.counts.blocks_hit; /* Clamp live_tuples in case of negative delta_live_tuples */ tabentry->live_tuples = Max(tabentry->live_tuples, 0); @@ -947,13 +949,13 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) /* The entry was successfully flushed, add the same to database stats */ dbentry = pgstat_prep_database_pending(dboid); - dbentry->tuples_returned += lstats->counts.tuples_returned; - dbentry->tuples_fetched += lstats->counts.tuples_fetched; - dbentry->tuples_inserted += lstats->counts.tuples_inserted; - dbentry->tuples_updated += lstats->counts.tuples_updated; - dbentry->tuples_deleted += lstats->counts.tuples_deleted; - dbentry->blocks_fetched += lstats->counts.blocks_fetched; - dbentry->blocks_hit += lstats->counts.blocks_hit; + dbentry->tuples_returned += lstats->tab.counts.tuples_returned; + dbentry->tuples_fetched += lstats->tab.counts.tuples_fetched; + dbentry->tuples_inserted += lstats->tab.counts.tuples_inserted; + dbentry->tuples_updated += lstats->tab.counts.tuples_updated; + dbentry->tuples_deleted += lstats->tab.counts.tuples_deleted; + dbentry->blocks_fetched += lstats->tab.counts.blocks_fetched; + dbentry->blocks_hit += lstats->tab.counts.blocks_hit; return true; } @@ -961,7 +963,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref) { - PgStat_TableStatus *pending = (PgStat_TableStatus *) entry_ref->pending; + PgStat_RelationStatus *pending = (PgStat_RelationStatus *) entry_ref->pending; if (pending->relation) pgstat_unlink_relation(pending->relation); @@ -974,21 +976,25 @@ pgstat_relation_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts) } /* - * 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 created 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; entry_ref = pgstat_prep_pending_entry(kind, isshared ? InvalidOid : MyDatabaseId, rel_id, NULL); pending = entry_ref->pending; - pending->id = rel_id; - pending->shared = isshared; + pending->kind = kind; + if (kind != PGSTAT_KIND_INDEX) + { + pending->tab.id = rel_id; + pending->tab.shared = isshared; + } return pending; } @@ -997,10 +1003,10 @@ pgstat_prep_relation_pending(PgStat_Kind kind, Oid rel_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; /* * 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_info, int nest_level) xact_state = pgstat_get_xact_stack_level(nest_level); /* Now make a per-table stack entry */ - trans = (PgStat_TableXactStatus *) + trans = (PgStat_RelXactStatus *) MemoryContextAllocZero(TopTransactionContext, - sizeof(PgStat_TableXactStatus)); + sizeof(PgStat_RelXactStatus)); trans->nest_level = nest_level; - trans->upper = pgstat_info->trans; + trans->upper = pgstat_info->tab.trans; trans->parent = pgstat_info; trans->next = xact_state->first; xact_state->first = trans; - pgstat_info->trans = trans; + pgstat_info->tab.trans = trans; } /* * 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 = GetCurrentTransactionNestLevel(); - if (pgstat_info->trans == NULL || - pgstat_info->trans->nest_level != nest_level) + if (pgstat_info->tab.trans == NULL || + pgstat_info->tab.trans->nest_level != nest_level) add_tabstat_xact_level(pgstat_info, nest_level); } @@ -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/pgstatfuncs.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 = PG_GETARG_OID(0); \ int64 result; \ - PgStat_TableStatus *tabentry; \ + PgStat_RelationStatus *tabentry; \ \ - if ((tabentry = find_tabstat_entry(relid)) == NULL) \ + if ((tabentry = find_relstat_entry_kind(PGSTAT_KIND_RELATION, \ + relid)) == NULL) \ result = 0; \ else \ - result = (int64) (tabentry->counts.stat); \ + result = (int64) (tabentry->tab.counts.stat); \ \ PG_RETURN_INT64(result); \ } @@ -1977,13 +1978,13 @@ CppConcat(pg_stat_get_xact_idx_,stat)(PG_FUNCTION_ARGS) \ { \ Oid relid = PG_GETARG_OID(0); \ int64 result; \ - PgStat_TableStatus *tabentry; \ + PgStat_RelationStatus *tabentry; \ \ - tabentry = find_tabstat_entry_kind(PGSTAT_KIND_INDEX, relid); \ + tabentry = find_relstat_entry_kind(PGSTAT_KIND_INDEX, relid); \ if (!tabentry) \ result = 0; \ else \ - result = (int64) (tabentry->counts.stat); \ + result = (int64) (tabentry->idx.stat); \ \ PG_RETURN_INT64(result); \ } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 19c4ff6e75e1..9abbaeab4a9d 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -2760,7 +2760,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.list index 792e62a2518d..9ecb68622fd7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2348,6 +2348,7 @@ PgStat_FunctionCallUsage PgStat_FunctionCounts PgStat_HashKey PgStat_IO +PgStat_IndexCounts PgStat_KindInfo PgStat_LocalState PgStat_Lock @@ -2355,6 +2356,8 @@ PgStat_LockEntry PgStat_PendingDroppedStatsItem PgStat_PendingIO PgStat_PendingLock +PgStat_RelXactStatus +PgStat_RelationStatus PgStat_SLRUStats PgStat_ShmemControl PgStat_Snapshot @@ -2370,8 +2373,6 @@ PgStat_StatTabEntry PgStat_StatsFileOp PgStat_SubXactStatus PgStat_TableCounts -PgStat_TableStatus -PgStat_TableXactStatus PgStat_WalCounters PgStat_WalStats PgXmlErrorContext -- 2.55.0
signature.asc
Description: PGP signature
