PostgreSQL Source Code  git master
pg_stat_statements.c File Reference
#include "postgres.h"
#include <math.h>
#include <sys/stat.h>
#include <unistd.h>
#include "catalog/pg_authid.h"
#include "executor/instrument.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "parser/scanner.h"
#include "parser/scansup.h"
#include "pgstat.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/spin.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/hashutils.h"
#include "utils/memutils.h"
Include dependency graph for pg_stat_statements.c:

Go to the source code of this file.

Data Structures

struct  pgssHashKey
 
struct  Counters
 
struct  pgssEntry
 
struct  pgssSharedState
 
struct  pgssLocationLen
 
struct  pgssJumbleState
 

Macros

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
 
#define PGSS_TEXT_FILE   PG_STAT_TMP_DIR "/pgss_query_texts.stat"
 
#define USAGE_EXEC(duration)   (1.0)
 
#define USAGE_INIT   (1.0) /* including initial planning */
 
#define ASSUMED_MEDIAN_INIT   (10.0) /* initial assumed median usage */
 
#define ASSUMED_LENGTH_INIT   1024 /* initial assumed mean query length */
 
#define USAGE_DECREASE_FACTOR   (0.99) /* decreased every entry_dealloc */
 
#define STICKY_DECREASE_FACTOR   (0.50) /* factor for sticky entries */
 
#define USAGE_DEALLOC_PERCENT   5 /* free this % of entries at once */
 
#define JUMBLE_SIZE   1024 /* query serialization buffer size */
 
#define pgss_enabled()
 
#define record_gc_qtexts()
 
#define PG_STAT_STATEMENTS_COLS_V1_0   14
 
#define PG_STAT_STATEMENTS_COLS_V1_1   18
 
#define PG_STAT_STATEMENTS_COLS_V1_2   19
 
#define PG_STAT_STATEMENTS_COLS_V1_3   23
 
#define PG_STAT_STATEMENTS_COLS   23 /* maximum of above */
 
#define APP_JUMB(item)   AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
 
#define APP_JUMB_STRING(str)   AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)
 

Typedefs

typedef enum pgssVersion pgssVersion
 
typedef struct pgssHashKey pgssHashKey
 
typedef struct Counters Counters
 
typedef struct pgssEntry pgssEntry
 
typedef struct pgssSharedState pgssSharedState
 
typedef struct pgssLocationLen pgssLocationLen
 
typedef struct pgssJumbleState pgssJumbleState
 

Enumerations

enum  pgssVersion { PGSS_V1_0 = 0, PGSS_V1_1, PGSS_V1_2, PGSS_V1_3 }
 
enum  PGSSTrackLevel { PGSS_TRACK_NONE, PGSS_TRACK_TOP, PGSS_TRACK_ALL }
 

Functions

void _PG_init (void)
 
void _PG_fini (void)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_reset)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_reset_1_7)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_1_2)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_1_3)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements)
 
static void pgss_shmem_startup (void)
 
static void pgss_shmem_shutdown (int code, Datum arg)
 
static void pgss_post_parse_analyze (ParseState *pstate, Query *query)
 
static void pgss_ExecutorStart (QueryDesc *queryDesc, int eflags)
 
static void pgss_ExecutorRun (QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
 
static void pgss_ExecutorFinish (QueryDesc *queryDesc)
 
static void pgss_ExecutorEnd (QueryDesc *queryDesc)
 
static void pgss_ProcessUtility (PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, char *completionTag)
 
static uint64 pgss_hash_string (const char *str, int len)
 
static void pgss_store (const char *query, uint64 queryId, int query_location, int query_len, double total_time, uint64 rows, const BufferUsage *bufusage, pgssJumbleState *jstate)
 
static void pg_stat_statements_internal (FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
 
static Size pgss_memsize (void)
 
static pgssEntryentry_alloc (pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
 
static void entry_dealloc (void)
 
static bool qtext_store (const char *query, int query_len, Size *query_offset, int *gc_count)
 
static char * qtext_load_file (Size *buffer_size)
 
static char * qtext_fetch (Size query_offset, int query_len, char *buffer, Size buffer_size)
 
static bool need_gc_qtexts (void)
 
static void gc_qtexts (void)
 
static void entry_reset (Oid userid, Oid dbid, uint64 queryid)
 
static void AppendJumble (pgssJumbleState *jstate, const unsigned char *item, Size size)
 
static void JumbleQuery (pgssJumbleState *jstate, Query *query)
 
static void JumbleRangeTable (pgssJumbleState *jstate, List *rtable)
 
static void JumbleRowMarks (pgssJumbleState *jstate, List *rowMarks)
 
static void JumbleExpr (pgssJumbleState *jstate, Node *node)
 
static void RecordConstLocation (pgssJumbleState *jstate, int location)
 
static char * generate_normalized_query (pgssJumbleState *jstate, const char *query, int query_loc, int *query_len_p, int encoding)
 
static void fill_in_constant_lengths (pgssJumbleState *jstate, const char *query, int query_loc)
 
static int comp_location (const void *a, const void *b)
 
Datum pg_stat_statements_reset_1_7 (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_reset (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_1_3 (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_1_2 (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements (PG_FUNCTION_ARGS)
 
static int entry_cmp (const void *lhs, const void *rhs)
 

Variables

 PG_MODULE_MAGIC
 
static const uint32 PGSS_FILE_HEADER = 0x20171004
 
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100
 
static int nested_level = 0
 
static shmem_startup_hook_type prev_shmem_startup_hook = NULL
 
static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL
 
static ExecutorStart_hook_type prev_ExecutorStart = NULL
 
static ExecutorRun_hook_type prev_ExecutorRun = NULL
 
static ExecutorFinish_hook_type prev_ExecutorFinish = NULL
 
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL
 
static ProcessUtility_hook_type prev_ProcessUtility = NULL
 
static pgssSharedStatepgss = NULL
 
static HTABpgss_hash = NULL
 
static const struct config_enum_entry track_options []
 
static int pgss_max
 
static int pgss_track
 
static bool pgss_track_utility
 
static bool pgss_save
 

Macro Definition Documentation

◆ APP_JUMB

#define APP_JUMB (   item)    AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))

Definition at line 2398 of file pg_stat_statements.c.

Referenced by JumbleExpr(), JumbleQuery(), JumbleRangeTable(), and JumbleRowMarks().

◆ APP_JUMB_STRING

#define APP_JUMB_STRING (   str)    AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)

Definition at line 2400 of file pg_stat_statements.c.

Referenced by JumbleExpr(), and JumbleRangeTable().

◆ ASSUMED_LENGTH_INIT

#define ASSUMED_LENGTH_INIT   1024 /* initial assumed mean query length */

Definition at line 108 of file pg_stat_statements.c.

Referenced by entry_dealloc(), gc_qtexts(), and pgss_shmem_startup().

◆ ASSUMED_MEDIAN_INIT

#define ASSUMED_MEDIAN_INIT   (10.0) /* initial assumed median usage */

Definition at line 107 of file pg_stat_statements.c.

Referenced by pgss_shmem_startup().

◆ JUMBLE_SIZE

#define JUMBLE_SIZE   1024 /* query serialization buffer size */

Definition at line 113 of file pg_stat_statements.c.

Referenced by AppendJumble(), and pgss_post_parse_analyze().

◆ PG_STAT_STATEMENTS_COLS

#define PG_STAT_STATEMENTS_COLS   23 /* maximum of above */

Definition at line 1342 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_0

#define PG_STAT_STATEMENTS_COLS_V1_0   14

Definition at line 1338 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_1

#define PG_STAT_STATEMENTS_COLS_V1_1   18

Definition at line 1339 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_2

#define PG_STAT_STATEMENTS_COLS_V1_2   19

Definition at line 1340 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_3

#define PG_STAT_STATEMENTS_COLS_V1_3   23

Definition at line 1341 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PGSS_DUMP_FILE

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"

Definition at line 86 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_enabled

#define pgss_enabled ( )

◆ PGSS_TEXT_FILE

#define PGSS_TEXT_FILE   PG_STAT_TMP_DIR "/pgss_query_texts.stat"

◆ record_gc_qtexts

#define record_gc_qtexts ( )
Value:
do { \
volatile pgssSharedState *s = (volatile pgssSharedState *) pgss; \
SpinLockAcquire(&s->mutex); \
s->gc_count++; \
SpinLockRelease(&s->mutex); \
} while(0)
static pgssSharedState * pgss

Definition at line 279 of file pg_stat_statements.c.

Referenced by entry_reset(), and gc_qtexts().

◆ STICKY_DECREASE_FACTOR

#define STICKY_DECREASE_FACTOR   (0.50) /* factor for sticky entries */

Definition at line 110 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_DEALLOC_PERCENT

#define USAGE_DEALLOC_PERCENT   5 /* free this % of entries at once */

Definition at line 111 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_DECREASE_FACTOR

#define USAGE_DECREASE_FACTOR   (0.99) /* decreased every entry_dealloc */

Definition at line 109 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_EXEC

#define USAGE_EXEC (   duration)    (1.0)

Definition at line 105 of file pg_stat_statements.c.

Referenced by pgss_store().

◆ USAGE_INIT

#define USAGE_INIT   (1.0) /* including initial planning */

Definition at line 106 of file pg_stat_statements.c.

Referenced by entry_alloc(), and pgss_store().

Typedef Documentation

◆ Counters

typedef struct Counters Counters

◆ pgssEntry

typedef struct pgssEntry pgssEntry

◆ pgssHashKey

typedef struct pgssHashKey pgssHashKey

◆ pgssJumbleState

◆ pgssLocationLen

◆ pgssSharedState

◆ pgssVersion

typedef enum pgssVersion pgssVersion

Enumeration Type Documentation

◆ PGSSTrackLevel

Enumerator
PGSS_TRACK_NONE 
PGSS_TRACK_TOP 
PGSS_TRACK_ALL 

Definition at line 254 of file pg_stat_statements.c.

255 {
256  PGSS_TRACK_NONE, /* track no statements */
257  PGSS_TRACK_TOP, /* only top level statements */
258  PGSS_TRACK_ALL /* all statements, including nested ones */
PGSSTrackLevel

◆ pgssVersion

Enumerator
PGSS_V1_0 
PGSS_V1_1 
PGSS_V1_2 
PGSS_V1_3 

Definition at line 118 of file pg_stat_statements.c.

Function Documentation

◆ _PG_fini()

void _PG_fini ( void  )

Definition at line 446 of file pg_stat_statements.c.

References ExecutorEnd_hook, ExecutorFinish_hook, ExecutorRun_hook, ExecutorStart_hook, post_parse_analyze_hook, prev_ExecutorEnd, prev_ExecutorFinish, prev_ExecutorRun, prev_ExecutorStart, prev_post_parse_analyze_hook, prev_ProcessUtility, prev_shmem_startup_hook, ProcessUtility_hook, and shmem_startup_hook.

447 {
448  /* Uninstall hooks. */
456 }
static ProcessUtility_hook_type prev_ProcessUtility
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:76
static ExecutorStart_hook_type prev_ExecutorStart
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:70
ExecutorRun_hook_type ExecutorRun_hook
Definition: execMain.c:71
ExecutorEnd_hook_type ExecutorEnd_hook
Definition: execMain.c:73
static ExecutorFinish_hook_type prev_ExecutorFinish
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:52
static shmem_startup_hook_type prev_shmem_startup_hook
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:72
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:51

◆ _PG_init()

void _PG_init ( void  )

Definition at line 350 of file pg_stat_statements.c.

References DefineCustomBoolVariable(), DefineCustomEnumVariable(), DefineCustomIntVariable(), EmitWarningsOnPlaceholders(), ExecutorEnd_hook, ExecutorFinish_hook, ExecutorRun_hook, ExecutorStart_hook, PGC_POSTMASTER, PGC_SIGHUP, PGC_SUSET, pgss_ExecutorEnd(), pgss_ExecutorFinish(), pgss_ExecutorRun(), pgss_ExecutorStart(), pgss_max, pgss_memsize(), pgss_post_parse_analyze(), pgss_ProcessUtility(), pgss_save, pgss_shmem_startup(), pgss_track, PGSS_TRACK_TOP, pgss_track_utility, post_parse_analyze_hook, prev_ExecutorEnd, prev_ExecutorFinish, prev_ExecutorRun, prev_ExecutorStart, prev_post_parse_analyze_hook, prev_ProcessUtility, prev_shmem_startup_hook, process_shared_preload_libraries_in_progress, ProcessUtility_hook, RequestAddinShmemSpace(), RequestNamedLWLockTranche(), and shmem_startup_hook.

351 {
352  /*
353  * In order to create our shared memory area, we have to be loaded via
354  * shared_preload_libraries. If not, fall out without hooking into any of
355  * the main system. (We don't throw error here because it seems useful to
356  * allow the pg_stat_statements functions to be created even when the
357  * module isn't active. The functions must protect themselves against
358  * being called then, however.)
359  */
361  return;
362 
363  /*
364  * Define (or redefine) custom GUC variables.
365  */
366  DefineCustomIntVariable("pg_stat_statements.max",
367  "Sets the maximum number of statements tracked by pg_stat_statements.",
368  NULL,
369  &pgss_max,
370  5000,
371  100,
372  INT_MAX,
374  0,
375  NULL,
376  NULL,
377  NULL);
378 
379  DefineCustomEnumVariable("pg_stat_statements.track",
380  "Selects which statements are tracked by pg_stat_statements.",
381  NULL,
382  &pgss_track,
385  PGC_SUSET,
386  0,
387  NULL,
388  NULL,
389  NULL);
390 
391  DefineCustomBoolVariable("pg_stat_statements.track_utility",
392  "Selects whether utility commands are tracked by pg_stat_statements.",
393  NULL,
395  true,
396  PGC_SUSET,
397  0,
398  NULL,
399  NULL,
400  NULL);
401 
402  DefineCustomBoolVariable("pg_stat_statements.save",
403  "Save pg_stat_statements statistics across server shutdowns.",
404  NULL,
405  &pgss_save,
406  true,
407  PGC_SIGHUP,
408  0,
409  NULL,
410  NULL,
411  NULL);
412 
413  EmitWarningsOnPlaceholders("pg_stat_statements");
414 
415  /*
416  * Request additional shared resources. (These are no-ops if we're not in
417  * the postmaster process.) We'll allocate or attach to the shared
418  * resources in pgss_shmem_startup().
419  */
421  RequestNamedLWLockTranche("pg_stat_statements", 1);
422 
423  /*
424  * Install hooks.
425  */
440 }
void DefineCustomIntVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, int bootValue, int minValue, int maxValue, GucContext context, int flags, GucIntCheckHook check_hook, GucIntAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:8599
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:70
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1522
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:638
static ProcessUtility_hook_type prev_ProcessUtility
static void pgss_shmem_startup(void)
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:76
static int pgss_track
static ExecutorStart_hook_type prev_ExecutorStart
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
void DefineCustomEnumVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, int bootValue, const struct config_enum_entry *options, GucContext context, int flags, GucEnumCheckHook check_hook, GucEnumAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:8684
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:70
static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
static int pgss_max
ExecutorRun_hook_type ExecutorRun_hook
Definition: execMain.c:71
ExecutorEnd_hook_type ExecutorEnd_hook
Definition: execMain.c:73
static void pgss_post_parse_analyze(ParseState *pstate, Query *query)
Definition: guc.h:75
void EmitWarningsOnPlaceholders(const char *className)
Definition: guc.c:8712
static ExecutorFinish_hook_type prev_ExecutorFinish
Definition: guc.h:72
static Size pgss_memsize(void)
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:52
static bool pgss_track_utility
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, char *completionTag)
static shmem_startup_hook_type prev_shmem_startup_hook
static void pgss_ExecutorFinish(QueryDesc *queryDesc)
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:72
static void pgss_ExecutorEnd(QueryDesc *queryDesc)
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:8573
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:51
static const struct config_enum_entry track_options[]
static bool pgss_save

◆ AppendJumble()

static void AppendJumble ( pgssJumbleState jstate,
const unsigned char *  item,
Size  size 
)
static

Definition at line 2362 of file pg_stat_statements.c.

References DatumGetUInt64, hash_any_extended(), pgssJumbleState::jumble, pgssJumbleState::jumble_len, JUMBLE_SIZE, and Min.

2363 {
2364  unsigned char *jumble = jstate->jumble;
2365  Size jumble_len = jstate->jumble_len;
2366 
2367  /*
2368  * Whenever the jumble buffer is full, we hash the current contents and
2369  * reset the buffer to contain just that hash value, thus relying on the
2370  * hash to summarize everything so far.
2371  */
2372  while (size > 0)
2373  {
2374  Size part_size;
2375 
2376  if (jumble_len >= JUMBLE_SIZE)
2377  {
2378  uint64 start_hash;
2379 
2380  start_hash = DatumGetUInt64(hash_any_extended(jumble,
2381  JUMBLE_SIZE, 0));
2382  memcpy(jumble, &start_hash, sizeof(start_hash));
2383  jumble_len = sizeof(start_hash);
2384  }
2385  part_size = Min(size, JUMBLE_SIZE - jumble_len);
2386  memcpy(jumble + jumble_len, item, part_size);
2387  jumble_len += part_size;
2388  item += part_size;
2389  size -= part_size;
2390  }
2391  jstate->jumble_len = jumble_len;
2392 }
#define Min(x, y)
Definition: c.h:904
Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.c:374
unsigned char * jumble
#define JUMBLE_SIZE
#define DatumGetUInt64(X)
Definition: postgres.h:634
size_t Size
Definition: c.h:466

◆ comp_location()

static int comp_location ( const void *  a,
const void *  b 
)
static

Definition at line 3255 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

3256 {
3257  int l = ((const pgssLocationLen *) a)->location;
3258  int r = ((const pgssLocationLen *) b)->location;
3259 
3260  if (l < r)
3261  return -1;
3262  else if (l > r)
3263  return +1;
3264  else
3265  return 0;
3266 }

◆ entry_alloc()

static pgssEntry * entry_alloc ( pgssHashKey key,
Size  query_offset,
int  query_len,
int  encoding,
bool  sticky 
)
static

Definition at line 1707 of file pg_stat_statements.c.

References Assert, pgssEntry::counters, pgssSharedState::cur_median_usage, encoding, pgssEntry::encoding, entry_dealloc(), HASH_ENTER, hash_get_num_entries(), hash_search(), pgssEntry::mutex, pgss_max, pgssEntry::query_len, pgssEntry::query_offset, SpinLockInit, Counters::usage, and USAGE_INIT.

Referenced by pgss_shmem_startup(), and pgss_store().

1709 {
1710  pgssEntry *entry;
1711  bool found;
1712 
1713  /* Make space if needed */
1715  entry_dealloc();
1716 
1717  /* Find or create an entry with desired hash code */
1718  entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
1719 
1720  if (!found)
1721  {
1722  /* New entry, initialize it */
1723 
1724  /* reset the statistics */
1725  memset(&entry->counters, 0, sizeof(Counters));
1726  /* set the appropriate initial usage count */
1727  entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
1728  /* re-initialize the mutex each time ... we assume no one using it */
1729  SpinLockInit(&entry->mutex);
1730  /* ... and don't forget the query text metadata */
1731  Assert(query_len >= 0);
1732  entry->query_offset = query_offset;
1733  entry->query_len = query_len;
1734  entry->encoding = encoding;
1735  }
1736 
1737  return entry;
1738 }
#define SpinLockInit(lock)
Definition: spin.h:60
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1335
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
Counters counters
static pgssSharedState * pgss
static int pgss_max
static void entry_dealloc(void)
static HTAB * pgss_hash
#define USAGE_INIT
#define Assert(condition)
Definition: c.h:732
int32 encoding
Definition: pg_database.h:41

◆ entry_cmp()

static int entry_cmp ( const void *  lhs,
const void *  rhs 
)
static

Definition at line 1744 of file pg_stat_statements.c.

Referenced by entry_dealloc().

1745 {
1746  double l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
1747  double r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
1748 
1749  if (l_usage < r_usage)
1750  return -1;
1751  else if (l_usage > r_usage)
1752  return +1;
1753  else
1754  return 0;
1755 }

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

Definition at line 1763 of file pg_stat_statements.c.

References ASSUMED_LENGTH_INIT, Counters::calls, pgssEntry::counters, pgssSharedState::cur_median_usage, entry_cmp(), hash_get_num_entries(), HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), i, sort-test::key, Max, pgssSharedState::mean_query_len, Min, palloc(), pfree(), qsort, pgssEntry::query_len, STICKY_DECREASE_FACTOR, Counters::usage, USAGE_DEALLOC_PERCENT, and USAGE_DECREASE_FACTOR.

Referenced by entry_alloc().

1764 {
1765  HASH_SEQ_STATUS hash_seq;
1766  pgssEntry **entries;
1767  pgssEntry *entry;
1768  int nvictims;
1769  int i;
1770  Size tottextlen;
1771  int nvalidtexts;
1772 
1773  /*
1774  * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
1775  * While we're scanning the table, apply the decay factor to the usage
1776  * values, and update the mean query length.
1777  *
1778  * Note that the mean query length is almost immediately obsolete, since
1779  * we compute it before not after discarding the least-used entries.
1780  * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
1781  * making two passes to get a more current result. Likewise, the new
1782  * cur_median_usage includes the entries we're about to zap.
1783  */
1784 
1785  entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
1786 
1787  i = 0;
1788  tottextlen = 0;
1789  nvalidtexts = 0;
1790 
1791  hash_seq_init(&hash_seq, pgss_hash);
1792  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1793  {
1794  entries[i++] = entry;
1795  /* "Sticky" entries get a different usage decay rate. */
1796  if (entry->counters.calls == 0)
1798  else
1800  /* In the mean length computation, ignore dropped texts. */
1801  if (entry->query_len >= 0)
1802  {
1803  tottextlen += entry->query_len + 1;
1804  nvalidtexts++;
1805  }
1806  }
1807 
1808  /* Sort into increasing order by usage */
1809  qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
1810 
1811  /* Record the (approximate) median usage */
1812  if (i > 0)
1813  pgss->cur_median_usage = entries[i / 2]->counters.usage;
1814  /* Record the mean query length */
1815  if (nvalidtexts > 0)
1816  pgss->mean_query_len = tottextlen / nvalidtexts;
1817  else
1819 
1820  /* Now zap an appropriate fraction of lowest-usage entries */
1821  nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
1822  nvictims = Min(nvictims, i);
1823 
1824  for (i = 0; i < nvictims; i++)
1825  {
1826  hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
1827  }
1828 
1829  pfree(entries);
1830 }
#define USAGE_DEALLOC_PERCENT
#define Min(x, y)
Definition: c.h:904
static int entry_cmp(const void *lhs, const void *rhs)
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1335
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
Counters counters
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
void pfree(void *pointer)
Definition: mcxt.c:1056
static HTAB * pgss_hash
#define STICKY_DECREASE_FACTOR
#define USAGE_DECREASE_FACTOR
#define Max(x, y)
Definition: c.h:898
size_t Size
Definition: c.h:466
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1389
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1379
void * palloc(Size size)
Definition: mcxt.c:949
int i
#define qsort(a, b, c, d)
Definition: port.h:492

◆ entry_reset()

static void entry_reset ( Oid  userid,
Oid  dbid,
uint64  queryid 
)
static

Definition at line 2266 of file pg_stat_statements.c.

References AllocateFile(), pgssHashKey::dbid, ereport, errcode(), errcode_for_file_access(), errmsg(), ERROR, pgssSharedState::extent, FreeFile(), ftruncate, hash_get_num_entries(), HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), sort-test::key, pgssEntry::key, pgssSharedState::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), PG_BINARY_W, PGSS_TEXT_FILE, pgssHashKey::queryid, record_gc_qtexts, and pgssHashKey::userid.

Referenced by pg_stat_statements_reset(), and pg_stat_statements_reset_1_7().

2267 {
2268  HASH_SEQ_STATUS hash_seq;
2269  pgssEntry *entry;
2270  FILE *qfile;
2271  long num_entries;
2272  long num_remove = 0;
2273  pgssHashKey key;
2274 
2275  if (!pgss || !pgss_hash)
2276  ereport(ERROR,
2277  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2278  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2279 
2281  num_entries = hash_get_num_entries(pgss_hash);
2282 
2283  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2284  {
2285  /* If all the parameters are available, use the fast path. */
2286  key.userid = userid;
2287  key.dbid = dbid;
2288  key.queryid = queryid;
2289 
2290  /* Remove the key if exists */
2291  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2292  if (entry) /* found */
2293  num_remove++;
2294  }
2295  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2296  {
2297  /* Remove entries corresponding to valid parameters. */
2298  hash_seq_init(&hash_seq, pgss_hash);
2299  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2300  {
2301  if ((!userid || entry->key.userid == userid) &&
2302  (!dbid || entry->key.dbid == dbid) &&
2303  (!queryid || entry->key.queryid == queryid))
2304  {
2305  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2306  num_remove++;
2307  }
2308  }
2309  }
2310  else
2311  {
2312  /* Remove all entries. */
2313  hash_seq_init(&hash_seq, pgss_hash);
2314  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2315  {
2316  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2317  num_remove++;
2318  }
2319  }
2320 
2321  /* All entries are removed? */
2322  if (num_entries != num_remove)
2323  goto release_lock;
2324 
2325  /*
2326  * Write new empty query file, perhaps even creating a new one to recover
2327  * if the file was missing.
2328  */
2330  if (qfile == NULL)
2331  {
2332  ereport(LOG,
2334  errmsg("could not create file \"%s\": %m",
2335  PGSS_TEXT_FILE)));
2336  goto done;
2337  }
2338 
2339  /* If ftruncate fails, log it, but it's not a fatal problem */
2340  if (ftruncate(fileno(qfile), 0) != 0)
2341  ereport(LOG,
2343  errmsg("could not truncate file \"%s\": %m",
2344  PGSS_TEXT_FILE)));
2345 
2346  FreeFile(qfile);
2347 
2348 done:
2349  pgss->extent = 0;
2350  /* This counts as a query text garbage collection for our purposes */
2351  record_gc_qtexts();
2352 
2353 release_lock:
2355 }
int errcode(int sqlerrcode)
Definition: elog.c:570
#define PG_BINARY_W
Definition: c.h:1194
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1335
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
#define LOG
Definition: elog.h:26
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
static pgssSharedState * pgss
static HTAB * pgss_hash
#define ERROR
Definition: elog.h:43
int errcode_for_file_access(void)
Definition: elog.c:593
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2205
#define ereport(elevel, rest)
Definition: elog.h:141
pgssHashKey key
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
#define record_gc_qtexts()
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1389
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1379
int FreeFile(FILE *file)
Definition: fd.c:2404
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define PGSS_TEXT_FILE
#define ftruncate(a, b)
Definition: win32_port.h:60

◆ fill_in_constant_lengths()

static void fill_in_constant_lengths ( pgssJumbleState jstate,
const char *  query,
int  query_loc 
)
static

Definition at line 3155 of file pg_stat_statements.c.

References Assert, pgssJumbleState::clocations, pgssJumbleState::clocations_count, comp_location(), core_yylex(), core_yy_extra_type::escape_string_warning, i, pgssLocationLen::length, pgssLocationLen::location, qsort, core_yy_extra_type::scanbuf, ScanKeywords, ScanKeywordTokens, scanner_finish(), scanner_init(), YYLTYPE, and yyscanner.

Referenced by generate_normalized_query().

3157 {
3158  pgssLocationLen *locs;
3160  core_yy_extra_type yyextra;
3161  core_YYSTYPE yylval;
3162  YYLTYPE yylloc;
3163  int last_loc = -1;
3164  int i;
3165 
3166  /*
3167  * Sort the records by location so that we can process them in order while
3168  * scanning the query text.
3169  */
3170  if (jstate->clocations_count > 1)
3171  qsort(jstate->clocations, jstate->clocations_count,
3172  sizeof(pgssLocationLen), comp_location);
3173  locs = jstate->clocations;
3174 
3175  /* initialize the flex scanner --- should match raw_parser() */
3176  yyscanner = scanner_init(query,
3177  &yyextra,
3178  &ScanKeywords,
3180 
3181  /* we don't want to re-emit any escape string warnings */
3182  yyextra.escape_string_warning = false;
3183 
3184  /* Search for each constant, in sequence */
3185  for (i = 0; i < jstate->clocations_count; i++)
3186  {
3187  int loc = locs[i].location;
3188  int tok;
3189 
3190  /* Adjust recorded location if we're dealing with partial string */
3191  loc -= query_loc;
3192 
3193  Assert(loc >= 0);
3194 
3195  if (loc <= last_loc)
3196  continue; /* Duplicate constant, ignore */
3197 
3198  /* Lex tokens until we find the desired constant */
3199  for (;;)
3200  {
3201  tok = core_yylex(&yylval, &yylloc, yyscanner);
3202 
3203  /* We should not hit end-of-string, but if we do, behave sanely */
3204  if (tok == 0)
3205  break; /* out of inner for-loop */
3206 
3207  /*
3208  * We should find the token position exactly, but if we somehow
3209  * run past it, work with that.
3210  */
3211  if (yylloc >= loc)
3212  {
3213  if (query[loc] == '-')
3214  {
3215  /*
3216  * It's a negative value - this is the one and only case
3217  * where we replace more than a single token.
3218  *
3219  * Do not compensate for the core system's special-case
3220  * adjustment of location to that of the leading '-'
3221  * operator in the event of a negative constant. It is
3222  * also useful for our purposes to start from the minus
3223  * symbol. In this way, queries like "select * from foo
3224  * where bar = 1" and "select * from foo where bar = -2"
3225  * will have identical normalized query strings.
3226  */
3227  tok = core_yylex(&yylval, &yylloc, yyscanner);
3228  if (tok == 0)
3229  break; /* out of inner for-loop */
3230  }
3231 
3232  /*
3233  * We now rely on the assumption that flex has placed a zero
3234  * byte after the text of the current token in scanbuf.
3235  */
3236  locs[i].length = strlen(yyextra.scanbuf + loc);
3237  break; /* out of inner for-loop */
3238  }
3239  }
3240 
3241  /* If we hit end-of-string, give up, leaving remaining lengths -1 */
3242  if (tok == 0)
3243  break;
3244 
3245  last_loc = loc;
3246  }
3247 
3248  scanner_finish(yyscanner);
3249 }
void * core_yyscan_t
Definition: scanner.h:116
PGDLLIMPORT const uint16 ScanKeywordTokens[]
char * scanbuf
Definition: scanner.h:72
PGDLLIMPORT const ScanKeywordList ScanKeywords
pgssLocationLen * clocations
#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)
static int comp_location(const void *a, const void *b)
int core_yylex(core_YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
#define Assert(condition)
Definition: c.h:732
static core_yyscan_t yyscanner
Definition: pl_scanner.c:106
int i
bool escape_string_warning
Definition: scanner.h:88
void scanner_finish(core_yyscan_t yyscanner)
#define qsort(a, b, c, d)
Definition: port.h:492

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

Definition at line 2082 of file pg_stat_statements.c.

References AllocateFile(), ASSUMED_LENGTH_INIT, DEBUG1, elog, ereport, errcode_for_file_access(), errmsg(), pgssSharedState::extent, free, FreeFile(), ftruncate, hash_seq_init(), hash_seq_search(), hash_seq_term(), LOG, pgssSharedState::mean_query_len, need_gc_qtexts(), PG_BINARY_W, PGSS_TEXT_FILE, qtext_fetch(), qtext_load_file(), pgssEntry::query_len, pgssEntry::query_offset, and record_gc_qtexts.

Referenced by pgss_store().

2083 {
2084  char *qbuffer;
2085  Size qbuffer_size;
2086  FILE *qfile = NULL;
2087  HASH_SEQ_STATUS hash_seq;
2088  pgssEntry *entry;
2089  Size extent;
2090  int nentries;
2091 
2092  /*
2093  * When called from pgss_store, some other session might have proceeded
2094  * with garbage collection in the no-lock-held interim of lock strength
2095  * escalation. Check once more that this is actually necessary.
2096  */
2097  if (!need_gc_qtexts())
2098  return;
2099 
2100  /*
2101  * Load the old texts file. If we fail (out of memory, for instance),
2102  * invalidate query texts. Hopefully this is rare. It might seem better
2103  * to leave things alone on an OOM failure, but the problem is that the
2104  * file is only going to get bigger; hoping for a future non-OOM result is
2105  * risky and can easily lead to complete denial of service.
2106  */
2107  qbuffer = qtext_load_file(&qbuffer_size);
2108  if (qbuffer == NULL)
2109  goto gc_fail;
2110 
2111  /*
2112  * We overwrite the query texts file in place, so as to reduce the risk of
2113  * an out-of-disk-space failure. Since the file is guaranteed not to get
2114  * larger, this should always work on traditional filesystems; though we
2115  * could still lose on copy-on-write filesystems.
2116  */
2118  if (qfile == NULL)
2119  {
2120  ereport(LOG,
2122  errmsg("could not write file \"%s\": %m",
2123  PGSS_TEXT_FILE)));
2124  goto gc_fail;
2125  }
2126 
2127  extent = 0;
2128  nentries = 0;
2129 
2130  hash_seq_init(&hash_seq, pgss_hash);
2131  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2132  {
2133  int query_len = entry->query_len;
2134  char *qry = qtext_fetch(entry->query_offset,
2135  query_len,
2136  qbuffer,
2137  qbuffer_size);
2138 
2139  if (qry == NULL)
2140  {
2141  /* Trouble ... drop the text */
2142  entry->query_offset = 0;
2143  entry->query_len = -1;
2144  /* entry will not be counted in mean query length computation */
2145  continue;
2146  }
2147 
2148  if (fwrite(qry, 1, query_len + 1, qfile) != query_len + 1)
2149  {
2150  ereport(LOG,
2152  errmsg("could not write file \"%s\": %m",
2153  PGSS_TEXT_FILE)));
2154  hash_seq_term(&hash_seq);
2155  goto gc_fail;
2156  }
2157 
2158  entry->query_offset = extent;
2159  extent += query_len + 1;
2160  nentries++;
2161  }
2162 
2163  /*
2164  * Truncate away any now-unused space. If this fails for some odd reason,
2165  * we log it, but there's no need to fail.
2166  */
2167  if (ftruncate(fileno(qfile), extent) != 0)
2168  ereport(LOG,
2170  errmsg("could not truncate file \"%s\": %m",
2171  PGSS_TEXT_FILE)));
2172 
2173  if (FreeFile(qfile))
2174  {
2175  ereport(LOG,
2177  errmsg("could not write file \"%s\": %m",
2178  PGSS_TEXT_FILE)));
2179  qfile = NULL;
2180  goto gc_fail;
2181  }
2182 
2183  elog(DEBUG1, "pgss gc of queries file shrunk size from %zu to %zu",
2184  pgss->extent, extent);
2185 
2186  /* Reset the shared extent pointer */
2187  pgss->extent = extent;
2188 
2189  /*
2190  * Also update the mean query length, to be sure that need_gc_qtexts()
2191  * won't still think we have a problem.
2192  */
2193  if (nentries > 0)
2194  pgss->mean_query_len = extent / nentries;
2195  else
2197 
2198  free(qbuffer);
2199 
2200  /*
2201  * OK, count a garbage collection cycle. (Note: even though we have
2202  * exclusive lock on pgss->lock, we must take pgss->mutex for this, since
2203  * other processes may examine gc_count while holding only the mutex.
2204  * Also, we have to advance the count *after* we've rewritten the file,
2205  * else other processes might not realize they read a stale file.)
2206  */
2207  record_gc_qtexts();
2208 
2209  return;
2210 
2211 gc_fail:
2212  /* clean up resources */
2213  if (qfile)
2214  FreeFile(qfile);
2215  if (qbuffer)
2216  free(qbuffer);
2217 
2218  /*
2219  * Since the contents of the external file are now uncertain, mark all
2220  * hashtable entries as having invalid texts.
2221  */
2222  hash_seq_init(&hash_seq, pgss_hash);
2223  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2224  {
2225  entry->query_offset = 0;
2226  entry->query_len = -1;
2227  }
2228 
2229  /*
2230  * Destroy the query text file and create a new, empty one
2231  */
2232  (void) unlink(PGSS_TEXT_FILE);
2234  if (qfile == NULL)
2235  ereport(LOG,
2237  errmsg("could not recreate file \"%s\": %m",
2238  PGSS_TEXT_FILE)));
2239  else
2240  FreeFile(qfile);
2241 
2242  /* Reset the shared extent pointer */
2243  pgss->extent = 0;
2244 
2245  /* Reset mean_query_len to match the new state */
2247 
2248  /*
2249  * Bump the GC count even though we failed.
2250  *
2251  * This is needed to make concurrent readers of file without any lock on
2252  * pgss->lock notice existence of new version of file. Once readers
2253  * subsequently observe a change in GC count with pgss->lock held, that
2254  * forces a safe reopen of file. Writers also require that we bump here,
2255  * of course. (As required by locking protocol, readers and writers don't
2256  * trust earlier file contents until gc_count is found unchanged after
2257  * pgss->lock acquired in shared or exclusive mode respectively.)
2258  */
2259  record_gc_qtexts();
2260 }
#define DEBUG1
Definition: elog.h:25
#define PG_BINARY_W
Definition: c.h:1194
#define LOG
Definition: elog.h:26
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
static HTAB * pgss_hash
int errcode_for_file_access(void)
Definition: elog.c:593
static char * qtext_load_file(Size *buffer_size)
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2205
#define ereport(elevel, rest)
Definition: elog.h:141
static bool need_gc_qtexts(void)
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:466
#define record_gc_qtexts()
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1389
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1379
int FreeFile(FILE *file)
Definition: fd.c:2404
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
#define PGSS_TEXT_FILE
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1465
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)
#define ftruncate(a, b)
Definition: win32_port.h:60

◆ generate_normalized_query()

static char * generate_normalized_query ( pgssJumbleState jstate,
const char *  query,
int  query_loc,
int *  query_len_p,
int  encoding 
)
static

Definition at line 3048 of file pg_stat_statements.c.

References Assert, pgssJumbleState::clocations, pgssJumbleState::clocations_count, fill_in_constant_lengths(), pgssJumbleState::highest_extern_param_id, i, pgssLocationLen::length, pgssLocationLen::location, palloc(), and sprintf.

Referenced by pgss_store().

3050 {
3051  char *norm_query;
3052  int query_len = *query_len_p;
3053  int i,
3054  norm_query_buflen, /* Space allowed for norm_query */
3055  len_to_wrt, /* Length (in bytes) to write */
3056  quer_loc = 0, /* Source query byte location */
3057  n_quer_loc = 0, /* Normalized query byte location */
3058  last_off = 0, /* Offset from start for previous tok */
3059  last_tok_len = 0; /* Length (in bytes) of that tok */
3060 
3061  /*
3062  * Get constants' lengths (core system only gives us locations). Note
3063  * this also ensures the items are sorted by location.
3064  */
3065  fill_in_constant_lengths(jstate, query, query_loc);
3066 
3067  /*
3068  * Allow for $n symbols to be longer than the constants they replace.
3069  * Constants must take at least one byte in text form, while a $n symbol
3070  * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
3071  * could refine that limit based on the max value of n for the current
3072  * query, but it hardly seems worth any extra effort to do so.
3073  */
3074  norm_query_buflen = query_len + jstate->clocations_count * 10;
3075 
3076  /* Allocate result buffer */
3077  norm_query = palloc(norm_query_buflen + 1);
3078 
3079  for (i = 0; i < jstate->clocations_count; i++)
3080  {
3081  int off, /* Offset from start for cur tok */
3082  tok_len; /* Length (in bytes) of that tok */
3083 
3084  off = jstate->clocations[i].location;
3085  /* Adjust recorded location if we're dealing with partial string */
3086  off -= query_loc;
3087 
3088  tok_len = jstate->clocations[i].length;
3089 
3090  if (tok_len < 0)
3091  continue; /* ignore any duplicates */
3092 
3093  /* Copy next chunk (what precedes the next constant) */
3094  len_to_wrt = off - last_off;
3095  len_to_wrt -= last_tok_len;
3096 
3097  Assert(len_to_wrt >= 0);
3098  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
3099  n_quer_loc += len_to_wrt;
3100 
3101  /* And insert a param symbol in place of the constant token */
3102  n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
3103  i + 1 + jstate->highest_extern_param_id);
3104 
3105  quer_loc = off + tok_len;
3106  last_off = off;
3107  last_tok_len = tok_len;
3108  }
3109 
3110  /*
3111  * We've copied up until the last ignorable constant. Copy over the
3112  * remaining bytes of the original query string.
3113  */
3114  len_to_wrt = query_len - quer_loc;
3115 
3116  Assert(len_to_wrt >= 0);
3117  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
3118  n_quer_loc += len_to_wrt;
3119 
3120  Assert(n_quer_loc <= norm_query_buflen);
3121  norm_query[n_quer_loc] = '\0';
3122 
3123  *query_len_p = n_quer_loc;
3124  return norm_query;
3125 }
#define sprintf
Definition: port.h:194
pgssLocationLen * clocations
static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query, int query_loc)
#define Assert(condition)
Definition: c.h:732
void * palloc(Size size)
Definition: mcxt.c:949
int i

◆ JumbleExpr()

static void JumbleExpr ( pgssJumbleState jstate,
Node node 
)
static

Definition at line 2528 of file pg_stat_statements.c.

References OnConflictExpr::action, Aggref::aggdirectargs, Aggref::aggdistinct, Aggref::aggfilter, WindowFunc::aggfilter, Aggref::aggfnoid, Aggref::aggorder, SetOperationStmt::all, APP_JUMB, APP_JUMB_STRING, OnConflictExpr::arbiterElems, OnConflictExpr::arbiterWhere, NamedArgExpr::arg, FieldSelect::arg, FieldStore::arg, RelabelType::arg, CoerceViaIO::arg, ArrayCoerceExpr::arg, ConvertRowtypeExpr::arg, CollateExpr::arg, CaseExpr::arg, NullTest::arg, BooleanTest::arg, CoerceToDomain::arg, NamedArgExpr::argnumber, generate_unaccent_rules::args, Aggref::args, WindowFunc::args, FuncExpr::args, OpExpr::args, ScalarArrayOpExpr::args, BoolExpr::args, CaseExpr::args, MinMaxExpr::args, TableSampleClause::args, XmlExpr::args, BoolExpr::boolop, BooleanTest::booltesttype, castNode, check_stack_depth(), TableFunc::colexprs, CollateExpr::collOid, OnConflictExpr::constraint, Const::consttype, GroupingSet::content, CommonTableExpr::ctematerialized, CommonTableExpr::ctename, CommonTableExpr::ctequery, CurrentOfExpr::cursor_name, CurrentOfExpr::cursor_param, CurrentOfExpr::cvarno, CaseExpr::defresult, TableFunc::docexpr, ArrayCoerceExpr::elemexpr, elog, WindowClause::endOffset, SortGroupClause::eqop, OnConflictExpr::exclRelIndex, OnConflictExpr::exclRelTlist, CaseWhen::expr, InferenceElem::expr, TargetEntry::expr, FieldSelect::fieldnum, WindowClause::frameOptions, FromExpr::fromlist, RangeTblFunction::funcexpr, FuncExpr::funcid, pgssJumbleState::highest_extern_param_id, InferenceElem::infercollid, InferenceElem::inferopclass, JoinExpr::isNatural, JoinExpr::jointype, JumbleQuery(), JoinExpr::larg, SetOperationStmt::larg, RowCompareExpr::largs, lfirst, lfirst_int, lfirst_node, Const::location, XmlExpr::named_args, FieldStore::newvals, nodeTag, SortGroupClause::nulls_first, NullTest::nulltesttype, OnConflictExpr::onConflictSet, OnConflictExpr::onConflictWhere, MinMaxExpr::op, SQLValueFunction::op, XmlExpr::op, SetOperationStmt::op, OpExpr::opno, ScalarArrayOpExpr::opno, WindowClause::orderClause, PARAM_EXTERN, Param::paramid, Param::paramkind, Param::paramtype, WindowClause::partitionClause, JoinExpr::quals, FromExpr::quals, JoinExpr::rarg, SetOperationStmt::rarg, RowCompareExpr::rargs, RowCompareExpr::rctype, RecordConstLocation(), SubscriptingRef::refassgnexpr, SubscriptingRef::refexpr, SubscriptingRef::reflowerindexpr, GroupingFunc::refs, SubscriptingRef::refupperindexpr, TableSampleClause::repeatable, TargetEntry::resno, TargetEntry::ressortgroupref, CaseWhen::result, RelabelType::resulttype, CoerceViaIO::resulttype, ArrayCoerceExpr::resulttype, ConvertRowtypeExpr::resulttype, CoerceToDomain::resulttype, TableFunc::rowexpr, RangeTblRef::rtindex, JoinExpr::rtindex, NextValueExpr::seqid, SortGroupClause::sortop, WindowClause::startOffset, SubLink::subLinkId, SubLink::subLinkType, SubLink::subselect, T_Aggref, T_ArrayCoerceExpr, T_ArrayExpr, T_BooleanTest, T_BoolExpr, T_CaseExpr, T_CaseTestExpr, T_CoalesceExpr, T_CoerceToDomain, T_CoerceToDomainValue, T_CoerceViaIO, T_CollateExpr, T_CommonTableExpr, T_Const, T_ConvertRowtypeExpr, T_CurrentOfExpr, T_DistinctExpr, T_FieldSelect, T_FieldStore, T_FromExpr, T_FuncExpr, T_GroupingFunc, T_GroupingSet, T_InferenceElem, T_IntList, T_JoinExpr, T_List, T_MinMaxExpr, T_NamedArgExpr, T_NextValueExpr, T_NullIfExpr, T_NullTest, T_OnConflictExpr, T_OpExpr, T_Param, T_RangeTblFunction, T_RangeTblRef, T_RelabelType, T_RowCompareExpr, T_RowExpr, T_ScalarArrayOpExpr, T_SetOperationStmt, T_SetToDefault, T_SortGroupClause, T_SQLValueFunction, T_SubLink, T_SubscriptingRef, T_TableFunc, T_TableSampleClause, T_TargetEntry, T_Var, T_WindowClause, T_WindowFunc, T_XmlExpr, SubLink::testexpr, SortGroupClause::tleSortGroupRef, TableSampleClause::tsmhandler, Node::type, CaseTestExpr::typeId, CoerceToDomainValue::typeId, SetToDefault::typeId, NextValueExpr::typeId, SQLValueFunction::typmod, ScalarArrayOpExpr::useOr, Var::varattno, Var::varlevelsup, Var::varno, WARNING, WindowFunc::winfnoid, WindowFunc::winref, and WindowClause::winref.

Referenced by JumbleQuery(), and JumbleRangeTable().

2529 {
2530  ListCell *temp;
2531 
2532  if (node == NULL)
2533  return;
2534 
2535  /* Guard against stack overflow due to overly complex expressions */
2537 
2538  /*
2539  * We always emit the node's NodeTag, then any additional fields that are
2540  * considered significant, and then we recurse to any child nodes.
2541  */
2542  APP_JUMB(node->type);
2543 
2544  switch (nodeTag(node))
2545  {
2546  case T_Var:
2547  {
2548  Var *var = (Var *) node;
2549 
2550  APP_JUMB(var->varno);
2551  APP_JUMB(var->varattno);
2552  APP_JUMB(var->varlevelsup);
2553  }
2554  break;
2555  case T_Const:
2556  {
2557  Const *c = (Const *) node;
2558 
2559  /* We jumble only the constant's type, not its value */
2560  APP_JUMB(c->consttype);
2561  /* Also, record its parse location for query normalization */
2562  RecordConstLocation(jstate, c->location);
2563  }
2564  break;
2565  case T_Param:
2566  {
2567  Param *p = (Param *) node;
2568 
2569  APP_JUMB(p->paramkind);
2570  APP_JUMB(p->paramid);
2571  APP_JUMB(p->paramtype);
2572  /* Also, track the highest external Param id */
2573  if (p->paramkind == PARAM_EXTERN &&
2574  p->paramid > jstate->highest_extern_param_id)
2575  jstate->highest_extern_param_id = p->paramid;
2576  }
2577  break;
2578  case T_Aggref:
2579  {
2580  Aggref *expr = (Aggref *) node;
2581 
2582  APP_JUMB(expr->aggfnoid);
2583  JumbleExpr(jstate, (Node *) expr->aggdirectargs);
2584  JumbleExpr(jstate, (Node *) expr->args);
2585  JumbleExpr(jstate, (Node *) expr->aggorder);
2586  JumbleExpr(jstate, (Node *) expr->aggdistinct);
2587  JumbleExpr(jstate, (Node *) expr->aggfilter);
2588  }
2589  break;
2590  case T_GroupingFunc:
2591  {
2592  GroupingFunc *grpnode = (GroupingFunc *) node;
2593 
2594  JumbleExpr(jstate, (Node *) grpnode->refs);
2595  }
2596  break;
2597  case T_WindowFunc:
2598  {
2599  WindowFunc *expr = (WindowFunc *) node;
2600 
2601  APP_JUMB(expr->winfnoid);
2602  APP_JUMB(expr->winref);
2603  JumbleExpr(jstate, (Node *) expr->args);
2604  JumbleExpr(jstate, (Node *) expr->aggfilter);
2605  }
2606  break;
2607  case T_SubscriptingRef:
2608  {
2609  SubscriptingRef *sbsref = (SubscriptingRef *) node;
2610 
2611  JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
2612  JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
2613  JumbleExpr(jstate, (Node *) sbsref->refexpr);
2614  JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
2615  }
2616  break;
2617  case T_FuncExpr:
2618  {
2619  FuncExpr *expr = (FuncExpr *) node;
2620 
2621  APP_JUMB(expr->funcid);
2622  JumbleExpr(jstate, (Node *) expr->args);
2623  }
2624  break;
2625  case T_NamedArgExpr:
2626  {
2627  NamedArgExpr *nae = (NamedArgExpr *) node;
2628 
2629  APP_JUMB(nae->argnumber);
2630  JumbleExpr(jstate, (Node *) nae->arg);
2631  }
2632  break;
2633  case T_OpExpr:
2634  case T_DistinctExpr: /* struct-equivalent to OpExpr */
2635  case T_NullIfExpr: /* struct-equivalent to OpExpr */
2636  {
2637  OpExpr *expr = (OpExpr *) node;
2638 
2639  APP_JUMB(expr->opno);
2640  JumbleExpr(jstate, (Node *) expr->args);
2641  }
2642  break;
2643  case T_ScalarArrayOpExpr:
2644  {
2645  ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
2646 
2647  APP_JUMB(expr->opno);
2648  APP_JUMB(expr->useOr);
2649  JumbleExpr(jstate, (Node *) expr->args);
2650  }
2651  break;
2652  case T_BoolExpr:
2653  {
2654  BoolExpr *expr = (BoolExpr *) node;
2655 
2656  APP_JUMB(expr->boolop);
2657  JumbleExpr(jstate, (Node *) expr->args);
2658  }
2659  break;
2660  case T_SubLink:
2661  {
2662  SubLink *sublink = (SubLink *) node;
2663 
2664  APP_JUMB(sublink->subLinkType);
2665  APP_JUMB(sublink->subLinkId);
2666  JumbleExpr(jstate, (Node *) sublink->testexpr);
2667  JumbleQuery(jstate, castNode(Query, sublink->subselect));
2668  }
2669  break;
2670  case T_FieldSelect:
2671  {
2672  FieldSelect *fs = (FieldSelect *) node;
2673 
2674  APP_JUMB(fs->fieldnum);
2675  JumbleExpr(jstate, (Node *) fs->arg);
2676  }
2677  break;
2678  case T_FieldStore:
2679  {
2680  FieldStore *fstore = (FieldStore *) node;
2681 
2682  JumbleExpr(jstate, (Node *) fstore->arg);
2683  JumbleExpr(jstate, (Node *) fstore->newvals);
2684  }
2685  break;
2686  case T_RelabelType:
2687  {
2688  RelabelType *rt = (RelabelType *) node;
2689 
2690  APP_JUMB(rt->resulttype);
2691  JumbleExpr(jstate, (Node *) rt->arg);
2692  }
2693  break;
2694  case T_CoerceViaIO:
2695  {
2696  CoerceViaIO *cio = (CoerceViaIO *) node;
2697 
2698  APP_JUMB(cio->resulttype);
2699  JumbleExpr(jstate, (Node *) cio->arg);
2700  }
2701  break;
2702  case T_ArrayCoerceExpr:
2703  {
2704  ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
2705 
2706  APP_JUMB(acexpr->resulttype);
2707  JumbleExpr(jstate, (Node *) acexpr->arg);
2708  JumbleExpr(jstate, (Node *) acexpr->elemexpr);
2709  }
2710  break;
2711  case T_ConvertRowtypeExpr:
2712  {
2713  ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
2714 
2715  APP_JUMB(crexpr->resulttype);
2716  JumbleExpr(jstate, (Node *) crexpr->arg);
2717  }
2718  break;
2719  case T_CollateExpr:
2720  {
2721  CollateExpr *ce = (CollateExpr *) node;
2722 
2723  APP_JUMB(ce->collOid);
2724  JumbleExpr(jstate, (Node *) ce->arg);
2725  }
2726  break;
2727  case T_CaseExpr:
2728  {
2729  CaseExpr *caseexpr = (CaseExpr *) node;
2730 
2731  JumbleExpr(jstate, (Node *) caseexpr->arg);
2732  foreach(temp, caseexpr->args)
2733  {
2734  CaseWhen *when = lfirst_node(CaseWhen, temp);
2735 
2736  JumbleExpr(jstate, (Node *) when->expr);
2737  JumbleExpr(jstate, (Node *) when->result);
2738  }
2739  JumbleExpr(jstate, (Node *) caseexpr->defresult);
2740  }
2741  break;
2742  case T_CaseTestExpr:
2743  {
2744  CaseTestExpr *ct = (CaseTestExpr *) node;
2745 
2746  APP_JUMB(ct->typeId);
2747  }
2748  break;
2749  case T_ArrayExpr:
2750  JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
2751  break;
2752  case T_RowExpr:
2753  JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
2754  break;
2755  case T_RowCompareExpr:
2756  {
2757  RowCompareExpr *rcexpr = (RowCompareExpr *) node;
2758 
2759  APP_JUMB(rcexpr->rctype);
2760  JumbleExpr(jstate, (Node *) rcexpr->largs);
2761  JumbleExpr(jstate, (Node *) rcexpr->rargs);
2762  }
2763  break;
2764  case T_CoalesceExpr:
2765  JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
2766  break;
2767  case T_MinMaxExpr:
2768  {
2769  MinMaxExpr *mmexpr = (MinMaxExpr *) node;
2770 
2771  APP_JUMB(mmexpr->op);
2772  JumbleExpr(jstate, (Node *) mmexpr->args);
2773  }
2774  break;
2775  case T_SQLValueFunction:
2776  {
2777  SQLValueFunction *svf = (SQLValueFunction *) node;
2778 
2779  APP_JUMB(svf->op);
2780  /* type is fully determined by op */
2781  APP_JUMB(svf->typmod);
2782  }
2783  break;
2784  case T_XmlExpr:
2785  {
2786  XmlExpr *xexpr = (XmlExpr *) node;
2787 
2788  APP_JUMB(xexpr->op);
2789  JumbleExpr(jstate, (Node *) xexpr->named_args);
2790  JumbleExpr(jstate, (Node *) xexpr->args);
2791  }
2792  break;
2793  case T_NullTest:
2794  {
2795  NullTest *nt = (NullTest *) node;
2796 
2797  APP_JUMB(nt->nulltesttype);
2798  JumbleExpr(jstate, (Node *) nt->arg);
2799  }
2800  break;
2801  case T_BooleanTest:
2802  {
2803  BooleanTest *bt = (BooleanTest *) node;
2804 
2805  APP_JUMB(bt->booltesttype);
2806  JumbleExpr(jstate, (Node *) bt->arg);
2807  }
2808  break;
2809  case T_CoerceToDomain:
2810  {
2811  CoerceToDomain *cd = (CoerceToDomain *) node;
2812 
2813  APP_JUMB(cd->resulttype);
2814  JumbleExpr(jstate, (Node *) cd->arg);
2815  }
2816  break;
2817  case T_CoerceToDomainValue:
2818  {
2819  CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
2820 
2821  APP_JUMB(cdv->typeId);
2822  }
2823  break;
2824  case T_SetToDefault:
2825  {
2826  SetToDefault *sd = (SetToDefault *) node;
2827 
2828  APP_JUMB(sd->typeId);
2829  }
2830  break;
2831  case T_CurrentOfExpr:
2832  {
2833  CurrentOfExpr *ce = (CurrentOfExpr *) node;
2834 
2835  APP_JUMB(ce->cvarno);
2836  if (ce->cursor_name)
2838  APP_JUMB(ce->cursor_param);
2839  }
2840  break;
2841  case T_NextValueExpr:
2842  {
2843  NextValueExpr *nve = (NextValueExpr *) node;
2844 
2845  APP_JUMB(nve->seqid);
2846  APP_JUMB(nve->typeId);
2847  }
2848  break;
2849  case T_InferenceElem:
2850  {
2851  InferenceElem *ie = (InferenceElem *) node;
2852 
2853  APP_JUMB(ie->infercollid);
2854  APP_JUMB(ie->inferopclass);
2855  JumbleExpr(jstate, ie->expr);
2856  }
2857  break;
2858  case T_TargetEntry:
2859  {
2860  TargetEntry *tle = (TargetEntry *) node;
2861 
2862  APP_JUMB(tle->resno);
2863  APP_JUMB(tle->ressortgroupref);
2864  JumbleExpr(jstate, (Node *) tle->expr);
2865  }
2866  break;
2867  case T_RangeTblRef:
2868  {
2869  RangeTblRef *rtr = (RangeTblRef *) node;
2870 
2871  APP_JUMB(rtr->rtindex);
2872  }
2873  break;
2874  case T_JoinExpr:
2875  {
2876  JoinExpr *join = (JoinExpr *) node;
2877 
2878  APP_JUMB(join->jointype);
2879  APP_JUMB(join->isNatural);
2880  APP_JUMB(join->rtindex);
2881  JumbleExpr(jstate, join->larg);
2882  JumbleExpr(jstate, join->rarg);
2883  JumbleExpr(jstate, join->quals);
2884  }
2885  break;
2886  case T_FromExpr:
2887  {
2888  FromExpr *from = (FromExpr *) node;
2889 
2890  JumbleExpr(jstate, (Node *) from->fromlist);
2891  JumbleExpr(jstate, from->quals);
2892  }
2893  break;
2894  case T_OnConflictExpr:
2895  {
2896  OnConflictExpr *conf = (OnConflictExpr *) node;
2897 
2898  APP_JUMB(conf->action);
2899  JumbleExpr(jstate, (Node *) conf->arbiterElems);
2900  JumbleExpr(jstate, conf->arbiterWhere);
2901  JumbleExpr(jstate, (Node *) conf->onConflictSet);
2902  JumbleExpr(jstate, conf->onConflictWhere);
2903  APP_JUMB(conf->constraint);
2904  APP_JUMB(conf->exclRelIndex);
2905  JumbleExpr(jstate, (Node *) conf->exclRelTlist);
2906  }
2907  break;
2908  case T_List:
2909  foreach(temp, (List *) node)
2910  {
2911  JumbleExpr(jstate, (Node *) lfirst(temp));
2912  }
2913  break;
2914  case T_IntList:
2915  foreach(temp, (List *) node)
2916  {
2917  APP_JUMB(lfirst_int(temp));
2918  }
2919  break;
2920  case T_SortGroupClause:
2921  {
2922  SortGroupClause *sgc = (SortGroupClause *) node;
2923 
2924  APP_JUMB(sgc->tleSortGroupRef);
2925  APP_JUMB(sgc->eqop);
2926  APP_JUMB(sgc->sortop);
2927  APP_JUMB(sgc->nulls_first);
2928  }
2929  break;
2930  case T_GroupingSet:
2931  {
2932  GroupingSet *gsnode = (GroupingSet *) node;
2933 
2934  JumbleExpr(jstate, (Node *) gsnode->content);
2935  }
2936  break;
2937  case T_WindowClause:
2938  {
2939  WindowClause *wc = (WindowClause *) node;
2940 
2941  APP_JUMB(wc->winref);
2942  APP_JUMB(wc->frameOptions);
2943  JumbleExpr(jstate, (Node *) wc->partitionClause);
2944  JumbleExpr(jstate, (Node *) wc->orderClause);
2945  JumbleExpr(jstate, wc->startOffset);
2946  JumbleExpr(jstate, wc->endOffset);
2947  }
2948  break;
2949  case T_CommonTableExpr:
2950  {
2951  CommonTableExpr *cte = (CommonTableExpr *) node;
2952 
2953  /* we store the string name because RTE_CTE RTEs need it */
2954  APP_JUMB_STRING(cte->ctename);
2955  APP_JUMB(cte->ctematerialized);
2956  JumbleQuery(jstate, castNode(Query, cte->ctequery));
2957  }
2958  break;
2959  case T_SetOperationStmt:
2960  {
2961  SetOperationStmt *setop = (SetOperationStmt *) node;
2962 
2963  APP_JUMB(setop->op);
2964  APP_JUMB(setop->all);
2965  JumbleExpr(jstate, setop->larg);
2966  JumbleExpr(jstate, setop->rarg);
2967  }
2968  break;
2969  case T_RangeTblFunction:
2970  {
2971  RangeTblFunction *rtfunc = (RangeTblFunction *) node;
2972 
2973  JumbleExpr(jstate, rtfunc->funcexpr);
2974  }
2975  break;
2976  case T_TableFunc:
2977  {
2978  TableFunc *tablefunc = (TableFunc *) node;
2979 
2980  JumbleExpr(jstate, tablefunc->docexpr);
2981  JumbleExpr(jstate, tablefunc->rowexpr);
2982  JumbleExpr(jstate, (Node *) tablefunc->colexprs);
2983  }
2984  break;
2985  case T_TableSampleClause:
2986  {
2987  TableSampleClause *tsc = (TableSampleClause *) node;
2988 
2989  APP_JUMB(tsc->tsmhandler);
2990  JumbleExpr(jstate, (Node *) tsc->args);
2991  JumbleExpr(jstate, (Node *) tsc->repeatable);
2992  }
2993  break;
2994  default:
2995  /* Only a warning, since we can stumble along anyway */
2996  elog(WARNING, "unrecognized node type: %d",
2997  (int) nodeTag(node));
2998  break;
2999  }
3000 }
List * aggdistinct
Definition: primnodes.h:307
List * args
Definition: primnodes.h:1092
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
Node * docexpr
Definition: primnodes.h:87
Expr * arg
Definition: primnodes.h:777
Index varlevelsup
Definition: primnodes.h:177
List * content
Definition: parsenodes.h:1304
List * refs
Definition: primnodes.h:347
List * args
Definition: primnodes.h:363
List * args
Definition: primnodes.h:463
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
Oid resulttype
Definition: primnodes.h:821
RowCompareType rctype
Definition: primnodes.h:1056
Index tleSortGroupRef
Definition: parsenodes.h:1234
Expr * arg
Definition: primnodes.h:800
ParamKind paramkind
Definition: primnodes.h:248
Definition: nodes.h:525
List * args
Definition: primnodes.h:305
AttrNumber varattno
Definition: primnodes.h:172
Expr * arg
Definition: primnodes.h:748
List * fromlist
Definition: primnodes.h:1496
Index winref
Definition: primnodes.h:365
Definition: primnodes.h:167
List * refupperindexpr
Definition: primnodes.h:408
Node * quals
Definition: primnodes.h:1497
SQLValueFunctionOp op
Definition: primnodes.h:1129
#define APP_JUMB_STRING(str)
static void RecordConstLocation(pgssJumbleState *jstate, int location)
List * arbiterElems
Definition: primnodes.h:1515
Node * larg
Definition: primnodes.h:1476
Oid consttype
Definition: primnodes.h:196
Oid funcid
Definition: primnodes.h:455
#define lfirst_int(lc)
Definition: pg_list.h:191
List * partitionClause
Definition: parsenodes.h:1330
BoolExprType boolop
Definition: primnodes.h:568
Expr * arg
Definition: primnodes.h:1205
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
Node * rowexpr
Definition: primnodes.h:88
char * c
NodeTag type
Definition: nodes.h:527
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
List * exclRelTlist
Definition: primnodes.h:1524
void check_stack_depth(void)
Definition: postgres.c:3262
List * aggorder
Definition: primnodes.h:306
Expr * arg
Definition: primnodes.h:1228
AttrNumber resno
Definition: primnodes.h:1394
char * cursor_name
Definition: primnodes.h:1303
List * aggdirectargs
Definition: primnodes.h:304
Oid winfnoid
Definition: primnodes.h:359
Expr * arg
Definition: primnodes.h:820
Expr * elemexpr
Definition: primnodes.h:845
Definition: type.h:82
Definition: nodes.h:297
List * newvals
Definition: primnodes.h:778
Definition: nodes.h:152
OnConflictAction action
Definition: primnodes.h:1512
bool isNatural
Definition: primnodes.h:1475
#define WARNING
Definition: elog.h:40
Index varno
Definition: primnodes.h:170
Definition: nodes.h:151
XmlExprOp op
Definition: primnodes.h:1167
Node * startOffset
Definition: parsenodes.h:1333
List * args
Definition: primnodes.h:919
int location
Definition: primnodes.h:207
Node * quals
Definition: primnodes.h:1479
BoolTestType booltesttype
Definition: primnodes.h:1229
Oid resulttype
Definition: primnodes.h:801
NullTestType nulltesttype
Definition: primnodes.h:1206
Oid aggfnoid
Definition: primnodes.h:298
List * colexprs
Definition: primnodes.h:93
List * named_args
Definition: primnodes.h:1169
List * args
Definition: primnodes.h:1171
Node * rarg
Definition: primnodes.h:1477
Expr * arg
Definition: primnodes.h:484
JoinType jointype
Definition: primnodes.h:1474
#define lfirst(lc)
Definition: pg_list.h:190
CTEMaterialize ctematerialized
Definition: parsenodes.h:1429
Expr * aggfilter
Definition: primnodes.h:364
Expr * expr
Definition: primnodes.h:1393
int paramid
Definition: primnodes.h:249
Node * endOffset
Definition: parsenodes.h:1334
Expr * arg
Definition: primnodes.h:886
Expr * aggfilter
Definition: primnodes.h:308
SetOperation op
Definition: parsenodes.h:1634
List * args
Definition: primnodes.h:569
#define nodeTag(nodeptr)
Definition: nodes.h:530
List * orderClause
Definition: parsenodes.h:1331
Node * arbiterWhere
Definition: primnodes.h:1517
Expr * refassgnexpr
Definition: primnodes.h:416
List * reflowerindexpr
Definition: primnodes.h:410
#define elog(elevel,...)
Definition: elog.h:226
List * onConflictSet
Definition: primnodes.h:1521
Index ressortgroupref
Definition: primnodes.h:1396
MinMaxOp op
Definition: primnodes.h:1091
Expr * refexpr
Definition: primnodes.h:413
Expr * arg
Definition: primnodes.h:918
Oid opno
Definition: primnodes.h:502
Expr * result
Definition: primnodes.h:931
List * args
Definition: primnodes.h:508
Expr * defresult
Definition: primnodes.h:920
Expr * expr
Definition: primnodes.h:930
Node * onConflictWhere
Definition: primnodes.h:1522
int rtindex
Definition: primnodes.h:1481
Definition: pg_list.h:50
Oid paramtype
Definition: primnodes.h:250
Definition: nodes.h:153
AttrNumber fieldnum
Definition: primnodes.h:749

◆ JumbleQuery()

static void JumbleQuery ( pgssJumbleState jstate,
Query query 
)
static

Definition at line 2413 of file pg_stat_statements.c.

References APP_JUMB, Assert, Query::commandType, Query::cteList, Query::distinctClause, Query::groupClause, Query::groupingSets, Query::havingQual, IsA, Query::jointree, JumbleExpr(), JumbleRangeTable(), JumbleRowMarks(), Query::limitCount, Query::limitOffset, Query::onConflict, Query::returningList, Query::rowMarks, Query::rtable, Query::setOperations, Query::sortClause, Query::targetList, Query::utilityStmt, and Query::windowClause.

Referenced by JumbleExpr(), JumbleRangeTable(), and pgss_post_parse_analyze().

2414 {
2415  Assert(IsA(query, Query));
2416  Assert(query->utilityStmt == NULL);
2417 
2418  APP_JUMB(query->commandType);
2419  /* resultRelation is usually predictable from commandType */
2420  JumbleExpr(jstate, (Node *) query->cteList);
2421  JumbleRangeTable(jstate, query->rtable);
2422  JumbleExpr(jstate, (Node *) query->jointree);
2423  JumbleExpr(jstate, (Node *) query->targetList);
2424  JumbleExpr(jstate, (Node *) query->onConflict);
2425  JumbleExpr(jstate, (Node *) query->returningList);
2426  JumbleExpr(jstate, (Node *) query->groupClause);
2427  JumbleExpr(jstate, (Node *) query->groupingSets);
2428  JumbleExpr(jstate, query->havingQual);
2429  JumbleExpr(jstate, (Node *) query->windowClause);
2430  JumbleExpr(jstate, (Node *) query->distinctClause);
2431  JumbleExpr(jstate, (Node *) query->sortClause);
2432  JumbleExpr(jstate, query->limitOffset);
2433  JumbleExpr(jstate, query->limitCount);
2434  JumbleRowMarks(jstate, query->rowMarks);
2435  JumbleExpr(jstate, query->setOperations);
2436 }
Node * limitOffset
Definition: parsenodes.h:160
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
List * sortClause
Definition: parsenodes.h:158
static void JumbleRowMarks(pgssJumbleState *jstate, List *rowMarks)
FromExpr * jointree
Definition: parsenodes.h:138
OnConflictExpr * onConflict
Definition: parsenodes.h:144
List * groupingSets
Definition: parsenodes.h:150
Definition: nodes.h:525
List * rowMarks
Definition: parsenodes.h:163
Node * utilityStmt
Definition: parsenodes.h:120
List * windowClause
Definition: parsenodes.h:154
List * targetList
Definition: parsenodes.h:140
static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
List * rtable
Definition: parsenodes.h:137
List * distinctClause
Definition: parsenodes.h:156
#define APP_JUMB(item)
Node * limitCount
Definition: parsenodes.h:161
List * returningList
Definition: parsenodes.h:146
CmdType commandType
Definition: parsenodes.h:112
#define Assert(condition)
Definition: c.h:732
List * cteList
Definition: parsenodes.h:135
Node * setOperations
Definition: parsenodes.h:165
List * groupClause
Definition: parsenodes.h:148
Node * havingQual
Definition: parsenodes.h:152

◆ JumbleRangeTable()

static void JumbleRangeTable ( pgssJumbleState jstate,
List rtable 
)
static

Definition at line 2442 of file pg_stat_statements.c.

References APP_JUMB, APP_JUMB_STRING, RangeTblEntry::ctelevelsup, RangeTblEntry::ctename, elog, RangeTblEntry::enrname, ERROR, RangeTblEntry::functions, RangeTblEntry::jointype, JumbleExpr(), JumbleQuery(), lfirst_node, RangeTblEntry::relid, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_NAMEDTUPLESTORE, RTE_RELATION, RTE_RESULT, RTE_SUBQUERY, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::subquery, RangeTblEntry::tablefunc, RangeTblEntry::tablesample, and RangeTblEntry::values_lists.

Referenced by JumbleQuery().

2443 {
2444  ListCell *lc;
2445 
2446  foreach(lc, rtable)
2447  {
2449 
2450  APP_JUMB(rte->rtekind);
2451  switch (rte->rtekind)
2452  {
2453  case RTE_RELATION:
2454  APP_JUMB(rte->relid);
2455  JumbleExpr(jstate, (Node *) rte->tablesample);
2456  break;
2457  case RTE_SUBQUERY:
2458  JumbleQuery(jstate, rte->subquery);
2459  break;
2460  case RTE_JOIN:
2461  APP_JUMB(rte->jointype);
2462  break;
2463  case RTE_FUNCTION:
2464  JumbleExpr(jstate, (Node *) rte->functions);
2465  break;
2466  case RTE_TABLEFUNC:
2467  JumbleExpr(jstate, (Node *) rte->tablefunc);
2468  break;
2469  case RTE_VALUES:
2470  JumbleExpr(jstate, (Node *) rte->values_lists);
2471  break;
2472  case RTE_CTE:
2473 
2474  /*
2475  * Depending on the CTE name here isn't ideal, but it's the
2476  * only info we have to identify the referenced WITH item.
2477  */
2478  APP_JUMB_STRING(rte->ctename);
2479  APP_JUMB(rte->ctelevelsup);
2480  break;
2481  case RTE_NAMEDTUPLESTORE:
2482  APP_JUMB_STRING(rte->enrname);
2483  break;
2484  case RTE_RESULT:
2485  break;
2486  default:
2487  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
2488  break;
2489  }
2490  }
2491 }
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
Definition: nodes.h:525
List * values_lists
Definition: parsenodes.h:1051
#define APP_JUMB_STRING(str)
#define ERROR
Definition: elog.h:43
TableFunc * tablefunc
Definition: parsenodes.h:1046
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
JoinType jointype
Definition: parsenodes.h:1029
char * enrname
Definition: parsenodes.h:1085
List * functions
Definition: parsenodes.h:1040
Index ctelevelsup
Definition: parsenodes.h:1057
RTEKind rtekind
Definition: parsenodes.h:974
char * ctename
Definition: parsenodes.h:1056
Query * subquery
Definition: parsenodes.h:1009
#define elog(elevel,...)
Definition: elog.h:226
struct TableSampleClause * tablesample
Definition: parsenodes.h:1004

◆ JumbleRowMarks()

static void JumbleRowMarks ( pgssJumbleState jstate,
List rowMarks 
)
static

Definition at line 2497 of file pg_stat_statements.c.

References APP_JUMB, lfirst_node, RowMarkClause::pushedDown, RowMarkClause::rti, RowMarkClause::strength, and RowMarkClause::waitPolicy.

Referenced by JumbleQuery().

2498 {
2499  ListCell *lc;
2500 
2501  foreach(lc, rowMarks)
2502  {
2503  RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
2504  if (!rowmark->pushedDown)
2505  {
2506  APP_JUMB(rowmark->rti);
2507  APP_JUMB(rowmark->strength);
2508  APP_JUMB(rowmark->waitPolicy);
2509  }
2510  }
2511 }
LockClauseStrength strength
Definition: parsenodes.h:1360
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
LockWaitPolicy waitPolicy
Definition: parsenodes.h:1361

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2035 of file pg_stat_statements.c.

References pgssSharedState::extent, pgssSharedState::mutex, pgss_max, SpinLockAcquire, and SpinLockRelease.

Referenced by gc_qtexts(), and pgss_store().

2036 {
2037  Size extent;
2038 
2039  /* Read shared extent pointer */
2040  {
2041  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2042 
2043  SpinLockAcquire(&s->mutex);
2044  extent = s->extent;
2045  SpinLockRelease(&s->mutex);
2046  }
2047 
2048  /* Don't proceed if file does not exceed 512 bytes per possible entry */
2049  if (extent < 512 * pgss_max)
2050  return false;
2051 
2052  /*
2053  * Don't proceed if file is less than about 50% bloat. Nothing can or
2054  * should be done in the event of unusually large query texts accounting
2055  * for file's large size. We go to the trouble of maintaining the mean
2056  * query length in order to prevent garbage collection from thrashing
2057  * uselessly.
2058  */
2059  if (extent < pgss->mean_query_len * pgss_max * 2)
2060  return false;
2061 
2062  return true;
2063 }
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
static int pgss_max
#define SpinLockRelease(lock)
Definition: spin.h:64
size_t Size
Definition: c.h:466

◆ PG_FUNCTION_INFO_V1() [1/5]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset  )

◆ PG_FUNCTION_INFO_V1() [2/5]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset_1_7  )

◆ PG_FUNCTION_INFO_V1() [3/5]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_2  )

◆ PG_FUNCTION_INFO_V1() [4/5]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_3  )

◆ PG_FUNCTION_INFO_V1() [5/5]

PG_FUNCTION_INFO_V1 ( pg_stat_statements  )

◆ pg_stat_statements()

Datum pg_stat_statements ( PG_FUNCTION_ARGS  )

Definition at line 1379 of file pg_stat_statements.c.

References pg_stat_statements_internal(), and PGSS_V1_0.

1380 {
1381  /* If it's really API 1.1, we'll figure that out below */
1382  pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
1383 
1384  return (Datum) 0;
1385 }
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:367

◆ pg_stat_statements_1_2()

Datum pg_stat_statements_1_2 ( PG_FUNCTION_ARGS  )

Definition at line 1365 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

1366 {
1367  bool showtext = PG_GETARG_BOOL(0);
1368 
1369  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1370 
1371  return (Datum) 0;
1372 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:269
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:367

◆ pg_stat_statements_1_3()

Datum pg_stat_statements_1_3 ( PG_FUNCTION_ARGS  )

Definition at line 1355 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

1356 {
1357  bool showtext = PG_GETARG_BOOL(0);
1358 
1359  pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
1360 
1361  return (Datum) 0;
1362 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:269
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:367

◆ pg_stat_statements_internal()

static void pg_stat_statements_internal ( FunctionCallInfo  fcinfo,
pgssVersion  api_version,
bool  showtext 
)
static

Definition at line 1389 of file pg_stat_statements.c.

References ReturnSetInfo::allowedModes, Assert, Counters::blk_read_time, Counters::blk_write_time, Counters::calls, pgssEntry::counters, CStringGetTextDatum, pgssHashKey::dbid, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, enc, pgssEntry::encoding, ereport, errcode(), errmsg(), ERROR, pgssSharedState::extent, Float8GetDatumFast, free, pgssSharedState::gc_count, get_call_result_type(), GetUserId(), hash_seq_init(), hash_seq_search(), i, Int64GetDatumFast, is_member_of_role(), IsA, pgssEntry::key, Counters::local_blks_dirtied, Counters::local_blks_hit, Counters::local_blks_read, Counters::local_blks_written, pgssSharedState::lock, LW_SHARED, LWLockAcquire(), LWLockRelease(), Counters::max_time, Counters::mean_time, MemoryContextSwitchTo(), Counters::min_time, pgssEntry::mutex, pgssSharedState::mutex, pgssSharedState::n_writers, ObjectIdGetDatum, pfree(), pg_any_to_server(), PG_STAT_STATEMENTS_COLS, PG_STAT_STATEMENTS_COLS_V1_0, PG_STAT_STATEMENTS_COLS_V1_1, PG_STAT_STATEMENTS_COLS_V1_2, PG_STAT_STATEMENTS_COLS_V1_3, PGSS_V1_0, PGSS_V1_1, PGSS_V1_2, PGSS_V1_3, qtext_fetch(), qtext_load_file(), pgssEntry::query_len, pgssEntry::query_offset, pgssHashKey::queryid, FunctionCallInfoBaseData::resultinfo, ReturnSetInfo::returnMode, Counters::rows, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, Counters::shared_blks_dirtied, Counters::shared_blks_hit, Counters::shared_blks_read, Counters::shared_blks_written, SpinLockAcquire, SpinLockRelease, Counters::sum_var_time, Counters::temp_blks_read, Counters::temp_blks_written, Counters::total_time, tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), TYPEFUNC_COMPOSITE, pgssHashKey::userid, values, and work_mem.

Referenced by pg_stat_statements(), pg_stat_statements_1_2(), and pg_stat_statements_1_3().

1392 {
1393  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1394  TupleDesc tupdesc;
1395  Tuplestorestate *tupstore;
1396  MemoryContext per_query_ctx;
1397  MemoryContext oldcontext;
1398  Oid userid = GetUserId();
1399  bool is_allowed_role = false;
1400  char *qbuffer = NULL;
1401  Size qbuffer_size = 0;
1402  Size extent = 0;
1403  int gc_count = 0;
1404  HASH_SEQ_STATUS hash_seq;
1405  pgssEntry *entry;
1406 
1407  /* Superusers or members of pg_read_all_stats members are allowed */
1408  is_allowed_role = is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS);
1409 
1410  /* hash table must exist already */
1411  if (!pgss || !pgss_hash)
1412  ereport(ERROR,
1413  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1414  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1415 
1416  /* check to see if caller supports us returning a tuplestore */
1417  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1418  ereport(ERROR,
1419  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1420  errmsg("set-valued function called in context that cannot accept a set")));
1421  if (!(rsinfo->allowedModes & SFRM_Materialize))
1422  ereport(ERROR,
1423  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1424  errmsg("materialize mode required, but it is not " \
1425  "allowed in this context")));
1426 
1427  /* Switch into long-lived context to construct returned data structures */
1428  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1429  oldcontext = MemoryContextSwitchTo(per_query_ctx);
1430 
1431  /* Build a tuple descriptor for our result type */
1432  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1433  elog(ERROR, "return type must be a row type");
1434 
1435  /*
1436  * Check we have the expected number of output arguments. Aside from
1437  * being a good safety check, we need a kluge here to detect API version
1438  * 1.1, which was wedged into the code in an ill-considered way.
1439  */
1440  switch (tupdesc->natts)
1441  {
1443  if (api_version != PGSS_V1_0)
1444  elog(ERROR, "incorrect number of output arguments");
1445  break;
1447  /* pg_stat_statements() should have told us 1.0 */
1448  if (api_version != PGSS_V1_0)
1449  elog(ERROR, "incorrect number of output arguments");
1450  api_version = PGSS_V1_1;
1451  break;
1453  if (api_version != PGSS_V1_2)
1454  elog(ERROR, "incorrect number of output arguments");
1455  break;
1457  if (api_version != PGSS_V1_3)
1458  elog(ERROR, "incorrect number of output arguments");
1459  break;
1460  default:
1461  elog(ERROR, "incorrect number of output arguments");
1462  }
1463 
1464  tupstore = tuplestore_begin_heap(true, false, work_mem);
1465  rsinfo->returnMode = SFRM_Materialize;
1466  rsinfo->setResult = tupstore;
1467  rsinfo->setDesc = tupdesc;
1468 
1469  MemoryContextSwitchTo(oldcontext);
1470 
1471  /*
1472  * We'd like to load the query text file (if needed) while not holding any
1473  * lock on pgss->lock. In the worst case we'll have to do this again
1474  * after we have the lock, but it's unlikely enough to make this a win
1475  * despite occasional duplicated work. We need to reload if anybody
1476  * writes to the file (either a retail qtext_store(), or a garbage
1477  * collection) between this point and where we've gotten shared lock. If
1478  * a qtext_store is actually in progress when we look, we might as well
1479  * skip the speculative load entirely.
1480  */
1481  if (showtext)
1482  {
1483  int n_writers;
1484 
1485  /* Take the mutex so we can examine variables */
1486  {
1487  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1488 
1489  SpinLockAcquire(&s->mutex);
1490  extent = s->extent;
1491  n_writers = s->n_writers;
1492  gc_count = s->gc_count;
1493  SpinLockRelease(&s->mutex);
1494  }
1495 
1496  /* No point in loading file now if there are active writers */
1497  if (n_writers == 0)
1498  qbuffer = qtext_load_file(&qbuffer_size);
1499  }
1500 
1501  /*
1502  * Get shared lock, load or reload the query text file if we must, and
1503  * iterate over the hashtable entries.
1504  *
1505  * With a large hash table, we might be holding the lock rather longer
1506  * than one could wish. However, this only blocks creation of new hash
1507  * table entries, and the larger the hash table the less likely that is to
1508  * be needed. So we can hope this is okay. Perhaps someday we'll decide
1509  * we need to partition the hash table to limit the time spent holding any
1510  * one lock.
1511  */
1513 
1514  if (showtext)
1515  {
1516  /*
1517  * Here it is safe to examine extent and gc_count without taking the
1518  * mutex. Note that although other processes might change
1519  * pgss->extent just after we look at it, the strings they then write
1520  * into the file cannot yet be referenced in the hashtable, so we
1521  * don't care whether we see them or not.
1522  *
1523  * If qtext_load_file fails, we just press on; we'll return NULL for
1524  * every query text.
1525  */
1526  if (qbuffer == NULL ||
1527  pgss->extent != extent ||
1528  pgss->gc_count != gc_count)
1529  {
1530  if (qbuffer)
1531  free(qbuffer);
1532  qbuffer = qtext_load_file(&qbuffer_size);
1533  }
1534  }
1535 
1536  hash_seq_init(&hash_seq, pgss_hash);
1537  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1538  {
1540  bool nulls[PG_STAT_STATEMENTS_COLS];
1541  int i = 0;
1542  Counters tmp;
1543  double stddev;
1544  int64 queryid = entry->key.queryid;
1545 
1546  memset(values, 0, sizeof(values));
1547  memset(nulls, 0, sizeof(nulls));
1548 
1549  values[i++] = ObjectIdGetDatum(entry->key.userid);
1550  values[i++] = ObjectIdGetDatum(entry->key.dbid);
1551 
1552  if (is_allowed_role || entry->key.userid == userid)
1553  {
1554  if (api_version >= PGSS_V1_2)
1555  values[i++] = Int64GetDatumFast(queryid);
1556 
1557  if (showtext)
1558  {
1559  char *qstr = qtext_fetch(entry->query_offset,
1560  entry->query_len,
1561  qbuffer,
1562  qbuffer_size);
1563 
1564  if (qstr)
1565  {
1566  char *enc;
1567 
1568  enc = pg_any_to_server(qstr,
1569  entry->query_len,
1570  entry->encoding);
1571 
1572  values[i++] = CStringGetTextDatum(enc);
1573 
1574  if (enc != qstr)
1575  pfree(enc);
1576  }
1577  else
1578  {
1579  /* Just return a null if we fail to find the text */
1580  nulls[i++] = true;
1581  }
1582  }
1583  else
1584  {
1585  /* Query text not requested */
1586  nulls[i++] = true;
1587  }
1588  }
1589  else
1590  {
1591  /* Don't show queryid */
1592  if (api_version >= PGSS_V1_2)
1593  nulls[i++] = true;
1594 
1595  /*
1596  * Don't show query text, but hint as to the reason for not doing
1597  * so if it was requested
1598  */
1599  if (showtext)
1600  values[i++] = CStringGetTextDatum("<insufficient privilege>");
1601  else
1602  nulls[i++] = true;
1603  }
1604 
1605  /* copy counters to a local variable to keep locking time short */
1606  {
1607  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1608 
1609  SpinLockAcquire(&e->mutex);
1610  tmp = e->counters;
1611  SpinLockRelease(&e->mutex);
1612  }
1613 
1614  /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */
1615  if (tmp.calls == 0)
1616  continue;
1617 
1618  values[i++] = Int64GetDatumFast(tmp.calls);
1619  values[i++] = Float8GetDatumFast(tmp.total_time);
1620  if (api_version >= PGSS_V1_3)
1621  {
1622  values[i++] = Float8GetDatumFast(tmp.min_time);
1623  values[i++] = Float8GetDatumFast(tmp.max_time);
1624  values[i++] = Float8GetDatumFast(tmp.mean_time);
1625 
1626  /*
1627  * Note we are calculating the population variance here, not the
1628  * sample variance, as we have data for the whole population, so
1629  * Bessel's correction is not used, and we don't divide by
1630  * tmp.calls - 1.
1631  */
1632  if (tmp.calls > 1)
1633  stddev = sqrt(tmp.sum_var_time / tmp.calls);
1634  else
1635  stddev = 0.0;
1636  values[i++] = Float8GetDatumFast(stddev);
1637  }
1638  values[i++] = Int64GetDatumFast(tmp.rows);
1639  values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
1640  values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
1641  if (api_version >= PGSS_V1_1)
1642  values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied);
1643  values[i++] = Int64GetDatumFast(tmp.shared_blks_written);
1644  values[i++] = Int64GetDatumFast(tmp.local_blks_hit);
1645  values[i++] = Int64GetDatumFast(tmp.local_blks_read);
1646  if (api_version >= PGSS_V1_1)
1647  values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied);
1648  values[i++] = Int64GetDatumFast(tmp.local_blks_written);
1649  values[i++] = Int64GetDatumFast(tmp.temp_blks_read);
1650  values[i++] = Int64GetDatumFast(tmp.temp_blks_written);
1651  if (api_version >= PGSS_V1_1)
1652  {
1653  values[i++] = Float8GetDatumFast(tmp.blk_read_time);
1654  values[i++] = Float8GetDatumFast(tmp.blk_write_time);
1655  }
1656 
1657  Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
1658  api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
1659  api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
1660  api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
1661  -1 /* fail if you forget to update this assert */ ));
1662 
1663  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
1664  }
1665 
1666  /* clean up and return the tuplestore */
1668 
1669  if (qbuffer)
1670  free(qbuffer);
1671 
1672  tuplestore_donestoring(tupstore);
1673 }
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
#define PG_STAT_STATEMENTS_COLS_V1_3
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:196
#define PG_STAT_STATEMENTS_COLS_V1_0
int64 shared_blks_read
int64 local_blks_written
Oid GetUserId(void)
Definition: miscinit.c:380
int64 shared_blks_dirtied
#define tuplestore_donestoring(state)
Definition: tuplestore.h:60
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:570
Counters counters
int64 temp_blks_read
#define PG_STAT_STATEMENTS_COLS_V1_1
unsigned int Oid
Definition: postgres_ext.h:31
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1056
int64 shared_blks_hit
static HTAB * pgss_hash
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
struct pg_encoding enc
Definition: encode.c:522
fmNodePtr resultinfo
Definition: fmgr.h:89
static char * qtext_load_file(Size *buffer_size)
int64 temp_blks_written
int64 shared_blks_written
#define ereport(elevel, rest)
Definition: elog.h:141
#define SpinLockRelease(lock)
Definition: spin.h:64
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
int64 local_blks_read
uintptr_t Datum
Definition: postgres.h:367
double sum_var_time
#define Int64GetDatumFast(X)
Definition: postgres.h:775
int work_mem
Definition: globals.c:121
pgssHashKey key
int allowedModes
Definition: execnodes.h:303
#define PG_STAT_STATEMENTS_COLS
#define free(a)
Definition: header.h:65
bool is_member_of_role(Oid member, Oid role)
Definition: acl.c:4932
#define Float8GetDatumFast(X)
Definition: postgres.h:776
SetFunctionReturnMode returnMode
Definition: execnodes.h:305
double blk_read_time
#define Assert(condition)
Definition: c.h:732
int64 local_blks_dirtied
int64 local_blks_hit
size_t Size
Definition: c.h:466
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
double blk_write_time
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1389
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:231
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1379
Tuplestorestate * setResult
Definition: execnodes.h:308
static Datum values[MAXATTR]
Definition: bootstrap.c:167
ExprContext * econtext
Definition: execnodes.h:301
e
Definition: preproc-init.c:82
TupleDesc setDesc
Definition: execnodes.h:309
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
int i
#define CStringGetTextDatum(s)
Definition: builtins.h:83
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:581
#define PG_STAT_STATEMENTS_COLS_V1_2
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)

◆ pg_stat_statements_reset()

Datum pg_stat_statements_reset ( PG_FUNCTION_ARGS  )

Definition at line 1330 of file pg_stat_statements.c.

References entry_reset(), and PG_RETURN_VOID.

1331 {
1332  entry_reset(0, 0, 0);
1333 
1334  PG_RETURN_VOID();
1335 }
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)
#define PG_RETURN_VOID()
Definition: fmgr.h:339

◆ pg_stat_statements_reset_1_7()

Datum pg_stat_statements_reset_1_7 ( PG_FUNCTION_ARGS  )

Definition at line 1311 of file pg_stat_statements.c.

References pgssHashKey::dbid, entry_reset(), PG_GETARG_INT64, PG_GETARG_OID, PG_RETURN_VOID, pgssHashKey::queryid, and pgssHashKey::userid.

1312 {
1313  Oid userid;
1314  Oid dbid;
1315  uint64 queryid;
1316 
1317  userid = PG_GETARG_OID(0);
1318  dbid = PG_GETARG_OID(1);
1319  queryid = (uint64) PG_GETARG_INT64(2);
1320 
1321  entry_reset(userid, dbid, queryid);
1322 
1323  PG_RETURN_VOID();
1324 }
unsigned int Oid
Definition: postgres_ext.h:31
#define PG_GETARG_OID(n)
Definition: fmgr.h:270
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)
#define PG_RETURN_VOID()
Definition: fmgr.h:339
#define PG_GETARG_INT64(n)
Definition: fmgr.h:277

◆ pgss_ExecutorEnd()

static void pgss_ExecutorEnd ( QueryDesc queryDesc)
static

Definition at line 932 of file pg_stat_statements.c.

References Instrumentation::bufusage, EState::es_processed, QueryDesc::estate, InstrEndLoop(), pgss_enabled, pgss_store(), QueryDesc::plannedstmt, prev_ExecutorEnd, PlannedStmt::queryId, QueryDesc::sourceText, standard_ExecutorEnd(), PlannedStmt::stmt_len, PlannedStmt::stmt_location, Instrumentation::total, and QueryDesc::totaltime.

Referenced by _PG_init().

933 {
934  uint64 queryId = queryDesc->plannedstmt->queryId;
935 
936  if (queryId != UINT64CONST(0) && queryDesc->totaltime && pgss_enabled())
937  {
938  /*
939  * Make sure stats accumulation is done. (Note: it's okay if several
940  * levels of hook all do this.)
941  */
942  InstrEndLoop(queryDesc->totaltime);
943 
944  pgss_store(queryDesc->sourceText,
945  queryId,
946  queryDesc->plannedstmt->stmt_location,
947  queryDesc->plannedstmt->stmt_len,
948  queryDesc->totaltime->total * 1000.0, /* convert to msec */
949  queryDesc->estate->es_processed,
950  &queryDesc->totaltime->bufusage,
951  NULL);
952  }
953 
954  if (prev_ExecutorEnd)
955  prev_ExecutorEnd(queryDesc);
956  else
957  standard_ExecutorEnd(queryDesc);
958 }
EState * estate
Definition: execdesc.h:48
static ExecutorEnd_hook_type prev_ExecutorEnd
void standard_ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:471
#define pgss_enabled()
int stmt_len
Definition: plannodes.h:94
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:110
int stmt_location
Definition: plannodes.h:93
BufferUsage bufusage
Definition: instrument.h:64
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 es_processed
Definition: execnodes.h:554
const char * sourceText
Definition: execdesc.h:38
uint64 queryId
Definition: plannodes.h:48
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, double total_time, uint64 rows, const BufferUsage *bufusage, pgssJumbleState *jstate)
PlannedStmt * plannedstmt
Definition: execdesc.h:37

◆ pgss_ExecutorFinish()

static void pgss_ExecutorFinish ( QueryDesc queryDesc)
static

Definition at line 909 of file pg_stat_statements.c.

References nested_level, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, prev_ExecutorFinish, and standard_ExecutorFinish().

Referenced by _PG_init().

910 {
911  nested_level++;
912  PG_TRY();
913  {
915  prev_ExecutorFinish(queryDesc);
916  else
917  standard_ExecutorFinish(queryDesc);
918  nested_level--;
919  }
920  PG_CATCH();
921  {
922  nested_level--;
923  PG_RE_THROW();
924  }
925  PG_END_TRY();
926 }
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:411
static ExecutorFinish_hook_type prev_ExecutorFinish
#define PG_CATCH()
Definition: elog.h:310
#define PG_RE_THROW()
Definition: elog.h:331
#define PG_TRY()
Definition: elog.h:301
static int nested_level
#define PG_END_TRY()
Definition: elog.h:317

◆ pgss_ExecutorRun()

static void pgss_ExecutorRun ( QueryDesc queryDesc,
ScanDirection  direction,
uint64  count,
bool  execute_once 
)
static

Definition at line 885 of file pg_stat_statements.c.

References nested_level, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, prev_ExecutorRun, and standard_ExecutorRun().

Referenced by _PG_init().

887 {
888  nested_level++;
889  PG_TRY();
890  {
891  if (prev_ExecutorRun)
892  prev_ExecutorRun(queryDesc, direction, count, execute_once);
893  else
894  standard_ExecutorRun(queryDesc, direction, count, execute_once);
895  nested_level--;
896  }
897  PG_CATCH();
898  {
899  nested_level--;
900  PG_RE_THROW();
901  }
902  PG_END_TRY();
903 }
void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:312
static ExecutorRun_hook_type prev_ExecutorRun
#define PG_CATCH()
Definition: elog.h:310
#define PG_RE_THROW()
Definition: elog.h:331
#define PG_TRY()
Definition: elog.h:301
static int nested_level
#define PG_END_TRY()
Definition: elog.h:317

◆ pgss_ExecutorStart()

static void pgss_ExecutorStart ( QueryDesc queryDesc,
int  eflags 
)
static

Definition at line 851 of file pg_stat_statements.c.

References EState::es_query_cxt, QueryDesc::estate, InstrAlloc(), INSTRUMENT_ALL, MemoryContextSwitchTo(), pgss_enabled, QueryDesc::plannedstmt, prev_ExecutorStart, PlannedStmt::queryId, standard_ExecutorStart(), and QueryDesc::totaltime.

Referenced by _PG_init().

852 {
853  if (prev_ExecutorStart)
854  prev_ExecutorStart(queryDesc, eflags);
855  else
856  standard_ExecutorStart(queryDesc, eflags);
857 
858  /*
859  * If query has queryId zero, don't track it. This prevents double
860  * counting of optimizable statements that are directly contained in
861  * utility statements.
862  */
863  if (pgss_enabled() && queryDesc->plannedstmt->queryId != UINT64CONST(0))
864  {
865  /*
866  * Set up to track total elapsed time in ExecutorRun. Make sure the
867  * space is allocated in the per-query context so it will go away at
868  * ExecutorEnd.
869  */
870  if (queryDesc->totaltime == NULL)
871  {
872  MemoryContext oldcxt;
873 
874  oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
875  queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL);
876  MemoryContextSwitchTo(oldcxt);
877  }
878  }
879 }
EState * estate
Definition: execdesc.h:48
void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:152
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Instrumentation * InstrAlloc(int n, int instrument_options)
Definition: instrument.c:30
static ExecutorStart_hook_type prev_ExecutorStart
#define pgss_enabled()
MemoryContext es_query_cxt
Definition: execnodes.h:550
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 queryId
Definition: plannodes.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37

◆ pgss_hash_string()

static uint64 pgss_hash_string ( const char *  str,
int  len 
)
static

Definition at line 1083 of file pg_stat_statements.c.

References DatumGetUInt64, and hash_any_extended().

Referenced by pgss_store().

1084 {
1085  return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
1086  len, 0));
1087 }
Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.c:374
#define DatumGetUInt64(X)
Definition: postgres.h:634

◆ pgss_memsize()

static Size pgss_memsize ( void  )
static

Definition at line 1679 of file pg_stat_statements.c.

References add_size(), hash_estimate_size(), MAXALIGN, and pgss_max.

Referenced by _PG_init().

1680 {
1681  Size size;
1682 
1683  size = MAXALIGN(sizeof(pgssSharedState));
1684  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1685 
1686  return size;
1687 }
static int pgss_max
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:732
Size add_size(Size s1, Size s2)
Definition: shmem.c:475
size_t Size
Definition: c.h:466
#define MAXALIGN(LEN)
Definition: c.h:685

◆ pgss_post_parse_analyze()

static void pgss_post_parse_analyze ( ParseState pstate,
Query query 
)
static

Definition at line 780 of file pg_stat_statements.c.

References Assert, pgssJumbleState::clocations, pgssJumbleState::clocations_buf_size, pgssJumbleState::clocations_count, DatumGetUInt64, hash_any_extended(), pgssJumbleState::highest_extern_param_id, pgssJumbleState::jumble, pgssJumbleState::jumble_len, JUMBLE_SIZE, JumbleQuery(), ParseState::p_sourcetext, palloc(), pgss_enabled, pgss_store(), prev_post_parse_analyze_hook, Query::queryId, Query::stmt_len, Query::stmt_location, and Query::utilityStmt.

Referenced by _PG_init().

781 {
782  pgssJumbleState jstate;
783 
785  prev_post_parse_analyze_hook(pstate, query);
786 
787  /* Assert we didn't do this already */
788  Assert(query->queryId == UINT64CONST(0));
789 
790  /* Safety check... */
791  if (!pgss || !pgss_hash || !pgss_enabled())
792  return;
793 
794  /*
795  * Utility statements get queryId zero. We do this even in cases where
796  * the statement contains an optimizable statement for which a queryId
797  * could be derived (such as EXPLAIN or DECLARE CURSOR). For such cases,
798  * runtime control will first go through ProcessUtility and then the
799  * executor, and we don't want the executor hooks to do anything, since we
800  * are already measuring the statement's costs at the utility level.
801  */
802  if (query->utilityStmt)
803  {
804  query->queryId = UINT64CONST(0);
805  return;
806  }
807 
808  /* Set up workspace for query jumbling */
809  jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
810  jstate.jumble_len = 0;
811  jstate.clocations_buf_size = 32;
812  jstate.clocations = (pgssLocationLen *)
813  palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
814  jstate.clocations_count = 0;
815  jstate.highest_extern_param_id = 0;
816 
817  /* Compute query ID and mark the Query node with it */
818  JumbleQuery(&jstate, query);
819  query->queryId =
821 
822  /*
823  * If we are unlucky enough to get a hash of zero, use 1 instead, to
824  * prevent confusion with the utility-statement case.
825  */
826  if (query->queryId == UINT64CONST(0))
827  query->queryId = UINT64CONST(1);
828 
829  /*
830  * If we were able to identify any ignorable constants, we immediately
831  * create a hash table entry for the query, so that we can record the
832  * normalized form of the query string. If there were no such constants,
833  * the normalized string would be the same as the query text anyway, so
834  * there's no need for an early entry.
835  */
836  if (jstate.clocations_count > 0)
837  pgss_store(pstate->p_sourcetext,
838  query->queryId,
839  query->stmt_location,
840  query->stmt_len,
841  0,
842  0,
843  NULL,
844  &jstate);
845 }
int stmt_location
Definition: parsenodes.h:180
Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.c:374
#define pgss_enabled()
unsigned char * jumble
#define JUMBLE_SIZE
Node * utilityStmt
Definition: parsenodes.h:120
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
static pgssSharedState * pgss
pgssLocationLen * clocations
static HTAB * pgss_hash
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
const char * p_sourcetext
Definition: parse_node.h:176
uint64 queryId
Definition: parsenodes.h:116
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:732
void * palloc(Size size)
Definition: mcxt.c:949
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, double total_time, uint64 rows, const BufferUsage *bufusage, pgssJumbleState *jstate)
int stmt_len
Definition: parsenodes.h:181

◆ pgss_ProcessUtility()

static void pgss_ProcessUtility ( PlannedStmt pstmt,
const char *  queryString,
ProcessUtilityContext  context,
ParamListInfo  params,
QueryEnvironment queryEnv,
DestReceiver dest,
char *  completionTag 
)
static

Definition at line 964 of file pg_stat_statements.c.

References BufferUsage::blk_read_time, BufferUsage::blk_write_time, duration, INSTR_TIME_GET_MILLISEC, INSTR_TIME_SET_CURRENT, INSTR_TIME_SUBTRACT, IsA, BufferUsage::local_blks_dirtied, BufferUsage::local_blks_hit, BufferUsage::local_blks_read, BufferUsage::local_blks_written, nested_level, PG_CATCH, PG_END_TRY, PG_RE_THROW, pg_strtouint64(), PG_TRY, pgBufferUsage, pgss_enabled, pgss_store(), pgss_track_utility, prev_ProcessUtility, BufferUsage::shared_blks_dirtied, BufferUsage::shared_blks_hit, BufferUsage::shared_blks_read, BufferUsage::shared_blks_written, standard_ProcessUtility(), PlannedStmt::stmt_len, PlannedStmt::stmt_location, BufferUsage::temp_blks_read, BufferUsage::temp_blks_written, and PlannedStmt::utilityStmt.

Referenced by _PG_init().

968 {
969  Node *parsetree = pstmt->utilityStmt;
970 
971  /*
972  * If it's an EXECUTE statement, we don't track it and don't increment the
973  * nesting level. This allows the cycles to be charged to the underlying
974  * PREPARE instead (by the Executor hooks), which is much more useful.
975  *
976  * We also don't track execution of PREPARE. If we did, we would get one
977  * hash table entry for the PREPARE (with hash calculated from the query
978  * string), and then a different one with the same query string (but hash
979  * calculated from the query tree) would be used to accumulate costs of
980  * ensuing EXECUTEs. This would be confusing, and inconsistent with other
981  * cases where planning time is not included at all.
982  *
983  * Likewise, we don't track execution of DEALLOCATE.
984  */
985  if (pgss_track_utility && pgss_enabled() &&
986  !IsA(parsetree, ExecuteStmt) &&
987  !IsA(parsetree, PrepareStmt) &&
988  !IsA(parsetree, DeallocateStmt))
989  {
990  instr_time start;
992  uint64 rows;
993  BufferUsage bufusage_start,
994  bufusage;
995 
996  bufusage_start = pgBufferUsage;
997  INSTR_TIME_SET_CURRENT(start);
998 
999  nested_level++;
1000  PG_TRY();
1001  {
1002  if (prev_ProcessUtility)
1003  prev_ProcessUtility(pstmt, queryString,
1004  context, params, queryEnv,
1005  dest, completionTag);
1006  else
1007  standard_ProcessUtility(pstmt, queryString,
1008  context, params, queryEnv,
1009  dest, completionTag);
1010  nested_level--;
1011  }
1012  PG_CATCH();
1013  {
1014  nested_level--;
1015  PG_RE_THROW();
1016  }
1017  PG_END_TRY();
1018 
1019  INSTR_TIME_SET_CURRENT(duration);
1020  INSTR_TIME_SUBTRACT(duration, start);
1021 
1022  /* parse command tag to retrieve the number of affected rows. */
1023  if (completionTag &&
1024  strncmp(completionTag, "COPY ", 5) == 0)
1025  rows = pg_strtouint64(completionTag + 5, NULL, 10);
1026  else
1027  rows = 0;
1028 
1029  /* calc differences of buffer counters. */
1030  bufusage.shared_blks_hit =
1032  bufusage.shared_blks_read =
1034  bufusage.shared_blks_dirtied =
1036  bufusage.shared_blks_written =
1038  bufusage.local_blks_hit =
1039  pgBufferUsage.local_blks_hit - bufusage_start.local_blks_hit;
1040  bufusage.local_blks_read =
1042  bufusage.local_blks_dirtied =
1044  bufusage.local_blks_written =
1046  bufusage.temp_blks_read =
1047  pgBufferUsage.temp_blks_read - bufusage_start.temp_blks_read;
1048  bufusage.temp_blks_written =
1051  INSTR_TIME_SUBTRACT(bufusage.blk_read_time, bufusage_start.blk_read_time);
1053  INSTR_TIME_SUBTRACT(bufusage.blk_write_time, bufusage_start.blk_write_time);
1054 
1055  pgss_store(queryString,
1056  0, /* signal that it's a utility stmt */
1057  pstmt->stmt_location,
1058  pstmt->stmt_len,
1059  INSTR_TIME_GET_MILLISEC(duration),
1060  rows,
1061  &bufusage,
1062  NULL);
1063  }
1064  else
1065  {
1066  if (prev_ProcessUtility)
1067  prev_ProcessUtility(pstmt, queryString,
1068  context, params, queryEnv,
1069  dest, completionTag);
1070  else
1071  standard_ProcessUtility(pstmt, queryString,
1072  context, params, queryEnv,
1073  dest, completionTag);
1074  }
1075 }
long local_blks_hit
Definition: instrument.h:25
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
long local_blks_dirtied
Definition: instrument.h:27
long local_blks_read
Definition: instrument.h:26
instr_time blk_read_time
Definition: instrument.h:31
static ProcessUtility_hook_type prev_ProcessUtility
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
struct timeval instr_time
Definition: instr_time.h:150
long shared_blks_read
Definition: instrument.h:22
Definition: nodes.h:525
#define pgss_enabled()
long temp_blks_written
Definition: instrument.h:30
int stmt_len
Definition: plannodes.h:94
int duration
Definition: pgbench.c:144
long shared_blks_written
Definition: instrument.h:24
void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, char *completionTag)
Definition: utility.c:377
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
int stmt_location
Definition: plannodes.h:93
Node * utilityStmt
Definition: plannodes.h:90
long shared_blks_dirtied
Definition: instrument.h:23
long temp_blks_read
Definition: instrument.h:29
uint64 pg_strtouint64(const char *str, char **endptr, int base)
Definition: numutils.c:558
#define PG_CATCH()
Definition: elog.h:310
static bool pgss_track_utility
instr_time blk_write_time
Definition: instrument.h:32
#define PG_RE_THROW()
Definition: elog.h:331
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, double total_time, uint64 rows, const BufferUsage *bufusage, pgssJumbleState *jstate)
long shared_blks_hit
Definition: instrument.h:21
long local_blks_written
Definition: instrument.h:28
#define PG_TRY()
Definition: elog.h:301
static int nested_level
#define PG_END_TRY()
Definition: elog.h:317
BufferUsage pgBufferUsage
Definition: instrument.c:20

◆ pgss_shmem_shutdown()

static void pgss_shmem_shutdown ( int  code,
Datum  arg 
)
static

Definition at line 684 of file pg_stat_statements.c.

References AllocateFile(), durable_rename(), ereport, errcode_for_file_access(), errmsg(), error(), free, FreeFile(), hash_get_num_entries(), hash_seq_init(), hash_seq_search(), hash_seq_term(), LOG, PG_BINARY_W, PGSS_DUMP_FILE, PGSS_FILE_HEADER, PGSS_PG_MAJOR_VERSION, pgss_save, PGSS_TEXT_FILE, qtext_fetch(), qtext_load_file(), pgssEntry::query_len, and pgssEntry::query_offset.

Referenced by pgss_shmem_startup().

685 {
686  FILE *file;
687  char *qbuffer = NULL;
688  Size qbuffer_size = 0;
689  HASH_SEQ_STATUS hash_seq;
690  int32 num_entries;
691  pgssEntry *entry;
692 
693  /* Don't try to dump during a crash. */
694  if (code)
695  return;
696 
697  /* Safety check ... shouldn't get here unless shmem is set up. */
698  if (!pgss || !pgss_hash)
699  return;
700 
701  /* Don't dump if told not to. */
702  if (!pgss_save)
703  return;
704 
705  file = AllocateFile(PGSS_DUMP_FILE ".tmp", PG_BINARY_W);
706  if (file == NULL)
707  goto error;
708 
709  if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1)
710  goto error;
711  if (fwrite(&PGSS_PG_MAJOR_VERSION, sizeof(uint32), 1, file) != 1)
712  goto error;
713  num_entries = hash_get_num_entries(pgss_hash);
714  if (fwrite(&num_entries, sizeof(int32), 1, file) != 1)
715  goto error;
716 
717  qbuffer = qtext_load_file(&qbuffer_size);
718  if (qbuffer == NULL)
719  goto error;
720 
721  /*
722  * When serializing to disk, we store query texts immediately after their
723  * entry data. Any orphaned query texts are thereby excluded.
724  */
725  hash_seq_init(&hash_seq, pgss_hash);
726  while ((entry = hash_seq_search(&hash_seq)) != NULL)
727  {
728  int len = entry->query_len;
729  char *qstr = qtext_fetch(entry->query_offset, len,
730  qbuffer, qbuffer_size);
731 
732  if (qstr == NULL)
733  continue; /* Ignore any entries with bogus texts */
734 
735  if (fwrite(entry, sizeof(pgssEntry), 1, file) != 1 ||
736  fwrite(qstr, 1, len + 1, file) != len + 1)
737  {
738  /* note: we assume hash_seq_term won't change errno */
739  hash_seq_term(&hash_seq);
740  goto error;
741  }
742  }
743 
744  free(qbuffer);
745  qbuffer = NULL;
746 
747  if (FreeFile(file))
748  {
749  file = NULL;
750  goto error;
751  }
752 
753  /*
754  * Rename file into place, so we atomically replace any old one.
755  */
757 
758  /* Unlink query-texts file; it's not needed while shutdown */
759  unlink(PGSS_TEXT_FILE);
760 
761  return;
762 
763 error:
764  ereport(LOG,
766  errmsg("could not write file \"%s\": %m",
767  PGSS_DUMP_FILE ".tmp")));
768  if (qbuffer)
769  free(qbuffer);
770  if (file)
771  FreeFile(file);
772  unlink(PGSS_DUMP_FILE ".tmp");
773  unlink(PGSS_TEXT_FILE);
774 }
static void error(void)
Definition: sql-dyntest.c:147
static const uint32 PGSS_PG_MAJOR_VERSION
#define PGSS_DUMP_FILE
#define PG_BINARY_W
Definition: c.h:1194
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1335
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:346
static pgssSharedState * pgss
static HTAB * pgss_hash
int errcode_for_file_access(void)
Definition: elog.c:593
static char * qtext_load_file(Size *buffer_size)
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2205
unsigned int uint32
Definition: c.h:358
#define ereport(elevel, rest)
Definition: elog.h:141
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:606
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:466
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1389
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1379
int FreeFile(FILE *file)
Definition: fd.c:2404
int errmsg(const char *fmt,...)
Definition: elog.c:784
static const uint32 PGSS_FILE_HEADER
#define PGSS_TEXT_FILE
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1465
static bool pgss_save
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)

◆ pgss_shmem_startup()

static void pgss_shmem_startup ( void  )
static

Definition at line 465 of file pg_stat_statements.c.

References AllocateFile(), ASSUMED_LENGTH_INIT, ASSUMED_MEDIAN_INIT, Counters::calls, pgssEntry::counters, pgssSharedState::cur_median_usage, pgssEntry::encoding, entry_alloc(), HASHCTL::entrysize, ereport, errcode(), errcode_for_file_access(), errmsg(), pgssSharedState::extent, FreeFile(), pgssSharedState::gc_count, GetNamedLWLockTranche(), HASH_BLOBS, HASH_ELEM, header(), i, IsUnderPostmaster, pgssEntry::key, HASHCTL::keysize, pgssSharedState::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), Max, pgssSharedState::mean_query_len, pgssSharedState::mutex, pgssSharedState::n_writers, on_shmem_exit(), palloc(), pfree(), PG_BINARY_R, PG_BINARY_W, PG_VALID_BE_ENCODING, PGSS_DUMP_FILE, PGSS_FILE_HEADER, pgss_max, PGSS_PG_MAJOR_VERSION, pgss_save, pgss_shmem_shutdown(), PGSS_TEXT_FILE, prev_shmem_startup_hook, pgssEntry::query_len, repalloc(), ShmemInitHash(), ShmemInitStruct(), and SpinLockInit.

Referenced by _PG_init().

466 {
467  bool found;
468  HASHCTL info;
469  FILE *file = NULL;
470  FILE *qfile = NULL;
471  uint32 header;
472  int32 num;
473  int32 pgver;
474  int32 i;
475  int buffer_size;
476  char *buffer = NULL;
477 
480 
481  /* reset in case this is a restart within the postmaster */
482  pgss = NULL;
483  pgss_hash = NULL;
484 
485  /*
486  * Create or attach to the shared memory state, including hash table
487  */
488  LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
489 
490  pgss = ShmemInitStruct("pg_stat_statements",
491  sizeof(pgssSharedState),
492  &found);
493 
494  if (!found)
495  {
496  /* First time through ... */
497  pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock;
501  pgss->extent = 0;
502  pgss->n_writers = 0;
503  pgss->gc_count = 0;
504  }
505 
506  memset(&info, 0, sizeof(info));
507  info.keysize = sizeof(pgssHashKey);
508  info.entrysize = sizeof(pgssEntry);
509  pgss_hash = ShmemInitHash("pg_stat_statements hash",
511  &info,
513 
514  LWLockRelease(AddinShmemInitLock);
515 
516  /*
517  * If we're in the postmaster (or a standalone backend...), set up a shmem
518  * exit hook to dump the statistics to disk.
519  */
520  if (!IsUnderPostmaster)
522 
523  /*
524  * Done if some other process already completed our initialization.
525  */
526  if (found)
527  return;
528 
529  /*
530  * Note: we don't bother with locks here, because there should be no other
531  * processes running when this code is reached.
532  */
533 
534  /* Unlink query text file possibly left over from crash */
535  unlink(PGSS_TEXT_FILE);
536 
537  /* Allocate new query text temp file */
539  if (qfile == NULL)
540  goto write_error;
541 
542  /*
543  * If we were told not to load old statistics, we're done. (Note we do
544  * not try to unlink any old dump file in this case. This seems a bit
545  * questionable but it's the historical behavior.)
546  */
547  if (!pgss_save)
548  {
549  FreeFile(qfile);
550  return;
551  }
552 
553  /*
554  * Attempt to load old statistics from the dump file.
555  */
557  if (file == NULL)
558  {
559  if (errno != ENOENT)
560  goto read_error;
561  /* No existing persisted stats file, so we're done */
562  FreeFile(qfile);
563  return;
564  }
565 
566  buffer_size = 2048;
567  buffer = (char *) palloc(buffer_size);
568 
569  if (fread(&header, sizeof(uint32), 1, file) != 1 ||
570  fread(&pgver, sizeof(uint32), 1, file) != 1 ||
571  fread(&num, sizeof(int32), 1, file) != 1)
572  goto read_error;
573 
574  if (header != PGSS_FILE_HEADER ||
575  pgver != PGSS_PG_MAJOR_VERSION)
576  goto data_error;
577 
578  for (i = 0; i < num; i++)
579  {
580  pgssEntry temp;
581  pgssEntry *entry;
582  Size query_offset;
583 
584  if (fread(&temp, sizeof(pgssEntry), 1, file) != 1)
585  goto read_error;
586 
587  /* Encoding is the only field we can easily sanity-check */
588  if (!PG_VALID_BE_ENCODING(temp.encoding))
589  goto data_error;
590 
591  /* Resize buffer as needed */
592  if (temp.query_len >= buffer_size)
593  {
594  buffer_size = Max(buffer_size * 2, temp.query_len + 1);
595  buffer = repalloc(buffer, buffer_size);
596  }
597 
598  if (fread(buffer, 1, temp.query_len + 1, file) != temp.query_len + 1)
599  goto read_error;
600 
601  /* Should have a trailing null, but let's make sure */
602  buffer[temp.query_len] = '\0';
603 
604  /* Skip loading "sticky" entries */
605  if (temp.counters.calls == 0)
606  continue;
607 
608  /* Store the query text */
609  query_offset = pgss->extent;
610  if (fwrite(buffer, 1, temp.query_len + 1, qfile) != temp.query_len + 1)
611  goto write_error;
612  pgss->extent += temp.query_len + 1;
613 
614  /* make the hashtable entry (discards old entries if too many) */
615  entry = entry_alloc(&temp.key, query_offset, temp.query_len,
616  temp.encoding,
617  false);
618 
619  /* copy in the actual stats */
620  entry->counters = temp.counters;
621  }
622 
623  pfree(buffer);
624  FreeFile(file);
625  FreeFile(qfile);
626 
627  /*
628  * Remove the persisted stats file so it's not included in
629  * backups/replication standbys, etc. A new file will be written on next
630  * shutdown.
631  *
632  * Note: it's okay if the PGSS_TEXT_FILE is included in a basebackup,
633  * because we remove that file on startup; it acts inversely to
634  * PGSS_DUMP_FILE, in that it is only supposed to be around when the
635  * server is running, whereas PGSS_DUMP_FILE is only supposed to be around
636  * when the server is not running. Leaving the file creates no danger of
637  * a newly restored database having a spurious record of execution costs,
638  * which is what we're really concerned about here.
639  */
640  unlink(PGSS_DUMP_FILE);
641 
642  return;
643 
644 read_error:
645  ereport(LOG,
647  errmsg("could not read file \"%s\": %m",
648  PGSS_DUMP_FILE)));
649  goto fail;
650 data_error:
651  ereport(LOG,
652  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
653  errmsg("ignoring invalid data in file \"%s\"",
654  PGSS_DUMP_FILE)));
655  goto fail;
656 write_error:
657  ereport(LOG,
659  errmsg("could not write file \"%s\": %m",
660  PGSS_TEXT_FILE)));
661 fail:
662  if (buffer)
663  pfree(buffer);
664  if (file)
665  FreeFile(file);
666  if (qfile)
667  FreeFile(qfile);
668  /* If possible, throw away the bogus file; ignore any error */
669  unlink(PGSS_DUMP_FILE);
670 
671  /*
672  * Don't unlink PGSS_TEXT_FILE here; it should always be around while the
673  * server is running with pg_stat_statements enabled
674  */
675 }
#define HASH_ELEM
Definition: hsearch.h:87
static const uint32 PGSS_PG_MAJOR_VERSION
#define SpinLockInit(lock)
Definition: spin.h:60
Size entrysize
Definition: hsearch.h:73
#define PGSS_DUMP_FILE
int errcode(int sqlerrcode)
Definition: elog.c:570
struct pgssEntry pgssEntry
#define PG_BINARY_W
Definition: c.h:1194
Counters counters
#define LOG
Definition: elog.h:26
#define PG_BINARY_R
Definition: c.h:1193
signed int int32
Definition: c.h:346
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
void pfree(void *pointer)
Definition: mcxt.c:1056
static int pgss_max
static HTAB * pgss_hash
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:372
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:361
bool IsUnderPostmaster
Definition: globals.c:109
static pgssEntry * entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
int errcode_for_file_access(void)
Definition: elog.c:593
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2205
unsigned int uint32
Definition: c.h:358
#define ereport(elevel, rest)
Definition: elog.h:141
#define HASH_BLOBS
Definition: hsearch.h:88
uintptr_t Datum
Definition: postgres.h:367
static void pgss_shmem_shutdown(int code, Datum arg)
Size keysize
Definition: hsearch.h:72
pgssHashKey key
#define Max(x, y)
Definition: c.h:898
#define PG_VALID_BE_ENCODING(_enc)
Definition: pg_wchar.h:295
LWLockPadded * GetNamedLWLockTranche(const char *tranche_name)
Definition: lwlock.c:552
size_t Size
Definition: c.h:466
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
static void header(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:210
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
static shmem_startup_hook_type prev_shmem_startup_hook
int FreeFile(FILE *file)
Definition: fd.c:2404
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:784
int i
static const uint32 PGSS_FILE_HEADER
HTAB * ShmemInitHash(const char *name, long init_size, long max_size, HASHCTL *infoP, int hash_flags)
Definition: shmem.c:317
struct pgssHashKey pgssHashKey
#define PGSS_TEXT_FILE
#define ASSUMED_MEDIAN_INIT
static bool pgss_save

◆ pgss_store()

static void pgss_store ( const char *  query,
uint64  queryId,
int  query_location,
int  query_len,
double  total_time,
uint64  rows,
const BufferUsage bufusage,
pgssJumbleState jstate 
)
static

Definition at line 1100 of file pg_stat_statements.c.

References Assert, BufferUsage::blk_read_time, Counters::blk_read_time, BufferUsage::blk_write_time, Counters::blk_write_time, Counters::calls, pgssEntry::counters, pgssHashKey::dbid, encoding, entry_alloc(), pgssSharedState::gc_count, gc_qtexts(), generate_normalized_query(), GetDatabaseEncoding(), GetUserId(), HASH_FIND, hash_search(), INSTR_TIME_GET_MILLISEC, sort-test::key, BufferUsage::local_blks_dirtied, Counters::local_blks_dirtied, BufferUsage::local_blks_hit, Counters::local_blks_hit, BufferUsage::local_blks_read, Counters::local_blks_read, BufferUsage::local_blks_written, Counters::local_blks_written, pgssSharedState::lock, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), Counters::max_time, Counters::mean_time, Counters::min_time, pgssEntry::mutex, MyDatabaseId, need_gc_qtexts(), pfree(), pgss_hash_string(), qtext_store(), pgssHashKey::queryid, Counters::rows, scanner_isspace(), BufferUsage::shared_blks_dirtied, Counters::shared_blks_dirtied, BufferUsage::shared_blks_hit, Counters::shared_blks_hit, BufferUsage::shared_blks_read, Counters::shared_blks_read, BufferUsage::shared_blks_written, Counters::shared_blks_written, SpinLockAcquire, SpinLockRelease, Counters::sum_var_time, BufferUsage::temp_blks_read, Counters::temp_blks_read, BufferUsage::temp_blks_written, Counters::temp_blks_written, Counters::total_time, Counters::usage, USAGE_EXEC, USAGE_INIT, and pgssHashKey::userid.

Referenced by pgss_ExecutorEnd(), pgss_post_parse_analyze(), and pgss_ProcessUtility().

1105 {
1106  pgssHashKey key;
1107  pgssEntry *entry;
1108  char *norm_query = NULL;
1109  int encoding = GetDatabaseEncoding();
1110 
1111  Assert(query != NULL);
1112 
1113  /* Safety check... */
1114  if (!pgss || !pgss_hash)
1115  return;
1116 
1117  /*
1118  * Confine our attention to the relevant part of the string, if the query
1119  * is a portion of a multi-statement source string.
1120  *
1121  * First apply starting offset, unless it's -1 (unknown).
1122  */
1123  if (query_location >= 0)
1124  {
1125  Assert(query_location <= strlen(query));
1126  query += query_location;
1127  /* Length of 0 (or -1) means "rest of string" */
1128  if (query_len <= 0)
1129  query_len = strlen(query);
1130  else
1131  Assert(query_len <= strlen(query));
1132  }
1133  else
1134  {
1135  /* If query location is unknown, distrust query_len as well */
1136  query_location = 0;
1137  query_len = strlen(query);
1138  }
1139 
1140  /*
1141  * Discard leading and trailing whitespace, too. Use scanner_isspace()
1142  * not libc's isspace(), because we want to match the lexer's behavior.
1143  */
1144  while (query_len > 0 && scanner_isspace(query[0]))
1145  query++, query_location++, query_len--;
1146  while (query_len > 0 && scanner_isspace(query[query_len - 1]))
1147  query_len--;
1148 
1149  /*
1150  * For utility statements, we just hash the query string to get an ID.
1151  */
1152  if (queryId == UINT64CONST(0))
1153  {
1154  queryId = pgss_hash_string(query, query_len);
1155 
1156  /*
1157  * If we are unlucky enough to get a hash of zero(invalid), use
1158  * queryID as 2 instead, queryID 1 is already in use for normal
1159  * statements.
1160  */
1161  if (queryId == UINT64CONST(0))
1162  queryId = UINT64CONST(2);
1163  }
1164 
1165  /* Set up key for hashtable search */
1166  key.userid = GetUserId();
1167  key.dbid = MyDatabaseId;
1168  key.queryid = queryId;
1169 
1170  /* Lookup the hash table entry with shared lock. */
1172 
1173  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
1174 
1175  /* Create new entry, if not present */
1176  if (!entry)
1177  {
1178  Size query_offset;
1179  int gc_count;
1180  bool stored;
1181  bool do_gc;
1182 
1183  /*
1184  * Create a new, normalized query string if caller asked. We don't
1185  * need to hold the lock while doing this work. (Note: in any case,
1186  * it's possible that someone else creates a duplicate hashtable entry
1187  * in the interval where we don't hold the lock below. That case is
1188  * handled by entry_alloc.)
1189  */
1190  if (jstate)
1191  {
1193  norm_query = generate_normalized_query(jstate, query,
1194  query_location,
1195  &query_len,
1196  encoding);
1198  }
1199 
1200  /* Append new query text to file with only shared lock held */
1201  stored = qtext_store(norm_query ? norm_query : query, query_len,
1202  &query_offset, &gc_count);
1203 
1204  /*
1205  * Determine whether we need to garbage collect external query texts
1206  * while the shared lock is still held. This micro-optimization
1207  * avoids taking the time to decide this while holding exclusive lock.
1208  */
1209  do_gc = need_gc_qtexts();
1210 
1211  /* Need exclusive lock to make a new hashtable entry - promote */
1214 
1215  /*
1216  * A garbage collection may have occurred while we weren't holding the
1217  * lock. In the unlikely event that this happens, the query text we
1218  * stored above will have been garbage collected, so write it again.
1219  * This should be infrequent enough that doing it while holding
1220  * exclusive lock isn't a performance problem.
1221  */
1222  if (!stored || pgss->gc_count != gc_count)
1223  stored = qtext_store(norm_query ? norm_query : query, query_len,
1224  &query_offset, NULL);
1225 
1226  /* If we failed to write to the text file, give up */
1227  if (!stored)
1228  goto done;
1229 
1230  /* OK to create a new hashtable entry */
1231  entry = entry_alloc(&key, query_offset, query_len, encoding,
1232  jstate != NULL);
1233 
1234  /* If needed, perform garbage collection while exclusive lock held */
1235  if (do_gc)
1236  gc_qtexts();
1237  }
1238 
1239  /* Increment the counts, except when jstate is not NULL */
1240  if (!jstate)
1241  {
1242  /*
1243  * Grab the spinlock while updating the counters (see comment about
1244  * locking rules at the head of the file)
1245  */
1246  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1247 
1248  SpinLockAcquire(&e->mutex);
1249 
1250  /* "Unstick" entry if it was previously sticky */
1251  if (e->counters.calls == 0)
1252  e->counters.usage = USAGE_INIT;
1253 
1254  e->counters.calls += 1;
1255  e->counters.total_time += total_time;
1256  if (e->counters.calls == 1)
1257  {
1258  e->counters.min_time = total_time;
1259  e->counters.max_time = total_time;
1260  e->counters.mean_time = total_time;
1261  }
1262  else
1263  {
1264  /*
1265  * Welford's method for accurately computing variance. See
1266  * <http://www.johndcook.com/blog/standard_deviation/>
1267  */
1268  double old_mean = e->counters.mean_time;
1269 
1270  e->counters.mean_time +=
1271  (total_time - old_mean) / e->counters.calls;
1272  e->counters.sum_var_time +=
1273  (total_time - old_mean) * (total_time - e->counters.mean_time);
1274 
1275  /* calculate min and max time */
1276  if (e->counters.min_time > total_time)
1277  e->counters.min_time = total_time;
1278  if (e->counters.max_time < total_time)
1279  e->counters.max_time = total_time;
1280  }
1281  e->counters.rows += rows;
1282  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1283  e->counters.shared_blks_read += bufusage->shared_blks_read;
1286  e->counters.local_blks_hit += bufusage->local_blks_hit;
1287  e->counters.local_blks_read += bufusage->local_blks_read;
1290  e->counters.temp_blks_read += bufusage->temp_blks_read;
1294  e->counters.usage += USAGE_EXEC(total_time);
1295 
1296  SpinLockRelease(&e->mutex);
1297  }
1298 
1299 done:
1301 
1302  /* We postpone this clean-up until we're out of the lock */
1303  if (norm_query)
1304  pfree(norm_query);
1305 }
long local_blks_hit
Definition: instrument.h:25
static uint64 pgss_hash_string(const char *str, int len)
long local_blks_dirtied
Definition: instrument.h:27
long local_blks_read
Definition: instrument.h:26
int64 shared_blks_read
int64 local_blks_written
Oid GetUserId(void)
Definition: miscinit.c:380
instr_time blk_read_time
Definition: instrument.h:31
int64 shared_blks_dirtied
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
long shared_blks_read
Definition: instrument.h:22
long temp_blks_written
Definition: instrument.h:30
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
Counters counters
int64 temp_blks_read
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
long shared_blks_written
Definition: instrument.h:24
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1056
int64 shared_blks_hit
static HTAB * pgss_hash
#define USAGE_EXEC(duration)
static pgssEntry * entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
static char * generate_normalized_query(pgssJumbleState *jstate, const char *query, int query_loc, int *query_len_p, int encoding)
long shared_blks_dirtied
Definition: instrument.h:23
long temp_blks_read
Definition: instrument.h:29
int64 temp_blks_written
static bool qtext_store(const char *query, int query_len, Size *query_offset, int *gc_count)
int64 shared_blks_written
#define SpinLockRelease(lock)
Definition: spin.h:64
int64 local_blks_read
static bool need_gc_qtexts(void)
int GetDatabaseEncoding(void)
Definition: mbutils.c:1046
static void gc_qtexts(void)
double sum_var_time
Oid MyDatabaseId
Definition: globals.c:85
bool scanner_isspace(char ch)
Definition: scansup.c:221
#define USAGE_INIT
double blk_read_time
#define Assert(condition)
Definition: c.h:732
int64 local_blks_dirtied
instr_time blk_write_time
Definition: instrument.h:32
int64 local_blks_hit
size_t Size
Definition: c.h:466
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
double blk_write_time
int32 encoding
Definition: pg_database.h:41
e
Definition: preproc-init.c:82
long shared_blks_hit
Definition: instrument.h:21
long local_blks_written
Definition: instrument.h:28

◆ qtext_fetch()

static char * qtext_fetch ( Size  query_offset,
int  query_len,
char *  buffer,
Size  buffer_size 
)
static

Definition at line 2012 of file pg_stat_statements.c.

Referenced by gc_qtexts(), pg_stat_statements_internal(), and pgss_shmem_shutdown().

2014 {
2015  /* File read failed? */
2016  if (buffer == NULL)
2017  return NULL;
2018  /* Bogus offset/length? */
2019  if (query_len < 0 ||
2020  query_offset + query_len >= buffer_size)
2021  return NULL;
2022  /* As a further sanity check, make sure there's a trailing null */
2023  if (buffer[query_offset + query_len] != '\0')
2024  return NULL;
2025  /* Looks OK */
2026  return buffer + query_offset;
2027 }

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

Definition at line 1932 of file pg_stat_statements.c.

References buf, CloseTransientFile(), ereport, errcode(), errcode_for_file_access(), errdetail(), errmsg(), fd(), free, LOG, malloc, MaxAllocHugeSize, OpenTransientFile(), PG_BINARY, PGSS_TEXT_FILE, read, and stat.

Referenced by gc_qtexts(), pg_stat_statements_internal(), and pgss_shmem_shutdown().

1933 {
1934  char *buf;
1935  int fd;
1936  struct stat stat;
1937 
1938  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
1939  if (fd < 0)
1940  {
1941  if (errno != ENOENT)
1942  ereport(LOG,
1944  errmsg("could not read file \"%s\": %m",
1945  PGSS_TEXT_FILE)));
1946  return NULL;
1947  }
1948 
1949  /* Get file length */
1950  if (fstat(fd, &stat))
1951  {
1952  ereport(LOG,
1954  errmsg("could not stat file \"%s\": %m",
1955  PGSS_TEXT_FILE)));
1956  CloseTransientFile(fd);
1957  return NULL;
1958  }
1959 
1960  /* Allocate buffer; beware that off_t might be wider than size_t */
1961  if (stat.st_size <= MaxAllocHugeSize)
1962  buf = (char *) malloc(stat.st_size);
1963  else
1964  buf = NULL;
1965  if (buf == NULL)
1966  {
1967  ereport(LOG,
1968  (errcode(ERRCODE_OUT_OF_MEMORY),
1969  errmsg("out of memory"),
1970  errdetail("Could not allocate enough memory to read file \"%s\".",
1971  PGSS_TEXT_FILE)));
1972  CloseTransientFile(fd);
1973  return NULL;
1974  }
1975 
1976  /*
1977  * OK, slurp in the file. If we get a short read and errno doesn't get
1978  * set, the reason is probably that garbage collection truncated the file
1979  * since we did the fstat(), so we don't log a complaint --- but we don't
1980  * return the data, either, since it's most likely corrupt due to
1981  * concurrent writes from garbage collection.
1982  */
1983  errno = 0;
1984  if (read(fd, buf, stat.st_size) != stat.st_size)
1985  {
1986  if (errno)
1987  ereport(LOG,
1989  errmsg("could not read file \"%s\": %m",
1990  PGSS_TEXT_FILE)));
1991  free(buf);
1992  CloseTransientFile(fd);
1993  return NULL;
1994  }
1995 
1996  if (CloseTransientFile(fd) != 0)
1997  ereport(LOG,
1999  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
2000 
2001  *buffer_size = stat.st_size;
2002  return buf;
2003 }
int errcode(int sqlerrcode)
Definition: elog.c:570
#define LOG
Definition: elog.h:26
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1191
#define MaxAllocHugeSize
Definition: memutils.h:44
#define malloc(a)
Definition: header.h:50
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2255
static char * buf
Definition: pg_test_fsync.c:68
int errdetail(const char *fmt,...)
Definition: elog.c:860
int errcode_for_file_access(void)
Definition: elog.c:593
#define ereport(elevel, rest)
Definition: elog.h:141
int CloseTransientFile(int fd)
Definition: fd.c:2432
#define stat(a, b)
Definition: win32_port.h:255
#define free(a)
Definition: header.h:65
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define PGSS_TEXT_FILE
#define read(a, b, c)
Definition: win32.h:13

◆ qtext_store()

static bool qtext_store ( const char *  query,
int  query_len,
Size query_offset,
int *  gc_count 
)
static

Definition at line 1849 of file pg_stat_statements.c.

References CloseTransientFile(), ereport, errcode_for_file_access(), errmsg(), error(), pgssSharedState::extent, fd(), pgssSharedState::gc_count, LOG, pgssSharedState::mutex, pgssSharedState::n_writers, OpenTransientFile(), PG_BINARY, PGSS_TEXT_FILE, SpinLockAcquire, SpinLockRelease, and write.

Referenced by pgss_store().

1851 {
1852  Size off;
1853  int fd;
1854 
1855  /*
1856  * We use a spinlock to protect extent/n_writers/gc_count, so that
1857  * multiple processes may execute this function concurrently.
1858  */
1859  {
1860  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1861 
1862  SpinLockAcquire(&s->mutex);
1863  off = s->extent;
1864  s->extent += query_len + 1;
1865  s->n_writers++;
1866  if (gc_count)
1867  *gc_count = s->gc_count;
1868  SpinLockRelease(&s->mutex);
1869  }
1870 
1871  *query_offset = off;
1872 
1873  /* Now write the data into the successfully-reserved part of the file */
1874  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDWR | O_CREAT | PG_BINARY);
1875  if (fd < 0)
1876  goto error;
1877 
1878  if (lseek(fd, off, SEEK_SET) != off)
1879  goto error;
1880 
1881  if (write(fd, query, query_len) != query_len)
1882  goto error;
1883  if (write(fd, "\0", 1) != 1)
1884  goto error;
1885 
1886  CloseTransientFile(fd);
1887 
1888  /* Mark our write complete */
1889  {
1890  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1891 
1892  SpinLockAcquire(&s->mutex);
1893  s->n_writers--;
1894  SpinLockRelease(&s->mutex);
1895  }
1896 
1897  return true;
1898 
1899 error:
1900  ereport(LOG,
1902  errmsg("could not write file \"%s\": %m",
1903  PGSS_TEXT_FILE)));
1904 
1905  if (fd >= 0)
1906  CloseTransientFile(fd);
1907 
1908  /* Mark our write complete */
1909  {
1910  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1911 
1912  SpinLockAcquire(&s->mutex);
1913  s->n_writers--;
1914  SpinLockRelease(&s->mutex);
1915  }
1916 
1917  return false;
1918 }
static void error(void)
Definition: sql-dyntest.c:147
#define write(a, b, c)
Definition: win32.h:14
#define LOG
Definition: elog.h:26
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1191
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2255
int errcode_for_file_access(void)
Definition: elog.c:593
#define ereport(elevel, rest)
Definition: elog.h:141
int CloseTransientFile(int fd)
Definition: fd.c:2432
#define SpinLockRelease(lock)
Definition: spin.h:64
size_t Size
Definition: c.h:466
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define PGSS_TEXT_FILE

◆ RecordConstLocation()

static void RecordConstLocation ( pgssJumbleState jstate,
int  location 
)
static

Definition at line 3007 of file pg_stat_statements.c.

References pgssJumbleState::clocations, pgssJumbleState::clocations_buf_size, pgssJumbleState::clocations_count, pgssLocationLen::length, pgssLocationLen::location, and repalloc().

Referenced by JumbleExpr().

3008 {
3009  /* -1 indicates unknown or undefined location */
3010  if (location >= 0)
3011  {
3012  /* enlarge array if needed */
3013  if (jstate->clocations_count >= jstate->clocations_buf_size)
3014  {
3015  jstate->clocations_buf_size *= 2;
3016  jstate->clocations = (pgssLocationLen *)
3017  repalloc(jstate->clocations,
3018  jstate->clocations_buf_size *
3019  sizeof(pgssLocationLen));
3020  }
3021  jstate->clocations[jstate->clocations_count].location = location;
3022  /* initialize lengths to -1 to simplify fill_in_constant_lengths */
3023  jstate->clocations[jstate->clocations_count].length = -1;
3024  jstate->clocations_count++;
3025  }
3026 }
pgssLocationLen * clocations
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069

Variable Documentation

◆ nested_level

int nested_level = 0
static

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 83 of file pg_stat_statements.c.

◆ pgss

pgssSharedState* pgss = NULL
static

Definition at line 249 of file pg_stat_statements.c.

◆ PGSS_FILE_HEADER

const uint32 PGSS_FILE_HEADER = 0x20171004
static

Definition at line 99 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_hash

HTAB* pgss_hash = NULL
static

Definition at line 250 of file pg_stat_statements.c.

◆ pgss_max

int pgss_max
static

◆ PGSS_PG_MAJOR_VERSION

const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100
static

Definition at line 102 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_save

bool pgss_save
static

Definition at line 272 of file pg_stat_statements.c.

Referenced by _PG_init(), pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_track

int pgss_track
static

Definition at line 270 of file pg_stat_statements.c.

Referenced by _PG_init().

◆ pgss_track_utility

bool pgss_track_utility
static

Definition at line 271 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_ProcessUtility().

◆ prev_ExecutorEnd

ExecutorEnd_hook_type prev_ExecutorEnd = NULL
static

Definition at line 245 of file pg_stat_statements.c.

Referenced by _PG_fini(), _PG_init(), and pgss_ExecutorEnd().

◆ prev_ExecutorFinish

ExecutorFinish_hook_type prev_ExecutorFinish = NULL
static

Definition at line 244 of file pg_stat_statements.c.

Referenced by _PG_fini(), _PG_init(), and pgss_ExecutorFinish().

◆ prev_ExecutorRun

ExecutorRun_hook_type prev_ExecutorRun = NULL
static

Definition at line 243 of file pg_stat_statements.c.

Referenced by _PG_fini(), _PG_init(), and pgss_ExecutorRun().

◆ prev_ExecutorStart

ExecutorStart_hook_type prev_ExecutorStart = NULL
static

Definition at line 242 of file pg_stat_statements.c.

Referenced by _PG_fini(), _PG_init(), and pgss_ExecutorStart().

◆ prev_post_parse_analyze_hook

post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL
static

Definition at line 241 of file pg_stat_statements.c.

Referenced by _PG_fini(), _PG_init(), and pgss_post_parse_analyze().

◆ prev_ProcessUtility

ProcessUtility_hook_type prev_ProcessUtility = NULL
static

Definition at line 246 of file pg_stat_statements.c.

Referenced by _PG_fini(), _PG_init(), and pgss_ProcessUtility().

◆ prev_shmem_startup_hook

shmem_startup_hook_type prev_shmem_startup_hook = NULL
static

Definition at line 240 of file pg_stat_statements.c.

Referenced by _PG_fini(), _PG_init(), and pgss_shmem_startup().

◆ track_options

const struct config_enum_entry track_options[]
static
Initial value:
=
{
{"none", PGSS_TRACK_NONE, false},
{"top", PGSS_TRACK_TOP, false},
{"all", PGSS_TRACK_ALL, false},
{NULL, 0, false}
}

Definition at line 261 of file pg_stat_statements.c.