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 2388 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 2390 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 1336 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 1332 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 1333 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 1334 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 1335 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:75
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:8710
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:70
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1498
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:639
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:75
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:8795
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:8823
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:8684
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 2352 of file pg_stat_statements.c.

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

2353 {
2354  unsigned char *jumble = jstate->jumble;
2355  Size jumble_len = jstate->jumble_len;
2356 
2357  /*
2358  * Whenever the jumble buffer is full, we hash the current contents and
2359  * reset the buffer to contain just that hash value, thus relying on the
2360  * hash to summarize everything so far.
2361  */
2362  while (size > 0)
2363  {
2364  Size part_size;
2365 
2366  if (jumble_len >= JUMBLE_SIZE)
2367  {
2368  uint64 start_hash;
2369 
2370  start_hash = DatumGetUInt64(hash_any_extended(jumble,
2371  JUMBLE_SIZE, 0));
2372  memcpy(jumble, &start_hash, sizeof(start_hash));
2373  jumble_len = sizeof(start_hash);
2374  }
2375  part_size = Min(size, JUMBLE_SIZE - jumble_len);
2376  memcpy(jumble + jumble_len, item, part_size);
2377  jumble_len += part_size;
2378  item += part_size;
2379  size -= part_size;
2380  }
2381  jstate->jumble_len = jumble_len;
2382 }
#define Min(x, y)
Definition: c.h:920
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 3245 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

3246 {
3247  int l = ((const pgssLocationLen *) a)->location;
3248  int r = ((const pgssLocationLen *) b)->location;
3249 
3250  if (l < r)
3251  return -1;
3252  else if (l > r)
3253  return +1;
3254  else
3255  return 0;
3256 }

◆ entry_alloc()

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

Definition at line 1700 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().

1702 {
1703  pgssEntry *entry;
1704  bool found;
1705 
1706  /* Make space if needed */
1708  entry_dealloc();
1709 
1710  /* Find or create an entry with desired hash code */
1711  entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
1712 
1713  if (!found)
1714  {
1715  /* New entry, initialize it */
1716 
1717  /* reset the statistics */
1718  memset(&entry->counters, 0, sizeof(Counters));
1719  /* set the appropriate initial usage count */
1720  entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
1721  /* re-initialize the mutex each time ... we assume no one using it */
1722  SpinLockInit(&entry->mutex);
1723  /* ... and don't forget the query text metadata */
1724  Assert(query_len >= 0);
1725  entry->query_offset = query_offset;
1726  entry->query_len = query_len;
1727  entry->encoding = encoding;
1728  }
1729 
1730  return entry;
1731 }
#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:738
int32 encoding
Definition: pg_database.h:41

◆ entry_cmp()

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

Definition at line 1737 of file pg_stat_statements.c.

Referenced by entry_dealloc().

1738 {
1739  double l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
1740  double r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
1741 
1742  if (l_usage < r_usage)
1743  return -1;
1744  else if (l_usage > r_usage)
1745  return +1;
1746  else
1747  return 0;
1748 }

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

Definition at line 1756 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().

1757 {
1758  HASH_SEQ_STATUS hash_seq;
1759  pgssEntry **entries;
1760  pgssEntry *entry;
1761  int nvictims;
1762  int i;
1763  Size tottextlen;
1764  int nvalidtexts;
1765 
1766  /*
1767  * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
1768  * While we're scanning the table, apply the decay factor to the usage
1769  * values, and update the mean query length.
1770  *
1771  * Note that the mean query length is almost immediately obsolete, since
1772  * we compute it before not after discarding the least-used entries.
1773  * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
1774  * making two passes to get a more current result. Likewise, the new
1775  * cur_median_usage includes the entries we're about to zap.
1776  */
1777 
1778  entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
1779 
1780  i = 0;
1781  tottextlen = 0;
1782  nvalidtexts = 0;
1783 
1784  hash_seq_init(&hash_seq, pgss_hash);
1785  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1786  {
1787  entries[i++] = entry;
1788  /* "Sticky" entries get a different usage decay rate. */
1789  if (entry->counters.calls == 0)
1791  else
1793  /* In the mean length computation, ignore dropped texts. */
1794  if (entry->query_len >= 0)
1795  {
1796  tottextlen += entry->query_len + 1;
1797  nvalidtexts++;
1798  }
1799  }
1800 
1801  /* Sort into increasing order by usage */
1802  qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
1803 
1804  /* Record the (approximate) median usage */
1805  if (i > 0)
1806  pgss->cur_median_usage = entries[i / 2]->counters.usage;
1807  /* Record the mean query length */
1808  if (nvalidtexts > 0)
1809  pgss->mean_query_len = tottextlen / nvalidtexts;
1810  else
1812 
1813  /* Now zap an appropriate fraction of lowest-usage entries */
1814  nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
1815  nvictims = Min(nvictims, i);
1816 
1817  for (i = 0; i < nvictims; i++)
1818  {
1819  hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
1820  }
1821 
1822  pfree(entries);
1823 }
#define USAGE_DEALLOC_PERCENT
#define Min(x, y)
Definition: c.h:920
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:914
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:474

◆ entry_reset()

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

Definition at line 2256 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().

2257 {
2258  HASH_SEQ_STATUS hash_seq;
2259  pgssEntry *entry;
2260  FILE *qfile;
2261  long num_entries;
2262  long num_remove = 0;
2263  pgssHashKey key;
2264 
2265  if (!pgss || !pgss_hash)
2266  ereport(ERROR,
2267  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2268  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2269 
2271  num_entries = hash_get_num_entries(pgss_hash);
2272 
2273  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2274  {
2275  /* If all the parameters are available, use the fast path. */
2276  key.userid = userid;
2277  key.dbid = dbid;
2278  key.queryid = queryid;
2279 
2280  /* Remove the key if exists */
2281  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2282  if (entry) /* found */
2283  num_remove++;
2284  }
2285  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2286  {
2287  /* Remove entries corresponding to valid parameters. */
2288  hash_seq_init(&hash_seq, pgss_hash);
2289  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2290  {
2291  if ((!userid || entry->key.userid == userid) &&
2292  (!dbid || entry->key.dbid == dbid) &&
2293  (!queryid || entry->key.queryid == queryid))
2294  {
2295  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2296  num_remove++;
2297  }
2298  }
2299  }
2300  else
2301  {
2302  /* Remove all entries. */
2303  hash_seq_init(&hash_seq, pgss_hash);
2304  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2305  {
2306  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2307  num_remove++;
2308  }
2309  }
2310 
2311  /* All entries are removed? */
2312  if (num_entries != num_remove)
2313  goto release_lock;
2314 
2315  /*
2316  * Write new empty query file, perhaps even creating a new one to recover
2317  * if the file was missing.
2318  */
2320  if (qfile == NULL)
2321  {
2322  ereport(LOG,
2324  errmsg("could not create file \"%s\": %m",
2325  PGSS_TEXT_FILE)));
2326  goto done;
2327  }
2328 
2329  /* If ftruncate fails, log it, but it's not a fatal problem */
2330  if (ftruncate(fileno(qfile), 0) != 0)
2331  ereport(LOG,
2333  errmsg("could not truncate file \"%s\": %m",
2334  PGSS_TEXT_FILE)));
2335 
2336  FreeFile(qfile);
2337 
2338 done:
2339  pgss->extent = 0;
2340  /* This counts as a query text garbage collection for our purposes */
2341  record_gc_qtexts();
2342 
2343 release_lock:
2345 }
int errcode(int sqlerrcode)
Definition: elog.c:608
#define PG_BINARY_W
Definition: c.h:1233
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:1727
static pgssSharedState * pgss
static HTAB * pgss_hash
#define ERROR
Definition: elog.h:43
int errcode_for_file_access(void)
Definition: elog.c:631
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2241
#define ereport(elevel, rest)
Definition: elog.h:141
pgssHashKey key
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1123
#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:2440
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define PGSS_TEXT_FILE
#define ftruncate(a, b)
Definition: win32_port.h:59

◆ fill_in_constant_lengths()

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

Definition at line 3145 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().

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

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

Definition at line 2072 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().

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

◆ 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 3038 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().

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

◆ JumbleExpr()

static void JumbleExpr ( pgssJumbleState jstate,
Node node 
)
static

Definition at line 2518 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().

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

◆ JumbleQuery()

static void JumbleQuery ( pgssJumbleState jstate,
Query query 
)
static

Definition at line 2403 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().

2404 {
2405  Assert(IsA(query, Query));
2406  Assert(query->utilityStmt == NULL);
2407 
2408  APP_JUMB(query->commandType);
2409  /* resultRelation is usually predictable from commandType */
2410  JumbleExpr(jstate, (Node *) query->cteList);
2411  JumbleRangeTable(jstate, query->rtable);
2412  JumbleExpr(jstate, (Node *) query->jointree);
2413  JumbleExpr(jstate, (Node *) query->targetList);
2414  JumbleExpr(jstate, (Node *) query->onConflict);
2415  JumbleExpr(jstate, (Node *) query->returningList);
2416  JumbleExpr(jstate, (Node *) query->groupClause);
2417  JumbleExpr(jstate, (Node *) query->groupingSets);
2418  JumbleExpr(jstate, query->havingQual);
2419  JumbleExpr(jstate, (Node *) query->windowClause);
2420  JumbleExpr(jstate, (Node *) query->distinctClause);
2421  JumbleExpr(jstate, (Node *) query->sortClause);
2422  JumbleExpr(jstate, query->limitOffset);
2423  JumbleExpr(jstate, query->limitCount);
2424  JumbleRowMarks(jstate, query->rowMarks);
2425  JumbleExpr(jstate, query->setOperations);
2426 }
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:738
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 2432 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().

2433 {
2434  ListCell *lc;
2435 
2436  foreach(lc, rtable)
2437  {
2439 
2440  APP_JUMB(rte->rtekind);
2441  switch (rte->rtekind)
2442  {
2443  case RTE_RELATION:
2444  APP_JUMB(rte->relid);
2445  JumbleExpr(jstate, (Node *) rte->tablesample);
2446  break;
2447  case RTE_SUBQUERY:
2448  JumbleQuery(jstate, rte->subquery);
2449  break;
2450  case RTE_JOIN:
2451  APP_JUMB(rte->jointype);
2452  break;
2453  case RTE_FUNCTION:
2454  JumbleExpr(jstate, (Node *) rte->functions);
2455  break;
2456  case RTE_TABLEFUNC:
2457  JumbleExpr(jstate, (Node *) rte->tablefunc);
2458  break;
2459  case RTE_VALUES:
2460  JumbleExpr(jstate, (Node *) rte->values_lists);
2461  break;
2462  case RTE_CTE:
2463 
2464  /*
2465  * Depending on the CTE name here isn't ideal, but it's the
2466  * only info we have to identify the referenced WITH item.
2467  */
2468  APP_JUMB_STRING(rte->ctename);
2469  APP_JUMB(rte->ctelevelsup);
2470  break;
2471  case RTE_NAMEDTUPLESTORE:
2472  APP_JUMB_STRING(rte->enrname);
2473  break;
2474  case RTE_RESULT:
2475  break;
2476  default:
2477  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
2478  break;
2479  }
2480  }
2481 }
static void JumbleExpr(pgssJumbleState *jstate, Node *node)
Definition: nodes.h:525
List * values_lists
Definition: parsenodes.h:1072
#define APP_JUMB_STRING(str)
#define ERROR
Definition: elog.h:43
TableFunc * tablefunc
Definition: parsenodes.h:1067
#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:1047
char * enrname
Definition: parsenodes.h:1106
List * functions
Definition: parsenodes.h:1061
Index ctelevelsup
Definition: parsenodes.h:1078
RTEKind rtekind
Definition: parsenodes.h:974
char * ctename
Definition: parsenodes.h:1077
Query * subquery
Definition: parsenodes.h:1009
#define elog(elevel,...)
Definition: elog.h:228
struct TableSampleClause * tablesample
Definition: parsenodes.h:1004

◆ JumbleRowMarks()

static void JumbleRowMarks ( pgssJumbleState jstate,
List rowMarks 
)
static

Definition at line 2487 of file pg_stat_statements.c.

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

Referenced by JumbleQuery().

2488 {
2489  ListCell *lc;
2490 
2491  foreach(lc, rowMarks)
2492  {
2493  RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
2494  if (!rowmark->pushedDown)
2495  {
2496  APP_JUMB(rowmark->rti);
2497  APP_JUMB(rowmark->strength);
2498  APP_JUMB(rowmark->waitPolicy);
2499  }
2500  }
2501 }
LockClauseStrength strength
Definition: parsenodes.h:1381
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
LockWaitPolicy waitPolicy
Definition: parsenodes.h:1382

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2025 of file pg_stat_statements.c.

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

Referenced by gc_qtexts(), and pgss_store().

2026 {
2027  Size extent;
2028 
2029  /* Read shared extent pointer */
2030  {
2031  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2032 
2033  SpinLockAcquire(&s->mutex);
2034  extent = s->extent;
2035  SpinLockRelease(&s->mutex);
2036  }
2037 
2038  /* Don't proceed if file does not exceed 512 bytes per possible entry */
2039  if (extent < 512 * pgss_max)
2040  return false;
2041 
2042  /*
2043  * Don't proceed if file is less than about 50% bloat. Nothing can or
2044  * should be done in the event of unusually large query texts accounting
2045  * for file's large size. We go to the trouble of maintaining the mean
2046  * query length in order to prevent garbage collection from thrashing
2047  * uselessly.
2048  */
2049  if (extent < pgss->mean_query_len * pgss_max * 2)
2050  return false;
2051 
2052  return true;
2053 }
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 1373 of file pg_stat_statements.c.

References pg_stat_statements_internal(), and PGSS_V1_0.

1374 {
1375  /* If it's really API 1.1, we'll figure that out below */
1376  pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
1377 
1378  return (Datum) 0;
1379 }
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 1359 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

1360 {
1361  bool showtext = PG_GETARG_BOOL(0);
1362 
1363  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1364 
1365  return (Datum) 0;
1366 }
#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 1349 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

1350 {
1351  bool showtext = PG_GETARG_BOOL(0);
1352 
1353  pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
1354 
1355  return (Datum) 0;
1356 }
#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 1383 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().

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

References entry_reset(), and PG_RETURN_VOID.

1325 {
1326  entry_reset(0, 0, 0);
1327 
1328  PG_RETURN_VOID();
1329 }
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 1305 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.

1306 {
1307  Oid userid;
1308  Oid dbid;
1309  uint64 queryid;
1310 
1311  userid = PG_GETARG_OID(0);
1312  dbid = PG_GETARG_OID(1);
1313  queryid = (uint64) PG_GETARG_INT64(2);
1314 
1315  entry_reset(userid, dbid, queryid);
1316 
1317  PG_RETURN_VOID();
1318 }
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 928 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().

929 {
930  uint64 queryId = queryDesc->plannedstmt->queryId;
931 
932  if (queryId != UINT64CONST(0) && queryDesc->totaltime && pgss_enabled())
933  {
934  /*
935  * Make sure stats accumulation is done. (Note: it's okay if several
936  * levels of hook all do this.)
937  */
938  InstrEndLoop(queryDesc->totaltime);
939 
940  pgss_store(queryDesc->sourceText,
941  queryId,
942  queryDesc->plannedstmt->stmt_location,
943  queryDesc->plannedstmt->stmt_len,
944  queryDesc->totaltime->total * 1000.0, /* convert to msec */
945  queryDesc->estate->es_processed,
946  &queryDesc->totaltime->bufusage,
947  NULL);
948  }
949 
950  if (prev_ExecutorEnd)
951  prev_ExecutorEnd(queryDesc);
952  else
953  standard_ExecutorEnd(queryDesc);
954 }
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:96
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:110
int stmt_location
Definition: plannodes.h:95
BufferUsage bufusage
Definition: instrument.h:64
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 es_processed
Definition: execnodes.h:557
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 907 of file pg_stat_statements.c.

References nested_level, PG_END_TRY, PG_FINALLY, PG_TRY, prev_ExecutorFinish, and standard_ExecutorFinish().

Referenced by _PG_init().

908 {
909  nested_level++;
910  PG_TRY();
911  {
913  prev_ExecutorFinish(queryDesc);
914  else
915  standard_ExecutorFinish(queryDesc);
916  }
917  PG_FINALLY();
918  {
919  nested_level--;
920  }
921  PG_END_TRY();
922 }
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:411
static ExecutorFinish_hook_type prev_ExecutorFinish
#define PG_FINALLY()
Definition: elog.h:339
#define PG_TRY()
Definition: elog.h:322
static int nested_level
#define PG_END_TRY()
Definition: elog.h:347

◆ 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_END_TRY, PG_FINALLY, 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  }
896  PG_FINALLY();
897  {
898  nested_level--;
899  }
900  PG_END_TRY();
901 }
void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:312
static ExecutorRun_hook_type prev_ExecutorRun
#define PG_FINALLY()
Definition: elog.h:339
#define PG_TRY()
Definition: elog.h:322
static int nested_level
#define PG_END_TRY()
Definition: elog.h:347

◆ 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:553
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 1077 of file pg_stat_statements.c.

References DatumGetUInt64, and hash_any_extended().

Referenced by pgss_store().

1078 {
1079  return DatumGetUInt64(hash_any_extended((const unsigned char *) str,
1080  len, 0));
1081 }
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 1672 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1673 {
1674  Size size;
1675 
1676  size = MAXALIGN(sizeof(pgssSharedState));
1677  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1678 
1679  return size;
1680 }
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:498
size_t Size
Definition: c.h:466
#define MAXALIGN(LEN)
Definition: c.h:691

◆ 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:179
uint64 queryId
Definition: parsenodes.h:116
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:738
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 960 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_END_TRY, PG_FINALLY, 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().

964 {
965  Node *parsetree = pstmt->utilityStmt;
966 
967  /*
968  * If it's an EXECUTE statement, we don't track it and don't increment the
969  * nesting level. This allows the cycles to be charged to the underlying
970  * PREPARE instead (by the Executor hooks), which is much more useful.
971  *
972  * We also don't track execution of PREPARE. If we did, we would get one
973  * hash table entry for the PREPARE (with hash calculated from the query
974  * string), and then a different one with the same query string (but hash
975  * calculated from the query tree) would be used to accumulate costs of
976  * ensuing EXECUTEs. This would be confusing, and inconsistent with other
977  * cases where planning time is not included at all.
978  *
979  * Likewise, we don't track execution of DEALLOCATE.
980  */
981  if (pgss_track_utility && pgss_enabled() &&
982  !IsA(parsetree, ExecuteStmt) &&
983  !IsA(parsetree, PrepareStmt) &&
984  !IsA(parsetree, DeallocateStmt))
985  {
986  instr_time start;
988  uint64 rows;
989  BufferUsage bufusage_start,
990  bufusage;
991 
992  bufusage_start = pgBufferUsage;
993  INSTR_TIME_SET_CURRENT(start);
994 
995  nested_level++;
996  PG_TRY();
997  {
999  prev_ProcessUtility(pstmt, queryString,
1000  context, params, queryEnv,
1001  dest, completionTag);
1002  else
1003  standard_ProcessUtility(pstmt, queryString,
1004  context, params, queryEnv,
1005  dest, completionTag);
1006  }
1007  PG_FINALLY();
1008  {
1009  nested_level--;
1010  }
1011  PG_END_TRY();
1012 
1013  INSTR_TIME_SET_CURRENT(duration);
1014  INSTR_TIME_SUBTRACT(duration, start);
1015 
1016  /* parse command tag to retrieve the number of affected rows. */
1017  if (completionTag &&
1018  strncmp(completionTag, "COPY ", 5) == 0)
1019  rows = pg_strtouint64(completionTag + 5, NULL, 10);
1020  else
1021  rows = 0;
1022 
1023  /* calc differences of buffer counters. */
1024  bufusage.shared_blks_hit =
1026  bufusage.shared_blks_read =
1028  bufusage.shared_blks_dirtied =
1030  bufusage.shared_blks_written =
1032  bufusage.local_blks_hit =
1033  pgBufferUsage.local_blks_hit - bufusage_start.local_blks_hit;
1034  bufusage.local_blks_read =
1036  bufusage.local_blks_dirtied =
1038  bufusage.local_blks_written =
1040  bufusage.temp_blks_read =
1041  pgBufferUsage.temp_blks_read - bufusage_start.temp_blks_read;
1042  bufusage.temp_blks_written =
1045  INSTR_TIME_SUBTRACT(bufusage.blk_read_time, bufusage_start.blk_read_time);
1047  INSTR_TIME_SUBTRACT(bufusage.blk_write_time, bufusage_start.blk_write_time);
1048 
1049  pgss_store(queryString,
1050  0, /* signal that it's a utility stmt */
1051  pstmt->stmt_location,
1052  pstmt->stmt_len,
1053  INSTR_TIME_GET_MILLISEC(duration),
1054  rows,
1055  &bufusage,
1056  NULL);
1057  }
1058  else
1059  {
1060  if (prev_ProcessUtility)
1061  prev_ProcessUtility(pstmt, queryString,
1062  context, params, queryEnv,
1063  dest, completionTag);
1064  else
1065  standard_ProcessUtility(pstmt, queryString,
1066  context, params, queryEnv,
1067  dest, completionTag);
1068  }
1069 }
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:96
int duration
Definition: pgbench.c:146
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:543
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
int stmt_location
Definition: plannodes.h:95
Node * utilityStmt
Definition: plannodes.h:92
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:615
#define PG_FINALLY()
Definition: elog.h:339
static bool pgss_track_utility
instr_time blk_write_time
Definition: instrument.h:32
#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:322
static int nested_level
#define PG_END_TRY()
Definition: elog.h:347
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:1233
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1335
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:355
static pgssSharedState * pgss
static HTAB * pgss_hash
int errcode_for_file_access(void)
Definition: elog.c:631
static char * qtext_load_file(Size *buffer_size)
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2241
unsigned int uint32
Definition: c.h:367
#define ereport(elevel, rest)
Definition: elog.h:141
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:642
#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:2440
int errmsg(const char *fmt,...)
Definition: elog.c:822
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:608
struct pgssEntry pgssEntry
#define PG_BINARY_W
Definition: c.h:1233
Counters counters
#define LOG
Definition: elog.h:26
#define PG_BINARY_R
Definition: c.h:1232
signed int int32
Definition: c.h:355
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1727
#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:392
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:631
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2241
unsigned int uint32
Definition: c.h:367
#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:914
#define PG_VALID_BE_ENCODING(_enc)
Definition: pg_wchar.h:295
LWLockPadded * GetNamedLWLockTranche(const char *tranche_name)
Definition: lwlock.c:553
size_t Size
Definition: c.h:466
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1123
static void header(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:208
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:2440
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:822
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:337
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 1094 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().

1099 {
1100  pgssHashKey key;
1101  pgssEntry *entry;
1102  char *norm_query = NULL;
1103  int encoding = GetDatabaseEncoding();
1104 
1105  Assert(query != NULL);
1106 
1107  /* Safety check... */
1108  if (!pgss || !pgss_hash)
1109  return;
1110 
1111  /*
1112  * Confine our attention to the relevant part of the string, if the query
1113  * is a portion of a multi-statement source string.
1114  *
1115  * First apply starting offset, unless it's -1 (unknown).
1116  */
1117  if (query_location >= 0)
1118  {
1119  Assert(query_location <= strlen(query));
1120  query += query_location;
1121  /* Length of 0 (or -1) means "rest of string" */
1122  if (query_len <= 0)
1123  query_len = strlen(query);
1124  else
1125  Assert(query_len <= strlen(query));
1126  }
1127  else
1128  {
1129  /* If query location is unknown, distrust query_len as well */
1130  query_location = 0;
1131  query_len = strlen(query);
1132  }
1133 
1134  /*
1135  * Discard leading and trailing whitespace, too. Use scanner_isspace()
1136  * not libc's isspace(), because we want to match the lexer's behavior.
1137  */
1138  while (query_len > 0 && scanner_isspace(query[0]))
1139  query++, query_location++, query_len--;
1140  while (query_len > 0 && scanner_isspace(query[query_len - 1]))
1141  query_len--;
1142 
1143  /*
1144  * For utility statements, we just hash the query string to get an ID.
1145  */
1146  if (queryId == UINT64CONST(0))
1147  {
1148  queryId = pgss_hash_string(query, query_len);
1149 
1150  /*
1151  * If we are unlucky enough to get a hash of zero(invalid), use
1152  * queryID as 2 instead, queryID 1 is already in use for normal
1153  * statements.
1154  */
1155  if (queryId == UINT64CONST(0))
1156  queryId = UINT64CONST(2);
1157  }
1158 
1159  /* Set up key for hashtable search */
1160  key.userid = GetUserId();
1161  key.dbid = MyDatabaseId;
1162  key.queryid = queryId;
1163 
1164  /* Lookup the hash table entry with shared lock. */
1166 
1167  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
1168 
1169  /* Create new entry, if not present */
1170  if (!entry)
1171  {
1172  Size query_offset;
1173  int gc_count;
1174  bool stored;
1175  bool do_gc;
1176 
1177  /*
1178  * Create a new, normalized query string if caller asked. We don't
1179  * need to hold the lock while doing this work. (Note: in any case,
1180  * it's possible that someone else creates a duplicate hashtable entry
1181  * in the interval where we don't hold the lock below. That case is
1182  * handled by entry_alloc.)
1183  */
1184  if (jstate)
1185  {
1187  norm_query = generate_normalized_query(jstate, query,
1188  query_location,
1189  &query_len,
1190  encoding);
1192  }
1193 
1194  /* Append new query text to file with only shared lock held */
1195  stored = qtext_store(norm_query ? norm_query : query, query_len,
1196  &query_offset, &gc_count);
1197 
1198  /*
1199  * Determine whether we need to garbage collect external query texts
1200  * while the shared lock is still held. This micro-optimization
1201  * avoids taking the time to decide this while holding exclusive lock.
1202  */
1203  do_gc = need_gc_qtexts();
1204 
1205  /* Need exclusive lock to make a new hashtable entry - promote */
1208 
1209  /*
1210  * A garbage collection may have occurred while we weren't holding the
1211  * lock. In the unlikely event that this happens, the query text we
1212  * stored above will have been garbage collected, so write it again.
1213  * This should be infrequent enough that doing it while holding
1214  * exclusive lock isn't a performance problem.
1215  */
1216  if (!stored || pgss->gc_count != gc_count)
1217  stored = qtext_store(norm_query ? norm_query : query, query_len,
1218  &query_offset, NULL);
1219 
1220  /* If we failed to write to the text file, give up */
1221  if (!stored)
1222  goto done;
1223 
1224  /* OK to create a new hashtable entry */
1225  entry = entry_alloc(&key, query_offset, query_len, encoding,
1226  jstate != NULL);
1227 
1228  /* If needed, perform garbage collection while exclusive lock held */
1229  if (do_gc)
1230  gc_qtexts();
1231  }
1232 
1233  /* Increment the counts, except when jstate is not NULL */
1234  if (!jstate)
1235  {
1236  /*
1237  * Grab the spinlock while updating the counters (see comment about
1238  * locking rules at the head of the file)
1239  */
1240  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1241 
1242  SpinLockAcquire(&e->mutex);
1243 
1244  /* "Unstick" entry if it was previously sticky */
1245  if (e->counters.calls == 0)
1246  e->counters.usage = USAGE_INIT;
1247 
1248  e->counters.calls += 1;
1249  e->counters.total_time += total_time;
1250  if (e->counters.calls == 1)
1251  {
1252  e->counters.min_time = total_time;
1253  e->counters.max_time = total_time;
1254  e->counters.mean_time = total_time;
1255  }
1256  else
1257  {
1258  /*
1259  * Welford's method for accurately computing variance. See
1260  * <http://www.johndcook.com/blog/standard_deviation/>
1261  */
1262  double old_mean = e->counters.mean_time;
1263 
1264  e->counters.mean_time +=
1265  (total_time - old_mean) / e->counters.calls;
1266  e->counters.sum_var_time +=
1267  (total_time - old_mean) * (total_time - e->counters.mean_time);
1268 
1269  /* calculate min and max time */
1270  if (e->counters.min_time > total_time)
1271  e->counters.min_time = total_time;
1272  if (e->counters.max_time < total_time)
1273  e->counters.max_time = total_time;
1274  }
1275  e->counters.rows += rows;
1276  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1277  e->counters.shared_blks_read += bufusage->shared_blks_read;
1280  e->counters.local_blks_hit += bufusage->local_blks_hit;
1281  e->counters.local_blks_read += bufusage->local_blks_read;
1284  e->counters.temp_blks_read += bufusage->temp_blks_read;
1288  e->counters.usage += USAGE_EXEC(total_time);
1289 
1290  SpinLockRelease(&e->mutex);
1291  }
1292 
1293 done:
1295 
1296  /* We postpone this clean-up until we're out of the lock */
1297  if (norm_query)
1298  pfree(norm_query);
1299 }
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:378
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:1727
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:220
#define USAGE_INIT
double blk_read_time
#define Assert(condition)
Definition: c.h:738
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:1123
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 2002 of file pg_stat_statements.c.

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

2004 {
2005  /* File read failed? */
2006  if (buffer == NULL)
2007  return NULL;
2008  /* Bogus offset/length? */
2009  if (query_len < 0 ||
2010  query_offset + query_len >= buffer_size)
2011  return NULL;
2012  /* As a further sanity check, make sure there's a trailing null */
2013  if (buffer[query_offset + query_len] != '\0')
2014  return NULL;
2015  /* Looks OK */
2016  return buffer + query_offset;
2017 }

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

Definition at line 1922 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().

1923 {
1924  char *buf;
1925  int fd;
1926  struct stat stat;
1927 
1928  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
1929  if (fd < 0)
1930  {
1931  if (errno != ENOENT)
1932  ereport(LOG,
1934  errmsg("could not read file \"%s\": %m",
1935  PGSS_TEXT_FILE)));
1936  return NULL;
1937  }
1938 
1939  /* Get file length */
1940  if (fstat(fd, &stat))
1941  {
1942  ereport(LOG,
1944  errmsg("could not stat file \"%s\": %m",
1945  PGSS_TEXT_FILE)));
1946  CloseTransientFile(fd);
1947  return NULL;
1948  }
1949 
1950  /* Allocate buffer; beware that off_t might be wider than size_t */
1951  if (stat.st_size <= MaxAllocHugeSize)
1952  buf = (char *) malloc(stat.st_size);
1953  else
1954  buf = NULL;
1955  if (buf == NULL)
1956  {
1957  ereport(LOG,
1958  (errcode(ERRCODE_OUT_OF_MEMORY),
1959  errmsg("out of memory"),
1960  errdetail("Could not allocate enough memory to read file \"%s\".",
1961  PGSS_TEXT_FILE)));
1962  CloseTransientFile(fd);
1963  return NULL;
1964  }
1965 
1966  /*
1967  * OK, slurp in the file. If we get a short read and errno doesn't get
1968  * set, the reason is probably that garbage collection truncated the file
1969  * since we did the fstat(), so we don't log a complaint --- but we don't
1970  * return the data, either, since it's most likely corrupt due to
1971  * concurrent writes from garbage collection.
1972  */
1973  errno = 0;
1974  if (read(fd, buf, stat.st_size) != stat.st_size)
1975  {
1976  if (errno)
1977  ereport(LOG,
1979  errmsg("could not read file \"%s\": %m",
1980  PGSS_TEXT_FILE)));
1981  free(buf);
1982  CloseTransientFile(fd);
1983  return NULL;
1984  }
1985 
1986  if (CloseTransientFile(fd) != 0)
1987  ereport(LOG,
1989  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
1990 
1991  *buffer_size = stat.st_size;
1992  return buf;
1993 }
int errcode(int sqlerrcode)
Definition: elog.c:608
#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:1230
#define MaxAllocHugeSize
Definition: memutils.h:44
#define malloc(a)
Definition: header.h:50
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2291
static char * buf
Definition: pg_test_fsync.c:67
int errdetail(const char *fmt,...)
Definition: elog.c:955
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
int CloseTransientFile(int fd)
Definition: fd.c:2468
#define stat(a, b)
Definition: win32_port.h:255
#define free(a)
Definition: header.h:65
int errmsg(const char *fmt,...)
Definition: elog.c:822
#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 1842 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, pg_pwrite(), PGSS_TEXT_FILE, SpinLockAcquire, and SpinLockRelease.

Referenced by pgss_store().

1844 {
1845  Size off;
1846  int fd;
1847 
1848  /*
1849  * We use a spinlock to protect extent/n_writers/gc_count, so that
1850  * multiple processes may execute this function concurrently.
1851  */
1852  {
1853  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1854 
1855  SpinLockAcquire(&s->mutex);
1856  off = s->extent;
1857  s->extent += query_len + 1;
1858  s->n_writers++;
1859  if (gc_count)
1860  *gc_count = s->gc_count;
1861  SpinLockRelease(&s->mutex);
1862  }
1863 
1864  *query_offset = off;
1865 
1866  /* Now write the data into the successfully-reserved part of the file */
1867  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDWR | O_CREAT | PG_BINARY);
1868  if (fd < 0)
1869  goto error;
1870 
1871  if (pg_pwrite(fd, query, query_len, off) != query_len)
1872  goto error;
1873  if (pg_pwrite(fd, "\0", 1, off + query_len) != 1)
1874  goto error;
1875 
1876  CloseTransientFile(fd);
1877 
1878  /* Mark our write complete */
1879  {
1880  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1881 
1882  SpinLockAcquire(&s->mutex);
1883  s->n_writers--;
1884  SpinLockRelease(&s->mutex);
1885  }
1886 
1887  return true;
1888 
1889 error:
1890  ereport(LOG,
1892  errmsg("could not write file \"%s\": %m",
1893  PGSS_TEXT_FILE)));
1894 
1895  if (fd >= 0)
1896  CloseTransientFile(fd);
1897 
1898  /* Mark our write complete */
1899  {
1900  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1901 
1902  SpinLockAcquire(&s->mutex);
1903  s->n_writers--;
1904  SpinLockRelease(&s->mutex);
1905  }
1906 
1907  return false;
1908 }
static void error(void)
Definition: sql-dyntest.c:147
#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:1230
ssize_t pg_pwrite(int fd, const void *buf, size_t nbyte, off_t offset)
Definition: pwrite.c:27
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2291
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
int CloseTransientFile(int fd)
Definition: fd.c:2468
#define SpinLockRelease(lock)
Definition: spin.h:64
size_t Size
Definition: c.h:466
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define PGSS_TEXT_FILE

◆ RecordConstLocation()

static void RecordConstLocation ( pgssJumbleState jstate,
int  location 
)
static

Definition at line 2997 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().

2998 {
2999  /* -1 indicates unknown or undefined location */
3000  if (location >= 0)
3001  {
3002  /* enlarge array if needed */
3003  if (jstate->clocations_count >= jstate->clocations_buf_size)
3004  {
3005  jstate->clocations_buf_size *= 2;
3006  jstate->clocations = (pgssLocationLen *)
3007  repalloc(jstate->clocations,
3008  jstate->clocations_buf_size *
3009  sizeof(pgssLocationLen));
3010  }
3011  jstate->clocations[jstate->clocations_count].location = location;
3012  /* initialize lengths to -1 to simplify fill_in_constant_lengths */
3013  jstate->clocations[jstate->clocations_count].length = -1;
3014  jstate->clocations_count++;
3015  }
3016 }
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.