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 2392 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 2394 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:8652
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:70
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1522
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:638
static ProcessUtility_hook_type prev_ProcessUtility
static void pgss_shmem_startup(void)
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c: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:8737
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:8765
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:8626
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 2356 of file pg_stat_statements.c.

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

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

◆ comp_location()

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

Definition at line 3249 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

3250 {
3251  int l = ((const pgssLocationLen *) a)->location;
3252  int r = ((const pgssLocationLen *) b)->location;
3253 
3254  if (l < r)
3255  return -1;
3256  else if (l > r)
3257  return +1;
3258  else
3259  return 0;
3260 }

◆ entry_alloc()

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

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

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

◆ entry_cmp()

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

Definition at line 1738 of file pg_stat_statements.c.

Referenced by entry_dealloc().

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

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

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

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

◆ entry_reset()

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

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

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

◆ fill_in_constant_lengths()

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

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

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

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

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

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

◆ generate_normalized_query()

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

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

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

◆ JumbleExpr()

static void JumbleExpr ( pgssJumbleState jstate,
Node node 
)
static

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

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

◆ JumbleQuery()

static void JumbleQuery ( pgssJumbleState jstate,
Query query 
)
static

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

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

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

◆ JumbleRowMarks()

static void JumbleRowMarks ( pgssJumbleState jstate,
List rowMarks 
)
static

Definition at line 2491 of file pg_stat_statements.c.

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

Referenced by JumbleQuery().

2492 {
2493  ListCell *lc;
2494 
2495  foreach(lc, rowMarks)
2496  {
2497  RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
2498  if (!rowmark->pushedDown)
2499  {
2500  APP_JUMB(rowmark->rti);
2501  APP_JUMB(rowmark->strength);
2502  APP_JUMB(rowmark->waitPolicy);
2503  }
2504  }
2505 }
LockClauseStrength strength
Definition: parsenodes.h:1360
#define lfirst_node(type, lc)
Definition: pg_list.h:193
#define APP_JUMB(item)
LockWaitPolicy waitPolicy
Definition: parsenodes.h:1361

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2029 of file pg_stat_statements.c.

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

Referenced by gc_qtexts(), and pgss_store().

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

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

◆ pg_stat_statements_reset()

Datum pg_stat_statements_reset ( PG_FUNCTION_ARGS  )

Definition at line 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:94
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:110
int stmt_location
Definition: plannodes.h:93
BufferUsage bufusage
Definition: instrument.h:64
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 es_processed
Definition: execnodes.h:553
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:549
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 1673 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1674 {
1675  Size size;
1676 
1677  size = MAXALIGN(sizeof(pgssSharedState));
1678  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1679 
1680  return size;
1681 }
static int pgss_max
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:732
Size add_size(Size s1, Size s2)
Definition: shmem.c:475
size_t Size
Definition: c.h:467
#define MAXALIGN(LEN)
Definition: c.h:692

◆ pgss_post_parse_analyze()

static void pgss_post_parse_analyze ( ParseState pstate,
Query query 
)
static

Definition at line 780 of file pg_stat_statements.c.

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

Referenced by _PG_init().

781 {
782  pgssJumbleState jstate;
783 
785  prev_post_parse_analyze_hook(pstate, query);
786 
787  /* Assert we didn't do this already */
788  Assert(query->queryId == UINT64CONST(0));
789 
790  /* Safety check... */
791  if (!pgss || !pgss_hash || !pgss_enabled())
792  return;
793 
794  /*
795  * Utility statements get queryId zero. We do this even in cases where
796  * the statement contains an optimizable statement for which a queryId
797  * could be derived (such as EXPLAIN or DECLARE CURSOR). For such cases,
798  * runtime control will first go through ProcessUtility and then the
799  * executor, and we don't want the executor hooks to do anything, since we
800  * are already measuring the statement's costs at the utility level.
801  */
802  if (query->utilityStmt)
803  {
804  query->queryId = UINT64CONST(0);
805  return;
806  }
807 
808  /* Set up workspace for query jumbling */
809  jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
810  jstate.jumble_len = 0;
811  jstate.clocations_buf_size = 32;
812  jstate.clocations = (pgssLocationLen *)
813  palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
814  jstate.clocations_count = 0;
815  jstate.highest_extern_param_id = 0;
816 
817  /* Compute query ID and mark the Query node with it */
818  JumbleQuery(&jstate, query);
819  query->queryId =
821 
822  /*
823  * If we are unlucky enough to get a hash of zero, use 1 instead, to
824  * prevent confusion with the utility-statement case.
825  */
826  if (query->queryId == UINT64CONST(0))
827  query->queryId = UINT64CONST(1);
828 
829  /*
830  * If we were able to identify any ignorable constants, we immediately
831  * create a hash table entry for the query, so that we can record the
832  * normalized form of the query string. If there were no such constants,
833  * the normalized string would be the same as the query text anyway, so
834  * there's no need for an early entry.
835  */
836  if (jstate.clocations_count > 0)
837  pgss_store(pstate->p_sourcetext,
838  query->queryId,
839  query->stmt_location,
840  query->stmt_len,
841  0,
842  0,
843  NULL,
844  &jstate);
845 }
int stmt_location
Definition: parsenodes.h:180
Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.c:374
#define pgss_enabled()
unsigned char * jumble
#define JUMBLE_SIZE
Node * utilityStmt
Definition: parsenodes.h:120
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
static pgssSharedState * pgss
pgssLocationLen * clocations
static HTAB * pgss_hash
static void JumbleQuery(pgssJumbleState *jstate, Query *query)
const char * p_sourcetext
Definition: parse_node.h:176
uint64 queryId
Definition: parsenodes.h:116
#define DatumGetUInt64(X)
Definition: postgres.h:634
#define Assert(condition)
Definition: c.h:739
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:94
int duration
Definition: pgbench.c:147
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:376
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
int stmt_location
Definition: plannodes.h:93
Node * utilityStmt
Definition: plannodes.h:90
long shared_blks_dirtied
Definition: instrument.h:23
long temp_blks_read
Definition: instrument.h:29
uint64 pg_strtouint64(const char *str, char **endptr, int base)
Definition: numutils.c:558
#define PG_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:1225
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1335
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:347
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:2242
unsigned int uint32
Definition: c.h:359
#define ereport(elevel, rest)
Definition: elog.h:141
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:643
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:467
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:2441
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:1225
Counters counters
#define LOG
Definition: elog.h:26
#define PG_BINARY_R
Definition: c.h:1224
signed int int32
Definition: c.h:347
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
void pfree(void *pointer)
Definition: mcxt.c:1056
static int pgss_max
static HTAB * pgss_hash
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:372
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:361
bool IsUnderPostmaster
Definition: globals.c:109
static pgssEntry * entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
int errcode_for_file_access(void)
Definition: elog.c:631
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2242
unsigned int uint32
Definition: c.h:359
#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:905
#define PG_VALID_BE_ENCODING(_enc)
Definition: pg_wchar.h:295
LWLockPadded * GetNamedLWLockTranche(const char *tranche_name)
Definition: lwlock.c:552
size_t Size
Definition: c.h:467
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
static void header(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:209
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:2441
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:317
struct pgssHashKey pgssHashKey
#define PGSS_TEXT_FILE
#define ASSUMED_MEDIAN_INIT
static bool pgss_save

◆ pgss_store()

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

Definition at line 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:380
instr_time blk_read_time
Definition: instrument.h:31
int64 shared_blks_dirtied
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
long shared_blks_read
Definition: instrument.h:22
long temp_blks_written
Definition: instrument.h:30
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
Counters counters
int64 temp_blks_read
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
long shared_blks_written
Definition: instrument.h:24
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1056
int64 shared_blks_hit
static HTAB * pgss_hash
#define USAGE_EXEC(duration)
static pgssEntry * entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
static char * generate_normalized_query(pgssJumbleState *jstate, const char *query, int query_loc, int *query_len_p, int encoding)
long shared_blks_dirtied
Definition: instrument.h:23
long temp_blks_read
Definition: instrument.h:29
int64 temp_blks_written
static bool qtext_store(const char *query, int query_len, Size *query_offset, int *gc_count)
int64 shared_blks_written
#define SpinLockRelease(lock)
Definition: spin.h:64
int64 local_blks_read
static bool need_gc_qtexts(void)
int GetDatabaseEncoding(void)
Definition: mbutils.c:1046
static void gc_qtexts(void)
double sum_var_time
Oid MyDatabaseId
Definition: globals.c:85
bool scanner_isspace(char ch)
Definition: scansup.c:220
#define USAGE_INIT
double blk_read_time
#define Assert(condition)
Definition: c.h:739
int64 local_blks_dirtied
instr_time blk_write_time
Definition: instrument.h:32
int64 local_blks_hit
size_t Size
Definition: c.h:467
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
double blk_write_time
int32 encoding
Definition: pg_database.h:41
e
Definition: preproc-init.c:82
long shared_blks_hit
Definition: instrument.h:21
long local_blks_written
Definition: instrument.h:28

◆ qtext_fetch()

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

Definition at line 2006 of file pg_stat_statements.c.

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

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

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

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

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

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

Referenced by pgss_store().

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

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