LCOV - code coverage report
Current view: top level - contrib/pg_stat_statements - pg_stat_statements.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 755 1259 60.0 %
Date: 2020-11-27 11:06:40 Functions: 38 48 79.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13