Hi hackers,
Please find attached a patch proposal to split index and table
statistics into different types of stats.
This idea has been proposed by Andres in a couple of threads, see [1]
and [2].
To sum up:
We currently track index and table types of statistics in the same
format (so that a number of the "columns" in index stats are currently
unused) and we rename column in views etc to make them somewhat sensible.
So that the immediate benefits to $SUBJECT are:
- have reasonable names for the fields
- shrink the current memory usage
The attached patch proposal:
- renames PGSTAT_KIND_RELATION to PGSTAT_KIND_TABLE
- creates a new PGSTAT_KIND_INDEX
- creates new macros: pgstat_count_index_fetch(),
pgstat_count_index_scan(), pgstat_count_index_tuples(),
pgstat_count_index_buffer_read() and pgstat_count_index_buffer_hit() to
increment the indexes related stats
- creates new SQL callable functions dedicated to the indexes that are
used in system_views.sql
It also adds basic tests in src/test/regress/sql/stats.sql for toast and
partitions (we may want to create a dedicated patch for those additional
tests though).
The fields renaming has not been done to ease the reading of this patch
(I think it would be better to create a dedicated patch for the renaming
once the split is done).
I'm adding a new CF entry for this patch.
Looking forward to your feedback,
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
[1]:
https://www.postgresql.org/message-id/flat/20221019181930.bx73kul4nbiftr65%40awork3.anarazel.de
[2]:
https://www.postgresql.org/message-id/20220818195124.c7ipzf6c5v7vxymc%40awork3.anarazel.de
diff --git a/src/backend/access/index/indexam.c
b/src/backend/access/index/indexam.c
index fe80b8b0ba..a1defc6838 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -582,7 +582,7 @@ index_fetch_heap(IndexScanDesc scan, TupleTableSlot *slot)
&scan->xs_heap_continue, &all_dead);
if (found)
- pgstat_count_heap_fetch(scan->indexRelation);
+ pgstat_count_index_fetch(scan->indexRelation);
/*
* If we scanned a whole HOT chain and found only dead tuples, tell
index
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 5b49cc5a09..8a715db82e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1853,7 +1853,7 @@ heap_drop_with_catalog(Oid relid)
RelationDropStorage(rel);
/* ensure that stats are dropped if transaction commits */
- pgstat_drop_relation(rel);
+ pgstat_drop_heap(rel);
/*
* Close relcache entry, but *keep* AccessExclusiveLock on the relation
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 61f1d3926a..28b94fef7f 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1752,7 +1752,7 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId,
const char *oldName)
changeDependenciesOn(RelationRelationId, oldIndexId, newIndexId);
/* copy over statistics from old to new index */
- pgstat_copy_relation_stats(newClassRel, oldClassRel);
+ pgstat_copy_index_stats(newClassRel, oldClassRel);
/* Copy data of pg_statistic from the old index to the new one */
CopyStatistics(oldIndexId, newIndexId);
@@ -2326,7 +2326,7 @@ index_drop(Oid indexId, bool concurrent, bool
concurrent_lock_mode)
RelationDropStorage(userIndexRelation);
/* ensure that stats are dropped if transaction commits */
- pgstat_drop_relation(userIndexRelation);
+ pgstat_drop_index(userIndexRelation);
/*
* Close and flush the index's relcache entry, to ensure relcache
doesn't
diff --git a/src/backend/catalog/system_views.sql
b/src/backend/catalog/system_views.sql
index 2d8104b090..e92e50edf7 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,13 +655,13 @@ CREATE VIEW pg_stat_all_tables AS
C.oid AS relid,
N.nspname AS schemaname,
C.relname AS relname,
- 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,
+ pg_stat_get_heap_numscans(C.oid) AS seq_scan,
+ pg_stat_get_heap_lastscan(C.oid) AS last_seq_scan,
+ pg_stat_get_heap_tuples_returned(C.oid) AS seq_tup_read,
+ sum(pg_stat_get_index_numscans(I.indexrelid))::bigint AS idx_scan,
+ max(pg_stat_get_index_lastscan(I.indexrelid)) AS last_idx_scan,
+ sum(pg_stat_get_index_tuples_fetched(I.indexrelid))::bigint +
+ pg_stat_get_heap_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,
@@ -689,9 +689,9 @@ CREATE VIEW pg_stat_xact_all_tables AS
C.oid AS relid,
N.nspname AS schemaname,
C.relname AS relname,
- pg_stat_get_xact_numscans(C.oid) AS seq_scan,
+ pg_stat_get_heap_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_index_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,
pg_stat_get_xact_tuples_inserted(C.oid) AS n_tup_ins,
@@ -729,31 +729,31 @@ CREATE VIEW pg_statio_all_tables AS
C.oid AS relid,
N.nspname AS schemaname,
C.relname AS relname,
- pg_stat_get_blocks_fetched(C.oid) -
- pg_stat_get_blocks_hit(C.oid) AS heap_blks_read,
- pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit,
+ pg_stat_get_heap_blocks_fetched(C.oid) -
+ pg_stat_get_heap_blocks_hit(C.oid) AS heap_blks_read,
+ pg_stat_get_heap_blocks_hit(C.oid) AS heap_blks_hit,
I.idx_blks_read AS idx_blks_read,
I.idx_blks_hit AS idx_blks_hit,
- pg_stat_get_blocks_fetched(T.oid) -
- pg_stat_get_blocks_hit(T.oid) AS toast_blks_read,
- pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit,
+ pg_stat_get_heap_blocks_fetched(T.oid) -
+ pg_stat_get_heap_blocks_hit(T.oid) AS toast_blks_read,
+ pg_stat_get_heap_blocks_hit(T.oid) AS toast_blks_hit,
X.idx_blks_read AS tidx_blks_read,
X.idx_blks_hit AS tidx_blks_hit
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(indexrelid) -
- pg_stat_get_blocks_hit(indexrelid))::bigint
+ SELECT sum(pg_stat_get_index_blocks_fetched(indexrelid) -
+ pg_stat_get_index_blocks_hit(indexrelid))::bigint
AS idx_blks_read,
- sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+ sum(pg_stat_get_index_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_index_blocks_fetched(indexrelid) -
+ pg_stat_get_index_blocks_hit(indexrelid))::bigint
AS idx_blks_read,
- sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+ sum(pg_stat_get_index_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');
@@ -775,10 +775,10 @@ 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_index_numscans(I.oid) AS idx_scan,
+ pg_stat_get_index_lastscan(I.oid) AS last_idx_scan,
+ pg_stat_get_index_tuples_returned(I.oid) AS idx_tup_read,
+ pg_stat_get_index_tuples_fetched(I.oid) AS idx_tup_fetch
FROM pg_class C JOIN
pg_index X ON C.oid = X.indrelid JOIN
pg_class I ON I.oid = X.indexrelid
@@ -802,9 +802,9 @@ 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_index_blocks_fetched(I.oid) -
+ pg_stat_get_index_blocks_hit(I.oid) AS idx_blks_read,
+ pg_stat_get_index_blocks_hit(I.oid) AS idx_blks_hit
FROM pg_class C JOIN
pg_index X ON C.oid = X.indrelid JOIN
pg_class I ON I.oid = X.indexrelid
@@ -826,9 +826,9 @@ CREATE VIEW pg_statio_all_sequences AS
C.oid AS relid,
N.nspname AS schemaname,
C.relname AS relname,
- pg_stat_get_blocks_fetched(C.oid) -
- pg_stat_get_blocks_hit(C.oid) AS blks_read,
- pg_stat_get_blocks_hit(C.oid) AS blks_hit
+ pg_stat_get_index_blocks_fetched(C.oid) -
+ pg_stat_get_heap_blocks_hit(C.oid) AS blks_read,
+ pg_stat_get_heap_blocks_hit(C.oid) AS blks_hit
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE C.relkind = 'S';
diff --git a/src/backend/storage/buffer/bufmgr.c
b/src/backend/storage/buffer/bufmgr.c
index 73d30bf619..2e3b982aae 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -776,11 +776,19 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum,
BlockNumber blockNum,
* Read the buffer, and update pgstat counters to reflect a cache hit or
* miss.
*/
- pgstat_count_buffer_read(reln);
+ if (reln->rd_rel->relkind == RELKIND_INDEX)
+ pgstat_count_index_buffer_read(reln);
+ else
+ pgstat_count_heap_buffer_read(reln);
buf = ReadBuffer_common(RelationGetSmgr(reln),
reln->rd_rel->relpersistence,
forkNum, blockNum,
mode, strategy, &hit);
if (hit)
- pgstat_count_buffer_hit(reln);
+ {
+ if (reln->rd_rel->relkind == RELKIND_INDEX)
+ pgstat_count_index_buffer_hit(reln);
+ else
+ pgstat_count_heap_buffer_hit(reln);
+ }
return buf;
}
diff --git a/src/backend/utils/activity/pgstat.c
b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..9349d7f00c 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -269,18 +269,32 @@ static const PgStat_KindInfo
pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
.reset_timestamp_cb = pgstat_database_reset_timestamp_cb,
},
- [PGSTAT_KIND_RELATION] = {
- .name = "relation",
+ [PGSTAT_KIND_TABLE] = {
+ .name = "table",
.fixed_amount = false,
- .shared_size = sizeof(PgStatShared_Relation),
- .shared_data_off = offsetof(PgStatShared_Relation, stats),
- .shared_data_len = sizeof(((PgStatShared_Relation *) 0)->stats),
+ .shared_size = sizeof(PgStatShared_Table),
+ .shared_data_off = offsetof(PgStatShared_Table, stats),
+ .shared_data_len = sizeof(((PgStatShared_Table *) 0)->stats),
.pending_size = sizeof(PgStat_TableStatus),
- .flush_pending_cb = pgstat_relation_flush_cb,
- .delete_pending_cb = pgstat_relation_delete_pending_cb,
+ .flush_pending_cb = pgstat_table_flush_cb,
+ .delete_pending_cb = pgstat_table_delete_pending_cb,
+ },
+
+ [PGSTAT_KIND_INDEX] = {
+ .name = "index",
+
+ .fixed_amount = false,
+
+ .shared_size = sizeof(PgStatShared_Index),
+ .shared_data_off = offsetof(PgStatShared_Index, stats),
+ .shared_data_len = sizeof(((PgStatShared_Index *) 0)->stats),
+ .pending_size = sizeof(PgStat_IndexStatus),
+
+ .flush_pending_cb = pgstat_index_flush_cb,
+ .delete_pending_cb = pgstat_index_delete_pending_cb,
},
[PGSTAT_KIND_FUNCTION] = {
diff --git a/src/backend/utils/activity/pgstat_relation.c
b/src/backend/utils/activity/pgstat_relation.c
index 55a355f583..5d30b4c242 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -43,35 +43,35 @@ typedef struct TwoPhasePgStatRecord
} TwoPhasePgStatRecord;
-static PgStat_TableStatus *pgstat_prep_relation_pending(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 PgStat_TableStatus *pgstat_prep_table_pending(Oid rel_id, bool
isshared);
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool
isshared);
+static void add_tabstat_xact_level(PgStat_TableStatus *pgstattab_info, int
nest_level);
+static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstattab_info);
static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool
is_drop);
static void restore_truncdrop_counters(PgStat_TableXactStatus *trans);
-
/*
- * Copy stats between relations. This is used for things like REINDEX
+ * Copy stats between indexes. This is used for things like REINDEX
* CONCURRENTLY.
*/
void
-pgstat_copy_relation_stats(Relation dst, Relation src)
+pgstat_copy_index_stats(Relation dst, Relation src)
{
- PgStat_StatTabEntry *srcstats;
- PgStatShared_Relation *dstshstats;
+ PgStat_StatIndEntry *srcstats;
+ PgStatShared_Index *dstshstats;
PgStat_EntryRef *dst_ref;
- srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
+ srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
RelationGetRelid(src));
if (!srcstats)
return;
- dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
RelationGetRelid(dst),
false);
- dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
+ dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
dstshstats->stats = *srcstats;
pgstat_unlock_entry(dst_ref);
@@ -81,8 +81,9 @@ pgstat_copy_relation_stats(Relation dst, Relation src)
* Initialize a relcache entry to count access statistics. Called whenever a
* relation is opened.
*
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
- * when the relcache entry is made; thereafter it is long-lived data.
+ * We assume that a relcache entry's pgstattab_info and pgstatind_info fields
+ * are zeroed by relcache.c when the relcache entry is made; thereafter it is
+ * long-lived data.
*
* This does not create a reference to a stats entry in shared memory, nor
* allocate memory for the pending stats. That happens in
@@ -99,18 +100,20 @@ pgstat_init_relation(Relation rel)
if (!RELKIND_HAS_STORAGE(relkind) && relkind !=
RELKIND_PARTITIONED_TABLE)
{
rel->pgstat_enabled = false;
- rel->pgstat_info = NULL;
+ rel->pgstattab_info = NULL;
+ rel->pgstatind_info = NULL;
return;
}
if (!pgstat_track_counts)
{
- if (rel->pgstat_info)
+ if (rel->pgstattab_info != NULL || rel->pgstatind_info != NULL)
pgstat_unlink_relation(rel);
/* We're not counting at all */
rel->pgstat_enabled = false;
- rel->pgstat_info = NULL;
+ rel->pgstattab_info = NULL;
+ rel->pgstatind_info = NULL;
return;
}
@@ -118,10 +121,10 @@ pgstat_init_relation(Relation rel)
}
/*
- * Prepare for statistics for this relation to be collected.
+ * Prepare for statistics for this table to be collected.
*
* This ensures we have a reference to the stats entry before stats can be
- * generated. That is important because a relation drop in another connection
+ * generated. That is important because a table drop in another connection
* could otherwise lead to the stats entry being dropped, which then later
* would get recreated when flushing stats.
*
@@ -129,20 +132,48 @@ pgstat_init_relation(Relation rel)
* relcache entries to be opened without ever getting stats reported.
*/
void
-pgstat_assoc_relation(Relation rel)
+pgstat_assoc_table(Relation rel)
{
Assert(rel->pgstat_enabled);
- Assert(rel->pgstat_info == NULL);
+ Assert(rel->pgstattab_info == NULL);
/* Else find or make the PgStat_TableStatus entry, and update link */
- rel->pgstat_info = pgstat_prep_relation_pending(RelationGetRelid(rel),
+ rel->pgstattab_info = pgstat_prep_table_pending(RelationGetRelid(rel),
+
rel->rd_rel->relisshared);
+
+ /* don't allow link a stats to multiple relcache entries */
+ Assert(rel->pgstattab_info->relation == NULL);
+
+ /* mark this relation as the owner */
+ rel->pgstattab_info->relation = rel;
+}
+
+/*
+ * Prepare for statistics for this index to be collected.
+ *
+ * This ensures we have a reference to the stats entry before stats can be
+ * generated. That is important because an index drop in another
+ * connection could otherwise lead to the stats entry being dropped, which then
+ * later would get recreated when flushing stats.
+ *
+ * This is separate from pgstat_init_relation() as it is not uncommon for
+ * relcache entries to be opened without ever getting stats reported.
+ */
+void
+pgstat_assoc_index(Relation rel)
+{
+ Assert(rel->pgstat_enabled);
+ Assert(rel->pgstatind_info == NULL);
+
+ /* Else find or make the PgStat_IndexStatus entry, and update link */
+ rel->pgstatind_info = pgstat_prep_index_pending(RelationGetRelid(rel),
rel->rd_rel->relisshared);
/* don't allow link a stats to multiple relcache entries */
- Assert(rel->pgstat_info->relation == NULL);
+ Assert(rel->pgstatind_info->relation == NULL);
/* mark this relation as the owner */
- rel->pgstat_info->relation = rel;
+ rel->pgstatind_info->relation = rel;
}
/*
@@ -152,14 +183,25 @@ pgstat_assoc_relation(Relation rel)
void
pgstat_unlink_relation(Relation rel)
{
- /* remove the link to stats info if any */
- if (rel->pgstat_info == NULL)
+
+ if (rel->pgstatind_info == NULL && rel->pgstattab_info == NULL)
return;
- /* link sanity check */
- Assert(rel->pgstat_info->relation == rel);
- rel->pgstat_info->relation = NULL;
- rel->pgstat_info = NULL;
+ /* link sanity check for the table stats */
+ if (rel->pgstattab_info)
+ {
+ Assert(rel->pgstattab_info->relation == rel);
+ rel->pgstattab_info->relation = NULL;
+ rel->pgstattab_info = NULL;
+ }
+
+ /* link sanity check for the index stats */
+ if (rel->pgstatind_info)
+ {
+ Assert(rel->pgstatind_info->relation == rel);
+ rel->pgstatind_info->relation = NULL;
+ rel->pgstatind_info = NULL;
+ }
}
/*
@@ -168,39 +210,55 @@ pgstat_unlink_relation(Relation rel)
void
pgstat_create_relation(Relation rel)
{
- pgstat_create_transactional(PGSTAT_KIND_RELATION,
-
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-
RelationGetRelid(rel));
+ if (rel->rd_rel->relkind == RELKIND_INDEX)
+ pgstat_create_transactional(PGSTAT_KIND_INDEX,
+
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+
RelationGetRelid(rel));
+ else
+ pgstat_create_transactional(PGSTAT_KIND_TABLE,
+
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+
RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+ pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+
RelationGetRelid(rel));
}
/*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
*/
void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_heap(Relation rel)
{
int nest_level = GetCurrentTransactionNestLevel();
- PgStat_TableStatus *pgstat_info;
+ PgStat_TableStatus *pgstattab_info;
- pgstat_drop_transactional(PGSTAT_KIND_RELATION,
+ pgstat_drop_transactional(PGSTAT_KIND_TABLE,
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
RelationGetRelid(rel));
- if (!pgstat_should_count_relation(rel))
+ if (!pgstat_should_count_table(rel))
return;
/*
* Transactionally set counters to 0. That ensures that accesses to
* pg_stat_xact_all_tables inside the transaction show 0.
*/
- pgstat_info = rel->pgstat_info;
- if (pgstat_info->trans &&
- pgstat_info->trans->nest_level == nest_level)
+ pgstattab_info = rel->pgstattab_info;
+ if (pgstattab_info->trans &&
+ pgstattab_info->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(pgstattab_info->trans, true);
+ pgstattab_info->trans->tuples_inserted = 0;
+ pgstattab_info->trans->tuples_updated = 0;
+ pgstattab_info->trans->tuples_deleted = 0;
}
}
@@ -212,7 +270,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
PgStat_Counter livetuples,
PgStat_Counter deadtuples)
{
PgStat_EntryRef *entry_ref;
- PgStatShared_Relation *shtabentry;
+ PgStatShared_Table *shtabentry;
PgStat_StatTabEntry *tabentry;
Oid dboid = (shared ? InvalidOid : MyDatabaseId);
TimestampTz ts;
@@ -224,10 +282,10 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
ts = GetCurrentTimestamp();
/* block acquiring lock for the same reason as pgstat_report_autovac()
*/
- entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_TABLE,
dboid, tableoid, false);
- shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ shtabentry = (PgStatShared_Table *) entry_ref->shared_stats;
tabentry = &shtabentry->stats;
tabentry->n_live_tuples = livetuples;
@@ -271,7 +329,7 @@ pgstat_report_analyze(Relation rel,
bool resetcounter)
{
PgStat_EntryRef *entry_ref;
- PgStatShared_Relation *shtabentry;
+ PgStatShared_Table *shtabentry;
PgStat_StatTabEntry *tabentry;
Oid dboid = (rel->rd_rel->relisshared ? InvalidOid
: MyDatabaseId);
@@ -290,31 +348,31 @@ pgstat_report_analyze(Relation rel,
*
* Waste no time on partitioned tables, though.
*/
- if (pgstat_should_count_relation(rel) &&
+ if (pgstat_should_count_table(rel) &&
rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
{
PgStat_TableXactStatus *trans;
- for (trans = rel->pgstat_info->trans; trans; trans =
trans->upper)
+ for (trans = rel->pgstattab_info->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->t_counts.t_delta_dead_tuples;
+ deadtuples -= rel->pgstattab_info->t_counts.t_delta_dead_tuples;
/* Since ANALYZE's counts are estimates, we could have
underflowed */
livetuples = Max(livetuples, 0);
deadtuples = Max(deadtuples, 0);
}
/* block acquiring lock for the same reason as pgstat_report_autovac()
*/
- entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, dboid,
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_TABLE, dboid,
RelationGetRelid(rel),
false);
/* can't get dropped while accessed */
Assert(entry_ref != NULL && entry_ref->shared_stats != NULL);
- shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ shtabentry = (PgStatShared_Table *) entry_ref->shared_stats;
tabentry = &shtabentry->stats;
tabentry->n_live_tuples = livetuples;
@@ -348,12 +406,12 @@ pgstat_report_analyze(Relation rel,
void
pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
{
- if (pgstat_should_count_relation(rel))
+ if (pgstat_should_count_table(rel))
{
- PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+ PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
- ensure_tabstat_xact_level(pgstat_info);
- pgstat_info->trans->tuples_inserted += n;
+ ensure_tabstat_xact_level(pgstattab_info);
+ pgstattab_info->trans->tuples_inserted += n;
}
}
@@ -363,16 +421,16 @@ pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
void
pgstat_count_heap_update(Relation rel, bool hot)
{
- if (pgstat_should_count_relation(rel))
+ if (pgstat_should_count_table(rel))
{
- PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+ PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
- ensure_tabstat_xact_level(pgstat_info);
- pgstat_info->trans->tuples_updated++;
+ ensure_tabstat_xact_level(pgstattab_info);
+ pgstattab_info->trans->tuples_updated++;
/* t_tuples_hot_updated is nontransactional, so just advance it
*/
if (hot)
- pgstat_info->t_counts.t_tuples_hot_updated++;
+ pgstattab_info->t_counts.t_tuples_hot_updated++;
}
}
@@ -382,12 +440,12 @@ pgstat_count_heap_update(Relation rel, bool hot)
void
pgstat_count_heap_delete(Relation rel)
{
- if (pgstat_should_count_relation(rel))
+ if (pgstat_should_count_table(rel))
{
- PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+ PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
- ensure_tabstat_xact_level(pgstat_info);
- pgstat_info->trans->tuples_deleted++;
+ ensure_tabstat_xact_level(pgstattab_info);
+ pgstattab_info->trans->tuples_deleted++;
}
}
@@ -397,15 +455,15 @@ pgstat_count_heap_delete(Relation rel)
void
pgstat_count_truncate(Relation rel)
{
- if (pgstat_should_count_relation(rel))
+ if (pgstat_should_count_table(rel))
{
- PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+ PgStat_TableStatus *pgstattab_info = rel->pgstattab_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;
+ ensure_tabstat_xact_level(pgstattab_info);
+ save_truncdrop_counters(pgstattab_info->trans, false);
+ pgstattab_info->trans->tuples_inserted = 0;
+ pgstattab_info->trans->tuples_updated = 0;
+ pgstattab_info->trans->tuples_deleted = 0;
}
}
@@ -420,11 +478,11 @@ pgstat_count_truncate(Relation rel)
void
pgstat_update_heap_dead_tuples(Relation rel, int delta)
{
- if (pgstat_should_count_relation(rel))
+ if (pgstat_should_count_table(rel))
{
- PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+ PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
- pgstat_info->t_counts.t_delta_dead_tuples -= delta;
+ pgstattab_info->t_counts.t_delta_dead_tuples -= delta;
}
}
@@ -460,7 +518,42 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid)
Oid dboid = (shared ? InvalidOid : MyDatabaseId);
return (PgStat_StatTabEntry *)
- pgstat_fetch_entry(PGSTAT_KIND_RELATION, dboid, reloid);
+ pgstat_fetch_entry(PGSTAT_KIND_TABLE, dboid, reloid);
+}
+
+/*
+ * 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_StatIndEntry *
+pgstat_fetch_stat_indentry(Oid relid)
+{
+ PgStat_StatIndEntry *indentry;
+
+ indentry = pgstat_fetch_stat_indentry_ext(false, relid);
+ if (indentry != NULL)
+ return indentry;
+
+ /*
+ * If we didn't find it, maybe it's a shared index.
+ */
+ indentry = pgstat_fetch_stat_indentry_ext(true, relid);
+ return indentry;
+}
+
+/*
+ * More efficient version of pgstat_fetch_stat_indentry(), allowing to specify
+ * whether the to-be-accessed index is shared or not.
+ */
+PgStat_StatIndEntry *
+pgstat_fetch_stat_indentry_ext(bool shared, Oid reloid)
+{
+ Oid dboid = (shared ? InvalidOid : MyDatabaseId);
+
+ return (PgStat_StatIndEntry *)
+ pgstat_fetch_entry(PGSTAT_KIND_INDEX, dboid, reloid);
}
/*
@@ -476,9 +569,31 @@ find_tabstat_entry(Oid rel_id)
{
PgStat_EntryRef *entry_ref;
- entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION,
MyDatabaseId, rel_id);
+ entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_TABLE, MyDatabaseId,
rel_id);
+ if (!entry_ref)
+ entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_TABLE,
InvalidOid, rel_id);
+
+ if (entry_ref)
+ return entry_ref->pending;
+ return NULL;
+}
+
+/*
+ * find any existing PgStat_IndexStatus entry for rel
+ *
+ * Find any existing PgStat_IndexStatus entry for rel_id in the current
+ * database. If not found, try finding from shared indexes.
+ *
+ * If no entry found, return NULL, don't create a new one
+ */
+PgStat_IndexStatus *
+find_indstat_entry(Oid rel_id)
+{
+ PgStat_EntryRef *entry_ref;
+
+ entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_INDEX, MyDatabaseId,
rel_id);
if (!entry_ref)
- entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION,
InvalidOid, rel_id);
+ entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_INDEX,
InvalidOid, rel_id);
if (entry_ref)
return entry_ref->pending;
@@ -694,27 +809,27 @@ pgstat_twophase_postcommit(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
- PgStat_TableStatus *pgstat_info;
+ PgStat_TableStatus *pgstattab_info;
/* Find or create a tabstat entry for the rel */
- pgstat_info = pgstat_prep_relation_pending(rec->t_id, rec->t_shared);
+ pgstattab_info = pgstat_prep_table_pending(rec->t_id, rec->t_shared);
/* Same math as in AtEOXact_PgStat, commit case */
- pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
- pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
- pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
- pgstat_info->t_counts.t_truncdropped = rec->t_truncdropped;
+ pgstattab_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+ pgstattab_info->t_counts.t_tuples_updated += rec->tuples_updated;
+ pgstattab_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+ pgstattab_info->t_counts.t_truncdropped = rec->t_truncdropped;
if (rec->t_truncdropped)
{
/* forget live/dead stats seen by backend thus far */
- pgstat_info->t_counts.t_delta_live_tuples = 0;
- pgstat_info->t_counts.t_delta_dead_tuples = 0;
+ pgstattab_info->t_counts.t_delta_live_tuples = 0;
+ pgstattab_info->t_counts.t_delta_dead_tuples = 0;
}
- pgstat_info->t_counts.t_delta_live_tuples +=
+ pgstattab_info->t_counts.t_delta_live_tuples +=
rec->tuples_inserted - rec->tuples_deleted;
- pgstat_info->t_counts.t_delta_dead_tuples +=
+ pgstattab_info->t_counts.t_delta_dead_tuples +=
rec->tuples_updated + rec->tuples_deleted;
- pgstat_info->t_counts.t_changed_tuples +=
+ pgstattab_info->t_counts.t_changed_tuples +=
rec->tuples_inserted + rec->tuples_updated +
rec->tuples_deleted;
}
@@ -730,10 +845,10 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
- PgStat_TableStatus *pgstat_info;
+ PgStat_TableStatus *pgstattab_info;
/* Find or create a tabstat entry for the rel */
- pgstat_info = pgstat_prep_relation_pending(rec->t_id, rec->t_shared);
+ pgstattab_info = pgstat_prep_table_pending(rec->t_id, rec->t_shared);
/* Same math as in AtEOXact_PgStat, abort case */
if (rec->t_truncdropped)
@@ -742,10 +857,10 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
rec->tuples_updated = rec->updated_pre_truncdrop;
rec->tuples_deleted = rec->deleted_pre_truncdrop;
}
- pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
- pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
- pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
- pgstat_info->t_counts.t_delta_dead_tuples +=
+ pgstattab_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+ pgstattab_info->t_counts.t_tuples_updated += rec->tuples_updated;
+ pgstattab_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+ pgstattab_info->t_counts.t_delta_dead_tuples +=
rec->tuples_inserted + rec->tuples_updated;
}
@@ -759,22 +874,21 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
* entry when successfully flushing.
*/
bool
-pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
+pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
{
static const PgStat_TableCounts all_zeroes;
Oid dboid;
PgStat_TableStatus *lstats; /* pending stats entry */
- PgStatShared_Relation *shtabstats;
+ PgStatShared_Table *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;
- shtabstats = (PgStatShared_Relation *) entry_ref->shared_stats;
+ shtabstats = (PgStatShared_Table *) 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.
+ * Ignore entries that didn't accumulate any actual counts.
*/
if (memcmp(&lstats->t_counts, &all_zeroes,
sizeof(PgStat_TableCounts)) == 0)
@@ -792,6 +906,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool
nowait)
if (lstats->t_counts.t_numscans)
{
TimestampTz t = GetCurrentTransactionStopTimestamp();
+
if (t > tabentry->lastscan)
tabentry->lastscan = t;
}
@@ -839,8 +954,74 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool
nowait)
return true;
}
+/*
+ * Flush out pending stats for the entry
+ *
+ * If nowait is true, this function returns false if lock could not
+ * immediately acquired, otherwise true is returned.
+ *
+ * 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)
+{
+ static const PgStat_IndexCounts all_zeroes;
+ Oid dboid;
+
+ PgStat_IndexStatus *lstats; /* pending stats entry */
+ PgStatShared_Index *shrelcomstats;
+ PgStat_StatIndEntry *indentry; /* index entry of shared stats */
+ PgStat_StatDBEntry *dbentry; /* pending database entry */
+
+ dboid = entry_ref->shared_entry->key.dboid;
+ lstats = (PgStat_IndexStatus *) entry_ref->pending;
+ shrelcomstats = (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 (memcmp(&lstats->i_counts, &all_zeroes,
+ sizeof(PgStat_IndexCounts)) == 0)
+ {
+ return true;
+ }
+
+ if (!pgstat_lock_entry(entry_ref, nowait))
+ return false;
+
+ /* add the values to the shared entry. */
+ indentry = &shrelcomstats->stats;
+
+ indentry->numscans += lstats->i_counts.i_numscans;
+
+ if (lstats->i_counts.i_numscans)
+ {
+ TimestampTz t = GetCurrentTransactionStopTimestamp();
+
+ if (t > indentry->lastscan)
+ indentry->lastscan = t;
+ }
+ indentry->tuples_returned += lstats->i_counts.i_tuples_returned;
+ indentry->tuples_fetched += lstats->i_counts.i_tuples_fetched;
+ indentry->blocks_fetched += lstats->i_counts.i_blocks_fetched;
+ indentry->blocks_hit += lstats->i_counts.i_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->n_tuples_returned += lstats->i_counts.i_tuples_returned;
+ dbentry->n_tuples_fetched += lstats->i_counts.i_tuples_fetched;
+ dbentry->n_blocks_fetched += lstats->i_counts.i_blocks_fetched;
+ dbentry->n_blocks_hit += lstats->i_counts.i_blocks_hit;
+
+ return true;
+}
+
void
-pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
+pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref)
{
PgStat_TableStatus *pending = (PgStat_TableStatus *) entry_ref->pending;
@@ -848,17 +1029,26 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef
*entry_ref)
pgstat_unlink_relation(pending->relation);
}
+void
+pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+ PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+ if (pending->relation)
+ pgstat_unlink_relation(pending->relation);
+}
+
/*
* Find or create a PgStat_TableStatus entry for rel. New entry is created and
* initialized if not exists.
*/
static PgStat_TableStatus *
-pgstat_prep_relation_pending(Oid rel_id, bool isshared)
+pgstat_prep_table_pending(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(PGSTAT_KIND_TABLE,
isshared ? InvalidOid : MyDatabaseId,
rel_id, NULL);
pending = entry_ref->pending;
@@ -868,11 +1058,31 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
return pending;
}
+/*
+ * Find or create a PgStat_IndexStatus entry for rel. New entry is created and
+ * initialized if not exists.
+ */
+static PgStat_IndexStatus *
+pgstat_prep_index_pending(Oid rel_id, bool isshared)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStat_IndexStatus *pending;
+
+ entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_INDEX,
+
isshared ? InvalidOid : MyDatabaseId,
+
rel_id, NULL);
+ pending = entry_ref->pending;
+ pending->r_id = rel_id;
+ pending->r_shared = isshared;
+
+ return pending;
+}
+
/*
* 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_TableStatus *pgstattab_info, int nest_level)
{
PgStat_SubXactStatus *xact_state;
PgStat_TableXactStatus *trans;
@@ -888,24 +1098,24 @@ add_tabstat_xact_level(PgStat_TableStatus *pgstat_info,
int nest_level)
MemoryContextAllocZero(TopTransactionContext,
sizeof(PgStat_TableXactStatus));
trans->nest_level = nest_level;
- trans->upper = pgstat_info->trans;
- trans->parent = pgstat_info;
+ trans->upper = pgstattab_info->trans;
+ trans->parent = pgstattab_info;
trans->next = xact_state->first;
xact_state->first = trans;
- pgstat_info->trans = trans;
+ pgstattab_info->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_TableStatus *pgstattab_info)
{
int nest_level = GetCurrentTransactionNestLevel();
- if (pgstat_info->trans == NULL ||
- pgstat_info->trans->nest_level != nest_level)
- add_tabstat_xact_level(pgstat_info, nest_level);
+ if (pgstattab_info->trans == NULL ||
+ pgstattab_info->trans->nest_level != nest_level)
+ add_tabstat_xact_level(pgstattab_info, nest_level);
}
/*
diff --git a/src/backend/utils/adt/pgstatfuncs.c
b/src/backend/utils/adt/pgstatfuncs.c
index 96bffc0f2a..e1a5347e13 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -37,23 +37,37 @@
#define HAS_PGSTAT_PERMISSIONS(role) (has_privs_of_role(GetUserId(),
ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
Datum
-pg_stat_get_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_index_numscans(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
- PgStat_StatTabEntry *tabentry;
+ PgStat_StatIndEntry *indentry;
- if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+ if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
result = 0;
else
- result = (int64) (tabentry->numscans);
+ result = (int64) (indentry->numscans);
PG_RETURN_INT64(result);
}
+Datum
+pg_stat_get_heap_numscans(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ int64 result;
+ PgStat_StatTabEntry *relentry;
+
+ if ((relentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+ result = 0;
+ else
+ result = (int64) (relentry->numscans);
+
+ PG_RETURN_INT64(result);
+}
Datum
-pg_stat_get_lastscan(PG_FUNCTION_ARGS)
+pg_stat_get_heap_lastscan(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
PgStat_StatTabEntry *tabentry;
@@ -61,12 +75,23 @@ pg_stat_get_lastscan(PG_FUNCTION_ARGS)
if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
PG_RETURN_NULL();
else
- PG_RETURN_TIMESTAMPTZ(tabentry->lastscan);
+ PG_RETURN_TIMESTAMPTZ(tabentry->lastscan);;
}
+Datum
+pg_stat_get_index_lastscan(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ PgStat_StatIndEntry *indentry;
+
+ if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_TIMESTAMPTZ(indentry->lastscan);;
+}
Datum
-pg_stat_get_tuples_returned(PG_FUNCTION_ARGS)
+pg_stat_get_heap_tuples_returned(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
@@ -79,10 +104,23 @@ pg_stat_get_tuples_returned(PG_FUNCTION_ARGS)
PG_RETURN_INT64(result);
}
+Datum
+pg_stat_get_index_tuples_returned(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ int64 result;
+ PgStat_StatIndEntry *indentry;
+
+ if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+ result = 0;
+ else
+ result = (int64) (indentry->tuples_returned);
+ PG_RETURN_INT64(result);
+}
Datum
-pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS)
+pg_stat_get_heap_tuples_fetched(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
@@ -96,6 +134,21 @@ pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS)
PG_RETURN_INT64(result);
}
+Datum
+pg_stat_get_index_tuples_fetched(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ int64 result;
+ PgStat_StatIndEntry *indentry;
+
+ if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+ result = 0;
+ else
+ result = (int64) (indentry->tuples_fetched);
+
+ PG_RETURN_INT64(result);
+}
+
Datum
pg_stat_get_tuples_inserted(PG_FUNCTION_ARGS)
@@ -226,7 +279,7 @@ pg_stat_get_ins_since_vacuum(PG_FUNCTION_ARGS)
Datum
-pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS)
+pg_stat_get_heap_blocks_fetched(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
@@ -240,9 +293,23 @@ pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS)
PG_RETURN_INT64(result);
}
+Datum
+pg_stat_get_index_blocks_fetched(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ int64 result;
+ PgStat_StatIndEntry *indentry;
+
+ if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+ result = 0;
+ else
+ result = (int64) (indentry->blocks_fetched);
+
+ PG_RETURN_INT64(result);
+}
Datum
-pg_stat_get_blocks_hit(PG_FUNCTION_ARGS)
+pg_stat_get_heap_blocks_hit(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
@@ -256,6 +323,21 @@ pg_stat_get_blocks_hit(PG_FUNCTION_ARGS)
PG_RETURN_INT64(result);
}
+Datum
+pg_stat_get_index_blocks_hit(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ int64 result;
+ PgStat_StatIndEntry *indentry;
+
+ if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+ result = 0;
+ else
+ result = (int64) (indentry->blocks_hit);
+
+ PG_RETURN_INT64(result);
+}
+
Datum
pg_stat_get_last_vacuum_time(PG_FUNCTION_ARGS)
{
@@ -1837,7 +1919,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
}
Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_heap_xact_numscans(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
@@ -1851,17 +1933,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
PG_RETURN_INT64(result);
}
+Datum
+pg_stat_get_index_xact_numscans(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ int64 result;
+ PgStat_IndexStatus *indentry;
+
+ if ((indentry = find_indstat_entry(relid)) == NULL)
+ result = 0;
+ else
+ result = (int64) (indentry->i_counts.i_numscans);
+
+ PG_RETURN_INT64(result);
+}
+
Datum
pg_stat_get_xact_tuples_returned(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
- PgStat_TableStatus *tabentry;
+ PgStat_IndexStatus *indentry;
- if ((tabentry = find_tabstat_entry(relid)) == NULL)
+ if ((indentry = find_indstat_entry(relid)) == NULL)
result = 0;
else
- result = (int64) (tabentry->t_counts.t_tuples_returned);
+ result = (int64) (indentry->i_counts.i_tuples_returned);
PG_RETURN_INT64(result);
}
@@ -1871,12 +1968,12 @@ pg_stat_get_xact_tuples_fetched(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
- PgStat_TableStatus *tabentry;
+ PgStat_IndexStatus *indentry;
- if ((tabentry = find_tabstat_entry(relid)) == NULL)
+ if ((indentry = find_indstat_entry(relid)) == NULL)
result = 0;
else
- result = (int64) (tabentry->t_counts.t_tuples_fetched);
+ result = (int64) (indentry->i_counts.i_tuples_fetched);
PG_RETURN_INT64(result);
}
@@ -1964,12 +2061,12 @@ pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
- PgStat_TableStatus *tabentry;
+ PgStat_IndexStatus *indentry;
- if ((tabentry = find_tabstat_entry(relid)) == NULL)
+ if ((indentry = find_indstat_entry(relid)) == NULL)
result = 0;
else
- result = (int64) (tabentry->t_counts.t_blocks_fetched);
+ result = (int64) (indentry->i_counts.i_blocks_fetched);
PG_RETURN_INT64(result);
}
@@ -1979,12 +2076,12 @@ pg_stat_get_xact_blocks_hit(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
int64 result;
- PgStat_TableStatus *tabentry;
+ PgStat_IndexStatus *indentry;
- if ((tabentry = find_tabstat_entry(relid)) == NULL)
+ if ((indentry = find_indstat_entry(relid)) == NULL)
result = 0;
else
- result = (int64) (tabentry->t_counts.t_blocks_hit);
+ result = (int64) (indentry->i_counts.i_blocks_hit);
PG_RETURN_INT64(result);
}
@@ -2103,7 +2200,8 @@ pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS)
{
Oid taboid = PG_GETARG_OID(0);
- pgstat_reset(PGSTAT_KIND_RELATION, MyDatabaseId, taboid);
+ pgstat_reset(PGSTAT_KIND_TABLE, MyDatabaseId, taboid);
+ pgstat_reset(PGSTAT_KIND_INDEX, MyDatabaseId, taboid);
PG_RETURN_VOID();
}
diff --git a/src/backend/utils/cache/relcache.c
b/src/backend/utils/cache/relcache.c
index bd6cd4e47b..2d24abc3df 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2720,8 +2720,9 @@ RelationClearRelation(Relation relation, bool rebuild)
SWAPFIELD(RowSecurityDesc *, rd_rsdesc);
/* toast OID override must be preserved */
SWAPFIELD(Oid, rd_toastoid);
- /* pgstat_info / enabled must be preserved */
- SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
+ /* pgstattab_info / pgstatind_info / enabled must be preserved
*/
+ SWAPFIELD(struct PgStat_TableStatus *, pgstattab_info);
+ SWAPFIELD(struct PgStat_IndexStatus *, pgstatind_info);
SWAPFIELD(bool, pgstat_enabled);
/* preserve old partition key if we have one */
if (keep_partkey)
@@ -6314,7 +6315,8 @@ load_relcache_init_file(bool shared)
rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
rel->rd_droppedSubid = InvalidSubTransactionId;
rel->rd_amcache = NULL;
- rel->pgstat_info = NULL;
+ rel->pgstattab_info = NULL;
+ rel->pgstatind_info = NULL;
/*
* Recompute lock and physical addressing info. This is needed
in
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 20f5aa56ea..7733ebfcb9 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5248,22 +5248,38 @@
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',
- proname => 'pg_stat_get_numscans', provolatile => 's', proparallel => 'r',
+{ oid => '1928', descr => 'statistics: number of scans done for table',
+ proname => 'pg_stat_get_heap_numscans', provolatile => 's', proparallel =>
'r',
prorettype => 'int8', proargtypes => 'oid',
- prosrc => 'pg_stat_get_numscans' },
-{ oid => '9976', descr => 'statistics: time of the last scan for table/index',
- proname => 'pg_stat_get_lastscan', provolatile => 's', proparallel => 'r',
+ prosrc => 'pg_stat_get_heap_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+ proname => 'pg_stat_get_index_numscans', provolatile => 's', proparallel =>
'r',
+ prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_index_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+ proname => 'pg_stat_get_heap_lastscan', provolatile => 's', proparallel =>
'r',
+ prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_heap_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+ proname => 'pg_stat_get_index_lastscan', provolatile => 's', proparallel =>
'r',
prorettype => 'timestamptz', proargtypes => 'oid',
- prosrc => 'pg_stat_get_lastscan' },
+ prosrc => 'pg_stat_get_index_lastscan' },
{ oid => '1929', descr => 'statistics: number of tuples read by seqscan',
- proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+ proname => 'pg_stat_get_heap_tuples_returned', provolatile => 's',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
- prosrc => 'pg_stat_get_tuples_returned' },
+ prosrc => 'pg_stat_get_heap_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+ proname => 'pg_stat_get_index_tuples_returned', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_index_tuples_returned' },
{ oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
- proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+ proname => 'pg_stat_get_heap_tuples_fetched', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_heap_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+ proname => 'pg_stat_get_index_tuples_fetched', provolatile => 's',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
- prosrc => 'pg_stat_get_tuples_fetched' },
+ prosrc => 'pg_stat_get_index_tuples_fetched' },
{ oid => '1931', descr => 'statistics: number of tuples inserted',
proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
@@ -5299,13 +5315,21 @@
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
prosrc => 'pg_stat_get_ins_since_vacuum' },
{ oid => '1934', descr => 'statistics: number of blocks fetched',
- proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+ proname => 'pg_stat_get_heap_blocks_fetched', provolatile => 's',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
- prosrc => 'pg_stat_get_blocks_fetched' },
+ prosrc => 'pg_stat_get_heap_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+ proname => 'pg_stat_get_index_blocks_fetched', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_index_blocks_fetched' },
{ oid => '1935', descr => 'statistics: number of blocks found in cache',
- proname => 'pg_stat_get_blocks_hit', provolatile => 's', proparallel => 'r',
+ proname => 'pg_stat_get_heap_blocks_hit', provolatile => 's', proparallel =>
'r',
+ prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_heap_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+ proname => 'pg_stat_get_index_blocks_hit', provolatile => 's', proparallel
=> 'r',
prorettype => 'int8', proargtypes => 'oid',
- prosrc => 'pg_stat_get_blocks_hit' },
+ prosrc => 'pg_stat_get_index_blocks_hit' },
{ oid => '2781', descr => 'statistics: last manual vacuum time for a table',
proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
@@ -5693,10 +5717,15 @@
prosrc => 'pg_stat_get_function_self_time' },
{ oid => '3037',
- descr => 'statistics: number of scans done for table/index in current
transaction',
- proname => 'pg_stat_get_xact_numscans', provolatile => 'v',
+ descr => 'statistics: number of scans done for table in current transaction',
+ proname => 'pg_stat_get_heap_xact_numscans', provolatile => 'v',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_heap_xact_numscans' },
+{ oid => '8892',
+ descr => 'statistics: number of scans done for index in current transaction',
+ proname => 'pg_stat_get_index_xact_numscans', provolatile => 'v',
proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
- prosrc => 'pg_stat_get_xact_numscans' },
+ prosrc => 'pg_stat_get_index_xact_numscans' },
{ oid => '3038',
descr => 'statistics: number of tuples read by seqscan in current
transaction',
proname => 'pg_stat_get_xact_tuples_returned', provolatile => 'v',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 9e2ce6f011..65b1afe971 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -39,7 +39,8 @@ typedef enum PgStat_Kind
/* stats for variable-numbered objects */
PGSTAT_KIND_DATABASE, /* database-wide statistics */
- PGSTAT_KIND_RELATION, /* per-table statistics */
+ PGSTAT_KIND_TABLE, /* per-table statistics */
+ PGSTAT_KIND_INDEX, /* per-index statistics */
PGSTAT_KIND_FUNCTION, /* per-function statistics */
PGSTAT_KIND_REPLSLOT, /* per-slot statistics */
PGSTAT_KIND_SUBSCRIPTION, /* per-subscription statistics */
@@ -145,6 +146,28 @@ typedef struct PgStat_BackendSubEntry
PgStat_Counter sync_error_count;
} PgStat_BackendSubEntry;
+/* ----------
+ * PgStat_IndexCounts The actual per-index counts kept by a
backend
+ *
+ * This struct should contain only actual event counters, because we memcmp
+ * it against zeroes to detect whether there are any stats updates to apply.
+ * It is a component of PgStat_IndexStatus (within-backend state).
+ *
+ * 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.
+ * ----------
+ */
+typedef struct PgStat_IndexCounts
+{
+ PgStat_Counter i_numscans;
+
+ PgStat_Counter i_tuples_returned;
+ PgStat_Counter i_tuples_fetched;
+ PgStat_Counter i_blocks_fetched;
+ PgStat_Counter i_blocks_hit;
+} PgStat_IndexCounts;
+
/* ----------
* PgStat_TableCounts The actual per-table counts kept by a
backend
*
@@ -152,12 +175,9 @@ typedef struct PgStat_BackendSubEntry
* it against zeroes to detect whether there are any stats updates to apply.
* It is a component of PgStat_TableStatus (within-backend state).
*
- * Note: for a table, tuples_returned is the number of tuples successfully
+ * 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.
- * 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.
*
* tuples_inserted/updated/deleted/hot_updated count attempted actions,
* regardless of whether the transaction committed. delta_live_tuples,
@@ -210,6 +230,22 @@ typedef struct PgStat_TableStatus
Relation relation; /* rel that is using this entry
*/
} PgStat_TableStatus;
+/* ----------
+ * PgStat_IndexStatus Per-index 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_IndexStatus.
+ * ----------
+ */
+typedef struct PgStat_IndexStatus
+{
+ Oid r_id; /* relation's OID */
+ bool r_shared; /* is it a shared catalog? */
+ PgStat_IndexCounts i_counts; /* event counts to be sent */
+ Relation relation; /* rel that is using this entry
*/
+} PgStat_IndexStatus;
+
/* ----------
* PgStat_TableXactStatus Per-table, per-subtransaction status
* ----------
@@ -382,6 +418,17 @@ typedef struct PgStat_StatTabEntry
PgStat_Counter autovac_analyze_count;
} PgStat_StatTabEntry;
+typedef struct PgStat_StatIndEntry
+{
+ PgStat_Counter numscans;
+ TimestampTz lastscan;
+
+ PgStat_Counter tuples_returned;
+ PgStat_Counter tuples_fetched;
+ PgStat_Counter blocks_fetched;
+ PgStat_Counter blocks_hit;
+} PgStat_StatIndEntry;
+
typedef struct PgStat_WalStats
{
PgStat_Counter wal_records;
@@ -498,11 +545,15 @@ extern PgStat_BackendFunctionEntry
*find_funcstat_entry(Oid func_id);
*/
extern void pgstat_create_relation(Relation rel);
-extern void pgstat_drop_relation(Relation rel);
-extern void pgstat_copy_relation_stats(Relation dst, Relation src);
+extern void pgstat_create_table(Relation rel);
+extern void pgstat_drop_heap(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
extern void pgstat_init_relation(Relation rel);
extern void pgstat_assoc_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
extern void pgstat_unlink_relation(Relation rel);
extern void pgstat_report_vacuum(Oid tableoid, bool shared,
@@ -513,51 +564,69 @@ extern void pgstat_report_analyze(Relation rel,
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
- * pgstat_assoc_relation() to do so. See its comment for why this is done
- * separately from pgstat_init_relation().
+ * pgstat_assoc_table() / pgstat_assoc_index() to do so.
+ * See their comment for why this is done separately from
pgstat_init_relation().
*/
-#define pgstat_should_count_relation(rel) \
- (likely((rel)->pgstat_info != NULL) ? true : \
- ((rel)->pgstat_enabled ? pgstat_assoc_relation(rel), true : false))
+#define pgstat_should_count_table(rel) \
+ (likely((rel)->pgstattab_info != NULL) ? true : \
+ ((rel)->pgstat_enabled ? pgstat_assoc_table(rel), true : false))
+
+#define pgstat_should_count_index(rel) \
+ (likely((rel)->pgstatind_info != NULL) ? true : \
+ ((rel)->pgstat_enabled ? pgstat_assoc_index(rel), true : false))
/* nontransactional event counts are simple enough to inline */
#define pgstat_count_heap_scan(rel)
\
do {
\
- if (pgstat_should_count_relation(rel))
\
- (rel)->pgstat_info->t_counts.t_numscans++;
\
+ if (pgstat_should_count_table(rel))
\
+ (rel)->pgstattab_info->t_counts.t_numscans++;
\
} while (0)
#define pgstat_count_heap_getnext(rel)
\
do {
\
- if (pgstat_should_count_relation(rel))
\
- (rel)->pgstat_info->t_counts.t_tuples_returned++;
\
+ if (pgstat_should_count_table(rel))
\
+ (rel)->pgstattab_info->t_counts.t_tuples_returned++;
\
} while (0)
#define pgstat_count_heap_fetch(rel)
\
do {
\
- if (pgstat_should_count_relation(rel))
\
- (rel)->pgstat_info->t_counts.t_tuples_fetched++;
\
+ if (pgstat_should_count_table(rel))
\
+ (rel)->pgstattab_info->t_counts.t_tuples_fetched++;
\
+ } while (0)
+#define pgstat_count_index_fetch(rel)
\
+ do {
\
+ if (pgstat_should_count_index(rel))
\
+ (rel)->pgstatind_info->i_counts.i_tuples_fetched++;
\
} while (0)
#define pgstat_count_index_scan(rel)
\
do {
\
- if (pgstat_should_count_relation(rel))
\
- (rel)->pgstat_info->t_counts.t_numscans++;
\
+ if (pgstat_should_count_index(rel))
\
+ (rel)->pgstatind_info->i_counts.i_numscans++;
\
} while (0)
#define pgstat_count_index_tuples(rel, n)
\
do {
\
- if (pgstat_should_count_relation(rel))
\
- (rel)->pgstat_info->t_counts.t_tuples_returned += (n);
\
+ if (pgstat_should_count_index(rel))
\
+ (rel)->pgstatind_info->i_counts.i_tuples_returned +=
(n); \
} while (0)
-#define pgstat_count_buffer_read(rel)
\
+#define pgstat_count_heap_buffer_read(rel)
\
do {
\
- if (pgstat_should_count_relation(rel))
\
- (rel)->pgstat_info->t_counts.t_blocks_fetched++;
\
+ if (pgstat_should_count_table(rel)) \
+ (rel)->pgstattab_info->t_counts.t_blocks_fetched++;
\
} while (0)
-#define pgstat_count_buffer_hit(rel)
\
+#define pgstat_count_index_buffer_read(rel)
\
do {
\
- if (pgstat_should_count_relation(rel))
\
- (rel)->pgstat_info->t_counts.t_blocks_hit++;
\
+ if (pgstat_should_count_index(rel)) \
+ (rel)->pgstatind_info->i_counts.i_blocks_fetched++;
\
+ } while (0)
+#define pgstat_count_heap_buffer_hit(rel)
\
+ do {
\
+ if (pgstat_should_count_table(rel)) \
+ (rel)->pgstattab_info->t_counts.t_blocks_hit++; \
+ } while (0)
+#define pgstat_count_index_buffer_hit(rel)
\
+ do {
\
+ if (pgstat_should_count_index(rel)) \
+ (rel)->pgstatind_info->i_counts.i_blocks_hit++; \
} while (0)
-
extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
extern void pgstat_count_heap_update(Relation rel, bool hot);
extern void pgstat_count_heap_delete(Relation rel);
@@ -573,6 +642,10 @@ extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid
relid);
extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry_ext(bool shared,
Oid reloid);
extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
+extern PgStat_IndexStatus *find_indstat_entry(Oid rel_id);
+extern PgStat_StatIndEntry *pgstat_fetch_stat_indentry(Oid relid);
+extern PgStat_StatIndEntry *pgstat_fetch_stat_indentry_ext(bool shared,
+
Oid relid);
/*
diff --git a/src/include/utils/pgstat_internal.h
b/src/include/utils/pgstat_internal.h
index c869533b28..b7464b5a5c 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -360,11 +360,17 @@ typedef struct PgStatShared_Database
PgStat_StatDBEntry stats;
} PgStatShared_Database;
-typedef struct PgStatShared_Relation
+typedef struct PgStatShared_Table
{
PgStatShared_Common header;
PgStat_StatTabEntry stats;
-} PgStatShared_Relation;
+} PgStatShared_Table;
+
+typedef struct PgStatShared_Index
+{
+ PgStatShared_Common header;
+ PgStat_StatIndEntry stats;
+} PgStatShared_Index;
typedef struct PgStatShared_Function
{
@@ -559,8 +565,10 @@ extern void
AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool
extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
-extern void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref);
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern bool pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+extern void pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref);
/*
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f383a2fca9..796423f3e3 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -248,7 +248,10 @@ 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 */
+ /* table's statistics collection area */
+ struct PgStat_TableStatus *pgstattab_info;
+ /* Index's statistics collection area */
+ struct PgStat_IndexStatus *pgstatind_info;
} RelationData;
diff --git a/src/test/isolation/expected/stats.out
b/src/test/isolation/expected/stats.out
index 61b5a710ec..6ff2cfbd97 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,8 +2166,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2210,8 +2210,8 @@ step s2_table_update_k1: UPDATE test_stat_tab SET value =
value + 1 WHERE key =
step s1_table_drop: DROP TABLE test_stat_tab;
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2236,8 +2236,8 @@ pg_stat_force_next_flush
step s1_track_counts_off: SET track_counts = off;
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2275,8 +2275,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2314,8 +2314,8 @@ pg_stat_force_next_flush
step s1_track_counts_off: SET track_counts = off;
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2369,8 +2369,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2401,8 +2401,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2463,8 +2463,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2495,8 +2495,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2558,8 +2558,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2627,8 +2627,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2688,8 +2688,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2755,8 +2755,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2795,8 +2795,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2841,8 +2841,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2881,8 +2881,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2927,8 +2927,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -2968,8 +2968,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
@@ -3015,8 +3015,8 @@ pg_stat_force_next_flush
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
diff --git a/src/test/isolation/specs/stats.spec
b/src/test/isolation/specs/stats.spec
index 5b922d788c..0ca25393e4 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,8 +93,8 @@ step s1_table_drop { DROP TABLE test_stat_tab; }
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_heap_numscans(tso.oid) AS seq_scan,
+ pg_stat_get_heap_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,
diff --git a/src/test/recovery/t/029_stats_restart.pl
b/src/test/recovery/t/029_stats_restart.pl
index 1bf7b568cc..c25634d33f 100644
--- a/src/test/recovery/t/029_stats_restart.pl
+++ b/src/test/recovery/t/029_stats_restart.pl
@@ -43,8 +43,8 @@ my $sect = "initial";
is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
is(have_stats('function', $dboid, $funcoid),
't', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
- 't', "$sect: relation stats do exist");
+is(have_stats('table', $dboid, $tableoid),
+ 't', "$sect: table stats do exist");
# regular shutdown
$node->stop();
@@ -67,8 +67,8 @@ $sect = "copy";
is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
is(have_stats('function', $dboid, $funcoid),
't', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
- 't', "$sect: relation stats do exist");
+is(have_stats('table', $dboid, $tableoid),
+ 't', "$sect: table stats do exist");
$node->stop('immediate');
@@ -84,8 +84,8 @@ $sect = "post immediate";
is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
is(have_stats('function', $dboid, $funcoid),
'f', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
- 'f', "$sect: relation stats do not exist");
+is(have_stats('table', $dboid, $tableoid),
+ 'f', "$sect: table stats do not exist");
# get rid of backup statsfile
unlink $statsfile or die "cannot unlink $statsfile $!";
@@ -98,8 +98,8 @@ $sect = "post immediate, new";
is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
is(have_stats('function', $dboid, $funcoid),
't', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
- 't', "$sect: relation stats do exist");
+is(have_stats('table', $dboid, $tableoid),
+ 't', "$sect: table stats do exist");
# regular shutdown
$node->stop();
@@ -117,8 +117,7 @@ $sect = "invalid_overwrite";
is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
is(have_stats('function', $dboid, $funcoid),
'f', "$sect: function stats do not exist");
-is(have_stats('relation', $dboid, $tableoid),
- 'f', "$sect: relation stats do not exist");
+is(have_stats('table', $dboid, $tableoid), 'f', "$sect: table do not exist");
## check invalid stats file starting with valid contents, but followed by
@@ -133,8 +132,8 @@ $sect = "invalid_append";
is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
is(have_stats('function', $dboid, $funcoid),
'f', "$sect: function stats do not exist");
-is(have_stats('relation', $dboid, $tableoid),
- 'f', "$sect: relation stats do not exist");
+is(have_stats('table', $dboid, $tableoid),
+ 'f', "$sect: table stats do not exist");
## checks related to stats persistency around restarts and resets
diff --git a/src/test/recovery/t/030_stats_cleanup_replica.pl
b/src/test/recovery/t/030_stats_cleanup_replica.pl
index cc92ddbb52..1e78b13cf9 100644
--- a/src/test/recovery/t/030_stats_cleanup_replica.pl
+++ b/src/test/recovery/t/030_stats_cleanup_replica.pl
@@ -185,7 +185,7 @@ sub test_standby_func_tab_stats_status
my %stats;
$stats{rel} = $node_standby->safe_psql($connect_db,
- "SELECT pg_stat_have_stats('relation', $dboid, $tableoid)");
+ "SELECT pg_stat_have_stats('table', $dboid, $tableoid)");
$stats{func} = $node_standby->safe_psql($connect_db,
"SELECT pg_stat_have_stats('function', $dboid, $funcoid)");
diff --git a/src/test/regress/expected/rules.out
b/src/test/regress/expected/rules.out
index 624d0e5aae..531164e030 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1764,10 +1764,10 @@ 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_index_numscans(i.oid) AS idx_scan,
+ pg_stat_get_index_lastscan(i.oid) AS last_idx_scan,
+ pg_stat_get_index_tuples_returned(i.oid) AS idx_tup_read,
+ pg_stat_get_index_tuples_fetched(i.oid) AS idx_tup_fetch
FROM (((pg_class c
JOIN pg_index x ON ((c.oid = x.indrelid)))
JOIN pg_class i ON ((i.oid = x.indexrelid)))
@@ -1776,12 +1776,12 @@ pg_stat_all_indexes| SELECT c.oid AS relid,
pg_stat_all_tables| SELECT c.oid AS relid,
n.nspname AS schemaname,
c.relname,
- 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,
+ pg_stat_get_heap_numscans(c.oid) AS seq_scan,
+ pg_stat_get_heap_lastscan(c.oid) AS last_seq_scan,
+ pg_stat_get_heap_tuples_returned(c.oid) AS seq_tup_read,
+ (sum(pg_stat_get_index_numscans(i.indexrelid)))::bigint AS idx_scan,
+ max(pg_stat_get_index_lastscan(i.indexrelid)) AS last_idx_scan,
+ ((sum(pg_stat_get_index_tuples_fetched(i.indexrelid)))::bigint +
pg_stat_get_heap_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,
@@ -2221,9 +2221,9 @@ pg_stat_wal_receiver| SELECT s.pid,
pg_stat_xact_all_tables| SELECT c.oid AS relid,
n.nspname AS schemaname,
c.relname,
- pg_stat_get_xact_numscans(c.oid) AS seq_scan,
+ pg_stat_get_heap_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_index_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,
pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins,
pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd,
@@ -2274,8 +2274,8 @@ 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_index_blocks_fetched(i.oid) -
pg_stat_get_index_blocks_hit(i.oid)) AS idx_blks_read,
+ pg_stat_get_index_blocks_hit(i.oid) AS idx_blks_hit
FROM (((pg_class c
JOIN pg_index x ON ((c.oid = x.indrelid)))
JOIN pg_class i ON ((i.oid = x.indexrelid)))
@@ -2284,31 +2284,31 @@ pg_statio_all_indexes| SELECT c.oid AS relid,
pg_statio_all_sequences| SELECT c.oid AS relid,
n.nspname AS schemaname,
c.relname,
- (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS
blks_read,
- pg_stat_get_blocks_hit(c.oid) AS blks_hit
+ (pg_stat_get_index_blocks_fetched(c.oid) -
pg_stat_get_heap_blocks_hit(c.oid)) AS blks_read,
+ pg_stat_get_heap_blocks_hit(c.oid) AS blks_hit
FROM (pg_class c
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE (c.relkind = 'S'::"char");
pg_statio_all_tables| SELECT c.oid AS relid,
n.nspname AS schemaname,
c.relname,
- (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS
heap_blks_read,
- pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit,
+ (pg_stat_get_heap_blocks_fetched(c.oid) -
pg_stat_get_heap_blocks_hit(c.oid)) AS heap_blks_read,
+ pg_stat_get_heap_blocks_hit(c.oid) AS heap_blks_hit,
i.idx_blks_read,
i.idx_blks_hit,
- (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS
toast_blks_read,
- pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit,
+ (pg_stat_get_heap_blocks_fetched(t.oid) -
pg_stat_get_heap_blocks_hit(t.oid)) AS toast_blks_read,
+ pg_stat_get_heap_blocks_hit(t.oid) AS toast_blks_hit,
x.idx_blks_read AS tidx_blks_read,
x.idx_blks_hit AS tidx_blks_hit
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_index_blocks_fetched(pg_index.indexrelid) -
pg_stat_get_index_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+ (sum(pg_stat_get_index_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_index_blocks_fetched(pg_index.indexrelid) -
pg_stat_get_index_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+ (sum(pg_stat_get_index_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 257a6a9da9..449d70f4c7 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -151,6 +151,117 @@ FROM prevstats AS pr;
COMMIT;
----
+-- Basic tests for toast
+---
+CREATE TABLE stats_toast(stuff text);
+ALTER TABLE stats_toast ALTER COLUMN stuff SET STORAGE EXTERNAL;
+INSERT INTO stats_toast VALUES (repeat('a',1000000));
+SELECT count(*) FROM stats_toast WHERE stuff like '%a%';
+ count
+-------
+ 1
+(1 row)
+
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+select toast_blks_read > 0 as toast_blks_read, toast_blks_hit > 0 as
toast_blks_hit,
+ tidx_blks_read > 0 as tidx_blks_read, tidx_blks_hit > 0 as
tidx_blks_hit,
+ toast_blks_hit >= toast_blks_read,
+ tidx_blks_hit >= tidx_blks_read
+ from pg_statio_all_tables
+ where relname = 'stats_toast';
+ toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit | ?column?
| ?column?
+-----------------+----------------+----------------+---------------+----------+----------
+ t | t | t | t | t
| t
+(1 row)
+
+----
+-- Basic tests for partition
+---
+CREATE TABLE stats_partition (
+ id1 int not null,
+ id2 int not null
+) PARTITION BY RANGE (id2);
+CREATE TABLE stats_partition_5 PARTITION OF stats_partition
+ FOR VALUES FROM (0) TO (5);
+CREATE TABLE stats_partition_20000 PARTITION OF stats_partition
+ FOR VALUES FROM (5) TO (20001);
+CREATE INDEX ON stats_partition (id2);
+insert into stats_partition select a,a from generate_series(0,20000) a;
+SET enable_seqscan TO off;
+SET enable_bitmapscan TO off;
+select * from stats_partition where id2 = 2000;
+ id1 | id2
+------+------
+ 2000 | 2000
+(1 row)
+
+select * from stats_partition where id2 = 2;
+ id1 | id2
+-----+-----
+ 2 | 2
+(1 row)
+
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush
+--------------------------
+
+(1 row)
+
+select relname, indexrelname, idx_scan > 0 as idx_scan, idx_tup_read > 0 as
idx_tup_read,
+ idx_tup_fetch > 0 as idx_tup_fetch
+ from pg_stat_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+ relname | indexrelname | idx_scan |
idx_tup_read | idx_tup_fetch
+-----------------------+-------------------------------+----------+--------------+---------------
+ stats_partition_20000 | stats_partition_20000_id2_idx | t | t
| t
+ stats_partition_5 | stats_partition_5_id2_idx | t | t
| t
+(2 rows)
+
+select relname, indexrelname, idx_blks_read > 0 as idx_blks_read,
+ idx_blks_hit > 0 as idx_blks_hit
+ from pg_statio_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+ relname | indexrelname | idx_blks_read |
idx_blks_hit
+-----------------------+-------------------------------+---------------+--------------
+ stats_partition_20000 | stats_partition_20000_id2_idx | t | t
+ stats_partition_5 | stats_partition_5_id2_idx | t | t
+(2 rows)
+
+select relname,seq_scan > 0 as seq_scan, seq_tup_read > 0 as seq_tup_read,
+ idx_scan > 0 as idx_scan, idx_tup_fetch > 0 as idx_tup_fetch
+ from pg_stat_all_tables
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+ relname | seq_scan | seq_tup_read | idx_scan | idx_tup_fetch
+-----------------------+----------+--------------+----------+---------------
+ stats_partition | f | f | f | f
+ stats_partition_20000 | t | f | t | t
+ stats_partition_5 | t | f | t | t
+(3 rows)
+
+select relname, heap_blks_read > 0 as heap_blks_read, heap_blks_hit > 0 as
heap_blks_hit,
+ idx_blks_read > 0 as idx_blks_read, idx_blks_hit > 0 as idx_blks_hit
+ from pg_statio_all_tables
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+ relname | heap_blks_read | heap_blks_hit | idx_blks_read |
idx_blks_hit
+-----------------------+----------------+---------------+---------------+--------------
+ stats_partition_20000 | t | t | t | t
+ stats_partition_5 | t | t | t | t
+(2 rows)
+
+SET enable_seqscan TO on;
+SET enable_bitmapscan TO on;
+----
-- Basic tests for track_functions
---
CREATE FUNCTION stats_test_func1() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN
END;$$;
@@ -1015,21 +1126,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
@@ -1045,14 +1156,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
@@ -1067,7 +1178,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
@@ -1075,7 +1186,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
@@ -1083,7 +1194,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
@@ -1091,7 +1202,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
@@ -1099,7 +1210,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 f6270f7bad..c7fee71d6f 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -126,6 +126,77 @@ FROM prevstats AS pr;
COMMIT;
+----
+-- Basic tests for toast
+---
+CREATE TABLE stats_toast(stuff text);
+ALTER TABLE stats_toast ALTER COLUMN stuff SET STORAGE EXTERNAL;
+INSERT INTO stats_toast VALUES (repeat('a',1000000));
+SELECT count(*) FROM stats_toast WHERE stuff like '%a%';
+
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+
+select toast_blks_read > 0 as toast_blks_read, toast_blks_hit > 0 as
toast_blks_hit,
+ tidx_blks_read > 0 as tidx_blks_read, tidx_blks_hit > 0 as
tidx_blks_hit,
+ toast_blks_hit >= toast_blks_read,
+ tidx_blks_hit >= tidx_blks_read
+ from pg_statio_all_tables
+ where relname = 'stats_toast';
+
+----
+-- Basic tests for partition
+---
+CREATE TABLE stats_partition (
+ id1 int not null,
+ id2 int not null
+) PARTITION BY RANGE (id2);
+
+CREATE TABLE stats_partition_5 PARTITION OF stats_partition
+ FOR VALUES FROM (0) TO (5);
+
+CREATE TABLE stats_partition_20000 PARTITION OF stats_partition
+ FOR VALUES FROM (5) TO (20001);
+
+CREATE INDEX ON stats_partition (id2);
+
+insert into stats_partition select a,a from generate_series(0,20000) a;
+
+SET enable_seqscan TO off;
+SET enable_bitmapscan TO off;
+
+select * from stats_partition where id2 = 2000;
+select * from stats_partition where id2 = 2;
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+
+select relname, indexrelname, idx_scan > 0 as idx_scan, idx_tup_read > 0 as
idx_tup_read,
+ idx_tup_fetch > 0 as idx_tup_fetch
+ from pg_stat_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+
+select relname, indexrelname, idx_blks_read > 0 as idx_blks_read,
+ idx_blks_hit > 0 as idx_blks_hit
+ from pg_statio_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+
+select relname,seq_scan > 0 as seq_scan, seq_tup_read > 0 as seq_tup_read,
+ idx_scan > 0 as idx_scan, idx_tup_fetch > 0 as idx_tup_fetch
+ from pg_stat_all_tables
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+
+select relname, heap_blks_read > 0 as heap_blks_read, heap_blks_hit > 0 as
heap_blks_hit,
+ idx_blks_read > 0 as idx_blks_read, idx_blks_hit > 0 as idx_blks_hit
+ from pg_statio_all_tables
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+
+SET enable_seqscan TO on;
+SET enable_bitmapscan TO on;
+
----
-- Basic tests for track_functions
---
@@ -492,40 +563,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/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 2f02cc8f42..38806d5dee 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2009,10 +2009,11 @@ PgStatShared_Common
PgStatShared_Database
PgStatShared_Function
PgStatShared_HashEntry
-PgStatShared_Relation
+PgStatShared_Index
PgStatShared_ReplSlot
PgStatShared_SLRU
PgStatShared_Subscription
+PgStatShared_Table
PgStatShared_Wal
PgStat_ArchiverStats
PgStat_BackendFunctionEntry