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 "access/parallel.h"
#include "catalog/pg_authid.h"
#include "common/hashfn.h"
#include "executor/instrument.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "optimizer/planner.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/lwlock.h"
#include "storage/shmem.h"
#include "storage/spin.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/queryjumble.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
Include dependency graph for pg_stat_statements.c:

Go to the source code of this file.

Data Structures

struct  pgssHashKey
 
struct  Counters
 
struct  pgssGlobalStats
 
struct  pgssEntry
 
struct  pgssSharedState
 

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 IS_STICKY(c)   ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
 
#define PGSS_HANDLED_UTILITY(n)
 
#define pgss_enabled(level)
 
#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_V1_8   32
 
#define PG_STAT_STATEMENTS_COLS_V1_9   33
 
#define PG_STAT_STATEMENTS_COLS   33 /* maximum of above */
 
#define PG_STAT_STATEMENTS_INFO_COLS   2
 

Typedefs

typedef enum pgssVersion pgssVersion
 
typedef enum pgssStoreKind pgssStoreKind
 
typedef struct pgssHashKey pgssHashKey
 
typedef struct Counters Counters
 
typedef struct pgssGlobalStats pgssGlobalStats
 
typedef struct pgssEntry pgssEntry
 
typedef struct pgssSharedState pgssSharedState
 

Enumerations

enum  pgssVersion {
  PGSS_V1_0 = 0, PGSS_V1_1, PGSS_V1_2, PGSS_V1_3,
  PGSS_V1_8, PGSS_V1_9
}
 
enum  pgssStoreKind { PGSS_INVALID = -1, PGSS_PLAN = 0, PGSS_EXEC, PGSS_NUMKIND }
 
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_1_8)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_1_9)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_info)
 
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, JumbleState *jstate)
 
static PlannedStmtpgss_planner (Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
 
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, QueryCompletion *qc)
 
static void pgss_store (const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, JumbleState *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 char * generate_normalized_query (JumbleState *jstate, const char *query, int query_loc, int *query_len_p)
 
static void fill_in_constant_lengths (JumbleState *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_9 (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_1_8 (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)
 
Datum pg_stat_statements_info (PG_FUNCTION_ARGS)
 
static int entry_cmp (const void *lhs, const void *rhs)
 

Variables

 PG_MODULE_MAGIC
 
static const uint32 PGSS_FILE_HEADER = 0x20201227
 
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100
 
static int exec_nested_level = 0
 
static int plan_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 planner_hook_type prev_planner_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_track_planning
 
static bool pgss_save
 

Macro Definition Documentation

◆ ASSUMED_LENGTH_INIT

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

Definition at line 100 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 99 of file pg_stat_statements.c.

Referenced by pgss_shmem_startup().

◆ IS_STICKY

#define IS_STICKY (   c)    ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)

◆ PG_STAT_STATEMENTS_COLS

#define PG_STAT_STATEMENTS_COLS   33 /* maximum of above */

Definition at line 1427 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 1421 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 1422 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 1423 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 1424 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_8

#define PG_STAT_STATEMENTS_COLS_V1_8   32

Definition at line 1425 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_COLS_V1_9

#define PG_STAT_STATEMENTS_COLS_V1_9   33

Definition at line 1426 of file pg_stat_statements.c.

Referenced by pg_stat_statements_internal().

◆ PG_STAT_STATEMENTS_INFO_COLS

#define PG_STAT_STATEMENTS_INFO_COLS   2

Definition at line 1818 of file pg_stat_statements.c.

Referenced by pg_stat_statements_info().

◆ PGSS_DUMP_FILE

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"

Definition at line 78 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_enabled

#define pgss_enabled (   level)

◆ PGSS_HANDLED_UTILITY

#define PGSS_HANDLED_UTILITY (   n)
Value:
(!IsA(n, ExecuteStmt) && \
!IsA(n, PrepareStmt) && \
#define IsA(nodeptr, _type_)
Definition: nodes.h:590

Definition at line 110 of file pg_stat_statements.c.

Referenced by pgss_post_parse_analyze(), and pgss_ProcessUtility().

◆ 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 286 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 102 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 103 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 101 of file pg_stat_statements.c.

Referenced by entry_dealloc().

◆ USAGE_EXEC

#define USAGE_EXEC (   duration)    (1.0)

Definition at line 97 of file pg_stat_statements.c.

Referenced by pgss_store().

◆ USAGE_INIT

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

Definition at line 98 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

◆ pgssGlobalStats

◆ pgssHashKey

typedef struct pgssHashKey pgssHashKey

◆ pgssSharedState

◆ pgssStoreKind

◆ pgssVersion

typedef enum pgssVersion pgssVersion

Enumeration Type Documentation

◆ pgssStoreKind

Enumerator
PGSS_INVALID 
PGSS_PLAN 
PGSS_EXEC 
PGSS_NUMKIND 

Definition at line 127 of file pg_stat_statements.c.

128 {
129  PGSS_INVALID = -1,
130 
131  /*
132  * PGSS_PLAN and PGSS_EXEC must be respectively 0 and 1 as they're used to
133  * reference the underlying values in the arrays in the Counters struct,
134  * and this order is required in pg_stat_statements_internal().
135  */
136  PGSS_PLAN = 0,
137  PGSS_EXEC,
138 
139  PGSS_NUMKIND /* Must be last value of this enum */
140 } pgssStoreKind;
pgssStoreKind

◆ PGSSTrackLevel

Enumerator
PGSS_TRACK_NONE 
PGSS_TRACK_TOP 
PGSS_TRACK_ALL 

Definition at line 259 of file pg_stat_statements.c.

260 {
261  PGSS_TRACK_NONE, /* track no statements */
262  PGSS_TRACK_TOP, /* only top level statements */
263  PGSS_TRACK_ALL /* all statements, including nested ones */
PGSSTrackLevel

◆ pgssVersion

Enumerator
PGSS_V1_0 
PGSS_V1_1 
PGSS_V1_2 
PGSS_V1_3 
PGSS_V1_8 
PGSS_V1_9 

Definition at line 117 of file pg_stat_statements.c.

Function Documentation

◆ _PG_fini()

void _PG_fini ( void  )

Definition at line 474 of file pg_stat_statements.c.

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

475 {
476  /* Uninstall hooks. */
485 }
static ProcessUtility_hook_type prev_ProcessUtility
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:76
static ExecutorStart_hook_type prev_ExecutorStart
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:71
static planner_hook_type prev_planner_hook
ExecutorRun_hook_type ExecutorRun_hook
Definition: execMain.c:72
ExecutorEnd_hook_type ExecutorEnd_hook
Definition: execMain.c:74
static ExecutorFinish_hook_type prev_ExecutorFinish
planner_hook_type planner_hook
Definition: planner.c:74
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:53
static shmem_startup_hook_type prev_shmem_startup_hook
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:73
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:56

◆ _PG_init()

void _PG_init ( void  )

Definition at line 359 of file pg_stat_statements.c.

References DefineCustomBoolVariable(), DefineCustomEnumVariable(), DefineCustomIntVariable(), EmitWarningsOnPlaceholders(), EnableQueryId(), 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_planner(), pgss_post_parse_analyze(), pgss_ProcessUtility(), pgss_save, pgss_shmem_startup(), pgss_track, pgss_track_planning, PGSS_TRACK_TOP, pgss_track_utility, planner_hook, post_parse_analyze_hook, prev_ExecutorEnd, prev_ExecutorFinish, prev_ExecutorRun, prev_ExecutorStart, prev_planner_hook, 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.

360 {
361  /*
362  * In order to create our shared memory area, we have to be loaded via
363  * shared_preload_libraries. If not, fall out without hooking into any of
364  * the main system. (We don't throw error here because it seems useful to
365  * allow the pg_stat_statements functions to be created even when the
366  * module isn't active. The functions must protect themselves against
367  * being called then, however.)
368  */
370  return;
371 
372  /*
373  * Inform the postmaster that we want to enable query_id calculation if
374  * compute_query_id is set to auto.
375  */
376  EnableQueryId();
377 
378  /*
379  * Define (or redefine) custom GUC variables.
380  */
381  DefineCustomIntVariable("pg_stat_statements.max",
382  "Sets the maximum number of statements tracked by pg_stat_statements.",
383  NULL,
384  &pgss_max,
385  5000,
386  100,
387  INT_MAX,
389  0,
390  NULL,
391  NULL,
392  NULL);
393 
394  DefineCustomEnumVariable("pg_stat_statements.track",
395  "Selects which statements are tracked by pg_stat_statements.",
396  NULL,
397  &pgss_track,
400  PGC_SUSET,
401  0,
402  NULL,
403  NULL,
404  NULL);
405 
406  DefineCustomBoolVariable("pg_stat_statements.track_utility",
407  "Selects whether utility commands are tracked by pg_stat_statements.",
408  NULL,
410  true,
411  PGC_SUSET,
412  0,
413  NULL,
414  NULL,
415  NULL);
416 
417  DefineCustomBoolVariable("pg_stat_statements.track_planning",
418  "Selects whether planning duration is tracked by pg_stat_statements.",
419  NULL,
421  false,
422  PGC_SUSET,
423  0,
424  NULL,
425  NULL,
426  NULL);
427 
428  DefineCustomBoolVariable("pg_stat_statements.save",
429  "Save pg_stat_statements statistics across server shutdowns.",
430  NULL,
431  &pgss_save,
432  true,
433  PGC_SIGHUP,
434  0,
435  NULL,
436  NULL,
437  NULL);
438 
439  EmitWarningsOnPlaceholders("pg_stat_statements");
440 
441  /*
442  * Request additional shared resources. (These are no-ops if we're not in
443  * the postmaster process.) We'll allocate or attach to the shared
444  * resources in pgss_shmem_startup().
445  */
447  RequestNamedLWLockTranche("pg_stat_statements", 1);
448 
449  /*
450  * Install hooks.
451  */
468 }
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:9159
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:71
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1598
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:700
static ProcessUtility_hook_type prev_ProcessUtility
static void pgss_shmem_startup(void)
static ExecutorRun_hook_type prev_ExecutorRun
static ExecutorEnd_hook_type prev_ExecutorEnd
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:76
static int pgss_track
static ExecutorStart_hook_type prev_ExecutorStart
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
void DefineCustomEnumVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, int bootValue, const struct config_enum_entry *options, GucContext context, int flags, GucEnumCheckHook check_hook, GucEnumAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:9244
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:71
static planner_hook_type prev_planner_hook
static bool pgss_track_planning
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:72
ExecutorEnd_hook_type ExecutorEnd_hook
Definition: execMain.c:74
Definition: guc.h:75
void EmitWarningsOnPlaceholders(const char *className)
Definition: guc.c:9272
static ExecutorFinish_hook_type prev_ExecutorFinish
planner_hook_type planner_hook
Definition: planner.c:74
Definition: guc.h:72
static PlannedStmt * pgss_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
static Size pgss_memsize(void)
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:53
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
static void pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
static bool pgss_track_utility
void EnableQueryId(void)
Definition: queryjumble.c:150
static shmem_startup_hook_type prev_shmem_startup_hook
static void pgss_ExecutorFinish(QueryDesc *queryDesc)
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:73
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:9133
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:56
static const struct config_enum_entry track_options[]
static bool pgss_save

◆ comp_location()

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

Definition at line 2797 of file pg_stat_statements.c.

Referenced by fill_in_constant_lengths().

2798 {
2799  int l = ((const LocationLen *) a)->location;
2800  int r = ((const LocationLen *) b)->location;
2801 
2802  if (l < r)
2803  return -1;
2804  else if (l > r)
2805  return +1;
2806  else
2807  return 0;
2808 }

◆ entry_alloc()

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

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

1892 {
1893  pgssEntry *entry;
1894  bool found;
1895 
1896  /* Make space if needed */
1898  entry_dealloc();
1899 
1900  /* Find or create an entry with desired hash code */
1901  entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
1902 
1903  if (!found)
1904  {
1905  /* New entry, initialize it */
1906 
1907  /* reset the statistics */
1908  memset(&entry->counters, 0, sizeof(Counters));
1909  /* set the appropriate initial usage count */
1910  entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
1911  /* re-initialize the mutex each time ... we assume no one using it */
1912  SpinLockInit(&entry->mutex);
1913  /* ... and don't forget the query text metadata */
1914  Assert(query_len >= 0);
1915  entry->query_offset = query_offset;
1916  entry->query_len = query_len;
1917  entry->encoding = encoding;
1918  }
1919 
1920  return entry;
1921 }
#define SpinLockInit(lock)
Definition: spin.h:60
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
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:804
int32 encoding
Definition: pg_database.h:41

◆ entry_cmp()

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

Definition at line 1927 of file pg_stat_statements.c.

Referenced by entry_dealloc().

1928 {
1929  double l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
1930  double r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
1931 
1932  if (l_usage < r_usage)
1933  return -1;
1934  else if (l_usage > r_usage)
1935  return +1;
1936  else
1937  return 0;
1938 }

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

Definition at line 1946 of file pg_stat_statements.c.

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

Referenced by entry_alloc().

1947 {
1948  HASH_SEQ_STATUS hash_seq;
1949  pgssEntry **entries;
1950  pgssEntry *entry;
1951  int nvictims;
1952  int i;
1953  Size tottextlen;
1954  int nvalidtexts;
1955 
1956  /*
1957  * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
1958  * While we're scanning the table, apply the decay factor to the usage
1959  * values, and update the mean query length.
1960  *
1961  * Note that the mean query length is almost immediately obsolete, since
1962  * we compute it before not after discarding the least-used entries.
1963  * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
1964  * making two passes to get a more current result. Likewise, the new
1965  * cur_median_usage includes the entries we're about to zap.
1966  */
1967 
1968  entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
1969 
1970  i = 0;
1971  tottextlen = 0;
1972  nvalidtexts = 0;
1973 
1974  hash_seq_init(&hash_seq, pgss_hash);
1975  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1976  {
1977  entries[i++] = entry;
1978  /* "Sticky" entries get a different usage decay rate. */
1979  if (IS_STICKY(entry->counters))
1981  else
1983  /* In the mean length computation, ignore dropped texts. */
1984  if (entry->query_len >= 0)
1985  {
1986  tottextlen += entry->query_len + 1;
1987  nvalidtexts++;
1988  }
1989  }
1990 
1991  /* Sort into increasing order by usage */
1992  qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
1993 
1994  /* Record the (approximate) median usage */
1995  if (i > 0)
1996  pgss->cur_median_usage = entries[i / 2]->counters.usage;
1997  /* Record the mean query length */
1998  if (nvalidtexts > 0)
1999  pgss->mean_query_len = tottextlen / nvalidtexts;
2000  else
2002 
2003  /* Now zap an appropriate fraction of lowest-usage entries */
2004  nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
2005  nvictims = Min(nvictims, i);
2006 
2007  for (i = 0; i < nvictims; i++)
2008  {
2009  hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
2010  }
2011 
2012  pfree(entries);
2013 
2014  /* Increment the number of times entries are deallocated */
2015  {
2016  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2017 
2018  SpinLockAcquire(&s->mutex);
2019  s->stats.dealloc += 1;
2020  SpinLockRelease(&s->mutex);
2021  }
2022 }
#define USAGE_DEALLOC_PERCENT
#define Min(x, y)
Definition: c.h:986
static int entry_cmp(const void *lhs, const void *rhs)
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
Counters counters
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1169
static HTAB * pgss_hash
#define IS_STICKY(c)
#define STICKY_DECREASE_FACTOR
#define USAGE_DECREASE_FACTOR
pgssGlobalStats stats
#define SpinLockRelease(lock)
Definition: spin.h:64
#define Max(x, y)
Definition: c.h:980
size_t Size
Definition: c.h:540
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
void * palloc(Size size)
Definition: mcxt.c:1062
int i
#define qsort(a, b, c, d)
Definition: port.h:504

◆ entry_reset()

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

Definition at line 2455 of file pg_stat_statements.c.

References AllocateFile(), pgssHashKey::dbid, pgssGlobalStats::dealloc, ereport, errcode(), errcode_for_file_access(), errmsg(), ERROR, pgssSharedState::extent, FreeFile(), ftruncate, GetCurrentTimestamp(), 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(), pgssSharedState::mutex, PG_BINARY_W, PGSS_TEXT_FILE, pgssHashKey::queryid, record_gc_qtexts, SpinLockAcquire, SpinLockRelease, pgssSharedState::stats, pgssGlobalStats::stats_reset, pgssHashKey::toplevel, and pgssHashKey::userid.

Referenced by pg_stat_statements_reset(), and pg_stat_statements_reset_1_7().

2456 {
2457  HASH_SEQ_STATUS hash_seq;
2458  pgssEntry *entry;
2459  FILE *qfile;
2460  long num_entries;
2461  long num_remove = 0;
2462  pgssHashKey key;
2463 
2464  if (!pgss || !pgss_hash)
2465  ereport(ERROR,
2466  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2467  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2468 
2470  num_entries = hash_get_num_entries(pgss_hash);
2471 
2472  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2473  {
2474  /* If all the parameters are available, use the fast path. */
2475  memset(&key, 0, sizeof(pgssHashKey));
2476  key.userid = userid;
2477  key.dbid = dbid;
2478  key.queryid = queryid;
2479 
2480  /* Remove the key if it exists, starting with the top-level entry */
2481  key.toplevel = false;
2482  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2483  if (entry) /* found */
2484  num_remove++;
2485 
2486  /* Also remove entries for top level statements */
2487  key.toplevel = true;
2488 
2489  /* Remove the key if exists */
2490  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2491  if (entry) /* found */
2492  num_remove++;
2493  }
2494  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2495  {
2496  /* Remove entries corresponding to valid parameters. */
2497  hash_seq_init(&hash_seq, pgss_hash);
2498  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2499  {
2500  if ((!userid || entry->key.userid == userid) &&
2501  (!dbid || entry->key.dbid == dbid) &&
2502  (!queryid || entry->key.queryid == queryid))
2503  {
2504  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2505  num_remove++;
2506  }
2507  }
2508  }
2509  else
2510  {
2511  /* Remove all entries. */
2512  hash_seq_init(&hash_seq, pgss_hash);
2513  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2514  {
2515  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2516  num_remove++;
2517  }
2518  }
2519 
2520  /* All entries are removed? */
2521  if (num_entries != num_remove)
2522  goto release_lock;
2523 
2524  /*
2525  * Reset global statistics for pg_stat_statements since all entries are
2526  * removed.
2527  */
2528  {
2529  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2530  TimestampTz stats_reset = GetCurrentTimestamp();
2531 
2532  SpinLockAcquire(&s->mutex);
2533  s->stats.dealloc = 0;
2534  s->stats.stats_reset = stats_reset;
2535  SpinLockRelease(&s->mutex);
2536  }
2537 
2538  /*
2539  * Write new empty query file, perhaps even creating a new one to recover
2540  * if the file was missing.
2541  */
2543  if (qfile == NULL)
2544  {
2545  ereport(LOG,
2547  errmsg("could not create file \"%s\": %m",
2548  PGSS_TEXT_FILE)));
2549  goto done;
2550  }
2551 
2552  /* If ftruncate fails, log it, but it's not a fatal problem */
2553  if (ftruncate(fileno(qfile), 0) != 0)
2554  ereport(LOG,
2556  errmsg("could not truncate file \"%s\": %m",
2557  PGSS_TEXT_FILE)));
2558 
2559  FreeFile(qfile);
2560 
2561 done:
2562  pgss->extent = 0;
2563  /* This counts as a query text garbage collection for our purposes */
2564  record_gc_qtexts();
2565 
2566 release_lock:
2568 }
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1580
int64 TimestampTz
Definition: timestamp.h:39
int errcode(int sqlerrcode)
Definition: elog.c:698
#define PG_BINARY_W
Definition: c.h:1274
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
#define LOG
Definition: elog.h:26
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1816
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
static HTAB * pgss_hash
#define ERROR
Definition: elog.h:46
int errcode_for_file_access(void)
Definition: elog.c:721
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2373
pgssGlobalStats stats
#define SpinLockRelease(lock)
Definition: spin.h:64
pgssHashKey key
#define ereport(elevel,...)
Definition: elog.h:157
TimestampTz stats_reset
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1203
#define record_gc_qtexts()
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
int FreeFile(FILE *file)
Definition: fd.c:2572
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define PGSS_TEXT_FILE
#define ftruncate(a, b)
Definition: win32_port.h:65

◆ fill_in_constant_lengths()

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

Definition at line 2697 of file pg_stat_statements.c.

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

Referenced by generate_normalized_query().

2699 {
2700  LocationLen *locs;
2702  core_yy_extra_type yyextra;
2703  core_YYSTYPE yylval;
2704  YYLTYPE yylloc;
2705  int last_loc = -1;
2706  int i;
2707 
2708  /*
2709  * Sort the records by location so that we can process them in order while
2710  * scanning the query text.
2711  */
2712  if (jstate->clocations_count > 1)
2713  qsort(jstate->clocations, jstate->clocations_count,
2714  sizeof(LocationLen), comp_location);
2715  locs = jstate->clocations;
2716 
2717  /* initialize the flex scanner --- should match raw_parser() */
2718  yyscanner = scanner_init(query,
2719  &yyextra,
2720  &ScanKeywords,
2722 
2723  /* we don't want to re-emit any escape string warnings */
2724  yyextra.escape_string_warning = false;
2725 
2726  /* Search for each constant, in sequence */
2727  for (i = 0; i < jstate->clocations_count; i++)
2728  {
2729  int loc = locs[i].location;
2730  int tok;
2731 
2732  /* Adjust recorded location if we're dealing with partial string */
2733  loc -= query_loc;
2734 
2735  Assert(loc >= 0);
2736 
2737  if (loc <= last_loc)
2738  continue; /* Duplicate constant, ignore */
2739 
2740  /* Lex tokens until we find the desired constant */
2741  for (;;)
2742  {
2743  tok = core_yylex(&yylval, &yylloc, yyscanner);
2744 
2745  /* We should not hit end-of-string, but if we do, behave sanely */
2746  if (tok == 0)
2747  break; /* out of inner for-loop */
2748 
2749  /*
2750  * We should find the token position exactly, but if we somehow
2751  * run past it, work with that.
2752  */
2753  if (yylloc >= loc)
2754  {
2755  if (query[loc] == '-')
2756  {
2757  /*
2758  * It's a negative value - this is the one and only case
2759  * where we replace more than a single token.
2760  *
2761  * Do not compensate for the core system's special-case
2762  * adjustment of location to that of the leading '-'
2763  * operator in the event of a negative constant. It is
2764  * also useful for our purposes to start from the minus
2765  * symbol. In this way, queries like "select * from foo
2766  * where bar = 1" and "select * from foo where bar = -2"
2767  * will have identical normalized query strings.
2768  */
2769  tok = core_yylex(&yylval, &yylloc, yyscanner);
2770  if (tok == 0)
2771  break; /* out of inner for-loop */
2772  }
2773 
2774  /*
2775  * We now rely on the assumption that flex has placed a zero
2776  * byte after the text of the current token in scanbuf.
2777  */
2778  locs[i].length = strlen(yyextra.scanbuf + loc);
2779  break; /* out of inner for-loop */
2780  }
2781  }
2782 
2783  /* If we hit end-of-string, give up, leaving remaining lengths -1 */
2784  if (tok == 0)
2785  break;
2786 
2787  last_loc = loc;
2788  }
2789 
2790  scanner_finish(yyscanner);
2791 }
void * core_yyscan_t
Definition: scanner.h:121
PGDLLIMPORT const uint16 ScanKeywordTokens[]
char * scanbuf
Definition: scanner.h:72
int clocations_count
Definition: queryjumble.h:49
PGDLLIMPORT const ScanKeywordList ScanKeywords
#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:804
static core_yyscan_t yyscanner
Definition: pl_scanner.c:106
LocationLen * clocations
Definition: queryjumble.h:43
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:504

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

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

2272 {
2273  char *qbuffer;
2274  Size qbuffer_size;
2275  FILE *qfile = NULL;
2276  HASH_SEQ_STATUS hash_seq;
2277  pgssEntry *entry;
2278  Size extent;
2279  int nentries;
2280 
2281  /*
2282  * When called from pgss_store, some other session might have proceeded
2283  * with garbage collection in the no-lock-held interim of lock strength
2284  * escalation. Check once more that this is actually necessary.
2285  */
2286  if (!need_gc_qtexts())
2287  return;
2288 
2289  /*
2290  * Load the old texts file. If we fail (out of memory, for instance),
2291  * invalidate query texts. Hopefully this is rare. It might seem better
2292  * to leave things alone on an OOM failure, but the problem is that the
2293  * file is only going to get bigger; hoping for a future non-OOM result is
2294  * risky and can easily lead to complete denial of service.
2295  */
2296  qbuffer = qtext_load_file(&qbuffer_size);
2297  if (qbuffer == NULL)
2298  goto gc_fail;
2299 
2300  /*
2301  * We overwrite the query texts file in place, so as to reduce the risk of
2302  * an out-of-disk-space failure. Since the file is guaranteed not to get
2303  * larger, this should always work on traditional filesystems; though we
2304  * could still lose on copy-on-write filesystems.
2305  */
2307  if (qfile == NULL)
2308  {
2309  ereport(LOG,
2311  errmsg("could not write file \"%s\": %m",
2312  PGSS_TEXT_FILE)));
2313  goto gc_fail;
2314  }
2315 
2316  extent = 0;
2317  nentries = 0;
2318 
2319  hash_seq_init(&hash_seq, pgss_hash);
2320  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2321  {
2322  int query_len = entry->query_len;
2323  char *qry = qtext_fetch(entry->query_offset,
2324  query_len,
2325  qbuffer,
2326  qbuffer_size);
2327 
2328  if (qry == NULL)
2329  {
2330  /* Trouble ... drop the text */
2331  entry->query_offset = 0;
2332  entry->query_len = -1;
2333  /* entry will not be counted in mean query length computation */
2334  continue;
2335  }
2336 
2337  if (fwrite(qry, 1, query_len + 1, qfile) != query_len + 1)
2338  {
2339  ereport(LOG,
2341  errmsg("could not write file \"%s\": %m",
2342  PGSS_TEXT_FILE)));
2343  hash_seq_term(&hash_seq);
2344  goto gc_fail;
2345  }
2346 
2347  entry->query_offset = extent;
2348  extent += query_len + 1;
2349  nentries++;
2350  }
2351 
2352  /*
2353  * Truncate away any now-unused space. If this fails for some odd reason,
2354  * we log it, but there's no need to fail.
2355  */
2356  if (ftruncate(fileno(qfile), extent) != 0)
2357  ereport(LOG,
2359  errmsg("could not truncate file \"%s\": %m",
2360  PGSS_TEXT_FILE)));
2361 
2362  if (FreeFile(qfile))
2363  {
2364  ereport(LOG,
2366  errmsg("could not write file \"%s\": %m",
2367  PGSS_TEXT_FILE)));
2368  qfile = NULL;
2369  goto gc_fail;
2370  }
2371 
2372  elog(DEBUG1, "pgss gc of queries file shrunk size from %zu to %zu",
2373  pgss->extent, extent);
2374 
2375  /* Reset the shared extent pointer */
2376  pgss->extent = extent;
2377 
2378  /*
2379  * Also update the mean query length, to be sure that need_gc_qtexts()
2380  * won't still think we have a problem.
2381  */
2382  if (nentries > 0)
2383  pgss->mean_query_len = extent / nentries;
2384  else
2386 
2387  free(qbuffer);
2388 
2389  /*
2390  * OK, count a garbage collection cycle. (Note: even though we have
2391  * exclusive lock on pgss->lock, we must take pgss->mutex for this, since
2392  * other processes may examine gc_count while holding only the mutex.
2393  * Also, we have to advance the count *after* we've rewritten the file,
2394  * else other processes might not realize they read a stale file.)
2395  */
2396  record_gc_qtexts();
2397 
2398  return;
2399 
2400 gc_fail:
2401  /* clean up resources */
2402  if (qfile)
2403  FreeFile(qfile);
2404  if (qbuffer)
2405  free(qbuffer);
2406 
2407  /*
2408  * Since the contents of the external file are now uncertain, mark all
2409  * hashtable entries as having invalid texts.
2410  */
2411  hash_seq_init(&hash_seq, pgss_hash);
2412  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2413  {
2414  entry->query_offset = 0;
2415  entry->query_len = -1;
2416  }
2417 
2418  /*
2419  * Destroy the query text file and create a new, empty one
2420  */
2421  (void) unlink(PGSS_TEXT_FILE);
2423  if (qfile == NULL)
2424  ereport(LOG,
2426  errmsg("could not recreate file \"%s\": %m",
2427  PGSS_TEXT_FILE)));
2428  else
2429  FreeFile(qfile);
2430 
2431  /* Reset the shared extent pointer */
2432  pgss->extent = 0;
2433 
2434  /* Reset mean_query_len to match the new state */
2436 
2437  /*
2438  * Bump the GC count even though we failed.
2439  *
2440  * This is needed to make concurrent readers of file without any lock on
2441  * pgss->lock notice existence of new version of file. Once readers
2442  * subsequently observe a change in GC count with pgss->lock held, that
2443  * forces a safe reopen of file. Writers also require that we bump here,
2444  * of course. (As required by locking protocol, readers and writers don't
2445  * trust earlier file contents until gc_count is found unchanged after
2446  * pgss->lock acquired in shared or exclusive mode respectively.)
2447  */
2448  record_gc_qtexts();
2449 }
#define DEBUG1
Definition: elog.h:25
#define PG_BINARY_W
Definition: c.h:1274
#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:721
static char * qtext_load_file(Size *buffer_size)
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2373
static bool need_gc_qtexts(void)
#define ereport(elevel,...)
Definition: elog.h:157
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:540
#define record_gc_qtexts()
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
int FreeFile(FILE *file)
Definition: fd.c:2572
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
#define PGSS_TEXT_FILE
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1512
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)
#define ftruncate(a, b)
Definition: win32_port.h:65

◆ generate_normalized_query()

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

Definition at line 2590 of file pg_stat_statements.c.

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

Referenced by pgss_store().

2592 {
2593  char *norm_query;
2594  int query_len = *query_len_p;
2595  int i,
2596  norm_query_buflen, /* Space allowed for norm_query */
2597  len_to_wrt, /* Length (in bytes) to write */
2598  quer_loc = 0, /* Source query byte location */
2599  n_quer_loc = 0, /* Normalized query byte location */
2600  last_off = 0, /* Offset from start for previous tok */
2601  last_tok_len = 0; /* Length (in bytes) of that tok */
2602 
2603  /*
2604  * Get constants' lengths (core system only gives us locations). Note
2605  * this also ensures the items are sorted by location.
2606  */
2607  fill_in_constant_lengths(jstate, query, query_loc);
2608 
2609  /*
2610  * Allow for $n symbols to be longer than the constants they replace.
2611  * Constants must take at least one byte in text form, while a $n symbol
2612  * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
2613  * could refine that limit based on the max value of n for the current
2614  * query, but it hardly seems worth any extra effort to do so.
2615  */
2616  norm_query_buflen = query_len + jstate->clocations_count * 10;
2617 
2618  /* Allocate result buffer */
2619  norm_query = palloc(norm_query_buflen + 1);
2620 
2621  for (i = 0; i < jstate->clocations_count; i++)
2622  {
2623  int off, /* Offset from start for cur tok */
2624  tok_len; /* Length (in bytes) of that tok */
2625 
2626  off = jstate->clocations[i].location;
2627  /* Adjust recorded location if we're dealing with partial string */
2628  off -= query_loc;
2629 
2630  tok_len = jstate->clocations[i].length;
2631 
2632  if (tok_len < 0)
2633  continue; /* ignore any duplicates */
2634 
2635  /* Copy next chunk (what precedes the next constant) */
2636  len_to_wrt = off - last_off;
2637  len_to_wrt -= last_tok_len;
2638 
2639  Assert(len_to_wrt >= 0);
2640  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
2641  n_quer_loc += len_to_wrt;
2642 
2643  /* And insert a param symbol in place of the constant token */
2644  n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
2645  i + 1 + jstate->highest_extern_param_id);
2646 
2647  quer_loc = off + tok_len;
2648  last_off = off;
2649  last_tok_len = tok_len;
2650  }
2651 
2652  /*
2653  * We've copied up until the last ignorable constant. Copy over the
2654  * remaining bytes of the original query string.
2655  */
2656  len_to_wrt = query_len - quer_loc;
2657 
2658  Assert(len_to_wrt >= 0);
2659  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
2660  n_quer_loc += len_to_wrt;
2661 
2662  Assert(n_quer_loc <= norm_query_buflen);
2663  norm_query[n_quer_loc] = '\0';
2664 
2665  *query_len_p = n_quer_loc;
2666  return norm_query;
2667 }
int clocations_count
Definition: queryjumble.h:49
#define sprintf
Definition: port.h:218
int highest_extern_param_id
Definition: queryjumble.h:52
#define Assert(condition)
Definition: c.h:804
static void fill_in_constant_lengths(JumbleState *jstate, const char *query, int query_loc)
LocationLen * clocations
Definition: queryjumble.h:43
void * palloc(Size size)
Definition: mcxt.c:1062
int i

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2224 of file pg_stat_statements.c.

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

Referenced by gc_qtexts(), and pgss_store().

2225 {
2226  Size extent;
2227 
2228  /* Read shared extent pointer */
2229  {
2230  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2231 
2232  SpinLockAcquire(&s->mutex);
2233  extent = s->extent;
2234  SpinLockRelease(&s->mutex);
2235  }
2236 
2237  /* Don't proceed if file does not exceed 512 bytes per possible entry */
2238  if (extent < 512 * pgss_max)
2239  return false;
2240 
2241  /*
2242  * Don't proceed if file is less than about 50% bloat. Nothing can or
2243  * should be done in the event of unusually large query texts accounting
2244  * for file's large size. We go to the trouble of maintaining the mean
2245  * query length in order to prevent garbage collection from thrashing
2246  * uselessly.
2247  */
2248  if (extent < pgss->mean_query_len * pgss_max * 2)
2249  return false;
2250 
2251  return true;
2252 }
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:540

◆ PG_FUNCTION_INFO_V1() [1/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset  )

◆ PG_FUNCTION_INFO_V1() [2/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset_1_7  )

◆ PG_FUNCTION_INFO_V1() [3/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_2  )

◆ PG_FUNCTION_INFO_V1() [4/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_3  )

◆ PG_FUNCTION_INFO_V1() [5/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_8  )

◆ PG_FUNCTION_INFO_V1() [6/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_9  )

◆ PG_FUNCTION_INFO_V1() [7/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements  )

◆ PG_FUNCTION_INFO_V1() [8/8]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_info  )

◆ pg_stat_statements()

Datum pg_stat_statements ( PG_FUNCTION_ARGS  )

Definition at line 1484 of file pg_stat_statements.c.

References pg_stat_statements_internal(), and PGSS_V1_0.

1485 {
1486  /* If it's really API 1.1, we'll figure that out below */
1487  pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
1488 
1489  return (Datum) 0;
1490 }
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:411

◆ pg_stat_statements_1_2()

Datum pg_stat_statements_1_2 ( PG_FUNCTION_ARGS  )

Definition at line 1470 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

1471 {
1472  bool showtext = PG_GETARG_BOOL(0);
1473 
1474  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1475 
1476  return (Datum) 0;
1477 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:411

◆ pg_stat_statements_1_3()

Datum pg_stat_statements_1_3 ( PG_FUNCTION_ARGS  )

Definition at line 1460 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

1461 {
1462  bool showtext = PG_GETARG_BOOL(0);
1463 
1464  pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
1465 
1466  return (Datum) 0;
1467 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:411

◆ pg_stat_statements_1_8()

Datum pg_stat_statements_1_8 ( PG_FUNCTION_ARGS  )

Definition at line 1450 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_8.

1451 {
1452  bool showtext = PG_GETARG_BOOL(0);
1453 
1454  pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
1455 
1456  return (Datum) 0;
1457 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:411

◆ pg_stat_statements_1_9()

Datum pg_stat_statements_1_9 ( PG_FUNCTION_ARGS  )

Definition at line 1440 of file pg_stat_statements.c.

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_9.

1441 {
1442  bool showtext = PG_GETARG_BOOL(0);
1443 
1444  pg_stat_statements_internal(fcinfo, PGSS_V1_9, showtext);
1445 
1446  return (Datum) 0;
1447 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:411

◆ pg_stat_statements_info()

Datum pg_stat_statements_info ( PG_FUNCTION_ARGS  )

Definition at line 1824 of file pg_stat_statements.c.

References pgssGlobalStats::dealloc, elog, ereport, errcode(), errmsg(), ERROR, get_call_result_type(), heap_form_tuple(), HeapTupleGetDatum, Int64GetDatum(), MemSet, pgssSharedState::mutex, PG_RETURN_DATUM, PG_STAT_STATEMENTS_INFO_COLS, SpinLockAcquire, SpinLockRelease, pgssSharedState::stats, pgssGlobalStats::stats_reset, TimestampTzGetDatum, TYPEFUNC_COMPOSITE, and values.

1825 {
1826  pgssGlobalStats stats;
1827  TupleDesc tupdesc;
1829  bool nulls[PG_STAT_STATEMENTS_INFO_COLS];
1830 
1831  if (!pgss || !pgss_hash)
1832  ereport(ERROR,
1833  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1834  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1835 
1836  /* Build a tuple descriptor for our result type */
1837  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1838  elog(ERROR, "return type must be a row type");
1839 
1840  MemSet(values, 0, sizeof(values));
1841  MemSet(nulls, 0, sizeof(nulls));
1842 
1843  /* Read global statistics for pg_stat_statements */
1844  {
1845  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1846 
1847  SpinLockAcquire(&s->mutex);
1848  stats = s->stats;
1849  SpinLockRelease(&s->mutex);
1850  }
1851 
1852  values[0] = Int64GetDatum(stats.dealloc);
1853  values[1] = TimestampTzGetDatum(stats.stats_reset);
1854 
1855  PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
1856 }
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:207
#define PG_STAT_STATEMENTS_INFO_COLS
int errcode(int sqlerrcode)
Definition: elog.c:698
#define MemSet(start, val, len)
Definition: c.h:1008
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
static HTAB * pgss_hash
#define ERROR
Definition: elog.h:46
#define TimestampTzGetDatum(X)
Definition: timestamp.h:32
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1697
pgssGlobalStats stats
#define SpinLockRelease(lock)
Definition: spin.h:64
uintptr_t Datum
Definition: postgres.h:411
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define ereport(elevel,...)
Definition: elog.h:157
TimestampTz stats_reset
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:221
static Datum values[MAXATTR]
Definition: bootstrap.c:166
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232

◆ pg_stat_statements_internal()

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

Definition at line 1494 of file pg_stat_statements.c.

References ReturnSetInfo::allowedModes, Assert, Counters::blk_read_time, Counters::blk_write_time, BoolGetDatum, buf, Counters::calls, pgssEntry::counters, CStringGetDatum, CStringGetTextDatum, pgssHashKey::dbid, DirectFunctionCall3, 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, Int32GetDatum, Int64GetDatumFast, is_member_of_role(), IS_STICKY, 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, numeric_in(), 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, PG_STAT_STATEMENTS_COLS_V1_8, PG_STAT_STATEMENTS_COLS_V1_9, PGSS_EXEC, PGSS_NUMKIND, PGSS_V1_0, PGSS_V1_1, PGSS_V1_2, PGSS_V1_3, PGSS_V1_8, PGSS_V1_9, 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, snprintf, SpinLockAcquire, SpinLockRelease, Counters::sum_var_time, Counters::temp_blks_read, Counters::temp_blks_written, pgssHashKey::toplevel, Counters::total_time, tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), TYPEFUNC_COMPOSITE, UINT64_FORMAT, pgssHashKey::userid, values, Counters::wal_bytes, Counters::wal_fpi, Counters::wal_records, and work_mem.

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

1497 {
1498  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1499  TupleDesc tupdesc;
1500  Tuplestorestate *tupstore;
1501  MemoryContext per_query_ctx;
1502  MemoryContext oldcontext;
1503  Oid userid = GetUserId();
1504  bool is_allowed_role = false;
1505  char *qbuffer = NULL;
1506  Size qbuffer_size = 0;
1507  Size extent = 0;
1508  int gc_count = 0;
1509  HASH_SEQ_STATUS hash_seq;
1510  pgssEntry *entry;
1511 
1512  /* Superusers or members of pg_read_all_stats members are allowed */
1513  is_allowed_role = is_member_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS);
1514 
1515  /* hash table must exist already */
1516  if (!pgss || !pgss_hash)
1517  ereport(ERROR,
1518  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1519  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1520 
1521  /* check to see if caller supports us returning a tuplestore */
1522  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1523  ereport(ERROR,
1524  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1525  errmsg("set-valued function called in context that cannot accept a set")));
1526  if (!(rsinfo->allowedModes & SFRM_Materialize))
1527  ereport(ERROR,
1528  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1529  errmsg("materialize mode required, but it is not allowed in this context")));
1530 
1531  /* Switch into long-lived context to construct returned data structures */
1532  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1533  oldcontext = MemoryContextSwitchTo(per_query_ctx);
1534 
1535  /* Build a tuple descriptor for our result type */
1536  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1537  elog(ERROR, "return type must be a row type");
1538 
1539  /*
1540  * Check we have the expected number of output arguments. Aside from
1541  * being a good safety check, we need a kluge here to detect API version
1542  * 1.1, which was wedged into the code in an ill-considered way.
1543  */
1544  switch (tupdesc->natts)
1545  {
1547  if (api_version != PGSS_V1_0)
1548  elog(ERROR, "incorrect number of output arguments");
1549  break;
1551  /* pg_stat_statements() should have told us 1.0 */
1552  if (api_version != PGSS_V1_0)
1553  elog(ERROR, "incorrect number of output arguments");
1554  api_version = PGSS_V1_1;
1555  break;
1557  if (api_version != PGSS_V1_2)
1558  elog(ERROR, "incorrect number of output arguments");
1559  break;
1561  if (api_version != PGSS_V1_3)
1562  elog(ERROR, "incorrect number of output arguments");
1563  break;
1565  if (api_version != PGSS_V1_8)
1566  elog(ERROR, "incorrect number of output arguments");
1567  break;
1569  if (api_version != PGSS_V1_9)
1570  elog(ERROR, "incorrect number of output arguments");
1571  break;
1572  default:
1573  elog(ERROR, "incorrect number of output arguments");
1574  }
1575 
1576  tupstore = tuplestore_begin_heap(true, false, work_mem);
1577  rsinfo->returnMode = SFRM_Materialize;
1578  rsinfo->setResult = tupstore;
1579  rsinfo->setDesc = tupdesc;
1580 
1581  MemoryContextSwitchTo(oldcontext);
1582 
1583  /*
1584  * We'd like to load the query text file (if needed) while not holding any
1585  * lock on pgss->lock. In the worst case we'll have to do this again
1586  * after we have the lock, but it's unlikely enough to make this a win
1587  * despite occasional duplicated work. We need to reload if anybody
1588  * writes to the file (either a retail qtext_store(), or a garbage
1589  * collection) between this point and where we've gotten shared lock. If
1590  * a qtext_store is actually in progress when we look, we might as well
1591  * skip the speculative load entirely.
1592  */
1593  if (showtext)
1594  {
1595  int n_writers;
1596 
1597  /* Take the mutex so we can examine variables */
1598  {
1599  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1600 
1601  SpinLockAcquire(&s->mutex);
1602  extent = s->extent;
1603  n_writers = s->n_writers;
1604  gc_count = s->gc_count;
1605  SpinLockRelease(&s->mutex);
1606  }
1607 
1608  /* No point in loading file now if there are active writers */
1609  if (n_writers == 0)
1610  qbuffer = qtext_load_file(&qbuffer_size);
1611  }
1612 
1613  /*
1614  * Get shared lock, load or reload the query text file if we must, and
1615  * iterate over the hashtable entries.
1616  *
1617  * With a large hash table, we might be holding the lock rather longer
1618  * than one could wish. However, this only blocks creation of new hash
1619  * table entries, and the larger the hash table the less likely that is to
1620  * be needed. So we can hope this is okay. Perhaps someday we'll decide
1621  * we need to partition the hash table to limit the time spent holding any
1622  * one lock.
1623  */
1625 
1626  if (showtext)
1627  {
1628  /*
1629  * Here it is safe to examine extent and gc_count without taking the
1630  * mutex. Note that although other processes might change
1631  * pgss->extent just after we look at it, the strings they then write
1632  * into the file cannot yet be referenced in the hashtable, so we
1633  * don't care whether we see them or not.
1634  *
1635  * If qtext_load_file fails, we just press on; we'll return NULL for
1636  * every query text.
1637  */
1638  if (qbuffer == NULL ||
1639  pgss->extent != extent ||
1640  pgss->gc_count != gc_count)
1641  {
1642  if (qbuffer)
1643  free(qbuffer);
1644  qbuffer = qtext_load_file(&qbuffer_size);
1645  }
1646  }
1647 
1648  hash_seq_init(&hash_seq, pgss_hash);
1649  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1650  {
1652  bool nulls[PG_STAT_STATEMENTS_COLS];
1653  int i = 0;
1654  Counters tmp;
1655  double stddev;
1656  int64 queryid = entry->key.queryid;
1657 
1658  memset(values, 0, sizeof(values));
1659  memset(nulls, 0, sizeof(nulls));
1660 
1661  values[i++] = ObjectIdGetDatum(entry->key.userid);
1662  values[i++] = ObjectIdGetDatum(entry->key.dbid);
1663  if (api_version >= PGSS_V1_9)
1664  values[i++] = BoolGetDatum(entry->key.toplevel);
1665 
1666  if (is_allowed_role || entry->key.userid == userid)
1667  {
1668  if (api_version >= PGSS_V1_2)
1669  values[i++] = Int64GetDatumFast(queryid);
1670 
1671  if (showtext)
1672  {
1673  char *qstr = qtext_fetch(entry->query_offset,
1674  entry->query_len,
1675  qbuffer,
1676  qbuffer_size);
1677 
1678  if (qstr)
1679  {
1680  char *enc;
1681 
1682  enc = pg_any_to_server(qstr,
1683  entry->query_len,
1684  entry->encoding);
1685 
1686  values[i++] = CStringGetTextDatum(enc);
1687 
1688  if (enc != qstr)
1689  pfree(enc);
1690  }
1691  else
1692  {
1693  /* Just return a null if we fail to find the text */
1694  nulls[i++] = true;
1695  }
1696  }
1697  else
1698  {
1699  /* Query text not requested */
1700  nulls[i++] = true;
1701  }
1702  }
1703  else
1704  {
1705  /* Don't show queryid */
1706  if (api_version >= PGSS_V1_2)
1707  nulls[i++] = true;
1708 
1709  /*
1710  * Don't show query text, but hint as to the reason for not doing
1711  * so if it was requested
1712  */
1713  if (showtext)
1714  values[i++] = CStringGetTextDatum("<insufficient privilege>");
1715  else
1716  nulls[i++] = true;
1717  }
1718 
1719  /* copy counters to a local variable to keep locking time short */
1720  {
1721  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1722 
1723  SpinLockAcquire(&e->mutex);
1724  tmp = e->counters;
1725  SpinLockRelease(&e->mutex);
1726  }
1727 
1728  /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */
1729  if (IS_STICKY(tmp))
1730  continue;
1731 
1732  /* Note that we rely on PGSS_PLAN being 0 and PGSS_EXEC being 1. */
1733  for (int kind = 0; kind < PGSS_NUMKIND; kind++)
1734  {
1735  if (kind == PGSS_EXEC || api_version >= PGSS_V1_8)
1736  {
1737  values[i++] = Int64GetDatumFast(tmp.calls[kind]);
1738  values[i++] = Float8GetDatumFast(tmp.total_time[kind]);
1739  }
1740 
1741  if ((kind == PGSS_EXEC && api_version >= PGSS_V1_3) ||
1742  api_version >= PGSS_V1_8)
1743  {
1744  values[i++] = Float8GetDatumFast(tmp.min_time[kind]);
1745  values[i++] = Float8GetDatumFast(tmp.max_time[kind]);
1746  values[i++] = Float8GetDatumFast(tmp.mean_time[kind]);
1747 
1748  /*
1749  * Note we are calculating the population variance here, not
1750  * the sample variance, as we have data for the whole
1751  * population, so Bessel's correction is not used, and we
1752  * don't divide by tmp.calls - 1.
1753  */
1754  if (tmp.calls[kind] > 1)
1755  stddev = sqrt(tmp.sum_var_time[kind] / tmp.calls[kind]);
1756  else
1757  stddev = 0.0;
1758  values[i++] = Float8GetDatumFast(stddev);
1759  }
1760  }
1761  values[i++] = Int64GetDatumFast(tmp.rows);
1762  values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
1763  values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
1764  if (api_version >= PGSS_V1_1)
1765  values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied);
1766  values[i++] = Int64GetDatumFast(tmp.shared_blks_written);
1767  values[i++] = Int64GetDatumFast(tmp.local_blks_hit);
1768  values[i++] = Int64GetDatumFast(tmp.local_blks_read);
1769  if (api_version >= PGSS_V1_1)
1770  values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied);
1771  values[i++] = Int64GetDatumFast(tmp.local_blks_written);
1772  values[i++] = Int64GetDatumFast(tmp.temp_blks_read);
1773  values[i++] = Int64GetDatumFast(tmp.temp_blks_written);
1774  if (api_version >= PGSS_V1_1)
1775  {
1776  values[i++] = Float8GetDatumFast(tmp.blk_read_time);
1777  values[i++] = Float8GetDatumFast(tmp.blk_write_time);
1778  }
1779  if (api_version >= PGSS_V1_8)
1780  {
1781  char buf[256];
1782  Datum wal_bytes;
1783 
1784  values[i++] = Int64GetDatumFast(tmp.wal_records);
1785  values[i++] = Int64GetDatumFast(tmp.wal_fpi);
1786 
1787  snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.wal_bytes);
1788 
1789  /* Convert to numeric. */
1790  wal_bytes = DirectFunctionCall3(numeric_in,
1791  CStringGetDatum(buf),
1792  ObjectIdGetDatum(0),
1793  Int32GetDatum(-1));
1794  values[i++] = wal_bytes;
1795  }
1796 
1797  Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
1798  api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
1799  api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
1800  api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
1801  api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
1802  api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
1803  -1 /* fail if you forget to update this assert */ ));
1804 
1805  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
1806  }
1807 
1808  /* clean up and return the tuplestore */
1810 
1811  if (qbuffer)
1812  free(qbuffer);
1813 
1814  tuplestore_donestoring(tupstore);
1815 }
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
#define PG_STAT_STATEMENTS_COLS_V1_3
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:207
double total_time[PGSS_NUMKIND]
#define PG_STAT_STATEMENTS_COLS_V1_8
#define PG_STAT_STATEMENTS_COLS_V1_0
int64 shared_blks_read
int64 local_blks_written
Oid GetUserId(void)
Definition: miscinit.c:478
int64 shared_blks_dirtied
double max_time[PGSS_NUMKIND]
#define tuplestore_donestoring(state)
Definition: tuplestore.h:60
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:698
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:1816
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1169
int64 shared_blks_hit
static HTAB * pgss_hash
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
struct pg_encoding enc
Definition: encode.c:516
fmNodePtr resultinfo
Definition: fmgr.h:89
static char * buf
Definition: pg_test_fsync.c:68
#define CStringGetDatum(X)
Definition: postgres.h:622
static char * qtext_load_file(Size *buffer_size)
#define IS_STICKY(c)
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:621
int64 temp_blks_written
int64 shared_blks_written
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:630
#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:411
double mean_time[PGSS_NUMKIND]
#define Int64GetDatumFast(X)
Definition: postgres.h:804
int work_mem
Definition: globals.c:124
#define BoolGetDatum(X)
Definition: postgres.h:446
pgssHashKey key
#define ereport(elevel,...)
Definition: elog.h:157
int allowedModes
Definition: execnodes.h:305
#define PG_STAT_STATEMENTS_COLS
#define free(a)
Definition: header.h:65
int64 calls[PGSS_NUMKIND]
bool is_member_of_role(Oid member, Oid role)
Definition: acl.c:4869
#define Float8GetDatumFast(X)
Definition: postgres.h:805
SetFunctionReturnMode returnMode
Definition: execnodes.h:307
double min_time[PGSS_NUMKIND]
double blk_read_time
#define Assert(condition)
Definition: c.h:804
int64 local_blks_dirtied
int64 local_blks_hit
size_t Size
Definition: c.h:540
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1203
double blk_write_time
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:233
double sum_var_time[PGSS_NUMKIND]
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
Tuplestorestate * setResult
Definition: execnodes.h:310
static Datum values[MAXATTR]
Definition: bootstrap.c:166
ExprContext * econtext
Definition: execnodes.h:303
#define Int32GetDatum(X)
Definition: postgres.h:523
e
Definition: preproc-init.c:82
TupleDesc setDesc
Definition: execnodes.h:311
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
int i
#define CStringGetTextDatum(s)
Definition: builtins.h:82
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676
#define snprintf
Definition: port.h:216
#define UINT64_FORMAT
Definition: c.h:484
#define PG_STAT_STATEMENTS_COLS_V1_2
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)
#define PG_STAT_STATEMENTS_COLS_V1_9

◆ pg_stat_statements_reset()

Datum pg_stat_statements_reset ( PG_FUNCTION_ARGS  )

Definition at line 1413 of file pg_stat_statements.c.

References entry_reset(), and PG_RETURN_VOID.

1414 {
1415  entry_reset(0, 0, 0);
1416 
1417  PG_RETURN_VOID();
1418 }
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)
#define PG_RETURN_VOID()
Definition: fmgr.h:349

◆ pg_stat_statements_reset_1_7()

Datum pg_stat_statements_reset_1_7 ( PG_FUNCTION_ARGS  )

Definition at line 1394 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.

1395 {
1396  Oid userid;
1397  Oid dbid;
1398  uint64 queryid;
1399 
1400  userid = PG_GETARG_OID(0);
1401  dbid = PG_GETARG_OID(1);
1402  queryid = (uint64) PG_GETARG_INT64(2);
1403 
1404  entry_reset(userid, dbid, queryid);
1405 
1406  PG_RETURN_VOID();
1407 }
unsigned int Oid
Definition: postgres_ext.h:31
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283

◆ pgss_ExecutorEnd()

static void pgss_ExecutorEnd ( QueryDesc queryDesc)
static

Definition at line 1036 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1037 {
1038  uint64 queryId = queryDesc->plannedstmt->queryId;
1039 
1040  if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
1042  {
1043  /*
1044  * Make sure stats accumulation is done. (Note: it's okay if several
1045  * levels of hook all do this.)
1046  */
1047  InstrEndLoop(queryDesc->totaltime);
1048 
1049  pgss_store(queryDesc->sourceText,
1050  queryId,
1051  queryDesc->plannedstmt->stmt_location,
1052  queryDesc->plannedstmt->stmt_len,
1053  PGSS_EXEC,
1054  queryDesc->totaltime->total * 1000.0, /* convert to msec */
1055  queryDesc->estate->es_processed,
1056  &queryDesc->totaltime->bufusage,
1057  &queryDesc->totaltime->walusage,
1058  NULL);
1059  }
1060 
1061  if (prev_ExecutorEnd)
1062  prev_ExecutorEnd(queryDesc);
1063  else
1064  standard_ExecutorEnd(queryDesc);
1065 }
WalUsage walusage
Definition: instrument.h:76
EState * estate
Definition: execdesc.h:48
static ExecutorEnd_hook_type prev_ExecutorEnd
#define pgss_enabled(level)
void standard_ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:468
static int exec_nested_level
int stmt_len
Definition: plannodes.h:90
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:140
int stmt_location
Definition: plannodes.h:89
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, JumbleState *jstate)
BufferUsage bufusage
Definition: instrument.h:75
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 es_processed
Definition: execnodes.h:603
const char * sourceText
Definition: execdesc.h:38
uint64 queryId
Definition: plannodes.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37

◆ pgss_ExecutorFinish()

static void pgss_ExecutorFinish ( QueryDesc queryDesc)
static

Definition at line 1015 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1016 {
1018  PG_TRY();
1019  {
1020  if (prev_ExecutorFinish)
1021  prev_ExecutorFinish(queryDesc);
1022  else
1023  standard_ExecutorFinish(queryDesc);
1024  }
1025  PG_FINALLY();
1026  {
1028  }
1029  PG_END_TRY();
1030 }
static int exec_nested_level
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:408
static ExecutorFinish_hook_type prev_ExecutorFinish
#define PG_FINALLY()
Definition: elog.h:330
#define PG_TRY()
Definition: elog.h:313
#define PG_END_TRY()
Definition: elog.h:338

◆ pgss_ExecutorRun()

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

Definition at line 993 of file pg_stat_statements.c.

References exec_nested_level, PG_END_TRY, PG_FINALLY, PG_TRY, prev_ExecutorRun, and standard_ExecutorRun().

Referenced by _PG_init().

995 {
997  PG_TRY();
998  {
999  if (prev_ExecutorRun)
1000  prev_ExecutorRun(queryDesc, direction, count, execute_once);
1001  else
1002  standard_ExecutorRun(queryDesc, direction, count, execute_once);
1003  }
1004  PG_FINALLY();
1005  {
1007  }
1008  PG_END_TRY();
1009 }
void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:309
static ExecutorRun_hook_type prev_ExecutorRun
static int exec_nested_level
#define PG_FINALLY()
Definition: elog.h:330
#define PG_TRY()
Definition: elog.h:313
#define PG_END_TRY()
Definition: elog.h:338

◆ pgss_ExecutorStart()

static void pgss_ExecutorStart ( QueryDesc queryDesc,
int  eflags 
)
static

Definition at line 959 of file pg_stat_statements.c.

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

Referenced by _PG_init().

960 {
961  if (prev_ExecutorStart)
962  prev_ExecutorStart(queryDesc, eflags);
963  else
964  standard_ExecutorStart(queryDesc, eflags);
965 
966  /*
967  * If query has queryId zero, don't track it. This prevents double
968  * counting of optimizable statements that are directly contained in
969  * utility statements.
970  */
971  if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
972  {
973  /*
974  * Set up to track total elapsed time in ExecutorRun. Make sure the
975  * space is allocated in the per-query context so it will go away at
976  * ExecutorEnd.
977  */
978  if (queryDesc->totaltime == NULL)
979  {
980  MemoryContext oldcxt;
981 
982  oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
983  queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false);
984  MemoryContextSwitchTo(oldcxt);
985  }
986  }
987 }
EState * estate
Definition: execdesc.h:48
void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:147
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Instrumentation * InstrAlloc(int n, int instrument_options, bool async_mode)
Definition: instrument.c:31
#define pgss_enabled(level)
static ExecutorStart_hook_type prev_ExecutorStart
static int exec_nested_level
MemoryContext es_query_cxt
Definition: execnodes.h:599
struct Instrumentation * totaltime
Definition: execdesc.h:55
uint64 queryId
Definition: plannodes.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37

◆ pgss_memsize()

static Size pgss_memsize ( void  )
static

Definition at line 1862 of file pg_stat_statements.c.

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

Referenced by _PG_init().

1863 {
1864  Size size;
1865 
1866  size = MAXALIGN(sizeof(pgssSharedState));
1867  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1868 
1869  return size;
1870 }
static int pgss_max
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:780
Size add_size(Size s1, Size s2)
Definition: shmem.c:502
size_t Size
Definition: c.h:540
#define MAXALIGN(LEN)
Definition: c.h:757

◆ pgss_planner()

static PlannedStmt * pgss_planner ( Query parse,
const char *  query_string,
int  cursorOptions,
ParamListInfo  boundParams 
)
static

Definition at line 864 of file pg_stat_statements.c.

References BufferUsageAccumDiff(), duration, exec_nested_level, INSTR_TIME_GET_MILLISEC, INSTR_TIME_SET_CURRENT, INSTR_TIME_SUBTRACT, PG_END_TRY, PG_FINALLY, PG_TRY, pgBufferUsage, pgss_enabled, PGSS_PLAN, pgss_store(), pgss_track_planning, pgWalUsage, plan_nested_level, prev_planner_hook, Query::queryId, standard_planner(), Query::stmt_len, Query::stmt_location, and WalUsageAccumDiff().

Referenced by _PG_init().

868 {
869  PlannedStmt *result;
870 
871  /*
872  * We can't process the query if no query_string is provided, as
873  * pgss_store needs it. We also ignore query without queryid, as it would
874  * be treated as a utility statement, which may not be the case.
875  *
876  * Note that planner_hook can be called from the planner itself, so we
877  * have a specific nesting level for the planner. However, utility
878  * commands containing optimizable statements can also call the planner,
879  * same for regular DML (for instance for underlying foreign key queries).
880  * So testing the planner nesting level only is not enough to detect real
881  * top level planner call.
882  */
884  && pgss_track_planning && query_string
885  && parse->queryId != UINT64CONST(0))
886  {
887  instr_time start;
889  BufferUsage bufusage_start,
890  bufusage;
891  WalUsage walusage_start,
892  walusage;
893 
894  /* We need to track buffer usage as the planner can access them. */
895  bufusage_start = pgBufferUsage;
896 
897  /*
898  * Similarly the planner could write some WAL records in some cases
899  * (e.g. setting a hint bit with those being WAL-logged)
900  */
901  walusage_start = pgWalUsage;
902  INSTR_TIME_SET_CURRENT(start);
903 
905  PG_TRY();
906  {
907  if (prev_planner_hook)
908  result = prev_planner_hook(parse, query_string, cursorOptions,
909  boundParams);
910  else
911  result = standard_planner(parse, query_string, cursorOptions,
912  boundParams);
913  }
914  PG_FINALLY();
915  {
917  }
918  PG_END_TRY();
919 
920  INSTR_TIME_SET_CURRENT(duration);
921  INSTR_TIME_SUBTRACT(duration, start);
922 
923  /* calc differences of buffer counters. */
924  memset(&bufusage, 0, sizeof(BufferUsage));
925  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
926 
927  /* calc differences of WAL counters. */
928  memset(&walusage, 0, sizeof(WalUsage));
929  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
930 
931  pgss_store(query_string,
932  parse->queryId,
933  parse->stmt_location,
934  parse->stmt_len,
935  PGSS_PLAN,
936  INSTR_TIME_GET_MILLISEC(duration),
937  0,
938  &bufusage,
939  &walusage,
940  NULL);
941  }
942  else
943  {
944  if (prev_planner_hook)
945  result = prev_planner_hook(parse, query_string, cursorOptions,
946  boundParams);
947  else
948  result = standard_planner(parse, query_string, cursorOptions,
949  boundParams);
950  }
951 
952  return result;
953 }
int stmt_location
Definition: parsenodes.h:192
WalUsage pgWalUsage
Definition: instrument.c:22
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
struct timeval instr_time
Definition: instr_time.h:150
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:274
#define pgss_enabled(level)
static int exec_nested_level
int duration
Definition: pgbench.c:182
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:244
static planner_hook_type prev_planner_hook
static bool pgss_track_planning
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, JumbleState *jstate)
uint64 queryId
Definition: parsenodes.h:124
#define PG_FINALLY()
Definition: elog.h:330
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
static int plan_nested_level
PlannedStmt * standard_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: planner.c:276
#define PG_TRY()
Definition: elog.h:313
int stmt_len
Definition: parsenodes.h:193
#define PG_END_TRY()
Definition: elog.h:338
BufferUsage pgBufferUsage
Definition: instrument.c:20

◆ pgss_post_parse_analyze()

static void pgss_post_parse_analyze ( ParseState pstate,
Query query,
JumbleState jstate 
)
static

Definition at line 818 of file pg_stat_statements.c.

References JumbleState::clocations_count, exec_nested_level, ParseState::p_sourcetext, pgss_enabled, PGSS_HANDLED_UTILITY, PGSS_INVALID, pgss_store(), pgss_track_utility, prev_post_parse_analyze_hook, Query::queryId, Query::stmt_len, Query::stmt_location, and Query::utilityStmt.

Referenced by _PG_init().

819 {
821  prev_post_parse_analyze_hook(pstate, query, jstate);
822 
823  /* Safety check... */
825  return;
826 
827  /*
828  * Clear queryId for prepared statements related utility, as those will
829  * inherit from the underlying statement's one (except DEALLOCATE which is
830  * entirely untracked).
831  */
832  if (query->utilityStmt)
833  {
835  query->queryId = UINT64CONST(0);
836  return;
837  }
838 
839  /*
840  * If query jumbling were able to identify any ignorable constants, we
841  * immediately create a hash table entry for the query, so that we can
842  * record the normalized form of the query string. If there were no such
843  * constants, the normalized string would be the same as the query text
844  * anyway, so there's no need for an early entry.
845  */
846  if (jstate && jstate->clocations_count > 0)
847  pgss_store(pstate->p_sourcetext,
848  query->queryId,
849  query->stmt_location,
850  query->stmt_len,
851  PGSS_INVALID,
852  0,
853  0,
854  NULL,
855  NULL,
856  jstate);
857 }
int stmt_location
Definition: parsenodes.h:192
#define pgss_enabled(level)
static int exec_nested_level
Node * utilityStmt
Definition: parsenodes.h:128
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
int clocations_count
Definition: queryjumble.h:49
static pgssSharedState * pgss
static HTAB * pgss_hash
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, JumbleState *jstate)
const char * p_sourcetext
Definition: parse_node.h:181
uint64 queryId
Definition: parsenodes.h:124
#define PGSS_HANDLED_UTILITY(n)
static bool pgss_track_utility
int stmt_len
Definition: parsenodes.h:193

◆ pgss_ProcessUtility()

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

Definition at line 1071 of file pg_stat_statements.c.

References BufferUsageAccumDiff(), QueryCompletion::commandTag, duration, exec_nested_level, INSTR_TIME_GET_MILLISEC, INSTR_TIME_SET_CURRENT, INSTR_TIME_SUBTRACT, QueryCompletion::nprocessed, PG_END_TRY, PG_FINALLY, PG_TRY, pgBufferUsage, pgss_enabled, PGSS_EXEC, PGSS_HANDLED_UTILITY, pgss_store(), pgss_track_utility, pgWalUsage, prev_ProcessUtility, PlannedStmt::queryId, standard_ProcessUtility(), PlannedStmt::stmt_len, PlannedStmt::stmt_location, PlannedStmt::utilityStmt, and WalUsageAccumDiff().

Referenced by _PG_init().

1075 {
1076  Node *parsetree = pstmt->utilityStmt;
1077  uint64 saved_queryId = pstmt->queryId;
1078 
1079  /*
1080  * Force utility statements to get queryId zero. We do this even in cases
1081  * where the statement contains an optimizable statement for which a
1082  * queryId could be derived (such as EXPLAIN or DECLARE CURSOR). For such
1083  * cases, runtime control will first go through ProcessUtility and then
1084  * the executor, and we don't want the executor hooks to do anything,
1085  * since we are already measuring the statement's costs at the utility
1086  * level.
1087  *
1088  * Note that this is only done if pg_stat_statements is enabled and
1089  * configured to track utility statements, in the unlikely possibility
1090  * that user configured another extension to handle utility statements
1091  * only.
1092  */
1094  pstmt->queryId = UINT64CONST(0);
1095 
1096  /*
1097  * If it's an EXECUTE statement, we don't track it and don't increment the
1098  * nesting level. This allows the cycles to be charged to the underlying
1099  * PREPARE instead (by the Executor hooks), which is much more useful.
1100  *
1101  * We also don't track execution of PREPARE. If we did, we would get one
1102  * hash table entry for the PREPARE (with hash calculated from the query
1103  * string), and then a different one with the same query string (but hash
1104  * calculated from the query tree) would be used to accumulate costs of
1105  * ensuing EXECUTEs. This would be confusing, and inconsistent with other
1106  * cases where planning time is not included at all.
1107  *
1108  * Likewise, we don't track execution of DEALLOCATE.
1109  */
1111  PGSS_HANDLED_UTILITY(parsetree))
1112  {
1113  instr_time start;
1115  uint64 rows;
1116  BufferUsage bufusage_start,
1117  bufusage;
1118  WalUsage walusage_start,
1119  walusage;
1120 
1121  bufusage_start = pgBufferUsage;
1122  walusage_start = pgWalUsage;
1123  INSTR_TIME_SET_CURRENT(start);
1124 
1126  PG_TRY();
1127  {
1128  if (prev_ProcessUtility)
1129  prev_ProcessUtility(pstmt, queryString,
1130  context, params, queryEnv,
1131  dest, qc);
1132  else
1133  standard_ProcessUtility(pstmt, queryString,
1134  context, params, queryEnv,
1135  dest, qc);
1136  }
1137  PG_FINALLY();
1138  {
1140  }
1141  PG_END_TRY();
1142 
1143  INSTR_TIME_SET_CURRENT(duration);
1144  INSTR_TIME_SUBTRACT(duration, start);
1145 
1146  /*
1147  * Track the total number of rows retrieved or affected by the utility
1148  * statements of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED
1149  * VIEW, REFRESH MATERIALIZED VIEW and SELECT INTO.
1150  */
1151  rows = (qc && (qc->commandTag == CMDTAG_COPY ||
1152  qc->commandTag == CMDTAG_FETCH ||
1153  qc->commandTag == CMDTAG_SELECT ||
1154  qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW)) ?
1155  qc->nprocessed : 0;
1156 
1157  /* calc differences of buffer counters. */
1158  memset(&bufusage, 0, sizeof(BufferUsage));
1159  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
1160 
1161  /* calc differences of WAL counters. */
1162  memset(&walusage, 0, sizeof(WalUsage));
1163  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
1164 
1165  pgss_store(queryString,
1166  saved_queryId,
1167  pstmt->stmt_location,
1168  pstmt->stmt_len,
1169  PGSS_EXEC,
1170  INSTR_TIME_GET_MILLISEC(duration),
1171  rows,
1172  &bufusage,
1173  &walusage,
1174  NULL);
1175  }
1176  else
1177  {
1178  if (prev_ProcessUtility)
1179  prev_ProcessUtility(pstmt, queryString,
1180  context, params, queryEnv,
1181  dest, qc);
1182  else
1183  standard_ProcessUtility(pstmt, queryString,
1184  context, params, queryEnv,
1185  dest, qc);
1186  }
1187 }
static ProcessUtility_hook_type prev_ProcessUtility
WalUsage pgWalUsage
Definition: instrument.c:22
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
struct timeval instr_time
Definition: instr_time.h:150
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:274
#define pgss_enabled(level)
Definition: nodes.h:539
static int exec_nested_level
int stmt_len
Definition: plannodes.h:90
int duration
Definition: pgbench.c:182
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:244
uint64 nprocessed
Definition: cmdtag.h:31
void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:542
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
int stmt_location
Definition: plannodes.h:89
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, JumbleState *jstate)
Node * utilityStmt
Definition: plannodes.h:86
#define PGSS_HANDLED_UTILITY(n)
#define PG_FINALLY()
Definition: elog.h:330
CommandTag commandTag
Definition: cmdtag.h:30
static bool pgss_track_utility
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
uint64 queryId
Definition: plannodes.h:48
#define PG_TRY()
Definition: elog.h:313
#define PG_END_TRY()
Definition: elog.h:338
BufferUsage pgBufferUsage
Definition: instrument.c:20

◆ pgss_shmem_shutdown()

static void pgss_shmem_shutdown ( int  code,
Datum  arg 
)
static

Definition at line 718 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, pgssEntry::query_offset, and pgssSharedState::stats.

Referenced by pgss_shmem_startup().

719 {
720  FILE *file;
721  char *qbuffer = NULL;
722  Size qbuffer_size = 0;
723  HASH_SEQ_STATUS hash_seq;
724  int32 num_entries;
725  pgssEntry *entry;
726 
727  /* Don't try to dump during a crash. */
728  if (code)
729  return;
730 
731  /* Safety check ... shouldn't get here unless shmem is set up. */
732  if (!pgss || !pgss_hash)
733  return;
734 
735  /* Don't dump if told not to. */
736  if (!pgss_save)
737  return;
738 
739  file = AllocateFile(PGSS_DUMP_FILE ".tmp", PG_BINARY_W);
740  if (file == NULL)
741  goto error;
742 
743  if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1)
744  goto error;
745  if (fwrite(&PGSS_PG_MAJOR_VERSION, sizeof(uint32), 1, file) != 1)
746  goto error;
747  num_entries = hash_get_num_entries(pgss_hash);
748  if (fwrite(&num_entries, sizeof(int32), 1, file) != 1)
749  goto error;
750 
751  qbuffer = qtext_load_file(&qbuffer_size);
752  if (qbuffer == NULL)
753  goto error;
754 
755  /*
756  * When serializing to disk, we store query texts immediately after their
757  * entry data. Any orphaned query texts are thereby excluded.
758  */
759  hash_seq_init(&hash_seq, pgss_hash);
760  while ((entry = hash_seq_search(&hash_seq)) != NULL)
761  {
762  int len = entry->query_len;
763  char *qstr = qtext_fetch(entry->query_offset, len,
764  qbuffer, qbuffer_size);
765 
766  if (qstr == NULL)
767  continue; /* Ignore any entries with bogus texts */
768 
769  if (fwrite(entry, sizeof(pgssEntry), 1, file) != 1 ||
770  fwrite(qstr, 1, len + 1, file) != len + 1)
771  {
772  /* note: we assume hash_seq_term won't change errno */
773  hash_seq_term(&hash_seq);
774  goto error;
775  }
776  }
777 
778  /* Dump global statistics for pg_stat_statements */
779  if (fwrite(&pgss->stats, sizeof(pgssGlobalStats), 1, file) != 1)
780  goto error;
781 
782  free(qbuffer);
783  qbuffer = NULL;
784 
785  if (FreeFile(file))
786  {
787  file = NULL;
788  goto error;
789  }
790 
791  /*
792  * Rename file into place, so we atomically replace any old one.
793  */
795 
796  /* Unlink query-texts file; it's not needed while shutdown */
797  unlink(PGSS_TEXT_FILE);
798 
799  return;
800 
801 error:
802  ereport(LOG,
804  errmsg("could not write file \"%s\": %m",
805  PGSS_DUMP_FILE ".tmp")));
806  if (qbuffer)
807  free(qbuffer);
808  if (file)
809  FreeFile(file);
810  unlink(PGSS_DUMP_FILE ".tmp");
811  unlink(PGSS_TEXT_FILE);
812 }
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:1274
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1382
#define LOG
Definition: elog.h:26
signed int int32
Definition: c.h:429
static pgssSharedState * pgss
static HTAB * pgss_hash
int errcode_for_file_access(void)
Definition: elog.c:721
static char * qtext_load_file(Size *buffer_size)
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2373
unsigned int uint32
Definition: c.h:441
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:692
pgssGlobalStats stats
#define ereport(elevel,...)
Definition: elog.h:157
#define free(a)
Definition: header.h:65
size_t Size
Definition: c.h:540
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
int FreeFile(FILE *file)
Definition: fd.c:2572
int errmsg(const char *fmt,...)
Definition: elog.c:909
static const uint32 PGSS_FILE_HEADER
#define PGSS_TEXT_FILE
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1512
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 494 of file pg_stat_statements.c.

References AllocateFile(), ASSUMED_LENGTH_INIT, ASSUMED_MEDIAN_INIT, pgssEntry::counters, pgssSharedState::cur_median_usage, pgssGlobalStats::dealloc, pgssEntry::encoding, entry_alloc(), HASHCTL::entrysize, ereport, errcode(), errcode_for_file_access(), errmsg(), pgssSharedState::extent, FreeFile(), pgssSharedState::gc_count, GetCurrentTimestamp(), GetNamedLWLockTranche(), HASH_BLOBS, HASH_ELEM, header(), i, IS_STICKY, 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(), SpinLockInit, pgssSharedState::stats, and pgssGlobalStats::stats_reset.

Referenced by _PG_init().

495 {
496  bool found;
497  HASHCTL info;
498  FILE *file = NULL;
499  FILE *qfile = NULL;
500  uint32 header;
501  int32 num;
502  int32 pgver;
503  int32 i;
504  int buffer_size;
505  char *buffer = NULL;
506 
509 
510  /* reset in case this is a restart within the postmaster */
511  pgss = NULL;
512  pgss_hash = NULL;
513 
514  /*
515  * Create or attach to the shared memory state, including hash table
516  */
517  LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
518 
519  pgss = ShmemInitStruct("pg_stat_statements",
520  sizeof(pgssSharedState),
521  &found);
522 
523  if (!found)
524  {
525  /* First time through ... */
526  pgss->lock = &(GetNamedLWLockTranche("pg_stat_statements"))->lock;
530  pgss->extent = 0;
531  pgss->n_writers = 0;
532  pgss->gc_count = 0;
533  pgss->stats.dealloc = 0;
535  }
536 
537  info.keysize = sizeof(pgssHashKey);
538  info.entrysize = sizeof(pgssEntry);
539  pgss_hash = ShmemInitHash("pg_stat_statements hash",
541  &info,
543 
544  LWLockRelease(AddinShmemInitLock);
545 
546  /*
547  * If we're in the postmaster (or a standalone backend...), set up a shmem
548  * exit hook to dump the statistics to disk.
549  */
550  if (!IsUnderPostmaster)
552 
553  /*
554  * Done if some other process already completed our initialization.
555  */
556  if (found)
557  return;
558 
559  /*
560  * Note: we don't bother with locks here, because there should be no other
561  * processes running when this code is reached.
562  */
563 
564  /* Unlink query text file possibly left over from crash */
565  unlink(PGSS_TEXT_FILE);
566 
567  /* Allocate new query text temp file */
569  if (qfile == NULL)
570  goto write_error;
571 
572  /*
573  * If we were told not to load old statistics, we're done. (Note we do
574  * not try to unlink any old dump file in this case. This seems a bit
575  * questionable but it's the historical behavior.)
576  */
577  if (!pgss_save)
578  {
579  FreeFile(qfile);
580  return;
581  }
582 
583  /*
584  * Attempt to load old statistics from the dump file.
585  */
587  if (file == NULL)
588  {
589  if (errno != ENOENT)
590  goto read_error;
591  /* No existing persisted stats file, so we're done */
592  FreeFile(qfile);
593  return;
594  }
595 
596  buffer_size = 2048;
597  buffer = (char *) palloc(buffer_size);
598 
599  if (fread(&header, sizeof(uint32), 1, file) != 1 ||
600  fread(&pgver, sizeof(uint32), 1, file) != 1 ||
601  fread(&num, sizeof(int32), 1, file) != 1)
602  goto read_error;
603 
604  if (header != PGSS_FILE_HEADER ||
605  pgver != PGSS_PG_MAJOR_VERSION)
606  goto data_error;
607 
608  for (i = 0; i < num; i++)
609  {
610  pgssEntry temp;
611  pgssEntry *entry;
612  Size query_offset;
613 
614  if (fread(&temp, sizeof(pgssEntry), 1, file) != 1)
615  goto read_error;
616 
617  /* Encoding is the only field we can easily sanity-check */
618  if (!PG_VALID_BE_ENCODING(temp.encoding))
619  goto data_error;
620 
621  /* Resize buffer as needed */
622  if (temp.query_len >= buffer_size)
623  {
624  buffer_size = Max(buffer_size * 2, temp.query_len + 1);
625  buffer = repalloc(buffer, buffer_size);
626  }
627 
628  if (fread(buffer, 1, temp.query_len + 1, file) != temp.query_len + 1)
629  goto read_error;
630 
631  /* Should have a trailing null, but let's make sure */
632  buffer[temp.query_len] = '\0';
633 
634  /* Skip loading "sticky" entries */
635  if (IS_STICKY(temp.counters))
636  continue;
637 
638  /* Store the query text */
639  query_offset = pgss->extent;
640  if (fwrite(buffer, 1, temp.query_len + 1, qfile) != temp.query_len + 1)
641  goto write_error;
642  pgss->extent += temp.query_len + 1;
643 
644  /* make the hashtable entry (discards old entries if too many) */
645  entry = entry_alloc(&temp.key, query_offset, temp.query_len,
646  temp.encoding,
647  false);
648 
649  /* copy in the actual stats */
650  entry->counters = temp.counters;
651  }
652 
653  /* Read global statistics for pg_stat_statements */
654  if (fread(&pgss->stats, sizeof(pgssGlobalStats), 1, file) != 1)
655  goto read_error;
656 
657  pfree(buffer);
658  FreeFile(file);
659  FreeFile(qfile);
660 
661  /*
662  * Remove the persisted stats file so it's not included in
663  * backups/replication standbys, etc. A new file will be written on next
664  * shutdown.
665  *
666  * Note: it's okay if the PGSS_TEXT_FILE is included in a basebackup,
667  * because we remove that file on startup; it acts inversely to
668  * PGSS_DUMP_FILE, in that it is only supposed to be around when the
669  * server is running, whereas PGSS_DUMP_FILE is only supposed to be around
670  * when the server is not running. Leaving the file creates no danger of
671  * a newly restored database having a spurious record of execution costs,
672  * which is what we're really concerned about here.
673  */
674  unlink(PGSS_DUMP_FILE);
675 
676  return;
677 
678 read_error:
679  ereport(LOG,
681  errmsg("could not read file \"%s\": %m",
682  PGSS_DUMP_FILE)));
683  goto fail;
684 data_error:
685  ereport(LOG,
686  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
687  errmsg("ignoring invalid data in file \"%s\"",
688  PGSS_DUMP_FILE)));
689  goto fail;
690 write_error:
691  ereport(LOG,
693  errmsg("could not write file \"%s\": %m",
694  PGSS_TEXT_FILE)));
695 fail:
696  if (buffer)
697  pfree(buffer);
698  if (file)
699  FreeFile(file);
700  if (qfile)
701  FreeFile(qfile);
702  /* If possible, throw away the bogus file; ignore any error */
703  unlink(PGSS_DUMP_FILE);
704 
705  /*
706  * Don't unlink PGSS_TEXT_FILE here; it should always be around while the
707  * server is running with pg_stat_statements enabled
708  */
709 }
#define HASH_ELEM
Definition: hsearch.h:95
static const uint32 PGSS_PG_MAJOR_VERSION
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1580
#define SpinLockInit(lock)
Definition: spin.h:60
Size entrysize
Definition: hsearch.h:76
#define PGSS_DUMP_FILE
int errcode(int sqlerrcode)
Definition: elog.c:698
struct pgssEntry pgssEntry
#define PG_BINARY_W
Definition: c.h:1274
Counters counters
#define LOG
Definition: elog.h:26
#define PG_BINARY_R
Definition: c.h:1273
signed int int32
Definition: c.h:429
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1816
#define ASSUMED_LENGTH_INIT
static pgssSharedState * pgss
void pfree(void *pointer)
Definition: mcxt.c:1169
static int pgss_max
static HTAB * pgss_hash
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:396
void on_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:361
bool IsUnderPostmaster
Definition: globals.c:112
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:721
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2373
unsigned int uint32
Definition: c.h:441
#define IS_STICKY(c)
pgssGlobalStats stats
#define HASH_BLOBS
Definition: hsearch.h:97
uintptr_t Datum
Definition: postgres.h:411
static void pgss_shmem_shutdown(int code, Datum arg)
Size keysize
Definition: hsearch.h:75
pgssHashKey key
#define ereport(elevel,...)
Definition: elog.h:157
#define Max(x, y)
Definition: c.h:980
#define PG_VALID_BE_ENCODING(_enc)
Definition: pg_wchar.h:295
LWLockPadded * GetNamedLWLockTranche(const char *tranche_name)
Definition: lwlock.c:594
size_t Size
Definition: c.h:540
TimestampTz stats_reset
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1203
static void header(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:210
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
static shmem_startup_hook_type prev_shmem_startup_hook
int FreeFile(FILE *file)
Definition: fd.c:2572
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
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:341
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,
pgssStoreKind  kind,
double  total_time,
uint64  rows,
const BufferUsage bufusage,
const WalUsage walusage,
JumbleState jstate 
)
static

Definition at line 1205 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, CleanQuerytext(), pgssEntry::counters, pgssHashKey::dbid, encoding, entry_alloc(), exec_nested_level, pgssSharedState::gc_count, gc_qtexts(), generate_normalized_query(), GetDatabaseEncoding(), GetUserId(), HASH_FIND, hash_search(), INSTR_TIME_GET_MILLISEC, IS_STICKY, 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_EXEC, PGSS_PLAN, qtext_store(), pgssHashKey::queryid, Counters::rows, 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, pgssHashKey::toplevel, Counters::total_time, Counters::usage, USAGE_EXEC, USAGE_INIT, pgssHashKey::userid, WalUsage::wal_bytes, Counters::wal_bytes, WalUsage::wal_fpi, Counters::wal_fpi, WalUsage::wal_records, and Counters::wal_records.

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

1212 {
1213  pgssHashKey key;
1214  pgssEntry *entry;
1215  char *norm_query = NULL;
1216  int encoding = GetDatabaseEncoding();
1217 
1218  Assert(query != NULL);
1219 
1220  /* Safety check... */
1221  if (!pgss || !pgss_hash)
1222  return;
1223 
1224  /*
1225  * Nothing to do if compute_query_id isn't enabled and no other module
1226  * computed a query identifier.
1227  */
1228  if (queryId == UINT64CONST(0))
1229  return;
1230 
1231  /*
1232  * Confine our attention to the relevant part of the string, if the query
1233  * is a portion of a multi-statement source string, and update query
1234  * location and length if needed.
1235  */
1236  query = CleanQuerytext(query, &query_location, &query_len);
1237 
1238  /* Set up key for hashtable search */
1239 
1240  /* memset() is required when pgssHashKey is without padding only */
1241  memset(&key, 0, sizeof(pgssHashKey));
1242 
1243  key.userid = GetUserId();
1244  key.dbid = MyDatabaseId;
1245  key.queryid = queryId;
1246  key.toplevel = (exec_nested_level == 0);
1247 
1248  /* Lookup the hash table entry with shared lock. */
1250 
1251  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
1252 
1253  /* Create new entry, if not present */
1254  if (!entry)
1255  {
1256  Size query_offset;
1257  int gc_count;
1258  bool stored;
1259  bool do_gc;
1260 
1261  /*
1262  * Create a new, normalized query string if caller asked. We don't
1263  * need to hold the lock while doing this work. (Note: in any case,
1264  * it's possible that someone else creates a duplicate hashtable entry
1265  * in the interval where we don't hold the lock below. That case is
1266  * handled by entry_alloc.)
1267  */
1268  if (jstate)
1269  {
1271  norm_query = generate_normalized_query(jstate, query,
1272  query_location,
1273  &query_len);
1275  }
1276 
1277  /* Append new query text to file with only shared lock held */
1278  stored = qtext_store(norm_query ? norm_query : query, query_len,
1279  &query_offset, &gc_count);
1280 
1281  /*
1282  * Determine whether we need to garbage collect external query texts
1283  * while the shared lock is still held. This micro-optimization
1284  * avoids taking the time to decide this while holding exclusive lock.
1285  */
1286  do_gc = need_gc_qtexts();
1287 
1288  /* Need exclusive lock to make a new hashtable entry - promote */
1291 
1292  /*
1293  * A garbage collection may have occurred while we weren't holding the
1294  * lock. In the unlikely event that this happens, the query text we
1295  * stored above will have been garbage collected, so write it again.
1296  * This should be infrequent enough that doing it while holding
1297  * exclusive lock isn't a performance problem.
1298  */
1299  if (!stored || pgss->gc_count != gc_count)
1300  stored = qtext_store(norm_query ? norm_query : query, query_len,
1301  &query_offset, NULL);
1302 
1303  /* If we failed to write to the text file, give up */
1304  if (!stored)
1305  goto done;
1306 
1307  /* OK to create a new hashtable entry */
1308  entry = entry_alloc(&key, query_offset, query_len, encoding,
1309  jstate != NULL);
1310 
1311  /* If needed, perform garbage collection while exclusive lock held */
1312  if (do_gc)
1313  gc_qtexts();
1314  }
1315 
1316  /* Increment the counts, except when jstate is not NULL */
1317  if (!jstate)
1318  {
1319  /*
1320  * Grab the spinlock while updating the counters (see comment about
1321  * locking rules at the head of the file)
1322  */
1323  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1324 
1325  Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
1326 
1327  SpinLockAcquire(&e->mutex);
1328 
1329  /* "Unstick" entry if it was previously sticky */
1330  if (IS_STICKY(e->counters))
1331  e->counters.usage = USAGE_INIT;
1332 
1333  e->counters.calls[kind] += 1;
1334  e->counters.total_time[kind] += total_time;
1335 
1336  if (e->counters.calls[kind] == 1)
1337  {
1338  e->counters.min_time[kind] = total_time;
1339  e->counters.max_time[kind] = total_time;
1340  e->counters.mean_time[kind] = total_time;
1341  }
1342  else
1343  {
1344  /*
1345  * Welford's method for accurately computing variance. See
1346  * <http://www.johndcook.com/blog/standard_deviation/>
1347  */
1348  double old_mean = e->counters.mean_time[kind];
1349 
1350  e->counters.mean_time[kind] +=
1351  (total_time - old_mean) / e->counters.calls[kind];
1352  e->counters.sum_var_time[kind] +=
1353  (total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
1354 
1355  /* calculate min and max time */
1356  if (e->counters.min_time[kind] > total_time)
1357  e->counters.min_time[kind] = total_time;
1358  if (e->counters.max_time[kind] < total_time)
1359  e->counters.max_time[kind] = total_time;
1360  }
1361  e->counters.rows += rows;
1362  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1363  e->counters.shared_blks_read += bufusage->shared_blks_read;
1366  e->counters.local_blks_hit += bufusage->local_blks_hit;
1367  e->counters.local_blks_read += bufusage->local_blks_read;
1370  e->counters.temp_blks_read += bufusage->temp_blks_read;
1374  e->counters.usage += USAGE_EXEC(total_time);
1375  e->counters.wal_records += walusage->wal_records;
1376  e->counters.wal_fpi += walusage->wal_fpi;
1377  e->counters.wal_bytes += walusage->wal_bytes;
1378 
1379  SpinLockRelease(&e->mutex);
1380  }
1381 
1382 done:
1384 
1385  /* We postpone this clean-up until we're out of the lock */
1386  if (norm_query)
1387  pfree(norm_query);
1388 }
double total_time[PGSS_NUMKIND]
int64 shared_blks_read
int64 local_blks_written
Oid GetUserId(void)
Definition: miscinit.c:478
instr_time blk_read_time
Definition: instrument.h:31
int64 shared_blks_read
Definition: instrument.h:22
int64 shared_blks_dirtied
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
int64 wal_fpi
Definition: instrument.h:38
double max_time[PGSS_NUMKIND]
int64 temp_blks_written
Definition: instrument.h:30
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
Counters counters
static int exec_nested_level
int64 temp_blks_read
int64 shared_blks_dirtied
Definition: instrument.h:23
int64 local_blks_read
Definition: instrument.h:26
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1816
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
void pfree(void *pointer)
Definition: mcxt.c:1169
int64 shared_blks_hit
static HTAB * pgss_hash
int64 local_blks_hit
Definition: instrument.h:25
#define USAGE_EXEC(duration)
static pgssEntry * entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding, bool sticky)
#define IS_STICKY(c)
int64 local_blks_dirtied
Definition: instrument.h:27
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:1210
static void gc_qtexts(void)
double mean_time[PGSS_NUMKIND]
Oid MyDatabaseId
Definition: globals.c:88
#define USAGE_INIT
int64 calls[PGSS_NUMKIND]
double min_time[PGSS_NUMKIND]
double blk_read_time
const char * CleanQuerytext(const char *query, int *location, int *len)
Definition: queryjumble.c:62
#define Assert(condition)
Definition: c.h:804
int64 local_blks_dirtied
instr_time blk_write_time
Definition: instrument.h:32
int64 local_blks_hit
size_t Size
Definition: c.h:540
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1203
double blk_write_time
static char * generate_normalized_query(JumbleState *jstate, const char *query, int query_loc, int *query_len_p)
int32 encoding
Definition: pg_database.h:41
double sum_var_time[PGSS_NUMKIND]
int64 wal_records
Definition: instrument.h:37
e
Definition: preproc-init.c:82
int64 local_blks_written
Definition: instrument.h:28
int64 shared_blks_hit
Definition: instrument.h:21
int64 temp_blks_read
Definition: instrument.h:29
uint64 wal_bytes
Definition: instrument.h:39
int64 shared_blks_written
Definition: instrument.h:24

◆ qtext_fetch()

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

Definition at line 2201 of file pg_stat_statements.c.

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

2203 {
2204  /* File read failed? */
2205  if (buffer == NULL)
2206  return NULL;
2207  /* Bogus offset/length? */
2208  if (query_len < 0 ||
2209  query_offset + query_len >= buffer_size)
2210  return NULL;
2211  /* As a further sanity check, make sure there's a trailing null */
2212  if (buffer[query_offset + query_len] != '\0')
2213  return NULL;
2214  /* Looks OK */
2215  return buffer + query_offset;
2216 }

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

Definition at line 2121 of file pg_stat_statements.c.

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

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

2122 {
2123  char *buf;
2124  int fd;
2125  struct stat stat;
2126 
2127  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDONLY | PG_BINARY);
2128  if (fd < 0)
2129  {
2130  if (errno != ENOENT)
2131  ereport(LOG,
2133  errmsg("could not read file \"%s\": %m",
2134  PGSS_TEXT_FILE)));
2135  return NULL;
2136  }
2137 
2138  /* Get file length */
2139  if (fstat(fd, &stat))
2140  {
2141  ereport(LOG,
2143  errmsg("could not stat file \"%s\": %m",
2144  PGSS_TEXT_FILE)));
2145  CloseTransientFile(fd);
2146  return NULL;
2147  }
2148 
2149  /* Allocate buffer; beware that off_t might be wider than size_t */
2150  if (stat.st_size <= MaxAllocHugeSize)
2151  buf = (char *) malloc(stat.st_size);
2152  else
2153  buf = NULL;
2154  if (buf == NULL)
2155  {
2156  ereport(LOG,
2157  (errcode(ERRCODE_OUT_OF_MEMORY),
2158  errmsg("out of memory"),
2159  errdetail("Could not allocate enough memory to read file \"%s\".",
2160  PGSS_TEXT_FILE)));
2161  CloseTransientFile(fd);
2162  return NULL;
2163  }
2164 
2165  /*
2166  * OK, slurp in the file. If we get a short read and errno doesn't get
2167  * set, the reason is probably that garbage collection truncated the file
2168  * since we did the fstat(), so we don't log a complaint --- but we don't
2169  * return the data, either, since it's most likely corrupt due to
2170  * concurrent writes from garbage collection.
2171  */
2172  errno = 0;
2173  if (read(fd, buf, stat.st_size) != stat.st_size)
2174  {
2175  if (errno)
2176  ereport(LOG,
2178  errmsg("could not read file \"%s\": %m",
2179  PGSS_TEXT_FILE)));
2180  free(buf);
2181  CloseTransientFile(fd);
2182  return NULL;
2183  }
2184 
2185  if (CloseTransientFile(fd) != 0)
2186  ereport(LOG,
2188  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
2189 
2190  *buffer_size = stat.st_size;
2191  return buf;
2192 }
int errcode(int sqlerrcode)
Definition: elog.c:698
#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:1271
#define MaxAllocHugeSize
Definition: memutils.h:44
#define malloc(a)
Definition: header.h:50
#define fstat
Definition: win32_port.h:274
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2423
static char * buf
Definition: pg_test_fsync.c:68
int errdetail(const char *fmt,...)
Definition: elog.c:1042
int errcode_for_file_access(void)
Definition: elog.c:721
__int64 st_size
Definition: win32_port.h:265
int CloseTransientFile(int fd)
Definition: fd.c:2600
#define ereport(elevel,...)
Definition: elog.h:157
#define free(a)
Definition: header.h:65
int errmsg(const char *fmt,...)
Definition: elog.c:909
#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 2041 of file pg_stat_statements.c.

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

Referenced by pgss_store().

2043 {
2044  Size off;
2045  int fd;
2046 
2047  /*
2048  * We use a spinlock to protect extent/n_writers/gc_count, so that
2049  * multiple processes may execute this function concurrently.
2050  */
2051  {
2052  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2053 
2054  SpinLockAcquire(&s->mutex);
2055  off = s->extent;
2056  s->extent += query_len + 1;
2057  s->n_writers++;
2058  if (gc_count)
2059  *gc_count = s->gc_count;
2060  SpinLockRelease(&s->mutex);
2061  }
2062 
2063  *query_offset = off;
2064 
2065  /* Now write the data into the successfully-reserved part of the file */
2066  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDWR | O_CREAT | PG_BINARY);
2067  if (fd < 0)
2068  goto error;
2069 
2070  if (pg_pwrite(fd, query, query_len, off) != query_len)
2071  goto error;
2072  if (pg_pwrite(fd, "\0", 1, off + query_len) != 1)
2073  goto error;
2074 
2075  CloseTransientFile(fd);
2076 
2077  /* Mark our write complete */
2078  {
2079  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2080 
2081  SpinLockAcquire(&s->mutex);
2082  s->n_writers--;
2083  SpinLockRelease(&s->mutex);
2084  }
2085 
2086  return true;
2087 
2088 error:
2089  ereport(LOG,
2091  errmsg("could not write file \"%s\": %m",
2092  PGSS_TEXT_FILE)));
2093 
2094  if (fd >= 0)
2095  CloseTransientFile(fd);
2096 
2097  /* Mark our write complete */
2098  {
2099  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2100 
2101  SpinLockAcquire(&s->mutex);
2102  s->n_writers--;
2103  SpinLockRelease(&s->mutex);
2104  }
2105 
2106  return false;
2107 }
static void error(void)
Definition: sql-dyntest.c:147
#define LOG
Definition: elog.h:26
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1271
ssize_t pg_pwrite(int fd, const void *buf, size_t nbyte, off_t offset)
Definition: pwrite.c:27
static pgssSharedState * pgss
#define SpinLockAcquire(lock)
Definition: spin.h:62
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2423
int errcode_for_file_access(void)
Definition: elog.c:721
int CloseTransientFile(int fd)
Definition: fd.c:2600
#define SpinLockRelease(lock)
Definition: spin.h:64
#define ereport(elevel,...)
Definition: elog.h:157
size_t Size
Definition: c.h:540
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define PGSS_TEXT_FILE

Variable Documentation

◆ exec_nested_level

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 75 of file pg_stat_statements.c.

◆ pgss

pgssSharedState* pgss = NULL
static

Definition at line 254 of file pg_stat_statements.c.

◆ PGSS_FILE_HEADER

const uint32 PGSS_FILE_HEADER = 0x20201227
static

Definition at line 91 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 255 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 94 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_save

bool pgss_save
static

Definition at line 278 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 275 of file pg_stat_statements.c.

Referenced by _PG_init().

◆ pgss_track_planning

bool pgss_track_planning
static

Definition at line 277 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_planner().

◆ pgss_track_utility

bool pgss_track_utility
static

Definition at line 276 of file pg_stat_statements.c.

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

◆ plan_nested_level

int plan_nested_level = 0
static

Definition at line 241 of file pg_stat_statements.c.

Referenced by pgss_planner().

◆ prev_ExecutorEnd

ExecutorEnd_hook_type prev_ExecutorEnd = NULL
static

Definition at line 250 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 249 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 248 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 247 of file pg_stat_statements.c.

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

◆ prev_planner_hook

planner_hook_type prev_planner_hook = NULL
static

Definition at line 246 of file pg_stat_statements.c.

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

◆ prev_post_parse_analyze_hook

post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL
static

Definition at line 245 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 251 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 244 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 266 of file pg_stat_statements.c.