PostgreSQL Source Code  git master
pg_stat_statements.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_stat_statements.c
4  * Track statement planning and execution times as well as resource
5  * usage across a whole database cluster.
6  *
7  * Execution costs are totaled for each distinct source query, and kept in
8  * a shared hashtable. (We track only as many distinct queries as will fit
9  * in the designated amount of shared memory.)
10  *
11  * Starting in Postgres 9.2, this module normalized query entries. As of
12  * Postgres 14, the normalization is done by the core if compute_query_id is
13  * enabled, or optionally by third-party modules.
14  *
15  * To facilitate presenting entries to users, we create "representative" query
16  * strings in which constants are replaced with parameter symbols ($n), to
17  * make it clearer what a normalized entry can represent. To save on shared
18  * memory, and to avoid having to truncate oversized query strings, we store
19  * these strings in a temporary external query-texts file. Offsets into this
20  * file are kept in shared memory.
21  *
22  * Note about locking issues: to create or delete an entry in the shared
23  * hashtable, one must hold pgss->lock exclusively. Modifying any field
24  * in an entry except the counters requires the same. To look up an entry,
25  * one must hold the lock shared. To read or update the counters within
26  * an entry, one must hold the lock shared or exclusive (so the entry doesn't
27  * disappear!) and also take the entry's mutex spinlock.
28  * The shared state variable pgss->extent (the next free spot in the external
29  * query-text file) should be accessed only while holding either the
30  * pgss->mutex spinlock, or exclusive lock on pgss->lock. We use the mutex to
31  * allow reserving file space while holding only shared lock on pgss->lock.
32  * Rewriting the entire external query-text file, eg for garbage collection,
33  * requires holding pgss->lock exclusively; this allows individual entries
34  * in the file to be read or written while holding only shared lock.
35  *
36  *
37  * Copyright (c) 2008-2024, PostgreSQL Global Development Group
38  *
39  * IDENTIFICATION
40  * contrib/pg_stat_statements/pg_stat_statements.c
41  *
42  *-------------------------------------------------------------------------
43  */
44 #include "postgres.h"
45 
46 #include <math.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 
50 #include "access/parallel.h"
51 #include "catalog/pg_authid.h"
52 #include "common/hashfn.h"
53 #include "common/int.h"
54 #include "executor/instrument.h"
55 #include "funcapi.h"
56 #include "jit/jit.h"
57 #include "mb/pg_wchar.h"
58 #include "miscadmin.h"
59 #include "nodes/queryjumble.h"
60 #include "optimizer/planner.h"
61 #include "parser/analyze.h"
62 #include "parser/parsetree.h"
63 #include "parser/scanner.h"
64 #include "parser/scansup.h"
65 #include "pgstat.h"
66 #include "storage/fd.h"
67 #include "storage/ipc.h"
68 #include "storage/lwlock.h"
69 #include "storage/shmem.h"
70 #include "storage/spin.h"
71 #include "tcop/utility.h"
72 #include "utils/acl.h"
73 #include "utils/builtins.h"
74 #include "utils/memutils.h"
75 #include "utils/timestamp.h"
76 
78 
79 /* Location of permanent stats file (valid when database is shut down) */
80 #define PGSS_DUMP_FILE PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
81 
82 /*
83  * Location of external query text file.
84  */
85 #define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
86 
87 /* Magic number identifying the stats file format */
88 static const uint32 PGSS_FILE_HEADER = 0x20220408;
89 
90 /* PostgreSQL major version number, changes in which invalidate all entries */
91 static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
92 
93 /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */
94 #define USAGE_EXEC(duration) (1.0)
95 #define USAGE_INIT (1.0) /* including initial planning */
96 #define ASSUMED_MEDIAN_INIT (10.0) /* initial assumed median usage */
97 #define ASSUMED_LENGTH_INIT 1024 /* initial assumed mean query length */
98 #define USAGE_DECREASE_FACTOR (0.99) /* decreased every entry_dealloc */
99 #define STICKY_DECREASE_FACTOR (0.50) /* factor for sticky entries */
100 #define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */
101 #define IS_STICKY(c) ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
102 
103 /*
104  * Extension version number, for supporting older extension versions' objects
105  */
106 typedef enum pgssVersion
107 {
117 
118 typedef enum pgssStoreKind
119 {
121 
122  /*
123  * PGSS_PLAN and PGSS_EXEC must be respectively 0 and 1 as they're used to
124  * reference the underlying values in the arrays in the Counters struct,
125  * and this order is required in pg_stat_statements_internal().
126  */
129 
130  PGSS_NUMKIND /* Must be last value of this enum */
132 
133 /*
134  * Hashtable key that defines the identity of a hashtable entry. We separate
135  * queries by user and by database even if they are otherwise identical.
136  *
137  * If you add a new key to this struct, make sure to teach pgss_store() to
138  * zero the padding bytes. Otherwise, things will break, because pgss_hash is
139  * created using HASH_BLOBS, and thus tag_hash is used to hash this.
140 
141  */
142 typedef struct pgssHashKey
143 {
144  Oid userid; /* user OID */
145  Oid dbid; /* database OID */
146  uint64 queryid; /* query identifier */
147  bool toplevel; /* query executed at top level */
149 
150 /*
151  * The actual stats counters kept within pgssEntry.
152  */
153 typedef struct Counters
154 {
155  int64 calls[PGSS_NUMKIND]; /* # of times planned/executed */
156  double total_time[PGSS_NUMKIND]; /* total planning/execution time,
157  * in msec */
158  double min_time[PGSS_NUMKIND]; /* minimum planning/execution time in
159  * msec since min/max reset */
160  double max_time[PGSS_NUMKIND]; /* maximum planning/execution time in
161  * msec since min/max reset */
162  double mean_time[PGSS_NUMKIND]; /* mean planning/execution time in
163  * msec */
164  double sum_var_time[PGSS_NUMKIND]; /* sum of variances in
165  * planning/execution time in msec */
166  int64 rows; /* total # of retrieved or affected rows */
167  int64 shared_blks_hit; /* # of shared buffer hits */
168  int64 shared_blks_read; /* # of shared disk blocks read */
169  int64 shared_blks_dirtied; /* # of shared disk blocks dirtied */
170  int64 shared_blks_written; /* # of shared disk blocks written */
171  int64 local_blks_hit; /* # of local buffer hits */
172  int64 local_blks_read; /* # of local disk blocks read */
173  int64 local_blks_dirtied; /* # of local disk blocks dirtied */
174  int64 local_blks_written; /* # of local disk blocks written */
175  int64 temp_blks_read; /* # of temp blocks read */
176  int64 temp_blks_written; /* # of temp blocks written */
177  double shared_blk_read_time; /* time spent reading shared blocks,
178  * in msec */
179  double shared_blk_write_time; /* time spent writing shared blocks,
180  * in msec */
181  double local_blk_read_time; /* time spent reading local blocks, in
182  * msec */
183  double local_blk_write_time; /* time spent writing local blocks, in
184  * msec */
185  double temp_blk_read_time; /* time spent reading temp blocks, in msec */
186  double temp_blk_write_time; /* time spent writing temp blocks, in
187  * msec */
188  double usage; /* usage factor */
189  int64 wal_records; /* # of WAL records generated */
190  int64 wal_fpi; /* # of WAL full page images generated */
191  uint64 wal_bytes; /* total amount of WAL generated in bytes */
192  int64 jit_functions; /* total number of JIT functions emitted */
193  double jit_generation_time; /* total time to generate jit code */
194  int64 jit_inlining_count; /* number of times inlining time has been
195  * > 0 */
196  double jit_deform_time; /* total time to deform tuples in jit code */
197  int64 jit_deform_count; /* number of times deform time has been >
198  * 0 */
199 
200  double jit_inlining_time; /* total time to inline jit code */
201  int64 jit_optimization_count; /* number of times optimization time
202  * has been > 0 */
203  double jit_optimization_time; /* total time to optimize jit code */
204  int64 jit_emission_count; /* number of times emission time has been
205  * > 0 */
206  double jit_emission_time; /* total time to emit jit code */
208 
209 /*
210  * Global statistics for pg_stat_statements
211  */
212 typedef struct pgssGlobalStats
213 {
214  int64 dealloc; /* # of times entries were deallocated */
215  TimestampTz stats_reset; /* timestamp with all stats reset */
217 
218 /*
219  * Statistics per statement
220  *
221  * Note: in event of a failure in garbage collection of the query text file,
222  * we reset query_offset to zero and query_len to -1. This will be seen as
223  * an invalid state by qtext_fetch().
224  */
225 typedef struct pgssEntry
226 {
227  pgssHashKey key; /* hash key of entry - MUST BE FIRST */
228  Counters counters; /* the statistics for this query */
229  Size query_offset; /* query text offset in external file */
230  int query_len; /* # of valid bytes in query string, or -1 */
231  int encoding; /* query text encoding */
232  TimestampTz stats_since; /* timestamp of entry allocation */
233  TimestampTz minmax_stats_since; /* timestamp of last min/max values reset */
234  slock_t mutex; /* protects the counters only */
236 
237 /*
238  * Global shared state
239  */
240 typedef struct pgssSharedState
241 {
242  LWLock *lock; /* protects hashtable search/modification */
243  double cur_median_usage; /* current median usage in hashtable */
244  Size mean_query_len; /* current mean entry text length */
245  slock_t mutex; /* protects following fields only: */
246  Size extent; /* current extent of query file */
247  int n_writers; /* number of active writers to query file */
248  int gc_count; /* query file garbage collection cycle count */
249  pgssGlobalStats stats; /* global statistics for pgss */
251 
252 /*---- Local variables ----*/
253 
254 /* Current nesting depth of planner/ExecutorRun/ProcessUtility calls */
255 static int nesting_level = 0;
256 
257 /* Saved hook values in case of unload */
267 
268 /* Links to shared memory state */
269 static pgssSharedState *pgss = NULL;
270 static HTAB *pgss_hash = NULL;
271 
272 /*---- GUC variables ----*/
273 
274 typedef enum
275 {
276  PGSS_TRACK_NONE, /* track no statements */
277  PGSS_TRACK_TOP, /* only top level statements */
278  PGSS_TRACK_ALL, /* all statements, including nested ones */
280 
281 static const struct config_enum_entry track_options[] =
282 {
283  {"none", PGSS_TRACK_NONE, false},
284  {"top", PGSS_TRACK_TOP, false},
285  {"all", PGSS_TRACK_ALL, false},
286  {NULL, 0, false}
287 };
288 
289 static int pgss_max = 5000; /* max # statements to track */
290 static int pgss_track = PGSS_TRACK_TOP; /* tracking level */
291 static bool pgss_track_utility = true; /* whether to track utility commands */
292 static bool pgss_track_planning = false; /* whether to track planning
293  * duration */
294 static bool pgss_save = true; /* whether to save stats across shutdown */
295 
296 
297 #define pgss_enabled(level) \
298  (!IsParallelWorker() && \
299  (pgss_track == PGSS_TRACK_ALL || \
300  (pgss_track == PGSS_TRACK_TOP && (level) == 0)))
301 
302 #define record_gc_qtexts() \
303  do { \
304  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss; \
305  SpinLockAcquire(&s->mutex); \
306  s->gc_count++; \
307  SpinLockRelease(&s->mutex); \
308  } while(0)
309 
310 /*---- Function declarations ----*/
311 
323 
324 static void pgss_shmem_request(void);
325 static void pgss_shmem_startup(void);
326 static void pgss_shmem_shutdown(int code, Datum arg);
327 static void pgss_post_parse_analyze(ParseState *pstate, Query *query,
328  JumbleState *jstate);
330  const char *query_string,
331  int cursorOptions,
332  ParamListInfo boundParams);
333 static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
334 static void pgss_ExecutorRun(QueryDesc *queryDesc,
335  ScanDirection direction,
336  uint64 count, bool execute_once);
337 static void pgss_ExecutorFinish(QueryDesc *queryDesc);
338 static void pgss_ExecutorEnd(QueryDesc *queryDesc);
339 static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
340  bool readOnlyTree,
341  ProcessUtilityContext context, ParamListInfo params,
342  QueryEnvironment *queryEnv,
344 static void pgss_store(const char *query, uint64 queryId,
345  int query_location, int query_len,
346  pgssStoreKind kind,
347  double total_time, uint64 rows,
348  const BufferUsage *bufusage,
349  const WalUsage *walusage,
350  const struct JitInstrumentation *jitusage,
351  JumbleState *jstate);
353  pgssVersion api_version,
354  bool showtext);
355 static Size pgss_memsize(void);
356 static pgssEntry *entry_alloc(pgssHashKey *key, Size query_offset, int query_len,
357  int encoding, bool sticky);
358 static void entry_dealloc(void);
359 static bool qtext_store(const char *query, int query_len,
360  Size *query_offset, int *gc_count);
361 static char *qtext_load_file(Size *buffer_size);
362 static char *qtext_fetch(Size query_offset, int query_len,
363  char *buffer, Size buffer_size);
364 static bool need_gc_qtexts(void);
365 static void gc_qtexts(void);
366 static TimestampTz entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only);
367 static char *generate_normalized_query(JumbleState *jstate, const char *query,
368  int query_loc, int *query_len_p);
369 static void fill_in_constant_lengths(JumbleState *jstate, const char *query,
370  int query_loc);
371 static int comp_location(const void *a, const void *b);
372 
373 
374 /*
375  * Module load callback
376  */
377 void
378 _PG_init(void)
379 {
380  /*
381  * In order to create our shared memory area, we have to be loaded via
382  * shared_preload_libraries. If not, fall out without hooking into any of
383  * the main system. (We don't throw error here because it seems useful to
384  * allow the pg_stat_statements functions to be created even when the
385  * module isn't active. The functions must protect themselves against
386  * being called then, however.)
387  */
389  return;
390 
391  /*
392  * Inform the postmaster that we want to enable query_id calculation if
393  * compute_query_id is set to auto.
394  */
395  EnableQueryId();
396 
397  /*
398  * Define (or redefine) custom GUC variables.
399  */
400  DefineCustomIntVariable("pg_stat_statements.max",
401  "Sets the maximum number of statements tracked by pg_stat_statements.",
402  NULL,
403  &pgss_max,
404  5000,
405  100,
406  INT_MAX / 2,
408  0,
409  NULL,
410  NULL,
411  NULL);
412 
413  DefineCustomEnumVariable("pg_stat_statements.track",
414  "Selects which statements are tracked by pg_stat_statements.",
415  NULL,
416  &pgss_track,
419  PGC_SUSET,
420  0,
421  NULL,
422  NULL,
423  NULL);
424 
425  DefineCustomBoolVariable("pg_stat_statements.track_utility",
426  "Selects whether utility commands are tracked by pg_stat_statements.",
427  NULL,
429  true,
430  PGC_SUSET,
431  0,
432  NULL,
433  NULL,
434  NULL);
435 
436  DefineCustomBoolVariable("pg_stat_statements.track_planning",
437  "Selects whether planning duration is tracked by pg_stat_statements.",
438  NULL,
440  false,
441  PGC_SUSET,
442  0,
443  NULL,
444  NULL,
445  NULL);
446 
447  DefineCustomBoolVariable("pg_stat_statements.save",
448  "Save pg_stat_statements statistics across server shutdowns.",
449  NULL,
450  &pgss_save,
451  true,
452  PGC_SIGHUP,
453  0,
454  NULL,
455  NULL,
456  NULL);
457 
458  MarkGUCPrefixReserved("pg_stat_statements");
459 
460  /*
461  * Install hooks.
462  */
481 }
482 
483 /*
484  * shmem_request hook: request additional shared resources. We'll allocate or
485  * attach to the shared resources in pgss_shmem_startup().
486  */
487 static void
489 {
492 
494  RequestNamedLWLockTranche("pg_stat_statements", 1);
495 }
496 
497 /*
498  * shmem_startup hook: allocate or attach to shared memory,
499  * then load any pre-existing statistics from file.
500  * Also create and load the query-texts file, which is expected to exist
501  * (even if empty) while the module is enabled.
502  */
503 static void
505 {
506  bool found;
507  HASHCTL info;
508  FILE *file = NULL;
509  FILE *qfile = NULL;
510  uint32 header;
511  int32 num;
512  int32 pgver;
513  int32 i;
514  int buffer_size;
515  char *buffer = NULL;
516 
519 
520  /* reset in case this is a restart within the postmaster */
521  pgss = NULL;
522  pgss_hash = NULL;
523 
524  /*
525  * Create or attach to the shared memory state, including hash table
526  */
527  LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
528 
529  pgss = ShmemInitStruct("pg_stat_statements",
530  sizeof(pgssSharedState),
531  &found);
532 
533  if (!found)
534  {
535  /* First time through ... */
536  pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock;
540  pgss->extent = 0;
541  pgss->n_writers = 0;
542  pgss->gc_count = 0;
543  pgss->stats.dealloc = 0;
545  }
546 
547  info.keysize = sizeof(pgssHashKey);
548  info.entrysize = sizeof(pgssEntry);
549  pgss_hash = ShmemInitHash("pg_stat_statements hash",
551  &info,
553 
554  LWLockRelease(AddinShmemInitLock);
555 
556  /*
557  * If we're in the postmaster (or a standalone backend...), set up a shmem
558  * exit hook to dump the statistics to disk.
559  */
560  if (!IsUnderPostmaster)
562 
563  /*
564  * Done if some other process already completed our initialization.
565  */
566  if (found)
567  return;
568 
569  /*
570  * Note: we don't bother with locks here, because there should be no other
571  * processes running when this code is reached.
572  */
573 
574  /* Unlink query text file possibly left over from crash */
575  unlink(PGSS_TEXT_FILE);
576 
577  /* Allocate new query text temp file */
579  if (qfile == NULL)
580  goto write_error;
581 
582  /*
583  * If we were told not to load old statistics, we're done. (Note we do
584  * not try to unlink any old dump file in this case. This seems a bit
585  * questionable but it's the historical behavior.)
586  */
587  if (!pgss_save)
588  {
589  FreeFile(qfile);
590  return;
591  }
592 
593  /*
594  * Attempt to load old statistics from the dump file.
595  */
597  if (file == NULL)
598  {
599  if (errno != ENOENT)
600  goto read_error;
601  /* No existing persisted stats file, so we're done */
602  FreeFile(qfile);
603  return;
604  }
605 
606  buffer_size = 2048;
607  buffer = (char *) palloc(buffer_size);
608 
609  if (fread(&header, sizeof(uint32), 1, file) != 1 ||
610  fread(&pgver, sizeof(uint32), 1, file) != 1 ||
611  fread(&num, sizeof(int32), 1, file) != 1)
612  goto read_error;
613 
614  if (header != PGSS_FILE_HEADER ||
615  pgver != PGSS_PG_MAJOR_VERSION)
616  goto data_error;
617 
618  for (i = 0; i < num; i++)
619  {
620  pgssEntry temp;
621  pgssEntry *entry;
622  Size query_offset;
623 
624  if (fread(&temp, sizeof(pgssEntry), 1, file) != 1)
625  goto read_error;
626 
627  /* Encoding is the only field we can easily sanity-check */
628  if (!PG_VALID_BE_ENCODING(temp.encoding))
629  goto data_error;
630 
631  /* Resize buffer as needed */
632  if (temp.query_len >= buffer_size)
633  {
634  buffer_size = Max(buffer_size * 2, temp.query_len + 1);
635  buffer = repalloc(buffer, buffer_size);
636  }
637 
638  if (fread(buffer, 1, temp.query_len + 1, file) != temp.query_len + 1)
639  goto read_error;
640 
641  /* Should have a trailing null, but let's make sure */
642  buffer[temp.query_len] = '\0';
643 
644  /* Skip loading "sticky" entries */
645  if (IS_STICKY(temp.counters))
646  continue;
647 
648  /* Store the query text */
649  query_offset = pgss->extent;
650  if (fwrite(buffer, 1, temp.query_len + 1, qfile) != temp.query_len + 1)
651  goto write_error;
652  pgss->extent += temp.query_len + 1;
653 
654  /* make the hashtable entry (discards old entries if too many) */
655  entry = entry_alloc(&temp.key, query_offset, temp.query_len,
656  temp.encoding,
657  false);
658 
659  /* copy in the actual stats */
660  entry->counters = temp.counters;
661  entry->stats_since = temp.stats_since;
663  }
664 
665  /* Read global statistics for pg_stat_statements */
666  if (fread(&pgss->stats, sizeof(pgssGlobalStats), 1, file) != 1)
667  goto read_error;
668 
669  pfree(buffer);
670  FreeFile(file);
671  FreeFile(qfile);
672 
673  /*
674  * Remove the persisted stats file so it's not included in
675  * backups/replication standbys, etc. A new file will be written on next
676  * shutdown.
677  *
678  * Note: it's okay if the PGSS_TEXT_FILE is included in a basebackup,
679  * because we remove that file on startup; it acts inversely to
680  * PGSS_DUMP_FILE, in that it is only supposed to be around when the
681  * server is running, whereas PGSS_DUMP_FILE is only supposed to be around
682  * when the server is not running. Leaving the file creates no danger of
683  * a newly restored database having a spurious record of execution costs,
684  * which is what we're really concerned about here.
685  */
686  unlink(PGSS_DUMP_FILE);
687 
688  return;
689 
690 read_error:
691  ereport(LOG,
693  errmsg("could not read file \"%s\": %m",
694  PGSS_DUMP_FILE)));
695  goto fail;
696 data_error:
697  ereport(LOG,
698  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
699  errmsg("ignoring invalid data in file \"%s\"",
700  PGSS_DUMP_FILE)));
701  goto fail;
702 write_error:
703  ereport(LOG,
705  errmsg("could not write file \"%s\": %m",
706  PGSS_TEXT_FILE)));
707 fail:
708  if (buffer)
709  pfree(buffer);
710  if (file)
711  FreeFile(file);
712  if (qfile)
713  FreeFile(qfile);
714  /* If possible, throw away the bogus file; ignore any error */
715  unlink(PGSS_DUMP_FILE);
716 
717  /*
718  * Don't unlink PGSS_TEXT_FILE here; it should always be around while the
719  * server is running with pg_stat_statements enabled
720  */
721 }
722 
723 /*
724  * shmem_shutdown hook: Dump statistics into file.
725  *
726  * Note: we don't bother with acquiring lock, because there should be no
727  * other processes running when this is called.
728  */
729 static void
731 {
732  FILE *file;
733  char *qbuffer = NULL;
734  Size qbuffer_size = 0;
735  HASH_SEQ_STATUS hash_seq;
736  int32 num_entries;
737  pgssEntry *entry;
738 
739  /* Don't try to dump during a crash. */
740  if (code)
741  return;
742 
743  /* Safety check ... shouldn't get here unless shmem is set up. */
744  if (!pgss || !pgss_hash)
745  return;
746 
747  /* Don't dump if told not to. */
748  if (!pgss_save)
749  return;
750 
751  file = AllocateFile(PGSS_DUMP_FILE ".tmp", PG_BINARY_W);
752  if (file == NULL)
753  goto error;
754 
755  if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1)
756  goto error;
757  if (fwrite(&PGSS_PG_MAJOR_VERSION, sizeof(uint32), 1, file) != 1)
758  goto error;
759  num_entries = hash_get_num_entries(pgss_hash);
760  if (fwrite(&num_entries, sizeof(int32), 1, file) != 1)
761  goto error;
762 
763  qbuffer = qtext_load_file(&qbuffer_size);
764  if (qbuffer == NULL)
765  goto error;
766 
767  /*
768  * When serializing to disk, we store query texts immediately after their
769  * entry data. Any orphaned query texts are thereby excluded.
770  */
771  hash_seq_init(&hash_seq, pgss_hash);
772  while ((entry = hash_seq_search(&hash_seq)) != NULL)
773  {
774  int len = entry->query_len;
775  char *qstr = qtext_fetch(entry->query_offset, len,
776  qbuffer, qbuffer_size);
777 
778  if (qstr == NULL)
779  continue; /* Ignore any entries with bogus texts */
780 
781  if (fwrite(entry, sizeof(pgssEntry), 1, file) != 1 ||
782  fwrite(qstr, 1, len + 1, file) != len + 1)
783  {
784  /* note: we assume hash_seq_term won't change errno */
785  hash_seq_term(&hash_seq);
786  goto error;
787  }
788  }
789 
790  /* Dump global statistics for pg_stat_statements */
791  if (fwrite(&pgss->stats, sizeof(pgssGlobalStats), 1, file) != 1)
792  goto error;
793 
794  free(qbuffer);
795  qbuffer = NULL;
796 
797  if (FreeFile(file))
798  {
799  file = NULL;
800  goto error;
801  }
802 
803  /*
804  * Rename file into place, so we atomically replace any old one.
805  */
807 
808  /* Unlink query-texts file; it's not needed while shutdown */
809  unlink(PGSS_TEXT_FILE);
810 
811  return;
812 
813 error:
814  ereport(LOG,
816  errmsg("could not write file \"%s\": %m",
817  PGSS_DUMP_FILE ".tmp")));
818  free(qbuffer);
819  if (file)
820  FreeFile(file);
821  unlink(PGSS_DUMP_FILE ".tmp");
822  unlink(PGSS_TEXT_FILE);
823 }
824 
825 /*
826  * Post-parse-analysis hook: mark query with a queryId
827  */
828 static void
830 {
832  prev_post_parse_analyze_hook(pstate, query, jstate);
833 
834  /* Safety check... */
836  return;
837 
838  /*
839  * If it's EXECUTE, clear the queryId so that stats will accumulate for
840  * the underlying PREPARE. But don't do this if we're not tracking
841  * utility statements, to avoid messing up another extension that might be
842  * tracking them.
843  */
844  if (query->utilityStmt)
845  {
847  {
848  query->queryId = UINT64CONST(0);
849  return;
850  }
851  }
852 
853  /*
854  * If query jumbling were able to identify any ignorable constants, we
855  * immediately create a hash table entry for the query, so that we can
856  * record the normalized form of the query string. If there were no such
857  * constants, the normalized string would be the same as the query text
858  * anyway, so there's no need for an early entry.
859  */
860  if (jstate && jstate->clocations_count > 0)
861  pgss_store(pstate->p_sourcetext,
862  query->queryId,
863  query->stmt_location,
864  query->stmt_len,
865  PGSS_INVALID,
866  0,
867  0,
868  NULL,
869  NULL,
870  NULL,
871  jstate);
872 }
873 
874 /*
875  * Planner hook: forward to regular planner, but measure planning time
876  * if needed.
877  */
878 static PlannedStmt *
880  const char *query_string,
881  int cursorOptions,
882  ParamListInfo boundParams)
883 {
884  PlannedStmt *result;
885 
886  /*
887  * We can't process the query if no query_string is provided, as
888  * pgss_store needs it. We also ignore query without queryid, as it would
889  * be treated as a utility statement, which may not be the case.
890  *
891  * Note that planner_hook can be called from the planner itself, so we
892  * have a specific nesting level for the planner. However, utility
893  * commands containing optimizable statements can also call the planner,
894  * same for regular DML (for instance for underlying foreign key queries).
895  * So testing the planner nesting level only is not enough to detect real
896  * top level planner call.
897  */
899  && pgss_track_planning && query_string
900  && parse->queryId != UINT64CONST(0))
901  {
902  instr_time start;
904  BufferUsage bufusage_start,
905  bufusage;
906  WalUsage walusage_start,
907  walusage;
908 
909  /* We need to track buffer usage as the planner can access them. */
910  bufusage_start = pgBufferUsage;
911 
912  /*
913  * Similarly the planner could write some WAL records in some cases
914  * (e.g. setting a hint bit with those being WAL-logged)
915  */
916  walusage_start = pgWalUsage;
917  INSTR_TIME_SET_CURRENT(start);
918 
919  nesting_level++;
920  PG_TRY();
921  {
922  if (prev_planner_hook)
923  result = prev_planner_hook(parse, query_string, cursorOptions,
924  boundParams);
925  else
926  result = standard_planner(parse, query_string, cursorOptions,
927  boundParams);
928  }
929  PG_FINALLY();
930  {
931  nesting_level--;
932  }
933  PG_END_TRY();
934 
937 
938  /* calc differences of buffer counters. */
939  memset(&bufusage, 0, sizeof(BufferUsage));
940  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
941 
942  /* calc differences of WAL counters. */
943  memset(&walusage, 0, sizeof(WalUsage));
944  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
945 
946  pgss_store(query_string,
947  parse->queryId,
948  parse->stmt_location,
949  parse->stmt_len,
950  PGSS_PLAN,
952  0,
953  &bufusage,
954  &walusage,
955  NULL,
956  NULL);
957  }
958  else
959  {
960  /*
961  * Even though we're not tracking plan time for this statement, we
962  * must still increment the nesting level, to ensure that functions
963  * evaluated during planning are not seen as top-level calls.
964  */
965  nesting_level++;
966  PG_TRY();
967  {
968  if (prev_planner_hook)
969  result = prev_planner_hook(parse, query_string, cursorOptions,
970  boundParams);
971  else
972  result = standard_planner(parse, query_string, cursorOptions,
973  boundParams);
974  }
975  PG_FINALLY();
976  {
977  nesting_level--;
978  }
979  PG_END_TRY();
980  }
981 
982  return result;
983 }
984 
985 /*
986  * ExecutorStart hook: start up tracking if needed
987  */
988 static void
989 pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
990 {
991  if (prev_ExecutorStart)
992  prev_ExecutorStart(queryDesc, eflags);
993  else
994  standard_ExecutorStart(queryDesc, eflags);
995 
996  /*
997  * If query has queryId zero, don't track it. This prevents double
998  * counting of optimizable statements that are directly contained in
999  * utility statements.
1000  */
1001  if (pgss_enabled(nesting_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
1002  {
1003  /*
1004  * Set up to track total elapsed time in ExecutorRun. Make sure the
1005  * space is allocated in the per-query context so it will go away at
1006  * ExecutorEnd.
1007  */
1008  if (queryDesc->totaltime == NULL)
1009  {
1010  MemoryContext oldcxt;
1011 
1012  oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
1013  queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false);
1014  MemoryContextSwitchTo(oldcxt);
1015  }
1016  }
1017 }
1018 
1019 /*
1020  * ExecutorRun hook: all we need do is track nesting depth
1021  */
1022 static void
1023 pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count,
1024  bool execute_once)
1025 {
1026  nesting_level++;
1027  PG_TRY();
1028  {
1029  if (prev_ExecutorRun)
1030  prev_ExecutorRun(queryDesc, direction, count, execute_once);
1031  else
1032  standard_ExecutorRun(queryDesc, direction, count, execute_once);
1033  }
1034  PG_FINALLY();
1035  {
1036  nesting_level--;
1037  }
1038  PG_END_TRY();
1039 }
1040 
1041 /*
1042  * ExecutorFinish hook: all we need do is track nesting depth
1043  */
1044 static void
1046 {
1047  nesting_level++;
1048  PG_TRY();
1049  {
1050  if (prev_ExecutorFinish)
1051  prev_ExecutorFinish(queryDesc);
1052  else
1053  standard_ExecutorFinish(queryDesc);
1054  }
1055  PG_FINALLY();
1056  {
1057  nesting_level--;
1058  }
1059  PG_END_TRY();
1060 }
1061 
1062 /*
1063  * ExecutorEnd hook: store results if needed
1064  */
1065 static void
1067 {
1068  uint64 queryId = queryDesc->plannedstmt->queryId;
1069 
1070  if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
1072  {
1073  /*
1074  * Make sure stats accumulation is done. (Note: it's okay if several
1075  * levels of hook all do this.)
1076  */
1077  InstrEndLoop(queryDesc->totaltime);
1078 
1079  pgss_store(queryDesc->sourceText,
1080  queryId,
1081  queryDesc->plannedstmt->stmt_location,
1082  queryDesc->plannedstmt->stmt_len,
1083  PGSS_EXEC,
1084  queryDesc->totaltime->total * 1000.0, /* convert to msec */
1085  queryDesc->estate->es_total_processed,
1086  &queryDesc->totaltime->bufusage,
1087  &queryDesc->totaltime->walusage,
1088  queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
1089  NULL);
1090  }
1091 
1092  if (prev_ExecutorEnd)
1093  prev_ExecutorEnd(queryDesc);
1094  else
1095  standard_ExecutorEnd(queryDesc);
1096 }
1097 
1098 /*
1099  * ProcessUtility hook
1100  */
1101 static void
1102 pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
1103  bool readOnlyTree,
1104  ProcessUtilityContext context,
1105  ParamListInfo params, QueryEnvironment *queryEnv,
1107 {
1108  Node *parsetree = pstmt->utilityStmt;
1109  uint64 saved_queryId = pstmt->queryId;
1110  int saved_stmt_location = pstmt->stmt_location;
1111  int saved_stmt_len = pstmt->stmt_len;
1112  bool enabled = pgss_track_utility && pgss_enabled(nesting_level);
1113 
1114  /*
1115  * Force utility statements to get queryId zero. We do this even in cases
1116  * where the statement contains an optimizable statement for which a
1117  * queryId could be derived (such as EXPLAIN or DECLARE CURSOR). For such
1118  * cases, runtime control will first go through ProcessUtility and then
1119  * the executor, and we don't want the executor hooks to do anything,
1120  * since we are already measuring the statement's costs at the utility
1121  * level.
1122  *
1123  * Note that this is only done if pg_stat_statements is enabled and
1124  * configured to track utility statements, in the unlikely possibility
1125  * that user configured another extension to handle utility statements
1126  * only.
1127  */
1128  if (enabled)
1129  pstmt->queryId = UINT64CONST(0);
1130 
1131  /*
1132  * If it's an EXECUTE statement, we don't track it and don't increment the
1133  * nesting level. This allows the cycles to be charged to the underlying
1134  * PREPARE instead (by the Executor hooks), which is much more useful.
1135  *
1136  * We also don't track execution of PREPARE. If we did, we would get one
1137  * hash table entry for the PREPARE (with hash calculated from the query
1138  * string), and then a different one with the same query string (but hash
1139  * calculated from the query tree) would be used to accumulate costs of
1140  * ensuing EXECUTEs. This would be confusing. Since PREPARE doesn't
1141  * actually run the planner (only parse+rewrite), its costs are generally
1142  * pretty negligible and it seems okay to just ignore it.
1143  */
1144  if (enabled &&
1145  !IsA(parsetree, ExecuteStmt) &&
1146  !IsA(parsetree, PrepareStmt))
1147  {
1148  instr_time start;
1150  uint64 rows;
1151  BufferUsage bufusage_start,
1152  bufusage;
1153  WalUsage walusage_start,
1154  walusage;
1155 
1156  bufusage_start = pgBufferUsage;
1157  walusage_start = pgWalUsage;
1158  INSTR_TIME_SET_CURRENT(start);
1159 
1160  nesting_level++;
1161  PG_TRY();
1162  {
1163  if (prev_ProcessUtility)
1164  prev_ProcessUtility(pstmt, queryString, readOnlyTree,
1165  context, params, queryEnv,
1166  dest, qc);
1167  else
1168  standard_ProcessUtility(pstmt, queryString, readOnlyTree,
1169  context, params, queryEnv,
1170  dest, qc);
1171  }
1172  PG_FINALLY();
1173  {
1174  nesting_level--;
1175  }
1176  PG_END_TRY();
1177 
1178  /*
1179  * CAUTION: do not access the *pstmt data structure again below here.
1180  * If it was a ROLLBACK or similar, that data structure may have been
1181  * freed. We must copy everything we still need into local variables,
1182  * which we did above.
1183  *
1184  * For the same reason, we can't risk restoring pstmt->queryId to its
1185  * former value, which'd otherwise be a good idea.
1186  */
1187 
1189  INSTR_TIME_SUBTRACT(duration, start);
1190 
1191  /*
1192  * Track the total number of rows retrieved or affected by the utility
1193  * statements of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED
1194  * VIEW, REFRESH MATERIALIZED VIEW and SELECT INTO.
1195  */
1196  rows = (qc && (qc->commandTag == CMDTAG_COPY ||
1197  qc->commandTag == CMDTAG_FETCH ||
1198  qc->commandTag == CMDTAG_SELECT ||
1199  qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW)) ?
1200  qc->nprocessed : 0;
1201 
1202  /* calc differences of buffer counters. */
1203  memset(&bufusage, 0, sizeof(BufferUsage));
1204  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
1205 
1206  /* calc differences of WAL counters. */
1207  memset(&walusage, 0, sizeof(WalUsage));
1208  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
1209 
1210  pgss_store(queryString,
1211  saved_queryId,
1212  saved_stmt_location,
1213  saved_stmt_len,
1214  PGSS_EXEC,
1216  rows,
1217  &bufusage,
1218  &walusage,
1219  NULL,
1220  NULL);
1221  }
1222  else
1223  {
1224  /*
1225  * Even though we're not tracking execution time for this statement,
1226  * we must still increment the nesting level, to ensure that functions
1227  * evaluated within it are not seen as top-level calls. But don't do
1228  * so for EXECUTE; that way, when control reaches pgss_planner or
1229  * pgss_ExecutorStart, we will treat the costs as top-level if
1230  * appropriate. Likewise, don't bump for PREPARE, so that parse
1231  * analysis will treat the statement as top-level if appropriate.
1232  *
1233  * To be absolutely certain we don't mess up the nesting level,
1234  * evaluate the bump_level condition just once.
1235  */
1236  bool bump_level =
1237  !IsA(parsetree, ExecuteStmt) &&
1238  !IsA(parsetree, PrepareStmt);
1239 
1240  if (bump_level)
1241  nesting_level++;
1242  PG_TRY();
1243  {
1244  if (prev_ProcessUtility)
1245  prev_ProcessUtility(pstmt, queryString, readOnlyTree,
1246  context, params, queryEnv,
1247  dest, qc);
1248  else
1249  standard_ProcessUtility(pstmt, queryString, readOnlyTree,
1250  context, params, queryEnv,
1251  dest, qc);
1252  }
1253  PG_FINALLY();
1254  {
1255  if (bump_level)
1256  nesting_level--;
1257  }
1258  PG_END_TRY();
1259  }
1260 }
1261 
1262 /*
1263  * Store some statistics for a statement.
1264  *
1265  * If jstate is not NULL then we're trying to create an entry for which
1266  * we have no statistics as yet; we just want to record the normalized
1267  * query string. total_time, rows, bufusage and walusage are ignored in this
1268  * case.
1269  *
1270  * If kind is PGSS_PLAN or PGSS_EXEC, its value is used as the array position
1271  * for the arrays in the Counters field.
1272  */
1273 static void
1274 pgss_store(const char *query, uint64 queryId,
1275  int query_location, int query_len,
1276  pgssStoreKind kind,
1277  double total_time, uint64 rows,
1278  const BufferUsage *bufusage,
1279  const WalUsage *walusage,
1280  const struct JitInstrumentation *jitusage,
1281  JumbleState *jstate)
1282 {
1283  pgssHashKey key;
1284  pgssEntry *entry;
1285  char *norm_query = NULL;
1286  int encoding = GetDatabaseEncoding();
1287 
1288  Assert(query != NULL);
1289 
1290  /* Safety check... */
1291  if (!pgss || !pgss_hash)
1292  return;
1293 
1294  /*
1295  * Nothing to do if compute_query_id isn't enabled and no other module
1296  * computed a query identifier.
1297  */
1298  if (queryId == UINT64CONST(0))
1299  return;
1300 
1301  /*
1302  * Confine our attention to the relevant part of the string, if the query
1303  * is a portion of a multi-statement source string, and update query
1304  * location and length if needed.
1305  */
1306  query = CleanQuerytext(query, &query_location, &query_len);
1307 
1308  /* Set up key for hashtable search */
1309 
1310  /* clear padding */
1311  memset(&key, 0, sizeof(pgssHashKey));
1312 
1313  key.userid = GetUserId();
1314  key.dbid = MyDatabaseId;
1315  key.queryid = queryId;
1316  key.toplevel = (nesting_level == 0);
1317 
1318  /* Lookup the hash table entry with shared lock. */
1320 
1321  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
1322 
1323  /* Create new entry, if not present */
1324  if (!entry)
1325  {
1326  Size query_offset;
1327  int gc_count;
1328  bool stored;
1329  bool do_gc;
1330 
1331  /*
1332  * Create a new, normalized query string if caller asked. We don't
1333  * need to hold the lock while doing this work. (Note: in any case,
1334  * it's possible that someone else creates a duplicate hashtable entry
1335  * in the interval where we don't hold the lock below. That case is
1336  * handled by entry_alloc.)
1337  */
1338  if (jstate)
1339  {
1341  norm_query = generate_normalized_query(jstate, query,
1342  query_location,
1343  &query_len);
1345  }
1346 
1347  /* Append new query text to file with only shared lock held */
1348  stored = qtext_store(norm_query ? norm_query : query, query_len,
1349  &query_offset, &gc_count);
1350 
1351  /*
1352  * Determine whether we need to garbage collect external query texts
1353  * while the shared lock is still held. This micro-optimization
1354  * avoids taking the time to decide this while holding exclusive lock.
1355  */
1356  do_gc = need_gc_qtexts();
1357 
1358  /* Need exclusive lock to make a new hashtable entry - promote */
1361 
1362  /*
1363  * A garbage collection may have occurred while we weren't holding the
1364  * lock. In the unlikely event that this happens, the query text we
1365  * stored above will have been garbage collected, so write it again.
1366  * This should be infrequent enough that doing it while holding
1367  * exclusive lock isn't a performance problem.
1368  */
1369  if (!stored || pgss->gc_count != gc_count)
1370  stored = qtext_store(norm_query ? norm_query : query, query_len,
1371  &query_offset, NULL);
1372 
1373  /* If we failed to write to the text file, give up */
1374  if (!stored)
1375  goto done;
1376 
1377  /* OK to create a new hashtable entry */
1378  entry = entry_alloc(&key, query_offset, query_len, encoding,
1379  jstate != NULL);
1380 
1381  /* If needed, perform garbage collection while exclusive lock held */
1382  if (do_gc)
1383  gc_qtexts();
1384  }
1385 
1386  /* Increment the counts, except when jstate is not NULL */
1387  if (!jstate)
1388  {
1389  /*
1390  * Grab the spinlock while updating the counters (see comment about
1391  * locking rules at the head of the file)
1392  */
1393  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1394 
1395  Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
1396 
1397  SpinLockAcquire(&e->mutex);
1398 
1399  /* "Unstick" entry if it was previously sticky */
1400  if (IS_STICKY(e->counters))
1401  e->counters.usage = USAGE_INIT;
1402 
1403  e->counters.calls[kind] += 1;
1404  e->counters.total_time[kind] += total_time;
1405 
1406  if (e->counters.calls[kind] == 1)
1407  {
1408  e->counters.min_time[kind] = total_time;
1409  e->counters.max_time[kind] = total_time;
1410  e->counters.mean_time[kind] = total_time;
1411  }
1412  else
1413  {
1414  /*
1415  * Welford's method for accurately computing variance. See
1416  * <http://www.johndcook.com/blog/standard_deviation/>
1417  */
1418  double old_mean = e->counters.mean_time[kind];
1419 
1420  e->counters.mean_time[kind] +=
1421  (total_time - old_mean) / e->counters.calls[kind];
1422  e->counters.sum_var_time[kind] +=
1423  (total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
1424 
1425  /*
1426  * Calculate min and max time. min = 0 and max = 0 means that the
1427  * min/max statistics were reset
1428  */
1429  if (e->counters.min_time[kind] == 0
1430  && e->counters.max_time[kind] == 0)
1431  {
1432  e->counters.min_time[kind] = total_time;
1433  e->counters.max_time[kind] = total_time;
1434  }
1435  else
1436  {
1437  if (e->counters.min_time[kind] > total_time)
1438  e->counters.min_time[kind] = total_time;
1439  if (e->counters.max_time[kind] < total_time)
1440  e->counters.max_time[kind] = total_time;
1441  }
1442  }
1443  e->counters.rows += rows;
1444  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1445  e->counters.shared_blks_read += bufusage->shared_blks_read;
1446  e->counters.shared_blks_dirtied += bufusage->shared_blks_dirtied;
1447  e->counters.shared_blks_written += bufusage->shared_blks_written;
1448  e->counters.local_blks_hit += bufusage->local_blks_hit;
1449  e->counters.local_blks_read += bufusage->local_blks_read;
1450  e->counters.local_blks_dirtied += bufusage->local_blks_dirtied;
1451  e->counters.local_blks_written += bufusage->local_blks_written;
1452  e->counters.temp_blks_read += bufusage->temp_blks_read;
1453  e->counters.temp_blks_written += bufusage->temp_blks_written;
1454  e->counters.shared_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->shared_blk_read_time);
1455  e->counters.shared_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->shared_blk_write_time);
1456  e->counters.local_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->local_blk_read_time);
1457  e->counters.local_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->local_blk_write_time);
1458  e->counters.temp_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_read_time);
1459  e->counters.temp_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_write_time);
1460  e->counters.usage += USAGE_EXEC(total_time);
1461  e->counters.wal_records += walusage->wal_records;
1462  e->counters.wal_fpi += walusage->wal_fpi;
1463  e->counters.wal_bytes += walusage->wal_bytes;
1464  if (jitusage)
1465  {
1466  e->counters.jit_functions += jitusage->created_functions;
1467  e->counters.jit_generation_time += INSTR_TIME_GET_MILLISEC(jitusage->generation_counter);
1468 
1469  if (INSTR_TIME_GET_MILLISEC(jitusage->deform_counter))
1470  e->counters.jit_deform_count++;
1471  e->counters.jit_deform_time += INSTR_TIME_GET_MILLISEC(jitusage->deform_counter);
1472 
1474  e->counters.jit_inlining_count++;
1475  e->counters.jit_inlining_time += INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter);
1476 
1478  e->counters.jit_optimization_count++;
1479  e->counters.jit_optimization_time += INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter);
1480 
1482  e->counters.jit_emission_count++;
1483  e->counters.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
1484  }
1485 
1486  SpinLockRelease(&e->mutex);
1487  }
1488 
1489 done:
1491 
1492  /* We postpone this clean-up until we're out of the lock */
1493  if (norm_query)
1494  pfree(norm_query);
1495 }
1496 
1497 /*
1498  * Reset statement statistics corresponding to userid, dbid, and queryid.
1499  */
1500 Datum
1502 {
1503  Oid userid;
1504  Oid dbid;
1505  uint64 queryid;
1506 
1507  userid = PG_GETARG_OID(0);
1508  dbid = PG_GETARG_OID(1);
1509  queryid = (uint64) PG_GETARG_INT64(2);
1510 
1511  entry_reset(userid, dbid, queryid, false);
1512 
1513  PG_RETURN_VOID();
1514 }
1515 
1516 Datum
1518 {
1519  Oid userid;
1520  Oid dbid;
1521  uint64 queryid;
1522  bool minmax_only;
1523 
1524  userid = PG_GETARG_OID(0);
1525  dbid = PG_GETARG_OID(1);
1526  queryid = (uint64) PG_GETARG_INT64(2);
1527  minmax_only = PG_GETARG_BOOL(3);
1528 
1529  PG_RETURN_TIMESTAMPTZ(entry_reset(userid, dbid, queryid, minmax_only));
1530 }
1531 
1532 /*
1533  * Reset statement statistics.
1534  */
1535 Datum
1537 {
1538  entry_reset(0, 0, 0, false);
1539 
1540  PG_RETURN_VOID();
1541 }
1542 
1543 /* Number of output arguments (columns) for various API versions */
1544 #define PG_STAT_STATEMENTS_COLS_V1_0 14
1545 #define PG_STAT_STATEMENTS_COLS_V1_1 18
1546 #define PG_STAT_STATEMENTS_COLS_V1_2 19
1547 #define PG_STAT_STATEMENTS_COLS_V1_3 23
1548 #define PG_STAT_STATEMENTS_COLS_V1_8 32
1549 #define PG_STAT_STATEMENTS_COLS_V1_9 33
1550 #define PG_STAT_STATEMENTS_COLS_V1_10 43
1551 #define PG_STAT_STATEMENTS_COLS_V1_11 49
1552 #define PG_STAT_STATEMENTS_COLS 49 /* maximum of above */
1553 
1554 /*
1555  * Retrieve statement statistics.
1556  *
1557  * The SQL API of this function has changed multiple times, and will likely
1558  * do so again in future. To support the case where a newer version of this
1559  * loadable module is being used with an old SQL declaration of the function,
1560  * we continue to support the older API versions. For 1.2 and later, the
1561  * expected API version is identified by embedding it in the C name of the
1562  * function. Unfortunately we weren't bright enough to do that for 1.1.
1563  */
1564 Datum
1566 {
1567  bool showtext = PG_GETARG_BOOL(0);
1568 
1569  pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
1570 
1571  return (Datum) 0;
1572 }
1573 
1574 Datum
1576 {
1577  bool showtext = PG_GETARG_BOOL(0);
1578 
1579  pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
1580 
1581  return (Datum) 0;
1582 }
1583 
1584 Datum
1586 {
1587  bool showtext = PG_GETARG_BOOL(0);
1588 
1589  pg_stat_statements_internal(fcinfo, PGSS_V1_9, showtext);
1590 
1591  return (Datum) 0;
1592 }
1593 
1594 Datum
1596 {
1597  bool showtext = PG_GETARG_BOOL(0);
1598 
1599  pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
1600 
1601  return (Datum) 0;
1602 }
1603 
1604 Datum
1606 {
1607  bool showtext = PG_GETARG_BOOL(0);
1608 
1609  pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
1610 
1611  return (Datum) 0;
1612 }
1613 
1614 Datum
1616 {
1617  bool showtext = PG_GETARG_BOOL(0);
1618 
1619  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1620 
1621  return (Datum) 0;
1622 }
1623 
1624 /*
1625  * Legacy entry point for pg_stat_statements() API versions 1.0 and 1.1.
1626  * This can be removed someday, perhaps.
1627  */
1628 Datum
1630 {
1631  /* If it's really API 1.1, we'll figure that out below */
1632  pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
1633 
1634  return (Datum) 0;
1635 }
1636 
1637 /* Common code for all versions of pg_stat_statements() */
1638 static void
1640  pgssVersion api_version,
1641  bool showtext)
1642 {
1643  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1644  Oid userid = GetUserId();
1645  bool is_allowed_role = false;
1646  char *qbuffer = NULL;
1647  Size qbuffer_size = 0;
1648  Size extent = 0;
1649  int gc_count = 0;
1650  HASH_SEQ_STATUS hash_seq;
1651  pgssEntry *entry;
1652 
1653  /*
1654  * Superusers or roles with the privileges of pg_read_all_stats members
1655  * are allowed
1656  */
1657  is_allowed_role = has_privs_of_role(userid, ROLE_PG_READ_ALL_STATS);
1658 
1659  /* hash table must exist already */
1660  if (!pgss || !pgss_hash)
1661  ereport(ERROR,
1662  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1663  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1664 
1665  InitMaterializedSRF(fcinfo, 0);
1666 
1667  /*
1668  * Check we have the expected number of output arguments. Aside from
1669  * being a good safety check, we need a kluge here to detect API version
1670  * 1.1, which was wedged into the code in an ill-considered way.
1671  */
1672  switch (rsinfo->setDesc->natts)
1673  {
1675  if (api_version != PGSS_V1_0)
1676  elog(ERROR, "incorrect number of output arguments");
1677  break;
1679  /* pg_stat_statements() should have told us 1.0 */
1680  if (api_version != PGSS_V1_0)
1681  elog(ERROR, "incorrect number of output arguments");
1682  api_version = PGSS_V1_1;
1683  break;
1685  if (api_version != PGSS_V1_2)
1686  elog(ERROR, "incorrect number of output arguments");
1687  break;
1689  if (api_version != PGSS_V1_3)
1690  elog(ERROR, "incorrect number of output arguments");
1691  break;
1693  if (api_version != PGSS_V1_8)
1694  elog(ERROR, "incorrect number of output arguments");
1695  break;
1697  if (api_version != PGSS_V1_9)
1698  elog(ERROR, "incorrect number of output arguments");
1699  break;
1701  if (api_version != PGSS_V1_10)
1702  elog(ERROR, "incorrect number of output arguments");
1703  break;
1705  if (api_version != PGSS_V1_11)
1706  elog(ERROR, "incorrect number of output arguments");
1707  break;
1708  default:
1709  elog(ERROR, "incorrect number of output arguments");
1710  }
1711 
1712  /*
1713  * We'd like to load the query text file (if needed) while not holding any
1714  * lock on pgss->lock. In the worst case we'll have to do this again
1715  * after we have the lock, but it's unlikely enough to make this a win
1716  * despite occasional duplicated work. We need to reload if anybody
1717  * writes to the file (either a retail qtext_store(), or a garbage
1718  * collection) between this point and where we've gotten shared lock. If
1719  * a qtext_store is actually in progress when we look, we might as well
1720  * skip the speculative load entirely.
1721  */
1722  if (showtext)
1723  {
1724  int n_writers;
1725 
1726  /* Take the mutex so we can examine variables */
1727  {
1728  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1729 
1730  SpinLockAcquire(&s->mutex);
1731  extent = s->extent;
1732  n_writers = s->n_writers;
1733  gc_count = s->gc_count;
1734  SpinLockRelease(&s->mutex);
1735  }
1736 
1737  /* No point in loading file now if there are active writers */
1738  if (n_writers == 0)
1739  qbuffer = qtext_load_file(&qbuffer_size);
1740  }
1741 
1742  /*
1743  * Get shared lock, load or reload the query text file if we must, and
1744  * iterate over the hashtable entries.
1745  *
1746  * With a large hash table, we might be holding the lock rather longer
1747  * than one could wish. However, this only blocks creation of new hash
1748  * table entries, and the larger the hash table the less likely that is to
1749  * be needed. So we can hope this is okay. Perhaps someday we'll decide
1750  * we need to partition the hash table to limit the time spent holding any
1751  * one lock.
1752  */
1754 
1755  if (showtext)
1756  {
1757  /*
1758  * Here it is safe to examine extent and gc_count without taking the
1759  * mutex. Note that although other processes might change
1760  * pgss->extent just after we look at it, the strings they then write
1761  * into the file cannot yet be referenced in the hashtable, so we
1762  * don't care whether we see them or not.
1763  *
1764  * If qtext_load_file fails, we just press on; we'll return NULL for
1765  * every query text.
1766  */
1767  if (qbuffer == NULL ||
1768  pgss->extent != extent ||
1769  pgss->gc_count != gc_count)
1770  {
1771  free(qbuffer);
1772  qbuffer = qtext_load_file(&qbuffer_size);
1773  }
1774  }
1775 
1776  hash_seq_init(&hash_seq, pgss_hash);
1777  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1778  {
1780  bool nulls[PG_STAT_STATEMENTS_COLS];
1781  int i = 0;
1782  Counters tmp;
1783  double stddev;
1784  int64 queryid = entry->key.queryid;
1785  TimestampTz stats_since;
1786  TimestampTz minmax_stats_since;
1787 
1788  memset(values, 0, sizeof(values));
1789  memset(nulls, 0, sizeof(nulls));
1790 
1791  values[i++] = ObjectIdGetDatum(entry->key.userid);
1792  values[i++] = ObjectIdGetDatum(entry->key.dbid);
1793  if (api_version >= PGSS_V1_9)
1794  values[i++] = BoolGetDatum(entry->key.toplevel);
1795 
1796  if (is_allowed_role || entry->key.userid == userid)
1797  {
1798  if (api_version >= PGSS_V1_2)
1799  values[i++] = Int64GetDatumFast(queryid);
1800 
1801  if (showtext)
1802  {
1803  char *qstr = qtext_fetch(entry->query_offset,
1804  entry->query_len,
1805  qbuffer,
1806  qbuffer_size);
1807 
1808  if (qstr)
1809  {
1810  char *enc;
1811 
1812  enc = pg_any_to_server(qstr,
1813  entry->query_len,
1814  entry->encoding);
1815 
1817 
1818  if (enc != qstr)
1819  pfree(enc);
1820  }
1821  else
1822  {
1823  /* Just return a null if we fail to find the text */
1824  nulls[i++] = true;
1825  }
1826  }
1827  else
1828  {
1829  /* Query text not requested */
1830  nulls[i++] = true;
1831  }
1832  }
1833  else
1834  {
1835  /* Don't show queryid */
1836  if (api_version >= PGSS_V1_2)
1837  nulls[i++] = true;
1838 
1839  /*
1840  * Don't show query text, but hint as to the reason for not doing
1841  * so if it was requested
1842  */
1843  if (showtext)
1844  values[i++] = CStringGetTextDatum("<insufficient privilege>");
1845  else
1846  nulls[i++] = true;
1847  }
1848 
1849  /* copy counters to a local variable to keep locking time short */
1850  {
1851  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1852 
1853  SpinLockAcquire(&e->mutex);
1854  tmp = e->counters;
1855  stats_since = e->stats_since;
1856  minmax_stats_since = e->minmax_stats_since;
1857  SpinLockRelease(&e->mutex);
1858  }
1859 
1860  /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */
1861  if (IS_STICKY(tmp))
1862  continue;
1863 
1864  /* Note that we rely on PGSS_PLAN being 0 and PGSS_EXEC being 1. */
1865  for (int kind = 0; kind < PGSS_NUMKIND; kind++)
1866  {
1867  if (kind == PGSS_EXEC || api_version >= PGSS_V1_8)
1868  {
1869  values[i++] = Int64GetDatumFast(tmp.calls[kind]);
1870  values[i++] = Float8GetDatumFast(tmp.total_time[kind]);
1871  }
1872 
1873  if ((kind == PGSS_EXEC && api_version >= PGSS_V1_3) ||
1874  api_version >= PGSS_V1_8)
1875  {
1876  values[i++] = Float8GetDatumFast(tmp.min_time[kind]);
1877  values[i++] = Float8GetDatumFast(tmp.max_time[kind]);
1878  values[i++] = Float8GetDatumFast(tmp.mean_time[kind]);
1879 
1880  /*
1881  * Note we are calculating the population variance here, not
1882  * the sample variance, as we have data for the whole
1883  * population, so Bessel's correction is not used, and we
1884  * don't divide by tmp.calls - 1.
1885  */
1886  if (tmp.calls[kind] > 1)
1887  stddev = sqrt(tmp.sum_var_time[kind] / tmp.calls[kind]);
1888  else
1889  stddev = 0.0;
1890  values[i++] = Float8GetDatumFast(stddev);
1891  }
1892  }
1893  values[i++] = Int64GetDatumFast(tmp.rows);
1896  if (api_version >= PGSS_V1_1)
1901  if (api_version >= PGSS_V1_1)
1906  if (api_version >= PGSS_V1_1)
1907  {
1910  }
1911  if (api_version >= PGSS_V1_11)
1912  {
1915  }
1916  if (api_version >= PGSS_V1_10)
1917  {
1920  }
1921  if (api_version >= PGSS_V1_8)
1922  {
1923  char buf[256];
1924  Datum wal_bytes;
1925 
1927  values[i++] = Int64GetDatumFast(tmp.wal_fpi);
1928 
1929  snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.wal_bytes);
1930 
1931  /* Convert to numeric. */
1932  wal_bytes = DirectFunctionCall3(numeric_in,
1934  ObjectIdGetDatum(0),
1935  Int32GetDatum(-1));
1936  values[i++] = wal_bytes;
1937  }
1938  if (api_version >= PGSS_V1_10)
1939  {
1948  }
1949  if (api_version >= PGSS_V1_11)
1950  {
1953  values[i++] = TimestampTzGetDatum(stats_since);
1954  values[i++] = TimestampTzGetDatum(minmax_stats_since);
1955  }
1956 
1957  Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
1958  api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
1959  api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
1960  api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
1961  api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
1962  api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
1963  api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
1964  api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
1965  -1 /* fail if you forget to update this assert */ ));
1966 
1967  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1968  }
1969 
1971 
1972  free(qbuffer);
1973 }
1974 
1975 /* Number of output arguments (columns) for pg_stat_statements_info */
1976 #define PG_STAT_STATEMENTS_INFO_COLS 2
1977 
1978 /*
1979  * Return statistics of pg_stat_statements.
1980  */
1981 Datum
1983 {
1984  pgssGlobalStats stats;
1985  TupleDesc tupdesc;
1987  bool nulls[PG_STAT_STATEMENTS_INFO_COLS] = {0};
1988 
1989  if (!pgss || !pgss_hash)
1990  ereport(ERROR,
1991  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1992  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1993 
1994  /* Build a tuple descriptor for our result type */
1995  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1996  elog(ERROR, "return type must be a row type");
1997 
1998  /* Read global statistics for pg_stat_statements */
1999  {
2000  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2001 
2002  SpinLockAcquire(&s->mutex);
2003  stats = s->stats;
2004  SpinLockRelease(&s->mutex);
2005  }
2006 
2007  values[0] = Int64GetDatum(stats.dealloc);
2009 
2011 }
2012 
2013 /*
2014  * Estimate shared memory space needed.
2015  */
2016 static Size
2018 {
2019  Size size;
2020 
2021  size = MAXALIGN(sizeof(pgssSharedState));
2023 
2024  return size;
2025 }
2026 
2027 /*
2028  * Allocate a new hashtable entry.
2029  * caller must hold an exclusive lock on pgss->lock
2030  *
2031  * "query" need not be null-terminated; we rely on query_len instead
2032  *
2033  * If "sticky" is true, make the new entry artificially sticky so that it will
2034  * probably still be there when the query finishes execution. We do this by
2035  * giving it a median usage value rather than the normal value. (Strictly
2036  * speaking, query strings are normalized on a best effort basis, though it
2037  * would be difficult to demonstrate this even under artificial conditions.)
2038  *
2039  * Note: despite needing exclusive lock, it's not an error for the target
2040  * entry to already exist. This is because pgss_store releases and
2041  * reacquires lock after failing to find a match; so someone else could
2042  * have made the entry while we waited to get exclusive lock.
2043  */
2044 static pgssEntry *
2045 entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding,
2046  bool sticky)
2047 {
2048  pgssEntry *entry;
2049  bool found;
2050 
2051  /* Make space if needed */
2053  entry_dealloc();
2054 
2055  /* Find or create an entry with desired hash code */
2056  entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
2057 
2058  if (!found)
2059  {
2060  /* New entry, initialize it */
2061 
2062  /* reset the statistics */
2063  memset(&entry->counters, 0, sizeof(Counters));
2064  /* set the appropriate initial usage count */
2065  entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
2066  /* re-initialize the mutex each time ... we assume no one using it */
2067  SpinLockInit(&entry->mutex);
2068  /* ... and don't forget the query text metadata */
2069  Assert(query_len >= 0);
2070  entry->query_offset = query_offset;
2071  entry->query_len = query_len;
2072  entry->encoding = encoding;
2073  entry->stats_since = GetCurrentTimestamp();
2074  entry->minmax_stats_since = entry->stats_since;
2075  }
2076 
2077  return entry;
2078 }
2079 
2080 /*
2081  * qsort comparator for sorting into increasing usage order
2082  */
2083 static int
2084 entry_cmp(const void *lhs, const void *rhs)
2085 {
2086  double l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
2087  double r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
2088 
2089  if (l_usage < r_usage)
2090  return -1;
2091  else if (l_usage > r_usage)
2092  return +1;
2093  else
2094  return 0;
2095 }
2096 
2097 /*
2098  * Deallocate least-used entries.
2099  *
2100  * Caller must hold an exclusive lock on pgss->lock.
2101  */
2102 static void
2104 {
2105  HASH_SEQ_STATUS hash_seq;
2106  pgssEntry **entries;
2107  pgssEntry *entry;
2108  int nvictims;
2109  int i;
2110  Size tottextlen;
2111  int nvalidtexts;
2112 
2113  /*
2114  * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
2115  * While we're scanning the table, apply the decay factor to the usage
2116  * values, and update the mean query length.
2117  *
2118  * Note that the mean query length is almost immediately obsolete, since
2119  * we compute it before not after discarding the least-used entries.
2120  * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
2121  * making two passes to get a more current result. Likewise, the new
2122  * cur_median_usage includes the entries we're about to zap.
2123  */
2124 
2125  entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
2126 
2127  i = 0;
2128  tottextlen = 0;
2129  nvalidtexts = 0;
2130 
2131  hash_seq_init(&hash_seq, pgss_hash);
2132  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2133  {
2134  entries[i++] = entry;
2135  /* "Sticky" entries get a different usage decay rate. */
2136  if (IS_STICKY(entry->counters))
2138  else
2140  /* In the mean length computation, ignore dropped texts. */
2141  if (entry->query_len >= 0)
2142  {
2143  tottextlen += entry->query_len + 1;
2144  nvalidtexts++;
2145  }
2146  }
2147 
2148  /* Sort into increasing order by usage */
2149  qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
2150 
2151  /* Record the (approximate) median usage */
2152  if (i > 0)
2153  pgss->cur_median_usage = entries[i / 2]->counters.usage;
2154  /* Record the mean query length */
2155  if (nvalidtexts > 0)
2156  pgss->mean_query_len = tottextlen / nvalidtexts;
2157  else
2159 
2160  /* Now zap an appropriate fraction of lowest-usage entries */
2161  nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
2162  nvictims = Min(nvictims, i);
2163 
2164  for (i = 0; i < nvictims; i++)
2165  {
2166  hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
2167  }
2168 
2169  pfree(entries);
2170 
2171  /* Increment the number of times entries are deallocated */
2172  {
2173  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2174 
2175  SpinLockAcquire(&s->mutex);
2176  s->stats.dealloc += 1;
2177  SpinLockRelease(&s->mutex);
2178  }
2179 }
2180 
2181 /*
2182  * Given a query string (not necessarily null-terminated), allocate a new
2183  * entry in the external query text file and store the string there.
2184  *
2185  * If successful, returns true, and stores the new entry's offset in the file
2186  * into *query_offset. Also, if gc_count isn't NULL, *gc_count is set to the
2187  * number of garbage collections that have occurred so far.
2188  *
2189  * On failure, returns false.
2190  *
2191  * At least a shared lock on pgss->lock must be held by the caller, so as
2192  * to prevent a concurrent garbage collection. Share-lock-holding callers
2193  * should pass a gc_count pointer to obtain the number of garbage collections,
2194  * so that they can recheck the count after obtaining exclusive lock to
2195  * detect whether a garbage collection occurred (and removed this entry).
2196  */
2197 static bool
2198 qtext_store(const char *query, int query_len,
2199  Size *query_offset, int *gc_count)
2200 {
2201  Size off;
2202  int fd;
2203 
2204  /*
2205  * We use a spinlock to protect extent/n_writers/gc_count, so that
2206  * multiple processes may execute this function concurrently.
2207  */
2208  {
2209  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2210 
2211  SpinLockAcquire(&s->mutex);
2212  off = s->extent;
2213  s->extent += query_len + 1;
2214  s->n_writers++;
2215  if (gc_count)
2216  *gc_count = s->gc_count;
2217  SpinLockRelease(&s->mutex);
2218  }
2219 
2220  *query_offset = off;
2221 
2222  /*
2223  * Don't allow the file to grow larger than what qtext_load_file can
2224  * (theoretically) handle. This has been seen to be reachable on 32-bit
2225  * platforms.
2226  */
2227  if (unlikely(query_len >= MaxAllocHugeSize - off))
2228  {
2229  errno = EFBIG; /* not quite right, but it'll do */
2230  fd = -1;
2231  goto error;
2232  }
2233 
2234  /* Now write the data into the successfully-reserved part of the file */
2235  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDWR | O_CREAT | PG_BINARY);
2236  if (fd < 0)
2237  goto error;
2238 
2239  if (pg_pwrite(fd, query, query_len, off) != query_len)
2240  goto error;
2241  if (pg_pwrite(fd, "\0", 1, off + query_len) != 1)
2242  goto error;
2243 
2245 
2246  /* Mark our write complete */
2247  {
2248  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2249 
2250  SpinLockAcquire(&s->mutex);
2251  s->n_writers--;
2252  SpinLockRelease(&s->mutex);
2253  }
2254 
2255  return true;
2256 
2257 error:
2258  ereport(LOG,
2260  errmsg("could not write file \"%s\": %m",
2261  PGSS_TEXT_FILE)));
2262 
2263  if (fd >= 0)
2265 
2266  /* Mark our write complete */
2267  {
2268  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2269 
2270  SpinLockAcquire(&s->mutex);
2271  s->n_writers--;
2272  SpinLockRelease(&s->mutex);
2273  }
2274 
2275  return false;
2276 }
2277 
2278 /*
2279  * Read the external query text file into a malloc'd buffer.
2280  *
2281  * Returns NULL (without throwing an error) if unable to read, eg
2282  * file not there or insufficient memory.
2283  *
2284  * On success, the buffer size is also returned into *buffer_size.
2285  *
2286  * This can be called without any lock on pgss->lock, but in that case
2287  * the caller is responsible for verifying that the result is sane.
2288  */
2289 static char *
2290 qtext_load_file(Size *buffer_size)
2291 {
2292  char *buf;
2293  int fd;
2294  struct stat stat;
2295  Size nread;
2296 
2298  if (fd < 0)
2299  {
2300  if (errno != ENOENT)
2301  ereport(LOG,
2303  errmsg("could not read file \"%s\": %m",
2304  PGSS_TEXT_FILE)));
2305  return NULL;
2306  }
2307 
2308  /* Get file length */
2309  if (fstat(fd, &stat))
2310  {
2311  ereport(LOG,
2313  errmsg("could not stat file \"%s\": %m",
2314  PGSS_TEXT_FILE)));
2316  return NULL;
2317  }
2318 
2319  /* Allocate buffer; beware that off_t might be wider than size_t */
2320  if (stat.st_size <= MaxAllocHugeSize)
2321  buf = (char *) malloc(stat.st_size);
2322  else
2323  buf = NULL;
2324  if (buf == NULL)
2325  {
2326  ereport(LOG,
2327  (errcode(ERRCODE_OUT_OF_MEMORY),
2328  errmsg("out of memory"),
2329  errdetail("Could not allocate enough memory to read file \"%s\".",
2330  PGSS_TEXT_FILE)));
2332  return NULL;
2333  }
2334 
2335  /*
2336  * OK, slurp in the file. Windows fails if we try to read more than
2337  * INT_MAX bytes at once, and other platforms might not like that either,
2338  * so read a very large file in 1GB segments.
2339  */
2340  nread = 0;
2341  while (nread < stat.st_size)
2342  {
2343  int toread = Min(1024 * 1024 * 1024, stat.st_size - nread);
2344 
2345  /*
2346  * If we get a short read and errno doesn't get set, the reason is
2347  * probably that garbage collection truncated the file since we did
2348  * the fstat(), so we don't log a complaint --- but we don't return
2349  * the data, either, since it's most likely corrupt due to concurrent
2350  * writes from garbage collection.
2351  */
2352  errno = 0;
2353  if (read(fd, buf + nread, toread) != toread)
2354  {
2355  if (errno)
2356  ereport(LOG,
2358  errmsg("could not read file \"%s\": %m",
2359  PGSS_TEXT_FILE)));
2360  free(buf);
2362  return NULL;
2363  }
2364  nread += toread;
2365  }
2366 
2367  if (CloseTransientFile(fd) != 0)
2368  ereport(LOG,
2370  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
2371 
2372  *buffer_size = nread;
2373  return buf;
2374 }
2375 
2376 /*
2377  * Locate a query text in the file image previously read by qtext_load_file().
2378  *
2379  * We validate the given offset/length, and return NULL if bogus. Otherwise,
2380  * the result points to a null-terminated string within the buffer.
2381  */
2382 static char *
2383 qtext_fetch(Size query_offset, int query_len,
2384  char *buffer, Size buffer_size)
2385 {
2386  /* File read failed? */
2387  if (buffer == NULL)
2388  return NULL;
2389  /* Bogus offset/length? */
2390  if (query_len < 0 ||
2391  query_offset + query_len >= buffer_size)
2392  return NULL;
2393  /* As a further sanity check, make sure there's a trailing null */
2394  if (buffer[query_offset + query_len] != '\0')
2395  return NULL;
2396  /* Looks OK */
2397  return buffer + query_offset;
2398 }
2399 
2400 /*
2401  * Do we need to garbage-collect the external query text file?
2402  *
2403  * Caller should hold at least a shared lock on pgss->lock.
2404  */
2405 static bool
2407 {
2408  Size extent;
2409 
2410  /* Read shared extent pointer */
2411  {
2412  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2413 
2414  SpinLockAcquire(&s->mutex);
2415  extent = s->extent;
2416  SpinLockRelease(&s->mutex);
2417  }
2418 
2419  /*
2420  * Don't proceed if file does not exceed 512 bytes per possible entry.
2421  *
2422  * Here and in the next test, 32-bit machines have overflow hazards if
2423  * pgss_max and/or mean_query_len are large. Force the multiplications
2424  * and comparisons to be done in uint64 arithmetic to forestall trouble.
2425  */
2426  if ((uint64) extent < (uint64) 512 * pgss_max)
2427  return false;
2428 
2429  /*
2430  * Don't proceed if file is less than about 50% bloat. Nothing can or
2431  * should be done in the event of unusually large query texts accounting
2432  * for file's large size. We go to the trouble of maintaining the mean
2433  * query length in order to prevent garbage collection from thrashing
2434  * uselessly.
2435  */
2436  if ((uint64) extent < (uint64) pgss->mean_query_len * pgss_max * 2)
2437  return false;
2438 
2439  return true;
2440 }
2441 
2442 /*
2443  * Garbage-collect orphaned query texts in external file.
2444  *
2445  * This won't be called often in the typical case, since it's likely that
2446  * there won't be too much churn, and besides, a similar compaction process
2447  * occurs when serializing to disk at shutdown or as part of resetting.
2448  * Despite this, it seems prudent to plan for the edge case where the file
2449  * becomes unreasonably large, with no other method of compaction likely to
2450  * occur in the foreseeable future.
2451  *
2452  * The caller must hold an exclusive lock on pgss->lock.
2453  *
2454  * At the first sign of trouble we unlink the query text file to get a clean
2455  * slate (although existing statistics are retained), rather than risk
2456  * thrashing by allowing the same problem case to recur indefinitely.
2457  */
2458 static void
2460 {
2461  char *qbuffer;
2462  Size qbuffer_size;
2463  FILE *qfile = NULL;
2464  HASH_SEQ_STATUS hash_seq;
2465  pgssEntry *entry;
2466  Size extent;
2467  int nentries;
2468 
2469  /*
2470  * When called from pgss_store, some other session might have proceeded
2471  * with garbage collection in the no-lock-held interim of lock strength
2472  * escalation. Check once more that this is actually necessary.
2473  */
2474  if (!need_gc_qtexts())
2475  return;
2476 
2477  /*
2478  * Load the old texts file. If we fail (out of memory, for instance),
2479  * invalidate query texts. Hopefully this is rare. It might seem better
2480  * to leave things alone on an OOM failure, but the problem is that the
2481  * file is only going to get bigger; hoping for a future non-OOM result is
2482  * risky and can easily lead to complete denial of service.
2483  */
2484  qbuffer = qtext_load_file(&qbuffer_size);
2485  if (qbuffer == NULL)
2486  goto gc_fail;
2487 
2488  /*
2489  * We overwrite the query texts file in place, so as to reduce the risk of
2490  * an out-of-disk-space failure. Since the file is guaranteed not to get
2491  * larger, this should always work on traditional filesystems; though we
2492  * could still lose on copy-on-write filesystems.
2493  */
2495  if (qfile == NULL)
2496  {
2497  ereport(LOG,
2499  errmsg("could not write file \"%s\": %m",
2500  PGSS_TEXT_FILE)));
2501  goto gc_fail;
2502  }
2503 
2504  extent = 0;
2505  nentries = 0;
2506 
2507  hash_seq_init(&hash_seq, pgss_hash);
2508  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2509  {
2510  int query_len = entry->query_len;
2511  char *qry = qtext_fetch(entry->query_offset,
2512  query_len,
2513  qbuffer,
2514  qbuffer_size);
2515 
2516  if (qry == NULL)
2517  {
2518  /* Trouble ... drop the text */
2519  entry->query_offset = 0;
2520  entry->query_len = -1;
2521  /* entry will not be counted in mean query length computation */
2522  continue;
2523  }
2524 
2525  if (fwrite(qry, 1, query_len + 1, qfile) != query_len + 1)
2526  {
2527  ereport(LOG,
2529  errmsg("could not write file \"%s\": %m",
2530  PGSS_TEXT_FILE)));
2531  hash_seq_term(&hash_seq);
2532  goto gc_fail;
2533  }
2534 
2535  entry->query_offset = extent;
2536  extent += query_len + 1;
2537  nentries++;
2538  }
2539 
2540  /*
2541  * Truncate away any now-unused space. If this fails for some odd reason,
2542  * we log it, but there's no need to fail.
2543  */
2544  if (ftruncate(fileno(qfile), extent) != 0)
2545  ereport(LOG,
2547  errmsg("could not truncate file \"%s\": %m",
2548  PGSS_TEXT_FILE)));
2549 
2550  if (FreeFile(qfile))
2551  {
2552  ereport(LOG,
2554  errmsg("could not write file \"%s\": %m",
2555  PGSS_TEXT_FILE)));
2556  qfile = NULL;
2557  goto gc_fail;
2558  }
2559 
2560  elog(DEBUG1, "pgss gc of queries file shrunk size from %zu to %zu",
2561  pgss->extent, extent);
2562 
2563  /* Reset the shared extent pointer */
2564  pgss->extent = extent;
2565 
2566  /*
2567  * Also update the mean query length, to be sure that need_gc_qtexts()
2568  * won't still think we have a problem.
2569  */
2570  if (nentries > 0)
2571  pgss->mean_query_len = extent / nentries;
2572  else
2574 
2575  free(qbuffer);
2576 
2577  /*
2578  * OK, count a garbage collection cycle. (Note: even though we have
2579  * exclusive lock on pgss->lock, we must take pgss->mutex for this, since
2580  * other processes may examine gc_count while holding only the mutex.
2581  * Also, we have to advance the count *after* we've rewritten the file,
2582  * else other processes might not realize they read a stale file.)
2583  */
2584  record_gc_qtexts();
2585 
2586  return;
2587 
2588 gc_fail:
2589  /* clean up resources */
2590  if (qfile)
2591  FreeFile(qfile);
2592  free(qbuffer);
2593 
2594  /*
2595  * Since the contents of the external file are now uncertain, mark all
2596  * hashtable entries as having invalid texts.
2597  */
2598  hash_seq_init(&hash_seq, pgss_hash);
2599  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2600  {
2601  entry->query_offset = 0;
2602  entry->query_len = -1;
2603  }
2604 
2605  /*
2606  * Destroy the query text file and create a new, empty one
2607  */
2608  (void) unlink(PGSS_TEXT_FILE);
2610  if (qfile == NULL)
2611  ereport(LOG,
2613  errmsg("could not recreate file \"%s\": %m",
2614  PGSS_TEXT_FILE)));
2615  else
2616  FreeFile(qfile);
2617 
2618  /* Reset the shared extent pointer */
2619  pgss->extent = 0;
2620 
2621  /* Reset mean_query_len to match the new state */
2623 
2624  /*
2625  * Bump the GC count even though we failed.
2626  *
2627  * This is needed to make concurrent readers of file without any lock on
2628  * pgss->lock notice existence of new version of file. Once readers
2629  * subsequently observe a change in GC count with pgss->lock held, that
2630  * forces a safe reopen of file. Writers also require that we bump here,
2631  * of course. (As required by locking protocol, readers and writers don't
2632  * trust earlier file contents until gc_count is found unchanged after
2633  * pgss->lock acquired in shared or exclusive mode respectively.)
2634  */
2635  record_gc_qtexts();
2636 }
2637 
2638 #define SINGLE_ENTRY_RESET(e) \
2639 if (e) { \
2640  if (minmax_only) { \
2641  /* When requested reset only min/max statistics of an entry */ \
2642  for (int kind = 0; kind < PGSS_NUMKIND; kind++) \
2643  { \
2644  e->counters.max_time[kind] = 0; \
2645  e->counters.min_time[kind] = 0; \
2646  } \
2647  e->minmax_stats_since = stats_reset; \
2648  } \
2649  else \
2650  { \
2651  /* Remove the key otherwise */ \
2652  hash_search(pgss_hash, &e->key, HASH_REMOVE, NULL); \
2653  num_remove++; \
2654  } \
2655 }
2656 
2657 /*
2658  * Reset entries corresponding to parameters passed.
2659  */
2660 static TimestampTz
2661 entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
2662 {
2663  HASH_SEQ_STATUS hash_seq;
2664  pgssEntry *entry;
2665  FILE *qfile;
2666  long num_entries;
2667  long num_remove = 0;
2668  pgssHashKey key;
2669  TimestampTz stats_reset;
2670 
2671  if (!pgss || !pgss_hash)
2672  ereport(ERROR,
2673  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2674  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2675 
2677  num_entries = hash_get_num_entries(pgss_hash);
2678 
2679  stats_reset = GetCurrentTimestamp();
2680 
2681  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2682  {
2683  /* If all the parameters are available, use the fast path. */
2684  memset(&key, 0, sizeof(pgssHashKey));
2685  key.userid = userid;
2686  key.dbid = dbid;
2687  key.queryid = queryid;
2688 
2689  /*
2690  * Reset the entry if it exists, starting with the non-top-level
2691  * entry.
2692  */
2693  key.toplevel = false;
2694  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
2695 
2696  SINGLE_ENTRY_RESET(entry);
2697 
2698  /* Also reset the top-level entry if it exists. */
2699  key.toplevel = true;
2700  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
2701 
2702  SINGLE_ENTRY_RESET(entry);
2703  }
2704  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2705  {
2706  /* Reset entries corresponding to valid parameters. */
2707  hash_seq_init(&hash_seq, pgss_hash);
2708  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2709  {
2710  if ((!userid || entry->key.userid == userid) &&
2711  (!dbid || entry->key.dbid == dbid) &&
2712  (!queryid || entry->key.queryid == queryid))
2713  {
2714  SINGLE_ENTRY_RESET(entry);
2715  }
2716  }
2717  }
2718  else
2719  {
2720  /* Reset all entries. */
2721  hash_seq_init(&hash_seq, pgss_hash);
2722  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2723  {
2724  SINGLE_ENTRY_RESET(entry);
2725  }
2726  }
2727 
2728  /* All entries are removed? */
2729  if (num_entries != num_remove)
2730  goto release_lock;
2731 
2732  /*
2733  * Reset global statistics for pg_stat_statements since all entries are
2734  * removed.
2735  */
2736  {
2737  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2738 
2739  SpinLockAcquire(&s->mutex);
2740  s->stats.dealloc = 0;
2741  s->stats.stats_reset = stats_reset;
2742  SpinLockRelease(&s->mutex);
2743  }
2744 
2745  /*
2746  * Write new empty query file, perhaps even creating a new one to recover
2747  * if the file was missing.
2748  */
2750  if (qfile == NULL)
2751  {
2752  ereport(LOG,
2754  errmsg("could not create file \"%s\": %m",
2755  PGSS_TEXT_FILE)));
2756  goto done;
2757  }
2758 
2759  /* If ftruncate fails, log it, but it's not a fatal problem */
2760  if (ftruncate(fileno(qfile), 0) != 0)
2761  ereport(LOG,
2763  errmsg("could not truncate file \"%s\": %m",
2764  PGSS_TEXT_FILE)));
2765 
2766  FreeFile(qfile);
2767 
2768 done:
2769  pgss->extent = 0;
2770  /* This counts as a query text garbage collection for our purposes */
2771  record_gc_qtexts();
2772 
2773 release_lock:
2775 
2776  return stats_reset;
2777 }
2778 
2779 /*
2780  * Generate a normalized version of the query string that will be used to
2781  * represent all similar queries.
2782  *
2783  * Note that the normalized representation may well vary depending on
2784  * just which "equivalent" query is used to create the hashtable entry.
2785  * We assume this is OK.
2786  *
2787  * If query_loc > 0, then "query" has been advanced by that much compared to
2788  * the original string start, so we need to translate the provided locations
2789  * to compensate. (This lets us avoid re-scanning statements before the one
2790  * of interest, so it's worth doing.)
2791  *
2792  * *query_len_p contains the input string length, and is updated with
2793  * the result string length on exit. The resulting string might be longer
2794  * or shorter depending on what happens with replacement of constants.
2795  *
2796  * Returns a palloc'd string.
2797  */
2798 static char *
2799 generate_normalized_query(JumbleState *jstate, const char *query,
2800  int query_loc, int *query_len_p)
2801 {
2802  char *norm_query;
2803  int query_len = *query_len_p;
2804  int i,
2805  norm_query_buflen, /* Space allowed for norm_query */
2806  len_to_wrt, /* Length (in bytes) to write */
2807  quer_loc = 0, /* Source query byte location */
2808  n_quer_loc = 0, /* Normalized query byte location */
2809  last_off = 0, /* Offset from start for previous tok */
2810  last_tok_len = 0; /* Length (in bytes) of that tok */
2811 
2812  /*
2813  * Get constants' lengths (core system only gives us locations). Note
2814  * this also ensures the items are sorted by location.
2815  */
2816  fill_in_constant_lengths(jstate, query, query_loc);
2817 
2818  /*
2819  * Allow for $n symbols to be longer than the constants they replace.
2820  * Constants must take at least one byte in text form, while a $n symbol
2821  * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
2822  * could refine that limit based on the max value of n for the current
2823  * query, but it hardly seems worth any extra effort to do so.
2824  */
2825  norm_query_buflen = query_len + jstate->clocations_count * 10;
2826 
2827  /* Allocate result buffer */
2828  norm_query = palloc(norm_query_buflen + 1);
2829 
2830  for (i = 0; i < jstate->clocations_count; i++)
2831  {
2832  int off, /* Offset from start for cur tok */
2833  tok_len; /* Length (in bytes) of that tok */
2834 
2835  off = jstate->clocations[i].location;
2836  /* Adjust recorded location if we're dealing with partial string */
2837  off -= query_loc;
2838 
2839  tok_len = jstate->clocations[i].length;
2840 
2841  if (tok_len < 0)
2842  continue; /* ignore any duplicates */
2843 
2844  /* Copy next chunk (what precedes the next constant) */
2845  len_to_wrt = off - last_off;
2846  len_to_wrt -= last_tok_len;
2847 
2848  Assert(len_to_wrt >= 0);
2849  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
2850  n_quer_loc += len_to_wrt;
2851 
2852  /* And insert a param symbol in place of the constant token */
2853  n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
2854  i + 1 + jstate->highest_extern_param_id);
2855 
2856  quer_loc = off + tok_len;
2857  last_off = off;
2858  last_tok_len = tok_len;
2859  }
2860 
2861  /*
2862  * We've copied up until the last ignorable constant. Copy over the
2863  * remaining bytes of the original query string.
2864  */
2865  len_to_wrt = query_len - quer_loc;
2866 
2867  Assert(len_to_wrt >= 0);
2868  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
2869  n_quer_loc += len_to_wrt;
2870 
2871  Assert(n_quer_loc <= norm_query_buflen);
2872  norm_query[n_quer_loc] = '\0';
2873 
2874  *query_len_p = n_quer_loc;
2875  return norm_query;
2876 }
2877 
2878 /*
2879  * Given a valid SQL string and an array of constant-location records,
2880  * fill in the textual lengths of those constants.
2881  *
2882  * The constants may use any allowed constant syntax, such as float literals,
2883  * bit-strings, single-quoted strings and dollar-quoted strings. This is
2884  * accomplished by using the public API for the core scanner.
2885  *
2886  * It is the caller's job to ensure that the string is a valid SQL statement
2887  * with constants at the indicated locations. Since in practice the string
2888  * has already been parsed, and the locations that the caller provides will
2889  * have originated from within the authoritative parser, this should not be
2890  * a problem.
2891  *
2892  * Duplicate constant pointers are possible, and will have their lengths
2893  * marked as '-1', so that they are later ignored. (Actually, we assume the
2894  * lengths were initialized as -1 to start with, and don't change them here.)
2895  *
2896  * If query_loc > 0, then "query" has been advanced by that much compared to
2897  * the original string start, so we need to translate the provided locations
2898  * to compensate. (This lets us avoid re-scanning statements before the one
2899  * of interest, so it's worth doing.)
2900  *
2901  * N.B. There is an assumption that a '-' character at a Const location begins
2902  * a negative numeric constant. This precludes there ever being another
2903  * reason for a constant to start with a '-'.
2904  */
2905 static void
2906 fill_in_constant_lengths(JumbleState *jstate, const char *query,
2907  int query_loc)
2908 {
2909  LocationLen *locs;
2911  core_yy_extra_type yyextra;
2912  core_YYSTYPE yylval;
2913  YYLTYPE yylloc;
2914  int last_loc = -1;
2915  int i;
2916 
2917  /*
2918  * Sort the records by location so that we can process them in order while
2919  * scanning the query text.
2920  */
2921  if (jstate->clocations_count > 1)
2922  qsort(jstate->clocations, jstate->clocations_count,
2923  sizeof(LocationLen), comp_location);
2924  locs = jstate->clocations;
2925 
2926  /* initialize the flex scanner --- should match raw_parser() */
2927  yyscanner = scanner_init(query,
2928  &yyextra,
2929  &ScanKeywords,
2931 
2932  /* we don't want to re-emit any escape string warnings */
2933  yyextra.escape_string_warning = false;
2934 
2935  /* Search for each constant, in sequence */
2936  for (i = 0; i < jstate->clocations_count; i++)
2937  {
2938  int loc = locs[i].location;
2939  int tok;
2940 
2941  /* Adjust recorded location if we're dealing with partial string */
2942  loc -= query_loc;
2943 
2944  Assert(loc >= 0);
2945 
2946  if (loc <= last_loc)
2947  continue; /* Duplicate constant, ignore */
2948 
2949  /* Lex tokens until we find the desired constant */
2950  for (;;)
2951  {
2952  tok = core_yylex(&yylval, &yylloc, yyscanner);
2953 
2954  /* We should not hit end-of-string, but if we do, behave sanely */
2955  if (tok == 0)
2956  break; /* out of inner for-loop */
2957 
2958  /*
2959  * We should find the token position exactly, but if we somehow
2960  * run past it, work with that.
2961  */
2962  if (yylloc >= loc)
2963  {
2964  if (query[loc] == '-')
2965  {
2966  /*
2967  * It's a negative value - this is the one and only case
2968  * where we replace more than a single token.
2969  *
2970  * Do not compensate for the core system's special-case
2971  * adjustment of location to that of the leading '-'
2972  * operator in the event of a negative constant. It is
2973  * also useful for our purposes to start from the minus
2974  * symbol. In this way, queries like "select * from foo
2975  * where bar = 1" and "select * from foo where bar = -2"
2976  * will have identical normalized query strings.
2977  */
2978  tok = core_yylex(&yylval, &yylloc, yyscanner);
2979  if (tok == 0)
2980  break; /* out of inner for-loop */
2981  }
2982 
2983  /*
2984  * We now rely on the assumption that flex has placed a zero
2985  * byte after the text of the current token in scanbuf.
2986  */
2987  locs[i].length = strlen(yyextra.scanbuf + loc);
2988  break; /* out of inner for-loop */
2989  }
2990  }
2991 
2992  /* If we hit end-of-string, give up, leaving remaining lengths -1 */
2993  if (tok == 0)
2994  break;
2995 
2996  last_loc = loc;
2997  }
2998 
3000 }
3001 
3002 /*
3003  * comp_location: comparator for qsorting LocationLen structs by location
3004  */
3005 static int
3006 comp_location(const void *a, const void *b)
3007 {
3008  int l = ((const LocationLen *) a)->location;
3009  int r = ((const LocationLen *) b)->location;
3010 
3011  return pg_cmp_s32(l, r);
3012 }
bool has_privs_of_role(Oid member, Oid role)
Definition: acl.c:5066
void(* post_parse_analyze_hook_type)(ParseState *pstate, Query *query, JumbleState *jstate)
Definition: analyze.h:22
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:626
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1654
static Datum values[MAXATTR]
Definition: bootstrap.c:152
#define CStringGetTextDatum(s)
Definition: builtins.h:97
unsigned int uint32
Definition: c.h:493
#define Min(x, y)
Definition: c.h:991
#define PG_BINARY_R
Definition: c.h:1262
#define MAXALIGN(LEN)
Definition: c.h:798
signed int int32
Definition: c.h:481
#define Max(x, y)
Definition: c.h:985
#define PG_BINARY
Definition: c.h:1260
#define UINT64_FORMAT
Definition: c.h:536
#define unlikely(x)
Definition: c.h:298
#define PG_BINARY_W
Definition: c.h:1263
size_t Size
Definition: c.h:592
enc
int64 TimestampTz
Definition: timestamp.h:39
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1471
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1341
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:783
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1395
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1385
int errcode_for_file_access(void)
Definition: elog.c:882
int errdetail(const char *fmt,...)
Definition: elog.c:1205
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define LOG
Definition: elog.h:31
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define PG_FINALLY(...)
Definition: elog.h:387
#define ereport(elevel,...)
Definition: elog.h:149
ExecutorEnd_hook_type ExecutorEnd_hook
Definition: execMain.c:68
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:67
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:65
void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:141
void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:308
ExecutorRun_hook_type ExecutorRun_hook
Definition: execMain.c:66
void standard_ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:476
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:416
void(* ExecutorRun_hook_type)(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: executor.h:79
void(* ExecutorFinish_hook_type)(QueryDesc *queryDesc)
Definition: executor.h:86
void(* ExecutorStart_hook_type)(QueryDesc *queryDesc, int eflags)
Definition: executor.h:75
void(* ExecutorEnd_hook_type)(QueryDesc *queryDesc)
Definition: executor.h:90
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2583
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:782
int CloseTransientFile(int fd)
Definition: fd.c:2809
int FreeFile(FILE *file)
Definition: fd.c:2781
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2633
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:646
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
bool IsUnderPostmaster
Definition: globals.c:117
Oid MyDatabaseId
Definition: globals.c:91
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:5181
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:5070
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5217
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:5096
@ PGC_SUSET
Definition: guc.h:74
@ PGC_POSTMASTER
Definition: guc.h:70
@ PGC_SIGHUP
Definition: guc.h:71
#define free(a)
Definition: header.h:65
#define malloc(a)
Definition: header.h:50
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_REMOVE
Definition: hsearch.h:115
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:181
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:191
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:140
Instrumentation * InstrAlloc(int n, int instrument_options, bool async_mode)
Definition: instrument.c:31
WalUsage pgWalUsage
Definition: instrument.c:22
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:286
BufferUsage pgBufferUsage
Definition: instrument.c:20
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:248
@ INSTRUMENT_ALL
Definition: instrument.h:65
static int pg_cmp_s32(int32 a, int32 b)
Definition: int.h:483
#define read(a, b, c)
Definition: win32.h:13
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:365
void(* shmem_startup_hook_type)(void)
Definition: ipc.h:22
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:59
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:75
int b
Definition: isn.c:70
int a
Definition: isn.c:69
int i
Definition: isn.c:73
PGDLLIMPORT const ScanKeywordList ScanKeywords
Assert(fmt[strlen(fmt) - 1] !='\n')
LWLockPadded * GetNamedLWLockTranche(const char *tranche_name)
Definition: lwlock.c:574
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1172
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:670
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1785
@ LW_SHARED
Definition: lwlock.h:117
@ LW_EXCLUSIVE
Definition: lwlock.h:116
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676
int GetDatabaseEncoding(void)
Definition: mbutils.c:1261
void pfree(void *pointer)
Definition: mcxt.c:1508
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1528
void * palloc(Size size)
Definition: mcxt.c:1304
#define MaxAllocHugeSize
Definition: memutils.h:45
void(* shmem_request_hook_type)(void)
Definition: miscadmin.h:506
Oid GetUserId(void)
Definition: miscinit.c:514
shmem_request_hook_type shmem_request_hook
Definition: miscinit.c:1781
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1778
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:58
void * arg
const void size_t len
int32 encoding
Definition: pg_database.h:41
static int entry_cmp(const void *lhs, const void *rhs)
#define PG_STAT_STATEMENTS_COLS_V1_0
static planner_hook_type prev_planner_hook
@ PGSS_V1_9
@ PGSS_V1_10
@ PGSS_V1_1
@ PGSS_V1_11
@ PGSS_V1_3
@ PGSS_V1_2
@ PGSS_V1_8
@ PGSS_V1_0
#define SINGLE_ENTRY_RESET(e)
static int pgss_track
static bool pgss_track_planning
#define ASSUMED_MEDIAN_INIT
#define PG_STAT_STATEMENTS_INFO_COLS
PG_FUNCTION_INFO_V1(pg_stat_statements_reset)
static ExecutorRun_hook_type prev_ExecutorRun
struct pgssSharedState pgssSharedState
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, const struct JitInstrumentation *jitusage, JumbleState *jstate)
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
#define record_gc_qtexts()
Datum pg_stat_statements_reset_1_7(PG_FUNCTION_ARGS)
static PlannedStmt * pgss_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
void _PG_init(void)
static void gc_qtexts(void)
#define PG_STAT_STATEMENTS_COLS_V1_8
static int comp_location(const void *a, const void *b)
Datum pg_stat_statements_1_11(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS
struct Counters Counters
Datum pg_stat_statements_1_9(PG_FUNCTION_ARGS)
#define PGSS_TEXT_FILE
PGSSTrackLevel
@ PGSS_TRACK_ALL
@ PGSS_TRACK_NONE
@ PGSS_TRACK_TOP
PG_MODULE_MAGIC
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)
static int pgss_max
#define USAGE_DEALLOC_PERCENT
static bool qtext_store(const char *query, int query_len, Size *query_offset, int *gc_count)
Datum pg_stat_statements_1_10(PG_FUNCTION_ARGS)
#define USAGE_EXEC(duration)
#define PG_STAT_STATEMENTS_COLS_V1_11
#define STICKY_DECREASE_FACTOR
#define IS_STICKY(c)
static const struct config_enum_entry track_options[]
#define PG_STAT_STATEMENTS_COLS_V1_2
Datum pg_stat_statements_reset(PG_FUNCTION_ARGS)
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
#define PGSS_DUMP_FILE
static char * qtext_load_file(Size *buffer_size)
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
static bool need_gc_qtexts(void)
#define pgss_enabled(level)
static shmem_startup_hook_type prev_shmem_startup_hook
static shmem_request_hook_type prev_shmem_request_hook
static void pgss_shmem_request(void)
static TimestampTz entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
pgssStoreKind
@ PGSS_PLAN
@ PGSS_EXEC
@ PGSS_INVALID
@ PGSS_NUMKIND
static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
#define ASSUMED_LENGTH_INIT
#define PG_STAT_STATEMENTS_COLS_V1_3
static Size pgss_memsize(void)
static bool pgss_save
static void pgss_shmem_startup(void)
static int nesting_level
struct pgssGlobalStats pgssGlobalStats
static const uint32 PGSS_PG_MAJOR_VERSION
Datum pg_stat_statements_1_2(PG_FUNCTION_ARGS)
struct pgssEntry pgssEntry
#define USAGE_DECREASE_FACTOR
static ExecutorStart_hook_type prev_ExecutorStart
Datum pg_stat_statements(PG_FUNCTION_ARGS)
Datum pg_stat_statements_info(PG_FUNCTION_ARGS)
static void entry_dealloc(void)
#define PG_STAT_STATEMENTS_COLS_V1_10
static pgssSharedState * pgss
Datum pg_stat_statements_1_3(PG_FUNCTION_ARGS)
static void pgss_ExecutorFinish(QueryDesc *queryDesc)
static ProcessUtility_hook_type prev_ProcessUtility
#define PG_STAT_STATEMENTS_COLS_V1_1
Datum pg_stat_statements_1_8(PG_FUNCTION_ARGS)
static void pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
struct pgssHashKey pgssHashKey
Datum pg_stat_statements_reset_1_11(PG_FUNCTION_ARGS)
static pgssEntry * entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
static void fill_in_constant_lengths(JumbleState *jstate, const char *query, int query_loc)
static bool pgss_track_utility
#define USAGE_INIT
static ExecutorEnd_hook_type prev_ExecutorEnd
#define PG_STAT_STATEMENTS_COLS_V1_9
static void pgss_ExecutorEnd(QueryDesc *queryDesc)
static char * generate_normalized_query(JumbleState *jstate, const char *query, int query_loc, int *query_len_p)
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
static HTAB * pgss_hash
static const uint32 PGSS_FILE_HEADER
static void pgss_shmem_shutdown(int code, Datum arg)
static ExecutorFinish_hook_type prev_ExecutorFinish
static char * buf
Definition: pg_test_fsync.c:73
#define PG_VALID_BE_ENCODING(_enc)
Definition: pg_wchar.h:281
int duration
Definition: pgbench.c:174
static core_yyscan_t yyscanner
Definition: pl_scanner.c:106
planner_hook_type planner_hook
Definition: planner.c:70
PlannedStmt * standard_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: planner.c:281
PlannedStmt *(* planner_hook_type)(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: planner.h:26
#define sprintf
Definition: port.h:240
#define pg_pwrite
Definition: port.h:226
#define snprintf
Definition: port.h:238
#define qsort(a, b, c, d)
Definition: port.h:449
#define Int64GetDatumFast(X)
Definition: postgres.h:554
uintptr_t Datum
Definition: postgres.h:64
#define Float8GetDatumFast(X)
Definition: postgres.h:556
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
unsigned int Oid
Definition: postgres_ext.h:31
e
Definition: preproc-init.c:82
static int fd(const char *x, int i)
Definition: preproc-init.c:105
const char * CleanQuerytext(const char *query, int *location, int *len)
void EnableQueryId(void)
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:715
int slock_t
Definition: s_lock.h:735
#define YYLTYPE
Definition: scanner.h:44
core_yyscan_t scanner_init(const char *str, core_yy_extra_type *yyext, const ScanKeywordList *keywordlist, const uint16 *keyword_tokens)
void scanner_finish(core_yyscan_t yyscanner)
PGDLLIMPORT const uint16 ScanKeywordTokens[]
void * core_yyscan_t
Definition: scanner.h:121
int core_yylex(core_YYSTYPE *yylval_param, YYLTYPE *yylloc_param, core_yyscan_t yyscanner)
ScanDirection
Definition: sdir.h:25
Size add_size(Size s1, Size s2)
Definition: shmem.c:493
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:387
HTAB * ShmemInitHash(const char *name, long init_size, long max_size, HASHCTL *infoP, int hash_flags)
Definition: shmem.c:332
static pg_noinline void Size size
Definition: slab.c:607
#define SpinLockInit(lock)
Definition: spin.h:60
#define SpinLockRelease(lock)
Definition: spin.h:64
#define SpinLockAcquire(lock)
Definition: spin.h:62
static void error(void)
Definition: sql-dyntest.c:147
instr_time local_blk_read_time
Definition: instrument.h:38
int64 shared_blks_dirtied
Definition: instrument.h:28
int64 local_blks_hit
Definition: instrument.h:30
instr_time temp_blk_write_time
Definition: instrument.h:41
instr_time shared_blk_read_time
Definition: instrument.h:36
instr_time shared_blk_write_time
Definition: instrument.h:37
int64 local_blks_written
Definition: instrument.h:33
instr_time temp_blk_read_time
Definition: instrument.h:40
instr_time local_blk_write_time
Definition: instrument.h:39
int64 temp_blks_read
Definition: instrument.h:34
int64 shared_blks_read
Definition: instrument.h:27
int64 shared_blks_written
Definition: instrument.h:29
int64 temp_blks_written
Definition: instrument.h:35
int64 local_blks_read
Definition: instrument.h:31
int64 local_blks_dirtied
Definition: instrument.h:32
int64 shared_blks_hit
Definition: instrument.h:26
int64 temp_blks_written
int64 calls[PGSS_NUMKIND]
int64 shared_blks_written
double jit_generation_time
int64 temp_blks_read
double min_time[PGSS_NUMKIND]
int64 local_blks_written
double sum_var_time[PGSS_NUMKIND]
double temp_blk_read_time
double local_blk_write_time
int64 jit_emission_count
int64 jit_deform_count
double jit_emission_time
int64 shared_blks_hit
double local_blk_read_time
double jit_optimization_time
double shared_blk_write_time
int64 jit_optimization_count
double total_time[PGSS_NUMKIND]
double max_time[PGSS_NUMKIND]
int64 shared_blks_dirtied
double mean_time[PGSS_NUMKIND]
double temp_blk_write_time
int64 local_blks_dirtied
int64 jit_inlining_count
int64 shared_blks_read
int64 local_blks_hit
double jit_deform_time
int64 local_blks_read
double shared_blk_read_time
double jit_inlining_time
struct JitContext * es_jit
Definition: execnodes.h:714
uint64 es_total_processed
Definition: execnodes.h:671
MemoryContext es_query_cxt
Definition: execnodes.h:665
fmNodePtr resultinfo
Definition: fmgr.h:89
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
Definition: dynahash.c:220
WalUsage walusage
Definition: instrument.h:92
BufferUsage bufusage
Definition: instrument.h:91
JitInstrumentation instr
Definition: jit.h:64
instr_time generation_counter
Definition: jit.h:33
size_t created_functions
Definition: jit.h:30
instr_time optimization_counter
Definition: jit.h:42
instr_time deform_counter
Definition: jit.h:36
instr_time emission_counter
Definition: jit.h:45
instr_time inlining_counter
Definition: jit.h:39
int highest_extern_param_id
Definition: queryjumble.h:50
LocationLen * clocations
Definition: queryjumble.h:41
int clocations_count
Definition: queryjumble.h:47
Definition: lwlock.h:41
Definition: nodes.h:129
const char * p_sourcetext
Definition: parse_node.h:193
int stmt_location
Definition: plannodes.h:98
int stmt_len
Definition: plannodes.h:99
Node * utilityStmt
Definition: plannodes.h:95
uint64 queryId
Definition: plannodes.h:54
uint64 nprocessed
Definition: cmdtag.h:33
CommandTag commandTag
Definition: cmdtag.h:32
const char * sourceText
Definition: execdesc.h:38
EState * estate
Definition: execdesc.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37
struct Instrumentation * totaltime
Definition: execdesc.h:55
int stmt_location
Definition: parsenodes.h:237
Node * utilityStmt
Definition: parsenodes.h:136
TupleDesc setDesc
Definition: execnodes.h:340
Tuplestorestate * setResult
Definition: execnodes.h:339
uint64 wal_bytes
Definition: instrument.h:55
int64 wal_fpi
Definition: instrument.h:54
int64 wal_records
Definition: instrument.h:53
Definition: guc.h:170
bool escape_string_warning
Definition: scanner.h:88
char * scanbuf
Definition: scanner.h:72
Counters counters
pgssHashKey key
TimestampTz minmax_stats_since
TimestampTz stats_since
TimestampTz stats_reset
pgssGlobalStats stats
__int64 st_size
Definition: win32_port.h:273
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:750
void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:540
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:70
void(* ProcessUtility_hook_type)(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.h:71
ProcessUtilityContext
Definition: utility.h:21
static Datum TimestampTzGetDatum(TimestampTz X)
Definition: timestamp.h:52
#define PG_RETURN_TIMESTAMPTZ(x)
Definition: timestamp.h:68
#define fstat
Definition: win32_port.h:283
#define ftruncate(a, b)
Definition: win32_port.h:82