LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_database.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.2 % 171 161
Test Date: 2026-03-10 01:14:48 Functions: 100.0 % 18 18
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* -------------------------------------------------------------------------
       2              :  *
       3              :  * pgstat_database.c
       4              :  *    Implementation of database statistics.
       5              :  *
       6              :  * This file contains the implementation of database statistics. It is kept
       7              :  * separate from pgstat.c to enforce the line between the statistics access /
       8              :  * storage implementation and the details about individual types of
       9              :  * statistics.
      10              :  *
      11              :  * Copyright (c) 2001-2026, PostgreSQL Global Development Group
      12              :  *
      13              :  * IDENTIFICATION
      14              :  *    src/backend/utils/activity/pgstat_database.c
      15              :  * -------------------------------------------------------------------------
      16              :  */
      17              : 
      18              : #include "postgres.h"
      19              : 
      20              : #include "storage/standby.h"
      21              : #include "utils/pgstat_internal.h"
      22              : #include "utils/timestamp.h"
      23              : 
      24              : 
      25              : static bool pgstat_should_report_connstat(void);
      26              : 
      27              : 
      28              : PgStat_Counter pgStatBlockReadTime = 0;
      29              : PgStat_Counter pgStatBlockWriteTime = 0;
      30              : PgStat_Counter pgStatActiveTime = 0;
      31              : PgStat_Counter pgStatTransactionIdleTime = 0;
      32              : SessionEndType pgStatSessionEndCause = DISCONNECT_NORMAL;
      33              : 
      34              : 
      35              : static int  pgStatXactCommit = 0;
      36              : static int  pgStatXactRollback = 0;
      37              : static PgStat_Counter pgLastSessionReportTime = 0;
      38              : 
      39              : 
      40              : /*
      41              :  * Remove entry for the database being dropped.
      42              :  */
      43              : void
      44           51 : pgstat_drop_database(Oid databaseid)
      45              : {
      46           51 :     pgstat_drop_transactional(PGSTAT_KIND_DATABASE, databaseid, InvalidOid);
      47           51 : }
      48              : 
      49              : /*
      50              :  * Called from autovacuum.c to report startup of an autovacuum process.
      51              :  * We are called before InitPostgres is done, so can't rely on MyDatabaseId;
      52              :  * the db OID must be passed in, instead.
      53              :  */
      54              : void
      55         1774 : pgstat_report_autovac(Oid dboid)
      56              : {
      57              :     PgStat_EntryRef *entry_ref;
      58              :     PgStatShared_Database *dbentry;
      59              : 
      60              :     /* can't get here in single user mode */
      61              :     Assert(IsUnderPostmaster);
      62              : 
      63              :     /*
      64              :      * End-of-vacuum is reported instantly. Report the start the same way for
      65              :      * consistency. Vacuum doesn't run frequently and is a long-lasting
      66              :      * operation so it doesn't matter if we get blocked here a little.
      67              :      */
      68         1774 :     entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE,
      69              :                                             dboid, InvalidOid, false);
      70              : 
      71         1774 :     dbentry = (PgStatShared_Database *) entry_ref->shared_stats;
      72         1774 :     dbentry->stats.last_autovac_time = GetCurrentTimestamp();
      73              : 
      74         1774 :     pgstat_unlock_entry(entry_ref);
      75         1774 : }
      76              : 
      77              : /*
      78              :  * Report a Hot Standby recovery conflict.
      79              :  */
      80              : void
      81           12 : pgstat_report_recovery_conflict(int reason)
      82              : {
      83              :     PgStat_StatDBEntry *dbentry;
      84              : 
      85              :     Assert(IsUnderPostmaster);
      86           12 :     if (!pgstat_track_counts)
      87            0 :         return;
      88              : 
      89           12 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
      90              : 
      91           12 :     switch ((RecoveryConflictReason) reason)
      92              :     {
      93            2 :         case RECOVERY_CONFLICT_DATABASE:
      94              : 
      95              :             /*
      96              :              * Since we drop the information about the database as soon as it
      97              :              * replicates, there is no point in counting these conflicts.
      98              :              */
      99            2 :             break;
     100            1 :         case RECOVERY_CONFLICT_TABLESPACE:
     101            1 :             dbentry->conflict_tablespace++;
     102            1 :             break;
     103            1 :         case RECOVERY_CONFLICT_LOCK:
     104            1 :             dbentry->conflict_lock++;
     105            1 :             break;
     106            1 :         case RECOVERY_CONFLICT_SNAPSHOT:
     107            1 :             dbentry->conflict_snapshot++;
     108            1 :             break;
     109            1 :         case RECOVERY_CONFLICT_BUFFERPIN:
     110            1 :             dbentry->conflict_bufferpin++;
     111            1 :             break;
     112            5 :         case RECOVERY_CONFLICT_LOGICALSLOT:
     113            5 :             dbentry->conflict_logicalslot++;
     114            5 :             break;
     115            0 :         case RECOVERY_CONFLICT_STARTUP_DEADLOCK:
     116            0 :             dbentry->conflict_startup_deadlock++;
     117            0 :             break;
     118            1 :         case RECOVERY_CONFLICT_BUFFERPIN_DEADLOCK:
     119              : 
     120              :             /*
     121              :              * The difference between RECOVERY_CONFLICT_STARTUP_DEADLOCK and
     122              :              * RECOVERY_CONFLICT_BUFFERPIN_DEADLOCK is merely whether a buffer
     123              :              * pin was part of the deadlock. We use the same counter for both
     124              :              * reasons.
     125              :              */
     126            1 :             dbentry->conflict_startup_deadlock++;
     127            1 :             break;
     128              :     }
     129              : }
     130              : 
     131              : /*
     132              :  * Report a detected deadlock.
     133              :  */
     134              : void
     135            6 : pgstat_report_deadlock(void)
     136              : {
     137              :     PgStat_StatDBEntry *dbent;
     138              : 
     139            6 :     if (!pgstat_track_counts)
     140            0 :         return;
     141              : 
     142            6 :     dbent = pgstat_prep_database_pending(MyDatabaseId);
     143            6 :     dbent->deadlocks++;
     144              : }
     145              : 
     146              : /*
     147              :  * Allow this backend to later report checksum failures for dboid, even if in
     148              :  * a critical section at the time of the report.
     149              :  *
     150              :  * Without this function having been called first, the backend might need to
     151              :  * allocate an EntryRef or might need to map in DSM segments. Neither should
     152              :  * happen in a critical section.
     153              :  */
     154              : void
     155      1320114 : pgstat_prepare_report_checksum_failure(Oid dboid)
     156              : {
     157              :     Assert(!CritSectionCount);
     158              : 
     159              :     /*
     160              :      * Just need to ensure this backend has an entry ref for the database.
     161              :      * That will allows us to report checksum failures without e.g. needing to
     162              :      * map in DSM segments.
     163              :      */
     164      1320114 :     pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
     165              :                          true, NULL);
     166      1320114 : }
     167              : 
     168              : /*
     169              :  * Report one or more checksum failures.
     170              :  *
     171              :  * To be allowed to report checksum failures in critical sections, we require
     172              :  * pgstat_prepare_report_checksum_failure() to have been called before this
     173              :  * function is called.
     174              :  */
     175              : void
     176           28 : pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount)
     177              : {
     178              :     PgStat_EntryRef *entry_ref;
     179              :     PgStatShared_Database *sharedent;
     180              : 
     181           28 :     if (!pgstat_track_counts)
     182            0 :         return;
     183              : 
     184              :     /*
     185              :      * Update the shared stats directly - checksum failures should never be
     186              :      * common enough for that to be a problem. Note that we pass create=false
     187              :      * here, as we want to be sure to not require memory allocations, so this
     188              :      * can be called in critical sections.
     189              :      */
     190           28 :     entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
     191              :                                      false, NULL);
     192              : 
     193              :     /*
     194              :      * Should always have been created by
     195              :      * pgstat_prepare_report_checksum_failure().
     196              :      *
     197              :      * When not using assertions, we don't want to crash should something have
     198              :      * gone wrong, so just return.
     199              :      */
     200              :     Assert(entry_ref);
     201           28 :     if (!entry_ref)
     202              :     {
     203            0 :         elog(WARNING, "could not report %d checksum failures for database %u",
     204              :              failurecount, dboid);
     205            0 :         return;
     206              :     }
     207              : 
     208           28 :     (void) pgstat_lock_entry(entry_ref, false);
     209              : 
     210           28 :     sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
     211           28 :     sharedent->stats.checksum_failures += failurecount;
     212           28 :     sharedent->stats.last_checksum_failure = GetCurrentTimestamp();
     213              : 
     214           28 :     pgstat_unlock_entry(entry_ref);
     215              : }
     216              : 
     217              : /*
     218              :  * Report creation of temporary file.
     219              :  */
     220              : void
     221         2979 : pgstat_report_tempfile(size_t filesize)
     222              : {
     223              :     PgStat_StatDBEntry *dbent;
     224              : 
     225         2979 :     if (!pgstat_track_counts)
     226            0 :         return;
     227              : 
     228         2979 :     dbent = pgstat_prep_database_pending(MyDatabaseId);
     229         2979 :     dbent->temp_bytes += filesize;
     230         2979 :     dbent->temp_files++;
     231              : }
     232              : 
     233              : /*
     234              :  * Notify stats system of a new connection.
     235              :  */
     236              : void
     237        13960 : pgstat_report_connect(Oid dboid)
     238              : {
     239              :     PgStat_StatDBEntry *dbentry;
     240              : 
     241        13960 :     if (!pgstat_should_report_connstat())
     242         1299 :         return;
     243              : 
     244        12661 :     pgLastSessionReportTime = MyStartTimestamp;
     245              : 
     246        12661 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     247        12661 :     dbentry->sessions++;
     248              : }
     249              : 
     250              : /*
     251              :  * Notify the stats system of a disconnect.
     252              :  */
     253              : void
     254        17361 : pgstat_report_disconnect(Oid dboid)
     255              : {
     256              :     PgStat_StatDBEntry *dbentry;
     257              : 
     258        17361 :     if (!pgstat_should_report_connstat())
     259         4700 :         return;
     260              : 
     261        12661 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     262              : 
     263        12661 :     switch (pgStatSessionEndCause)
     264              :     {
     265        12598 :         case DISCONNECT_NOT_YET:
     266              :         case DISCONNECT_NORMAL:
     267              :             /* we don't collect these */
     268        12598 :             break;
     269           37 :         case DISCONNECT_CLIENT_EOF:
     270           37 :             dbentry->sessions_abandoned++;
     271           37 :             break;
     272           12 :         case DISCONNECT_FATAL:
     273           12 :             dbentry->sessions_fatal++;
     274           12 :             break;
     275           14 :         case DISCONNECT_KILLED:
     276           14 :             dbentry->sessions_killed++;
     277           14 :             break;
     278              :     }
     279              : }
     280              : 
     281              : /*
     282              :  * Support function for the SQL-callable pgstat* functions. Returns
     283              :  * the collected statistics for one database or NULL. NULL doesn't mean
     284              :  * that the database doesn't exist, just that there are no statistics, so the
     285              :  * caller is better off to report ZERO instead.
     286              :  */
     287              : PgStat_StatDBEntry *
     288         5122 : pgstat_fetch_stat_dbentry(Oid dboid)
     289              : {
     290         5122 :     return (PgStat_StatDBEntry *)
     291         5122 :         pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid);
     292              : }
     293              : 
     294              : void
     295       562159 : AtEOXact_PgStat_Database(bool isCommit, bool parallel)
     296              : {
     297              :     /* Don't count parallel worker transaction stats */
     298       562159 :     if (!parallel)
     299              :     {
     300              :         /*
     301              :          * Count transaction commit or abort.  (We use counters, not just
     302              :          * bools, in case the reporting message isn't sent right away.)
     303              :          */
     304       560677 :         if (isCommit)
     305       534185 :             pgStatXactCommit++;
     306              :         else
     307        26492 :             pgStatXactRollback++;
     308              :     }
     309       562159 : }
     310              : 
     311              : /*
     312              :  * Notify the stats system about parallel worker information.
     313              :  */
     314              : void
     315          365 : pgstat_update_parallel_workers_stats(PgStat_Counter workers_to_launch,
     316              :                                      PgStat_Counter workers_launched)
     317              : {
     318              :     PgStat_StatDBEntry *dbentry;
     319              : 
     320          365 :     if (!OidIsValid(MyDatabaseId))
     321            0 :         return;
     322              : 
     323          365 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     324          365 :     dbentry->parallel_workers_to_launch += workers_to_launch;
     325          365 :     dbentry->parallel_workers_launched += workers_launched;
     326              : }
     327              : 
     328              : /*
     329              :  * Subroutine for pgstat_report_stat(): Handle xact commit/rollback and I/O
     330              :  * timings.
     331              :  */
     332              : void
     333        38588 : pgstat_update_dbstats(TimestampTz ts)
     334              : {
     335              :     PgStat_StatDBEntry *dbentry;
     336              : 
     337              :     /*
     338              :      * If not connected to a database yet, don't attribute time to "shared
     339              :      * state" (InvalidOid is used to track stats for shared relations, etc.).
     340              :      */
     341        38588 :     if (!OidIsValid(MyDatabaseId))
     342         4239 :         return;
     343              : 
     344        34349 :     dbentry = pgstat_prep_database_pending(MyDatabaseId);
     345              : 
     346              :     /*
     347              :      * Accumulate xact commit/rollback and I/O timings to stats entry of the
     348              :      * current database.
     349              :      */
     350        34349 :     dbentry->xact_commit += pgStatXactCommit;
     351        34349 :     dbentry->xact_rollback += pgStatXactRollback;
     352        34349 :     dbentry->blk_read_time += pgStatBlockReadTime;
     353        34349 :     dbentry->blk_write_time += pgStatBlockWriteTime;
     354              : 
     355        34349 :     if (pgstat_should_report_connstat())
     356              :     {
     357              :         long        secs;
     358              :         int         usecs;
     359              : 
     360              :         /*
     361              :          * pgLastSessionReportTime is initialized to MyStartTimestamp by
     362              :          * pgstat_report_connect().
     363              :          */
     364        27313 :         TimestampDifference(pgLastSessionReportTime, ts, &secs, &usecs);
     365        27313 :         pgLastSessionReportTime = ts;
     366        27313 :         dbentry->session_time += (PgStat_Counter) secs * 1000000 + usecs;
     367        27313 :         dbentry->active_time += pgStatActiveTime;
     368        27313 :         dbentry->idle_in_transaction_time += pgStatTransactionIdleTime;
     369              :     }
     370              : 
     371        34349 :     pgStatXactCommit = 0;
     372        34349 :     pgStatXactRollback = 0;
     373        34349 :     pgStatBlockReadTime = 0;
     374        34349 :     pgStatBlockWriteTime = 0;
     375        34349 :     pgStatActiveTime = 0;
     376        34349 :     pgStatTransactionIdleTime = 0;
     377              : }
     378              : 
     379              : /*
     380              :  * We report session statistics only for normal backend processes.  Parallel
     381              :  * workers run in parallel, so they don't contribute to session times, even
     382              :  * though they use CPU time. Walsender processes could be considered here,
     383              :  * but they have different session characteristics from normal backends (for
     384              :  * example, they are always "active"), so they would skew session statistics.
     385              :  */
     386              : static bool
     387        65670 : pgstat_should_report_connstat(void)
     388              : {
     389        65670 :     return MyBackendType == B_BACKEND;
     390              : }
     391              : 
     392              : /*
     393              :  * Find or create a local PgStat_StatDBEntry entry for dboid.
     394              :  */
     395              : PgStat_StatDBEntry *
     396      1097503 : pgstat_prep_database_pending(Oid dboid)
     397              : {
     398              :     PgStat_EntryRef *entry_ref;
     399              : 
     400              :     /*
     401              :      * This should not report stats on database objects before having
     402              :      * connected to a database.
     403              :      */
     404              :     Assert(!OidIsValid(dboid) || OidIsValid(MyDatabaseId));
     405              : 
     406      1097503 :     entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid,
     407              :                                           NULL);
     408              : 
     409      1097503 :     return entry_ref->pending;
     410              : }
     411              : 
     412              : /*
     413              :  * Reset the database's reset timestamp, without resetting the contents of the
     414              :  * database stats.
     415              :  */
     416              : void
     417           11 : pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts)
     418              : {
     419              :     PgStat_EntryRef *dbref;
     420              :     PgStatShared_Database *dbentry;
     421              : 
     422           11 :     dbref = pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, MyDatabaseId, InvalidOid,
     423              :                                         false);
     424              : 
     425           11 :     dbentry = (PgStatShared_Database *) dbref->shared_stats;
     426           11 :     dbentry->stats.stat_reset_timestamp = ts;
     427              : 
     428           11 :     pgstat_unlock_entry(dbref);
     429           11 : }
     430              : 
     431              : /*
     432              :  * Flush out pending stats for the entry
     433              :  *
     434              :  * If nowait is true and the lock could not be immediately acquired, returns
     435              :  * false without flushing the entry.  Otherwise returns true.
     436              :  */
     437              : bool
     438        62266 : pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
     439              : {
     440              :     PgStatShared_Database *sharedent;
     441              :     PgStat_StatDBEntry *pendingent;
     442              : 
     443        62266 :     pendingent = (PgStat_StatDBEntry *) entry_ref->pending;
     444        62266 :     sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
     445              : 
     446        62266 :     if (!pgstat_lock_entry(entry_ref, nowait))
     447            1 :         return false;
     448              : 
     449              : #define PGSTAT_ACCUM_DBCOUNT(item)      \
     450              :     (sharedent)->stats.item += (pendingent)->item
     451              : 
     452        62265 :     PGSTAT_ACCUM_DBCOUNT(xact_commit);
     453        62265 :     PGSTAT_ACCUM_DBCOUNT(xact_rollback);
     454        62265 :     PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
     455        62265 :     PGSTAT_ACCUM_DBCOUNT(blocks_hit);
     456              : 
     457        62265 :     PGSTAT_ACCUM_DBCOUNT(tuples_returned);
     458        62265 :     PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
     459        62265 :     PGSTAT_ACCUM_DBCOUNT(tuples_inserted);
     460        62265 :     PGSTAT_ACCUM_DBCOUNT(tuples_updated);
     461        62265 :     PGSTAT_ACCUM_DBCOUNT(tuples_deleted);
     462              : 
     463              :     /* last_autovac_time is reported immediately */
     464              :     Assert(pendingent->last_autovac_time == 0);
     465              : 
     466        62265 :     PGSTAT_ACCUM_DBCOUNT(conflict_tablespace);
     467        62265 :     PGSTAT_ACCUM_DBCOUNT(conflict_lock);
     468        62265 :     PGSTAT_ACCUM_DBCOUNT(conflict_snapshot);
     469        62265 :     PGSTAT_ACCUM_DBCOUNT(conflict_logicalslot);
     470        62265 :     PGSTAT_ACCUM_DBCOUNT(conflict_bufferpin);
     471        62265 :     PGSTAT_ACCUM_DBCOUNT(conflict_startup_deadlock);
     472              : 
     473        62265 :     PGSTAT_ACCUM_DBCOUNT(temp_bytes);
     474        62265 :     PGSTAT_ACCUM_DBCOUNT(temp_files);
     475        62265 :     PGSTAT_ACCUM_DBCOUNT(deadlocks);
     476              : 
     477              :     /* checksum failures are reported immediately */
     478              :     Assert(pendingent->checksum_failures == 0);
     479              :     Assert(pendingent->last_checksum_failure == 0);
     480              : 
     481        62265 :     PGSTAT_ACCUM_DBCOUNT(blk_read_time);
     482        62265 :     PGSTAT_ACCUM_DBCOUNT(blk_write_time);
     483              : 
     484        62265 :     PGSTAT_ACCUM_DBCOUNT(sessions);
     485        62265 :     PGSTAT_ACCUM_DBCOUNT(session_time);
     486        62265 :     PGSTAT_ACCUM_DBCOUNT(active_time);
     487        62265 :     PGSTAT_ACCUM_DBCOUNT(idle_in_transaction_time);
     488        62265 :     PGSTAT_ACCUM_DBCOUNT(sessions_abandoned);
     489        62265 :     PGSTAT_ACCUM_DBCOUNT(sessions_fatal);
     490        62265 :     PGSTAT_ACCUM_DBCOUNT(sessions_killed);
     491        62265 :     PGSTAT_ACCUM_DBCOUNT(parallel_workers_to_launch);
     492        62265 :     PGSTAT_ACCUM_DBCOUNT(parallel_workers_launched);
     493              : #undef PGSTAT_ACCUM_DBCOUNT
     494              : 
     495        62265 :     pgstat_unlock_entry(entry_ref);
     496              : 
     497        62265 :     memset(pendingent, 0, sizeof(*pendingent));
     498              : 
     499        62265 :     return true;
     500              : }
     501              : 
     502              : void
     503           13 : pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
     504              : {
     505           13 :     ((PgStatShared_Database *) header)->stats.stat_reset_timestamp = ts;
     506           13 : }
        

Generated by: LCOV version 2.0-1