LCOV - code coverage report
Current view: top level - contrib/pg_stat_statements - pg_stat_statements.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 738 1235 59.8 %
Date: 2020-06-01 08:06:25 Functions: 36 46 78.3 %
Legend: Lines: hit not hit

          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 totalled 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             :  * As of Postgres 9.2, this module normalizes query entries.  Normalization
      12             :  * is a process whereby similar queries, typically differing only in their
      13             :  * constants (though the exact rules are somewhat more subtle than that) are
      14             :  * recognized as equivalent, and are tracked as a single entry.  This is
      15             :  * particularly useful for non-prepared queries.
      16             :  *
      17             :  * Normalization is implemented by fingerprinting queries, selectively
      18             :  * serializing those fields of each query tree's nodes that are judged to be
      19             :  * essential to the query.  This is referred to as a query jumble.  This is
      20             :  * distinct from a regular serialization in that various extraneous
      21             :  * information is ignored as irrelevant or not essential to the query, such
      22             :  * as the collations of Vars and, most notably, the values of constants.
      23             :  *
      24             :  * This jumble is acquired at the end of parse analysis of each query, and
      25             :  * a 64-bit hash of it is stored into the query's Query.queryId field.
      26             :  * The server then copies this value around, making it available in plan
      27             :  * tree(s) generated from the query.  The executor can then use this value
      28             :  * to blame query costs on the proper queryId.
      29             :  *
      30             :  * To facilitate presenting entries to users, we create "representative" query
      31             :  * strings in which constants are replaced with parameter symbols ($n), to
      32             :  * make it clearer what a normalized entry can represent.  To save on shared
      33             :  * memory, and to avoid having to truncate oversized query strings, we store
      34             :  * these strings in a temporary external query-texts file.  Offsets into this
      35             :  * file are kept in shared memory.
      36             :  *
      37             :  * Note about locking issues: to create or delete an entry in the shared
      38             :  * hashtable, one must hold pgss->lock exclusively.  Modifying any field
      39             :  * in an entry except the counters requires the same.  To look up an entry,
      40             :  * one must hold the lock shared.  To read or update the counters within
      41             :  * an entry, one must hold the lock shared or exclusive (so the entry doesn't
      42             :  * disappear!) and also take the entry's mutex spinlock.
      43             :  * The shared state variable pgss->extent (the next free spot in the external
      44             :  * query-text file) should be accessed only while holding either the
      45             :  * pgss->mutex spinlock, or exclusive lock on pgss->lock.  We use the mutex to
      46             :  * allow reserving file space while holding only shared lock on pgss->lock.
      47             :  * Rewriting the entire external query-text file, eg for garbage collection,
      48             :  * requires holding pgss->lock exclusively; this allows individual entries
      49             :  * in the file to be read or written while holding only shared lock.
      50             :  *
      51             :  *
      52             :  * Copyright (c) 2008-2020, PostgreSQL Global Development Group
      53             :  *
      54             :  * IDENTIFICATION
      55             :  *    contrib/pg_stat_statements/pg_stat_statements.c
      56             :  *
      57             :  *-------------------------------------------------------------------------
      58             :  */
      59             : #include "postgres.h"
      60             : 
      61             : #include <math.h>
      62             : #include <sys/stat.h>
      63             : #include <unistd.h>
      64             : 
      65             : #include "catalog/pg_authid.h"
      66             : #include "common/hashfn.h"
      67             : #include "executor/instrument.h"
      68             : #include "funcapi.h"
      69             : #include "mb/pg_wchar.h"
      70             : #include "miscadmin.h"
      71             : #include "optimizer/planner.h"
      72             : #include "parser/analyze.h"
      73             : #include "parser/parsetree.h"
      74             : #include "parser/scanner.h"
      75             : #include "parser/scansup.h"
      76             : #include "pgstat.h"
      77             : #include "storage/fd.h"
      78             : #include "storage/ipc.h"
      79             : #include "storage/spin.h"
      80             : #include "tcop/utility.h"
      81             : #include "utils/acl.h"
      82             : #include "utils/builtins.h"
      83             : #include "utils/memutils.h"
      84             : 
      85           4 : PG_MODULE_MAGIC;
      86             : 
      87             : /* Location of permanent stats file (valid when database is shut down) */
      88             : #define PGSS_DUMP_FILE  PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
      89             : 
      90             : /*
      91             :  * Location of external query text file.  We don't keep it in the core
      92             :  * system's stats_temp_directory.  The core system can safely use that GUC
      93             :  * setting, because the statistics collector temp file paths are set only once
      94             :  * as part of changing the GUC, but pg_stat_statements has no way of avoiding
      95             :  * race conditions.  Besides, we only expect modest, infrequent I/O for query
      96             :  * strings, so placing the file on a faster filesystem is not compelling.
      97             :  */
      98             : #define PGSS_TEXT_FILE  PG_STAT_TMP_DIR "/pgss_query_texts.stat"
      99             : 
     100             : /* Magic number identifying the stats file format */
     101             : static const uint32 PGSS_FILE_HEADER = 0x20171004;
     102             : 
     103             : /* PostgreSQL major version number, changes in which invalidate all entries */
     104             : static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
     105             : 
     106             : /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */
     107             : #define USAGE_EXEC(duration)    (1.0)
     108             : #define USAGE_INIT              (1.0)   /* including initial planning */
     109             : #define ASSUMED_MEDIAN_INIT     (10.0)  /* initial assumed median usage */
     110             : #define ASSUMED_LENGTH_INIT     1024    /* initial assumed mean query length */
     111             : #define USAGE_DECREASE_FACTOR   (0.99)  /* decreased every entry_dealloc */
     112             : #define STICKY_DECREASE_FACTOR  (0.50)  /* factor for sticky entries */
     113             : #define USAGE_DEALLOC_PERCENT   5   /* free this % of entries at once */
     114             : #define IS_STICKY(c)    ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
     115             : 
     116             : #define JUMBLE_SIZE             1024    /* query serialization buffer size */
     117             : 
     118             : /*
     119             :  * Extension version number, for supporting older extension versions' objects
     120             :  */
     121             : typedef enum pgssVersion
     122             : {
     123             :     PGSS_V1_0 = 0,
     124             :     PGSS_V1_1,
     125             :     PGSS_V1_2,
     126             :     PGSS_V1_3,
     127             :     PGSS_V1_8
     128             : } pgssVersion;
     129             : 
     130             : typedef enum pgssStoreKind
     131             : {
     132             :     PGSS_INVALID = -1,
     133             : 
     134             :     /*
     135             :      * PGSS_PLAN and PGSS_EXEC must be respectively 0 and 1 as they're used to
     136             :      * reference the underlying values in the arrays in the Counters struct,
     137             :      * and this order is required in pg_stat_statements_internal().
     138             :      */
     139             :     PGSS_PLAN = 0,
     140             :     PGSS_EXEC,
     141             : 
     142             :     PGSS_NUMKIND                /* Must be last value of this enum */
     143             : } pgssStoreKind;
     144             : 
     145             : /*
     146             :  * Hashtable key that defines the identity of a hashtable entry.  We separate
     147             :  * queries by user and by database even if they are otherwise identical.
     148             :  *
     149             :  * Right now, this structure contains no padding.  If you add any, make sure
     150             :  * to teach pgss_store() to zero the padding bytes.  Otherwise, things will
     151             :  * break, because pgss_hash is created using HASH_BLOBS, and thus tag_hash
     152             :  * is used to hash this.
     153             :  */
     154             : typedef struct pgssHashKey
     155             : {
     156             :     Oid         userid;         /* user OID */
     157             :     Oid         dbid;           /* database OID */
     158             :     uint64      queryid;        /* query identifier */
     159             : } pgssHashKey;
     160             : 
     161             : /*
     162             :  * The actual stats counters kept within pgssEntry.
     163             :  */
     164             : typedef struct Counters
     165             : {
     166             :     int64       calls[PGSS_NUMKIND];    /* # of times planned/executed */
     167             :     double      total_time[PGSS_NUMKIND];   /* total planning/execution time,
     168             :                                              * in msec */
     169             :     double      min_time[PGSS_NUMKIND]; /* minimum planning/execution time in
     170             :                                          * msec */
     171             :     double      max_time[PGSS_NUMKIND]; /* maximum planning/execution time in
     172             :                                          * msec */
     173             :     double      mean_time[PGSS_NUMKIND];    /* mean planning/execution time in
     174             :                                              * msec */
     175             :     double      sum_var_time[PGSS_NUMKIND]; /* sum of variances in
     176             :                                              * planning/execution time in msec */
     177             :     int64       rows;           /* total # of retrieved or affected rows */
     178             :     int64       shared_blks_hit;    /* # of shared buffer hits */
     179             :     int64       shared_blks_read;   /* # of shared disk blocks read */
     180             :     int64       shared_blks_dirtied;    /* # of shared disk blocks dirtied */
     181             :     int64       shared_blks_written;    /* # of shared disk blocks written */
     182             :     int64       local_blks_hit; /* # of local buffer hits */
     183             :     int64       local_blks_read;    /* # of local disk blocks read */
     184             :     int64       local_blks_dirtied; /* # of local disk blocks dirtied */
     185             :     int64       local_blks_written; /* # of local disk blocks written */
     186             :     int64       temp_blks_read; /* # of temp blocks read */
     187             :     int64       temp_blks_written;  /* # of temp blocks written */
     188             :     double      blk_read_time;  /* time spent reading, in msec */
     189             :     double      blk_write_time; /* time spent writing, in msec */
     190             :     double      usage;          /* usage factor */
     191             :     int64       wal_records;    /* # of WAL records generated */
     192             :     int64       wal_fpi;        /* # of WAL full page images generated */
     193             :     uint64      wal_bytes;      /* total amount of WAL bytes generated */
     194             : } Counters;
     195             : 
     196             : /*
     197             :  * Statistics per statement
     198             :  *
     199             :  * Note: in event of a failure in garbage collection of the query text file,
     200             :  * we reset query_offset to zero and query_len to -1.  This will be seen as
     201             :  * an invalid state by qtext_fetch().
     202             :  */
     203             : typedef struct pgssEntry
     204             : {
     205             :     pgssHashKey key;            /* hash key of entry - MUST BE FIRST */
     206             :     Counters    counters;       /* the statistics for this query */
     207             :     Size        query_offset;   /* query text offset in external file */
     208             :     int         query_len;      /* # of valid bytes in query string, or -1 */
     209             :     int         encoding;       /* query text encoding */
     210             :     slock_t     mutex;          /* protects the counters only */
     211             : } pgssEntry;
     212             : 
     213             : /*
     214             :  * Global shared state
     215             :  */
     216             : typedef struct pgssSharedState
     217             : {
     218             :     LWLock     *lock;           /* protects hashtable search/modification */
     219             :     double      cur_median_usage;   /* current median usage in hashtable */
     220             :     Size        mean_query_len; /* current mean entry text length */
     221             :     slock_t     mutex;          /* protects following fields only: */
     222             :     Size        extent;         /* current extent of query file */
     223             :     int         n_writers;      /* number of active writers to query file */
     224             :     int         gc_count;       /* query file garbage collection cycle count */
     225             : } pgssSharedState;
     226             : 
     227             : /*
     228             :  * Struct for tracking locations/lengths of constants during normalization
     229             :  */
     230             : typedef struct pgssLocationLen
     231             : {
     232             :     int         location;       /* start offset in query text */
     233             :     int         length;         /* length in bytes, or -1 to ignore */
     234             : } pgssLocationLen;
     235             : 
     236             : /*
     237             :  * Working state for computing a query jumble and producing a normalized
     238             :  * query string
     239             :  */
     240             : typedef struct pgssJumbleState
     241             : {
     242             :     /* Jumble of current query tree */
     243             :     unsigned char *jumble;
     244             : 
     245             :     /* Number of bytes used in jumble[] */
     246             :     Size        jumble_len;
     247             : 
     248             :     /* Array of locations of constants that should be removed */
     249             :     pgssLocationLen *clocations;
     250             : 
     251             :     /* Allocated length of clocations array */
     252             :     int         clocations_buf_size;
     253             : 
     254             :     /* Current number of valid entries in clocations array */
     255             :     int         clocations_count;
     256             : 
     257             :     /* highest Param id we've seen, in order to start normalization correctly */
     258             :     int         highest_extern_param_id;
     259             : } pgssJumbleState;
     260             : 
     261             : /*---- Local variables ----*/
     262             : 
     263             : /* Current nesting depth of ExecutorRun+ProcessUtility calls */
     264             : static int  exec_nested_level = 0;
     265             : 
     266             : /* Current nesting depth of planner calls */
     267             : static int  plan_nested_level = 0;
     268             : 
     269             : /* Saved hook values in case of unload */
     270             : static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
     271             : static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
     272             : static planner_hook_type prev_planner_hook = NULL;
     273             : static ExecutorStart_hook_type prev_ExecutorStart = NULL;
     274             : static ExecutorRun_hook_type prev_ExecutorRun = NULL;
     275             : static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
     276             : static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
     277             : static ProcessUtility_hook_type prev_ProcessUtility = NULL;
     278             : 
     279             : /* Links to shared memory state */
     280             : static pgssSharedState *pgss = NULL;
     281             : static HTAB *pgss_hash = NULL;
     282             : 
     283             : /*---- GUC variables ----*/
     284             : 
     285             : typedef enum
     286             : {
     287             :     PGSS_TRACK_NONE,            /* track no statements */
     288             :     PGSS_TRACK_TOP,             /* only top level statements */
     289             :     PGSS_TRACK_ALL              /* all statements, including nested ones */
     290             : }           PGSSTrackLevel;
     291             : 
     292             : static const struct config_enum_entry track_options[] =
     293             : {
     294             :     {"none", PGSS_TRACK_NONE, false},
     295             :     {"top", PGSS_TRACK_TOP, false},
     296             :     {"all", PGSS_TRACK_ALL, false},
     297             :     {NULL, 0, false}
     298             : };
     299             : 
     300             : static int  pgss_max;           /* max # statements to track */
     301             : static int  pgss_track;         /* tracking level */
     302             : static bool pgss_track_utility; /* whether to track utility commands */
     303             : static bool pgss_track_planning;    /* whether to track planning duration */
     304             : static bool pgss_save;          /* whether to save stats across shutdown */
     305             : 
     306             : 
     307             : #define pgss_enabled(level) \
     308             :     (pgss_track == PGSS_TRACK_ALL || \
     309             :     (pgss_track == PGSS_TRACK_TOP && (level) == 0))
     310             : 
     311             : #define record_gc_qtexts() \
     312             :     do { \
     313             :         volatile pgssSharedState *s = (volatile pgssSharedState *) pgss; \
     314             :         SpinLockAcquire(&s->mutex); \
     315             :         s->gc_count++; \
     316             :         SpinLockRelease(&s->mutex); \
     317             :     } while(0)
     318             : 
     319             : /*---- Function declarations ----*/
     320             : 
     321             : void        _PG_init(void);
     322             : void        _PG_fini(void);
     323             : 
     324           4 : PG_FUNCTION_INFO_V1(pg_stat_statements_reset);
     325           6 : PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_7);
     326           0 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_2);
     327           4 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_3);
     328           6 : PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
     329           0 : PG_FUNCTION_INFO_V1(pg_stat_statements);
     330             : 
     331             : static void pgss_shmem_startup(void);
     332             : static void pgss_shmem_shutdown(int code, Datum arg);
     333             : static void pgss_post_parse_analyze(ParseState *pstate, Query *query);
     334             : static PlannedStmt *pgss_planner(Query *parse,
     335             :                                  const char *query_string,
     336             :                                  int cursorOptions,
     337             :                                  ParamListInfo boundParams);
     338             : static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
     339             : static void pgss_ExecutorRun(QueryDesc *queryDesc,
     340             :                              ScanDirection direction,
     341             :                              uint64 count, bool execute_once);
     342             : static void pgss_ExecutorFinish(QueryDesc *queryDesc);
     343             : static void pgss_ExecutorEnd(QueryDesc *queryDesc);
     344             : static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
     345             :                                 ProcessUtilityContext context, ParamListInfo params,
     346             :                                 QueryEnvironment *queryEnv,
     347             :                                 DestReceiver *dest, QueryCompletion *qc);
     348             : static uint64 pgss_hash_string(const char *str, int len);
     349             : static void pgss_store(const char *query, uint64 queryId,
     350             :                        int query_location, int query_len,
     351             :                        pgssStoreKind kind,
     352             :                        double total_time, uint64 rows,
     353             :                        const BufferUsage *bufusage,
     354             :                        const WalUsage *walusage,
     355             :                        pgssJumbleState *jstate);
     356             : static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
     357             :                                         pgssVersion api_version,
     358             :                                         bool showtext);
     359             : static Size pgss_memsize(void);
     360             : static pgssEntry *entry_alloc(pgssHashKey *key, Size query_offset, int query_len,
     361             :                               int encoding, bool sticky);
     362             : static void entry_dealloc(void);
     363             : static bool qtext_store(const char *query, int query_len,
     364             :                         Size *query_offset, int *gc_count);
     365             : static char *qtext_load_file(Size *buffer_size);
     366             : static char *qtext_fetch(Size query_offset, int query_len,
     367             :                          char *buffer, Size buffer_size);
     368             : static bool need_gc_qtexts(void);
     369             : static void gc_qtexts(void);
     370             : static void entry_reset(Oid userid, Oid dbid, uint64 queryid);
     371             : static void AppendJumble(pgssJumbleState *jstate,
     372             :                          const unsigned char *item, Size size);
     373             : static void JumbleQuery(pgssJumbleState *jstate, Query *query);
     374             : static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable);
     375             : static void JumbleRowMarks(pgssJumbleState *jstate, List *rowMarks);
     376             : static void JumbleExpr(pgssJumbleState *jstate, Node *node);
     377             : static void RecordConstLocation(pgssJumbleState *jstate, int location);
     378             : static char *generate_normalized_query(pgssJumbleState *jstate, const char *query,
     379             :                                        int query_loc, int *query_len_p, int encoding);
     380             : static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
     381             :                                      int query_loc);
     382             : static int  comp_location(const void *a, const void *b);
     383             : 
     384             : 
     385             : /*
     386             :  * Module load callback
     387             :  */
     388             : void
     389           4 : _PG_init(void)
     390             : {
     391             :     /*
     392             :      * In order to create our shared memory area, we have to be loaded via
     393             :      * shared_preload_libraries.  If not, fall out without hooking into any of
     394             :      * the main system.  (We don't throw error here because it seems useful to
     395             :      * allow the pg_stat_statements functions to be created even when the
     396             :      * module isn't active.  The functions must protect themselves against
     397             :      * being called then, however.)
     398             :      */
     399           4 :     if (!process_shared_preload_libraries_in_progress)
     400           2 :         return;
     401             : 
     402             :     /*
     403             :      * Define (or redefine) custom GUC variables.
     404             :      */
     405           2 :     DefineCustomIntVariable("pg_stat_statements.max",
     406             :                             "Sets the maximum number of statements tracked by pg_stat_statements.",
     407             :                             NULL,
     408             :                             &pgss_max,
     409             :                             5000,
     410             :                             100,
     411             :                             INT_MAX,
     412             :                             PGC_POSTMASTER,
     413             :                             0,
     414             :                             NULL,
     415             :                             NULL,
     416             :                             NULL);
     417             : 
     418           2 :     DefineCustomEnumVariable("pg_stat_statements.track",
     419             :                              "Selects which statements are tracked by pg_stat_statements.",
     420             :                              NULL,
     421             :                              &pgss_track,
     422             :                              PGSS_TRACK_TOP,
     423             :                              track_options,
     424             :                              PGC_SUSET,
     425             :                              0,
     426             :                              NULL,
     427             :                              NULL,
     428             :                              NULL);
     429             : 
     430           2 :     DefineCustomBoolVariable("pg_stat_statements.track_utility",
     431             :                              "Selects whether utility commands are tracked by pg_stat_statements.",
     432             :                              NULL,
     433             :                              &pgss_track_utility,
     434             :                              true,
     435             :                              PGC_SUSET,
     436             :                              0,
     437             :                              NULL,
     438             :                              NULL,
     439             :                              NULL);
     440             : 
     441           2 :     DefineCustomBoolVariable("pg_stat_statements.track_planning",
     442             :                              "Selects whether planning duration is tracked by pg_stat_statements.",
     443             :                              NULL,
     444             :                              &pgss_track_planning,
     445             :                              true,
     446             :                              PGC_SUSET,
     447             :                              0,
     448             :                              NULL,
     449             :                              NULL,
     450             :                              NULL);
     451             : 
     452           2 :     DefineCustomBoolVariable("pg_stat_statements.save",
     453             :                              "Save pg_stat_statements statistics across server shutdowns.",
     454             :                              NULL,
     455             :                              &pgss_save,
     456             :                              true,
     457             :                              PGC_SIGHUP,
     458             :                              0,
     459             :                              NULL,
     460             :                              NULL,
     461             :                              NULL);
     462             : 
     463           2 :     EmitWarningsOnPlaceholders("pg_stat_statements");
     464             : 
     465             :     /*
     466             :      * Request additional shared resources.  (These are no-ops if we're not in
     467             :      * the postmaster process.)  We'll allocate or attach to the shared
     468             :      * resources in pgss_shmem_startup().
     469             :      */
     470           2 :     RequestAddinShmemSpace(pgss_memsize());
     471           2 :     RequestNamedLWLockTranche("pg_stat_statements", 1);
     472             : 
     473             :     /*
     474             :      * Install hooks.
     475             :      */
     476           2 :     prev_shmem_startup_hook = shmem_startup_hook;
     477           2 :     shmem_startup_hook = pgss_shmem_startup;
     478           2 :     prev_post_parse_analyze_hook = post_parse_analyze_hook;
     479           2 :     post_parse_analyze_hook = pgss_post_parse_analyze;
     480           2 :     prev_planner_hook = planner_hook;
     481           2 :     planner_hook = pgss_planner;
     482           2 :     prev_ExecutorStart = ExecutorStart_hook;
     483           2 :     ExecutorStart_hook = pgss_ExecutorStart;
     484           2 :     prev_ExecutorRun = ExecutorRun_hook;
     485           2 :     ExecutorRun_hook = pgss_ExecutorRun;
     486           2 :     prev_ExecutorFinish = ExecutorFinish_hook;
     487           2 :     ExecutorFinish_hook = pgss_ExecutorFinish;
     488           2 :     prev_ExecutorEnd = ExecutorEnd_hook;
     489           2 :     ExecutorEnd_hook = pgss_ExecutorEnd;
     490           2 :     prev_ProcessUtility = ProcessUtility_hook;
     491           2 :     ProcessUtility_hook = pgss_ProcessUtility;
     492             : }
     493             : 
     494             : /*
     495             :  * Module unload callback
     496             :  */
     497             : void
     498           0 : _PG_fini(void)
     499             : {
     500             :     /* Uninstall hooks. */
     501           0 :     shmem_startup_hook = prev_shmem_startup_hook;
     502           0 :     post_parse_analyze_hook = prev_post_parse_analyze_hook;
     503           0 :     planner_hook = prev_planner_hook;
     504           0 :     ExecutorStart_hook = prev_ExecutorStart;
     505           0 :     ExecutorRun_hook = prev_ExecutorRun;
     506           0 :     ExecutorFinish_hook = prev_ExecutorFinish;
     507           0 :     ExecutorEnd_hook = prev_ExecutorEnd;
     508           0 :     ProcessUtility_hook = prev_ProcessUtility;
     509           0 : }
     510             : 
     511             : /*
     512             :  * shmem_startup hook: allocate or attach to shared memory,
     513             :  * then load any pre-existing statistics from file.
     514             :  * Also create and load the query-texts file, which is expected to exist
     515             :  * (even if empty) while the module is enabled.
     516             :  */
     517             : static void
     518           2 : pgss_shmem_startup(void)
     519             : {
     520             :     bool        found;
     521             :     HASHCTL     info;
     522           2 :     FILE       *file = NULL;
     523           2 :     FILE       *qfile = NULL;
     524             :     uint32      header;
     525             :     int32       num;
     526             :     int32       pgver;
     527             :     int32       i;
     528             :     int         buffer_size;
     529           2 :     char       *buffer = NULL;
     530             : 
     531           2 :     if (prev_shmem_startup_hook)
     532           0 :         prev_shmem_startup_hook();
     533             : 
     534             :     /* reset in case this is a restart within the postmaster */
     535           2 :     pgss = NULL;
     536           2 :     pgss_hash = NULL;
     537             : 
     538             :     /*
     539             :      * Create or attach to the shared memory state, including hash table
     540             :      */
     541           2 :     LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
     542             : 
     543           2 :     pgss = ShmemInitStruct("pg_stat_statements",
     544             :                            sizeof(pgssSharedState),
     545             :                            &found);
     546             : 
     547           2 :     if (!found)
     548             :     {
     549             :         /* First time through ... */
     550           2 :         pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock;
     551           2 :         pgss->cur_median_usage = ASSUMED_MEDIAN_INIT;
     552           2 :         pgss->mean_query_len = ASSUMED_LENGTH_INIT;
     553           2 :         SpinLockInit(&pgss->mutex);
     554           2 :         pgss->extent = 0;
     555           2 :         pgss->n_writers = 0;
     556           2 :         pgss->gc_count = 0;
     557             :     }
     558             : 
     559           2 :     memset(&info, 0, sizeof(info));
     560           2 :     info.keysize = sizeof(pgssHashKey);
     561           2 :     info.entrysize = sizeof(pgssEntry);
     562           2 :     pgss_hash = ShmemInitHash("pg_stat_statements hash",
     563             :                               pgss_max, pgss_max,
     564             :                               &info,
     565             :                               HASH_ELEM | HASH_BLOBS);
     566             : 
     567           2 :     LWLockRelease(AddinShmemInitLock);
     568             : 
     569             :     /*
     570             :      * If we're in the postmaster (or a standalone backend...), set up a shmem
     571             :      * exit hook to dump the statistics to disk.
     572             :      */
     573           2 :     if (!IsUnderPostmaster)
     574           2 :         on_shmem_exit(pgss_shmem_shutdown, (Datum) 0);
     575             : 
     576             :     /*
     577             :      * Done if some other process already completed our initialization.
     578             :      */
     579           2 :     if (found)
     580           2 :         return;
     581             : 
     582             :     /*
     583             :      * Note: we don't bother with locks here, because there should be no other
     584             :      * processes running when this code is reached.
     585             :      */
     586             : 
     587             :     /* Unlink query text file possibly left over from crash */
     588           2 :     unlink(PGSS_TEXT_FILE);
     589             : 
     590             :     /* Allocate new query text temp file */
     591           2 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
     592           2 :     if (qfile == NULL)
     593           0 :         goto write_error;
     594             : 
     595             :     /*
     596             :      * If we were told not to load old statistics, we're done.  (Note we do
     597             :      * not try to unlink any old dump file in this case.  This seems a bit
     598             :      * questionable but it's the historical behavior.)
     599             :      */
     600           2 :     if (!pgss_save)
     601             :     {
     602           0 :         FreeFile(qfile);
     603           0 :         return;
     604             :     }
     605             : 
     606             :     /*
     607             :      * Attempt to load old statistics from the dump file.
     608             :      */
     609           2 :     file = AllocateFile(PGSS_DUMP_FILE, PG_BINARY_R);
     610           2 :     if (file == NULL)
     611             :     {
     612           2 :         if (errno != ENOENT)
     613           0 :             goto read_error;
     614             :         /* No existing persisted stats file, so we're done */
     615           2 :         FreeFile(qfile);
     616           2 :         return;
     617             :     }
     618             : 
     619           0 :     buffer_size = 2048;
     620           0 :     buffer = (char *) palloc(buffer_size);
     621             : 
     622           0 :     if (fread(&header, sizeof(uint32), 1, file) != 1 ||
     623           0 :         fread(&pgver, sizeof(uint32), 1, file) != 1 ||
     624           0 :         fread(&num, sizeof(int32), 1, file) != 1)
     625           0 :         goto read_error;
     626             : 
     627           0 :     if (header != PGSS_FILE_HEADER ||
     628           0 :         pgver != PGSS_PG_MAJOR_VERSION)
     629           0 :         goto data_error;
     630             : 
     631           0 :     for (i = 0; i < num; i++)
     632             :     {
     633             :         pgssEntry   temp;
     634             :         pgssEntry  *entry;
     635             :         Size        query_offset;
     636             : 
     637           0 :         if (fread(&temp, sizeof(pgssEntry), 1, file) != 1)
     638           0 :             goto read_error;
     639             : 
     640             :         /* Encoding is the only field we can easily sanity-check */
     641           0 :         if (!PG_VALID_BE_ENCODING(temp.encoding))
     642           0 :             goto data_error;
     643             : 
     644             :         /* Resize buffer as needed */
     645           0 :         if (temp.query_len >= buffer_size)
     646             :         {
     647           0 :             buffer_size = Max(buffer_size * 2, temp.query_len + 1);
     648           0 :             buffer = repalloc(buffer, buffer_size);
     649             :         }
     650             : 
     651           0 :         if (fread(buffer, 1, temp.query_len + 1, file) != temp.query_len + 1)
     652           0 :             goto read_error;
     653             : 
     654             :         /* Should have a trailing null, but let's make sure */
     655           0 :         buffer[temp.query_len] = '\0';
     656             : 
     657             :         /* Skip loading "sticky" entries */
     658           0 :         if (IS_STICKY(temp.counters))
     659           0 :             continue;
     660             : 
     661             :         /* Store the query text */
     662           0 :         query_offset = pgss->extent;
     663           0 :         if (fwrite(buffer, 1, temp.query_len + 1, qfile) != temp.query_len + 1)
     664           0 :             goto write_error;
     665           0 :         pgss->extent += temp.query_len + 1;
     666             : 
     667             :         /* make the hashtable entry (discards old entries if too many) */
     668           0 :         entry = entry_alloc(&temp.key, query_offset, temp.query_len,
     669             :                             temp.encoding,
     670             :                             false);
     671             : 
     672             :         /* copy in the actual stats */
     673           0 :         entry->counters = temp.counters;
     674             :     }
     675             : 
     676           0 :     pfree(buffer);
     677           0 :     FreeFile(file);
     678           0 :     FreeFile(qfile);
     679             : 
     680             :     /*
     681             :      * Remove the persisted stats file so it's not included in
     682             :      * backups/replication standbys, etc.  A new file will be written on next
     683             :      * shutdown.
     684             :      *
     685             :      * Note: it's okay if the PGSS_TEXT_FILE is included in a basebackup,
     686             :      * because we remove that file on startup; it acts inversely to
     687             :      * PGSS_DUMP_FILE, in that it is only supposed to be around when the
     688             :      * server is running, whereas PGSS_DUMP_FILE is only supposed to be around
     689             :      * when the server is not running.  Leaving the file creates no danger of
     690             :      * a newly restored database having a spurious record of execution costs,
     691             :      * which is what we're really concerned about here.
     692             :      */
     693           0 :     unlink(PGSS_DUMP_FILE);
     694             : 
     695           0 :     return;
     696             : 
     697           0 : read_error:
     698           0 :     ereport(LOG,
     699             :             (errcode_for_file_access(),
     700             :              errmsg("could not read file \"%s\": %m",
     701             :                     PGSS_DUMP_FILE)));
     702           0 :     goto fail;
     703           0 : data_error:
     704           0 :     ereport(LOG,
     705             :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     706             :              errmsg("ignoring invalid data in file \"%s\"",
     707             :                     PGSS_DUMP_FILE)));
     708           0 :     goto fail;
     709           0 : write_error:
     710           0 :     ereport(LOG,
     711             :             (errcode_for_file_access(),
     712             :              errmsg("could not write file \"%s\": %m",
     713             :                     PGSS_TEXT_FILE)));
     714           0 : fail:
     715           0 :     if (buffer)
     716           0 :         pfree(buffer);
     717           0 :     if (file)
     718           0 :         FreeFile(file);
     719           0 :     if (qfile)
     720           0 :         FreeFile(qfile);
     721             :     /* If possible, throw away the bogus file; ignore any error */
     722           0 :     unlink(PGSS_DUMP_FILE);
     723             : 
     724             :     /*
     725             :      * Don't unlink PGSS_TEXT_FILE here; it should always be around while the
     726             :      * server is running with pg_stat_statements enabled
     727             :      */
     728             : }
     729             : 
     730             : /*
     731             :  * shmem_shutdown hook: Dump statistics into file.
     732             :  *
     733             :  * Note: we don't bother with acquiring lock, because there should be no
     734             :  * other processes running when this is called.
     735             :  */
     736             : static void
     737           2 : pgss_shmem_shutdown(int code, Datum arg)
     738             : {
     739             :     FILE       *file;
     740           2 :     char       *qbuffer = NULL;
     741           2 :     Size        qbuffer_size = 0;
     742             :     HASH_SEQ_STATUS hash_seq;
     743             :     int32       num_entries;
     744             :     pgssEntry  *entry;
     745             : 
     746             :     /* Don't try to dump during a crash. */
     747           2 :     if (code)
     748           2 :         return;
     749             : 
     750             :     /* Safety check ... shouldn't get here unless shmem is set up. */
     751           2 :     if (!pgss || !pgss_hash)
     752           0 :         return;
     753             : 
     754             :     /* Don't dump if told not to. */
     755           2 :     if (!pgss_save)
     756           0 :         return;
     757             : 
     758           2 :     file = AllocateFile(PGSS_DUMP_FILE ".tmp", PG_BINARY_W);
     759           2 :     if (file == NULL)
     760           0 :         goto error;
     761             : 
     762           2 :     if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1)
     763           0 :         goto error;
     764           2 :     if (fwrite(&PGSS_PG_MAJOR_VERSION, sizeof(uint32), 1, file) != 1)
     765           0 :         goto error;
     766           2 :     num_entries = hash_get_num_entries(pgss_hash);
     767           2 :     if (fwrite(&num_entries, sizeof(int32), 1, file) != 1)
     768           0 :         goto error;
     769             : 
     770           2 :     qbuffer = qtext_load_file(&qbuffer_size);
     771           2 :     if (qbuffer == NULL)
     772           0 :         goto error;
     773             : 
     774             :     /*
     775             :      * When serializing to disk, we store query texts immediately after their
     776             :      * entry data.  Any orphaned query texts are thereby excluded.
     777             :      */
     778           2 :     hash_seq_init(&hash_seq, pgss_hash);
     779          16 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
     780             :     {
     781          14 :         int         len = entry->query_len;
     782          14 :         char       *qstr = qtext_fetch(entry->query_offset, len,
     783             :                                        qbuffer, qbuffer_size);
     784             : 
     785          14 :         if (qstr == NULL)
     786           0 :             continue;           /* Ignore any entries with bogus texts */
     787             : 
     788          14 :         if (fwrite(entry, sizeof(pgssEntry), 1, file) != 1 ||
     789          14 :             fwrite(qstr, 1, len + 1, file) != len + 1)
     790             :         {
     791             :             /* note: we assume hash_seq_term won't change errno */
     792           0 :             hash_seq_term(&hash_seq);
     793           0 :             goto error;
     794             :         }
     795             :     }
     796             : 
     797           2 :     free(qbuffer);
     798           2 :     qbuffer = NULL;
     799             : 
     800           2 :     if (FreeFile(file))
     801             :     {
     802           0 :         file = NULL;
     803           0 :         goto error;
     804             :     }
     805             : 
     806             :     /*
     807             :      * Rename file into place, so we atomically replace any old one.
     808             :      */
     809           2 :     (void) durable_rename(PGSS_DUMP_FILE ".tmp", PGSS_DUMP_FILE, LOG);
     810             : 
     811             :     /* Unlink query-texts file; it's not needed while shutdown */
     812           2 :     unlink(PGSS_TEXT_FILE);
     813             : 
     814           2 :     return;
     815             : 
     816           0 : error:
     817           0 :     ereport(LOG,
     818             :             (errcode_for_file_access(),
     819             :              errmsg("could not write file \"%s\": %m",
     820             :                     PGSS_DUMP_FILE ".tmp")));
     821           0 :     if (qbuffer)
     822           0 :         free(qbuffer);
     823           0 :     if (file)
     824           0 :         FreeFile(file);
     825           0 :     unlink(PGSS_DUMP_FILE ".tmp");
     826           0 :     unlink(PGSS_TEXT_FILE);
     827             : }
     828             : 
     829             : /*
     830             :  * Post-parse-analysis hook: mark query with a queryId
     831             :  */
     832             : static void
     833         378 : pgss_post_parse_analyze(ParseState *pstate, Query *query)
     834             : {
     835             :     pgssJumbleState jstate;
     836             : 
     837         378 :     if (prev_post_parse_analyze_hook)
     838           0 :         prev_post_parse_analyze_hook(pstate, query);
     839             : 
     840             :     /* Assert we didn't do this already */
     841             :     Assert(query->queryId == UINT64CONST(0));
     842             : 
     843             :     /* Safety check... */
     844         378 :     if (!pgss || !pgss_hash || !pgss_enabled(exec_nested_level))
     845         184 :         return;
     846             : 
     847             :     /*
     848             :      * Utility statements get queryId zero.  We do this even in cases where
     849             :      * the statement contains an optimizable statement for which a queryId
     850             :      * could be derived (such as EXPLAIN or DECLARE CURSOR).  For such cases,
     851             :      * runtime control will first go through ProcessUtility and then the
     852             :      * executor, and we don't want the executor hooks to do anything, since we
     853             :      * are already measuring the statement's costs at the utility level.
     854             :      */
     855         322 :     if (query->utilityStmt)
     856             :     {
     857         128 :         query->queryId = UINT64CONST(0);
     858         128 :         return;
     859             :     }
     860             : 
     861             :     /* Set up workspace for query jumbling */
     862         194 :     jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
     863         194 :     jstate.jumble_len = 0;
     864         194 :     jstate.clocations_buf_size = 32;
     865         194 :     jstate.clocations = (pgssLocationLen *)
     866         194 :         palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
     867         194 :     jstate.clocations_count = 0;
     868         194 :     jstate.highest_extern_param_id = 0;
     869             : 
     870             :     /* Compute query ID and mark the Query node with it */
     871         194 :     JumbleQuery(&jstate, query);
     872         194 :     query->queryId =
     873         194 :         DatumGetUInt64(hash_any_extended(jstate.jumble, jstate.jumble_len, 0));
     874             : 
     875             :     /*
     876             :      * If we are unlucky enough to get a hash of zero, use 1 instead, to
     877             :      * prevent confusion with the utility-statement case.
     878             :      */
     879         194 :     if (query->queryId == UINT64CONST(0))
     880           0 :         query->queryId = UINT64CONST(1);
     881             : 
     882             :     /*
     883             :      * If we were able to identify any ignorable constants, we immediately
     884             :      * create a hash table entry for the query, so that we can record the
     885             :      * normalized form of the query string.  If there were no such constants,
     886             :      * the normalized string would be the same as the query text anyway, so
     887             :      * there's no need for an early entry.
     888             :      */
     889         194 :     if (jstate.clocations_count > 0)
     890         122 :         pgss_store(pstate->p_sourcetext,
     891             :                    query->queryId,
     892             :                    query->stmt_location,
     893             :                    query->stmt_len,
     894             :                    PGSS_INVALID,
     895             :                    0,
     896             :                    0,
     897             :                    NULL,
     898             :                    NULL,
     899             :                    &jstate);
     900             : }
     901             : 
     902             : /*
     903             :  * Planner hook: forward to regular planner, but measure planning time
     904             :  * if needed.
     905             :  */
     906             : static PlannedStmt *
     907         212 : pgss_planner(Query *parse,
     908             :              const char *query_string,
     909             :              int cursorOptions,
     910             :              ParamListInfo boundParams)
     911             : {
     912             :     PlannedStmt *result;
     913             : 
     914             :     /*
     915             :      * We can't process the query if no query_string is provided, as
     916             :      * pgss_store needs it.  We also ignore query without queryid, as it would
     917             :      * be treated as a utility statement, which may not be the case.
     918             :      *
     919             :      * Note that planner_hook can be called from the planner itself, so we
     920             :      * have a specific nesting level for the planner.  However, utility
     921             :      * commands containing optimizable statements can also call the planner,
     922             :      * same for regular DML (for instance for underlying foreign key queries).
     923             :      * So testing the planner nesting level only is not enough to detect real
     924             :      * top level planner call.
     925             :      */
     926         212 :     if (pgss_enabled(plan_nested_level + exec_nested_level)
     927         194 :         && pgss_track_planning && query_string
     928         194 :         && parse->queryId != UINT64CONST(0))
     929         194 :     {
     930             :         instr_time  start;
     931             :         instr_time  duration;
     932             :         BufferUsage bufusage_start,
     933             :                     bufusage;
     934             :         WalUsage    walusage_start,
     935             :                     walusage;
     936             : 
     937             :         /* We need to track buffer usage as the planner can access them. */
     938         194 :         bufusage_start = pgBufferUsage;
     939             : 
     940             :         /*
     941             :          * Similarly the planner could write some WAL records in some cases
     942             :          * (e.g. setting a hint bit with those being WAL-logged)
     943             :          */
     944         194 :         walusage_start = pgWalUsage;
     945         194 :         INSTR_TIME_SET_CURRENT(start);
     946             : 
     947         194 :         plan_nested_level++;
     948         194 :         PG_TRY();
     949             :         {
     950         194 :             if (prev_planner_hook)
     951           0 :                 result = prev_planner_hook(parse, query_string, cursorOptions,
     952             :                                            boundParams);
     953             :             else
     954         194 :                 result = standard_planner(parse, query_string, cursorOptions,
     955             :                                           boundParams);
     956             :         }
     957           0 :         PG_FINALLY();
     958             :         {
     959         194 :             plan_nested_level--;
     960             :         }
     961         194 :         PG_END_TRY();
     962             : 
     963         194 :         INSTR_TIME_SET_CURRENT(duration);
     964         194 :         INSTR_TIME_SUBTRACT(duration, start);
     965             : 
     966             :         /* calc differences of buffer counters. */
     967         194 :         memset(&bufusage, 0, sizeof(BufferUsage));
     968         194 :         BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
     969             : 
     970             :         /* calc differences of WAL counters. */
     971         194 :         memset(&walusage, 0, sizeof(WalUsage));
     972         194 :         WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
     973             : 
     974         194 :         pgss_store(query_string,
     975             :                    parse->queryId,
     976             :                    parse->stmt_location,
     977             :                    parse->stmt_len,
     978             :                    PGSS_PLAN,
     979         194 :                    INSTR_TIME_GET_MILLISEC(duration),
     980             :                    0,
     981             :                    &bufusage,
     982             :                    &walusage,
     983             :                    NULL);
     984             :     }
     985             :     else
     986             :     {
     987          18 :         if (prev_planner_hook)
     988           0 :             result = prev_planner_hook(parse, query_string, cursorOptions,
     989             :                                        boundParams);
     990             :         else
     991          18 :             result = standard_planner(parse, query_string, cursorOptions,
     992             :                                       boundParams);
     993             :     }
     994             : 
     995         212 :     return result;
     996             : }
     997             : 
     998             : /*
     999             :  * ExecutorStart hook: start up tracking if needed
    1000             :  */
    1001             : static void
    1002         212 : pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
    1003             : {
    1004         212 :     if (prev_ExecutorStart)
    1005           0 :         prev_ExecutorStart(queryDesc, eflags);
    1006             :     else
    1007         212 :         standard_ExecutorStart(queryDesc, eflags);
    1008             : 
    1009             :     /*
    1010             :      * If query has queryId zero, don't track it.  This prevents double
    1011             :      * counting of optimizable statements that are directly contained in
    1012             :      * utility statements.
    1013             :      */
    1014         212 :     if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
    1015             :     {
    1016             :         /*
    1017             :          * Set up to track total elapsed time in ExecutorRun.  Make sure the
    1018             :          * space is allocated in the per-query context so it will go away at
    1019             :          * ExecutorEnd.
    1020             :          */
    1021         196 :         if (queryDesc->totaltime == NULL)
    1022             :         {
    1023             :             MemoryContext oldcxt;
    1024             : 
    1025         196 :             oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
    1026         196 :             queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL);
    1027         196 :             MemoryContextSwitchTo(oldcxt);
    1028             :         }
    1029             :     }
    1030         212 : }
    1031             : 
    1032             : /*
    1033             :  * ExecutorRun hook: all we need do is track nesting depth
    1034             :  */
    1035             : static void
    1036         212 : pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count,
    1037             :                  bool execute_once)
    1038             : {
    1039         212 :     exec_nested_level++;
    1040         212 :     PG_TRY();
    1041             :     {
    1042         212 :         if (prev_ExecutorRun)
    1043           0 :             prev_ExecutorRun(queryDesc, direction, count, execute_once);
    1044             :         else
    1045         212 :             standard_ExecutorRun(queryDesc, direction, count, execute_once);
    1046             :     }
    1047           0 :     PG_FINALLY();
    1048             :     {
    1049         212 :         exec_nested_level--;
    1050             :     }
    1051         212 :     PG_END_TRY();
    1052         212 : }
    1053             : 
    1054             : /*
    1055             :  * ExecutorFinish hook: all we need do is track nesting depth
    1056             :  */
    1057             : static void
    1058         212 : pgss_ExecutorFinish(QueryDesc *queryDesc)
    1059             : {
    1060         212 :     exec_nested_level++;
    1061         212 :     PG_TRY();
    1062             :     {
    1063         212 :         if (prev_ExecutorFinish)
    1064           0 :             prev_ExecutorFinish(queryDesc);
    1065             :         else
    1066         212 :             standard_ExecutorFinish(queryDesc);
    1067             :     }
    1068           0 :     PG_FINALLY();
    1069             :     {
    1070         212 :         exec_nested_level--;
    1071             :     }
    1072         212 :     PG_END_TRY();
    1073         212 : }
    1074             : 
    1075             : /*
    1076             :  * ExecutorEnd hook: store results if needed
    1077             :  */
    1078             : static void
    1079         212 : pgss_ExecutorEnd(QueryDesc *queryDesc)
    1080             : {
    1081         212 :     uint64      queryId = queryDesc->plannedstmt->queryId;
    1082             : 
    1083         212 :     if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
    1084         196 :         pgss_enabled(exec_nested_level))
    1085             :     {
    1086             :         /*
    1087             :          * Make sure stats accumulation is done.  (Note: it's okay if several
    1088             :          * levels of hook all do this.)
    1089             :          */
    1090         196 :         InstrEndLoop(queryDesc->totaltime);
    1091             : 
    1092         784 :         pgss_store(queryDesc->sourceText,
    1093             :                    queryId,
    1094         196 :                    queryDesc->plannedstmt->stmt_location,
    1095         196 :                    queryDesc->plannedstmt->stmt_len,
    1096             :                    PGSS_EXEC,
    1097         196 :                    queryDesc->totaltime->total * 1000.0,  /* convert to msec */
    1098         196 :                    queryDesc->estate->es_processed,
    1099         196 :                    &queryDesc->totaltime->bufusage,
    1100         196 :                    &queryDesc->totaltime->walusage,
    1101             :                    NULL);
    1102             :     }
    1103             : 
    1104         212 :     if (prev_ExecutorEnd)
    1105           0 :         prev_ExecutorEnd(queryDesc);
    1106             :     else
    1107         212 :         standard_ExecutorEnd(queryDesc);
    1108         212 : }
    1109             : 
    1110             : /*
    1111             :  * ProcessUtility hook
    1112             :  */
    1113             : static void
    1114         172 : pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
    1115             :                     ProcessUtilityContext context,
    1116             :                     ParamListInfo params, QueryEnvironment *queryEnv,
    1117             :                     DestReceiver *dest, QueryCompletion *qc)
    1118             : {
    1119         172 :     Node       *parsetree = pstmt->utilityStmt;
    1120             : 
    1121             :     /*
    1122             :      * If it's an EXECUTE statement, we don't track it and don't increment the
    1123             :      * nesting level.  This allows the cycles to be charged to the underlying
    1124             :      * PREPARE instead (by the Executor hooks), which is much more useful.
    1125             :      *
    1126             :      * We also don't track execution of PREPARE.  If we did, we would get one
    1127             :      * hash table entry for the PREPARE (with hash calculated from the query
    1128             :      * string), and then a different one with the same query string (but hash
    1129             :      * calculated from the query tree) would be used to accumulate costs of
    1130             :      * ensuing EXECUTEs.  This would be confusing, and inconsistent with other
    1131             :      * cases where planning time is not included at all.
    1132             :      *
    1133             :      * Likewise, we don't track execution of DEALLOCATE.
    1134             :      */
    1135         172 :     if (pgss_track_utility && pgss_enabled(exec_nested_level) &&
    1136          70 :         !IsA(parsetree, ExecuteStmt) &&
    1137          62 :         !IsA(parsetree, PrepareStmt) &&
    1138          60 :         !IsA(parsetree, DeallocateStmt))
    1139          60 :     {
    1140             :         instr_time  start;
    1141             :         instr_time  duration;
    1142             :         uint64      rows;
    1143             :         BufferUsage bufusage_start,
    1144             :                     bufusage;
    1145             :         WalUsage    walusage_start,
    1146             :                     walusage;
    1147             : 
    1148          60 :         bufusage_start = pgBufferUsage;
    1149          60 :         walusage_start = pgWalUsage;
    1150          60 :         INSTR_TIME_SET_CURRENT(start);
    1151             : 
    1152          60 :         exec_nested_level++;
    1153          60 :         PG_TRY();
    1154             :         {
    1155          60 :             if (prev_ProcessUtility)
    1156           0 :                 prev_ProcessUtility(pstmt, queryString,
    1157             :                                     context, params, queryEnv,
    1158             :                                     dest, qc);
    1159             :             else
    1160          60 :                 standard_ProcessUtility(pstmt, queryString,
    1161             :                                         context, params, queryEnv,
    1162             :                                         dest, qc);
    1163             :         }
    1164           0 :         PG_FINALLY();
    1165             :         {
    1166          60 :             exec_nested_level--;
    1167             :         }
    1168          60 :         PG_END_TRY();
    1169             : 
    1170          60 :         INSTR_TIME_SET_CURRENT(duration);
    1171          60 :         INSTR_TIME_SUBTRACT(duration, start);
    1172             : 
    1173          60 :         rows = (qc && qc->commandTag == CMDTAG_COPY) ? qc->nprocessed : 0;
    1174             : 
    1175             :         /* calc differences of buffer counters. */
    1176          60 :         memset(&bufusage, 0, sizeof(BufferUsage));
    1177          60 :         BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
    1178             : 
    1179             :         /* calc differences of WAL counters. */
    1180          60 :         memset(&walusage, 0, sizeof(WalUsage));
    1181          60 :         WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
    1182             : 
    1183          60 :         pgss_store(queryString,
    1184             :                    0,           /* signal that it's a utility stmt */
    1185             :                    pstmt->stmt_location,
    1186             :                    pstmt->stmt_len,
    1187             :                    PGSS_EXEC,
    1188          60 :                    INSTR_TIME_GET_MILLISEC(duration),
    1189             :                    rows,
    1190             :                    &bufusage,
    1191             :                    &walusage,
    1192             :                    NULL);
    1193             :     }
    1194             :     else
    1195             :     {
    1196         112 :         if (prev_ProcessUtility)
    1197           0 :             prev_ProcessUtility(pstmt, queryString,
    1198             :                                 context, params, queryEnv,
    1199             :                                 dest, qc);
    1200             :         else
    1201         112 :             standard_ProcessUtility(pstmt, queryString,
    1202             :                                     context, params, queryEnv,
    1203             :                                     dest, qc);
    1204             :     }
    1205         172 : }
    1206             : 
    1207             : /*
    1208             :  * Given an arbitrarily long query string, produce a hash for the purposes of
    1209             :  * identifying the query, without normalizing constants.  Used when hashing
    1210             :  * utility statements.
    1211             :  */
    1212             : static uint64
    1213          60 : pgss_hash_string(const char *str, int len)
    1214             : {
    1215          60 :     return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
    1216             :                                             len, 0));
    1217             : }
    1218             : 
    1219             : /*
    1220             :  * Store some statistics for a statement.
    1221             :  *
    1222             :  * If queryId is 0 then this is a utility statement and we should compute
    1223             :  * a suitable queryId internally.
    1224             :  *
    1225             :  * If jstate is not NULL then we're trying to create an entry for which
    1226             :  * we have no statistics as yet; we just want to record the normalized
    1227             :  * query string.  total_time, rows, bufusage and walusage are ignored in this
    1228             :  * case.
    1229             :  *
    1230             :  * If kind is PGSS_PLAN or PGSS_EXEC, its value is used as the array position
    1231             :  * for the arrays in the Counters field.
    1232             :  */
    1233             : static void
    1234         572 : pgss_store(const char *query, uint64 queryId,
    1235             :            int query_location, int query_len,
    1236             :            pgssStoreKind kind,
    1237             :            double total_time, uint64 rows,
    1238             :            const BufferUsage *bufusage,
    1239             :            const WalUsage *walusage,
    1240             :            pgssJumbleState *jstate)
    1241             : {
    1242             :     pgssHashKey key;
    1243             :     pgssEntry  *entry;
    1244         572 :     char       *norm_query = NULL;
    1245         572 :     int         encoding = GetDatabaseEncoding();
    1246             : 
    1247             :     Assert(query != NULL);
    1248             : 
    1249             :     /* Safety check... */
    1250         572 :     if (!pgss || !pgss_hash)
    1251           0 :         return;
    1252             : 
    1253             :     /*
    1254             :      * Confine our attention to the relevant part of the string, if the query
    1255             :      * is a portion of a multi-statement source string.
    1256             :      *
    1257             :      * First apply starting offset, unless it's -1 (unknown).
    1258             :      */
    1259         572 :     if (query_location >= 0)
    1260             :     {
    1261             :         Assert(query_location <= strlen(query));
    1262         572 :         query += query_location;
    1263             :         /* Length of 0 (or -1) means "rest of string" */
    1264         572 :         if (query_len <= 0)
    1265          48 :             query_len = strlen(query);
    1266             :         else
    1267             :             Assert(query_len <= strlen(query));
    1268             :     }
    1269             :     else
    1270             :     {
    1271             :         /* If query location is unknown, distrust query_len as well */
    1272           0 :         query_location = 0;
    1273           0 :         query_len = strlen(query);
    1274             :     }
    1275             : 
    1276             :     /*
    1277             :      * Discard leading and trailing whitespace, too.  Use scanner_isspace()
    1278             :      * not libc's isspace(), because we want to match the lexer's behavior.
    1279             :      */
    1280         692 :     while (query_len > 0 && scanner_isspace(query[0]))
    1281         120 :         query++, query_location++, query_len--;
    1282         692 :     while (query_len > 0 && scanner_isspace(query[query_len - 1]))
    1283         120 :         query_len--;
    1284             : 
    1285             :     /*
    1286             :      * For utility statements, we just hash the query string to get an ID.
    1287             :      */
    1288         572 :     if (queryId == UINT64CONST(0))
    1289             :     {
    1290          60 :         queryId = pgss_hash_string(query, query_len);
    1291             : 
    1292             :         /*
    1293             :          * If we are unlucky enough to get a hash of zero(invalid), use
    1294             :          * queryID as 2 instead, queryID 1 is already in use for normal
    1295             :          * statements.
    1296             :          */
    1297          60 :         if (queryId == UINT64CONST(0))
    1298           0 :             queryId = UINT64CONST(2);
    1299             :     }
    1300             : 
    1301             :     /* Set up key for hashtable search */
    1302         572 :     key.userid = GetUserId();
    1303         572 :     key.dbid = MyDatabaseId;
    1304         572 :     key.queryid = queryId;
    1305             : 
    1306             :     /* Lookup the hash table entry with shared lock. */
    1307         572 :     LWLockAcquire(pgss->lock, LW_SHARED);
    1308             : 
    1309         572 :     entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
    1310             : 
    1311             :     /* Create new entry, if not present */
    1312         572 :     if (!entry)
    1313             :     {
    1314             :         Size        query_offset;
    1315             :         int         gc_count;
    1316             :         bool        stored;
    1317             :         bool        do_gc;
    1318             : 
    1319             :         /*
    1320             :          * Create a new, normalized query string if caller asked.  We don't
    1321             :          * need to hold the lock while doing this work.  (Note: in any case,
    1322             :          * it's possible that someone else creates a duplicate hashtable entry
    1323             :          * in the interval where we don't hold the lock below.  That case is
    1324             :          * handled by entry_alloc.)
    1325             :          */
    1326         202 :         if (jstate)
    1327             :         {
    1328          80 :             LWLockRelease(pgss->lock);
    1329          80 :             norm_query = generate_normalized_query(jstate, query,
    1330             :                                                    query_location,
    1331             :                                                    &query_len,
    1332             :                                                    encoding);
    1333          80 :             LWLockAcquire(pgss->lock, LW_SHARED);
    1334             :         }
    1335             : 
    1336             :         /* Append new query text to file with only shared lock held */
    1337         202 :         stored = qtext_store(norm_query ? norm_query : query, query_len,
    1338             :                              &query_offset, &gc_count);
    1339             : 
    1340             :         /*
    1341             :          * Determine whether we need to garbage collect external query texts
    1342             :          * while the shared lock is still held.  This micro-optimization
    1343             :          * avoids taking the time to decide this while holding exclusive lock.
    1344             :          */
    1345         202 :         do_gc = need_gc_qtexts();
    1346             : 
    1347             :         /* Need exclusive lock to make a new hashtable entry - promote */
    1348         202 :         LWLockRelease(pgss->lock);
    1349         202 :         LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
    1350             : 
    1351             :         /*
    1352             :          * A garbage collection may have occurred while we weren't holding the
    1353             :          * lock.  In the unlikely event that this happens, the query text we
    1354             :          * stored above will have been garbage collected, so write it again.
    1355             :          * This should be infrequent enough that doing it while holding
    1356             :          * exclusive lock isn't a performance problem.
    1357             :          */
    1358         202 :         if (!stored || pgss->gc_count != gc_count)
    1359           0 :             stored = qtext_store(norm_query ? norm_query : query, query_len,
    1360             :                                  &query_offset, NULL);
    1361             : 
    1362             :         /* If we failed to write to the text file, give up */
    1363         202 :         if (!stored)
    1364           0 :             goto done;
    1365             : 
    1366             :         /* OK to create a new hashtable entry */
    1367         202 :         entry = entry_alloc(&key, query_offset, query_len, encoding,
    1368             :                             jstate != NULL);
    1369             : 
    1370             :         /* If needed, perform garbage collection while exclusive lock held */
    1371         202 :         if (do_gc)
    1372           0 :             gc_qtexts();
    1373             :     }
    1374             : 
    1375             :     /* Increment the counts, except when jstate is not NULL */
    1376         572 :     if (!jstate)
    1377             :     {
    1378             :         /*
    1379             :          * Grab the spinlock while updating the counters (see comment about
    1380             :          * locking rules at the head of the file)
    1381             :          */
    1382         450 :         volatile pgssEntry *e = (volatile pgssEntry *) entry;
    1383             : 
    1384             :         Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
    1385             : 
    1386         450 :         SpinLockAcquire(&e->mutex);
    1387             : 
    1388             :         /* "Unstick" entry if it was previously sticky */
    1389         450 :         if (IS_STICKY(e->counters))
    1390         200 :             e->counters.usage = USAGE_INIT;
    1391             : 
    1392         450 :         e->counters.calls[kind] += 1;
    1393         450 :         e->counters.total_time[kind] += total_time;
    1394             : 
    1395         450 :         if (e->counters.calls[kind] == 1)
    1396             :         {
    1397         330 :             e->counters.min_time[kind] = total_time;
    1398         330 :             e->counters.max_time[kind] = total_time;
    1399         330 :             e->counters.mean_time[kind] = total_time;
    1400             :         }
    1401             :         else
    1402             :         {
    1403             :             /*
    1404             :              * Welford's method for accurately computing variance. See
    1405             :              * <http://www.johndcook.com/blog/standard_deviation/>
    1406             :              */
    1407         120 :             double      old_mean = e->counters.mean_time[kind];
    1408             : 
    1409         120 :             e->counters.mean_time[kind] +=
    1410         120 :                 (total_time - old_mean) / e->counters.calls[kind];
    1411         120 :             e->counters.sum_var_time[kind] +=
    1412         120 :                 (total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
    1413             : 
    1414             :             /* calculate min and max time */
    1415         120 :             if (e->counters.min_time[kind] > total_time)
    1416          82 :                 e->counters.min_time[kind] = total_time;
    1417         120 :             if (e->counters.max_time[kind] < total_time)
    1418          16 :                 e->counters.max_time[kind] = total_time;
    1419             :         }
    1420         450 :         e->counters.rows += rows;
    1421         450 :         e->counters.shared_blks_hit += bufusage->shared_blks_hit;
    1422         450 :         e->counters.shared_blks_read += bufusage->shared_blks_read;
    1423         450 :         e->counters.shared_blks_dirtied += bufusage->shared_blks_dirtied;
    1424         450 :         e->counters.shared_blks_written += bufusage->shared_blks_written;
    1425         450 :         e->counters.local_blks_hit += bufusage->local_blks_hit;
    1426         450 :         e->counters.local_blks_read += bufusage->local_blks_read;
    1427         450 :         e->counters.local_blks_dirtied += bufusage->local_blks_dirtied;
    1428         450 :         e->counters.local_blks_written += bufusage->local_blks_written;
    1429         450 :         e->counters.temp_blks_read += bufusage->temp_blks_read;
    1430         450 :         e->counters.temp_blks_written += bufusage->temp_blks_written;
    1431         450 :         e->counters.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time);
    1432         450 :         e->counters.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time);
    1433         450 :         e->counters.usage += USAGE_EXEC(total_time);
    1434         450 :         e->counters.wal_records += walusage->wal_records;
    1435         450 :         e->counters.wal_fpi += walusage->wal_fpi;
    1436         450 :         e->counters.wal_bytes += walusage->wal_bytes;
    1437             : 
    1438         450 :         SpinLockRelease(&e->mutex);
    1439             :     }
    1440             : 
    1441         122 : done:
    1442         572 :     LWLockRelease(pgss->lock);
    1443             : 
    1444             :     /* We postpone this clean-up until we're out of the lock */
    1445         572 :     if (norm_query)
    1446          80 :         pfree(norm_query);
    1447             : }
    1448             : 
    1449             : /*
    1450             :  * Reset statement statistics corresponding to userid, dbid, and queryid.
    1451             :  */
    1452             : Datum
    1453          30 : pg_stat_statements_reset_1_7(PG_FUNCTION_ARGS)
    1454             : {
    1455             :     Oid         userid;
    1456             :     Oid         dbid;
    1457             :     uint64      queryid;
    1458             : 
    1459          30 :     userid = PG_GETARG_OID(0);
    1460          30 :     dbid = PG_GETARG_OID(1);
    1461          30 :     queryid = (uint64) PG_GETARG_INT64(2);
    1462             : 
    1463          30 :     entry_reset(userid, dbid, queryid);
    1464             : 
    1465          30 :     PG_RETURN_VOID();
    1466             : }
    1467             : 
    1468             : /*
    1469             :  * Reset statement statistics.
    1470             :  */
    1471             : Datum
    1472           0 : pg_stat_statements_reset(PG_FUNCTION_ARGS)
    1473             : {
    1474           0 :     entry_reset(0, 0, 0);
    1475             : 
    1476           0 :     PG_RETURN_VOID();
    1477             : }
    1478             : 
    1479             : /* Number of output arguments (columns) for various API versions */
    1480             : #define PG_STAT_STATEMENTS_COLS_V1_0    14
    1481             : #define PG_STAT_STATEMENTS_COLS_V1_1    18
    1482             : #define PG_STAT_STATEMENTS_COLS_V1_2    19
    1483             : #define PG_STAT_STATEMENTS_COLS_V1_3    23
    1484             : #define PG_STAT_STATEMENTS_COLS_V1_8    32
    1485             : #define PG_STAT_STATEMENTS_COLS         32  /* maximum of above */
    1486             : 
    1487             : /*
    1488             :  * Retrieve statement statistics.
    1489             :  *
    1490             :  * The SQL API of this function has changed multiple times, and will likely
    1491             :  * do so again in future.  To support the case where a newer version of this
    1492             :  * loadable module is being used with an old SQL declaration of the function,
    1493             :  * we continue to support the older API versions.  For 1.2 and later, the
    1494             :  * expected API version is identified by embedding it in the C name of the
    1495             :  * function.  Unfortunately we weren't bright enough to do that for 1.1.
    1496             :  */
    1497             : Datum
    1498          34 : pg_stat_statements_1_8(PG_FUNCTION_ARGS)
    1499             : {
    1500          34 :     bool        showtext = PG_GETARG_BOOL(0);
    1501             : 
    1502          34 :     pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
    1503             : 
    1504          34 :     return (Datum) 0;
    1505             : }
    1506             : 
    1507             : Datum
    1508           0 : pg_stat_statements_1_3(PG_FUNCTION_ARGS)
    1509             : {
    1510           0 :     bool        showtext = PG_GETARG_BOOL(0);
    1511             : 
    1512           0 :     pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
    1513             : 
    1514           0 :     return (Datum) 0;
    1515             : }
    1516             : 
    1517             : Datum
    1518           0 : pg_stat_statements_1_2(PG_FUNCTION_ARGS)
    1519             : {
    1520           0 :     bool        showtext = PG_GETARG_BOOL(0);
    1521             : 
    1522           0 :     pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
    1523             : 
    1524           0 :     return (Datum) 0;
    1525             : }
    1526             : 
    1527             : /*
    1528             :  * Legacy entry point for pg_stat_statements() API versions 1.0 and 1.1.
    1529             :  * This can be removed someday, perhaps.
    1530             :  */
    1531             : Datum
    1532           0 : pg_stat_statements(PG_FUNCTION_ARGS)
    1533             : {
    1534             :     /* If it's really API 1.1, we'll figure that out below */
    1535           0 :     pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
    1536             : 
    1537           0 :     return (Datum) 0;
    1538             : }
    1539             : 
    1540             : /* Common code for all versions of pg_stat_statements() */
    1541             : static void
    1542          34 : pg_stat_statements_internal(FunctionCallInfo fcinfo,
    1543             :                             pgssVersion api_version,
    1544             :                             bool showtext)
    1545             : {
    1546          34 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    1547             :     TupleDesc   tupdesc;
    1548             :     Tuplestorestate *tupstore;
    1549             :     MemoryContext per_query_ctx;
    1550             :     MemoryContext oldcontext;
    1551          34 :     Oid         userid = GetUserId();
    1552          34 :     bool        is_allowed_role = false;
    1553          34 :     char       *qbuffer = NULL;
    1554          34 :     Size        qbuffer_size = 0;
    1555          34 :     Size        extent = 0;
    1556          34 :     int         gc_count = 0;
    1557             :     HASH_SEQ_STATUS hash_seq;
    1558             :     pgssEntry  *entry;
    1559             : 
    1560             :     /* Superusers or members of pg_read_all_stats members are allowed */
    1561          34 :     is_allowed_role = is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS);
    1562             : 
    1563             :     /* hash table must exist already */
    1564          34 :     if (!pgss || !pgss_hash)
    1565           0 :         ereport(ERROR,
    1566             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1567             :                  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
    1568             : 
    1569             :     /* check to see if caller supports us returning a tuplestore */
    1570          34 :     if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
    1571           0 :         ereport(ERROR,
    1572             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1573             :                  errmsg("set-valued function called in context that cannot accept a set")));
    1574          34 :     if (!(rsinfo->allowedModes & SFRM_Materialize))
    1575           0 :         ereport(ERROR,
    1576             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1577             :                  errmsg("materialize mode required, but it is not allowed in this context")));
    1578             : 
    1579             :     /* Switch into long-lived context to construct returned data structures */
    1580          34 :     per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    1581          34 :     oldcontext = MemoryContextSwitchTo(per_query_ctx);
    1582             : 
    1583             :     /* Build a tuple descriptor for our result type */
    1584          34 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
    1585           0 :         elog(ERROR, "return type must be a row type");
    1586             : 
    1587             :     /*
    1588             :      * Check we have the expected number of output arguments.  Aside from
    1589             :      * being a good safety check, we need a kluge here to detect API version
    1590             :      * 1.1, which was wedged into the code in an ill-considered way.
    1591             :      */
    1592          34 :     switch (tupdesc->natts)
    1593             :     {
    1594           0 :         case PG_STAT_STATEMENTS_COLS_V1_0:
    1595           0 :             if (api_version != PGSS_V1_0)
    1596           0 :                 elog(ERROR, "incorrect number of output arguments");
    1597           0 :             break;
    1598           0 :         case PG_STAT_STATEMENTS_COLS_V1_1:
    1599             :             /* pg_stat_statements() should have told us 1.0 */
    1600           0 :             if (api_version != PGSS_V1_0)
    1601           0 :                 elog(ERROR, "incorrect number of output arguments");
    1602           0 :             api_version = PGSS_V1_1;
    1603           0 :             break;
    1604           0 :         case PG_STAT_STATEMENTS_COLS_V1_2:
    1605           0 :             if (api_version != PGSS_V1_2)
    1606           0 :                 elog(ERROR, "incorrect number of output arguments");
    1607           0 :             break;
    1608           0 :         case PG_STAT_STATEMENTS_COLS_V1_3:
    1609           0 :             if (api_version != PGSS_V1_3)
    1610           0 :                 elog(ERROR, "incorrect number of output arguments");
    1611           0 :             break;
    1612          34 :         case PG_STAT_STATEMENTS_COLS_V1_8:
    1613          34 :             if (api_version != PGSS_V1_8)
    1614           0 :                 elog(ERROR, "incorrect number of output arguments");
    1615          34 :             break;
    1616           0 :         default:
    1617           0 :             elog(ERROR, "incorrect number of output arguments");
    1618             :     }
    1619             : 
    1620          34 :     tupstore = tuplestore_begin_heap(true, false, work_mem);
    1621          34 :     rsinfo->returnMode = SFRM_Materialize;
    1622          34 :     rsinfo->setResult = tupstore;
    1623          34 :     rsinfo->setDesc = tupdesc;
    1624             : 
    1625          34 :     MemoryContextSwitchTo(oldcontext);
    1626             : 
    1627             :     /*
    1628             :      * We'd like to load the query text file (if needed) while not holding any
    1629             :      * lock on pgss->lock.  In the worst case we'll have to do this again
    1630             :      * after we have the lock, but it's unlikely enough to make this a win
    1631             :      * despite occasional duplicated work.  We need to reload if anybody
    1632             :      * writes to the file (either a retail qtext_store(), or a garbage
    1633             :      * collection) between this point and where we've gotten shared lock.  If
    1634             :      * a qtext_store is actually in progress when we look, we might as well
    1635             :      * skip the speculative load entirely.
    1636             :      */
    1637          34 :     if (showtext)
    1638             :     {
    1639             :         int         n_writers;
    1640             : 
    1641             :         /* Take the mutex so we can examine variables */
    1642             :         {
    1643          34 :             volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
    1644             : 
    1645          34 :             SpinLockAcquire(&s->mutex);
    1646          34 :             extent = s->extent;
    1647          34 :             n_writers = s->n_writers;
    1648          34 :             gc_count = s->gc_count;
    1649          34 :             SpinLockRelease(&s->mutex);
    1650             :         }
    1651             : 
    1652             :         /* No point in loading file now if there are active writers */
    1653          34 :         if (n_writers == 0)
    1654          34 :             qbuffer = qtext_load_file(&qbuffer_size);
    1655             :     }
    1656             : 
    1657             :     /*
    1658             :      * Get shared lock, load or reload the query text file if we must, and
    1659             :      * iterate over the hashtable entries.
    1660             :      *
    1661             :      * With a large hash table, we might be holding the lock rather longer
    1662             :      * than one could wish.  However, this only blocks creation of new hash
    1663             :      * table entries, and the larger the hash table the less likely that is to
    1664             :      * be needed.  So we can hope this is okay.  Perhaps someday we'll decide
    1665             :      * we need to partition the hash table to limit the time spent holding any
    1666             :      * one lock.
    1667             :      */
    1668          34 :     LWLockAcquire(pgss->lock, LW_SHARED);
    1669             : 
    1670          34 :     if (showtext)
    1671             :     {
    1672             :         /*
    1673             :          * Here it is safe to examine extent and gc_count without taking the
    1674             :          * mutex.  Note that although other processes might change
    1675             :          * pgss->extent just after we look at it, the strings they then write
    1676             :          * into the file cannot yet be referenced in the hashtable, so we
    1677             :          * don't care whether we see them or not.
    1678             :          *
    1679             :          * If qtext_load_file fails, we just press on; we'll return NULL for
    1680             :          * every query text.
    1681             :          */
    1682          34 :         if (qbuffer == NULL ||
    1683          34 :             pgss->extent != extent ||
    1684          34 :             pgss->gc_count != gc_count)
    1685             :         {
    1686           0 :             if (qbuffer)
    1687           0 :                 free(qbuffer);
    1688           0 :             qbuffer = qtext_load_file(&qbuffer_size);
    1689             :         }
    1690             :     }
    1691             : 
    1692          34 :     hash_seq_init(&hash_seq, pgss_hash);
    1693         338 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    1694             :     {
    1695             :         Datum       values[PG_STAT_STATEMENTS_COLS];
    1696             :         bool        nulls[PG_STAT_STATEMENTS_COLS];
    1697         304 :         int         i = 0;
    1698             :         Counters    tmp;
    1699             :         double      stddev;
    1700         304 :         int64       queryid = entry->key.queryid;
    1701             : 
    1702         304 :         memset(values, 0, sizeof(values));
    1703         304 :         memset(nulls, 0, sizeof(nulls));
    1704             : 
    1705         304 :         values[i++] = ObjectIdGetDatum(entry->key.userid);
    1706         304 :         values[i++] = ObjectIdGetDatum(entry->key.dbid);
    1707             : 
    1708         304 :         if (is_allowed_role || entry->key.userid == userid)
    1709             :         {
    1710         304 :             if (api_version >= PGSS_V1_2)
    1711         304 :                 values[i++] = Int64GetDatumFast(queryid);
    1712             : 
    1713         608 :             if (showtext)
    1714             :             {
    1715         304 :                 char       *qstr = qtext_fetch(entry->query_offset,
    1716             :                                                entry->query_len,
    1717             :                                                qbuffer,
    1718             :                                                qbuffer_size);
    1719             : 
    1720         304 :                 if (qstr)
    1721             :                 {
    1722             :                     char       *enc;
    1723             : 
    1724         304 :                     enc = pg_any_to_server(qstr,
    1725             :                                            entry->query_len,
    1726             :                                            entry->encoding);
    1727             : 
    1728         304 :                     values[i++] = CStringGetTextDatum(enc);
    1729             : 
    1730         304 :                     if (enc != qstr)
    1731           0 :                         pfree(enc);
    1732             :                 }
    1733             :                 else
    1734             :                 {
    1735             :                     /* Just return a null if we fail to find the text */
    1736           0 :                     nulls[i++] = true;
    1737             :                 }
    1738             :             }
    1739             :             else
    1740             :             {
    1741             :                 /* Query text not requested */
    1742           0 :                 nulls[i++] = true;
    1743             :             }
    1744             :         }
    1745             :         else
    1746             :         {
    1747             :             /* Don't show queryid */
    1748           0 :             if (api_version >= PGSS_V1_2)
    1749           0 :                 nulls[i++] = true;
    1750             : 
    1751             :             /*
    1752             :              * Don't show query text, but hint as to the reason for not doing
    1753             :              * so if it was requested
    1754             :              */
    1755           0 :             if (showtext)
    1756           0 :                 values[i++] = CStringGetTextDatum("<insufficient privilege>");
    1757             :             else
    1758           0 :                 nulls[i++] = true;
    1759             :         }
    1760             : 
    1761             :         /* copy counters to a local variable to keep locking time short */
    1762             :         {
    1763         304 :             volatile pgssEntry *e = (volatile pgssEntry *) entry;
    1764             : 
    1765         304 :             SpinLockAcquire(&e->mutex);
    1766         304 :             tmp = e->counters;
    1767         304 :             SpinLockRelease(&e->mutex);
    1768             :         }
    1769             : 
    1770             :         /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */
    1771         304 :         if (IS_STICKY(tmp))
    1772           2 :             continue;
    1773             : 
    1774             :         /* Note that we rely on PGSS_PLAN being 0 and PGSS_EXEC being 1. */
    1775         906 :         for (int kind = 0; kind < PGSS_NUMKIND; kind++)
    1776             :         {
    1777         604 :             if (kind == PGSS_EXEC || api_version >= PGSS_V1_8)
    1778             :             {
    1779         604 :                 values[i++] = Int64GetDatumFast(tmp.calls[kind]);
    1780         604 :                 values[i++] = Float8GetDatumFast(tmp.total_time[kind]);
    1781             :             }
    1782             : 
    1783         604 :             if ((kind == PGSS_EXEC && api_version >= PGSS_V1_3) ||
    1784             :                 api_version >= PGSS_V1_8)
    1785             :             {
    1786         604 :                 values[i++] = Float8GetDatumFast(tmp.min_time[kind]);
    1787         604 :                 values[i++] = Float8GetDatumFast(tmp.max_time[kind]);
    1788         604 :                 values[i++] = Float8GetDatumFast(tmp.mean_time[kind]);
    1789             : 
    1790             :                 /*
    1791             :                  * Note we are calculating the population variance here, not
    1792             :                  * the sample variance, as we have data for the whole
    1793             :                  * population, so Bessel's correction is not used, and we
    1794             :                  * don't divide by tmp.calls - 1.
    1795             :                  */
    1796         604 :                 if (tmp.calls[kind] > 1)
    1797          98 :                     stddev = sqrt(tmp.sum_var_time[kind] / tmp.calls[kind]);
    1798             :                 else
    1799         506 :                     stddev = 0.0;
    1800         604 :                 values[i++] = Float8GetDatumFast(stddev);
    1801             :             }
    1802             :         }
    1803         302 :         values[i++] = Int64GetDatumFast(tmp.rows);
    1804         302 :         values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
    1805         302 :         values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
    1806         302 :         if (api_version >= PGSS_V1_1)
    1807         302 :             values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied);
    1808         302 :         values[i++] = Int64GetDatumFast(tmp.shared_blks_written);
    1809         302 :         values[i++] = Int64GetDatumFast(tmp.local_blks_hit);
    1810         302 :         values[i++] = Int64GetDatumFast(tmp.local_blks_read);
    1811         302 :         if (api_version >= PGSS_V1_1)
    1812         302 :             values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied);
    1813         302 :         values[i++] = Int64GetDatumFast(tmp.local_blks_written);
    1814         302 :         values[i++] = Int64GetDatumFast(tmp.temp_blks_read);
    1815         302 :         values[i++] = Int64GetDatumFast(tmp.temp_blks_written);
    1816         302 :         if (api_version >= PGSS_V1_1)
    1817             :         {
    1818         302 :             values[i++] = Float8GetDatumFast(tmp.blk_read_time);
    1819         302 :             values[i++] = Float8GetDatumFast(tmp.blk_write_time);
    1820             :         }
    1821         302 :         if (api_version >= PGSS_V1_8)
    1822             :         {
    1823             :             char        buf[256];
    1824             :             Datum       wal_bytes;
    1825             : 
    1826         302 :             values[i++] = Int64GetDatumFast(tmp.wal_records);
    1827         302 :             values[i++] = Int64GetDatumFast(tmp.wal_fpi);
    1828             : 
    1829         302 :             snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.wal_bytes);
    1830             : 
    1831             :             /* Convert to numeric. */
    1832         302 :             wal_bytes = DirectFunctionCall3(numeric_in,
    1833             :                                             CStringGetDatum(buf),
    1834             :                                             ObjectIdGetDatum(0),
    1835             :                                             Int32GetDatum(-1));
    1836         302 :             values[i++] = wal_bytes;
    1837             :         }
    1838             : 
    1839             :         Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
    1840             :                      api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
    1841             :                      api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
    1842             :                      api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
    1843             :                      api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
    1844             :                      -1 /* fail if you forget to update this assert */ ));
    1845             : 
    1846         302 :         tuplestore_putvalues(tupstore, tupdesc, values, nulls);
    1847             :     }
    1848             : 
    1849             :     /* clean up and return the tuplestore */
    1850          34 :     LWLockRelease(pgss->lock);
    1851             : 
    1852          34 :     if (qbuffer)
    1853          34 :         free(qbuffer);
    1854             : 
    1855             :     tuplestore_donestoring(tupstore);
    1856          34 : }
    1857             : 
    1858             : /*
    1859             :  * Estimate shared memory space needed.
    1860             :  */
    1861             : static Size
    1862           2 : pgss_memsize(void)
    1863             : {
    1864             :     Size        size;
    1865             : 
    1866           2 :     size = MAXALIGN(sizeof(pgssSharedState));
    1867           2 :     size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
    1868             : 
    1869           2 :     return size;
    1870             : }
    1871             : 
    1872             : /*
    1873             :  * Allocate a new hashtable entry.
    1874             :  * caller must hold an exclusive lock on pgss->lock
    1875             :  *
    1876             :  * "query" need not be null-terminated; we rely on query_len instead
    1877             :  *
    1878             :  * If "sticky" is true, make the new entry artificially sticky so that it will
    1879             :  * probably still be there when the query finishes execution.  We do this by
    1880             :  * giving it a median usage value rather than the normal value.  (Strictly
    1881             :  * speaking, query strings are normalized on a best effort basis, though it
    1882             :  * would be difficult to demonstrate this even under artificial conditions.)
    1883             :  *
    1884             :  * Note: despite needing exclusive lock, it's not an error for the target
    1885             :  * entry to already exist.  This is because pgss_store releases and
    1886             :  * reacquires lock after failing to find a match; so someone else could
    1887             :  * have made the entry while we waited to get exclusive lock.
    1888             :  */
    1889             : static pgssEntry *
    1890         202 : entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding,
    1891             :             bool sticky)
    1892             : {
    1893             :     pgssEntry  *entry;
    1894             :     bool        found;
    1895             : 
    1896             :     /* Make space if needed */
    1897         202 :     while (hash_get_num_entries(pgss_hash) >= pgss_max)
    1898           0 :         entry_dealloc();
    1899             : 
    1900             :     /* Find or create an entry with desired hash code */
    1901         202 :     entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
    1902             : 
    1903         202 :     if (!found)
    1904             :     {
    1905             :         /* New entry, initialize it */
    1906             : 
    1907             :         /* reset the statistics */
    1908         202 :         memset(&entry->counters, 0, sizeof(Counters));
    1909             :         /* set the appropriate initial usage count */
    1910         202 :         entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
    1911             :         /* re-initialize the mutex each time ... we assume no one using it */
    1912         202 :         SpinLockInit(&entry->mutex);
    1913             :         /* ... and don't forget the query text metadata */
    1914             :         Assert(query_len >= 0);
    1915         202 :         entry->query_offset = query_offset;
    1916         202 :         entry->query_len = query_len;
    1917         202 :         entry->encoding = encoding;
    1918             :     }
    1919             : 
    1920         202 :     return entry;
    1921             : }
    1922             : 
    1923             : /*
    1924             :  * qsort comparator for sorting into increasing usage order
    1925             :  */
    1926             : static int
    1927           0 : entry_cmp(const void *lhs, const void *rhs)
    1928             : {
    1929           0 :     double      l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
    1930           0 :     double      r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
    1931             : 
    1932           0 :     if (l_usage < r_usage)
    1933           0 :         return -1;
    1934           0 :     else if (l_usage > r_usage)
    1935           0 :         return +1;
    1936             :     else
    1937           0 :         return 0;
    1938             : }
    1939             : 
    1940             : /*
    1941             :  * Deallocate least-used entries.
    1942             :  *
    1943             :  * Caller must hold an exclusive lock on pgss->lock.
    1944             :  */
    1945             : static void
    1946           0 : entry_dealloc(void)
    1947             : {
    1948             :     HASH_SEQ_STATUS hash_seq;
    1949             :     pgssEntry **entries;
    1950             :     pgssEntry  *entry;
    1951             :     int         nvictims;
    1952             :     int         i;
    1953             :     Size        tottextlen;
    1954             :     int         nvalidtexts;
    1955             : 
    1956             :     /*
    1957             :      * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
    1958             :      * While we're scanning the table, apply the decay factor to the usage
    1959             :      * values, and update the mean query length.
    1960             :      *
    1961             :      * Note that the mean query length is almost immediately obsolete, since
    1962             :      * we compute it before not after discarding the least-used entries.
    1963             :      * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
    1964             :      * making two passes to get a more current result.  Likewise, the new
    1965             :      * cur_median_usage includes the entries we're about to zap.
    1966             :      */
    1967             : 
    1968           0 :     entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
    1969             : 
    1970           0 :     i = 0;
    1971           0 :     tottextlen = 0;
    1972           0 :     nvalidtexts = 0;
    1973             : 
    1974           0 :     hash_seq_init(&hash_seq, pgss_hash);
    1975           0 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    1976             :     {
    1977           0 :         entries[i++] = entry;
    1978             :         /* "Sticky" entries get a different usage decay rate. */
    1979           0 :         if (IS_STICKY(entry->counters))
    1980           0 :             entry->counters.usage *= STICKY_DECREASE_FACTOR;
    1981             :         else
    1982           0 :             entry->counters.usage *= USAGE_DECREASE_FACTOR;
    1983             :         /* In the mean length computation, ignore dropped texts. */
    1984           0 :         if (entry->query_len >= 0)
    1985             :         {
    1986           0 :             tottextlen += entry->query_len + 1;
    1987           0 :             nvalidtexts++;
    1988             :         }
    1989             :     }
    1990             : 
    1991             :     /* Sort into increasing order by usage */
    1992           0 :     qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
    1993             : 
    1994             :     /* Record the (approximate) median usage */
    1995           0 :     if (i > 0)
    1996           0 :         pgss->cur_median_usage = entries[i / 2]->counters.usage;
    1997             :     /* Record the mean query length */
    1998           0 :     if (nvalidtexts > 0)
    1999           0 :         pgss->mean_query_len = tottextlen / nvalidtexts;
    2000             :     else
    2001           0 :         pgss->mean_query_len = ASSUMED_LENGTH_INIT;
    2002             : 
    2003             :     /* Now zap an appropriate fraction of lowest-usage entries */
    2004           0 :     nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
    2005           0 :     nvictims = Min(nvictims, i);
    2006             : 
    2007           0 :     for (i = 0; i < nvictims; i++)
    2008             :     {
    2009           0 :         hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
    2010             :     }
    2011             : 
    2012           0 :     pfree(entries);
    2013           0 : }
    2014             : 
    2015             : /*
    2016             :  * Given a query string (not necessarily null-terminated), allocate a new
    2017             :  * entry in the external query text file and store the string there.
    2018             :  *
    2019             :  * If successful, returns true, and stores the new entry's offset in the file
    2020             :  * into *query_offset.  Also, if gc_count isn't NULL, *gc_count is set to the
    2021             :  * number of garbage collections that have occurred so far.
    2022             :  *
    2023             :  * On failure, returns false.
    2024             :  *
    2025             :  * At least a shared lock on pgss->lock must be held by the caller, so as
    2026             :  * to prevent a concurrent garbage collection.  Share-lock-holding callers
    2027             :  * should pass a gc_count pointer to obtain the number of garbage collections,
    2028             :  * so that they can recheck the count after obtaining exclusive lock to
    2029             :  * detect whether a garbage collection occurred (and removed this entry).
    2030             :  */
    2031             : static bool
    2032         202 : qtext_store(const char *query, int query_len,
    2033             :             Size *query_offset, int *gc_count)
    2034             : {
    2035             :     Size        off;
    2036             :     int         fd;
    2037             : 
    2038             :     /*
    2039             :      * We use a spinlock to protect extent/n_writers/gc_count, so that
    2040             :      * multiple processes may execute this function concurrently.
    2041             :      */
    2042             :     {
    2043         202 :         volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
    2044             : 
    2045         202 :         SpinLockAcquire(&s->mutex);
    2046         202 :         off = s->extent;
    2047         202 :         s->extent += query_len + 1;
    2048         202 :         s->n_writers++;
    2049         202 :         if (gc_count)
    2050         202 :             *gc_count = s->gc_count;
    2051         202 :         SpinLockRelease(&s->mutex);
    2052             :     }
    2053             : 
    2054         202 :     *query_offset = off;
    2055             : 
    2056             :     /* Now write the data into the successfully-reserved part of the file */
    2057         202 :     fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDWR | O_CREAT | PG_BINARY);
    2058         202 :     if (fd < 0)
    2059           0 :         goto error;
    2060             : 
    2061         202 :     if (pg_pwrite(fd, query, query_len, off) != query_len)
    2062           0 :         goto error;
    2063         202 :     if (pg_pwrite(fd, "\0", 1, off + query_len) != 1)
    2064           0 :         goto error;
    2065             : 
    2066         202 :     CloseTransientFile(fd);
    2067             : 
    2068             :     /* Mark our write complete */
    2069             :     {
    2070         202 :         volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
    2071             : 
    2072         202 :         SpinLockAcquire(&s->mutex);
    2073         202 :         s->n_writers--;
    2074         202 :         SpinLockRelease(&s->mutex);
    2075             :     }
    2076             : 
    2077         202 :     return true;
    2078             : 
    2079           0 : error:
    2080           0 :     ereport(LOG,
    2081             :             (errcode_for_file_access(),
    2082             :              errmsg("could not write file \"%s\": %m",
    2083             :                     PGSS_TEXT_FILE)));
    2084             : 
    2085           0 :     if (fd >= 0)
    2086           0 :         CloseTransientFile(fd);
    2087             : 
    2088             :     /* Mark our write complete */
    2089             :     {
    2090           0 :         volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
    2091             : 
    2092           0 :         SpinLockAcquire(&s->mutex);
    2093           0 :         s->n_writers--;
    2094           0 :         SpinLockRelease(&s->mutex);
    2095             :     }
    2096             : 
    2097           0 :     return false;
    2098             : }
    2099             : 
    2100             : /*
    2101             :  * Read the external query text file into a malloc'd buffer.
    2102             :  *
    2103             :  * Returns NULL (without throwing an error) if unable to read, eg
    2104             :  * file not there or insufficient memory.
    2105             :  *
    2106             :  * On success, the buffer size is also returned into *buffer_size.
    2107             :  *
    2108             :  * This can be called without any lock on pgss->lock, but in that case
    2109             :  * the caller is responsible for verifying that the result is sane.
    2110             :  */
    2111             : static char *
    2112          36 : qtext_load_file(Size *buffer_size)
    2113             : {
    2114             :     char       *buf;
    2115             :     int         fd;
    2116             :     struct stat stat;
    2117             : 
    2118          36 :     fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
    2119          36 :     if (fd < 0)
    2120             :     {
    2121           0 :         if (errno != ENOENT)
    2122           0 :             ereport(LOG,
    2123             :                     (errcode_for_file_access(),
    2124             :                      errmsg("could not read file \"%s\": %m",
    2125             :                             PGSS_TEXT_FILE)));
    2126           0 :         return NULL;
    2127             :     }
    2128             : 
    2129             :     /* Get file length */
    2130          36 :     if (fstat(fd, &stat))
    2131             :     {
    2132           0 :         ereport(LOG,
    2133             :                 (errcode_for_file_access(),
    2134             :                  errmsg("could not stat file \"%s\": %m",
    2135             :                         PGSS_TEXT_FILE)));
    2136           0 :         CloseTransientFile(fd);
    2137           0 :         return NULL;
    2138             :     }
    2139             : 
    2140             :     /* Allocate buffer; beware that off_t might be wider than size_t */
    2141          36 :     if (stat.st_size <= MaxAllocHugeSize)
    2142          36 :         buf = (char *) malloc(stat.st_size);
    2143             :     else
    2144           0 :         buf = NULL;
    2145          36 :     if (buf == NULL)
    2146             :     {
    2147           0 :         ereport(LOG,
    2148             :                 (errcode(ERRCODE_OUT_OF_MEMORY),
    2149             :                  errmsg("out of memory"),
    2150             :                  errdetail("Could not allocate enough memory to read file \"%s\".",
    2151             :                            PGSS_TEXT_FILE)));
    2152           0 :         CloseTransientFile(fd);
    2153           0 :         return NULL;
    2154             :     }
    2155             : 
    2156             :     /*
    2157             :      * OK, slurp in the file.  If we get a short read and errno doesn't get
    2158             :      * set, the reason is probably that garbage collection truncated the file
    2159             :      * since we did the fstat(), so we don't log a complaint --- but we don't
    2160             :      * return the data, either, since it's most likely corrupt due to
    2161             :      * concurrent writes from garbage collection.
    2162             :      */
    2163          36 :     errno = 0;
    2164          36 :     if (read(fd, buf, stat.st_size) != stat.st_size)
    2165             :     {
    2166           0 :         if (errno)
    2167           0 :             ereport(LOG,
    2168             :                     (errcode_for_file_access(),
    2169             :                      errmsg("could not read file \"%s\": %m",
    2170             :                             PGSS_TEXT_FILE)));
    2171           0 :         free(buf);
    2172           0 :         CloseTransientFile(fd);
    2173           0 :         return NULL;
    2174             :     }
    2175             : 
    2176          36 :     if (CloseTransientFile(fd) != 0)
    2177           0 :         ereport(LOG,
    2178             :                 (errcode_for_file_access(),
    2179             :                  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
    2180             : 
    2181          36 :     *buffer_size = stat.st_size;
    2182          36 :     return buf;
    2183             : }
    2184             : 
    2185             : /*
    2186             :  * Locate a query text in the file image previously read by qtext_load_file().
    2187             :  *
    2188             :  * We validate the given offset/length, and return NULL if bogus.  Otherwise,
    2189             :  * the result points to a null-terminated string within the buffer.
    2190             :  */
    2191             : static char *
    2192         318 : qtext_fetch(Size query_offset, int query_len,
    2193             :             char *buffer, Size buffer_size)
    2194             : {
    2195             :     /* File read failed? */
    2196         318 :     if (buffer == NULL)
    2197           0 :         return NULL;
    2198             :     /* Bogus offset/length? */
    2199         318 :     if (query_len < 0 ||
    2200         318 :         query_offset + query_len >= buffer_size)
    2201           0 :         return NULL;
    2202             :     /* As a further sanity check, make sure there's a trailing null */
    2203         318 :     if (buffer[query_offset + query_len] != '\0')
    2204           0 :         return NULL;
    2205             :     /* Looks OK */
    2206         318 :     return buffer + query_offset;
    2207             : }
    2208             : 
    2209             : /*
    2210             :  * Do we need to garbage-collect the external query text file?
    2211             :  *
    2212             :  * Caller should hold at least a shared lock on pgss->lock.
    2213             :  */
    2214             : static bool
    2215         202 : need_gc_qtexts(void)
    2216             : {
    2217             :     Size        extent;
    2218             : 
    2219             :     /* Read shared extent pointer */
    2220             :     {
    2221         202 :         volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
    2222             : 
    2223         202 :         SpinLockAcquire(&s->mutex);
    2224         202 :         extent = s->extent;
    2225         202 :         SpinLockRelease(&s->mutex);
    2226             :     }
    2227             : 
    2228             :     /* Don't proceed if file does not exceed 512 bytes per possible entry */
    2229         202 :     if (extent < 512 * pgss_max)
    2230         202 :         return false;
    2231             : 
    2232             :     /*
    2233             :      * Don't proceed if file is less than about 50% bloat.  Nothing can or
    2234             :      * should be done in the event of unusually large query texts accounting
    2235             :      * for file's large size.  We go to the trouble of maintaining the mean
    2236             :      * query length in order to prevent garbage collection from thrashing
    2237             :      * uselessly.
    2238             :      */
    2239           0 :     if (extent < pgss->mean_query_len * pgss_max * 2)
    2240           0 :         return false;
    2241             : 
    2242           0 :     return true;
    2243             : }
    2244             : 
    2245             : /*
    2246             :  * Garbage-collect orphaned query texts in external file.
    2247             :  *
    2248             :  * This won't be called often in the typical case, since it's likely that
    2249             :  * there won't be too much churn, and besides, a similar compaction process
    2250             :  * occurs when serializing to disk at shutdown or as part of resetting.
    2251             :  * Despite this, it seems prudent to plan for the edge case where the file
    2252             :  * becomes unreasonably large, with no other method of compaction likely to
    2253             :  * occur in the foreseeable future.
    2254             :  *
    2255             :  * The caller must hold an exclusive lock on pgss->lock.
    2256             :  *
    2257             :  * At the first sign of trouble we unlink the query text file to get a clean
    2258             :  * slate (although existing statistics are retained), rather than risk
    2259             :  * thrashing by allowing the same problem case to recur indefinitely.
    2260             :  */
    2261             : static void
    2262           0 : gc_qtexts(void)
    2263             : {
    2264             :     char       *qbuffer;
    2265             :     Size        qbuffer_size;
    2266           0 :     FILE       *qfile = NULL;
    2267             :     HASH_SEQ_STATUS hash_seq;
    2268             :     pgssEntry  *entry;
    2269             :     Size        extent;
    2270             :     int         nentries;
    2271             : 
    2272             :     /*
    2273             :      * When called from pgss_store, some other session might have proceeded
    2274             :      * with garbage collection in the no-lock-held interim of lock strength
    2275             :      * escalation.  Check once more that this is actually necessary.
    2276             :      */
    2277           0 :     if (!need_gc_qtexts())
    2278           0 :         return;
    2279             : 
    2280             :     /*
    2281             :      * Load the old texts file.  If we fail (out of memory, for instance),
    2282             :      * invalidate query texts.  Hopefully this is rare.  It might seem better
    2283             :      * to leave things alone on an OOM failure, but the problem is that the
    2284             :      * file is only going to get bigger; hoping for a future non-OOM result is
    2285             :      * risky and can easily lead to complete denial of service.
    2286             :      */
    2287           0 :     qbuffer = qtext_load_file(&qbuffer_size);
    2288           0 :     if (qbuffer == NULL)
    2289           0 :         goto gc_fail;
    2290             : 
    2291             :     /*
    2292             :      * We overwrite the query texts file in place, so as to reduce the risk of
    2293             :      * an out-of-disk-space failure.  Since the file is guaranteed not to get
    2294             :      * larger, this should always work on traditional filesystems; though we
    2295             :      * could still lose on copy-on-write filesystems.
    2296             :      */
    2297           0 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
    2298           0 :     if (qfile == NULL)
    2299             :     {
    2300           0 :         ereport(LOG,
    2301             :                 (errcode_for_file_access(),
    2302             :                  errmsg("could not write file \"%s\": %m",
    2303             :                         PGSS_TEXT_FILE)));
    2304           0 :         goto gc_fail;
    2305             :     }
    2306             : 
    2307           0 :     extent = 0;
    2308           0 :     nentries = 0;
    2309             : 
    2310           0 :     hash_seq_init(&hash_seq, pgss_hash);
    2311           0 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2312             :     {
    2313           0 :         int         query_len = entry->query_len;
    2314           0 :         char       *qry = qtext_fetch(entry->query_offset,
    2315             :                                       query_len,
    2316             :                                       qbuffer,
    2317             :                                       qbuffer_size);
    2318             : 
    2319           0 :         if (qry == NULL)
    2320             :         {
    2321             :             /* Trouble ... drop the text */
    2322           0 :             entry->query_offset = 0;
    2323           0 :             entry->query_len = -1;
    2324             :             /* entry will not be counted in mean query length computation */
    2325           0 :             continue;
    2326             :         }
    2327             : 
    2328           0 :         if (fwrite(qry, 1, query_len + 1, qfile) != query_len + 1)
    2329             :         {
    2330           0 :             ereport(LOG,
    2331             :                     (errcode_for_file_access(),
    2332             :                      errmsg("could not write file \"%s\": %m",
    2333             :                             PGSS_TEXT_FILE)));
    2334           0 :             hash_seq_term(&hash_seq);
    2335           0 :             goto gc_fail;
    2336             :         }
    2337             : 
    2338           0 :         entry->query_offset = extent;
    2339           0 :         extent += query_len + 1;
    2340           0 :         nentries++;
    2341             :     }
    2342             : 
    2343             :     /*
    2344             :      * Truncate away any now-unused space.  If this fails for some odd reason,
    2345             :      * we log it, but there's no need to fail.
    2346             :      */
    2347           0 :     if (ftruncate(fileno(qfile), extent) != 0)
    2348           0 :         ereport(LOG,
    2349             :                 (errcode_for_file_access(),
    2350             :                  errmsg("could not truncate file \"%s\": %m",
    2351             :                         PGSS_TEXT_FILE)));
    2352             : 
    2353           0 :     if (FreeFile(qfile))
    2354             :     {
    2355           0 :         ereport(LOG,
    2356             :                 (errcode_for_file_access(),
    2357             :                  errmsg("could not write file \"%s\": %m",
    2358             :                         PGSS_TEXT_FILE)));
    2359           0 :         qfile = NULL;
    2360           0 :         goto gc_fail;
    2361             :     }
    2362             : 
    2363           0 :     elog(DEBUG1, "pgss gc of queries file shrunk size from %zu to %zu",
    2364             :          pgss->extent, extent);
    2365             : 
    2366             :     /* Reset the shared extent pointer */
    2367           0 :     pgss->extent = extent;
    2368             : 
    2369             :     /*
    2370             :      * Also update the mean query length, to be sure that need_gc_qtexts()
    2371             :      * won't still think we have a problem.
    2372             :      */
    2373           0 :     if (nentries > 0)
    2374           0 :         pgss->mean_query_len = extent / nentries;
    2375             :     else
    2376           0 :         pgss->mean_query_len = ASSUMED_LENGTH_INIT;
    2377             : 
    2378           0 :     free(qbuffer);
    2379             : 
    2380             :     /*
    2381             :      * OK, count a garbage collection cycle.  (Note: even though we have
    2382             :      * exclusive lock on pgss->lock, we must take pgss->mutex for this, since
    2383             :      * other processes may examine gc_count while holding only the mutex.
    2384             :      * Also, we have to advance the count *after* we've rewritten the file,
    2385             :      * else other processes might not realize they read a stale file.)
    2386             :      */
    2387           0 :     record_gc_qtexts();
    2388             : 
    2389           0 :     return;
    2390             : 
    2391           0 : gc_fail:
    2392             :     /* clean up resources */
    2393           0 :     if (qfile)
    2394           0 :         FreeFile(qfile);
    2395           0 :     if (qbuffer)
    2396           0 :         free(qbuffer);
    2397             : 
    2398             :     /*
    2399             :      * Since the contents of the external file are now uncertain, mark all
    2400             :      * hashtable entries as having invalid texts.
    2401             :      */
    2402           0 :     hash_seq_init(&hash_seq, pgss_hash);
    2403           0 :     while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2404             :     {
    2405           0 :         entry->query_offset = 0;
    2406           0 :         entry->query_len = -1;
    2407             :     }
    2408             : 
    2409             :     /*
    2410             :      * Destroy the query text file and create a new, empty one
    2411             :      */
    2412           0 :     (void) unlink(PGSS_TEXT_FILE);
    2413           0 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
    2414           0 :     if (qfile == NULL)
    2415           0 :         ereport(LOG,
    2416             :                 (errcode_for_file_access(),
    2417             :                  errmsg("could not recreate file \"%s\": %m",
    2418             :                         PGSS_TEXT_FILE)));
    2419             :     else
    2420           0 :         FreeFile(qfile);
    2421             : 
    2422             :     /* Reset the shared extent pointer */
    2423           0 :     pgss->extent = 0;
    2424             : 
    2425             :     /* Reset mean_query_len to match the new state */
    2426           0 :     pgss->mean_query_len = ASSUMED_LENGTH_INIT;
    2427             : 
    2428             :     /*
    2429             :      * Bump the GC count even though we failed.
    2430             :      *
    2431             :      * This is needed to make concurrent readers of file without any lock on
    2432             :      * pgss->lock notice existence of new version of file.  Once readers
    2433             :      * subsequently observe a change in GC count with pgss->lock held, that
    2434             :      * forces a safe reopen of file.  Writers also require that we bump here,
    2435             :      * of course.  (As required by locking protocol, readers and writers don't
    2436             :      * trust earlier file contents until gc_count is found unchanged after
    2437             :      * pgss->lock acquired in shared or exclusive mode respectively.)
    2438             :      */
    2439           0 :     record_gc_qtexts();
    2440             : }
    2441             : 
    2442             : /*
    2443             :  * Release entries corresponding to parameters passed.
    2444             :  */
    2445             : static void
    2446          30 : entry_reset(Oid userid, Oid dbid, uint64 queryid)
    2447             : {
    2448             :     HASH_SEQ_STATUS hash_seq;
    2449             :     pgssEntry  *entry;
    2450             :     FILE       *qfile;
    2451             :     long        num_entries;
    2452          30 :     long        num_remove = 0;
    2453             :     pgssHashKey key;
    2454             : 
    2455          30 :     if (!pgss || !pgss_hash)
    2456           0 :         ereport(ERROR,
    2457             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    2458             :                  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
    2459             : 
    2460          30 :     LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
    2461          30 :     num_entries = hash_get_num_entries(pgss_hash);
    2462             : 
    2463          30 :     if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
    2464             :     {
    2465             :         /* If all the parameters are available, use the fast path. */
    2466           2 :         key.userid = userid;
    2467           2 :         key.dbid = dbid;
    2468           2 :         key.queryid = queryid;
    2469             : 
    2470             :         /* Remove the key if exists */
    2471           2 :         entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
    2472           4 :         if (entry)              /* found */
    2473           2 :             num_remove++;
    2474             :     }
    2475          34 :     else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
    2476             :     {
    2477             :         /* Remove entries corresponding to valid parameters. */
    2478           6 :         hash_seq_init(&hash_seq, pgss_hash);
    2479          78 :         while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2480             :         {
    2481          72 :             if ((!userid || entry->key.userid == userid) &&
    2482          52 :                 (!dbid || entry->key.dbid == dbid) &&
    2483          48 :                 (!queryid || entry->key.queryid == queryid))
    2484             :             {
    2485           8 :                 hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
    2486           8 :                 num_remove++;
    2487             :             }
    2488             :         }
    2489             :     }
    2490             :     else
    2491             :     {
    2492             :         /* Remove all entries. */
    2493          22 :         hash_seq_init(&hash_seq, pgss_hash);
    2494         200 :         while ((entry = hash_seq_search(&hash_seq)) != NULL)
    2495             :         {
    2496         178 :             hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
    2497         178 :             num_remove++;
    2498             :         }
    2499             :     }
    2500             : 
    2501             :     /* All entries are removed? */
    2502          30 :     if (num_entries != num_remove)
    2503           8 :         goto release_lock;
    2504             : 
    2505             :     /*
    2506             :      * Write new empty query file, perhaps even creating a new one to recover
    2507             :      * if the file was missing.
    2508             :      */
    2509          22 :     qfile = AllocateFile(PGSS_TEXT_FILE, PG_BINARY_W);
    2510          22 :     if (qfile == NULL)
    2511             :     {
    2512           0 :         ereport(LOG,
    2513             :                 (errcode_for_file_access(),
    2514             :                  errmsg("could not create file \"%s\": %m",
    2515             :                         PGSS_TEXT_FILE)));
    2516           0 :         goto done;
    2517             :     }
    2518             : 
    2519             :     /* If ftruncate fails, log it, but it's not a fatal problem */
    2520          22 :     if (ftruncate(fileno(qfile), 0) != 0)
    2521           0 :         ereport(LOG,
    2522             :                 (errcode_for_file_access(),
    2523             :                  errmsg("could not truncate file \"%s\": %m",
    2524             :                         PGSS_TEXT_FILE)));
    2525             : 
    2526          22 :     FreeFile(qfile);
    2527             : 
    2528          22 : done:
    2529          22 :     pgss->extent = 0;
    2530             :     /* This counts as a query text garbage collection for our purposes */
    2531          22 :     record_gc_qtexts();
    2532             : 
    2533          30 : release_lock:
    2534          30 :     LWLockRelease(pgss->lock);
    2535          30 : }
    2536             : 
    2537             : /*
    2538             :  * AppendJumble: Append a value that is substantive in a given query to
    2539             :  * the current jumble.
    2540             :  */
    2541             : static void
    2542        5282 : AppendJumble(pgssJumbleState *jstate, const unsigned char *item, Size size)
    2543             : {
    2544        5282 :     unsigned char *jumble = jstate->jumble;
    2545        5282 :     Size        jumble_len = jstate->jumble_len;
    2546             : 
    2547             :     /*
    2548             :      * Whenever the jumble buffer is full, we hash the current contents and
    2549             :      * reset the buffer to contain just that hash value, thus relying on the
    2550             :      * hash to summarize everything so far.
    2551             :      */
    2552       10564 :     while (size > 0)
    2553             :     {
    2554             :         Size        part_size;
    2555             : 
    2556        5282 :         if (jumble_len >= JUMBLE_SIZE)
    2557             :         {
    2558             :             uint64      start_hash;
    2559             : 
    2560           0 :             start_hash = DatumGetUInt64(hash_any_extended(jumble,
    2561             :                                                           JUMBLE_SIZE, 0));
    2562           0 :             memcpy(jumble, &start_hash, sizeof(start_hash));
    2563           0 :             jumble_len = sizeof(start_hash);
    2564             :         }
    2565        5282 :         part_size = Min(size, JUMBLE_SIZE - jumble_len);
    2566        5282 :         memcpy(jumble + jumble_len, item, part_size);
    2567        5282 :         jumble_len += part_size;
    2568        5282 :         item += part_size;
    2569        5282 :         size -= part_size;
    2570             :     }
    2571        5282 :     jstate->jumble_len = jumble_len;
    2572        5282 : }
    2573             : 
    2574             : /*
    2575             :  * Wrappers around AppendJumble to encapsulate details of serialization
    2576             :  * of individual local variable elements.
    2577             :  */
    2578             : #define APP_JUMB(item) \
    2579             :     AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
    2580             : #define APP_JUMB_STRING(str) \
    2581             :     AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)
    2582             : 
    2583             : /*
    2584             :  * JumbleQuery: Selectively serialize the query tree, appending significant
    2585             :  * data to the "query jumble" while ignoring nonsignificant data.
    2586             :  *
    2587             :  * Rule of thumb for what to include is that we should ignore anything not
    2588             :  * semantically significant (such as alias names) as well as anything that can
    2589             :  * be deduced from child nodes (else we'd just be double-hashing that piece
    2590             :  * of information).
    2591             :  */
    2592             : static void
    2593         206 : JumbleQuery(pgssJumbleState *jstate, Query *query)
    2594             : {
    2595             :     Assert(IsA(query, Query));
    2596             :     Assert(query->utilityStmt == NULL);
    2597             : 
    2598         206 :     APP_JUMB(query->commandType);
    2599             :     /* resultRelation is usually predictable from commandType */
    2600         206 :     JumbleExpr(jstate, (Node *) query->cteList);
    2601         206 :     JumbleRangeTable(jstate, query->rtable);
    2602         206 :     JumbleExpr(jstate, (Node *) query->jointree);
    2603         206 :     JumbleExpr(jstate, (Node *) query->targetList);
    2604         206 :     JumbleExpr(jstate, (Node *) query->onConflict);
    2605         206 :     JumbleExpr(jstate, (Node *) query->returningList);
    2606         206 :     JumbleExpr(jstate, (Node *) query->groupClause);
    2607         206 :     JumbleExpr(jstate, (Node *) query->groupingSets);
    2608         206 :     JumbleExpr(jstate, query->havingQual);
    2609         206 :     JumbleExpr(jstate, (Node *) query->windowClause);
    2610         206 :     JumbleExpr(jstate, (Node *) query->distinctClause);
    2611         206 :     JumbleExpr(jstate, (Node *) query->sortClause);
    2612         206 :     JumbleExpr(jstate, query->limitOffset);
    2613         206 :     JumbleExpr(jstate, query->limitCount);
    2614         206 :     JumbleRowMarks(jstate, query->rowMarks);
    2615         206 :     JumbleExpr(jstate, query->setOperations);
    2616         206 : }
    2617             : 
    2618             : /*
    2619             :  * Jumble a range table
    2620             :  */
    2621             : static void
    2622         206 : JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
    2623             : {
    2624             :     ListCell   *lc;
    2625             : 
    2626         358 :     foreach(lc, rtable)
    2627             :     {
    2628         152 :         RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
    2629             : 
    2630         152 :         APP_JUMB(rte->rtekind);
    2631         152 :         switch (rte->rtekind)
    2632             :         {
    2633         120 :             case RTE_RELATION:
    2634         120 :                 APP_JUMB(rte->relid);
    2635         120 :                 JumbleExpr(jstate, (Node *) rte->tablesample);
    2636         120 :                 break;
    2637           4 :             case RTE_SUBQUERY:
    2638           4 :                 JumbleQuery(jstate, rte->subquery);
    2639           4 :                 break;
    2640          22 :             case RTE_JOIN:
    2641          22 :                 APP_JUMB(rte->jointype);
    2642          22 :                 break;
    2643           0 :             case RTE_FUNCTION:
    2644           0 :                 JumbleExpr(jstate, (Node *) rte->functions);
    2645           0 :                 break;
    2646           0 :             case RTE_TABLEFUNC:
    2647           0 :                 JumbleExpr(jstate, (Node *) rte->tablefunc);
    2648           0 :                 break;
    2649           4 :             case RTE_VALUES:
    2650           4 :                 JumbleExpr(jstate, (Node *) rte->values_lists);
    2651           4 :                 break;
    2652           2 :             case RTE_CTE:
    2653             : 
    2654             :                 /*
    2655             :                  * Depending on the CTE name here isn't ideal, but it's the
    2656             :                  * only info we have to identify the referenced WITH item.
    2657             :                  */
    2658           2 :                 APP_JUMB_STRING(rte->ctename);
    2659           2 :                 APP_JUMB(rte->ctelevelsup);
    2660           2 :                 break;
    2661           0 :             case RTE_NAMEDTUPLESTORE:
    2662           0 :                 APP_JUMB_STRING(rte->enrname);
    2663           0 :                 break;
    2664           0 :             case RTE_RESULT:
    2665           0 :                 break;
    2666           0 :             default:
    2667           0 :                 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
    2668             :                 break;
    2669             :         }
    2670             :     }
    2671         206 : }
    2672             : 
    2673             : /*
    2674             :  * Jumble a rowMarks list
    2675             :  */
    2676             : static void
    2677         206 : JumbleRowMarks(pgssJumbleState *jstate, List *rowMarks)
    2678             : {
    2679             :     ListCell   *lc;
    2680             : 
    2681         242 :     foreach(lc, rowMarks)
    2682             :     {
    2683          36 :         RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
    2684             : 
    2685          36 :         if (!rowmark->pushedDown)
    2686             :         {
    2687          36 :             APP_JUMB(rowmark->rti);
    2688          36 :             APP_JUMB(rowmark->strength);
    2689          36 :             APP_JUMB(rowmark->waitPolicy);
    2690             :         }
    2691             :     }
    2692         206 : }
    2693             : 
    2694             : /*
    2695             :  * Jumble an expression tree
    2696             :  *
    2697             :  * In general this function should handle all the same node types that
    2698             :  * expression_tree_walker() does, and therefore it's coded to be as parallel
    2699             :  * to that function as possible.  However, since we are only invoked on
    2700             :  * queries immediately post-parse-analysis, we need not handle node types
    2701             :  * that only appear in planning.
    2702             :  *
    2703             :  * Note: the reason we don't simply use expression_tree_walker() is that the
    2704             :  * point of that function is to support tree walkers that don't care about
    2705             :  * most tree node types, but here we care about all types.  We should complain
    2706             :  * about any unrecognized node type.
    2707             :  */
    2708             : static void
    2709        5004 : JumbleExpr(pgssJumbleState *jstate, Node *node)
    2710             : {
    2711             :     ListCell   *temp;
    2712             : 
    2713        5004 :     if (node == NULL)
    2714        2866 :         return;
    2715             : 
    2716             :     /* Guard against stack overflow due to overly complex expressions */
    2717        2138 :     check_stack_depth();
    2718             : 
    2719             :     /*
    2720             :      * We always emit the node's NodeTag, then any additional fields that are
    2721             :      * considered significant, and then we recurse to any child nodes.
    2722             :      */
    2723        2138 :     APP_JUMB(node->type);
    2724             : 
    2725        2138 :     switch (nodeTag(node))
    2726             :     {
    2727         302 :         case T_Var:
    2728             :             {
    2729         302 :                 Var        *var = (Var *) node;
    2730             : 
    2731         302 :                 APP_JUMB(var->varno);
    2732         302 :                 APP_JUMB(var->varattno);
    2733         302 :                 APP_JUMB(var->varlevelsup);
    2734             :             }
    2735         302 :             break;
    2736         270 :         case T_Const:
    2737             :             {
    2738         270 :                 Const      *c = (Const *) node;
    2739             : 
    2740             :                 /* We jumble only the constant's type, not its value */
    2741         270 :                 APP_JUMB(c->consttype);
    2742             :                 /* Also, record its parse location for query normalization */
    2743         270 :                 RecordConstLocation(jstate, c->location);
    2744             :             }
    2745         270 :             break;
    2746          12 :         case T_Param:
    2747             :             {
    2748          12 :                 Param      *p = (Param *) node;
    2749             : 
    2750          12 :                 APP_JUMB(p->paramkind);
    2751          12 :                 APP_JUMB(p->paramid);
    2752          12 :                 APP_JUMB(p->paramtype);
    2753             :                 /* Also, track the highest external Param id */
    2754          12 :                 if (p->paramkind == PARAM_EXTERN &&
    2755          12 :                     p->paramid > jstate->highest_extern_param_id)
    2756          12 :                     jstate->highest_extern_param_id = p->paramid;
    2757             :             }
    2758          12 :             break;
    2759           4 :         case T_Aggref:
    2760             :             {
    2761           4 :                 Aggref     *expr = (Aggref *) node;
    2762             : 
    2763           4 :                 APP_JUMB(expr->aggfnoid);
    2764           4 :                 JumbleExpr(jstate, (Node *) expr->aggdirectargs);
    2765           4 :                 JumbleExpr(jstate, (Node *) expr->args);
    2766           4 :                 JumbleExpr(jstate, (Node *) expr->aggorder);
    2767           4 :                 JumbleExpr(jstate, (Node *) expr->aggdistinct);
    2768           4 :                 JumbleExpr(jstate, (Node *) expr->aggfilter);
    2769             :             }
    2770           4 :             break;
    2771           0 :         case T_GroupingFunc:
    2772             :             {
    2773           0 :                 GroupingFunc *grpnode = (GroupingFunc *) node;
    2774             : 
    2775           0 :                 JumbleExpr(jstate, (Node *) grpnode->refs);
    2776             :             }
    2777           0 :             break;
    2778           0 :         case T_WindowFunc:
    2779             :             {
    2780           0 :                 WindowFunc *expr = (WindowFunc *) node;
    2781             : 
    2782           0 :                 APP_JUMB(expr->winfnoid);
    2783           0 :                 APP_JUMB(expr->winref);
    2784           0 :                 JumbleExpr(jstate, (Node *) expr->args);
    2785           0 :                 JumbleExpr(jstate, (Node *) expr->aggfilter);
    2786             :             }
    2787           0 :             break;
    2788           0 :         case T_SubscriptingRef:
    2789             :             {
    2790           0 :                 SubscriptingRef *sbsref = (SubscriptingRef *) node;
    2791             : 
    2792           0 :                 JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
    2793           0 :                 JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
    2794           0 :                 JumbleExpr(jstate, (Node *) sbsref->refexpr);
    2795           0 :                 JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
    2796             :             }
    2797           0 :             break;
    2798         112 :         case T_FuncExpr:
    2799             :             {
    2800         112 :                 FuncExpr   *expr = (FuncExpr *) node;
    2801             : 
    2802         112 :                 APP_JUMB(expr->funcid);
    2803         112 :                 JumbleExpr(jstate, (Node *) expr->args);
    2804             :             }
    2805         112 :             break;
    2806           0 :         case T_NamedArgExpr:
    2807             :             {
    2808           0 :                 NamedArgExpr *nae = (NamedArgExpr *) node;
    2809             : 
    2810           0 :                 APP_JUMB(nae->argnumber);
    2811           0 :                 JumbleExpr(jstate, (Node *) nae->arg);
    2812             :             }
    2813           0 :             break;
    2814          98 :         case T_OpExpr:
    2815             :         case T_DistinctExpr:    /* struct-equivalent to OpExpr */
    2816             :         case T_NullIfExpr:      /* struct-equivalent to OpExpr */
    2817             :             {
    2818          98 :                 OpExpr     *expr = (OpExpr *) node;
    2819             : 
    2820          98 :                 APP_JUMB(expr->opno);
    2821          98 :                 JumbleExpr(jstate, (Node *) expr->args);
    2822             :             }
    2823          98 :             break;
    2824           2 :         case T_ScalarArrayOpExpr:
    2825             :             {
    2826           2 :                 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
    2827             : 
    2828           2 :                 APP_JUMB(expr->opno);
    2829           2 :                 APP_JUMB(expr->useOr);
    2830           2 :                 JumbleExpr(jstate, (Node *) expr->args);
    2831             :             }
    2832           2 :             break;
    2833           0 :         case T_BoolExpr:
    2834             :             {
    2835           0 :                 BoolExpr   *expr = (BoolExpr *) node;
    2836             : 
    2837           0 :                 APP_JUMB(expr->boolop);
    2838           0 :                 JumbleExpr(jstate, (Node *) expr->args);
    2839             :             }
    2840           0 :             break;
    2841           6 :         case T_SubLink:
    2842             :             {
    2843           6 :                 SubLink    *sublink = (SubLink *) node;
    2844             : 
    2845           6 :                 APP_JUMB(sublink->subLinkType);
    2846           6 :                 APP_JUMB(sublink->subLinkId);
    2847           6 :                 JumbleExpr(jstate, (Node *) sublink->testexpr);
    2848           6 :                 JumbleQuery(jstate, castNode(Query, sublink->subselect));
    2849             :             }
    2850           6 :             break;
    2851           0 :         case T_FieldSelect:
    2852             :             {
    2853           0 :                 FieldSelect *fs = (FieldSelect *) node;
    2854             : 
    2855           0 :                 APP_JUMB(fs->fieldnum);
    2856           0 :                 JumbleExpr(jstate, (Node *) fs->arg);
    2857             :             }
    2858           0 :             break;
    2859           0 :         case T_FieldStore:
    2860             :             {
    2861           0 :                 FieldStore *fstore = (FieldStore *) node;
    2862             : 
    2863           0 :                 JumbleExpr(jstate, (Node *) fstore->arg);
    2864           0 :                 JumbleExpr(jstate, (Node *) fstore->newvals);
    2865             :             }
    2866           0 :             break;
    2867           8 :         case T_RelabelType:
    2868             :             {
    2869           8 :                 RelabelType *rt = (RelabelType *) node;
    2870             : 
    2871           8 :                 APP_JUMB(rt->resulttype);
    2872           8 :                 JumbleExpr(jstate, (Node *) rt->arg);
    2873             :             }
    2874           8 :             break;
    2875           0 :         case T_CoerceViaIO:
    2876             :             {
    2877           0 :                 CoerceViaIO *cio = (CoerceViaIO *) node;
    2878             : 
    2879           0 :                 APP_JUMB(cio->resulttype);
    2880           0 :                 JumbleExpr(jstate, (Node *) cio->arg);
    2881             :             }
    2882           0 :             break;
    2883           0 :         case T_ArrayCoerceExpr:
    2884             :             {
    2885           0 :                 ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
    2886             : 
    2887           0 :                 APP_JUMB(acexpr->resulttype);
    2888           0 :                 JumbleExpr(jstate, (Node *) acexpr->arg);
    2889           0 :                 JumbleExpr(jstate, (Node *) acexpr->elemexpr);
    2890             :             }
    2891           0 :             break;
    2892           0 :         case T_ConvertRowtypeExpr:
    2893             :             {
    2894           0 :                 ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
    2895             : 
    2896           0 :                 APP_JUMB(crexpr->resulttype);
    2897           0 :                 JumbleExpr(jstate, (Node *) crexpr->arg);
    2898             :             }
    2899           0 :             break;
    2900          28 :         case T_CollateExpr:
    2901             :             {
    2902          28 :                 CollateExpr *ce = (CollateExpr *) node;
    2903             : 
    2904          28 :                 APP_JUMB(ce->collOid);
    2905          28 :                 JumbleExpr(jstate, (Node *) ce->arg);
    2906             :             }
    2907          28 :             break;
    2908           0 :         case T_CaseExpr:
    2909             :             {
    2910           0 :                 CaseExpr   *caseexpr = (CaseExpr *) node;
    2911             : 
    2912           0 :                 JumbleExpr(jstate, (Node *) caseexpr->arg);
    2913           0 :                 foreach(temp, caseexpr->args)
    2914             :                 {
    2915           0 :                     CaseWhen   *when = lfirst_node(CaseWhen, temp);
    2916             : 
    2917           0 :                     JumbleExpr(jstate, (Node *) when->expr);
    2918           0 :                     JumbleExpr(jstate, (Node *) when->result);
    2919             :                 }
    2920           0 :                 JumbleExpr(jstate, (Node *) caseexpr->defresult);
    2921             :             }
    2922           0 :             break;
    2923           0 :         case T_CaseTestExpr:
    2924             :             {
    2925           0 :                 CaseTestExpr *ct = (CaseTestExpr *) node;
    2926             : 
    2927           0 :                 APP_JUMB(ct->typeId);
    2928             :             }
    2929           0 :             break;
    2930           2 :         case T_ArrayExpr:
    2931           2 :             JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
    2932           2 :             break;
    2933           0 :         case T_RowExpr:
    2934           0 :             JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
    2935           0 :             break;
    2936           0 :         case T_RowCompareExpr:
    2937             :             {
    2938           0 :                 RowCompareExpr *rcexpr = (RowCompareExpr *) node;
    2939             : 
    2940           0 :                 APP_JUMB(rcexpr->rctype);
    2941           0 :                 JumbleExpr(jstate, (Node *) rcexpr->largs);
    2942           0 :                 JumbleExpr(jstate, (Node *) rcexpr->rargs);
    2943             :             }
    2944           0 :             break;
    2945           0 :         case T_CoalesceExpr:
    2946           0 :             JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
    2947           0 :             break;
    2948           0 :         case T_MinMaxExpr:
    2949             :             {
    2950           0 :                 MinMaxExpr *mmexpr = (MinMaxExpr *) node;
    2951             : 
    2952           0 :                 APP_JUMB(mmexpr->op);
    2953           0 :                 JumbleExpr(jstate, (Node *) mmexpr->args);
    2954             :             }
    2955           0 :             break;
    2956           0 :         case T_SQLValueFunction:
    2957             :             {
    2958           0 :                 SQLValueFunction *svf = (SQLValueFunction *) node;
    2959             : 
    2960           0 :                 APP_JUMB(svf->op);
    2961             :                 /* type is fully determined by op */
    2962           0 :                 APP_JUMB(svf->typmod);
    2963             :             }
    2964           0 :             break;
    2965           0 :         case T_XmlExpr:
    2966             :             {
    2967           0 :                 XmlExpr    *xexpr = (XmlExpr *) node;
    2968             : 
    2969           0 :                 APP_JUMB(xexpr->op);
    2970           0 :                 JumbleExpr(jstate, (Node *) xexpr->named_args);
    2971           0 :                 JumbleExpr(jstate, (Node *) xexpr->args);
    2972             :             }
    2973           0 :             break;
    2974           0 :         case T_NullTest:
    2975             :             {
    2976           0 :                 NullTest   *nt = (NullTest *) node;
    2977             : 
    2978           0 :                 APP_JUMB(nt->nulltesttype);
    2979           0 :                 JumbleExpr(jstate, (Node *) nt->arg);
    2980             :             }
    2981           0 :             break;
    2982           0 :         case T_BooleanTest:
    2983             :             {
    2984           0 :                 BooleanTest *bt = (BooleanTest *) node;
    2985             : 
    2986           0 :                 APP_JUMB(bt->booltesttype);
    2987           0 :                 JumbleExpr(jstate, (Node *) bt->arg);
    2988             :             }
    2989           0 :             break;
    2990           0 :         case T_CoerceToDomain:
    2991             :             {
    2992           0 :                 CoerceToDomain *cd = (CoerceToDomain *) node;
    2993             : 
    2994           0 :                 APP_JUMB(cd->resulttype);
    2995           0 :                 JumbleExpr(jstate, (Node *) cd->arg);
    2996             :             }
    2997           0 :             break;
    2998           0 :         case T_CoerceToDomainValue:
    2999             :             {
    3000           0 :                 CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
    3001             : 
    3002           0 :                 APP_JUMB(cdv->typeId);
    3003             :             }
    3004           0 :             break;
    3005           0 :         case T_SetToDefault:
    3006             :             {
    3007           0 :                 SetToDefault *sd = (SetToDefault *) node;
    3008             : 
    3009           0 :                 APP_JUMB(sd->typeId);
    3010             :             }
    3011           0 :             break;
    3012           0 :         case T_CurrentOfExpr:
    3013             :             {
    3014           0 :                 CurrentOfExpr *ce = (CurrentOfExpr *) node;
    3015             : 
    3016           0 :                 APP_JUMB(ce->cvarno);
    3017           0 :                 if (ce->cursor_name)
    3018           0 :                     APP_JUMB_STRING(ce->cursor_name);
    3019           0 :                 APP_JUMB(ce->cursor_param);
    3020             :             }
    3021           0 :             break;
    3022           0 :         case T_NextValueExpr:
    3023             :             {
    3024           0 :                 NextValueExpr *nve = (NextValueExpr *) node;
    3025             : 
    3026           0 :                 APP_JUMB(nve->seqid);
    3027           0 :                 APP_JUMB(nve->typeId);
    3028             :             }
    3029           0 :             break;
    3030           0 :         case T_InferenceElem:
    3031             :             {
    3032           0 :                 InferenceElem *ie = (InferenceElem *) node;
    3033             : 
    3034           0 :                 APP_JUMB(ie->infercollid);
    3035           0 :                 APP_JUMB(ie->inferopclass);
    3036           0 :                 JumbleExpr(jstate, ie->expr);
    3037             :             }
    3038           0 :             break;
    3039         352 :         case T_TargetEntry:
    3040             :             {
    3041         352 :                 TargetEntry *tle = (TargetEntry *) node;
    3042             : 
    3043         352 :                 APP_JUMB(tle->resno);
    3044         352 :                 APP_JUMB(tle->ressortgroupref);
    3045         352 :                 JumbleExpr(jstate, (Node *) tle->expr);
    3046             :             }
    3047         352 :             break;
    3048         124 :         case T_RangeTblRef:
    3049             :             {
    3050         124 :                 RangeTblRef *rtr = (RangeTblRef *) node;
    3051             : 
    3052         124 :                 APP_JUMB(rtr->rtindex);
    3053             :             }
    3054         124 :             break;
    3055          22 :         case T_JoinExpr:
    3056             :             {
    3057          22 :                 JoinExpr   *join = (JoinExpr *) node;
    3058             : 
    3059          22 :                 APP_JUMB(join->jointype);
    3060          22 :                 APP_JUMB(join->isNatural);
    3061          22 :                 APP_JUMB(join->rtindex);
    3062          22 :                 JumbleExpr(jstate, join->larg);
    3063          22 :                 JumbleExpr(jstate, join->rarg);
    3064          22 :                 JumbleExpr(jstate, join->quals);
    3065             :             }
    3066          22 :             break;
    3067         206 :         case T_FromExpr:
    3068             :             {
    3069         206 :                 FromExpr   *from = (FromExpr *) node;
    3070             : 
    3071         206 :                 JumbleExpr(jstate, (Node *) from->fromlist);
    3072         206 :                 JumbleExpr(jstate, from->quals);
    3073             :             }
    3074         206 :             break;
    3075           0 :         case T_OnConflictExpr:
    3076             :             {
    3077           0 :                 OnConflictExpr *conf = (OnConflictExpr *) node;
    3078             : 
    3079           0 :                 APP_JUMB(conf->action);
    3080           0 :                 JumbleExpr(jstate, (Node *) conf->arbiterElems);
    3081           0 :                 JumbleExpr(jstate, conf->arbiterWhere);
    3082           0 :                 JumbleExpr(jstate, (Node *) conf->onConflictSet);
    3083           0 :                 JumbleExpr(jstate, conf->onConflictWhere);
    3084           0 :                 APP_JUMB(conf->constraint);
    3085           0 :                 APP_JUMB(conf->exclRelIndex);
    3086           0 :                 JumbleExpr(jstate, (Node *) conf->exclRelTlist);
    3087             :             }
    3088           0 :             break;
    3089         548 :         case T_List:
    3090        1434 :             foreach(temp, (List *) node)
    3091             :             {
    3092         886 :                 JumbleExpr(jstate, (Node *) lfirst(temp));
    3093             :             }
    3094         548 :             break;
    3095           0 :         case T_IntList:
    3096           0 :             foreach(temp, (List *) node)
    3097             :             {
    3098           0 :                 APP_JUMB(lfirst_int(temp));
    3099             :             }
    3100           0 :             break;
    3101          38 :         case T_SortGroupClause:
    3102             :             {
    3103          38 :                 SortGroupClause *sgc = (SortGroupClause *) node;
    3104             : 
    3105          38 :                 APP_JUMB(sgc->tleSortGroupRef);
    3106          38 :                 APP_JUMB(sgc->eqop);
    3107          38 :                 APP_JUMB(sgc->sortop);
    3108          38 :                 APP_JUMB(sgc->nulls_first);
    3109             :             }
    3110          38 :             break;
    3111           0 :         case T_GroupingSet:
    3112             :             {
    3113           0 :                 GroupingSet *gsnode = (GroupingSet *) node;
    3114             : 
    3115           0 :                 JumbleExpr(jstate, (Node *) gsnode->content);
    3116             :             }
    3117           0 :             break;
    3118           0 :         case T_WindowClause:
    3119             :             {
    3120           0 :                 WindowClause *wc = (WindowClause *) node;
    3121             : 
    3122           0 :                 APP_JUMB(wc->winref);
    3123           0 :                 APP_JUMB(wc->frameOptions);
    3124           0 :                 JumbleExpr(jstate, (Node *) wc->partitionClause);
    3125           0 :                 JumbleExpr(jstate, (Node *) wc->orderClause);
    3126           0 :                 JumbleExpr(jstate, wc->startOffset);
    3127           0 :                 JumbleExpr(jstate, wc->endOffset);
    3128             :             }
    3129           0 :             break;
    3130           2 :         case T_CommonTableExpr:
    3131             :             {
    3132           2 :                 CommonTableExpr *cte = (CommonTableExpr *) node;
    3133             : 
    3134             :                 /* we store the string name because RTE_CTE RTEs need it */
    3135           2 :                 APP_JUMB_STRING(cte->ctename);
    3136           2 :                 APP_JUMB(cte->ctematerialized);
    3137           2 :                 JumbleQuery(jstate, castNode(Query, cte->ctequery));
    3138             :             }
    3139           2 :             break;
    3140           2 :         case T_SetOperationStmt:
    3141             :             {
    3142           2 :                 SetOperationStmt *setop = (SetOperationStmt *) node;
    3143             : 
    3144           2 :                 APP_JUMB(setop->op);
    3145           2 :                 APP_JUMB(setop->all);
    3146           2 :                 JumbleExpr(jstate, setop->larg);
    3147           2 :                 JumbleExpr(jstate, setop->rarg);
    3148             :             }
    3149           2 :             break;
    3150           0 :         case T_RangeTblFunction:
    3151             :             {
    3152           0 :                 RangeTblFunction *rtfunc = (RangeTblFunction *) node;
    3153             : 
    3154           0 :                 JumbleExpr(jstate, rtfunc->funcexpr);
    3155             :             }
    3156           0 :             break;
    3157           0 :         case T_TableFunc:
    3158             :             {
    3159           0 :                 TableFunc  *tablefunc = (TableFunc *) node;
    3160             : 
    3161           0 :                 JumbleExpr(jstate, tablefunc->docexpr);
    3162           0 :                 JumbleExpr(jstate, tablefunc->rowexpr);
    3163           0 :                 JumbleExpr(jstate, (Node *) tablefunc->colexprs);
    3164             :             }
    3165           0 :             break;
    3166           0 :         case T_TableSampleClause:
    3167             :             {
    3168           0 :                 TableSampleClause *tsc = (TableSampleClause *) node;
    3169             : 
    3170           0 :                 APP_JUMB(tsc->tsmhandler);
    3171           0 :                 JumbleExpr(jstate, (Node *) tsc->args);
    3172           0 :                 JumbleExpr(jstate, (Node *) tsc->repeatable);
    3173             :             }
    3174           0 :             break;
    3175           0 :         default:
    3176             :             /* Only a warning, since we can stumble along anyway */
    3177           0 :             elog(WARNING, "unrecognized node type: %d",
    3178             :                  (int) nodeTag(node));
    3179           0 :             break;
    3180             :     }
    3181             : }
    3182             : 
    3183             : /*
    3184             :  * Record location of constant within query string of query tree
    3185             :  * that is currently being walked.
    3186             :  */
    3187             : static void
    3188         270 : RecordConstLocation(pgssJumbleState *jstate, int location)
    3189             : {
    3190             :     /* -1 indicates unknown or undefined location */
    3191         270 :     if (location >= 0)
    3192             :     {
    3193             :         /* enlarge array if needed */
    3194         218 :         if (jstate->clocations_count >= jstate->clocations_buf_size)
    3195             :         {
    3196           0 :             jstate->clocations_buf_size *= 2;
    3197           0 :             jstate->clocations = (pgssLocationLen *)
    3198           0 :                 repalloc(jstate->clocations,
    3199           0 :                          jstate->clocations_buf_size *
    3200             :                          sizeof(pgssLocationLen));
    3201             :         }
    3202         218 :         jstate->clocations[jstate->clocations_count].location = location;
    3203             :         /* initialize lengths to -1 to simplify fill_in_constant_lengths */
    3204         218 :         jstate->clocations[jstate->clocations_count].length = -1;
    3205         218 :         jstate->clocations_count++;
    3206             :     }
    3207         270 : }
    3208             : 
    3209             : /*
    3210             :  * Generate a normalized version of the query string that will be used to
    3211             :  * represent all similar queries.
    3212             :  *
    3213             :  * Note that the normalized representation may well vary depending on
    3214             :  * just which "equivalent" query is used to create the hashtable entry.
    3215             :  * We assume this is OK.
    3216             :  *
    3217             :  * If query_loc > 0, then "query" has been advanced by that much compared to
    3218             :  * the original string start, so we need to translate the provided locations
    3219             :  * to compensate.  (This lets us avoid re-scanning statements before the one
    3220             :  * of interest, so it's worth doing.)
    3221             :  *
    3222             :  * *query_len_p contains the input string length, and is updated with
    3223             :  * the result string length on exit.  The resulting string might be longer
    3224             :  * or shorter depending on what happens with replacement of constants.
    3225             :  *
    3226             :  * Returns a palloc'd string.
    3227             :  */
    3228             : static char *
    3229          80 : generate_normalized_query(pgssJumbleState *jstate, const char *query,
    3230             :                           int query_loc, int *query_len_p, int encoding)
    3231             : {
    3232             :     char       *norm_query;
    3233          80 :     int         query_len = *query_len_p;
    3234             :     int         i,
    3235             :                 norm_query_buflen,  /* Space allowed for norm_query */
    3236             :                 len_to_wrt,     /* Length (in bytes) to write */
    3237          80 :                 quer_loc = 0,   /* Source query byte location */
    3238          80 :                 n_quer_loc = 0, /* Normalized query byte location */
    3239          80 :                 last_off = 0,   /* Offset from start for previous tok */
    3240          80 :                 last_tok_len = 0;   /* Length (in bytes) of that tok */
    3241             : 
    3242             :     /*
    3243             :      * Get constants' lengths (core system only gives us locations).  Note
    3244             :      * this also ensures the items are sorted by location.
    3245             :      */
    3246          80 :     fill_in_constant_lengths(jstate, query, query_loc);
    3247             : 
    3248             :     /*
    3249             :      * Allow for $n symbols to be longer than the constants they replace.
    3250             :      * Constants must take at least one byte in text form, while a $n symbol
    3251             :      * certainly isn't more than 11 bytes, even if n reaches INT_MAX.  We
    3252             :      * could refine that limit based on the max value of n for the current
    3253             :      * query, but it hardly seems worth any extra effort to do so.
    3254             :      */
    3255          80 :     norm_query_buflen = query_len + jstate->clocations_count * 10;
    3256             : 
    3257             :     /* Allocate result buffer */
    3258          80 :     norm_query = palloc(norm_query_buflen + 1);
    3259             : 
    3260         232 :     for (i = 0; i < jstate->clocations_count; i++)
    3261             :     {
    3262             :         int         off,        /* Offset from start for cur tok */
    3263             :                     tok_len;    /* Length (in bytes) of that tok */
    3264             : 
    3265         152 :         off = jstate->clocations[i].location;
    3266             :         /* Adjust recorded location if we're dealing with partial string */
    3267         152 :         off -= query_loc;
    3268             : 
    3269         152 :         tok_len = jstate->clocations[i].length;
    3270             : 
    3271         152 :         if (tok_len < 0)
    3272           0 :             continue;           /* ignore any duplicates */
    3273             : 
    3274             :         /* Copy next chunk (what precedes the next constant) */
    3275         152 :         len_to_wrt = off - last_off;
    3276         152 :         len_to_wrt -= last_tok_len;
    3277             : 
    3278             :         Assert(len_to_wrt >= 0);
    3279         152 :         memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
    3280         152 :         n_quer_loc += len_to_wrt;
    3281             : 
    3282             :         /* And insert a param symbol in place of the constant token */
    3283         304 :         n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
    3284         152 :                               i + 1 + jstate->highest_extern_param_id);
    3285             : 
    3286         152 :         quer_loc = off + tok_len;
    3287         152 :         last_off = off;
    3288         152 :         last_tok_len = tok_len;
    3289             :     }
    3290             : 
    3291             :     /*
    3292             :      * We've copied up until the last ignorable constant.  Copy over the
    3293             :      * remaining bytes of the original query string.
    3294             :      */
    3295          80 :     len_to_wrt = query_len - quer_loc;
    3296             : 
    3297             :     Assert(len_to_wrt >= 0);
    3298          80 :     memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
    3299          80 :     n_quer_loc += len_to_wrt;
    3300             : 
    3301             :     Assert(n_quer_loc <= norm_query_buflen);
    3302          80 :     norm_query[n_quer_loc] = '\0';
    3303             : 
    3304          80 :     *query_len_p = n_quer_loc;
    3305          80 :     return norm_query;
    3306             : }
    3307             : 
    3308             : /*
    3309             :  * Given a valid SQL string and an array of constant-location records,
    3310             :  * fill in the textual lengths of those constants.
    3311             :  *
    3312             :  * The constants may use any allowed constant syntax, such as float literals,
    3313             :  * bit-strings, single-quoted strings and dollar-quoted strings.  This is
    3314             :  * accomplished by using the public API for the core scanner.
    3315             :  *
    3316             :  * It is the caller's job to ensure that the string is a valid SQL statement
    3317             :  * with constants at the indicated locations.  Since in practice the string
    3318             :  * has already been parsed, and the locations that the caller provides will
    3319             :  * have originated from within the authoritative parser, this should not be
    3320             :  * a problem.
    3321             :  *
    3322             :  * Duplicate constant pointers are possible, and will have their lengths
    3323             :  * marked as '-1', so that they are later ignored.  (Actually, we assume the
    3324             :  * lengths were initialized as -1 to start with, and don't change them here.)
    3325             :  *
    3326             :  * If query_loc > 0, then "query" has been advanced by that much compared to
    3327             :  * the original string start, so we need to translate the provided locations
    3328             :  * to compensate.  (This lets us avoid re-scanning statements before the one
    3329             :  * of interest, so it's worth doing.)
    3330             :  *
    3331             :  * N.B. There is an assumption that a '-' character at a Const location begins
    3332             :  * a negative numeric constant.  This precludes there ever being another
    3333             :  * reason for a constant to start with a '-'.
    3334             :  */
    3335             : static void
    3336          80 : fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
    3337             :                          int query_loc)
    3338             : {
    3339             :     pgssLocationLen *locs;
    3340             :     core_yyscan_t yyscanner;
    3341             :     core_yy_extra_type yyextra;
    3342             :     core_YYSTYPE yylval;
    3343             :     YYLTYPE     yylloc;
    3344          80 :     int         last_loc = -1;
    3345             :     int         i;
    3346             : 
    3347             :     /*
    3348             :      * Sort the records by location so that we can process them in order while
    3349             :      * scanning the query text.
    3350             :      */
    3351          80 :     if (jstate->clocations_count > 1)
    3352          46 :         qsort(jstate->clocations, jstate->clocations_count,
    3353             :               sizeof(pgssLocationLen), comp_location);
    3354          80 :     locs = jstate->clocations;
    3355             : 
    3356             :     /* initialize the flex scanner --- should match raw_parser() */
    3357          80 :     yyscanner = scanner_init(query,
    3358             :                              &yyextra,
    3359             :                              &ScanKeywords,
    3360             :                              ScanKeywordTokens);
    3361             : 
    3362             :     /* we don't want to re-emit any escape string warnings */
    3363          80 :     yyextra.escape_string_warning = false;
    3364             : 
    3365             :     /* Search for each constant, in sequence */
    3366         232 :     for (i = 0; i < jstate->clocations_count; i++)
    3367             :     {
    3368         152 :         int         loc = locs[i].location;
    3369             :         int         tok;
    3370             : 
    3371             :         /* Adjust recorded location if we're dealing with partial string */
    3372         152 :         loc -= query_loc;
    3373             : 
    3374             :         Assert(loc >= 0);
    3375             : 
    3376         152 :         if (loc <= last_loc)
    3377           0 :             continue;           /* Duplicate constant, ignore */
    3378             : 
    3379             :         /* Lex tokens until we find the desired constant */
    3380             :         for (;;)
    3381             :         {
    3382         720 :             tok = core_yylex(&yylval, &yylloc, yyscanner);
    3383             : 
    3384             :             /* We should not hit end-of-string, but if we do, behave sanely */
    3385         720 :             if (tok == 0)
    3386           0 :                 break;          /* out of inner for-loop */
    3387             : 
    3388             :             /*
    3389             :              * We should find the token position exactly, but if we somehow
    3390             :              * run past it, work with that.
    3391             :              */
    3392         720 :             if (yylloc >= loc)
    3393             :             {
    3394         152 :                 if (query[loc] == '-')
    3395             :                 {
    3396             :                     /*
    3397             :                      * It's a negative value - this is the one and only case
    3398             :                      * where we replace more than a single token.
    3399             :                      *
    3400             :                      * Do not compensate for the core system's special-case
    3401             :                      * adjustment of location to that of the leading '-'
    3402             :                      * operator in the event of a negative constant.  It is
    3403             :                      * also useful for our purposes to start from the minus
    3404             :                      * symbol.  In this way, queries like "select * from foo
    3405             :                      * where bar = 1" and "select * from foo where bar = -2"
    3406             :                      * will have identical normalized query strings.
    3407             :                      */
    3408           2 :                     tok = core_yylex(&yylval, &yylloc, yyscanner);
    3409           2 :                     if (tok == 0)
    3410           0 :                         break;  /* out of inner for-loop */
    3411             :                 }
    3412             : 
    3413             :                 /*
    3414             :                  * We now rely on the assumption that flex has placed a zero
    3415             :                  * byte after the text of the current token in scanbuf.
    3416             :                  */
    3417         152 :                 locs[i].length = strlen(yyextra.scanbuf + loc);
    3418         152 :                 break;          /* out of inner for-loop */
    3419             :             }
    3420             :         }
    3421             : 
    3422             :         /* If we hit end-of-string, give up, leaving remaining lengths -1 */
    3423         152 :         if (tok == 0)
    3424           0 :             break;
    3425             : 
    3426         152 :         last_loc = loc;
    3427             :     }
    3428             : 
    3429          80 :     scanner_finish(yyscanner);
    3430          80 : }
    3431             : 
    3432             : /*
    3433             :  * comp_location: comparator for qsorting pgssLocationLen structs by location
    3434             :  */
    3435             : static int
    3436          74 : comp_location(const void *a, const void *b)
    3437             : {
    3438          74 :     int         l = ((const pgssLocationLen *) a)->location;
    3439          74 :     int         r = ((const pgssLocationLen *) b)->location;
    3440             : 
    3441          74 :     if (l < r)
    3442          64 :         return -1;
    3443          10 :     else if (l > r)
    3444          10 :         return +1;
    3445             :     else
    3446           0 :         return 0;
    3447             : }

Generated by: LCOV version 1.13