LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_function.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.9 % 82 77
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 59.1 % 22 13

             Branch data     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                 :        1004 : pgstat_create_function(Oid proid)
      46                 :             : {
      47                 :        1004 :         pgstat_create_transactional(PGSTAT_KIND_FUNCTION,
      48                 :        1004 :                                                                 MyDatabaseId,
      49                 :        1004 :                                                                 proid);
      50                 :        1004 : }
      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                 :         565 : pgstat_drop_function(Oid proid)
      61                 :             : {
      62                 :         565 :         pgstat_drop_transactional(PGSTAT_KIND_FUNCTION,
      63                 :         565 :                                                           MyDatabaseId,
      64                 :         565 :                                                           proid);
      65                 :         565 : }
      66                 :             : 
      67                 :             : /*
      68                 :             :  * Initialize function call usage data.
      69                 :             :  * Called by the executor before invoking a function.
      70                 :             :  */
      71                 :             : void
      72                 :     2239839 : pgstat_init_function_usage(FunctionCallInfo fcinfo,
      73                 :             :                                                    PgStat_FunctionCallUsage *fcu)
      74                 :             : {
      75                 :     2239839 :         PgStat_EntryRef *entry_ref;
      76                 :     2239839 :         PgStat_FunctionCounts *pending;
      77                 :     2239839 :         bool            created_entry;
      78                 :             : 
      79         [ +  + ]:     2239839 :         if (pgstat_track_functions <= fcinfo->flinfo->fn_stats)
      80                 :             :         {
      81                 :             :                 /* stats not wanted */
      82                 :     2239830 :                 fcu->fs = NULL;
      83                 :     2239830 :                 return;
      84                 :             :         }
      85                 :             : 
      86                 :           9 :         entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_FUNCTION,
      87                 :           9 :                                                                                   MyDatabaseId,
      88                 :           9 :                                                                                   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         [ +  + ]:           9 :         if (created_entry)
     111                 :             :         {
     112                 :           3 :                 AcceptInvalidationMessages();
     113         [ +  - ]:           3 :                 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                 :           0 :                 }
     120                 :           3 :         }
     121                 :             : 
     122                 :           9 :         pending = entry_ref->pending;
     123                 :             : 
     124                 :           9 :         fcu->fs = pending;
     125                 :             : 
     126                 :             :         /* save stats for this function, later used to compensate for recursion */
     127                 :           9 :         fcu->save_f_total_time = pending->total_time;
     128                 :             : 
     129                 :             :         /* save current backend-wide total time */
     130                 :           9 :         fcu->save_total = total_func_time;
     131                 :             : 
     132                 :             :         /* get clock time as of function start */
     133                 :           9 :         INSTR_TIME_SET_CURRENT(fcu->start);
     134         [ -  + ]:     2239839 : }
     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                 :     2238644 : pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
     147                 :             : {
     148                 :     2238644 :         PgStat_FunctionCounts *fs = fcu->fs;
     149                 :     2238644 :         instr_time      total;
     150                 :     2238644 :         instr_time      others;
     151                 :     2238644 :         instr_time      self;
     152                 :             : 
     153                 :             :         /* stats not wanted? */
     154         [ +  + ]:     2238644 :         if (fs == NULL)
     155                 :     2238635 :                 return;
     156                 :             : 
     157                 :             :         /* total elapsed time in this function call */
     158                 :           9 :         INSTR_TIME_SET_CURRENT(total);
     159                 :           9 :         INSTR_TIME_SUBTRACT(total, fcu->start);
     160                 :             : 
     161                 :             :         /* self usage: elapsed minus anything already charged to other calls */
     162                 :           9 :         others = total_func_time;
     163                 :           9 :         INSTR_TIME_SUBTRACT(others, fcu->save_total);
     164                 :           9 :         self = total;
     165                 :           9 :         INSTR_TIME_SUBTRACT(self, others);
     166                 :             : 
     167                 :             :         /* update backend-wide total time */
     168                 :           9 :         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                 :           9 :         INSTR_TIME_ADD(total, fcu->save_f_total_time);
     178                 :             : 
     179                 :             :         /* update counters in function stats table */
     180         [ -  + ]:           9 :         if (finalize)
     181                 :           9 :                 fs->numcalls++;
     182                 :           9 :         fs->total_time = total;
     183                 :           9 :         INSTR_TIME_ADD(fs->self_time, self);
     184         [ -  + ]:     2238644 : }
     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                 :           3 : pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
     194                 :             : {
     195                 :           3 :         PgStat_FunctionCounts *localent;
     196                 :           3 :         PgStatShared_Function *shfuncent;
     197                 :             : 
     198                 :           3 :         localent = (PgStat_FunctionCounts *) entry_ref->pending;
     199                 :           3 :         shfuncent = (PgStatShared_Function *) entry_ref->shared_stats;
     200                 :             : 
     201                 :             :         /* localent always has non-zero content */
     202                 :             : 
     203         [ -  + ]:           3 :         if (!pgstat_lock_entry(entry_ref, nowait))
     204                 :           0 :                 return false;
     205                 :             : 
     206                 :           3 :         shfuncent->stats.numcalls += localent->numcalls;
     207                 :           3 :         shfuncent->stats.total_time +=
     208                 :           3 :                 INSTR_TIME_GET_MICROSEC(localent->total_time);
     209                 :           3 :         shfuncent->stats.self_time +=
     210                 :           3 :                 INSTR_TIME_GET_MICROSEC(localent->self_time);
     211                 :             : 
     212                 :           3 :         pgstat_unlock_entry(entry_ref);
     213                 :             : 
     214                 :           3 :         return true;
     215                 :           3 : }
     216                 :             : 
     217                 :             : void
     218                 :           2 : pgstat_function_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
     219                 :             : {
     220                 :           2 :         ((PgStatShared_Function *) header)->stats.stat_reset_timestamp = ts;
     221                 :           2 : }
     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                 :           4 : find_funcstat_entry(Oid func_id)
     230                 :             : {
     231                 :           4 :         PgStat_EntryRef *entry_ref;
     232                 :             : 
     233                 :           4 :         entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     234                 :             : 
     235         [ +  + ]:           4 :         if (entry_ref)
     236                 :           3 :                 return entry_ref->pending;
     237                 :           1 :         return NULL;
     238                 :           4 : }
     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                 :          18 : pgstat_fetch_stat_funcentry(Oid func_id)
     246                 :             : {
     247                 :          18 :         return (PgStat_StatFuncEntry *)
     248                 :          18 :                 pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     249                 :             : }
        

Generated by: LCOV version 2.3.2-1