LCOV - code coverage report
Current view: top level - contrib/pg_stat_statements - pg_stat_statements.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 681 1130 60.3 %
Date: 2019-09-19 02:07:14 Functions: 34 43 79.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13