LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_xact.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 96.0 % 125 120
Test Date: 2026-03-04 08:14:57 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* -------------------------------------------------------------------------
       2              :  *
       3              :  * pgstat_xact.c
       4              :  *    Transactional integration for the cumulative statistics system.
       5              :  *
       6              :  * Copyright (c) 2001-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *    src/backend/utils/activity/pgstat_xact.c
      10              :  * -------------------------------------------------------------------------
      11              :  */
      12              : 
      13              : #include "postgres.h"
      14              : 
      15              : #include "access/xact.h"
      16              : #include "pgstat.h"
      17              : #include "utils/memutils.h"
      18              : #include "utils/pgstat_internal.h"
      19              : 
      20              : 
      21              : typedef struct PgStat_PendingDroppedStatsItem
      22              : {
      23              :     xl_xact_stats_item item;
      24              :     bool        is_create;
      25              :     dlist_node  node;
      26              : } PgStat_PendingDroppedStatsItem;
      27              : 
      28              : 
      29              : static void AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit);
      30              : static void AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state,
      31              :                                             bool isCommit, int nestDepth);
      32              : 
      33              : static PgStat_SubXactStatus *pgStatXactStack = NULL;
      34              : 
      35              : 
      36              : /*
      37              :  * Called from access/transam/xact.c at top-level transaction commit/abort.
      38              :  */
      39              : void
      40       542395 : AtEOXact_PgStat(bool isCommit, bool parallel)
      41              : {
      42              :     PgStat_SubXactStatus *xact_state;
      43              : 
      44       542395 :     AtEOXact_PgStat_Database(isCommit, parallel);
      45              : 
      46              :     /* handle transactional stats information */
      47       542395 :     xact_state = pgStatXactStack;
      48       542395 :     if (xact_state != NULL)
      49              :     {
      50              :         Assert(xact_state->nest_level == 1);
      51              :         Assert(xact_state->prev == NULL);
      52              : 
      53       129714 :         AtEOXact_PgStat_Relations(xact_state, isCommit);
      54       129714 :         AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
      55              :     }
      56       542395 :     pgStatXactStack = NULL;
      57              : 
      58              :     /* Make sure any stats snapshot is thrown away */
      59       542395 :     pgstat_clear_snapshot();
      60       542395 : }
      61              : 
      62              : /*
      63              :  * When committing, drop stats for objects dropped in the transaction. When
      64              :  * aborting, drop stats for objects created in the transaction.
      65              :  */
      66              : static void
      67       129714 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
      68              : {
      69              :     dlist_mutable_iter iter;
      70       129714 :     int         not_freed_count = 0;
      71              : 
      72       129714 :     if (dclist_count(&xact_state->pending_drops) == 0)
      73        84469 :         return;
      74              : 
      75       168565 :     dclist_foreach_modify(iter, &xact_state->pending_drops)
      76              :     {
      77       123320 :         PgStat_PendingDroppedStatsItem *pending =
      78       123320 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
      79       123320 :         xl_xact_stats_item *it = &pending->item;
      80       123320 :         uint64      objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
      81              : 
      82       123320 :         if (isCommit && !pending->is_create)
      83              :         {
      84              :             /*
      85              :              * Transaction that dropped an object committed. Drop the stats
      86              :              * too.
      87              :              */
      88        47535 :             if (!pgstat_drop_entry(it->kind, it->dboid, objid))
      89         4994 :                 not_freed_count++;
      90              :         }
      91        80779 :         else if (!isCommit && pending->is_create)
      92              :         {
      93              :             /*
      94              :              * Transaction that created an object aborted. Drop the stats
      95              :              * associated with the object.
      96              :              */
      97         2511 :             if (!pgstat_drop_entry(it->kind, it->dboid, objid))
      98            0 :                 not_freed_count++;
      99              :         }
     100              : 
     101       123320 :         dclist_delete_from(&xact_state->pending_drops, &pending->node);
     102       123320 :         pfree(pending);
     103              :     }
     104              : 
     105        45245 :     if (not_freed_count > 0)
     106         1942 :         pgstat_request_entry_refs_gc();
     107              : }
     108              : 
     109              : /*
     110              :  * Called from access/transam/xact.c at subtransaction commit/abort.
     111              :  */
     112              : void
     113        10092 : AtEOSubXact_PgStat(bool isCommit, int nestDepth)
     114              : {
     115              :     PgStat_SubXactStatus *xact_state;
     116              : 
     117              :     /* merge the sub-transaction's transactional stats into the parent */
     118        10092 :     xact_state = pgStatXactStack;
     119        10092 :     if (xact_state != NULL &&
     120         4715 :         xact_state->nest_level >= nestDepth)
     121              :     {
     122              :         /* delink xact_state from stack immediately to simplify reuse case */
     123         4242 :         pgStatXactStack = xact_state->prev;
     124              : 
     125         4242 :         AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
     126         4242 :         AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
     127              : 
     128         4242 :         pfree(xact_state);
     129              :     }
     130        10092 : }
     131              : 
     132              : /*
     133              :  * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
     134              :  */
     135              : static void
     136         4242 : AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state,
     137              :                                 bool isCommit, int nestDepth)
     138              : {
     139              :     PgStat_SubXactStatus *parent_xact_state;
     140              :     dlist_mutable_iter iter;
     141         4242 :     int         not_freed_count = 0;
     142              : 
     143         4242 :     if (dclist_count(&xact_state->pending_drops) == 0)
     144         4158 :         return;
     145              : 
     146           84 :     parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
     147              : 
     148          247 :     dclist_foreach_modify(iter, &xact_state->pending_drops)
     149              :     {
     150          163 :         PgStat_PendingDroppedStatsItem *pending =
     151          163 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
     152          163 :         xl_xact_stats_item *it = &pending->item;
     153          163 :         uint64      objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
     154              : 
     155          163 :         dclist_delete_from(&xact_state->pending_drops, &pending->node);
     156              : 
     157          163 :         if (!isCommit && pending->is_create)
     158              :         {
     159              :             /*
     160              :              * Subtransaction creating a new stats object aborted. Drop the
     161              :              * stats object.
     162              :              */
     163           75 :             if (!pgstat_drop_entry(it->kind, it->dboid, objid))
     164            0 :                 not_freed_count++;
     165           75 :             pfree(pending);
     166              :         }
     167           88 :         else if (isCommit)
     168              :         {
     169              :             /*
     170              :              * Subtransaction dropping a stats object committed. Can't yet
     171              :              * remove the stats object, the surrounding transaction might
     172              :              * still abort. Pass it on to the parent.
     173              :              */
     174           61 :             dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
     175              :         }
     176              :         else
     177              :         {
     178           27 :             pfree(pending);
     179              :         }
     180              :     }
     181              : 
     182              :     Assert(dclist_count(&xact_state->pending_drops) == 0);
     183           84 :     if (not_freed_count > 0)
     184            0 :         pgstat_request_entry_refs_gc();
     185              : }
     186              : 
     187              : /*
     188              :  * Save the transactional stats state at 2PC transaction prepare.
     189              :  */
     190              : void
     191          315 : AtPrepare_PgStat(void)
     192              : {
     193              :     PgStat_SubXactStatus *xact_state;
     194              : 
     195          315 :     xact_state = pgStatXactStack;
     196          315 :     if (xact_state != NULL)
     197              :     {
     198              :         Assert(xact_state->nest_level == 1);
     199              :         Assert(xact_state->prev == NULL);
     200              : 
     201          307 :         AtPrepare_PgStat_Relations(xact_state);
     202              :     }
     203          315 : }
     204              : 
     205              : /*
     206              :  * Clean up after successful PREPARE.
     207              :  *
     208              :  * Note: AtEOXact_PgStat is not called during PREPARE.
     209              :  */
     210              : void
     211          315 : PostPrepare_PgStat(void)
     212              : {
     213              :     PgStat_SubXactStatus *xact_state;
     214              : 
     215              :     /*
     216              :      * We don't bother to free any of the transactional state, since it's all
     217              :      * in TopTransactionContext and will go away anyway.
     218              :      */
     219          315 :     xact_state = pgStatXactStack;
     220          315 :     if (xact_state != NULL)
     221              :     {
     222              :         Assert(xact_state->nest_level == 1);
     223              :         Assert(xact_state->prev == NULL);
     224              : 
     225          307 :         PostPrepare_PgStat_Relations(xact_state);
     226              :     }
     227          315 :     pgStatXactStack = NULL;
     228              : 
     229              :     /* Make sure any stats snapshot is thrown away */
     230          315 :     pgstat_clear_snapshot();
     231          315 : }
     232              : 
     233              : /*
     234              :  * Ensure (sub)transaction stack entry for the given nest_level exists, adding
     235              :  * it if needed.
     236              :  */
     237              : PgStat_SubXactStatus *
     238       487125 : pgstat_get_xact_stack_level(int nest_level)
     239              : {
     240              :     PgStat_SubXactStatus *xact_state;
     241              : 
     242       487125 :     xact_state = pgStatXactStack;
     243       487125 :     if (xact_state == NULL || xact_state->nest_level != nest_level)
     244              :     {
     245              :         xact_state = (PgStat_SubXactStatus *)
     246       134263 :             MemoryContextAlloc(TopTransactionContext,
     247              :                                sizeof(PgStat_SubXactStatus));
     248       134263 :         dclist_init(&xact_state->pending_drops);
     249       134263 :         xact_state->nest_level = nest_level;
     250       134263 :         xact_state->prev = pgStatXactStack;
     251       134263 :         xact_state->first = NULL;
     252       134263 :         pgStatXactStack = xact_state;
     253              :     }
     254       487125 :     return xact_state;
     255              : }
     256              : 
     257              : /*
     258              :  * Get stat items that need to be dropped at commit / abort.
     259              :  *
     260              :  * When committing, stats for objects that have been dropped in the
     261              :  * transaction are returned. When aborting, stats for newly created objects are
     262              :  * returned.
     263              :  *
     264              :  * Used by COMMIT / ABORT and 2PC PREPARE processing when building their
     265              :  * respective WAL records, to ensure stats are dropped in case of a crash / on
     266              :  * standbys.
     267              :  *
     268              :  * The list of items is allocated in CurrentMemoryContext and must be freed by
     269              :  * the caller (directly or via memory context reset).
     270              :  */
     271              : int
     272       521633 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
     273              : {
     274       521633 :     PgStat_SubXactStatus *xact_state = pgStatXactStack;
     275       521633 :     int         nitems = 0;
     276              :     dlist_iter  iter;
     277              : 
     278       521633 :     if (xact_state == NULL)
     279       390639 :         return 0;
     280              : 
     281              :     /*
     282              :      * We expect to be called for subtransaction abort (which logs a WAL
     283              :      * record), but not for subtransaction commit (which doesn't).
     284              :      */
     285              :     Assert(!isCommit || xact_state->nest_level == 1);
     286              :     Assert(!isCommit || xact_state->prev == NULL);
     287              : 
     288       130994 :     *items = palloc(dclist_count(&xact_state->pending_drops)
     289              :                     * sizeof(xl_xact_stats_item));
     290              : 
     291       255211 :     dclist_foreach(iter, &xact_state->pending_drops)
     292              :     {
     293       124217 :         PgStat_PendingDroppedStatsItem *pending =
     294       124217 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
     295              : 
     296       124217 :         if (isCommit && pending->is_create)
     297        77816 :             continue;
     298        46401 :         if (!isCommit && !pending->is_create)
     299          557 :             continue;
     300              : 
     301              :         Assert(nitems < dclist_count(&xact_state->pending_drops));
     302        45844 :         (*items)[nitems++] = pending->item;
     303              :     }
     304              : 
     305       130994 :     return nitems;
     306              : }
     307              : 
     308              : /*
     309              :  * Execute scheduled drops post-commit. Called from xact_redo_commit() /
     310              :  * xact_redo_abort() during recovery, and from FinishPreparedTransaction()
     311              :  * during normal 2PC COMMIT/ABORT PREPARED processing.
     312              :  */
     313              : void
     314         3577 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
     315              : {
     316         3577 :     int         not_freed_count = 0;
     317              : 
     318         3577 :     if (ndrops == 0)
     319          298 :         return;
     320              : 
     321        13148 :     for (int i = 0; i < ndrops; i++)
     322              :     {
     323         9869 :         xl_xact_stats_item *it = &items[i];
     324         9869 :         uint64      objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
     325              : 
     326         9869 :         if (!pgstat_drop_entry(it->kind, it->dboid, objid))
     327            2 :             not_freed_count++;
     328              :     }
     329              : 
     330         3279 :     if (not_freed_count > 0)
     331            2 :         pgstat_request_entry_refs_gc();
     332              : }
     333              : 
     334              : static void
     335       123497 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bool is_create)
     336              : {
     337       123497 :     int         nest_level = GetCurrentTransactionNestLevel();
     338              :     PgStat_SubXactStatus *xact_state;
     339              :     PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
     340       123497 :         MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
     341              : 
     342       123497 :     xact_state = pgstat_get_xact_stack_level(nest_level);
     343              : 
     344       123497 :     drop->is_create = is_create;
     345       123497 :     drop->item.kind = kind;
     346       123497 :     drop->item.dboid = dboid;
     347       123497 :     drop->item.objid_lo = (uint32) objid;
     348       123497 :     drop->item.objid_hi = (uint32) (objid >> 32);
     349              : 
     350       123497 :     dclist_push_tail(&xact_state->pending_drops, &drop->node);
     351       123497 : }
     352              : 
     353              : /*
     354              :  * Create a stats entry for a newly created database object in a transactional
     355              :  * manner.
     356              :  *
     357              :  * I.e. if the current (sub-)transaction aborts, the stats entry will also be
     358              :  * dropped.
     359              :  */
     360              : void
     361        80402 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
     362              : {
     363        80402 :     if (pgstat_get_entry_ref(kind, dboid, objid, false, NULL))
     364              :     {
     365            0 :         ereport(WARNING,
     366              :                 errmsg("resetting existing statistics for kind %s, db=%u, oid=%" PRIu64,
     367              :                        (pgstat_get_kind_info(kind))->name, dboid,
     368              :                        objid));
     369              : 
     370            0 :         pgstat_reset(kind, dboid, objid);
     371              :     }
     372              : 
     373        80402 :     create_drop_transactional_internal(kind, dboid, objid, /* create */ true);
     374        80402 : }
     375              : 
     376              : /*
     377              :  * Drop a stats entry for a just dropped database object in a transactional
     378              :  * manner.
     379              :  *
     380              :  * I.e. if the current (sub-)transaction aborts, the stats entry will stay
     381              :  * alive.
     382              :  */
     383              : void
     384        43095 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
     385              : {
     386        43095 :     create_drop_transactional_internal(kind, dboid, objid, /* create */ false);
     387        43095 : }
        

Generated by: LCOV version 2.0-1