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