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