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