LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_function.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.5 % 62 58
Test Date: 2026-03-01 08:15:02 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* -------------------------------------------------------------------------
       2              :  *
       3              :  * pgstat_function.c
       4              :  *    Implementation of function statistics.
       5              :  *
       6              :  * This file contains the implementation of function 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_function.c
      15              :  * -------------------------------------------------------------------------
      16              :  */
      17              : 
      18              : #include "postgres.h"
      19              : 
      20              : #include "fmgr.h"
      21              : #include "utils/inval.h"
      22              : #include "utils/pgstat_internal.h"
      23              : #include "utils/syscache.h"
      24              : 
      25              : 
      26              : /* ----------
      27              :  * GUC parameters
      28              :  * ----------
      29              :  */
      30              : int         pgstat_track_functions = TRACK_FUNC_OFF;
      31              : 
      32              : 
      33              : /*
      34              :  * Total time charged to functions so far in the current backend.
      35              :  * We use this to help separate "self" and "other" time charges.
      36              :  * (We assume this initializes to zero.)
      37              :  */
      38              : static instr_time total_func_time;
      39              : 
      40              : 
      41              : /*
      42              :  * Ensure that stats are dropped if transaction aborts.
      43              :  */
      44              : void
      45         8936 : pgstat_create_function(Oid proid)
      46              : {
      47         8936 :     pgstat_create_transactional(PGSTAT_KIND_FUNCTION,
      48              :                                 MyDatabaseId,
      49              :                                 proid);
      50         8936 : }
      51              : 
      52              : /*
      53              :  * Ensure that stats are dropped if transaction commits.
      54              :  *
      55              :  * NB: This is only reliable because pgstat_init_function_usage() does some
      56              :  * extra work. If other places start emitting function stats they likely need
      57              :  * similar logic.
      58              :  */
      59              : void
      60         4213 : pgstat_drop_function(Oid proid)
      61              : {
      62         4213 :     pgstat_drop_transactional(PGSTAT_KIND_FUNCTION,
      63              :                               MyDatabaseId,
      64              :                               proid);
      65         4213 : }
      66              : 
      67              : /*
      68              :  * Initialize function call usage data.
      69              :  * Called by the executor before invoking a function.
      70              :  */
      71              : void
      72     11382138 : pgstat_init_function_usage(FunctionCallInfo fcinfo,
      73              :                            PgStat_FunctionCallUsage *fcu)
      74              : {
      75              :     PgStat_EntryRef *entry_ref;
      76              :     PgStat_FunctionCounts *pending;
      77              :     bool        created_entry;
      78              : 
      79     11382138 :     if (pgstat_track_functions <= fcinfo->flinfo->fn_stats)
      80              :     {
      81              :         /* stats not wanted */
      82     11382031 :         fcu->fs = NULL;
      83     11382031 :         return;
      84              :     }
      85              : 
      86          107 :     entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_FUNCTION,
      87              :                                           MyDatabaseId,
      88          107 :                                           fcinfo->flinfo->fn_oid,
      89              :                                           &created_entry);
      90              : 
      91              :     /*
      92              :      * If no shared entry already exists, check if the function has been
      93              :      * deleted concurrently. This can go unnoticed until here because
      94              :      * executing a statement that just calls a function, does not trigger
      95              :      * cache invalidation processing. The reason we care about this case is
      96              :      * that otherwise we could create a new stats entry for an already dropped
      97              :      * function (for relations etc this is not possible because emitting stats
      98              :      * requires a lock for the relation to already have been acquired).
      99              :      *
     100              :      * It's somewhat ugly to have a behavioral difference based on
     101              :      * track_functions being enabled/disabled. But it seems acceptable, given
     102              :      * that there's already behavioral differences depending on whether the
     103              :      * function is the caches etc.
     104              :      *
     105              :      * For correctness it'd be sufficient to set ->dropped to true. However,
     106              :      * the accepted invalidation will commonly cause "low level" failures in
     107              :      * PL code, with an OID in the error message. Making this harder to
     108              :      * test...
     109              :      */
     110          107 :     if (created_entry)
     111              :     {
     112           48 :         AcceptInvalidationMessages();
     113           48 :         if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid)))
     114              :         {
     115            0 :             pgstat_drop_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId,
     116            0 :                               fcinfo->flinfo->fn_oid);
     117            0 :             ereport(ERROR, errcode(ERRCODE_UNDEFINED_FUNCTION),
     118              :                     errmsg("function call to dropped function"));
     119              :         }
     120              :     }
     121              : 
     122          107 :     pending = entry_ref->pending;
     123              : 
     124          107 :     fcu->fs = pending;
     125              : 
     126              :     /* save stats for this function, later used to compensate for recursion */
     127          107 :     fcu->save_f_total_time = pending->total_time;
     128              : 
     129              :     /* save current backend-wide total time */
     130          107 :     fcu->save_total = total_func_time;
     131              : 
     132              :     /* get clock time as of function start */
     133          107 :     INSTR_TIME_SET_CURRENT(fcu->start);
     134              : }
     135              : 
     136              : /*
     137              :  * Calculate function call usage and update stat counters.
     138              :  * Called by the executor after invoking a function.
     139              :  *
     140              :  * In the case of a set-returning function that runs in value-per-call mode,
     141              :  * we will see multiple pgstat_init_function_usage/pgstat_end_function_usage
     142              :  * calls for what the user considers a single call of the function.  The
     143              :  * finalize flag should be TRUE on the last call.
     144              :  */
     145              : void
     146     11378059 : pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
     147              : {
     148     11378059 :     PgStat_FunctionCounts *fs = fcu->fs;
     149              :     instr_time  total;
     150              :     instr_time  others;
     151              :     instr_time  self;
     152              : 
     153              :     /* stats not wanted? */
     154     11378059 :     if (fs == NULL)
     155     11377952 :         return;
     156              : 
     157              :     /* total elapsed time in this function call */
     158          107 :     INSTR_TIME_SET_CURRENT(total);
     159          107 :     INSTR_TIME_SUBTRACT(total, fcu->start);
     160              : 
     161              :     /* self usage: elapsed minus anything already charged to other calls */
     162          107 :     others = total_func_time;
     163          107 :     INSTR_TIME_SUBTRACT(others, fcu->save_total);
     164          107 :     self = total;
     165          107 :     INSTR_TIME_SUBTRACT(self, others);
     166              : 
     167              :     /* update backend-wide total time */
     168          107 :     INSTR_TIME_ADD(total_func_time, self);
     169              : 
     170              :     /*
     171              :      * Compute the new total_time as the total elapsed time added to the
     172              :      * pre-call value of total_time.  This is necessary to avoid
     173              :      * double-counting any time taken by recursive calls of myself.  (We do
     174              :      * not need any similar kluge for self time, since that already excludes
     175              :      * any recursive calls.)
     176              :      */
     177          107 :     INSTR_TIME_ADD(total, fcu->save_f_total_time);
     178              : 
     179              :     /* update counters in function stats table */
     180          107 :     if (finalize)
     181          107 :         fs->numcalls++;
     182          107 :     fs->total_time = total;
     183          107 :     INSTR_TIME_ADD(fs->self_time, self);
     184              : }
     185              : 
     186              : /*
     187              :  * Flush out pending stats for the entry
     188              :  *
     189              :  * If nowait is true and the lock could not be immediately acquired, returns
     190              :  * false without flushing the entry.  Otherwise returns true.
     191              :  */
     192              : bool
     193           69 : pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
     194              : {
     195              :     PgStat_FunctionCounts *localent;
     196              :     PgStatShared_Function *shfuncent;
     197              : 
     198           69 :     localent = (PgStat_FunctionCounts *) entry_ref->pending;
     199           69 :     shfuncent = (PgStatShared_Function *) entry_ref->shared_stats;
     200              : 
     201              :     /* localent always has non-zero content */
     202              : 
     203           69 :     if (!pgstat_lock_entry(entry_ref, nowait))
     204            0 :         return false;
     205              : 
     206           69 :     shfuncent->stats.numcalls += localent->numcalls;
     207           69 :     shfuncent->stats.total_time +=
     208           69 :         INSTR_TIME_GET_MICROSEC(localent->total_time);
     209           69 :     shfuncent->stats.self_time +=
     210           69 :         INSTR_TIME_GET_MICROSEC(localent->self_time);
     211              : 
     212           69 :     pgstat_unlock_entry(entry_ref);
     213              : 
     214           69 :     return true;
     215              : }
     216              : 
     217              : void
     218            9 : pgstat_function_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
     219              : {
     220            9 :     ((PgStatShared_Function *) header)->stats.stat_reset_timestamp = ts;
     221            9 : }
     222              : 
     223              : /*
     224              :  * find any existing PgStat_FunctionCounts entry for specified function
     225              :  *
     226              :  * If no entry, return NULL, don't create a new one
     227              :  */
     228              : PgStat_FunctionCounts *
     229           12 : find_funcstat_entry(Oid func_id)
     230              : {
     231              :     PgStat_EntryRef *entry_ref;
     232              : 
     233           12 :     entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     234              : 
     235           12 :     if (entry_ref)
     236            9 :         return entry_ref->pending;
     237            3 :     return NULL;
     238              : }
     239              : 
     240              : /*
     241              :  * Support function for the SQL-callable pgstat* functions. Returns
     242              :  * the collected statistics for one function or NULL.
     243              :  */
     244              : PgStat_StatFuncEntry *
     245          295 : pgstat_fetch_stat_funcentry(Oid func_id)
     246              : {
     247          295 :     return (PgStat_StatFuncEntry *)
     248          295 :         pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     249              : }
        

Generated by: LCOV version 2.0-1