From 8ca3db5468e51b48242887b7ef6e03d40bc6663e Mon Sep 17 00:00:00 2001 From: Sami Imseih Date: Tue, 27 Oct 2026 20:13:23 +0000 Subject: [PATCH v1 1/1] pgstat: Add helper to drop entries from a stats kind's hash pgstat_drop_matching_entries() scans all variable-numbered stats hash tables. Some callers already know which stats kind they want to scan, and after 44539786b82 such a kind may have its own dshash. This fills in a piece left by that work for callers that drop matching entries of a known stats kind. Add pgstat_drop_matching_entries_of_kind(), which scans the hash table mapped to one stats kind. pgstat_drop_matching_entries() keeps the same behavior by using the same per-hash helper without a kind filter. An immediate motivation is pg_stat_statements, where follow-up work can use this to drop matching entries from its stats kind. Other stats kinds that know their kind can use it as well. Adjust test_custom_stats so its existing named drop path uses the new helper. --- src/backend/utils/activity/pgstat_shmem.c | 86 ++++++++++++++----- src/include/utils/pgstat_internal.h | 3 + .../test_custom_stats/test_custom_var_stats.c | 14 ++- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c index 8500927a68b..14934eac36c 100644 --- a/src/backend/utils/activity/pgstat_shmem.c +++ b/src/backend/utils/activity/pgstat_shmem.c @@ -48,6 +48,10 @@ typedef struct PgStat_EntryRefHashEntry static void pgstat_drop_database_and_contents(Oid dboid); static void pgstat_free_entry(PgStatShared_HashEntry *shent, dshash_seq_status *hstat); +static uint64 pgstat_drop_matching_entries_from_hash(dshash_table *hash, + PgStat_Kind filter_kind, + bool (*do_drop) (PgStatShared_HashEntry *, Datum), + Datum match_data); static void pgstat_release_entry_ref(PgStat_HashKey key, PgStat_EntryRef *entry_ref, bool discard_pending); static bool pgstat_need_entry_refs_gc(void); @@ -1234,39 +1238,79 @@ pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid, void pgstat_drop_matching_entries(bool (*do_drop) (PgStatShared_HashEntry *, Datum), Datum match_data) +{ + uint64 not_freed_count = 0; + + for (int h = 0; h < pgStatLocal.num_var_hashes; h++) + not_freed_count += + pgstat_drop_matching_entries_from_hash(pgStatLocal.var_hashes[h], + PGSTAT_KIND_INVALID, + do_drop, match_data); + + if (not_freed_count > 0) + pgstat_request_entry_refs_gc(); +} + +static uint64 +pgstat_drop_matching_entries_from_hash(dshash_table *hash, + PgStat_Kind filter_kind, + bool (*do_drop) (PgStatShared_HashEntry *, Datum), + Datum match_data) { dshash_seq_status hstat; PgStatShared_HashEntry *ps; uint64 not_freed_count = 0; - for (int h = 0; h < pgStatLocal.num_var_hashes; h++) + /* entries are removed, take an exclusive lock */ + dshash_seq_init(&hstat, hash, true); + while ((ps = dshash_seq_next(&hstat)) != NULL) { - /* entries are removed, take an exclusive lock */ - dshash_seq_init(&hstat, pgStatLocal.var_hashes[h], true); - while ((ps = dshash_seq_next(&hstat)) != NULL) - { - if (ps->dropped) - continue; + if (ps->dropped) + continue; - if (do_drop != NULL && !do_drop(ps, match_data)) - continue; + if (filter_kind != PGSTAT_KIND_INVALID && + ps->key.kind != filter_kind) + continue; - /* delete local reference */ - if (pgStatEntryRefHash) - { - PgStat_EntryRefHashEntry *lohashent = - pgstat_entry_ref_hash_lookup(pgStatEntryRefHash, ps->key); + if (do_drop != NULL && !do_drop(ps, match_data)) + continue; - if (lohashent) - pgstat_release_entry_ref(lohashent->key, lohashent->entry_ref, - true); - } + /* delete local reference */ + if (pgStatEntryRefHash) + { + PgStat_EntryRefHashEntry *lohashent = + pgstat_entry_ref_hash_lookup(pgStatEntryRefHash, ps->key); - if (!pgstat_drop_entry_internal(ps, &hstat)) - not_freed_count++; + if (lohashent) + pgstat_release_entry_ref(lohashent->key, lohashent->entry_ref, + true); } - dshash_seq_term(&hstat); + + if (!pgstat_drop_entry_internal(ps, &hstat)) + not_freed_count++; } + dshash_seq_term(&hstat); + + return not_freed_count; +} + +/* + * Scan through the hashtable containing the variable-numbered stats kind, + * dropping entries of that kind if approved by the optional do_drop() function. + */ +void +pgstat_drop_matching_entries_of_kind(PgStat_Kind kind, + bool (*do_drop) (PgStatShared_HashEntry *, Datum), + Datum match_data) +{ + uint64 not_freed_count; + + Assert(kind >= PGSTAT_KIND_MIN && kind <= PGSTAT_KIND_MAX); + Assert(pgStatLocal.kind_hash[kind] != NULL); + + not_freed_count = + pgstat_drop_matching_entries_from_hash(pgStatLocal.kind_hash[kind], + kind, do_drop, match_data); if (not_freed_count > 0) pgstat_request_entry_refs_gc(); diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index 21403516e28..b1e8d33d35d 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -845,6 +845,9 @@ extern bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid, extern void pgstat_drop_all_entries(void); extern void pgstat_drop_matching_entries(bool (*do_drop) (PgStatShared_HashEntry *, Datum), Datum match_data); +extern void pgstat_drop_matching_entries_of_kind(PgStat_Kind kind, + bool (*do_drop) (PgStatShared_HashEntry *, Datum), + Datum match_data); extern PgStat_EntryRef *pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, uint64 objid, bool nowait); extern void pgstat_reset_entry(PgStat_Kind kind, Oid dboid, uint64 objid, TimestampTz ts); diff --git a/src/test/modules/test_custom_stats/test_custom_var_stats.c b/src/test/modules/test_custom_stats/test_custom_var_stats.c index ba6486fff6a..06ee2a8c9f0 100644 --- a/src/test/modules/test_custom_stats/test_custom_var_stats.c +++ b/src/test/modules/test_custom_stats/test_custom_var_stats.c @@ -635,6 +635,12 @@ test_custom_stats_var_update(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +static bool +test_custom_stats_var_match_entry(PgStatShared_HashEntry *p, Datum match_data) +{ + return p->key.objid == DatumGetUInt64(match_data); +} + /* * test_custom_stats_var_drop * Remove custom statistic entry @@ -646,11 +652,11 @@ Datum test_custom_stats_var_drop(PG_FUNCTION_ARGS) { char *stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + uint64 stat_idx = PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name); - /* Drop entry and request GC if the entry could not be freed */ - if (!pgstat_drop_entry(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid, - PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), false)) - pgstat_request_entry_refs_gc(); + pgstat_drop_matching_entries_of_kind(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, + test_custom_stats_var_match_entry, + UInt64GetDatum(stat_idx)); PG_RETURN_VOID(); } -- 2.50.1