* Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* IDENTIFICATION
* src/gausskernel/process/postmaster/pgstat_shmem.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "pgstat_shmem.h"
#include "storage/shmem.h"
#include "storage/lock/lwlock.h"
#include "utils/atomic.h"
#include "utils/dynahash.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
#include "access/hash.h"
#define PGSTAT_SHMEM_DB_HASH_SIZE 256
#define PGSTAT_SHMEM_TAB_HASH_SIZE 524288
#define PGSTAT_SHMEM_FUNC_HASH_SIZE 8192
#define PGSTAT_SHMEM_HASH_NEAR_FULL_RATIO 0.9
#define PGSTAT_SNAPSHOT_DB_HASH_SIZE 16
#define PGSTAT_SNAPSHOT_TAB_HASH_SIZE 512
#define PGSTAT_SNAPSHOT_FUNC_HASH_SIZE 512
static inline uint32 pgstat_hash_dbid(Oid dbid)
{
return hash_uint32((uint32)dbid);
}
static inline uint32 pgstat_hash_tabkey(const PgStatSharedTabKey* key)
{
return tag_hash((const void*)key, sizeof(PgStatSharedTabKey));
}
static inline uint32 pgstat_hash_funckey(const PgStatSharedFuncKey* key)
{
return tag_hash((const void*)key, sizeof(PgStatSharedFuncKey));
}
static inline LWLock* pgstat_db_lock(PgStatSharedState* s, Oid dbid)
{
uint32 hash = pgstat_hash_dbid(dbid);
return &s->db_locks[hash % PGSTAT_DB_NPARTITIONS].lock;
}
static inline LWLock* pgstat_tab_lock(PgStatSharedState* s, const PgStatSharedTabKey* key)
{
uint32 hash = pgstat_hash_tabkey(key);
return &s->tab_locks[hash % PGSTAT_TAB_NPARTITIONS].lock;
}
static inline LWLock* pgstat_func_lock(PgStatSharedState* s, const PgStatSharedFuncKey* key)
{
uint32 hash = pgstat_hash_funckey(key);
return &s->func_locks[hash % PGSTAT_FUNC_NPARTITIONS].lock;
}
static void pgstat_lock_all(LWLockPadded* locks, int count, LWLockMode mode)
{
for (int i = 0; i < count; i++)
LWLockAcquire(&locks[i].lock, mode);
}
static void pgstat_unlock_all(LWLockPadded* locks, int count)
{
for (int i = count - 1; i >= 0; i--)
LWLockRelease(&locks[i].lock);
}
static inline bool pgstat_dbid_visible(Oid dbid, Oid onlydb)
{
if (onlydb == InvalidOid) {
return true;
}
return (dbid == onlydb);
}
static void pgstat_shared_init_db_entry(PgStatSharedDBEntry* entry, Oid dbid)
{
errno_t rc = memset_s(entry, sizeof(PgStatSharedDBEntry), 0, sizeof(PgStatSharedDBEntry));
securec_check(rc, "\0", "\0");
entry->databaseid = dbid;
entry->stat_reset_timestamp = GetCurrentTimestamp();
}
static void pgstat_shared_init_tab_entry(PgStatSharedTabEntry* entry, const PgStatSharedTabKey* key)
{
errno_t rc = memset_s(entry, sizeof(PgStatSharedTabEntry), 0, sizeof(PgStatSharedTabEntry));
securec_check(rc, "\0", "\0");
entry->key = *key;
}
static void pgstat_shared_init_func_entry(PgStatSharedFuncEntry* entry, const PgStatSharedFuncKey* key)
{
errno_t rc = memset_s(entry, sizeof(PgStatSharedFuncEntry), 0, sizeof(PgStatSharedFuncEntry));
securec_check(rc, "\0", "\0");
entry->key = *key;
}
static void pgstat_func_hash_warn_near_full(PgStatSharedState* s)
{
long num_entries = hash_get_num_entries(s->func_hash);
if (num_entries >= (long)(PGSTAT_SHMEM_FUNC_HASH_SIZE * PGSTAT_SHMEM_HASH_NEAR_FULL_RATIO)) {
ereport(WARNING,
(errmsg("pgstat func hash near full: current entries %ld, limit %d; "
"new functions may not be recorded",
num_entries, PGSTAT_SHMEM_FUNC_HASH_SIZE)));
}
}
* Total shared memory = PgStatSharedState + DB hash + TAB hash + FUNC hash.
* With default sizes (DB=256, TAB=524288, FUNC=8192) on 64-bit, approximate total is ~145 MB,
* of which the TAB hash (table/partition/TOAST stats) accounts for the vast majority (~140 MB).
*/
Size PgStatShmemSize(void)
{
Size size = MAXALIGN(sizeof(PgStatSharedState));
size = add_size(size, hash_estimate_size(PGSTAT_SHMEM_DB_HASH_SIZE, sizeof(PgStatSharedDBEntry)));
size = add_size(size, hash_estimate_size(PGSTAT_SHMEM_TAB_HASH_SIZE, sizeof(PgStatSharedTabEntry)));
size = add_size(size, hash_estimate_size(PGSTAT_SHMEM_FUNC_HASH_SIZE, sizeof(PgStatSharedFuncEntry)));
return size;
}
* Estimate of shared memory "in use" by current entry counts (DB/TAB/FUNC hashes).
* May be less than PgStatShmemSize() when tables are not full; useful for monitoring.
*/
Size PgStatShmemUsedSize(void)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL || s->db_hash == NULL || s->tab_hash == NULL || s->func_hash == NULL) {
return 0;
}
long n_db = hash_get_num_entries(s->db_hash);
long n_tab = hash_get_num_entries(s->tab_hash);
long n_func = hash_get_num_entries(s->func_hash);
Size used = MAXALIGN(sizeof(PgStatSharedState));
used = add_size(used, hash_estimate_size(n_db, sizeof(PgStatSharedDBEntry)));
used = add_size(used, hash_estimate_size(n_tab, sizeof(PgStatSharedTabEntry)));
used = add_size(used, hash_estimate_size(n_func, sizeof(PgStatSharedFuncEntry)));
return used;
}
void PgStatShmemInit(void)
{
bool found = false;
HASHCTL ctl;
errno_t rc;
g_instance.stat_cxt.pgstat_shared = (PgStatSharedState*)ShmemInitStruct("PgStat Shared State",
sizeof(PgStatSharedState), &found);
if (found) {
return;
}
PgStatSharedState* s = g_instance.stat_cxt.pgstat_shared;
for (int i = 0; i < PGSTAT_DB_NPARTITIONS; i++)
LWLockInitialize(&s->db_locks[i].lock, LWTRANCHE_PGSTAT_HASH);
for (int i = 0; i < PGSTAT_TAB_NPARTITIONS; i++)
LWLockInitialize(&s->tab_locks[i].lock, LWTRANCHE_PGSTAT_HASH);
for (int i = 0; i < PGSTAT_FUNC_NPARTITIONS; i++)
LWLockInitialize(&s->func_locks[i].lock, LWTRANCHE_PGSTAT_HASH);
LWLockInitialize(&s->global_lock.lock, LWTRANCHE_PGSTAT_HASH);
rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "\0", "\0");
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(PgStatSharedDBEntry);
ctl.hash = oid_hash;
s->db_hash = ShmemInitHash("PgStat DB Hash", PGSTAT_SHMEM_DB_HASH_SIZE,
PGSTAT_SHMEM_DB_HASH_SIZE, &ctl, HASH_ELEM | HASH_FUNCTION);
rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "\0", "\0");
ctl.keysize = sizeof(PgStatSharedTabKey);
ctl.entrysize = sizeof(PgStatSharedTabEntry);
ctl.hash = tag_hash;
s->tab_hash = ShmemInitHash("PgStat TAB Hash", PGSTAT_SHMEM_TAB_HASH_SIZE,
PGSTAT_SHMEM_TAB_HASH_SIZE, &ctl, HASH_ELEM | HASH_FUNCTION);
rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "\0", "\0");
ctl.keysize = sizeof(PgStatSharedFuncKey);
ctl.entrysize = sizeof(PgStatSharedFuncEntry);
ctl.hash = tag_hash;
s->func_hash = ShmemInitHash("PgStat FUNC Hash", PGSTAT_SHMEM_FUNC_HASH_SIZE,
PGSTAT_SHMEM_FUNC_HASH_SIZE, &ctl, HASH_ELEM | HASH_FUNCTION);
rc = memset_s(&s->global_stats, sizeof(PgStat_GlobalStats), 0, sizeof(PgStat_GlobalStats));
securec_check(rc, "\0", "\0");
s->global_stats.stat_reset_timestamp = GetCurrentTimestamp();
s->stats_file_load_attempted = false;
s->stats_epoch = 0;
}
uint64 pgstat_shared_get_epoch(void)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return 0;
}
return pg_atomic_read_u64((volatile uint64*)&s->stats_epoch);
}
void pgstat_shared_bump_epoch(void)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s != NULL) {
(void)pg_atomic_fetch_add_u64((volatile uint64*)&s->stats_epoch, 1);
}
}
PgStatSharedDBEntry* pgstat_shared_get_db_entry(Oid dbid, bool create, LWLockMode mode, LWLock** lock, bool* found)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return NULL;
}
LWLock* l = pgstat_db_lock(s, dbid);
bool locked = false;
if (!LWLockHeldByMe(l)) {
LWLockAcquire(l, mode);
locked = true;
}
HASHACTION action = create ? HASH_ENTER : HASH_FIND;
bool local_found = false;
PgStatSharedDBEntry* entry =
(PgStatSharedDBEntry*)hash_search(s->db_hash, &dbid, action, &local_found);
if (entry == NULL) {
if (create) {
ereport(WARNING,
(errmsg("pgstat db entry creation failed (hash may be full), database %u", dbid)));
}
if (locked) {
LWLockRelease(l);
}
if (lock) {
*lock = NULL;
}
if (found) {
*found = false;
}
return NULL;
}
if (!local_found && create) {
pgstat_shared_init_db_entry(entry, dbid);
{
long num_entries = hash_get_num_entries(s->db_hash);
if (num_entries >= (long)(PGSTAT_SHMEM_DB_HASH_SIZE * PGSTAT_SHMEM_HASH_NEAR_FULL_RATIO)) {
ereport(WARNING,
(errmsg("pgstat db hash near full: current entries %ld, limit %d; "
"new databases may not be recorded",
num_entries, PGSTAT_SHMEM_DB_HASH_SIZE)));
}
}
}
if (found) {
*found = local_found;
}
if (lock) {
*lock = locked ? l : NULL;
}
return entry;
}
uint32 pgstat_tab_partition_index(const PgStatSharedTabKey* key)
{
return pgstat_hash_tabkey(key) % (uint32)PGSTAT_TAB_NPARTITIONS;
}
LWLock* pgstat_shared_tab_lock_for_key(const PgStatSharedTabKey* key)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return NULL;
}
return pgstat_tab_lock(s, key);
}
* Like pgstat_shared_get_tab_entry, but caller must already hold the tab partition LWLock
* for this key (same lock as pgstat_shared_tab_lock_for_key(key)).
*/
PgStatSharedTabEntry* pgstat_shared_get_tab_entry_under_tablock(const PgStatSharedTabKey* key, bool create,
bool* found)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return NULL;
}
LWLock* l = pgstat_tab_lock(s, key);
Assert(LWLockHeldByMe(l));
HASHACTION action = create ? HASH_ENTER : HASH_FIND;
bool local_found = false;
PgStatSharedTabEntry* entry = (PgStatSharedTabEntry*)hash_search(s->tab_hash, key, action, &local_found);
if (entry == NULL) {
if (create) {
ereport(WARNING,
(errmsg("pgstat tab entry creation failed (hash may be full), database %u table %u statFlag %u",
key->databaseid, key->tableid, key->statFlag)));
}
if (found) {
*found = false;
}
return NULL;
}
if (!local_found && create) {
pgstat_shared_init_tab_entry(entry, key);
{
long num_entries = hash_get_num_entries(s->tab_hash);
if (num_entries >= (long)(PGSTAT_SHMEM_TAB_HASH_SIZE * PGSTAT_SHMEM_HASH_NEAR_FULL_RATIO)) {
ereport(WARNING,
(errmsg("pgstat tab hash near full: current entries %ld, limit %d; "
"new relations may not be recorded, consider running VACUUM or increasing capacity",
num_entries, PGSTAT_SHMEM_TAB_HASH_SIZE)));
}
}
}
if (found) {
*found = local_found;
}
return entry;
}
PgStatSharedTabEntry* pgstat_shared_get_tab_entry(const PgStatSharedTabKey* key, bool create, LWLockMode mode,
LWLock** lock, bool* found)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return NULL;
}
LWLock* l = pgstat_tab_lock(s, key);
bool locked = false;
if (!LWLockHeldByMe(l)) {
LWLockAcquire(l, mode);
locked = true;
}
HASHACTION action = create ? HASH_ENTER : HASH_FIND;
bool local_found = false;
PgStatSharedTabEntry* entry =
(PgStatSharedTabEntry*)hash_search(s->tab_hash, key, action, &local_found);
if (entry == NULL) {
if (create) {
ereport(WARNING,
(errmsg("pgstat tab entry creation failed (hash may be full), database %u table %u statFlag %u",
key->databaseid, key->tableid, key->statFlag)));
}
if (locked) {
LWLockRelease(l);
}
if (lock) {
*lock = NULL;
}
if (found) {
*found = false;
}
return NULL;
}
if (!local_found && create) {
pgstat_shared_init_tab_entry(entry, key);
{
long num_entries = hash_get_num_entries(s->tab_hash);
if (num_entries >= (long)(PGSTAT_SHMEM_TAB_HASH_SIZE * PGSTAT_SHMEM_HASH_NEAR_FULL_RATIO)) {
ereport(WARNING,
(errmsg("pgstat tab hash near full: current entries %ld, limit %d; "
"new relations may not be recorded, consider running VACUUM or increasing capacity",
num_entries, PGSTAT_SHMEM_TAB_HASH_SIZE)));
}
}
}
if (found) {
*found = local_found;
}
if (lock) {
*lock = locked ? l : NULL;
}
return entry;
}
PgStatSharedFuncEntry* pgstat_shared_get_func_entry(
const PgStatSharedFuncKey* key, bool create, LWLockMode mode, LWLock** lock, bool* found)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return NULL;
}
LWLock* l = pgstat_func_lock(s, key);
bool locked = false;
if (!LWLockHeldByMe(l)) {
LWLockAcquire(l, mode);
locked = true;
}
HASHACTION action = create ? HASH_ENTER : HASH_FIND;
bool local_found = false;
PgStatSharedFuncEntry* entry =
(PgStatSharedFuncEntry*)hash_search(s->func_hash, key, action, &local_found);
if (entry == NULL) {
if (create) {
ereport(WARNING,
(errmsg("pgstat func entry creation failed (hash may be full), database %u function %u",
key->databaseid, key->functionid)));
}
if (locked) {
LWLockRelease(l);
}
if (lock) {
*lock = NULL;
}
if (found) {
*found = false;
}
return NULL;
}
if (!local_found && create) {
pgstat_shared_init_func_entry(entry, key);
pgstat_func_hash_warn_near_full(s);
}
if (found) {
*found = local_found;
}
if (lock) {
*lock = locked ? l : NULL;
}
return entry;
}
void pgstat_shared_release_lock(LWLock* lock)
{
if (lock != NULL) {
LWLockRelease(lock);
}
}
bool pgstat_shared_remove_tab_entry(Oid dbid, Oid relid, uint32 statFlag, PgStatSharedTabEntry* removed)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return false;
}
PgStatSharedTabKey key;
key.databaseid = dbid;
key.tableid = relid;
key.statFlag = statFlag;
LWLock* l = pgstat_tab_lock(s, &key);
LWLockAcquire(l, LW_EXCLUSIVE);
PgStatSharedTabEntry* entry = (PgStatSharedTabEntry*)hash_search(s->tab_hash, &key, HASH_FIND, NULL);
if (entry == NULL) {
LWLockRelease(l);
return false;
}
if (removed != NULL) {
errno_t rc = memcpy_s(removed, sizeof(PgStatSharedTabEntry), entry, sizeof(PgStatSharedTabEntry));
securec_check(rc, "\0", "\0");
}
(void)hash_search(s->tab_hash, &key, HASH_REMOVE, NULL);
LWLockRelease(l);
return true;
}
bool pgstat_shared_remove_func_entry(Oid dbid, Oid funcid)
{
PgStatSharedState* s = pgstat_get_shared_state();
if (s == NULL) {
return false;
}
PgStatSharedFuncKey key;
key.databaseid = dbid;
key.functionid = funcid;
LWLock* l = pgstat_func_lock(s, &key);
LWLockAcquire(l, LW_EXCLUSIVE);
PgStatSharedFuncEntry* entry =
(PgStatSharedFuncEntry*)hash_search(s->func_hash, &key, HASH_FIND, NULL);
if (entry == NULL) {
LWLockRelease(l);
return false;
}
(void)hash_search(s->func_hash, &key, HASH_REMOVE, NULL);
LWLockRelease(l);
return true;
}
static void pgstat_shared_remove_tab_by_dbid(Oid dbid)
{
if (pgstat_get_shared_state() == NULL) {
return;
}
pgstat_lock_all(pgstat_get_shared_state()->tab_locks, PGSTAT_TAB_NPARTITIONS, LW_EXCLUSIVE);
HASH_SEQ_STATUS seq;
hash_seq_init(&seq, pgstat_get_shared_state()->tab_hash);
PgStatSharedTabEntry* entry = NULL;
while ((entry = (PgStatSharedTabEntry*)hash_seq_search(&seq)) != NULL) {
if (entry->key.databaseid != dbid) {
continue;
}
PgStatSharedTabKey key = entry->key;
(void)hash_search(pgstat_get_shared_state()->tab_hash, &key, HASH_REMOVE, NULL);
}
pgstat_unlock_all(pgstat_get_shared_state()->tab_locks, PGSTAT_TAB_NPARTITIONS);
}
static void pgstat_shared_remove_func_by_dbid(Oid dbid)
{
if (pgstat_get_shared_state() == NULL) {
return;
}
pgstat_lock_all(pgstat_get_shared_state()->func_locks, PGSTAT_FUNC_NPARTITIONS, LW_EXCLUSIVE);
HASH_SEQ_STATUS seq;
hash_seq_init(&seq, pgstat_get_shared_state()->func_hash);
PgStatSharedFuncEntry* entry = NULL;
while ((entry = (PgStatSharedFuncEntry*)hash_seq_search(&seq)) != NULL) {
if (entry->key.databaseid != dbid) {
continue;
}
PgStatSharedFuncKey key = entry->key;
(void)hash_search(pgstat_get_shared_state()->func_hash, &key, HASH_REMOVE, NULL);
}
pgstat_unlock_all(pgstat_get_shared_state()->func_locks, PGSTAT_FUNC_NPARTITIONS);
}
void pgstat_shared_reset_db(Oid dbid)
{
LWLock* lock = NULL;
PgStatSharedDBEntry* dbentry = pgstat_shared_get_db_entry(dbid, false, LW_EXCLUSIVE, &lock, NULL);
if (dbentry != NULL) {
errno_t rc = memset_s(dbentry, sizeof(PgStatSharedDBEntry), 0, sizeof(PgStatSharedDBEntry));
securec_check(rc, "\0", "\0");
dbentry->databaseid = dbid;
dbentry->stat_reset_timestamp = GetCurrentTimestamp();
pgstat_shared_release_lock(lock);
}
pgstat_shared_remove_tab_by_dbid(dbid);
pgstat_shared_remove_func_by_dbid(dbid);
pgstat_shared_bump_epoch();
}
void pgstat_shared_reset_sharedcounter(PgStat_Shared_Reset_Target target)
{
if (pgstat_get_shared_state() == NULL) {
return;
}
if (target != RESET_BGWRITER) {
return;
}
LWLock* lock = pgstat_shared_global_lock();
if (lock == NULL) {
return;
}
LWLockAcquire(lock, LW_EXCLUSIVE);
errno_t rc = memset_s(&pgstat_get_shared_state()->global_stats,
sizeof(PgStat_GlobalStats), 0, sizeof(PgStat_GlobalStats));
securec_check(rc, "\0", "\0");
pgstat_get_shared_state()->global_stats.stat_reset_timestamp = GetCurrentTimestamp();
LWLockRelease(lock);
}
void pgstat_shared_drop_db(Oid dbid)
{
LWLock* lock = NULL;
PgStatSharedDBEntry* dbentry = pgstat_shared_get_db_entry(dbid, false, LW_EXCLUSIVE, &lock, NULL);
if (dbentry != NULL) {
(void)hash_search(pgstat_get_shared_state()->db_hash, &dbid, HASH_REMOVE, NULL);
pgstat_shared_release_lock(lock);
}
pgstat_shared_remove_tab_by_dbid(dbid);
pgstat_shared_remove_func_by_dbid(dbid);
pgstat_shared_bump_epoch();
}
static PgStat_StatDBEntry* snapshot_get_db_entry(HTAB* dbhash, MemoryContext mcxt, Oid dbid, bool create)
{
bool found = false;
HASHACTION action = create ? HASH_ENTER : HASH_FIND;
PgStat_StatDBEntry* entry = (PgStat_StatDBEntry*)hash_search(dbhash, &dbid, action, &found);
if (entry == NULL) {
return NULL;
}
if (!found && create) {
errno_t rc = memset_s(entry, sizeof(PgStat_StatDBEntry), 0, sizeof(PgStat_StatDBEntry));
securec_check(rc, "\0", "\0");
entry->databaseid = dbid;
HASHCTL hash_ctl;
rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
securec_check(rc, "\0", "\0");
hash_ctl.keysize = sizeof(PgStat_StatTabKey);
hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
hash_ctl.hash = tag_hash;
hash_ctl.hcxt = mcxt;
entry->tables =
hash_create("Per-database table", PGSTAT_SNAPSHOT_TAB_HASH_SIZE, &hash_ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
hash_ctl.hash = oid_hash;
entry->functions =
hash_create("Per-database function", PGSTAT_SNAPSHOT_FUNC_HASH_SIZE, &hash_ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
}
return entry;
}
static void copy_snapshot_copy_tab_from_shared(PgStat_StatTabEntry* tabentry, const PgStatSharedTabEntry* stab)
{
tabentry->numscans = stab->numscans;
tabentry->lastscan = stab->lastscan;
tabentry->tuples_returned = stab->tuples_returned;
tabentry->tuples_fetched = stab->tuples_fetched;
tabentry->tuples_inserted = stab->tuples_inserted;
tabentry->tuples_updated = stab->tuples_updated;
tabentry->tuples_deleted = stab->tuples_deleted;
tabentry->tuples_inplace_updated = stab->tuples_inplace_updated;
tabentry->tuples_hot_updated = stab->tuples_hot_updated;
tabentry->n_live_tuples = stab->n_live_tuples;
tabentry->n_dead_tuples = stab->n_dead_tuples;
tabentry->changes_since_analyze = stab->changes_since_analyze;
tabentry->blocks_fetched = stab->blocks_fetched;
tabentry->blocks_hit = stab->blocks_hit;
tabentry->cu_mem_hit = stab->cu_mem_hit;
tabentry->cu_hdd_sync = stab->cu_hdd_sync;
tabentry->cu_hdd_asyn = stab->cu_hdd_asyn;
tabentry->success_prune_cnt = stab->success_prune_cnt;
tabentry->total_prune_cnt = stab->total_prune_cnt;
tabentry->vacuum_timestamp = stab->vacuum_timestamp;
tabentry->vacuum_count = stab->vacuum_count;
tabentry->autovac_vacuum_timestamp = stab->autovac_vacuum_timestamp;
tabentry->autovac_vacuum_count = stab->autovac_vacuum_count;
tabentry->analyze_timestamp = stab->analyze_timestamp;
tabentry->analyze_count = stab->analyze_count;
tabentry->autovac_analyze_timestamp = stab->autovac_analyze_timestamp;
tabentry->autovac_analyze_count = stab->autovac_analyze_count;
tabentry->data_changed_timestamp = stab->data_changed_timestamp;
tabentry->autovac_status = stab->autovac_status;
}
static void copy_snapshot_fill_db_entries(HTAB* dbhash, MemoryContext mcxt, Oid onlydb)
{
pgstat_lock_all(pgstat_get_shared_state()->db_locks, PGSTAT_DB_NPARTITIONS, LW_SHARED);
HASH_SEQ_STATUS hstat;
hash_seq_init(&hstat, pgstat_get_shared_state()->db_hash);
PgStatSharedDBEntry* sdb = NULL;
while ((sdb = (PgStatSharedDBEntry*)hash_seq_search(&hstat)) != NULL) {
if (!pgstat_dbid_visible(sdb->databaseid, onlydb)) {
continue;
}
PgStat_StatDBEntry* dbentry = snapshot_get_db_entry(dbhash, mcxt, sdb->databaseid, true);
if (dbentry == NULL) {
continue;
}
dbentry->n_xact_commit = sdb->n_xact_commit;
dbentry->n_xact_rollback = sdb->n_xact_rollback;
dbentry->n_blocks_fetched = sdb->n_blocks_fetched;
dbentry->n_blocks_hit = sdb->n_blocks_hit;
dbentry->n_cu_mem_hit = sdb->n_cu_mem_hit;
dbentry->n_cu_hdd_sync = sdb->n_cu_hdd_sync;
dbentry->n_cu_hdd_asyn = sdb->n_cu_hdd_asyn;
dbentry->n_tuples_returned = sdb->n_tuples_returned;
dbentry->n_tuples_fetched = sdb->n_tuples_fetched;
dbentry->n_tuples_inserted = sdb->n_tuples_inserted;
dbentry->n_tuples_updated = sdb->n_tuples_updated;
dbentry->n_tuples_deleted = sdb->n_tuples_deleted;
dbentry->last_autovac_time = sdb->last_autovac_time;
dbentry->n_conflict_tablespace = sdb->n_conflict_tablespace;
dbentry->n_conflict_lock = sdb->n_conflict_lock;
dbentry->n_conflict_snapshot = sdb->n_conflict_snapshot;
dbentry->n_conflict_bufferpin = sdb->n_conflict_bufferpin;
dbentry->n_conflict_startup_deadlock = sdb->n_conflict_startup_deadlock;
dbentry->n_temp_files = sdb->n_temp_files;
dbentry->n_temp_bytes = sdb->n_temp_bytes;
dbentry->n_deadlocks = sdb->n_deadlocks;
dbentry->n_block_read_time = sdb->n_block_read_time;
dbentry->n_block_write_time = sdb->n_block_write_time;
dbentry->n_mem_mbytes_reserved = sdb->n_mem_mbytes_reserved;
dbentry->stat_reset_timestamp = sdb->stat_reset_timestamp;
}
pgstat_unlock_all(pgstat_get_shared_state()->db_locks, PGSTAT_DB_NPARTITIONS);
}
static void copy_snapshot_fill_tab_entries(HTAB* dbhash, MemoryContext mcxt, Oid onlydb)
{
pgstat_lock_all(pgstat_get_shared_state()->tab_locks, PGSTAT_TAB_NPARTITIONS, LW_SHARED);
HASH_SEQ_STATUS tstat;
hash_seq_init(&tstat, pgstat_get_shared_state()->tab_hash);
PgStatSharedTabEntry* stab = NULL;
while ((stab = (PgStatSharedTabEntry*)hash_seq_search(&tstat)) != NULL) {
if (!pgstat_dbid_visible(stab->key.databaseid, onlydb)) {
continue;
}
PgStat_StatDBEntry* dbentry = snapshot_get_db_entry(dbhash, mcxt, stab->key.databaseid, true);
if (dbentry == NULL || dbentry->tables == NULL) {
continue;
}
PgStat_StatTabKey tabkey;
tabkey.tableid = stab->key.tableid;
tabkey.statFlag = stab->key.statFlag;
bool found = false;
PgStat_StatTabEntry* tabentry =
(PgStat_StatTabEntry*)hash_search(dbentry->tables, &tabkey, HASH_ENTER, &found);
if (tabentry != NULL) {
tabentry->tablekey = tabkey;
copy_snapshot_copy_tab_from_shared(tabentry, stab);
}
}
pgstat_unlock_all(pgstat_get_shared_state()->tab_locks, PGSTAT_TAB_NPARTITIONS);
}
static void copy_snapshot_fill_func_entries(HTAB* dbhash, MemoryContext mcxt, Oid onlydb)
{
pgstat_lock_all(pgstat_get_shared_state()->func_locks, PGSTAT_FUNC_NPARTITIONS, LW_SHARED);
HASH_SEQ_STATUS fstat;
hash_seq_init(&fstat, pgstat_get_shared_state()->func_hash);
PgStatSharedFuncEntry* sfunc = NULL;
while ((sfunc = (PgStatSharedFuncEntry*)hash_seq_search(&fstat)) != NULL) {
if (!pgstat_dbid_visible(sfunc->key.databaseid, onlydb)) {
continue;
}
PgStat_StatDBEntry* dbentry = snapshot_get_db_entry(dbhash, mcxt, sfunc->key.databaseid, true);
if (dbentry == NULL || dbentry->functions == NULL) {
continue;
}
bool found = false;
PgStat_StatFuncEntry* funcentry =
(PgStat_StatFuncEntry*)hash_search(dbentry->functions, &sfunc->key.functionid, HASH_ENTER, &found);
if (funcentry != NULL) {
funcentry->functionid = sfunc->key.functionid;
funcentry->f_numcalls = sfunc->f_numcalls;
funcentry->f_total_time = sfunc->f_total_time;
funcentry->f_self_time = sfunc->f_self_time;
}
}
pgstat_unlock_all(pgstat_get_shared_state()->func_locks, PGSTAT_FUNC_NPARTITIONS);
}
void pgstat_shared_copy_snapshot(Oid onlydb, MemoryContext mcxt, HTAB** out_dbhash, PgStat_GlobalStats* out_global)
{
if (out_dbhash != NULL) {
*out_dbhash = NULL;
}
if (out_global != NULL) {
(void)memset_s(out_global, sizeof(PgStat_GlobalStats), 0, sizeof(PgStat_GlobalStats));
}
if (pgstat_get_shared_state() == NULL || mcxt == NULL) {
return;
}
MemoryContext old = MemoryContextSwitchTo(mcxt);
HASHCTL hash_ctl;
errno_t rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
securec_check(rc, "\0", "\0");
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(PgStat_StatDBEntry);
hash_ctl.hash = oid_hash;
hash_ctl.hcxt = mcxt;
HTAB* dbhash =
hash_create("Databases hash", PGSTAT_SNAPSHOT_DB_HASH_SIZE,
&hash_ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
if (out_dbhash != NULL) {
*out_dbhash = dbhash;
}
LWLock* glock = pgstat_shared_global_lock();
if (glock != NULL && out_global != NULL) {
LWLockAcquire(glock, LW_SHARED);
*out_global = pgstat_get_shared_state()->global_stats;
LWLockRelease(glock);
}
copy_snapshot_fill_db_entries(dbhash, mcxt, onlydb);
copy_snapshot_fill_tab_entries(dbhash, mcxt, onlydb);
copy_snapshot_fill_func_entries(dbhash, mcxt, onlydb);
MemoryContextSwitchTo(old);
}
static void pgstat_shared_clear_all_hashes(void)
{
if (pgstat_get_shared_state() == NULL) {
return;
}
pgstat_lock_all(pgstat_get_shared_state()->db_locks, PGSTAT_DB_NPARTITIONS, LW_EXCLUSIVE);
HASH_SEQ_STATUS dbseq;
hash_seq_init(&dbseq, pgstat_get_shared_state()->db_hash);
PgStatSharedDBEntry* entry = NULL;
while ((entry = (PgStatSharedDBEntry*)hash_seq_search(&dbseq)) != NULL) {
Oid dbid = entry->databaseid;
(void)hash_search(pgstat_get_shared_state()->db_hash, &dbid, HASH_REMOVE, NULL);
}
pgstat_unlock_all(pgstat_get_shared_state()->db_locks, PGSTAT_DB_NPARTITIONS);
pgstat_lock_all(pgstat_get_shared_state()->tab_locks, PGSTAT_TAB_NPARTITIONS, LW_EXCLUSIVE);
HASH_SEQ_STATUS tabseq;
hash_seq_init(&tabseq, pgstat_get_shared_state()->tab_hash);
PgStatSharedTabEntry* tab_entry = NULL;
while ((tab_entry = (PgStatSharedTabEntry*)hash_seq_search(&tabseq)) != NULL) {
PgStatSharedTabKey key = tab_entry->key;
(void)hash_search(pgstat_get_shared_state()->tab_hash, &key, HASH_REMOVE, NULL);
}
pgstat_unlock_all(pgstat_get_shared_state()->tab_locks, PGSTAT_TAB_NPARTITIONS);
pgstat_lock_all(pgstat_get_shared_state()->func_locks, PGSTAT_FUNC_NPARTITIONS, LW_EXCLUSIVE);
HASH_SEQ_STATUS funcseq;
hash_seq_init(&funcseq, pgstat_get_shared_state()->func_hash);
PgStatSharedFuncEntry* func_entry = NULL;
while ((func_entry = (PgStatSharedFuncEntry*)hash_seq_search(&funcseq)) != NULL) {
PgStatSharedFuncKey key = func_entry->key;
(void)hash_search(pgstat_get_shared_state()->func_hash, &key, HASH_REMOVE, NULL);
}
pgstat_unlock_all(pgstat_get_shared_state()->func_locks, PGSTAT_FUNC_NPARTITIONS);
}
static void import_snapshot_copy_tab_to_shared(PgStatSharedTabEntry* stab, const PgStat_StatTabEntry* tabentry)
{
stab->numscans = tabentry->numscans;
stab->lastscan = tabentry->lastscan;
stab->tuples_returned = tabentry->tuples_returned;
stab->tuples_fetched = tabentry->tuples_fetched;
stab->tuples_inserted = tabentry->tuples_inserted;
stab->tuples_updated = tabentry->tuples_updated;
stab->tuples_deleted = tabentry->tuples_deleted;
stab->tuples_inplace_updated = tabentry->tuples_inplace_updated;
stab->tuples_hot_updated = tabentry->tuples_hot_updated;
stab->n_live_tuples = tabentry->n_live_tuples;
stab->n_dead_tuples = tabentry->n_dead_tuples;
stab->changes_since_analyze = tabentry->changes_since_analyze;
stab->blocks_fetched = tabentry->blocks_fetched;
stab->blocks_hit = tabentry->blocks_hit;
stab->cu_mem_hit = tabentry->cu_mem_hit;
stab->cu_hdd_sync = tabentry->cu_hdd_sync;
stab->cu_hdd_asyn = tabentry->cu_hdd_asyn;
stab->success_prune_cnt = tabentry->success_prune_cnt;
stab->total_prune_cnt = tabentry->total_prune_cnt;
stab->vacuum_timestamp = tabentry->vacuum_timestamp;
stab->vacuum_count = tabentry->vacuum_count;
stab->autovac_vacuum_timestamp = tabentry->autovac_vacuum_timestamp;
stab->autovac_vacuum_count = tabentry->autovac_vacuum_count;
stab->analyze_timestamp = tabentry->analyze_timestamp;
stab->analyze_count = tabentry->analyze_count;
stab->autovac_analyze_timestamp = tabentry->autovac_analyze_timestamp;
stab->autovac_analyze_count = tabentry->autovac_analyze_count;
stab->data_changed_timestamp = tabentry->data_changed_timestamp;
stab->autovac_status = tabentry->autovac_status;
}
static void import_snapshot_copy_tables_to_shmem(Oid databaseid, HTAB* tables)
{
if (tables == NULL) {
return;
}
HASH_SEQ_STATUS tstat;
hash_seq_init(&tstat, tables);
PgStat_StatTabEntry* tabentry = NULL;
while ((tabentry = (PgStat_StatTabEntry*)hash_seq_search(&tstat)) != NULL) {
LWLock* tlock = NULL;
PgStatSharedTabKey key;
key.databaseid = databaseid;
key.tableid = tabentry->tablekey.tableid;
key.statFlag = tabentry->tablekey.statFlag;
PgStatSharedTabEntry* stab =
pgstat_shared_get_tab_entry(&key, true, LW_EXCLUSIVE, &tlock, NULL);
if (stab != NULL) {
import_snapshot_copy_tab_to_shared(stab, tabentry);
pgstat_shared_release_lock(tlock);
}
}
}
static void import_snapshot_copy_functions_to_shmem(Oid databaseid, HTAB* functions)
{
if (functions == NULL) {
return;
}
HASH_SEQ_STATUS fstat;
hash_seq_init(&fstat, functions);
PgStat_StatFuncEntry* funcentry = NULL;
while ((funcentry = (PgStat_StatFuncEntry*)hash_seq_search(&fstat)) != NULL) {
LWLock* flock = NULL;
PgStatSharedFuncKey key;
key.databaseid = databaseid;
key.functionid = funcentry->functionid;
PgStatSharedFuncEntry* sfunc =
pgstat_shared_get_func_entry(&key, true, LW_EXCLUSIVE, &flock, NULL);
if (sfunc != NULL) {
sfunc->f_numcalls = funcentry->f_numcalls;
sfunc->f_total_time = funcentry->f_total_time;
sfunc->f_self_time = funcentry->f_self_time;
pgstat_shared_release_lock(flock);
}
}
}
static void import_snapshot_copy_one_db(PgStat_StatDBEntry* dbentry)
{
LWLock* dblock = NULL;
PgStatSharedDBEntry* sdb =
pgstat_shared_get_db_entry(dbentry->databaseid, true, LW_EXCLUSIVE, &dblock, NULL);
if (sdb == NULL) {
return;
}
sdb->n_xact_commit = dbentry->n_xact_commit;
sdb->n_xact_rollback = dbentry->n_xact_rollback;
sdb->n_blocks_fetched = dbentry->n_blocks_fetched;
sdb->n_blocks_hit = dbentry->n_blocks_hit;
sdb->n_cu_mem_hit = dbentry->n_cu_mem_hit;
sdb->n_cu_hdd_sync = dbentry->n_cu_hdd_sync;
sdb->n_cu_hdd_asyn = dbentry->n_cu_hdd_asyn;
sdb->n_tuples_returned = dbentry->n_tuples_returned;
sdb->n_tuples_fetched = dbentry->n_tuples_fetched;
sdb->n_tuples_inserted = dbentry->n_tuples_inserted;
sdb->n_tuples_updated = dbentry->n_tuples_updated;
sdb->n_tuples_deleted = dbentry->n_tuples_deleted;
sdb->last_autovac_time = dbentry->last_autovac_time;
sdb->n_conflict_tablespace = dbentry->n_conflict_tablespace;
sdb->n_conflict_lock = dbentry->n_conflict_lock;
sdb->n_conflict_snapshot = dbentry->n_conflict_snapshot;
sdb->n_conflict_bufferpin = dbentry->n_conflict_bufferpin;
sdb->n_conflict_startup_deadlock = dbentry->n_conflict_startup_deadlock;
sdb->n_temp_files = dbentry->n_temp_files;
sdb->n_temp_bytes = dbentry->n_temp_bytes;
sdb->n_deadlocks = dbentry->n_deadlocks;
sdb->n_block_read_time = dbentry->n_block_read_time;
sdb->n_block_write_time = dbentry->n_block_write_time;
sdb->n_mem_mbytes_reserved = dbentry->n_mem_mbytes_reserved;
sdb->stat_reset_timestamp = dbentry->stat_reset_timestamp;
pgstat_shared_release_lock(dblock);
import_snapshot_copy_tables_to_shmem(dbentry->databaseid, dbentry->tables);
import_snapshot_copy_functions_to_shmem(dbentry->databaseid, dbentry->functions);
}
void pgstat_shared_import_snapshot(HTAB* dbhash, const PgStat_GlobalStats* global)
{
if (pgstat_get_shared_state() == NULL || dbhash == NULL) {
return;
}
pgstat_shared_clear_all_hashes();
if (global != NULL) {
LWLock* glock = pgstat_shared_global_lock();
if (glock != NULL) {
LWLockAcquire(glock, LW_EXCLUSIVE);
pgstat_get_shared_state()->global_stats = *global;
LWLockRelease(glock);
}
}
HASH_SEQ_STATUS hstat;
hash_seq_init(&hstat, dbhash);
PgStat_StatDBEntry* dbentry = NULL;
while ((dbentry = (PgStat_StatDBEntry*)hash_seq_search(&hstat)) != NULL)
import_snapshot_copy_one_db(dbentry);
}