LCOV - code coverage report
Current view: top level - contrib/pg_stat_statements - pg_stat_statements.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 72.9 % 933 680
Test Date: 2026-07-25 22:15:46 Functions: 86.0 % 50 43
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 55.7 % 619 345

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pg_stat_statements.c
       4                 :             :  *      Track statement planning and execution times as well as resource
       5                 :             :  *      usage across a whole database cluster.
       6                 :             :  *
       7                 :             :  * Execution costs are totaled for each distinct source query, and kept in
       8                 :             :  * a shared hashtable.  (We track only as many distinct queries as will fit
       9                 :             :  * in the designated amount of shared memory.)
      10                 :             :  *
      11                 :             :  * Starting in Postgres 9.2, this module normalized query entries.  As of
      12                 :             :  * Postgres 14, the normalization is done by the core if compute_query_id is
      13                 :             :  * enabled, or optionally by third-party modules.
      14                 :             :  *
      15                 :             :  * To facilitate presenting entries to users, we create "representative" query
      16                 :             :  * strings in which constants are replaced with parameter symbols ($n), to
      17                 :             :  * make it clearer what a normalized entry can represent.  To save on shared
      18                 :             :  * memory, and to avoid having to truncate oversized query strings, we store
      19                 :             :  * these strings in a temporary external query-texts file.  Offsets into this
      20                 :             :  * file are kept in shared memory.
      21                 :             :  *
      22                 :             :  * Note about locking issues: to create or delete an entry in the shared
      23                 :             :  * hashtable, one must hold pgss->lock exclusively.  Modifying any field
      24                 :             :  * in an entry except the counters requires the same.  To look up an entry,
      25                 :             :  * one must hold the lock shared.  To read or update the counters within
      26                 :             :  * an entry, one must hold the lock shared or exclusive (so the entry doesn't
      27                 :             :  * disappear!) and also take the entry's mutex spinlock.
      28                 :             :  * The shared state variable pgss->extent (the next free spot in the external
      29                 :             :  * query-text file) should be accessed only while holding either the
      30                 :             :  * pgss->mutex spinlock, or exclusive lock on pgss->lock.  We use the mutex to
      31                 :             :  * allow reserving file space while holding only shared lock on pgss->lock.
      32                 :             :  * Rewriting the entire external query-text file, eg for garbage collection,
      33                 :             :  * requires holding pgss->lock exclusively; this allows individual entries
      34                 :             :  * in the file to be read or written while holding only shared lock.
      35                 :             :  *
      36                 :             :  *
      37                 :             :  * Copyright (c) 2008-2026, PostgreSQL Global Development Group
      38                 :             :  *
      39                 :             :  * IDENTIFICATION
      40                 :             :  *    contrib/pg_stat_statements/pg_stat_statements.c
      41                 :             :  *
      42                 :             :  *-------------------------------------------------------------------------
      43                 :             :  */
      44                 :             : #include "postgres.h"
      45                 :             : 
      46                 :             : #include <math.h>
      47                 :             : #include <sys/stat.h>
      48                 :             : #include <unistd.h>
      49                 :             : 
      50                 :             : #include "access/htup_details.h"
      51                 :             : #include "access/parallel.h"
      52                 :             : #include "catalog/pg_authid.h"
      53                 :             : #include "executor/instrument.h"
      54                 :             : #include "funcapi.h"
      55                 :             : #include "jit/jit.h"
      56                 :             : #include "mb/pg_wchar.h"
      57                 :             : #include "miscadmin.h"
      58                 :             : #include "nodes/queryjumble.h"
      59                 :             : #include "optimizer/planner.h"
      60                 :             : #include "parser/analyze.h"
      61                 :             : #include "pgstat.h"
      62                 :             : #include "storage/fd.h"
      63                 :             : #include "storage/ipc.h"
      64                 :             : #include "storage/lwlock.h"
      65                 :             : #include "storage/shmem.h"
      66                 :             : #include "storage/spin.h"
      67                 :             : #include "tcop/utility.h"
      68                 :             : #include "utils/acl.h"
      69                 :             : #include "utils/builtins.h"
      70                 :             : #include "utils/memutils.h"
      71                 :             : #include "utils/timestamp.h"
      72                 :             : #include "utils/tuplestore.h"
      73                 :             : 
      74                 :          10 : PG_MODULE_MAGIC_EXT(
      75                 :             :                     .name = "pg_stat_statements",
      76                 :             :                     .version = PG_VERSION
      77                 :             : );
      78                 :             : 
      79                 :             : /* Location of permanent stats file (valid when database is shut down) */
      80                 :             : #define PGSS_DUMP_FILE  PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
      81                 :             : 
      82                 :             : /*
      83                 :             :  * Location of external query text file.
      84                 :             :  */
      85                 :             : #define PGSS_TEXT_FILE  PG_STAT_TMP_DIR "/pgss_query_texts.stat"
      86                 :             : 
      87                 :             : /* Magic number identifying the stats file format */
      88                 :             : static const uint32 PGSS_FILE_HEADER = 0x20250731;
      89                 :             : 
      90                 :             : /* PostgreSQL major version number, changes in which invalidate all entries */
      91                 :             : static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
      92                 :             : 
      93                 :             : /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */
      94                 :             : #define USAGE_EXEC(duration)    (1.0)
      95                 :             : #define USAGE_INIT              (1.0)   /* including initial planning */
      96                 :             : #define ASSUMED_MEDIAN_INIT     (10.0)  /* initial assumed median usage */
      97                 :             : #define ASSUMED_LENGTH_INIT     1024    /* initial assumed mean query length */
      98                 :             : #define USAGE_DECREASE_FACTOR   (0.99)  /* decreased every entry_dealloc */
      99                 :             : #define STICKY_DECREASE_FACTOR  (0.50)  /* factor for sticky entries */
     100                 :             : #define USAGE_DEALLOC_PERCENT   5   /* free this % of entries at once */
     101                 :             : #define IS_STICKY(c)    ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
     102                 :             : 
     103                 :             : /*
     104                 :             :  * Extension version number, for supporting older extension versions' objects
     105                 :             :  */
     106                 :             : typedef enum pgssVersion
     107                 :             : {
     108                 :             :     PGSS_V1_0 = 0,
     109                 :             :     PGSS_V1_1,
     110                 :             :     PGSS_V1_2,
     111                 :             :     PGSS_V1_3,
     112                 :             :     PGSS_V1_8,
     113                 :             :     PGSS_V1_9,
     114                 :             :     PGSS_V1_10,
     115                 :             :     PGSS_V1_11,
     116                 :             :     PGSS_V1_12,
     117                 :             :     PGSS_V1_13,
     118                 :             : } pgssVersion;
     119                 :             : 
     120                 :             : typedef enum pgssStoreKind
     121                 :             : {
     122                 :             :     PGSS_INVALID = -1,
     123                 :             : 
     124                 :             :     /*
     125                 :             :      * PGSS_PLAN and PGSS_EXEC must be respectively 0 and 1 as they're used to
     126                 :             :      * reference the underlying values in the arrays in the Counters struct,
     127                 :             :      * and this order is required in pg_stat_statements_internal().
     128                 :             :      */
     129                 :             :     PGSS_PLAN = 0,
     130                 :             :     PGSS_EXEC,
     131                 :             : } pgssStoreKind;
     132                 :             : 
     133                 :             : #define PGSS_NUMKIND (PGSS_EXEC + 1)
     134                 :             : 
     135                 :             : /*
     136                 :             :  * Hashtable key that defines the identity of a hashtable entry.  We separate
     137                 :             :  * queries by user and by database even if they are otherwise identical.
     138                 :             :  *
     139                 :             :  * If you add a new key to this struct, make sure to teach pgss_store() to
     140                 :             :  * zero the padding bytes.  Otherwise, things will break, because pgss_hash is
     141                 :             :  * created using HASH_BLOBS, and thus tag_hash is used to hash this.
     142                 :             :  */
     143                 :             : typedef struct pgssHashKey
     144                 :             : {
     145                 :             :     Oid         userid;         /* user OID */
     146                 :             :     Oid         dbid;           /* database OID */
     147                 :             :     int64       queryid;        /* query identifier */
     148                 :             :     bool        toplevel;       /* query executed at top level */
     149                 :             : } pgssHashKey;
     150                 :             : 
     151                 :             : /*
     152                 :             :  * The actual stats counters kept within pgssEntry.
     153                 :             :  */
     154                 :             : typedef struct Counters
     155                 :             : {
     156                 :             :     int64       calls[PGSS_NUMKIND];    /* # of times planned/executed */
     157                 :             :     double      total_time[PGSS_NUMKIND];   /* total planning/execution time,
     158                 :             :                                              * in msec */
     159                 :             :     double      min_time[PGSS_NUMKIND]; /* minimum planning/execution time in
     160                 :             :                                          * msec since min/max reset */
     161                 :             :     double      max_time[PGSS_NUMKIND]; /* maximum planning/execution time in
     162                 :             :                                          * msec since min/max reset */
     163                 :             :     double      mean_time[PGSS_NUMKIND];    /* mean planning/execution time in
     164                 :             :                                              * msec */
     165                 :             :     double      sum_var_time[PGSS_NUMKIND]; /* sum of variances in
     166                 :             :                                              * planning/execution time in msec */
     167                 :             :     int64       rows;           /* total # of retrieved or affected rows */
     168                 :             :     int64       shared_blks_hit;    /* # of shared buffer hits */
     169                 :             :     int64       shared_blks_read;   /* # of shared disk blocks read */
     170                 :             :     int64       shared_blks_dirtied;    /* # of shared disk blocks dirtied */
     171                 :             :     int64       shared_blks_written;    /* # of shared disk blocks written */
     172                 :             :     int64       local_blks_hit; /* # of local buffer hits */
     173                 :             :     int64       local_blks_read;    /* # of local disk blocks read */
     174                 :             :     int64       local_blks_dirtied; /* # of local disk blocks dirtied */
     175                 :             :     int64       local_blks_written; /* # of local disk blocks written */
     176                 :             :     int64       temp_blks_read; /* # of temp blocks read */
     177                 :             :     int64       temp_blks_written;  /* # of temp blocks written */
     178                 :             :     double      shared_blk_read_time;   /* time spent reading shared blocks,
     179                 :             :                                          * in msec */
     180                 :             :     double      shared_blk_write_time;  /* time spent writing shared blocks,
     181                 :             :                                          * in msec */
     182                 :             :     double      local_blk_read_time;    /* time spent reading local blocks, in
     183                 :             :                                          * msec */
     184                 :             :     double      local_blk_write_time;   /* time spent writing local blocks, in
     185                 :             :                                          * msec */
     186                 :             :     double      temp_blk_read_time; /* time spent reading temp blocks, in msec */
     187                 :             :     double      temp_blk_write_time;    /* time spent writing temp blocks, in
     188                 :             :                                          * msec */
     189                 :             :     double      usage;          /* usage factor */
     190                 :             :     int64       wal_records;    /* # of WAL records generated */
     191                 :             :     int64       wal_fpi;        /* # of WAL full page images generated */
     192                 :             :     uint64      wal_bytes;      /* total amount of WAL generated in bytes */
     193                 :             :     int64       wal_buffers_full;   /* # of times the WAL buffers became full */
     194                 :             :     int64       jit_functions;  /* total number of JIT functions emitted */
     195                 :             :     double      jit_generation_time;    /* total time to generate jit code */
     196                 :             :     int64       jit_inlining_count; /* number of times inlining time has been
     197                 :             :                                      * > 0 */
     198                 :             :     double      jit_deform_time;    /* total time to deform tuples in jit code */
     199                 :             :     int64       jit_deform_count;   /* number of times deform time has been >
     200                 :             :                                      * 0 */
     201                 :             : 
     202                 :             :     double      jit_inlining_time;  /* total time to inline jit code */
     203                 :             :     int64       jit_optimization_count; /* number of times optimization time
     204                 :             :                                          * has been > 0 */
     205                 :             :     double      jit_optimization_time;  /* total time to optimize jit code */
     206                 :             :     int64       jit_emission_count; /* number of times emission time has been
     207                 :             :                                      * > 0 */
     208                 :             :     double      jit_emission_time;  /* total time to emit jit code */
     209                 :             :     int64       parallel_workers_to_launch; /* # of parallel workers planned
     210                 :             :                                              * to be launched */
     211                 :             :     int64       parallel_workers_launched;  /* # of parallel workers actually
     212                 :             :                                              * launched */
     213                 :             :     int64       generic_plan_calls; /* number of calls using a generic plan */
     214                 :             :     int64       custom_plan_calls;  /* number of calls using a custom plan */
     215                 :             : } Counters;
     216                 :             : 
     217                 :             : /*
     218                 :             :  * Global statistics for pg_stat_statements
     219                 :             :  */
     220                 :             : typedef struct pgssGlobalStats
     221                 :             : {
     222                 :             :     int64       dealloc;        /* # of times entries were deallocated */
     223                 :             :     TimestampTz stats_reset;    /* timestamp with all stats reset */
     224                 :             : } pgssGlobalStats;
     225                 :             : 
     226                 :             : /*
     227                 :             :  * Statistics per statement
     228                 :             :  *
     229                 :             :  * Note: in event of a failure in garbage collection of the query text file,
     230                 :             :  * we reset query_offset to zero and query_len to -1.  This will be seen as
     231                 :             :  * an invalid state by qtext_fetch().
     232                 :             :  */
     233                 :             : typedef struct pgssEntry
     234                 :             : {
     235                 :             :     pgssHashKey key;            /* hash key of entry - MUST BE FIRST */
     236                 :             :     Counters    counters;       /* the statistics for this query */
     237                 :             :     Size        query_offset;   /* query text offset in external file */
     238                 :             :     int         query_len;      /* # of valid bytes in query string, or -1 */
     239                 :             :     int         encoding;       /* query text encoding */
     240                 :             :     TimestampTz stats_since;    /* timestamp of entry allocation */
     241                 :             :     TimestampTz minmax_stats_since; /* timestamp of last min/max values reset */
     242                 :             :     slock_t     mutex;          /* protects the counters only */
     243                 :             : } pgssEntry;
     244                 :             : 
     245                 :             : /*
     246                 :             :  * Global shared state
     247                 :             :  */
     248                 :             : typedef struct pgssSharedState
     249                 :             : {
     250                 :             :     LWLockPadded lock;          /* protects hashtable search/modification */
     251                 :             :     double      cur_median_usage;   /* current median usage in hashtable */
     252                 :             :     Size        mean_query_len; /* current mean entry text length */
     253                 :             :     slock_t     mutex;          /* protects following fields only: */
     254                 :             :     Size        extent;         /* current extent of query file */
     255                 :             :     int         n_writers;      /* number of active writers to query file */
     256                 :             :     int         gc_count;       /* query file garbage collection cycle count */
     257                 :             :     pgssGlobalStats stats;      /* global statistics for pgss */
     258                 :             : } pgssSharedState;
     259                 :             : 
     260                 :             : /* Links to shared memory state */
     261                 :             : static pgssSharedState *pgss;
     262                 :             : static HTAB *pgss_hash;
     263                 :             : 
     264                 :             : static void pgss_shmem_request(void *arg);
     265                 :             : static void pgss_shmem_init(void *arg);
     266                 :             : 
     267                 :             : static const ShmemCallbacks pgss_shmem_callbacks = {
     268                 :             :     .request_fn = pgss_shmem_request,
     269                 :             :     .init_fn = pgss_shmem_init,
     270                 :             : };
     271                 :             : 
     272                 :             : /*---- Local variables ----*/
     273                 :             : 
     274                 :             : /* Current nesting depth of planner/ExecutorRun/ProcessUtility calls */
     275                 :             : static int  nesting_level = 0;
     276                 :             : 
     277                 :             : /* Saved hook values */
     278                 :             : static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
     279                 :             : static planner_hook_type prev_planner_hook = NULL;
     280                 :             : static ExecutorStart_hook_type prev_ExecutorStart = NULL;
     281                 :             : static ExecutorRun_hook_type prev_ExecutorRun = NULL;
     282                 :             : static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
     283                 :             : static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
     284                 :             : static ProcessUtility_hook_type prev_ProcessUtility = NULL;
     285                 :             : 
     286                 :             : /*---- GUC variables ----*/
     287                 :             : 
     288                 :             : typedef enum
     289                 :             : {
     290                 :             :     PGSS_TRACK_NONE,            /* track no statements */
     291                 :             :     PGSS_TRACK_TOP,             /* only top level statements */
     292                 :             :     PGSS_TRACK_ALL,             /* all statements, including nested ones */
     293                 :             : }           PGSSTrackLevel;
     294                 :             : 
     295                 :             : static const struct config_enum_entry track_options[] =
     296                 :             : {
     297                 :             :     {"none", PGSS_TRACK_NONE, false},
     298                 :             :     {"top", PGSS_TRACK_TOP, false},
     299                 :             :     {"all", PGSS_TRACK_ALL, false},
     300                 :             :     {NULL, 0, false}
     301                 :             : };
     302                 :             : 
     303                 :             : static int  pgss_max = 5000;    /* max # statements to track */
     304                 :             : static int  pgss_track = PGSS_TRACK_TOP;    /* tracking level */
     305                 :             : static bool pgss_track_utility = true;  /* whether to track utility commands */
     306                 :             : static bool pgss_track_planning = false;    /* whether to track planning
     307                 :             :                                              * duration */
     308                 :             : static bool pgss_save = true;   /* whether to save stats across shutdown */
     309                 :             : 
     310                 :             : #define pgss_enabled(level) \
     311                 :             :     (!IsParallelWorker() && \
     312                 :             :     (pgss_track == PGSS_TRACK_ALL || \
     313                 :             :     (pgss_track == PGSS_TRACK_TOP && (level) == 0)))
     314                 :             : 
     315                 :             : #define record_gc_qtexts() \
     316                 :             :     do { \
     317                 :             :         SpinLockAcquire(&pgss->mutex); \
     318                 :             :         pgss->gc_count++; \
     319                 :             :         SpinLockRelease(&pgss->mutex); \
     320                 :             :     } while(0)
     321                 :             : 
     322                 :             : /*---- Function declarations ----*/
     323                 :             : 
     324                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_reset);
     325                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_7);
     326                 :          21 : PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_11);
     327                 :           0 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_2);
     328                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_3);
     329                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
     330                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
     331                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
     332                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
     333                 :           7 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_12);
     334                 :          25 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_13);
     335                 :           0 : PG_FUNCTION_INFO_V1(pg_stat_statements);
     336                 :          10 : PG_FUNCTION_INFO_V1(pg_stat_statements_info);
     337                 :             : 
     338                 :             : static void pgss_shmem_shutdown(int code, Datum arg);
     339                 :             : static void pgss_post_parse_analyze(ParseState *pstate, Query *query,
     340                 :             :                                     const JumbleState *jstate);
     341                 :             : static PlannedStmt *pgss_planner(Query *parse,
     342                 :             :                                  const char *query_string,
     343                 :             :                                  int cursorOptions,
     344                 :             :                                  ParamListInfo boundParams,
     345                 :             :                                  ExplainState *es);
     346                 :             : static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
     347                 :             : static void pgss_ExecutorRun(QueryDesc *queryDesc,
     348                 :             :                              ScanDirection direction,
     349                 :             :                              uint64 count);
     350                 :             : static void pgss_ExecutorFinish(QueryDesc *queryDesc);
     351                 :             : static void pgss_ExecutorEnd(QueryDesc *queryDesc);
     352                 :             : static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
     353                 :             :                                 bool readOnlyTree,
     354                 :             :                                 ProcessUtilityContext context, ParamListInfo params,
     355                 :             :                                 QueryEnvironment *queryEnv,
     356                 :             :                                 DestReceiver *dest, QueryCompletion *qc);
     357                 :             : static void pgss_store(const char *query, int64 queryId,
     358                 :             :                        int query_location, int query_len,
     359                 :             :                        pgssStoreKind kind,
     360                 :             :                        double total_time, uint64 rows,
     361                 :             :                        const BufferUsage *bufusage,
     362                 :             :                        const WalUsage *walusage,
     363                 :             :                        const struct JitInstrumentation *jitusage,
     364                 :             :                        const JumbleState *jstate,
     365                 :             :                        int parallel_workers_to_launch,
     366                 :             :                        int parallel_workers_launched,
     367                 :             :                        PlannedStmtOrigin planOrigin);
     368                 :             : static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
     369                 :             :                                         pgssVersion api_version,
     370                 :             :                                         bool showtext);
     371                 :             : static pgssEntry *entry_alloc(pgssHashKey *key, Size query_offset, int query_len,
     372                 :             :                               int encoding, bool sticky);
     373                 :             : static void entry_dealloc(void);
     374                 :             : static bool qtext_store(const char *query, int query_len,
     375                 :             :                         Size *query_offset, int *gc_count);
     376                 :             : static char *qtext_load_file(Size *buffer_size);
     377                 :             : static char *qtext_fetch(Size query_offset, int query_len,
     378                 :             :                          char *buffer, Size buffer_size);
     379                 :             : static bool need_gc_qtexts(void);
     380                 :             : static void gc_qtexts(void);
     381                 :             : static TimestampTz entry_reset(Oid userid, Oid dbid, int64 queryid, bool minmax_only);
     382                 :             : static char *generate_normalized_query(const JumbleState *jstate,
     383                 :             :                                        const char *query,
     384                 :             :                                        int query_loc, int *query_len_p);
     385                 :             : 
     386                 :             : /*
     387                 :             :  * Module load callback
     388                 :             :  */
     389                 :             : void
     390                 :          10 : _PG_init(void)
     391                 :             : {
     392                 :             :     /*
     393                 :             :      * In order to create our shared memory area, we have to be loaded via
     394                 :             :      * shared_preload_libraries.  If not, fall out without hooking into any of
     395                 :             :      * the main system.  (We don't throw error here because it seems useful to
     396                 :             :      * allow the pg_stat_statements functions to be created even when the
     397                 :             :      * module isn't active.  The functions must protect themselves against
     398                 :             :      * being called then, however.)
     399                 :             :      */
     400         [ +  + ]:          10 :     if (!process_shared_preload_libraries_in_progress)
     401                 :           1 :         return;
     402                 :             : 
     403                 :             :     /*
     404                 :             :      * Inform the postmaster that we want to enable query_id calculation if
     405                 :             :      * compute_query_id is set to auto.
     406                 :             :      */
     407                 :           9 :     EnableQueryId();
     408                 :             : 
     409                 :             :     /*
     410                 :             :      * Define (or redefine) custom GUC variables.
     411                 :             :      */
     412                 :           9 :     DefineCustomIntVariable("pg_stat_statements.max",
     413                 :             :                             "Sets the maximum number of statements tracked by pg_stat_statements.",
     414                 :             :                             NULL,
     415                 :             :                             &pgss_max,
     416                 :             :                             5000,
     417                 :             :                             100,
     418                 :             :                             INT_MAX / 2,
     419                 :             :                             PGC_POSTMASTER,
     420                 :             :                             0,
     421                 :             :                             NULL,
     422                 :             :                             NULL,
     423                 :             :                             NULL);
     424                 :             : 
     425                 :           9 :     DefineCustomEnumVariable("pg_stat_statements.track",
     426                 :             :                              "Selects which statements are tracked by pg_stat_statements.",
     427                 :             :                              NULL,
     428                 :             :                              &pgss_track,
     429                 :             :                              PGSS_TRACK_TOP,
     430                 :             :                              track_options,
     431                 :             :                              PGC_SUSET,
     432                 :             :                              0,
     433                 :             :                              NULL,
     434                 :             :                              NULL,
     435                 :             :                              NULL);
     436                 :             : 
     437                 :           9 :     DefineCustomBoolVariable("pg_stat_statements.track_utility",
     438                 :             :                              "Selects whether utility commands are tracked by pg_stat_statements.",
     439                 :             :                              NULL,
     440                 :             :                              &pgss_track_utility,
     441                 :             :                              true,
     442                 :             :                              PGC_SUSET,
     443                 :             :                              0,
     444                 :             :                              NULL,
     445                 :             :                              NULL,
     446                 :             :                              NULL);
     447                 :             : 
     448                 :           9 :     DefineCustomBoolVariable("pg_stat_statements.track_planning",
     449                 :             :                              "Selects whether planning duration is tracked by pg_stat_statements.",
     450                 :             :                              NULL,
     451                 :             :                              &pgss_track_planning,
     452                 :             :                              false,
     453                 :             :                              PGC_SUSET,
     454                 :             :                              0,
     455                 :             :                              NULL,
     456                 :             :                              NULL,
     457                 :             :                              NULL);
     458                 :             : 
     459                 :           9 :     DefineCustomBoolVariable("pg_stat_statements.save",
     460                 :             :                              "Save pg_stat_statements statistics across server shutdowns.",
     461                 :             :                              NULL,
     462                 :             :                              &pgss_save,
     463                 :             :                              true,
     464                 :             :                              PGC_SIGHUP,
     465                 :             :                              0,
     466                 :             :                              NULL,
     467                 :             :                              NULL,
     468                 :             :                              NULL);
     469                 :             : 
     470                 :           9 :     MarkGUCPrefixReserved("pg_stat_statements");
     471                 :             : 
     472                 :             :     /*
     473                 :             :      * Register our shared memory needs.
     474                 :             :      */
     475                 :           9 :     RegisterShmemCallbacks(&pgss_shmem_callbacks);
     476                 :             : 
     477                 :             :     /*
     478                 :             :      * Install hooks.
     479                 :             :      */
     480                 :           9 :     prev_post_parse_analyze_hook = post_parse_analyze_hook;
     481                 :           9 :     post_parse_analyze_hook = pgss_post_parse_analyze;
     482                 :           9 :     prev_planner_hook = planner_hook;
     483                 :           9 :     planner_hook = pgss_planner;
     484                 :           9 :     prev_ExecutorStart = ExecutorStart_hook;
     485                 :           9 :     ExecutorStart_hook = pgss_ExecutorStart;
     486                 :           9 :     prev_ExecutorRun = ExecutorRun_hook;
     487                 :           9 :     ExecutorRun_hook = pgss_ExecutorRun;
     488                 :           9 :     prev_ExecutorFinish = ExecutorFinish_hook;
     489                 :           9 :     ExecutorFinish_hook = pgss_ExecutorFinish;
     490                 :           9 :     prev_ExecutorEnd = ExecutorEnd_hook;
     491                 :           9 :     ExecutorEnd_hook = pgss_ExecutorEnd;
     492                 :           9 :     prev_ProcessUtility = ProcessUtility_hook;
     493                 :           9 :     ProcessUtility_hook = pgss_ProcessUtility;
     494                 :             : }
     495                 :             : 
     496                 :             : /*
     497                 :             :  * shmem request callback: Request shared memory resources.
     498                 :             :  *
     499                 :             :  * This is called at postmaster startup.  Note that the shared memory isn't
     500                 :             :  * allocated here yet, this merely register our needs.
     501                 :             :  *
     502                 :             :  * In EXEC_BACKEND mode, this is also called in each backend, to re-attach to
     503                 :             :  * the shared memory area that was already initialized.
     504                 :             :  */
     505                 :             : static void
     506                 :          11 : pgss_shmem_request(void *arg)
     507                 :             : {
     508                 :          11 :     ShmemRequestHash(.name = "pg_stat_statements hash",
     509                 :             :                      .nelems = pgss_max,
     510                 :             :                      .hash_info.keysize = sizeof(pgssHashKey),
     511                 :             :                      .hash_info.entrysize = sizeof(pgssEntry),
     512                 :             :                      .hash_flags = HASH_ELEM | HASH_BLOBS,
     513                 :             :                      .ptr = &pgss_hash,
     514                 :             :         );
     515                 :          11 :     ShmemRequestStruct(.name = "pg_stat_statements",
     516                 :             :                        .size = sizeof(pgssSharedState),
     517                 :             :                        .ptr = (void **) &pgss,
     518                 :             :         );
     519                 :          11 : }
     520                 :             : 
     521                 :             : /*
     522                 :             :  * shmem init callback: Initialize our shared memory data structures at
     523                 :             :  * postmaster startup.
     524                 :             :  *
     525                 :             :  * Load any pre-existing statistics from file.  Also create and load the
     526                 :             :  * query-texts file, which is expected to exist (even if empty) while the
     527                 :             :  * module is enabled.
     528                 :             :  */
     529                 :             : static void
     530                 :          11 : pgss_shmem_init(void *arg)
     531                 :             : {
     532                 :             :     int         tranche_id;
     533                 :          11 :     FILE       *file = NULL;
     534                 :          11 :     FILE       *qfile = NULL;
     535                 :             :     uint32      header;
     536                 :             :     int32       num;
     537                 :             :     int32       pgver;
     538                 :             :     int32       i;
     539                 :             :     int         buffer_size;
     540                 :          11 :     char       *buffer = NULL;
     541                 :             : 
     542                 :             :     /*
     543                 :             :      * We already checked that we're loaded from shared_preload_libraries in
     544                 :             :      * _PG_init(), so we should not get here after postmaster startup.
     545                 :             :      */
     546                 :             :     Assert(!IsUnderPostmaster);
     547                 :             : 
     548                 :             :     /*
     549                 :             :      * Initialize the shmem area with no statistics.
     550                 :             :      */
     551                 :          11 :     tranche_id = LWLockNewTrancheId("pg_stat_statements");
     552                 :          11 :     LWLockInitialize(&pgss->lock.lock, tranche_id);
     553                 :          11 :     pgss->cur_median_usage = ASSUMED_MEDIAN_INIT;
     554                 :          11 :     pgss->mean_query_len = ASSUMED_LENGTH_INIT;
     555                 :          11 :     SpinLockInit(&pgss->mutex);
     556                 :          11 :     pgss->extent = 0;
     557                 :          11 :     pgss->n_writers = 0;
     558                 :          11 :     pgss->gc_count = 0;
     559                 :          11 :     pgss->stats.dealloc = 0;
     560                 :          11 :     pgss->stats.stats_reset = GetCurrentTimestamp();
     561                 :             : 
     562                 :             :     /* The hash table must've also been initialized by now */
     563                 :             :     Assert(pgss_hash != NULL);
     564                 :             : 
     565                 :             :     /*
     566                 :             :      * Set up a shmem exit hook to dump the statistics to disk on postmaster
     567                 :             :      * (or standalone backend) exit.
     568                 :             :      */
     569                 :          11 :     on_shmem_exit(pgss_shmem_shutdown, (Datum) 0);
     570                 :             : 
     571                 :             :     /*
     572                 :             :      * Load any pre-existing statistics from file.
     573                 :             :      *
     574                 :             :      * Note: we don't bother with locks here, because there should be no other
     575                 :             :      * processes running when this code is reached.
     576                 :             :      */
     577                 :             : 
     578                 :             :     /* Unlink query text file possibly left over from crash */
     579                 :          11 :     unlink(PGSS_TEXT_FILE);
     580                 :             : 
     581                 :             :     /* Allocate new query text temp file */
     582                 :          11 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
     583         [ -  + ]:          11 :     if (qfile == NULL)
     584                 :           0 :         goto write_error;
     585                 :             : 
     586                 :             :     /*
     587                 :             :      * If we were told not to load old statistics, we're done.  (Note we do
     588                 :             :      * not try to unlink any old dump file in this case.  This seems a bit
     589                 :             :      * questionable but it's the historical behavior.)
     590                 :             :      */
     591         [ +  + ]:          11 :     if (!pgss_save)
     592                 :             :     {
     593                 :           1 :         FreeFile(qfile);
     594                 :          11 :         return;
     595                 :             :     }
     596                 :             : 
     597                 :             :     /*
     598                 :             :      * Attempt to load old statistics from the dump file.
     599                 :             :      */
     600                 :          10 :     file = AllocateFile(PGSS_DUMP_FILE, PG_BINARY_R);
     601         [ +  + ]:          10 :     if (file == NULL)
     602                 :             :     {
     603         [ -  + ]:           7 :         if (errno != ENOENT)
     604                 :           0 :             goto read_error;
     605                 :             :         /* No existing persisted stats file, so we're done */
     606                 :           7 :         FreeFile(qfile);
     607                 :           7 :         return;
     608                 :             :     }
     609                 :             : 
     610                 :           3 :     buffer_size = 2048;
     611                 :           3 :     buffer = (char *) palloc(buffer_size);
     612                 :             : 
     613   [ +  -  +  - ]:           6 :     if (fread(&header, sizeof(uint32), 1, file) != 1 ||
     614         [ -  + ]:           6 :         fread(&pgver, sizeof(uint32), 1, file) != 1 ||
     615                 :           3 :         fread(&num, sizeof(int32), 1, file) != 1)
     616                 :           0 :         goto read_error;
     617                 :             : 
     618         [ +  - ]:           3 :     if (header != PGSS_FILE_HEADER ||
     619         [ -  + ]:           3 :         pgver != PGSS_PG_MAJOR_VERSION)
     620                 :           0 :         goto data_error;
     621                 :             : 
     622         [ +  + ]:       29370 :     for (i = 0; i < num; i++)
     623                 :             :     {
     624                 :             :         pgssEntry   temp;
     625                 :             :         pgssEntry  *entry;
     626                 :             :         Size        query_offset;
     627                 :             : 
     628         [ -  + ]:       29367 :         if (fread(&temp, sizeof(pgssEntry), 1, file) != 1)
     629                 :           0 :             goto read_error;
     630                 :             : 
     631                 :             :         /* Encoding is the only field we can easily sanity-check */
     632   [ +  -  +  -  :       29367 :         if (!PG_VALID_BE_ENCODING(temp.encoding))
                   -  + ]
     633                 :           0 :             goto data_error;
     634                 :             : 
     635                 :             :         /* Resize buffer as needed */
     636         [ +  + ]:       29367 :         if (temp.query_len >= buffer_size)
     637                 :             :         {
     638                 :           1 :             buffer_size = Max(buffer_size * 2, temp.query_len + 1);
     639                 :           1 :             buffer = repalloc(buffer, buffer_size);
     640                 :             :         }
     641                 :             : 
     642         [ -  + ]:       29367 :         if (fread(buffer, 1, temp.query_len + 1, file) != temp.query_len + 1)
     643                 :           0 :             goto read_error;
     644                 :             : 
     645                 :             :         /* Should have a trailing null, but let's make sure */
     646                 :       29367 :         buffer[temp.query_len] = '\0';
     647                 :             : 
     648                 :             :         /* Skip loading "sticky" entries */
     649         [ +  + ]:       29367 :         if (IS_STICKY(temp.counters))
     650                 :         800 :             continue;
     651                 :             : 
     652                 :             :         /* Store the query text */
     653                 :       28567 :         query_offset = pgss->extent;
     654         [ -  + ]:       28567 :         if (fwrite(buffer, 1, temp.query_len + 1, qfile) != temp.query_len + 1)
     655                 :           0 :             goto write_error;
     656                 :       28567 :         pgss->extent += temp.query_len + 1;
     657                 :             : 
     658                 :             :         /* make the hashtable entry (discards old entries if too many) */
     659                 :       28567 :         entry = entry_alloc(&temp.key, query_offset, temp.query_len,
     660                 :             :                             temp.encoding,
     661                 :             :                             false);
     662                 :             : 
     663                 :             :         /* copy in the actual stats */
     664                 :       28567 :         entry->counters = temp.counters;
     665                 :       28567 :         entry->stats_since = temp.stats_since;
     666                 :       28567 :         entry->minmax_stats_since = temp.minmax_stats_since;
     667                 :             :     }
     668                 :             : 
     669                 :             :     /* Read global statistics for pg_stat_statements */
     670         [ -  + ]:           3 :     if (fread(&pgss->stats, sizeof(pgssGlobalStats), 1, file) != 1)
     671                 :           0 :         goto read_error;
     672                 :             : 
     673                 :           3 :     pfree(buffer);
     674                 :           3 :     FreeFile(file);
     675                 :           3 :     FreeFile(qfile);
     676                 :             : 
     677                 :             :     /*
     678                 :             :      * Remove the persisted stats file so it's not included in
     679                 :             :      * backups/replication standbys, etc.  A new file will be written on next
     680                 :             :      * shutdown.
     681                 :             :      *
     682                 :             :      * Note: it's okay if the PGSS_TEXT_FILE is included in a basebackup,
     683                 :             :      * because we remove that file on startup; it acts inversely to
     684                 :             :      * PGSS_DUMP_FILE, in that it is only supposed to be around when the
     685                 :             :      * server is running, whereas PGSS_DUMP_FILE is only supposed to be around
     686                 :             :      * when the server is not running.  Leaving the file creates no danger of
     687                 :             :      * a newly restored database having a spurious record of execution costs,
     688                 :             :      * which is what we're really concerned about here.
     689                 :             :      */
     690                 :           3 :     unlink(PGSS_DUMP_FILE);
     691                 :             : 
     692                 :           3 :     return;
     693                 :             : 
     694                 :           0 : read_error:
     695         [ #  # ]:           0 :     ereport(LOG,
     696                 :             :             (errcode_for_file_access(),
     697                 :             :              errmsg("could not read file \"%s\": %m",
     698                 :             :                     PGSS_DUMP_FILE)));
     699                 :           0 :     goto fail;
     700                 :           0 : data_error:
     701         [ #  # ]:           0 :     ereport(LOG,
     702                 :             :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     703                 :             :              errmsg("ignoring invalid data in file \"%s\"",
     704                 :             :                     PGSS_DUMP_FILE)));
     705                 :           0 :     goto fail;
     706                 :           0 : write_error:
     707         [ #  # ]:           0 :     ereport(LOG,
     708                 :             :             (errcode_for_file_access(),
     709                 :             :              errmsg("could not write file \"%s\": %m",
     710                 :             :                     PGSS_TEXT_FILE)));
     711                 :           0 : fail:
     712         [ #  # ]:           0 :     if (buffer)
     713                 :           0 :         pfree(buffer);
     714         [ #  # ]:           0 :     if (file)
     715                 :           0 :         FreeFile(file);
     716         [ #  # ]:           0 :     if (qfile)
     717                 :           0 :         FreeFile(qfile);
     718                 :             :     /* If possible, throw away the bogus file; ignore any error */
     719                 :           0 :     unlink(PGSS_DUMP_FILE);
     720                 :             : 
     721                 :             :     /*
     722                 :             :      * Don't unlink PGSS_TEXT_FILE here; it should always be around while the
     723                 :             :      * server is running with pg_stat_statements enabled
     724                 :             :      */
     725                 :             : }
     726                 :             : 
     727                 :             : /*
     728                 :             :  * shmem_shutdown hook: Dump statistics into file.
     729                 :             :  *
     730                 :             :  * Note: we don't bother with acquiring lock, because there should be no
     731                 :             :  * other processes running when this is called.
     732                 :             :  */
     733                 :             : static void
     734                 :          11 : pgss_shmem_shutdown(int code, Datum arg)
     735                 :             : {
     736                 :             :     FILE       *file;
     737                 :          11 :     char       *qbuffer = NULL;
     738                 :          11 :     Size        qbuffer_size = 0;
     739                 :             :     HASH_SEQ_STATUS hash_seq;
     740                 :             :     int32       num_entries;
     741                 :             :     pgssEntry  *entry;
     742                 :             : 
     743                 :             :     /* Don't try to dump during a crash. */
     744         [ +  + ]:          11 :     if (code)
     745                 :          11 :         return;
     746                 :             : 
     747                 :             :     /* Safety check ... shouldn't get here unless shmem is set up. */
     748   [ +  -  -  + ]:           9 :     if (!pgss || !pgss_hash)
     749                 :           0 :         return;
     750                 :             : 
     751                 :             :     /* Don't dump if told not to. */
     752         [ +  + ]:           9 :     if (!pgss_save)
     753                 :           2 :         return;
     754                 :             : 
     755                 :           7 :     file = AllocateFile(PGSS_DUMP_FILE ".tmp", PG_BINARY_W);
     756         [ -  + ]:           7 :     if (file == NULL)
     757                 :           0 :         goto error;
     758                 :             : 
     759         [ -  + ]:           7 :     if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1)
     760                 :           0 :         goto error;
     761         [ -  + ]:           7 :     if (fwrite(&PGSS_PG_MAJOR_VERSION, sizeof(uint32), 1, file) != 1)
     762                 :           0 :         goto error;
     763                 :           7 :     num_entries = hash_get_num_entries(pgss_hash);
     764         [ -  + ]:           7 :     if (fwrite(&num_entries, sizeof(int32), 1, file) != 1)
     765                 :           0 :         goto error;
     766                 :             : 
     767                 :           7 :     qbuffer = qtext_load_file(&qbuffer_size);
     768         [ -  + ]:           7 :     if (qbuffer == NULL)
     769                 :           0 :         goto error;
     770                 :             : 
     771                 :             :     /*
     772                 :             :      * When serializing to disk, we store query texts immediately after their
     773                 :             :      * entry data.  Any orphaned query texts are thereby excluded.
     774                 :             :      */
     775                 :           7 :     hash_seq_init(&hash_seq, pgss_hash);
     776         [ +  + ]:       59022 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
     777                 :             :     {
     778                 :       59015 :         int         len = entry->query_len;
     779                 :       59015 :         char       *qstr = qtext_fetch(entry->query_offset, len,
     780                 :             :                                        qbuffer, qbuffer_size);
     781                 :             : 
     782         [ -  + ]:       59015 :         if (qstr == NULL)
     783                 :           0 :             continue;           /* Ignore any entries with bogus texts */
     784                 :             : 
     785         [ +  - ]:       59015 :         if (fwrite(entry, sizeof(pgssEntry), 1, file) != 1 ||
     786         [ -  + ]:       59015 :             fwrite(qstr, 1, len + 1, file) != len + 1)
     787                 :             :         {
     788                 :             :             /* note: we assume hash_seq_term won't change errno */
     789                 :           0 :             hash_seq_term(&hash_seq);
     790                 :           0 :             goto error;
     791                 :             :         }
     792                 :             :     }
     793                 :             : 
     794                 :             :     /* Dump global statistics for pg_stat_statements */
     795         [ -  + ]:           7 :     if (fwrite(&pgss->stats, sizeof(pgssGlobalStats), 1, file) != 1)
     796                 :           0 :         goto error;
     797                 :             : 
     798                 :           7 :     pfree(qbuffer);
     799                 :           7 :     qbuffer = NULL;
     800                 :             : 
     801         [ -  + ]:           7 :     if (FreeFile(file))
     802                 :             :     {
     803                 :           0 :         file = NULL;
     804                 :           0 :         goto error;
     805                 :             :     }
     806                 :             : 
     807                 :             :     /*
     808                 :             :      * Rename file into place, so we atomically replace any old one.
     809                 :             :      */
     810                 :           7 :     (void) durable_rename(PGSS_DUMP_FILE ".tmp", PGSS_DUMP_FILE, LOG);
     811                 :             : 
     812                 :             :     /* Unlink query-texts file; it's not needed while shutdown */
     813                 :           7 :     unlink(PGSS_TEXT_FILE);
     814                 :             : 
     815                 :           7 :     return;
     816                 :             : 
     817                 :           0 : error:
     818         [ #  # ]:           0 :     ereport(LOG,
     819                 :             :             (errcode_for_file_access(),
     820                 :             :              errmsg("could not write file \"%s\": %m",
     821                 :             :                     PGSS_DUMP_FILE ".tmp")));
     822         [ #  # ]:           0 :     if (qbuffer)
     823                 :           0 :         pfree(qbuffer);
     824         [ #  # ]:           0 :     if (file)
     825                 :           0 :         FreeFile(file);
     826                 :           0 :     unlink(PGSS_DUMP_FILE ".tmp");
     827                 :           0 :     unlink(PGSS_TEXT_FILE);
     828                 :             : }
     829                 :             : 
     830                 :             : /*
     831                 :             :  * Post-parse-analysis hook: mark query with a queryId
     832                 :             :  */
     833                 :             : static void
     834                 :       85060 : pgss_post_parse_analyze(ParseState *pstate, Query *query, const JumbleState *jstate)
     835                 :             : {
     836         [ -  + ]:       85060 :     if (prev_post_parse_analyze_hook)
     837                 :           0 :         prev_post_parse_analyze_hook(pstate, query, jstate);
     838                 :             : 
     839                 :             :     /* Safety check... */
     840   [ +  -  +  -  :       85060 :     if (!pgss || !pgss_hash || !pgss_enabled(nesting_level))
          +  +  +  +  +  
                +  +  + ]
     841                 :       13205 :         return;
     842                 :             : 
     843                 :             :     /*
     844                 :             :      * If it's EXECUTE, clear the queryId so that stats will accumulate for
     845                 :             :      * the underlying PREPARE.  But don't do this if we're not tracking
     846                 :             :      * utility statements, to avoid messing up another extension that might be
     847                 :             :      * tracking them.
     848                 :             :      */
     849         [ +  + ]:       71855 :     if (query->utilityStmt)
     850                 :             :     {
     851   [ +  +  +  + ]:       32351 :         if (pgss_track_utility && IsA(query->utilityStmt, ExecuteStmt))
     852                 :             :         {
     853                 :        3376 :             query->queryId = INT64CONST(0);
     854                 :        3376 :             return;
     855                 :             :         }
     856                 :             :     }
     857                 :             : 
     858                 :             :     /*
     859                 :             :      * If query jumbling were able to identify any ignorable constants, we
     860                 :             :      * immediately create a hash table entry for the query, so that we can
     861                 :             :      * record the normalized form of the query string.  If there were no such
     862                 :             :      * constants, the normalized string would be the same as the query text
     863                 :             :      * anyway, so there's no need for an early entry.
     864                 :             :      */
     865   [ +  -  +  + ]:       68479 :     if (jstate && jstate->clocations_count > 0)
     866                 :       39354 :         pgss_store(pstate->p_sourcetext,
     867                 :             :                    query->queryId,
     868                 :             :                    query->stmt_location,
     869                 :             :                    query->stmt_len,
     870                 :             :                    PGSS_INVALID,
     871                 :             :                    0,
     872                 :             :                    0,
     873                 :             :                    NULL,
     874                 :             :                    NULL,
     875                 :             :                    NULL,
     876                 :             :                    jstate,
     877                 :             :                    0,
     878                 :             :                    0,
     879                 :             :                    PLAN_STMT_UNKNOWN);
     880                 :             : }
     881                 :             : 
     882                 :             : /*
     883                 :             :  * Planner hook: forward to regular planner, but measure planning time
     884                 :             :  * if needed.
     885                 :             :  */
     886                 :             : static PlannedStmt *
     887                 :       50939 : pgss_planner(Query *parse,
     888                 :             :              const char *query_string,
     889                 :             :              int cursorOptions,
     890                 :             :              ParamListInfo boundParams,
     891                 :             :              ExplainState *es)
     892                 :             : {
     893                 :             :     PlannedStmt *result;
     894                 :             : 
     895                 :             :     /*
     896                 :             :      * We can't process the query if no query_string is provided, as
     897                 :             :      * pgss_store needs it.  We also ignore query without queryid, as it would
     898                 :             :      * be treated as a utility statement, which may not be the case.
     899                 :             :      */
     900   [ +  +  +  +  :       50939 :     if (pgss_enabled(nesting_level)
             +  +  +  + ]
     901   [ +  +  +  - ]:       39653 :         && pgss_track_planning && query_string
     902         [ +  - ]:         150 :         && parse->queryId != INT64CONST(0))
     903                 :         150 :     {
     904                 :             :         instr_time  start;
     905                 :             :         instr_time  duration;
     906                 :             :         BufferUsage bufusage_start,
     907                 :             :                     bufusage;
     908                 :             :         WalUsage    walusage_start,
     909                 :             :                     walusage;
     910                 :             : 
     911                 :             :         /* We need to track buffer usage as the planner can access them. */
     912                 :         150 :         bufusage_start = pgBufferUsage;
     913                 :             : 
     914                 :             :         /*
     915                 :             :          * Similarly the planner could write some WAL records in some cases
     916                 :             :          * (e.g. setting a hint bit with those being WAL-logged)
     917                 :             :          */
     918                 :         150 :         walusage_start = pgWalUsage;
     919                 :         150 :         INSTR_TIME_SET_CURRENT(start);
     920                 :             : 
     921                 :         150 :         nesting_level++;
     922         [ +  - ]:         150 :         PG_TRY();
     923                 :             :         {
     924         [ -  + ]:         150 :             if (prev_planner_hook)
     925                 :           0 :                 result = prev_planner_hook(parse, query_string, cursorOptions,
     926                 :             :                                            boundParams, es);
     927                 :             :             else
     928                 :         150 :                 result = standard_planner(parse, query_string, cursorOptions,
     929                 :             :                                           boundParams, es);
     930                 :             :         }
     931                 :           0 :         PG_FINALLY();
     932                 :             :         {
     933                 :         150 :             nesting_level--;
     934                 :             :         }
     935         [ -  + ]:         150 :         PG_END_TRY();
     936                 :             : 
     937                 :         150 :         INSTR_TIME_SET_CURRENT(duration);
     938                 :         150 :         INSTR_TIME_SUBTRACT(duration, start);
     939                 :             : 
     940                 :             :         /* calc differences of buffer counters. */
     941                 :         150 :         memset(&bufusage, 0, sizeof(BufferUsage));
     942                 :         150 :         BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
     943                 :             : 
     944                 :             :         /* calc differences of WAL counters. */
     945                 :         150 :         memset(&walusage, 0, sizeof(WalUsage));
     946                 :         150 :         WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
     947                 :             : 
     948                 :         300 :         pgss_store(query_string,
     949                 :             :                    parse->queryId,
     950                 :             :                    parse->stmt_location,
     951                 :             :                    parse->stmt_len,
     952                 :             :                    PGSS_PLAN,
     953                 :         150 :                    INSTR_TIME_GET_MILLISEC(duration),
     954                 :             :                    0,
     955                 :             :                    &bufusage,
     956                 :             :                    &walusage,
     957                 :             :                    NULL,
     958                 :             :                    NULL,
     959                 :             :                    0,
     960                 :             :                    0,
     961                 :             :                    result->planOrigin);
     962                 :             :     }
     963                 :             :     else
     964                 :             :     {
     965                 :             :         /*
     966                 :             :          * Even though we're not tracking plan time for this statement, we
     967                 :             :          * must still increment the nesting level, to ensure that functions
     968                 :             :          * evaluated during planning are not seen as top-level calls.
     969                 :             :          */
     970                 :       50789 :         nesting_level++;
     971         [ +  + ]:       50789 :         PG_TRY();
     972                 :             :         {
     973         [ -  + ]:       50789 :             if (prev_planner_hook)
     974                 :           0 :                 result = prev_planner_hook(parse, query_string, cursorOptions,
     975                 :             :                                            boundParams, es);
     976                 :             :             else
     977                 :       50789 :                 result = standard_planner(parse, query_string, cursorOptions,
     978                 :             :                                           boundParams, es);
     979                 :             :         }
     980                 :         788 :         PG_FINALLY();
     981                 :             :         {
     982                 :       50789 :             nesting_level--;
     983                 :             :         }
     984         [ +  + ]:       50789 :         PG_END_TRY();
     985                 :             :     }
     986                 :             : 
     987                 :       50151 :     return result;
     988                 :             : }
     989                 :             : 
     990                 :             : /*
     991                 :             :  * ExecutorStart hook: start up tracking if needed
     992                 :             :  */
     993                 :             : static void
     994                 :       61018 : pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
     995                 :             : {
     996                 :             :     /*
     997                 :             :      * If query has queryId zero, don't track it.  This prevents double
     998                 :             :      * counting of optimizable statements that are directly contained in
     999                 :             :      * utility statements.
    1000                 :             :      */
    1001   [ +  +  +  +  :       61018 :     if (pgss_enabled(nesting_level) && queryDesc->plannedstmt->queryId != INT64CONST(0))
          +  +  +  +  +  
                      + ]
    1002                 :             :     {
    1003                 :             :         /* Request all summary instrumentation, i.e. timing, buffers and WAL */
    1004                 :       41834 :         queryDesc->query_instr_options |= INSTRUMENT_ALL;
    1005                 :             :     }
    1006                 :             : 
    1007         [ -  + ]:       61018 :     if (prev_ExecutorStart)
    1008                 :           0 :         prev_ExecutorStart(queryDesc, eflags);
    1009                 :             :     else
    1010                 :       61018 :         standard_ExecutorStart(queryDesc, eflags);
    1011                 :       60731 : }
    1012                 :             : 
    1013                 :             : /*
    1014                 :             :  * ExecutorRun hook: all we need do is track nesting depth
    1015                 :             :  */
    1016                 :             : static void
    1017                 :       59322 : pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
    1018                 :             : {
    1019                 :       59322 :     nesting_level++;
    1020         [ +  + ]:       59322 :     PG_TRY();
    1021                 :             :     {
    1022         [ -  + ]:       59322 :         if (prev_ExecutorRun)
    1023                 :           0 :             prev_ExecutorRun(queryDesc, direction, count);
    1024                 :             :         else
    1025                 :       59322 :             standard_ExecutorRun(queryDesc, direction, count);
    1026                 :             :     }
    1027                 :        3423 :     PG_FINALLY();
    1028                 :             :     {
    1029                 :       59322 :         nesting_level--;
    1030                 :             :     }
    1031         [ +  + ]:       59322 :     PG_END_TRY();
    1032                 :       55899 : }
    1033                 :             : 
    1034                 :             : /*
    1035                 :             :  * ExecutorFinish hook: all we need do is track nesting depth
    1036                 :             :  */
    1037                 :             : static void
    1038                 :       53830 : pgss_ExecutorFinish(QueryDesc *queryDesc)
    1039                 :             : {
    1040                 :       53830 :     nesting_level++;
    1041         [ +  + ]:       53830 :     PG_TRY();
    1042                 :             :     {
    1043         [ -  + ]:       53830 :         if (prev_ExecutorFinish)
    1044                 :           0 :             prev_ExecutorFinish(queryDesc);
    1045                 :             :         else
    1046                 :       53830 :             standard_ExecutorFinish(queryDesc);
    1047                 :             :     }
    1048                 :         182 :     PG_FINALLY();
    1049                 :             :     {
    1050                 :       53830 :         nesting_level--;
    1051                 :             :     }
    1052         [ +  + ]:       53830 :     PG_END_TRY();
    1053                 :       53648 : }
    1054                 :             : 
    1055                 :             : /*
    1056                 :             :  * ExecutorEnd hook: store results if needed
    1057                 :             :  */
    1058                 :             : static void
    1059                 :       56795 : pgss_ExecutorEnd(QueryDesc *queryDesc)
    1060                 :             : {
    1061                 :       56795 :     int64       queryId = queryDesc->plannedstmt->queryId;
    1062                 :             : 
    1063   [ +  +  +  + ]:       56795 :     if (queryId != INT64CONST(0) && queryDesc->query_instr &&
    1064   [ +  -  +  +  :       39870 :         pgss_enabled(nesting_level))
             +  -  +  - ]
    1065                 :             :     {
    1066                 :       39870 :         pgss_store(queryDesc->sourceText,
    1067                 :             :                    queryId,
    1068                 :       39870 :                    queryDesc->plannedstmt->stmt_location,
    1069                 :       39870 :                    queryDesc->plannedstmt->stmt_len,
    1070                 :             :                    PGSS_EXEC,
    1071                 :       39870 :                    INSTR_TIME_GET_MILLISEC(queryDesc->query_instr->total),
    1072                 :       39870 :                    queryDesc->estate->es_total_processed,
    1073                 :       39870 :                    &queryDesc->query_instr->bufusage,
    1074                 :       39870 :                    &queryDesc->query_instr->walusage,
    1075                 :           0 :                    queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
    1076                 :             :                    NULL,
    1077                 :       39870 :                    queryDesc->estate->es_parallel_workers_to_launch,
    1078                 :       39870 :                    queryDesc->estate->es_parallel_workers_launched,
    1079         [ -  + ]:       39870 :                    queryDesc->plannedstmt->planOrigin);
    1080                 :             :     }
    1081                 :             : 
    1082         [ -  + ]:       56795 :     if (prev_ExecutorEnd)
    1083                 :           0 :         prev_ExecutorEnd(queryDesc);
    1084                 :             :     else
    1085                 :       56795 :         standard_ExecutorEnd(queryDesc);
    1086                 :       56795 : }
    1087                 :             : 
    1088                 :             : /*
    1089                 :             :  * ProcessUtility hook
    1090                 :             :  */
    1091                 :             : static void
    1092                 :       38061 : pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
    1093                 :             :                     bool readOnlyTree,
    1094                 :             :                     ProcessUtilityContext context,
    1095                 :             :                     ParamListInfo params, QueryEnvironment *queryEnv,
    1096                 :             :                     DestReceiver *dest, QueryCompletion *qc)
    1097                 :             : {
    1098                 :       38061 :     Node       *parsetree = pstmt->utilityStmt;
    1099                 :       38061 :     int64       saved_queryId = pstmt->queryId;
    1100                 :       38061 :     int         saved_stmt_location = pstmt->stmt_location;
    1101                 :       38061 :     int         saved_stmt_len = pstmt->stmt_len;
    1102                 :       38061 :     PlannedStmtOrigin saved_planOrigin = pstmt->planOrigin;
    1103   [ +  +  +  -  :       38061 :     bool        enabled = pgss_track_utility && pgss_enabled(nesting_level);
          +  +  +  -  +  
                      + ]
    1104                 :             : 
    1105                 :             :     /*
    1106                 :             :      * Force utility statements to get queryId zero.  We do this even in cases
    1107                 :             :      * where the statement contains an optimizable statement for which a
    1108                 :             :      * queryId could be derived (such as EXPLAIN or DECLARE CURSOR).  For such
    1109                 :             :      * cases, runtime control will first go through ProcessUtility and then
    1110                 :             :      * the executor, and we don't want the executor hooks to do anything,
    1111                 :             :      * since we are already measuring the statement's costs at the utility
    1112                 :             :      * level.
    1113                 :             :      *
    1114                 :             :      * Note that this is only done if pg_stat_statements is enabled and
    1115                 :             :      * configured to track utility statements, in the unlikely possibility
    1116                 :             :      * that user configured another extension to handle utility statements
    1117                 :             :      * only.
    1118                 :             :      */
    1119         [ +  + ]:       38061 :     if (enabled)
    1120                 :       32237 :         pstmt->queryId = INT64CONST(0);
    1121                 :             : 
    1122                 :             :     /*
    1123                 :             :      * If it's an EXECUTE statement, we don't track it and don't increment the
    1124                 :             :      * nesting level.  This allows the cycles to be charged to the underlying
    1125                 :             :      * PREPARE instead (by the Executor hooks), which is much more useful.
    1126                 :             :      *
    1127                 :             :      * We also don't track execution of PREPARE.  If we did, we would get one
    1128                 :             :      * hash table entry for the PREPARE (with hash calculated from the query
    1129                 :             :      * string), and then a different one with the same query string (but hash
    1130                 :             :      * calculated from the query tree) would be used to accumulate costs of
    1131                 :             :      * ensuing EXECUTEs.  This would be confusing.  Since PREPARE doesn't
    1132                 :             :      * actually run the planner (only parse+rewrite), its costs are generally
    1133                 :             :      * pretty negligible and it seems okay to just ignore it.
    1134                 :             :      */
    1135         [ +  + ]:       38061 :     if (enabled &&
    1136         [ +  + ]:       32237 :         !IsA(parsetree, ExecuteStmt) &&
    1137         [ +  + ]:       28867 :         !IsA(parsetree, PrepareStmt))
    1138                 :       26031 :     {
    1139                 :             :         instr_time  start;
    1140                 :             :         instr_time  duration;
    1141                 :             :         uint64      rows;
    1142                 :             :         BufferUsage bufusage_start,
    1143                 :             :                     bufusage;
    1144                 :             :         WalUsage    walusage_start,
    1145                 :             :                     walusage;
    1146                 :             : 
    1147                 :       28739 :         bufusage_start = pgBufferUsage;
    1148                 :       28739 :         walusage_start = pgWalUsage;
    1149                 :       28739 :         INSTR_TIME_SET_CURRENT(start);
    1150                 :             : 
    1151                 :       28739 :         nesting_level++;
    1152         [ +  + ]:       28739 :         PG_TRY();
    1153                 :             :         {
    1154         [ -  + ]:       28739 :             if (prev_ProcessUtility)
    1155                 :           0 :                 prev_ProcessUtility(pstmt, queryString, readOnlyTree,
    1156                 :             :                                     context, params, queryEnv,
    1157                 :             :                                     dest, qc);
    1158                 :             :             else
    1159                 :       28739 :                 standard_ProcessUtility(pstmt, queryString, readOnlyTree,
    1160                 :             :                                         context, params, queryEnv,
    1161                 :             :                                         dest, qc);
    1162                 :             :         }
    1163                 :        2708 :         PG_FINALLY();
    1164                 :             :         {
    1165                 :       28739 :             nesting_level--;
    1166                 :             :         }
    1167         [ +  + ]:       28739 :         PG_END_TRY();
    1168                 :             : 
    1169                 :             :         /*
    1170                 :             :          * CAUTION: do not access the *pstmt data structure again below here.
    1171                 :             :          * If it was a ROLLBACK or similar, that data structure may have been
    1172                 :             :          * freed.  We must copy everything we still need into local variables,
    1173                 :             :          * which we did above.
    1174                 :             :          *
    1175                 :             :          * For the same reason, we can't risk restoring pstmt->queryId to its
    1176                 :             :          * former value, which'd otherwise be a good idea.
    1177                 :             :          */
    1178                 :       26031 :         pstmt = NULL;
    1179                 :             : 
    1180                 :       26031 :         INSTR_TIME_SET_CURRENT(duration);
    1181                 :       26031 :         INSTR_TIME_SUBTRACT(duration, start);
    1182                 :             : 
    1183                 :             :         /*
    1184                 :             :          * Track the total number of rows retrieved or affected by the utility
    1185                 :             :          * statements of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED
    1186                 :             :          * VIEW, REFRESH MATERIALIZED VIEW and SELECT INTO.
    1187                 :             :          */
    1188         [ +  + ]:       26028 :         rows = (qc && (qc->commandTag == CMDTAG_COPY ||
    1189         [ +  + ]:       24210 :                        qc->commandTag == CMDTAG_FETCH ||
    1190         [ +  + ]:       23946 :                        qc->commandTag == CMDTAG_SELECT ||
    1191         [ +  + ]:       23752 :                        qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW)) ?
    1192         [ +  + ]:       52059 :             qc->nprocessed : 0;
    1193                 :             : 
    1194                 :             :         /* calc differences of buffer counters. */
    1195                 :       26031 :         memset(&bufusage, 0, sizeof(BufferUsage));
    1196                 :       26031 :         BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
    1197                 :             : 
    1198                 :             :         /* calc differences of WAL counters. */
    1199                 :       26031 :         memset(&walusage, 0, sizeof(WalUsage));
    1200                 :       26031 :         WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
    1201                 :             : 
    1202                 :       26031 :         pgss_store(queryString,
    1203                 :             :                    saved_queryId,
    1204                 :             :                    saved_stmt_location,
    1205                 :             :                    saved_stmt_len,
    1206                 :             :                    PGSS_EXEC,
    1207                 :       26031 :                    INSTR_TIME_GET_MILLISEC(duration),
    1208                 :             :                    rows,
    1209                 :             :                    &bufusage,
    1210                 :             :                    &walusage,
    1211                 :             :                    NULL,
    1212                 :             :                    NULL,
    1213                 :             :                    0,
    1214                 :             :                    0,
    1215                 :             :                    saved_planOrigin);
    1216                 :             :     }
    1217                 :             :     else
    1218                 :             :     {
    1219                 :             :         /*
    1220                 :             :          * Even though we're not tracking execution time for this statement,
    1221                 :             :          * we must still increment the nesting level, to ensure that functions
    1222                 :             :          * evaluated within it are not seen as top-level calls.  But don't do
    1223                 :             :          * so for EXECUTE; that way, when control reaches pgss_planner or
    1224                 :             :          * pgss_ExecutorStart, we will treat the costs as top-level if
    1225                 :             :          * appropriate.  Likewise, don't bump for PREPARE, so that parse
    1226                 :             :          * analysis will treat the statement as top-level if appropriate.
    1227                 :             :          *
    1228                 :             :          * To be absolutely certain we don't mess up the nesting level,
    1229                 :             :          * evaluate the bump_level condition just once.
    1230                 :             :          */
    1231                 :        9322 :         bool        bump_level =
    1232         [ +  + ]:       15273 :             !IsA(parsetree, ExecuteStmt) &&
    1233         [ +  + ]:        5951 :             !IsA(parsetree, PrepareStmt);
    1234                 :             : 
    1235         [ +  + ]:        9322 :         if (bump_level)
    1236                 :        5822 :             nesting_level++;
    1237         [ +  + ]:        9322 :         PG_TRY();
    1238                 :             :         {
    1239         [ -  + ]:        9322 :             if (prev_ProcessUtility)
    1240                 :           0 :                 prev_ProcessUtility(pstmt, queryString, readOnlyTree,
    1241                 :             :                                     context, params, queryEnv,
    1242                 :             :                                     dest, qc);
    1243                 :             :             else
    1244                 :        9322 :                 standard_ProcessUtility(pstmt, queryString, readOnlyTree,
    1245                 :             :                                         context, params, queryEnv,
    1246                 :             :                                         dest, qc);
    1247                 :             :         }
    1248                 :         139 :         PG_FINALLY();
    1249                 :             :         {
    1250         [ +  + ]:        9322 :             if (bump_level)
    1251                 :        5822 :                 nesting_level--;
    1252                 :             :         }
    1253         [ +  + ]:        9322 :         PG_END_TRY();
    1254                 :             :     }
    1255                 :       35214 : }
    1256                 :             : 
    1257                 :             : /*
    1258                 :             :  * Store some statistics for a statement.
    1259                 :             :  *
    1260                 :             :  * If jstate is not NULL then we're trying to create an entry for which
    1261                 :             :  * we have no statistics as yet; we just want to record the normalized
    1262                 :             :  * query string.  total_time, rows, bufusage and walusage are ignored in this
    1263                 :             :  * case.
    1264                 :             :  *
    1265                 :             :  * If kind is PGSS_PLAN or PGSS_EXEC, its value is used as the array position
    1266                 :             :  * for the arrays in the Counters field.
    1267                 :             :  */
    1268                 :             : static void
    1269                 :      105405 : pgss_store(const char *query, int64 queryId,
    1270                 :             :            int query_location, int query_len,
    1271                 :             :            pgssStoreKind kind,
    1272                 :             :            double total_time, uint64 rows,
    1273                 :             :            const BufferUsage *bufusage,
    1274                 :             :            const WalUsage *walusage,
    1275                 :             :            const struct JitInstrumentation *jitusage,
    1276                 :             :            const JumbleState *jstate,
    1277                 :             :            int parallel_workers_to_launch,
    1278                 :             :            int parallel_workers_launched,
    1279                 :             :            PlannedStmtOrigin planOrigin)
    1280                 :             : {
    1281                 :             :     pgssHashKey key;
    1282                 :             :     pgssEntry  *entry;
    1283                 :      105405 :     char       *norm_query = NULL;
    1284                 :      105405 :     int         encoding = GetDatabaseEncoding();
    1285                 :             : 
    1286                 :             :     Assert(query != NULL);
    1287                 :             : 
    1288                 :             :     /* Safety check... */
    1289   [ +  -  -  + ]:      105405 :     if (!pgss || !pgss_hash)
    1290                 :           0 :         return;
    1291                 :             : 
    1292                 :             :     /*
    1293                 :             :      * Nothing to do if compute_query_id isn't enabled and no other module
    1294                 :             :      * computed a query identifier.
    1295                 :             :      */
    1296         [ -  + ]:      105405 :     if (queryId == INT64CONST(0))
    1297                 :           0 :         return;
    1298                 :             : 
    1299                 :             :     /*
    1300                 :             :      * Confine our attention to the relevant part of the string, if the query
    1301                 :             :      * is a portion of a multi-statement source string, and update query
    1302                 :             :      * location and length if needed.
    1303                 :             :      */
    1304                 :      105405 :     query = CleanQuerytext(query, &query_location, &query_len);
    1305                 :             : 
    1306                 :             :     /* Set up key for hashtable search */
    1307                 :             : 
    1308                 :             :     /* clear padding */
    1309                 :      105405 :     memset(&key, 0, sizeof(pgssHashKey));
    1310                 :             : 
    1311                 :      105405 :     key.userid = GetUserId();
    1312                 :      105405 :     key.dbid = MyDatabaseId;
    1313                 :      105405 :     key.queryid = queryId;
    1314                 :      105405 :     key.toplevel = (nesting_level == 0);
    1315                 :             : 
    1316                 :             :     /* Lookup the hash table entry with shared lock. */
    1317                 :      105405 :     LWLockAcquire(&pgss->lock.lock, LW_SHARED);
    1318                 :             : 
    1319                 :      105405 :     entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
    1320                 :             : 
    1321                 :             :     /* Create new entry, if not present */
    1322         [ +  + ]:      105405 :     if (!entry)
    1323                 :             :     {
    1324                 :             :         Size        query_offset;
    1325                 :             :         int         gc_count;
    1326                 :             :         bool        stored;
    1327                 :             :         bool        do_gc;
    1328                 :             : 
    1329                 :             :         /*
    1330                 :             :          * Create a new, normalized query string if caller asked.  We don't
    1331                 :             :          * need to hold the lock while doing this work.  (Note: in any case,
    1332                 :             :          * it's possible that someone else creates a duplicate hashtable entry
    1333                 :             :          * in the interval where we don't hold the lock below.  That case is
    1334                 :             :          * handled by entry_alloc.)
    1335                 :             :          */
    1336         [ +  + ]:       31377 :         if (jstate)
    1337                 :             :         {
    1338                 :       11660 :             LWLockRelease(&pgss->lock.lock);
    1339                 :       11660 :             norm_query = generate_normalized_query(jstate, query,
    1340                 :             :                                                    query_location,
    1341                 :             :                                                    &query_len);
    1342                 :       11660 :             LWLockAcquire(&pgss->lock.lock, LW_SHARED);
    1343                 :             :         }
    1344                 :             : 
    1345                 :             :         /* Append new query text to file with only shared lock held */
    1346         [ +  + ]:       31377 :         stored = qtext_store(norm_query ? norm_query : query, query_len,
    1347                 :             :                              &query_offset, &gc_count);
    1348                 :             : 
    1349                 :             :         /*
    1350                 :             :          * Determine whether we need to garbage collect external query texts
    1351                 :             :          * while the shared lock is still held.  This micro-optimization
    1352                 :             :          * avoids taking the time to decide this while holding exclusive lock.
    1353                 :             :          */
    1354                 :       31377 :         do_gc = need_gc_qtexts();
    1355                 :             : 
    1356                 :             :         /* Need exclusive lock to make a new hashtable entry - promote */
    1357                 :       31377 :         LWLockRelease(&pgss->lock.lock);
    1358                 :       31377 :         LWLockAcquire(&pgss->lock.lock, LW_EXCLUSIVE);
    1359                 :             : 
    1360                 :             :         /*
    1361                 :             :          * A garbage collection may have occurred while we weren't holding the
    1362                 :             :          * lock.  In the unlikely event that this happens, the query text we
    1363                 :             :          * stored above will have been garbage collected, so write it again.
    1364                 :             :          * This should be infrequent enough that doing it while holding
    1365                 :             :          * exclusive lock isn't a performance problem.
    1366                 :             :          */
    1367   [ +  -  -  + ]:       31377 :         if (!stored || pgss->gc_count != gc_count)
    1368         [ #  # ]:           0 :             stored = qtext_store(norm_query ? norm_query : query, query_len,
    1369                 :             :                                  &query_offset, NULL);
    1370                 :             : 
    1371                 :             :         /* If we failed to write to the text file, give up */
    1372         [ -  + ]:       31377 :         if (!stored)
    1373                 :           0 :             goto done;
    1374                 :             : 
    1375                 :             :         /* OK to create a new hashtable entry */
    1376                 :       31377 :         entry = entry_alloc(&key, query_offset, query_len, encoding,
    1377                 :             :                             jstate != NULL);
    1378                 :             : 
    1379                 :             :         /* If needed, perform garbage collection while exclusive lock held */
    1380         [ -  + ]:       31377 :         if (do_gc)
    1381                 :           0 :             gc_qtexts();
    1382                 :             :     }
    1383                 :             : 
    1384                 :             :     /* Increment the counts, except when jstate is not NULL */
    1385         [ +  + ]:      105405 :     if (!jstate)
    1386                 :             :     {
    1387                 :             :         Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
    1388                 :             : 
    1389                 :             :         /*
    1390                 :             :          * Grab the spinlock while updating the counters (see comment about
    1391                 :             :          * locking rules at the head of the file)
    1392                 :             :          */
    1393                 :       66051 :         SpinLockAcquire(&entry->mutex);
    1394                 :             : 
    1395                 :             :         /* "Unstick" entry if it was previously sticky */
    1396         [ +  + ]:       66051 :         if (IS_STICKY(entry->counters))
    1397                 :       30544 :             entry->counters.usage = USAGE_INIT;
    1398                 :             : 
    1399                 :       66051 :         entry->counters.calls[kind] += 1;
    1400                 :       66051 :         entry->counters.total_time[kind] += total_time;
    1401                 :             : 
    1402         [ +  + ]:       66051 :         if (entry->counters.calls[kind] == 1)
    1403                 :             :         {
    1404                 :       30638 :             entry->counters.min_time[kind] = total_time;
    1405                 :       30638 :             entry->counters.max_time[kind] = total_time;
    1406                 :       30638 :             entry->counters.mean_time[kind] = total_time;
    1407                 :             :         }
    1408                 :             :         else
    1409                 :             :         {
    1410                 :             :             /*
    1411                 :             :              * Welford's method for accurately computing variance. See
    1412                 :             :              * <http://www.johndcook.com/blog/standard_deviation/>
    1413                 :             :              */
    1414                 :       35413 :             double      old_mean = entry->counters.mean_time[kind];
    1415                 :             : 
    1416                 :       35413 :             entry->counters.mean_time[kind] +=
    1417                 :       35413 :                 (total_time - old_mean) / entry->counters.calls[kind];
    1418                 :       35413 :             entry->counters.sum_var_time[kind] +=
    1419                 :       35413 :                 (total_time - old_mean) * (total_time - entry->counters.mean_time[kind]);
    1420                 :             : 
    1421                 :             :             /*
    1422                 :             :              * Calculate min and max time. min = 0 and max = 0 means that the
    1423                 :             :              * min/max statistics were reset
    1424                 :             :              */
    1425         [ +  + ]:       35413 :             if (entry->counters.min_time[kind] == 0
    1426         [ +  + ]:           6 :                 && entry->counters.max_time[kind] == 0)
    1427                 :             :             {
    1428                 :           3 :                 entry->counters.min_time[kind] = total_time;
    1429                 :           3 :                 entry->counters.max_time[kind] = total_time;
    1430                 :             :             }
    1431                 :             :             else
    1432                 :             :             {
    1433         [ +  + ]:       35410 :                 if (entry->counters.min_time[kind] > total_time)
    1434                 :        6708 :                     entry->counters.min_time[kind] = total_time;
    1435         [ +  + ]:       35410 :                 if (entry->counters.max_time[kind] < total_time)
    1436                 :        3675 :                     entry->counters.max_time[kind] = total_time;
    1437                 :             :             }
    1438                 :             :         }
    1439                 :       66051 :         entry->counters.rows += rows;
    1440                 :       66051 :         entry->counters.shared_blks_hit += bufusage->shared_blks_hit;
    1441                 :       66051 :         entry->counters.shared_blks_read += bufusage->shared_blks_read;
    1442                 :       66051 :         entry->counters.shared_blks_dirtied += bufusage->shared_blks_dirtied;
    1443                 :       66051 :         entry->counters.shared_blks_written += bufusage->shared_blks_written;
    1444                 :       66051 :         entry->counters.local_blks_hit += bufusage->local_blks_hit;
    1445                 :       66051 :         entry->counters.local_blks_read += bufusage->local_blks_read;
    1446                 :       66051 :         entry->counters.local_blks_dirtied += bufusage->local_blks_dirtied;
    1447                 :       66051 :         entry->counters.local_blks_written += bufusage->local_blks_written;
    1448                 :       66051 :         entry->counters.temp_blks_read += bufusage->temp_blks_read;
    1449                 :       66051 :         entry->counters.temp_blks_written += bufusage->temp_blks_written;
    1450                 :       66051 :         entry->counters.shared_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->shared_blk_read_time);
    1451                 :       66051 :         entry->counters.shared_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->shared_blk_write_time);
    1452                 :       66051 :         entry->counters.local_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->local_blk_read_time);
    1453                 :       66051 :         entry->counters.local_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->local_blk_write_time);
    1454                 :       66051 :         entry->counters.temp_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_read_time);
    1455                 :       66051 :         entry->counters.temp_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_write_time);
    1456                 :       66051 :         entry->counters.usage += USAGE_EXEC(total_time);
    1457                 :       66051 :         entry->counters.wal_records += walusage->wal_records;
    1458                 :       66051 :         entry->counters.wal_fpi += walusage->wal_fpi;
    1459                 :       66051 :         entry->counters.wal_bytes += walusage->wal_bytes;
    1460                 :       66051 :         entry->counters.wal_buffers_full += walusage->wal_buffers_full;
    1461         [ -  + ]:       66051 :         if (jitusage)
    1462                 :             :         {
    1463                 :           0 :             entry->counters.jit_functions += jitusage->created_functions;
    1464                 :           0 :             entry->counters.jit_generation_time += INSTR_TIME_GET_MILLISEC(jitusage->generation_counter);
    1465                 :             : 
    1466         [ #  # ]:           0 :             if (INSTR_TIME_GET_MILLISEC(jitusage->deform_counter))
    1467                 :           0 :                 entry->counters.jit_deform_count++;
    1468                 :           0 :             entry->counters.jit_deform_time += INSTR_TIME_GET_MILLISEC(jitusage->deform_counter);
    1469                 :             : 
    1470         [ #  # ]:           0 :             if (INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter))
    1471                 :           0 :                 entry->counters.jit_inlining_count++;
    1472                 :           0 :             entry->counters.jit_inlining_time += INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter);
    1473                 :             : 
    1474         [ #  # ]:           0 :             if (INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter))
    1475                 :           0 :                 entry->counters.jit_optimization_count++;
    1476                 :           0 :             entry->counters.jit_optimization_time += INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter);
    1477                 :             : 
    1478         [ #  # ]:           0 :             if (INSTR_TIME_GET_MILLISEC(jitusage->emission_counter))
    1479                 :           0 :                 entry->counters.jit_emission_count++;
    1480                 :           0 :             entry->counters.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
    1481                 :             :         }
    1482                 :             : 
    1483                 :             :         /* parallel worker counters */
    1484                 :       66051 :         entry->counters.parallel_workers_to_launch += parallel_workers_to_launch;
    1485                 :       66051 :         entry->counters.parallel_workers_launched += parallel_workers_launched;
    1486                 :             : 
    1487                 :             :         /* plan cache counters */
    1488         [ +  + ]:       66051 :         if (planOrigin == PLAN_STMT_CACHE_GENERIC)
    1489                 :        3153 :             entry->counters.generic_plan_calls++;
    1490         [ +  + ]:       62898 :         else if (planOrigin == PLAN_STMT_CACHE_CUSTOM)
    1491                 :         395 :             entry->counters.custom_plan_calls++;
    1492                 :             : 
    1493                 :       66051 :         SpinLockRelease(&entry->mutex);
    1494                 :             :     }
    1495                 :             : 
    1496                 :       39354 : done:
    1497                 :      105405 :     LWLockRelease(&pgss->lock.lock);
    1498                 :             : 
    1499                 :             :     /* We postpone this clean-up until we're out of the lock */
    1500         [ +  + ]:      105405 :     if (norm_query)
    1501                 :       11660 :         pfree(norm_query);
    1502                 :             : }
    1503                 :             : 
    1504                 :             : /*
    1505                 :             :  * Reset statement statistics corresponding to userid, dbid, and queryid.
    1506                 :             :  */
    1507                 :             : Datum
    1508                 :           1 : pg_stat_statements_reset_1_7(PG_FUNCTION_ARGS)
    1509                 :             : {
    1510                 :             :     Oid         userid;
    1511                 :             :     Oid         dbid;
    1512                 :             :     int64       queryid;
    1513                 :             : 
    1514                 :           1 :     userid = PG_GETARG_OID(0);
    1515                 :           1 :     dbid = PG_GETARG_OID(1);
    1516                 :           1 :     queryid = PG_GETARG_INT64(2);
    1517                 :             : 
    1518                 :           1 :     entry_reset(userid, dbid, queryid, false);
    1519                 :             : 
    1520                 :           1 :     PG_RETURN_VOID();
    1521                 :             : }
    1522                 :             : 
    1523                 :             : Datum
    1524                 :         120 : pg_stat_statements_reset_1_11(PG_FUNCTION_ARGS)
    1525                 :             : {
    1526                 :             :     Oid         userid;
    1527                 :             :     Oid         dbid;
    1528                 :             :     int64       queryid;
    1529                 :             :     bool        minmax_only;
    1530                 :             : 
    1531                 :         120 :     userid = PG_GETARG_OID(0);
    1532                 :         120 :     dbid = PG_GETARG_OID(1);
    1533                 :         120 :     queryid = PG_GETARG_INT64(2);
    1534                 :         120 :     minmax_only = PG_GETARG_BOOL(3);
    1535                 :             : 
    1536                 :         120 :     PG_RETURN_TIMESTAMPTZ(entry_reset(userid, dbid, queryid, minmax_only));
    1537                 :             : }
    1538                 :             : 
    1539                 :             : /*
    1540                 :             :  * Reset statement statistics.
    1541                 :             :  */
    1542                 :             : Datum
    1543                 :           1 : pg_stat_statements_reset(PG_FUNCTION_ARGS)
    1544                 :             : {
    1545                 :           1 :     entry_reset(0, 0, 0, false);
    1546                 :             : 
    1547                 :           1 :     PG_RETURN_VOID();
    1548                 :             : }
    1549                 :             : 
    1550                 :             : /* Number of output arguments (columns) for various API versions */
    1551                 :             : #define PG_STAT_STATEMENTS_COLS_V1_0    14
    1552                 :             : #define PG_STAT_STATEMENTS_COLS_V1_1    18
    1553                 :             : #define PG_STAT_STATEMENTS_COLS_V1_2    19
    1554                 :             : #define PG_STAT_STATEMENTS_COLS_V1_3    23
    1555                 :             : #define PG_STAT_STATEMENTS_COLS_V1_8    32
    1556                 :             : #define PG_STAT_STATEMENTS_COLS_V1_9    33
    1557                 :             : #define PG_STAT_STATEMENTS_COLS_V1_10   43
    1558                 :             : #define PG_STAT_STATEMENTS_COLS_V1_11   49
    1559                 :             : #define PG_STAT_STATEMENTS_COLS_V1_12   52
    1560                 :             : #define PG_STAT_STATEMENTS_COLS_V1_13   54
    1561                 :             : #define PG_STAT_STATEMENTS_COLS         54  /* maximum of above */
    1562                 :             : 
    1563                 :             : /*
    1564                 :             :  * Retrieve statement statistics.
    1565                 :             :  *
    1566                 :             :  * The SQL API of this function has changed multiple times, and will likely
    1567                 :             :  * do so again in future.  To support the case where a newer version of this
    1568                 :             :  * loadable module is being used with an old SQL declaration of the function,
    1569                 :             :  * we continue to support the older API versions.  For 1.2 and later, the
    1570                 :             :  * expected API version is identified by embedding it in the C name of the
    1571                 :             :  * function.  Unfortunately we weren't bright enough to do that for 1.1.
    1572                 :             :  */
    1573                 :             : Datum
    1574                 :         130 : pg_stat_statements_1_13(PG_FUNCTION_ARGS)
    1575                 :             : {
    1576                 :         130 :     bool        showtext = PG_GETARG_BOOL(0);
    1577                 :             : 
    1578                 :         130 :     pg_stat_statements_internal(fcinfo, PGSS_V1_13, showtext);
    1579                 :             : 
    1580                 :         130 :     return (Datum) 0;
    1581                 :             : }
    1582                 :             : 
    1583                 :             : Datum
    1584                 :           1 : pg_stat_statements_1_12(PG_FUNCTION_ARGS)
    1585                 :             : {
    1586                 :           1 :     bool        showtext = PG_GETARG_BOOL(0);
    1587                 :             : 
    1588                 :           1 :     pg_stat_statements_internal(fcinfo, PGSS_V1_12, showtext);
    1589                 :             : 
    1590                 :           1 :     return (Datum) 0;
    1591                 :             : }
    1592                 :             : 
    1593                 :             : Datum
    1594                 :           1 : pg_stat_statements_1_11(PG_FUNCTION_ARGS)
    1595                 :             : {
    1596                 :           1 :     bool        showtext = PG_GETARG_BOOL(0);
    1597                 :             : 
    1598                 :           1 :     pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
    1599                 :             : 
    1600                 :           1 :     return (Datum) 0;
    1601                 :             : }
    1602                 :             : 
    1603                 :             : Datum
    1604                 :           1 : pg_stat_statements_1_10(PG_FUNCTION_ARGS)
    1605                 :             : {
    1606                 :           1 :     bool        showtext = PG_GETARG_BOOL(0);
    1607                 :             : 
    1608                 :           1 :     pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
    1609                 :             : 
    1610                 :           1 :     return (Datum) 0;
    1611                 :             : }
    1612                 :             : 
    1613                 :             : Datum
    1614                 :           1 : pg_stat_statements_1_9(PG_FUNCTION_ARGS)
    1615                 :             : {
    1616                 :           1 :     bool        showtext = PG_GETARG_BOOL(0);
    1617                 :             : 
    1618                 :           1 :     pg_stat_statements_internal(fcinfo, PGSS_V1_9, showtext);
    1619                 :             : 
    1620                 :           1 :     return (Datum) 0;
    1621                 :             : }
    1622                 :             : 
    1623                 :             : Datum
    1624                 :           1 : pg_stat_statements_1_8(PG_FUNCTION_ARGS)
    1625                 :             : {
    1626                 :           1 :     bool        showtext = PG_GETARG_BOOL(0);
    1627                 :             : 
    1628                 :           1 :     pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
    1629                 :             : 
    1630                 :           1 :     return (Datum) 0;
    1631                 :             : }
    1632                 :             : 
    1633                 :             : Datum
    1634                 :           1 : pg_stat_statements_1_3(PG_FUNCTION_ARGS)
    1635                 :             : {
    1636                 :           1 :     bool        showtext = PG_GETARG_BOOL(0);
    1637                 :             : 
    1638                 :           1 :     pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
    1639                 :             : 
    1640                 :           1 :     return (Datum) 0;
    1641                 :             : }
    1642                 :             : 
    1643                 :             : Datum
    1644                 :           0 : pg_stat_statements_1_2(PG_FUNCTION_ARGS)
    1645                 :             : {
    1646                 :           0 :     bool        showtext = PG_GETARG_BOOL(0);
    1647                 :             : 
    1648                 :           0 :     pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
    1649                 :             : 
    1650                 :           0 :     return (Datum) 0;
    1651                 :             : }
    1652                 :             : 
    1653                 :             : /*
    1654                 :             :  * Legacy entry point for pg_stat_statements() API versions 1.0 and 1.1.
    1655                 :             :  * This can be removed someday, perhaps.
    1656                 :             :  */
    1657                 :             : Datum
    1658                 :           0 : pg_stat_statements(PG_FUNCTION_ARGS)
    1659                 :             : {
    1660                 :             :     /* If it's really API 1.1, we'll figure that out below */
    1661                 :           0 :     pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
    1662                 :             : 
    1663                 :           0 :     return (Datum) 0;
    1664                 :             : }
    1665                 :             : 
    1666                 :             : /* Common code for all versions of pg_stat_statements() */
    1667                 :             : static void
    1668                 :         136 : pg_stat_statements_internal(FunctionCallInfo fcinfo,
    1669                 :             :                             pgssVersion api_version,
    1670                 :             :                             bool showtext)
    1671                 :             : {
    1672                 :         136 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    1673                 :         136 :     Oid         userid = GetUserId();
    1674                 :         136 :     bool        is_allowed_role = false;
    1675                 :         136 :     char       *qbuffer = NULL;
    1676                 :         136 :     Size        qbuffer_size = 0;
    1677                 :         136 :     Size        extent = 0;
    1678                 :         136 :     int         gc_count = 0;
    1679                 :             :     HASH_SEQ_STATUS hash_seq;
    1680                 :             :     pgssEntry  *entry;
    1681                 :             : 
    1682                 :             :     /*
    1683                 :             :      * Superusers or roles with the privileges of pg_read_all_stats members
    1684                 :             :      * are allowed
    1685                 :             :      */
    1686                 :         136 :     is_allowed_role = has_privs_of_role(userid, ROLE_PG_READ_ALL_STATS);
    1687                 :             : 
    1688                 :             :     /* hash table must exist already */
    1689   [ +  -  -  + ]:         136 :     if (!pgss || !pgss_hash)
    1690         [ #  # ]:           0 :         ereport(ERROR,
    1691                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1692                 :             :                  errmsg("pg_stat_statements must be loaded via \"shared_preload_libraries\"")));
    1693                 :             : 
    1694                 :         136 :     InitMaterializedSRF(fcinfo, 0);
    1695                 :             : 
    1696                 :             :     /*
    1697                 :             :      * Check we have the expected number of output arguments.  Aside from
    1698                 :             :      * being a good safety check, we need a kluge here to detect API version
    1699                 :             :      * 1.1, which was wedged into the code in an ill-considered way.
    1700                 :             :      */
    1701   [ -  -  -  +  :         136 :     switch (rsinfo->setDesc->natts)
          +  +  +  +  +  
                   +  - ]
    1702                 :             :     {
    1703                 :           0 :         case PG_STAT_STATEMENTS_COLS_V1_0:
    1704         [ #  # ]:           0 :             if (api_version != PGSS_V1_0)
    1705         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1706                 :           0 :             break;
    1707                 :           0 :         case PG_STAT_STATEMENTS_COLS_V1_1:
    1708                 :             :             /* pg_stat_statements() should have told us 1.0 */
    1709         [ #  # ]:           0 :             if (api_version != PGSS_V1_0)
    1710         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1711                 :           0 :             api_version = PGSS_V1_1;
    1712                 :           0 :             break;
    1713                 :           0 :         case PG_STAT_STATEMENTS_COLS_V1_2:
    1714         [ #  # ]:           0 :             if (api_version != PGSS_V1_2)
    1715         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1716                 :           0 :             break;
    1717                 :           1 :         case PG_STAT_STATEMENTS_COLS_V1_3:
    1718         [ -  + ]:           1 :             if (api_version != PGSS_V1_3)
    1719         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1720                 :           1 :             break;
    1721                 :           1 :         case PG_STAT_STATEMENTS_COLS_V1_8:
    1722         [ -  + ]:           1 :             if (api_version != PGSS_V1_8)
    1723         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1724                 :           1 :             break;
    1725                 :           1 :         case PG_STAT_STATEMENTS_COLS_V1_9:
    1726         [ -  + ]:           1 :             if (api_version != PGSS_V1_9)
    1727         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1728                 :           1 :             break;
    1729                 :           1 :         case PG_STAT_STATEMENTS_COLS_V1_10:
    1730         [ -  + ]:           1 :             if (api_version != PGSS_V1_10)
    1731         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1732                 :           1 :             break;
    1733                 :           1 :         case PG_STAT_STATEMENTS_COLS_V1_11:
    1734         [ -  + ]:           1 :             if (api_version != PGSS_V1_11)
    1735         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1736                 :           1 :             break;
    1737                 :           1 :         case PG_STAT_STATEMENTS_COLS_V1_12:
    1738         [ -  + ]:           1 :             if (api_version != PGSS_V1_12)
    1739         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1740                 :           1 :             break;
    1741                 :         130 :         case PG_STAT_STATEMENTS_COLS_V1_13:
    1742         [ -  + ]:         130 :             if (api_version != PGSS_V1_13)
    1743         [ #  # ]:           0 :                 elog(ERROR, "incorrect number of output arguments");
    1744                 :         130 :             break;
    1745                 :           0 :         default:
    1746         [ #  # ]:           0 :             elog(ERROR, "incorrect number of output arguments");
    1747                 :             :     }
    1748                 :             : 
    1749                 :             :     /*
    1750                 :             :      * We'd like to load the query text file (if needed) while not holding any
    1751                 :             :      * lock on pgss->lock.  In the worst case we'll have to do this again
    1752                 :             :      * after we have the lock, but it's unlikely enough to make this a win
    1753                 :             :      * despite occasional duplicated work.  We need to reload if anybody
    1754                 :             :      * writes to the file (either a retail qtext_store(), or a garbage
    1755                 :             :      * collection) between this point and where we've gotten shared lock.  If
    1756                 :             :      * a qtext_store is actually in progress when we look, we might as well
    1757                 :             :      * skip the speculative load entirely.
    1758                 :             :      */
    1759         [ +  - ]:         136 :     if (showtext)
    1760                 :             :     {
    1761                 :             :         int         n_writers;
    1762                 :             : 
    1763                 :             :         /* Take the mutex so we can examine variables */
    1764                 :         136 :         SpinLockAcquire(&pgss->mutex);
    1765                 :         136 :         extent = pgss->extent;
    1766                 :         136 :         n_writers = pgss->n_writers;
    1767                 :         136 :         gc_count = pgss->gc_count;
    1768                 :         136 :         SpinLockRelease(&pgss->mutex);
    1769                 :             : 
    1770                 :             :         /* No point in loading file now if there are active writers */
    1771         [ +  - ]:         136 :         if (n_writers == 0)
    1772                 :         136 :             qbuffer = qtext_load_file(&qbuffer_size);
    1773                 :             :     }
    1774                 :             : 
    1775                 :             :     /*
    1776                 :             :      * Get shared lock, load or reload the query text file if we must, and
    1777                 :             :      * iterate over the hashtable entries.
    1778                 :             :      *
    1779                 :             :      * With a large hash table, we might be holding the lock rather longer
    1780                 :             :      * than one could wish.  However, this only blocks creation of new hash
    1781                 :             :      * table entries, and the larger the hash table the less likely that is to
    1782                 :             :      * be needed.  So we can hope this is okay.  Perhaps someday we'll decide
    1783                 :             :      * we need to partition the hash table to limit the time spent holding any
    1784                 :             :      * one lock.
    1785                 :             :      */
    1786                 :         136 :     LWLockAcquire(&pgss->lock.lock, LW_SHARED);
    1787                 :             : 
    1788         [ +  - ]:         136 :     if (showtext)
    1789                 :             :     {
    1790                 :             :         /*
    1791                 :             :          * Here it is safe to examine extent and gc_count without taking the
    1792                 :             :          * mutex.  Note that although other processes might change
    1793                 :             :          * pgss->extent just after we look at it, the strings they then write
    1794                 :             :          * into the file cannot yet be referenced in the hashtable, so we
    1795                 :             :          * don't care whether we see them or not.
    1796                 :             :          *
    1797                 :             :          * If qtext_load_file fails, we just press on; we'll return NULL for
    1798                 :             :          * every query text.
    1799                 :             :          */
    1800         [ +  - ]:         136 :         if (qbuffer == NULL ||
    1801         [ +  - ]:         136 :             pgss->extent != extent ||
    1802         [ -  + ]:         136 :             pgss->gc_count != gc_count)
    1803                 :             :         {
    1804         [ #  # ]:           0 :             if (qbuffer)
    1805                 :           0 :                 pfree(qbuffer);
    1806                 :           0 :             qbuffer = qtext_load_file(&qbuffer_size);
    1807                 :             :         }
    1808                 :             :     }
    1809                 :             : 
    1810                 :         136 :     hash_seq_init(&hash_seq, pgss_hash);
    1811         [ +  + ]:       29725 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    1812                 :             :     {
    1813                 :             :         Datum       values[PG_STAT_STATEMENTS_COLS];
    1814                 :             :         bool        nulls[PG_STAT_STATEMENTS_COLS];
    1815                 :       29589 :         int         i = 0;
    1816                 :             :         Counters    tmp;
    1817                 :             :         double      stddev;
    1818                 :       29589 :         int64       queryid = entry->key.queryid;
    1819                 :             :         TimestampTz stats_since;
    1820                 :             :         TimestampTz minmax_stats_since;
    1821                 :             : 
    1822                 :       29589 :         memset(values, 0, sizeof(values));
    1823                 :       29589 :         memset(nulls, 0, sizeof(nulls));
    1824                 :             : 
    1825                 :       29589 :         values[i++] = ObjectIdGetDatum(entry->key.userid);
    1826                 :       29589 :         values[i++] = ObjectIdGetDatum(entry->key.dbid);
    1827         [ +  + ]:       29589 :         if (api_version >= PGSS_V1_9)
    1828                 :       29577 :             values[i++] = BoolGetDatum(entry->key.toplevel);
    1829                 :             : 
    1830   [ +  +  +  + ]:       29589 :         if (is_allowed_role || entry->key.userid == userid)
    1831                 :             :         {
    1832         [ +  - ]:       29585 :             if (api_version >= PGSS_V1_2)
    1833                 :       29585 :                 values[i++] = Int64GetDatumFast(queryid);
    1834                 :             : 
    1835         [ +  - ]:       29585 :             if (showtext)
    1836                 :             :             {
    1837                 :       29585 :                 char       *qstr = qtext_fetch(entry->query_offset,
    1838                 :             :                                                entry->query_len,
    1839                 :             :                                                qbuffer,
    1840                 :             :                                                qbuffer_size);
    1841                 :             : 
    1842         [ +  - ]:       29585 :                 if (qstr)
    1843                 :             :                 {
    1844                 :             :                     char       *enc;
    1845                 :             : 
    1846                 :       29585 :                     enc = pg_any_to_server(qstr,
    1847                 :             :                                            entry->query_len,
    1848                 :             :                                            entry->encoding);
    1849                 :             : 
    1850                 :       29585 :                     values[i++] = CStringGetTextDatum(enc);
    1851                 :             : 
    1852         [ -  + ]:       29585 :                     if (enc != qstr)
    1853                 :           0 :                         pfree(enc);
    1854                 :             :                 }
    1855                 :             :                 else
    1856                 :             :                 {
    1857                 :             :                     /* Just return a null if we fail to find the text */
    1858                 :           0 :                     nulls[i++] = true;
    1859                 :             :                 }
    1860                 :             :             }
    1861                 :             :             else
    1862                 :             :             {
    1863                 :             :                 /* Query text not requested */
    1864                 :           0 :                 nulls[i++] = true;
    1865                 :             :             }
    1866                 :             :         }
    1867                 :             :         else
    1868                 :             :         {
    1869                 :             :             /* Don't show queryid */
    1870         [ +  - ]:           4 :             if (api_version >= PGSS_V1_2)
    1871                 :           4 :                 nulls[i++] = true;
    1872                 :             : 
    1873                 :             :             /*
    1874                 :             :              * Don't show query text, but hint as to the reason for not doing
    1875                 :             :              * so if it was requested
    1876                 :             :              */
    1877         [ +  - ]:           4 :             if (showtext)
    1878                 :           4 :                 values[i++] = CStringGetTextDatum("<insufficient privilege>");
    1879                 :             :             else
    1880                 :           0 :                 nulls[i++] = true;
    1881                 :             :         }
    1882                 :             : 
    1883                 :             :         /* copy counters to a local variable to keep locking time short */
    1884                 :       29589 :         SpinLockAcquire(&entry->mutex);
    1885                 :       29589 :         tmp = entry->counters;
    1886                 :       29589 :         SpinLockRelease(&entry->mutex);
    1887                 :             : 
    1888                 :             :         /*
    1889                 :             :          * The spinlock is not required when reading these two as they are
    1890                 :             :          * always updated when holding pgss->lock exclusively.
    1891                 :             :          */
    1892                 :       29589 :         stats_since = entry->stats_since;
    1893                 :       29589 :         minmax_stats_since = entry->minmax_stats_since;
    1894                 :             : 
    1895                 :             :         /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */
    1896         [ +  + ]:       29589 :         if (IS_STICKY(tmp))
    1897                 :          46 :             continue;
    1898                 :             : 
    1899                 :             :         /* Note that we rely on PGSS_PLAN being 0 and PGSS_EXEC being 1. */
    1900         [ +  + ]:       88629 :         for (int kind = 0; kind < PGSS_NUMKIND; kind++)
    1901                 :             :         {
    1902   [ +  +  +  + ]:       59086 :             if (kind == PGSS_EXEC || api_version >= PGSS_V1_8)
    1903                 :             :             {
    1904                 :       59082 :                 values[i++] = Int64GetDatumFast(tmp.calls[kind]);
    1905                 :       59082 :                 values[i++] = Float8GetDatumFast(tmp.total_time[kind]);
    1906                 :             :             }
    1907                 :             : 
    1908   [ +  +  -  +  :       59086 :             if ((kind == PGSS_EXEC && api_version >= PGSS_V1_3) ||
                   +  + ]
    1909                 :             :                 api_version >= PGSS_V1_8)
    1910                 :             :             {
    1911                 :       59082 :                 values[i++] = Float8GetDatumFast(tmp.min_time[kind]);
    1912                 :       59082 :                 values[i++] = Float8GetDatumFast(tmp.max_time[kind]);
    1913                 :       59082 :                 values[i++] = Float8GetDatumFast(tmp.mean_time[kind]);
    1914                 :             : 
    1915                 :             :                 /*
    1916                 :             :                  * Note we are calculating the population variance here, not
    1917                 :             :                  * the sample variance, as we have data for the whole
    1918                 :             :                  * population, so Bessel's correction is not used, and we
    1919                 :             :                  * don't divide by tmp.calls - 1.
    1920                 :             :                  */
    1921         [ +  + ]:       59082 :                 if (tmp.calls[kind] > 1)
    1922                 :        5494 :                     stddev = sqrt(tmp.sum_var_time[kind] / tmp.calls[kind]);
    1923                 :             :                 else
    1924                 :       53588 :                     stddev = 0.0;
    1925                 :       59082 :                 values[i++] = Float8GetDatumFast(stddev);
    1926                 :             :             }
    1927                 :             :         }
    1928                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.rows);
    1929                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
    1930                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
    1931         [ +  - ]:       29543 :         if (api_version >= PGSS_V1_1)
    1932                 :       29543 :             values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied);
    1933                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.shared_blks_written);
    1934                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.local_blks_hit);
    1935                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.local_blks_read);
    1936         [ +  - ]:       29543 :         if (api_version >= PGSS_V1_1)
    1937                 :       29543 :             values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied);
    1938                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.local_blks_written);
    1939                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.temp_blks_read);
    1940                 :       29543 :         values[i++] = Int64GetDatumFast(tmp.temp_blks_written);
    1941         [ +  - ]:       29543 :         if (api_version >= PGSS_V1_1)
    1942                 :             :         {
    1943                 :       29543 :             values[i++] = Float8GetDatumFast(tmp.shared_blk_read_time);
    1944                 :       29543 :             values[i++] = Float8GetDatumFast(tmp.shared_blk_write_time);
    1945                 :             :         }
    1946         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_11)
    1947                 :             :         {
    1948                 :       29515 :             values[i++] = Float8GetDatumFast(tmp.local_blk_read_time);
    1949                 :       29515 :             values[i++] = Float8GetDatumFast(tmp.local_blk_write_time);
    1950                 :             :         }
    1951         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_10)
    1952                 :             :         {
    1953                 :       29524 :             values[i++] = Float8GetDatumFast(tmp.temp_blk_read_time);
    1954                 :       29524 :             values[i++] = Float8GetDatumFast(tmp.temp_blk_write_time);
    1955                 :             :         }
    1956         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_8)
    1957                 :             :         {
    1958                 :             :             char        buf[256];
    1959                 :             :             Datum       wal_bytes;
    1960                 :             : 
    1961                 :       29539 :             values[i++] = Int64GetDatumFast(tmp.wal_records);
    1962                 :       29539 :             values[i++] = Int64GetDatumFast(tmp.wal_fpi);
    1963                 :             : 
    1964                 :       29539 :             snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.wal_bytes);
    1965                 :             : 
    1966                 :             :             /* Convert to numeric. */
    1967                 :       29539 :             wal_bytes = DirectFunctionCall3(numeric_in,
    1968                 :             :                                             CStringGetDatum(buf),
    1969                 :             :                                             ObjectIdGetDatum(0),
    1970                 :             :                                             Int32GetDatum(-1));
    1971                 :       29539 :             values[i++] = wal_bytes;
    1972                 :             :         }
    1973         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_12)
    1974                 :             :         {
    1975                 :       29505 :             values[i++] = Int64GetDatumFast(tmp.wal_buffers_full);
    1976                 :             :         }
    1977         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_10)
    1978                 :             :         {
    1979                 :       29524 :             values[i++] = Int64GetDatumFast(tmp.jit_functions);
    1980                 :       29524 :             values[i++] = Float8GetDatumFast(tmp.jit_generation_time);
    1981                 :       29524 :             values[i++] = Int64GetDatumFast(tmp.jit_inlining_count);
    1982                 :       29524 :             values[i++] = Float8GetDatumFast(tmp.jit_inlining_time);
    1983                 :       29524 :             values[i++] = Int64GetDatumFast(tmp.jit_optimization_count);
    1984                 :       29524 :             values[i++] = Float8GetDatumFast(tmp.jit_optimization_time);
    1985                 :       29524 :             values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
    1986                 :       29524 :             values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
    1987                 :             :         }
    1988         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_11)
    1989                 :             :         {
    1990                 :       29515 :             values[i++] = Int64GetDatumFast(tmp.jit_deform_count);
    1991                 :       29515 :             values[i++] = Float8GetDatumFast(tmp.jit_deform_time);
    1992                 :             :         }
    1993         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_12)
    1994                 :             :         {
    1995                 :       29505 :             values[i++] = Int64GetDatumFast(tmp.parallel_workers_to_launch);
    1996                 :       29505 :             values[i++] = Int64GetDatumFast(tmp.parallel_workers_launched);
    1997                 :             :         }
    1998         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_13)
    1999                 :             :         {
    2000                 :       29500 :             values[i++] = Int64GetDatumFast(tmp.generic_plan_calls);
    2001                 :       29500 :             values[i++] = Int64GetDatumFast(tmp.custom_plan_calls);
    2002                 :             :         }
    2003         [ +  + ]:       29543 :         if (api_version >= PGSS_V1_11)
    2004                 :             :         {
    2005                 :       29515 :             values[i++] = TimestampTzGetDatum(stats_since);
    2006                 :       29515 :             values[i++] = TimestampTzGetDatum(minmax_stats_since);
    2007                 :             :         }
    2008                 :             : 
    2009                 :             :         Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
    2010                 :             :                      api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
    2011                 :             :                      api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
    2012                 :             :                      api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
    2013                 :             :                      api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
    2014                 :             :                      api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
    2015                 :             :                      api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
    2016                 :             :                      api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
    2017                 :             :                      api_version == PGSS_V1_12 ? PG_STAT_STATEMENTS_COLS_V1_12 :
    2018                 :             :                      api_version == PGSS_V1_13 ? PG_STAT_STATEMENTS_COLS_V1_13 :
    2019                 :             :                      -1 /* fail if you forget to update this assert */ ));
    2020                 :             : 
    2021                 :       29543 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
    2022                 :             :     }
    2023                 :             : 
    2024                 :         136 :     LWLockRelease(&pgss->lock.lock);
    2025                 :             : 
    2026         [ +  - ]:         136 :     if (qbuffer)
    2027                 :         136 :         pfree(qbuffer);
    2028                 :         136 : }
    2029                 :             : 
    2030                 :             : /* Number of output arguments (columns) for pg_stat_statements_info */
    2031                 :             : #define PG_STAT_STATEMENTS_INFO_COLS    2
    2032                 :             : 
    2033                 :             : /*
    2034                 :             :  * Return statistics of pg_stat_statements.
    2035                 :             :  */
    2036                 :             : Datum
    2037                 :           4 : pg_stat_statements_info(PG_FUNCTION_ARGS)
    2038                 :             : {
    2039                 :             :     pgssGlobalStats stats;
    2040                 :             :     TupleDesc   tupdesc;
    2041                 :           4 :     Datum       values[PG_STAT_STATEMENTS_INFO_COLS] = {0};
    2042                 :           4 :     bool        nulls[PG_STAT_STATEMENTS_INFO_COLS] = {0};
    2043                 :             : 
    2044   [ +  -  -  + ]:           4 :     if (!pgss || !pgss_hash)
    2045         [ #  # ]:           0 :         ereport(ERROR,
    2046                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    2047                 :             :                  errmsg("pg_stat_statements must be loaded via \"shared_preload_libraries\"")));
    2048                 :             : 
    2049                 :             :     /* Build a tuple descriptor for our result type */
    2050         [ -  + ]:           4 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
    2051         [ #  # ]:           0 :         elog(ERROR, "return type must be a row type");
    2052                 :             : 
    2053                 :             :     /* Read global statistics for pg_stat_statements */
    2054                 :           4 :     SpinLockAcquire(&pgss->mutex);
    2055                 :           4 :     stats = pgss->stats;
    2056                 :           4 :     SpinLockRelease(&pgss->mutex);
    2057                 :             : 
    2058                 :           4 :     values[0] = Int64GetDatum(stats.dealloc);
    2059                 :           4 :     values[1] = TimestampTzGetDatum(stats.stats_reset);
    2060                 :             : 
    2061                 :           4 :     PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
    2062                 :             : }
    2063                 :             : 
    2064                 :             : /*
    2065                 :             :  * Allocate a new hashtable entry.
    2066                 :             :  * caller must hold an exclusive lock on pgss->lock
    2067                 :             :  *
    2068                 :             :  * "query" need not be null-terminated; we rely on query_len instead
    2069                 :             :  *
    2070                 :             :  * If "sticky" is true, make the new entry artificially sticky so that it will
    2071                 :             :  * probably still be there when the query finishes execution.  We do this by
    2072                 :             :  * giving it a median usage value rather than the normal value.  (Strictly
    2073                 :             :  * speaking, query strings are normalized on a best effort basis, though it
    2074                 :             :  * would be difficult to demonstrate this even under artificial conditions.)
    2075                 :             :  *
    2076                 :             :  * Note: despite needing exclusive lock, it's not an error for the target
    2077                 :             :  * entry to already exist.  This is because pgss_store releases and
    2078                 :             :  * reacquires lock after failing to find a match; so someone else could
    2079                 :             :  * have made the entry while we waited to get exclusive lock.
    2080                 :             :  */
    2081                 :             : static pgssEntry *
    2082                 :       59944 : entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding,
    2083                 :             :             bool sticky)
    2084                 :             : {
    2085                 :             :     pgssEntry  *entry;
    2086                 :             :     bool        found;
    2087                 :             : 
    2088                 :             :     /* Make space if needed */
    2089         [ -  + ]:       59944 :     while (hash_get_num_entries(pgss_hash) >= pgss_max)
    2090                 :           0 :         entry_dealloc();
    2091                 :             : 
    2092                 :             :     /* Find or create an entry with desired hash code */
    2093                 :       59944 :     entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
    2094                 :             : 
    2095         [ +  - ]:       59944 :     if (!found)
    2096                 :             :     {
    2097                 :             :         /* New entry, initialize it */
    2098                 :             : 
    2099                 :             :         /* reset the statistics */
    2100                 :       59944 :         memset(&entry->counters, 0, sizeof(Counters));
    2101                 :             :         /* set the appropriate initial usage count */
    2102         [ +  + ]:       59944 :         entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
    2103                 :             :         /* re-initialize the mutex each time ... we assume no one using it */
    2104                 :       59944 :         SpinLockInit(&entry->mutex);
    2105                 :             :         /* ... and don't forget the query text metadata */
    2106                 :             :         Assert(query_len >= 0);
    2107                 :       59944 :         entry->query_offset = query_offset;
    2108                 :       59944 :         entry->query_len = query_len;
    2109                 :       59944 :         entry->encoding = encoding;
    2110                 :       59944 :         entry->stats_since = GetCurrentTimestamp();
    2111                 :       59944 :         entry->minmax_stats_since = entry->stats_since;
    2112                 :             :     }
    2113                 :             : 
    2114                 :       59944 :     return entry;
    2115                 :             : }
    2116                 :             : 
    2117                 :             : /*
    2118                 :             :  * qsort comparator for sorting into increasing usage order
    2119                 :             :  */
    2120                 :             : static int
    2121                 :           0 : entry_cmp(const void *lhs, const void *rhs)
    2122                 :             : {
    2123                 :           0 :     double      l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
    2124                 :           0 :     double      r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
    2125                 :             : 
    2126         [ #  # ]:           0 :     if (l_usage < r_usage)
    2127                 :           0 :         return -1;
    2128         [ #  # ]:           0 :     else if (l_usage > r_usage)
    2129                 :           0 :         return +1;
    2130                 :             :     else
    2131                 :           0 :         return 0;
    2132                 :             : }
    2133                 :             : 
    2134                 :             : /*
    2135                 :             :  * Deallocate least-used entries.
    2136                 :             :  *
    2137                 :             :  * Caller must hold an exclusive lock on pgss->lock.
    2138                 :             :  */
    2139                 :             : static void
    2140                 :           0 : entry_dealloc(void)
    2141                 :             : {
    2142                 :             :     HASH_SEQ_STATUS hash_seq;
    2143                 :             :     pgssEntry **entries;
    2144                 :             :     pgssEntry  *entry;
    2145                 :             :     int         nvictims;
    2146                 :             :     int         i;
    2147                 :             :     Size        tottextlen;
    2148                 :             :     int         nvalidtexts;
    2149                 :             : 
    2150                 :             :     /*
    2151                 :             :      * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
    2152                 :             :      * While we're scanning the table, apply the decay factor to the usage
    2153                 :             :      * values, and update the mean query length.
    2154                 :             :      *
    2155                 :             :      * Note that the mean query length is almost immediately obsolete, since
    2156                 :             :      * we compute it before not after discarding the least-used entries.
    2157                 :             :      * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
    2158                 :             :      * making two passes to get a more current result.  Likewise, the new
    2159                 :             :      * cur_median_usage includes the entries we're about to zap.
    2160                 :             :      */
    2161                 :             : 
    2162                 :           0 :     entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
    2163                 :             : 
    2164                 :           0 :     i = 0;
    2165                 :           0 :     tottextlen = 0;
    2166                 :           0 :     nvalidtexts = 0;
    2167                 :             : 
    2168                 :           0 :     hash_seq_init(&hash_seq, pgss_hash);
    2169         [ #  # ]:           0 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2170                 :             :     {
    2171                 :           0 :         entries[i++] = entry;
    2172                 :             :         /* "Sticky" entries get a different usage decay rate. */
    2173         [ #  # ]:           0 :         if (IS_STICKY(entry->counters))
    2174                 :           0 :             entry->counters.usage *= STICKY_DECREASE_FACTOR;
    2175                 :             :         else
    2176                 :           0 :             entry->counters.usage *= USAGE_DECREASE_FACTOR;
    2177                 :             :         /* In the mean length computation, ignore dropped texts. */
    2178         [ #  # ]:           0 :         if (entry->query_len >= 0)
    2179                 :             :         {
    2180                 :           0 :             tottextlen += entry->query_len + 1;
    2181                 :           0 :             nvalidtexts++;
    2182                 :             :         }
    2183                 :             :     }
    2184                 :             : 
    2185                 :             :     /* Sort into increasing order by usage */
    2186                 :           0 :     qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
    2187                 :             : 
    2188                 :             :     /* Record the (approximate) median usage */
    2189         [ #  # ]:           0 :     if (i > 0)
    2190                 :           0 :         pgss->cur_median_usage = entries[i / 2]->counters.usage;
    2191                 :             :     /* Record the mean query length */
    2192         [ #  # ]:           0 :     if (nvalidtexts > 0)
    2193                 :           0 :         pgss->mean_query_len = tottextlen / nvalidtexts;
    2194                 :             :     else
    2195                 :           0 :         pgss->mean_query_len = ASSUMED_LENGTH_INIT;
    2196                 :             : 
    2197                 :             :     /* Now zap an appropriate fraction of lowest-usage entries */
    2198         [ #  # ]:           0 :     nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
    2199                 :           0 :     nvictims = Min(nvictims, i);
    2200                 :             : 
    2201         [ #  # ]:           0 :     for (i = 0; i < nvictims; i++)
    2202                 :             :     {
    2203                 :           0 :         hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
    2204                 :             :     }
    2205                 :             : 
    2206                 :           0 :     pfree(entries);
    2207                 :             : 
    2208                 :             :     /* Increment the number of times entries are deallocated */
    2209                 :           0 :     SpinLockAcquire(&pgss->mutex);
    2210                 :           0 :     pgss->stats.dealloc += 1;
    2211                 :           0 :     SpinLockRelease(&pgss->mutex);
    2212                 :           0 : }
    2213                 :             : 
    2214                 :             : /*
    2215                 :             :  * Given a query string (not necessarily null-terminated), allocate a new
    2216                 :             :  * entry in the external query text file and store the string there.
    2217                 :             :  *
    2218                 :             :  * If successful, returns true, and stores the new entry's offset in the file
    2219                 :             :  * into *query_offset.  Also, if gc_count isn't NULL, *gc_count is set to the
    2220                 :             :  * number of garbage collections that have occurred so far.
    2221                 :             :  *
    2222                 :             :  * On failure, returns false.
    2223                 :             :  *
    2224                 :             :  * At least a shared lock on pgss->lock must be held by the caller, so as
    2225                 :             :  * to prevent a concurrent garbage collection.  Share-lock-holding callers
    2226                 :             :  * should pass a gc_count pointer to obtain the number of garbage collections,
    2227                 :             :  * so that they can recheck the count after obtaining exclusive lock to
    2228                 :             :  * detect whether a garbage collection occurred (and removed this entry).
    2229                 :             :  */
    2230                 :             : static bool
    2231                 :       31377 : qtext_store(const char *query, int query_len,
    2232                 :             :             Size *query_offset, int *gc_count)
    2233                 :             : {
    2234                 :             :     Size        off;
    2235                 :             :     int         fd;
    2236                 :             : 
    2237                 :             :     /*
    2238                 :             :      * We use a spinlock to protect extent/n_writers/gc_count, so that
    2239                 :             :      * multiple processes may execute this function concurrently.
    2240                 :             :      */
    2241                 :       31377 :     SpinLockAcquire(&pgss->mutex);
    2242                 :       31377 :     off = pgss->extent;
    2243                 :       31377 :     pgss->extent += query_len + 1;
    2244                 :       31377 :     pgss->n_writers++;
    2245         [ +  - ]:       31377 :     if (gc_count)
    2246                 :       31377 :         *gc_count = pgss->gc_count;
    2247                 :       31377 :     SpinLockRelease(&pgss->mutex);
    2248                 :             : 
    2249                 :       31377 :     *query_offset = off;
    2250                 :             : 
    2251                 :             :     /*
    2252                 :             :      * Don't allow the file to grow larger than what qtext_load_file can
    2253                 :             :      * (theoretically) handle.  This has been seen to be reachable on 32-bit
    2254                 :             :      * platforms.
    2255                 :             :      */
    2256         [ -  + ]:       31377 :     if (unlikely(query_len >= MaxAllocHugeSize - off))
    2257                 :             :     {
    2258                 :           0 :         errno = EFBIG;          /* not quite right, but it'll do */
    2259                 :           0 :         fd = -1;
    2260                 :           0 :         goto error;
    2261                 :             :     }
    2262                 :             : 
    2263                 :             :     /* Now write the data into the successfully-reserved part of the file */
    2264                 :       31377 :     fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDWR | O_CREAT | PG_BINARY);
    2265         [ -  + ]:       31377 :     if (fd < 0)
    2266                 :           0 :         goto error;
    2267                 :             : 
    2268         [ -  + ]:       31377 :     if (pg_pwrite(fd, query, query_len, off) != query_len)
    2269                 :           0 :         goto error;
    2270         [ -  + ]:       31377 :     if (pg_pwrite(fd, "\0", 1, off + query_len) != 1)
    2271                 :           0 :         goto error;
    2272                 :             : 
    2273                 :       31377 :     CloseTransientFile(fd);
    2274                 :             : 
    2275                 :             :     /* Mark our write complete */
    2276                 :       31377 :     SpinLockAcquire(&pgss->mutex);
    2277                 :       31377 :     pgss->n_writers--;
    2278                 :       31377 :     SpinLockRelease(&pgss->mutex);
    2279                 :             : 
    2280                 :       31377 :     return true;
    2281                 :             : 
    2282                 :           0 : error:
    2283         [ #  # ]:           0 :     ereport(LOG,
    2284                 :             :             (errcode_for_file_access(),
    2285                 :             :              errmsg("could not write file \"%s\": %m",
    2286                 :             :                     PGSS_TEXT_FILE)));
    2287                 :             : 
    2288         [ #  # ]:           0 :     if (fd >= 0)
    2289                 :           0 :         CloseTransientFile(fd);
    2290                 :             : 
    2291                 :             :     /* Mark our write complete */
    2292                 :           0 :     SpinLockAcquire(&pgss->mutex);
    2293                 :           0 :     pgss->n_writers--;
    2294                 :           0 :     SpinLockRelease(&pgss->mutex);
    2295                 :             : 
    2296                 :           0 :     return false;
    2297                 :             : }
    2298                 :             : 
    2299                 :             : /*
    2300                 :             :  * Read the external query text file into a palloc'd buffer.
    2301                 :             :  *
    2302                 :             :  * Returns NULL (without throwing an error) if unable to read, eg
    2303                 :             :  * file not there or insufficient memory.
    2304                 :             :  *
    2305                 :             :  * On success, the buffer size is also returned into *buffer_size.
    2306                 :             :  *
    2307                 :             :  * This can be called without any lock on pgss->lock, but in that case
    2308                 :             :  * the caller is responsible for verifying that the result is sane.
    2309                 :             :  */
    2310                 :             : static char *
    2311                 :         143 : qtext_load_file(Size *buffer_size)
    2312                 :             : {
    2313                 :             :     char       *buf;
    2314                 :             :     int         fd;
    2315                 :             :     struct stat stat;
    2316                 :             :     Size        nread;
    2317                 :             : 
    2318                 :         143 :     fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
    2319         [ -  + ]:         143 :     if (fd < 0)
    2320                 :             :     {
    2321         [ #  # ]:           0 :         if (errno != ENOENT)
    2322         [ #  # ]:           0 :             ereport(LOG,
    2323                 :             :                     (errcode_for_file_access(),
    2324                 :             :                      errmsg("could not read file \"%s\": %m",
    2325                 :             :                             PGSS_TEXT_FILE)));
    2326                 :           0 :         return NULL;
    2327                 :             :     }
    2328                 :             : 
    2329                 :             :     /* Get file length */
    2330         [ -  + ]:         143 :     if (fstat(fd, &stat))
    2331                 :             :     {
    2332         [ #  # ]:           0 :         ereport(LOG,
    2333                 :             :                 (errcode_for_file_access(),
    2334                 :             :                  errmsg("could not stat file \"%s\": %m",
    2335                 :             :                         PGSS_TEXT_FILE)));
    2336                 :           0 :         CloseTransientFile(fd);
    2337                 :           0 :         return NULL;
    2338                 :             :     }
    2339                 :             : 
    2340                 :             :     /* Allocate buffer; beware that off_t might be wider than size_t */
    2341         [ +  - ]:         143 :     if (stat.st_size <= MaxAllocHugeSize)
    2342                 :         143 :         buf = (char *) palloc_extended(stat.st_size, MCXT_ALLOC_HUGE | MCXT_ALLOC_NO_OOM);
    2343                 :             :     else
    2344                 :           0 :         buf = NULL;
    2345         [ -  + ]:         143 :     if (buf == NULL)
    2346                 :             :     {
    2347         [ #  # ]:           0 :         ereport(LOG,
    2348                 :             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
    2349                 :             :                  errmsg("out of memory"),
    2350                 :             :                  errdetail("Could not allocate enough memory to read file \"%s\".",
    2351                 :             :                            PGSS_TEXT_FILE)));
    2352                 :           0 :         CloseTransientFile(fd);
    2353                 :           0 :         return NULL;
    2354                 :             :     }
    2355                 :             : 
    2356                 :             :     /*
    2357                 :             :      * OK, slurp in the file.  Windows fails if we try to read more than
    2358                 :             :      * INT_MAX bytes at once, and other platforms might not like that either,
    2359                 :             :      * so read a very large file in 1GB segments.
    2360                 :             :      */
    2361                 :         143 :     nread = 0;
    2362         [ +  + ]:         285 :     while (nread < stat.st_size)
    2363                 :             :     {
    2364                 :         142 :         int         toread = Min(1024 * 1024 * 1024, stat.st_size - nread);
    2365                 :             : 
    2366                 :             :         /*
    2367                 :             :          * If we get a short read and errno doesn't get set, the reason is
    2368                 :             :          * probably that garbage collection truncated the file since we did
    2369                 :             :          * the fstat(), so we don't log a complaint --- but we don't return
    2370                 :             :          * the data, either, since it's most likely corrupt due to concurrent
    2371                 :             :          * writes from garbage collection.
    2372                 :             :          */
    2373                 :         142 :         errno = 0;
    2374         [ -  + ]:         142 :         if (read(fd, buf + nread, toread) != toread)
    2375                 :             :         {
    2376         [ #  # ]:           0 :             if (errno)
    2377         [ #  # ]:           0 :                 ereport(LOG,
    2378                 :             :                         (errcode_for_file_access(),
    2379                 :             :                          errmsg("could not read file \"%s\": %m",
    2380                 :             :                                 PGSS_TEXT_FILE)));
    2381                 :           0 :             pfree(buf);
    2382                 :           0 :             CloseTransientFile(fd);
    2383                 :           0 :             return NULL;
    2384                 :             :         }
    2385                 :         142 :         nread += toread;
    2386                 :             :     }
    2387                 :             : 
    2388         [ -  + ]:         143 :     if (CloseTransientFile(fd) != 0)
    2389         [ #  # ]:           0 :         ereport(LOG,
    2390                 :             :                 (errcode_for_file_access(),
    2391                 :             :                  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
    2392                 :             : 
    2393                 :         143 :     *buffer_size = nread;
    2394                 :         143 :     return buf;
    2395                 :             : }
    2396                 :             : 
    2397                 :             : /*
    2398                 :             :  * Locate a query text in the file image previously read by qtext_load_file().
    2399                 :             :  *
    2400                 :             :  * We validate the given offset/length, and return NULL if bogus.  Otherwise,
    2401                 :             :  * the result points to a null-terminated string within the buffer.
    2402                 :             :  */
    2403                 :             : static char *
    2404                 :       88600 : qtext_fetch(Size query_offset, int query_len,
    2405                 :             :             char *buffer, Size buffer_size)
    2406                 :             : {
    2407                 :             :     /* File read failed? */
    2408         [ -  + ]:       88600 :     if (buffer == NULL)
    2409                 :           0 :         return NULL;
    2410                 :             :     /* Bogus offset/length? */
    2411         [ +  - ]:       88600 :     if (query_len < 0 ||
    2412         [ -  + ]:       88600 :         query_offset + query_len >= buffer_size)
    2413                 :           0 :         return NULL;
    2414                 :             :     /* As a further sanity check, make sure there's a trailing null */
    2415         [ -  + ]:       88600 :     if (buffer[query_offset + query_len] != '\0')
    2416                 :           0 :         return NULL;
    2417                 :             :     /* Looks OK */
    2418                 :       88600 :     return buffer + query_offset;
    2419                 :             : }
    2420                 :             : 
    2421                 :             : /*
    2422                 :             :  * Do we need to garbage-collect the external query text file?
    2423                 :             :  *
    2424                 :             :  * Caller should hold at least a shared lock on pgss->lock.
    2425                 :             :  */
    2426                 :             : static bool
    2427                 :       31377 : need_gc_qtexts(void)
    2428                 :             : {
    2429                 :             :     Size        extent;
    2430                 :             : 
    2431                 :             :     /* Read shared extent pointer */
    2432                 :       31377 :     SpinLockAcquire(&pgss->mutex);
    2433                 :       31377 :     extent = pgss->extent;
    2434                 :       31377 :     SpinLockRelease(&pgss->mutex);
    2435                 :             : 
    2436                 :             :     /*
    2437                 :             :      * Don't proceed if file does not exceed 512 bytes per possible entry.
    2438                 :             :      *
    2439                 :             :      * Here and in the next test, 32-bit machines have overflow hazards if
    2440                 :             :      * pgss_max and/or mean_query_len are large.  Force the multiplications
    2441                 :             :      * and comparisons to be done in uint64 arithmetic to forestall trouble.
    2442                 :             :      */
    2443         [ +  - ]:       31377 :     if ((uint64) extent < (uint64) 512 * pgss_max)
    2444                 :       31377 :         return false;
    2445                 :             : 
    2446                 :             :     /*
    2447                 :             :      * Don't proceed if file is less than about 50% bloat.  Nothing can or
    2448                 :             :      * should be done in the event of unusually large query texts accounting
    2449                 :             :      * for file's large size.  We go to the trouble of maintaining the mean
    2450                 :             :      * query length in order to prevent garbage collection from thrashing
    2451                 :             :      * uselessly.
    2452                 :             :      */
    2453         [ #  # ]:           0 :     if ((uint64) extent < (uint64) pgss->mean_query_len * pgss_max * 2)
    2454                 :           0 :         return false;
    2455                 :             : 
    2456                 :           0 :     return true;
    2457                 :             : }
    2458                 :             : 
    2459                 :             : /*
    2460                 :             :  * Garbage-collect orphaned query texts in external file.
    2461                 :             :  *
    2462                 :             :  * This won't be called often in the typical case, since it's likely that
    2463                 :             :  * there won't be too much churn, and besides, a similar compaction process
    2464                 :             :  * occurs when serializing to disk at shutdown or as part of resetting.
    2465                 :             :  * Despite this, it seems prudent to plan for the edge case where the file
    2466                 :             :  * becomes unreasonably large, with no other method of compaction likely to
    2467                 :             :  * occur in the foreseeable future.
    2468                 :             :  *
    2469                 :             :  * The caller must hold an exclusive lock on pgss->lock.
    2470                 :             :  *
    2471                 :             :  * At the first sign of trouble we unlink the query text file to get a clean
    2472                 :             :  * slate (although existing statistics are retained), rather than risk
    2473                 :             :  * thrashing by allowing the same problem case to recur indefinitely.
    2474                 :             :  */
    2475                 :             : static void
    2476                 :           0 : gc_qtexts(void)
    2477                 :             : {
    2478                 :             :     char       *qbuffer;
    2479                 :             :     Size        qbuffer_size;
    2480                 :           0 :     FILE       *qfile = NULL;
    2481                 :             :     HASH_SEQ_STATUS hash_seq;
    2482                 :             :     pgssEntry  *entry;
    2483                 :             :     Size        extent;
    2484                 :             :     int         nentries;
    2485                 :             : 
    2486                 :             :     /*
    2487                 :             :      * When called from pgss_store, some other session might have proceeded
    2488                 :             :      * with garbage collection in the no-lock-held interim of lock strength
    2489                 :             :      * escalation.  Check once more that this is actually necessary.
    2490                 :             :      */
    2491         [ #  # ]:           0 :     if (!need_gc_qtexts())
    2492                 :           0 :         return;
    2493                 :             : 
    2494                 :             :     /*
    2495                 :             :      * Load the old texts file.  If we fail (out of memory, for instance),
    2496                 :             :      * invalidate query texts.  Hopefully this is rare.  It might seem better
    2497                 :             :      * to leave things alone on an OOM failure, but the problem is that the
    2498                 :             :      * file is only going to get bigger; hoping for a future non-OOM result is
    2499                 :             :      * risky and can easily lead to complete denial of service.
    2500                 :             :      */
    2501                 :           0 :     qbuffer = qtext_load_file(&qbuffer_size);
    2502         [ #  # ]:           0 :     if (qbuffer == NULL)
    2503                 :           0 :         goto gc_fail;
    2504                 :             : 
    2505                 :             :     /*
    2506                 :             :      * We overwrite the query texts file in place, so as to reduce the risk of
    2507                 :             :      * an out-of-disk-space failure.  Since the file is guaranteed not to get
    2508                 :             :      * larger, this should always work on traditional filesystems; though we
    2509                 :             :      * could still lose on copy-on-write filesystems.
    2510                 :             :      */
    2511                 :           0 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
    2512         [ #  # ]:           0 :     if (qfile == NULL)
    2513                 :             :     {
    2514         [ #  # ]:           0 :         ereport(LOG,
    2515                 :             :                 (errcode_for_file_access(),
    2516                 :             :                  errmsg("could not write file \"%s\": %m",
    2517                 :             :                         PGSS_TEXT_FILE)));
    2518                 :           0 :         goto gc_fail;
    2519                 :             :     }
    2520                 :             : 
    2521                 :           0 :     extent = 0;
    2522                 :           0 :     nentries = 0;
    2523                 :             : 
    2524                 :           0 :     hash_seq_init(&hash_seq, pgss_hash);
    2525         [ #  # ]:           0 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2526                 :             :     {
    2527                 :           0 :         int         query_len = entry->query_len;
    2528                 :           0 :         char       *qry = qtext_fetch(entry->query_offset,
    2529                 :             :                                       query_len,
    2530                 :             :                                       qbuffer,
    2531                 :             :                                       qbuffer_size);
    2532                 :             : 
    2533         [ #  # ]:           0 :         if (qry == NULL)
    2534                 :             :         {
    2535                 :             :             /* Trouble ... drop the text */
    2536                 :           0 :             entry->query_offset = 0;
    2537                 :           0 :             entry->query_len = -1;
    2538                 :             :             /* entry will not be counted in mean query length computation */
    2539                 :           0 :             continue;
    2540                 :             :         }
    2541                 :             : 
    2542         [ #  # ]:           0 :         if (fwrite(qry, 1, query_len + 1, qfile) != query_len + 1)
    2543                 :             :         {
    2544         [ #  # ]:           0 :             ereport(LOG,
    2545                 :             :                     (errcode_for_file_access(),
    2546                 :             :                      errmsg("could not write file \"%s\": %m",
    2547                 :             :                             PGSS_TEXT_FILE)));
    2548                 :           0 :             hash_seq_term(&hash_seq);
    2549                 :           0 :             goto gc_fail;
    2550                 :             :         }
    2551                 :             : 
    2552                 :           0 :         entry->query_offset = extent;
    2553                 :           0 :         extent += query_len + 1;
    2554                 :           0 :         nentries++;
    2555                 :             :     }
    2556                 :             : 
    2557                 :             :     /*
    2558                 :             :      * Truncate away any now-unused space.  If this fails for some odd reason,
    2559                 :             :      * we log it, but there's no need to fail.
    2560                 :             :      */
    2561         [ #  # ]:           0 :     if (ftruncate(fileno(qfile), extent) != 0)
    2562         [ #  # ]:           0 :         ereport(LOG,
    2563                 :             :                 (errcode_for_file_access(),
    2564                 :             :                  errmsg("could not truncate file \"%s\": %m",
    2565                 :             :                         PGSS_TEXT_FILE)));
    2566                 :             : 
    2567         [ #  # ]:           0 :     if (FreeFile(qfile))
    2568                 :             :     {
    2569         [ #  # ]:           0 :         ereport(LOG,
    2570                 :             :                 (errcode_for_file_access(),
    2571                 :             :                  errmsg("could not write file \"%s\": %m",
    2572                 :             :                         PGSS_TEXT_FILE)));
    2573                 :           0 :         qfile = NULL;
    2574                 :           0 :         goto gc_fail;
    2575                 :             :     }
    2576                 :             : 
    2577         [ #  # ]:           0 :     elog(DEBUG1, "pgss gc of queries file shrunk size from %zu to %zu",
    2578                 :             :          pgss->extent, extent);
    2579                 :             : 
    2580                 :             :     /* Reset the shared extent pointer */
    2581                 :           0 :     pgss->extent = extent;
    2582                 :             : 
    2583                 :             :     /*
    2584                 :             :      * Also update the mean query length, to be sure that need_gc_qtexts()
    2585                 :             :      * won't still think we have a problem.
    2586                 :             :      */
    2587         [ #  # ]:           0 :     if (nentries > 0)
    2588                 :           0 :         pgss->mean_query_len = extent / nentries;
    2589                 :             :     else
    2590                 :           0 :         pgss->mean_query_len = ASSUMED_LENGTH_INIT;
    2591                 :             : 
    2592                 :           0 :     pfree(qbuffer);
    2593                 :             : 
    2594                 :             :     /*
    2595                 :             :      * OK, count a garbage collection cycle.  (Note: even though we have
    2596                 :             :      * exclusive lock on pgss->lock, we must take pgss->mutex for this, since
    2597                 :             :      * other processes may examine gc_count while holding only the mutex.
    2598                 :             :      * Also, we have to advance the count *after* we've rewritten the file,
    2599                 :             :      * else other processes might not realize they read a stale file.)
    2600                 :             :      */
    2601                 :           0 :     record_gc_qtexts();
    2602                 :             : 
    2603                 :           0 :     return;
    2604                 :             : 
    2605                 :           0 : gc_fail:
    2606                 :             :     /* clean up resources */
    2607         [ #  # ]:           0 :     if (qfile)
    2608                 :           0 :         FreeFile(qfile);
    2609         [ #  # ]:           0 :     if (qbuffer)
    2610                 :           0 :         pfree(qbuffer);
    2611                 :             : 
    2612                 :             :     /*
    2613                 :             :      * Since the contents of the external file are now uncertain, mark all
    2614                 :             :      * hashtable entries as having invalid texts.
    2615                 :             :      */
    2616                 :           0 :     hash_seq_init(&hash_seq, pgss_hash);
    2617         [ #  # ]:           0 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2618                 :             :     {
    2619                 :           0 :         entry->query_offset = 0;
    2620                 :           0 :         entry->query_len = -1;
    2621                 :             :     }
    2622                 :             : 
    2623                 :             :     /*
    2624                 :             :      * Destroy the query text file and create a new, empty one
    2625                 :             :      */
    2626                 :           0 :     (void) unlink(PGSS_TEXT_FILE);
    2627                 :           0 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
    2628         [ #  # ]:           0 :     if (qfile == NULL)
    2629         [ #  # ]:           0 :         ereport(LOG,
    2630                 :             :                 (errcode_for_file_access(),
    2631                 :             :                  errmsg("could not recreate file \"%s\": %m",
    2632                 :             :                         PGSS_TEXT_FILE)));
    2633                 :             :     else
    2634                 :           0 :         FreeFile(qfile);
    2635                 :             : 
    2636                 :             :     /* Reset the shared extent pointer */
    2637                 :           0 :     pgss->extent = 0;
    2638                 :             : 
    2639                 :             :     /* Reset mean_query_len to match the new state */
    2640                 :           0 :     pgss->mean_query_len = ASSUMED_LENGTH_INIT;
    2641                 :             : 
    2642                 :             :     /*
    2643                 :             :      * Bump the GC count even though we failed.
    2644                 :             :      *
    2645                 :             :      * This is needed to make concurrent readers of file without any lock on
    2646                 :             :      * pgss->lock notice existence of new version of file.  Once readers
    2647                 :             :      * subsequently observe a change in GC count with pgss->lock held, that
    2648                 :             :      * forces a safe reopen of file.  Writers also require that we bump here,
    2649                 :             :      * of course.  (As required by locking protocol, readers and writers don't
    2650                 :             :      * trust earlier file contents until gc_count is found unchanged after
    2651                 :             :      * pgss->lock acquired in shared or exclusive mode respectively.)
    2652                 :             :      */
    2653                 :           0 :     record_gc_qtexts();
    2654                 :             : }
    2655                 :             : 
    2656                 :             : #define SINGLE_ENTRY_RESET(e) \
    2657                 :             : if (e) { \
    2658                 :             :     if (minmax_only) { \
    2659                 :             :         /* When requested reset only min/max statistics of an entry */ \
    2660                 :             :         for (int kind = 0; kind < PGSS_NUMKIND; kind++) \
    2661                 :             :         { \
    2662                 :             :             e->counters.max_time[kind] = 0; \
    2663                 :             :             e->counters.min_time[kind] = 0; \
    2664                 :             :         } \
    2665                 :             :         e->minmax_stats_since = stats_reset; \
    2666                 :             :     } \
    2667                 :             :     else \
    2668                 :             :     { \
    2669                 :             :         /* Remove the key otherwise  */ \
    2670                 :             :         hash_search(pgss_hash, &e->key, HASH_REMOVE, NULL); \
    2671                 :             :         num_remove++; \
    2672                 :             :     } \
    2673                 :             : }
    2674                 :             : 
    2675                 :             : /*
    2676                 :             :  * Reset entries corresponding to parameters passed.
    2677                 :             :  */
    2678                 :             : static TimestampTz
    2679                 :         122 : entry_reset(Oid userid, Oid dbid, int64 queryid, bool minmax_only)
    2680                 :             : {
    2681                 :             :     HASH_SEQ_STATUS hash_seq;
    2682                 :             :     pgssEntry  *entry;
    2683                 :             :     FILE       *qfile;
    2684                 :             :     int64       num_entries;
    2685                 :         122 :     int64       num_remove = 0;
    2686                 :             :     pgssHashKey key;
    2687                 :             :     TimestampTz stats_reset;
    2688                 :             : 
    2689   [ +  -  -  + ]:         122 :     if (!pgss || !pgss_hash)
    2690         [ #  # ]:           0 :         ereport(ERROR,
    2691                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    2692                 :             :                  errmsg("pg_stat_statements must be loaded via \"shared_preload_libraries\"")));
    2693                 :             : 
    2694                 :         122 :     LWLockAcquire(&pgss->lock.lock, LW_EXCLUSIVE);
    2695                 :         122 :     num_entries = hash_get_num_entries(pgss_hash);
    2696                 :             : 
    2697                 :         122 :     stats_reset = GetCurrentTimestamp();
    2698                 :             : 
    2699   [ +  +  +  +  :         122 :     if (userid != 0 && dbid != 0 && queryid != INT64CONST(0))
                   +  - ]
    2700                 :             :     {
    2701                 :             :         /* If all the parameters are available, use the fast path. */
    2702                 :           1 :         memset(&key, 0, sizeof(pgssHashKey));
    2703                 :           1 :         key.userid = userid;
    2704                 :           1 :         key.dbid = dbid;
    2705                 :           1 :         key.queryid = queryid;
    2706                 :             : 
    2707                 :             :         /*
    2708                 :             :          * Reset the entry if it exists, starting with the non-top-level
    2709                 :             :          * entry.
    2710                 :             :          */
    2711                 :           1 :         key.toplevel = false;
    2712                 :           1 :         entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
    2713                 :             : 
    2714   [ -  +  -  -  :           1 :         SINGLE_ENTRY_RESET(entry);
                   -  - ]
    2715                 :             : 
    2716                 :             :         /* Also reset the top-level entry if it exists. */
    2717                 :           1 :         key.toplevel = true;
    2718                 :           1 :         entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
    2719                 :             : 
    2720   [ +  -  -  +  :           1 :         SINGLE_ENTRY_RESET(entry);
                   -  - ]
    2721                 :             :     }
    2722   [ +  +  +  -  :         121 :     else if (userid != 0 || dbid != 0 || queryid != INT64CONST(0))
                   +  + ]
    2723                 :             :     {
    2724                 :             :         /* Reset entries corresponding to valid parameters. */
    2725                 :           4 :         hash_seq_init(&hash_seq, pgss_hash);
    2726         [ +  + ]:          51 :         while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2727                 :             :         {
    2728   [ +  +  +  +  :          47 :             if ((!userid || entry->key.userid == userid) &&
                   -  + ]
    2729   [ -  -  +  + ]:          36 :                 (!dbid || entry->key.dbid == dbid) &&
    2730         [ +  + ]:          34 :                 (!queryid || entry->key.queryid == queryid))
    2731                 :             :             {
    2732   [ +  -  +  +  :           7 :                 SINGLE_ENTRY_RESET(entry);
                   +  + ]
    2733                 :             :             }
    2734                 :             :         }
    2735                 :             :     }
    2736                 :             :     else
    2737                 :             :     {
    2738                 :             :         /* Reset all entries. */
    2739                 :         117 :         hash_seq_init(&hash_seq, pgss_hash);
    2740         [ +  + ]:        1158 :         while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2741                 :             :         {
    2742   [ +  -  +  +  :         946 :             SINGLE_ENTRY_RESET(entry);
                   +  + ]
    2743                 :             :         }
    2744                 :             :     }
    2745                 :             : 
    2746                 :             :     /* All entries are removed? */
    2747         [ +  + ]:         122 :     if (num_entries != num_remove)
    2748                 :           6 :         goto release_lock;
    2749                 :             : 
    2750                 :             :     /*
    2751                 :             :      * Reset global statistics for pg_stat_statements since all entries are
    2752                 :             :      * removed.
    2753                 :             :      */
    2754                 :         116 :     SpinLockAcquire(&pgss->mutex);
    2755                 :         116 :     pgss->stats.dealloc = 0;
    2756                 :         116 :     pgss->stats.stats_reset = stats_reset;
    2757                 :         116 :     SpinLockRelease(&pgss->mutex);
    2758                 :             : 
    2759                 :             :     /*
    2760                 :             :      * Write new empty query file, perhaps even creating a new one to recover
    2761                 :             :      * if the file was missing.
    2762                 :             :      */
    2763                 :         116 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
    2764         [ -  + ]:         116 :     if (qfile == NULL)
    2765                 :             :     {
    2766         [ #  # ]:           0 :         ereport(LOG,
    2767                 :             :                 (errcode_for_file_access(),
    2768                 :             :                  errmsg("could not create file \"%s\": %m",
    2769                 :             :                         PGSS_TEXT_FILE)));
    2770                 :           0 :         goto done;
    2771                 :             :     }
    2772                 :             : 
    2773                 :             :     /* If ftruncate fails, log it, but it's not a fatal problem */
    2774         [ -  + ]:         116 :     if (ftruncate(fileno(qfile), 0) != 0)
    2775         [ #  # ]:           0 :         ereport(LOG,
    2776                 :             :                 (errcode_for_file_access(),
    2777                 :             :                  errmsg("could not truncate file \"%s\": %m",
    2778                 :             :                         PGSS_TEXT_FILE)));
    2779                 :             : 
    2780                 :         116 :     FreeFile(qfile);
    2781                 :             : 
    2782                 :         116 : done:
    2783                 :         116 :     pgss->extent = 0;
    2784                 :             :     /* This counts as a query text garbage collection for our purposes */
    2785                 :         116 :     record_gc_qtexts();
    2786                 :             : 
    2787                 :         122 : release_lock:
    2788                 :         122 :     LWLockRelease(&pgss->lock.lock);
    2789                 :             : 
    2790                 :         122 :     return stats_reset;
    2791                 :             : }
    2792                 :             : 
    2793                 :             : /*
    2794                 :             :  * Generate a normalized version of the query string that will be used to
    2795                 :             :  * represent all similar queries.
    2796                 :             :  *
    2797                 :             :  * Note that the normalized representation may well vary depending on
    2798                 :             :  * just which "equivalent" query is used to create the hashtable entry.
    2799                 :             :  * We assume this is OK.
    2800                 :             :  *
    2801                 :             :  * If query_loc > 0, then "query" has been advanced by that much compared to
    2802                 :             :  * the original string start, so we need to translate the provided locations
    2803                 :             :  * to compensate.  (This lets us avoid re-scanning statements before the one
    2804                 :             :  * of interest, so it's worth doing.)
    2805                 :             :  *
    2806                 :             :  * *query_len_p contains the input string length, and is updated with
    2807                 :             :  * the result string length on exit.  The resulting string might be longer
    2808                 :             :  * or shorter depending on what happens with replacement of constants.
    2809                 :             :  *
    2810                 :             :  * Returns a palloc'd string.
    2811                 :             :  */
    2812                 :             : static char *
    2813                 :       11660 : generate_normalized_query(const JumbleState *jstate, const char *query,
    2814                 :             :                           int query_loc, int *query_len_p)
    2815                 :             : {
    2816                 :             :     char       *norm_query;
    2817                 :       11660 :     int         query_len = *query_len_p;
    2818                 :             :     int         norm_query_buflen,  /* Space allowed for norm_query */
    2819                 :             :                 len_to_wrt,     /* Length (in bytes) to write */
    2820                 :       11660 :                 quer_loc = 0,   /* Source query byte location */
    2821                 :       11660 :                 n_quer_loc = 0, /* Normalized query byte location */
    2822                 :       11660 :                 last_off = 0,   /* Offset from start for previous tok */
    2823                 :       11660 :                 last_tok_len = 0;   /* Length (in bytes) of that tok */
    2824                 :       11660 :     int         num_constants_replaced = 0;
    2825                 :       11660 :     LocationLen *locs = NULL;
    2826                 :             : 
    2827                 :             :     /*
    2828                 :             :      * Determine constants' lengths (core system only gives us locations), and
    2829                 :             :      * return a sorted copy of jstate's LocationLen data with lengths filled
    2830                 :             :      * in.
    2831                 :             :      */
    2832                 :       11660 :     locs = ComputeConstantLengths(jstate, query, query_loc);
    2833                 :             : 
    2834                 :             :     /*
    2835                 :             :      * Allow for $n symbols to be longer than the constants they replace.
    2836                 :             :      * Constants must take at least one byte in text form, while a $n symbol
    2837                 :             :      * certainly isn't more than 11 bytes, even if n reaches INT_MAX.  We
    2838                 :             :      * could refine that limit based on the max value of n for the current
    2839                 :             :      * query, but it hardly seems worth any extra effort to do so.
    2840                 :             :      */
    2841                 :       11660 :     norm_query_buflen = query_len + jstate->clocations_count * 10;
    2842                 :             : 
    2843                 :             :     /* Allocate result buffer */
    2844                 :       11660 :     norm_query = palloc(norm_query_buflen + 1);
    2845                 :             : 
    2846         [ +  + ]:       46868 :     for (int i = 0; i < jstate->clocations_count; i++)
    2847                 :             :     {
    2848                 :             :         int         off,        /* Offset from start for cur tok */
    2849                 :             :                     tok_len;    /* Length (in bytes) of that tok */
    2850                 :             : 
    2851                 :             :         /*
    2852                 :             :          * If we have an external param at this location, but no lists are
    2853                 :             :          * being squashed across the query, then we skip here; this will make
    2854                 :             :          * us print the characters found in the original query that represent
    2855                 :             :          * the parameter in the next iteration (or after the loop is done),
    2856                 :             :          * which is a bit odd but seems to work okay in most cases.
    2857                 :             :          */
    2858   [ +  +  +  + ]:       35208 :         if (locs[i].extern_param && !jstate->has_squashed_lists)
    2859                 :         168 :             continue;
    2860                 :             : 
    2861                 :       35040 :         off = locs[i].location;
    2862                 :             : 
    2863                 :             :         /* Adjust recorded location if we're dealing with partial string */
    2864                 :       35040 :         off -= query_loc;
    2865                 :             : 
    2866                 :       35040 :         tok_len = locs[i].length;
    2867                 :             : 
    2868         [ +  + ]:       35040 :         if (tok_len < 0)
    2869                 :         692 :             continue;           /* ignore any duplicates */
    2870                 :             : 
    2871                 :             :         /* Copy next chunk (what precedes the next constant) */
    2872                 :       34348 :         len_to_wrt = off - last_off;
    2873                 :       34348 :         len_to_wrt -= last_tok_len;
    2874                 :             :         Assert(len_to_wrt >= 0);
    2875                 :       34348 :         memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
    2876                 :       34348 :         n_quer_loc += len_to_wrt;
    2877                 :             : 
    2878                 :             :         /*
    2879                 :             :          * And insert a param symbol in place of the constant token; and, if
    2880                 :             :          * we have a squashable list, insert a placeholder comment starting
    2881                 :             :          * from the list's second value.
    2882                 :             :          */
    2883                 :       34348 :         n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d%s",
    2884                 :       34348 :                               num_constants_replaced + 1 + jstate->highest_extern_param_id,
    2885         [ +  + ]:       34348 :                               locs[i].squashed ? " /*, ... */" : "");
    2886                 :       34348 :         num_constants_replaced++;
    2887                 :             : 
    2888                 :             :         /* move forward */
    2889                 :       34348 :         quer_loc = off + tok_len;
    2890                 :       34348 :         last_off = off;
    2891                 :       34348 :         last_tok_len = tok_len;
    2892                 :             :     }
    2893                 :             : 
    2894                 :             :     /* Clean up, if needed */
    2895         [ +  - ]:       11660 :     if (locs)
    2896                 :       11660 :         pfree(locs);
    2897                 :             : 
    2898                 :             :     /*
    2899                 :             :      * We've copied up until the last ignorable constant.  Copy over the
    2900                 :             :      * remaining bytes of the original query string.
    2901                 :             :      */
    2902                 :       11660 :     len_to_wrt = query_len - quer_loc;
    2903                 :             : 
    2904                 :             :     Assert(len_to_wrt >= 0);
    2905                 :       11660 :     memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
    2906                 :       11660 :     n_quer_loc += len_to_wrt;
    2907                 :             : 
    2908                 :             :     Assert(n_quer_loc <= norm_query_buflen);
    2909                 :       11660 :     norm_query[n_quer_loc] = '\0';
    2910                 :             : 
    2911                 :       11660 :     *query_len_p = n_quer_loc;
    2912                 :       11660 :     return norm_query;
    2913                 :             : }
        

Generated by: LCOV version 2.0-1