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 "jit/jit.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/queryjumble.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/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_V1_10   43
 
#define PG_STAT_STATEMENTS_COLS   43 /* 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 , PGSS_V1_10
}
 
enum  pgssStoreKind { PGSS_INVALID = -1 , PGSS_PLAN = 0 , PGSS_EXEC , PGSS_NUMKIND }
 
enum  PGSSTrackLevel { PGSS_TRACK_NONE , PGSS_TRACK_TOP , PGSS_TRACK_ALL }
 

Functions

 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_1_10)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements)
 
 PG_FUNCTION_INFO_V1 (pg_stat_statements_info)
 
static void pgss_shmem_request (void)
 
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, bool readOnlyTree, 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, const struct JitInstrumentation *jitusage, 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)
 
void _PG_init (void)
 
Datum pg_stat_statements_reset_1_7 (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_reset (PG_FUNCTION_ARGS)
 
Datum pg_stat_statements_1_10 (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 = 0x20220408
 
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_request_hook_type prev_shmem_request_hook = NULL
 
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 = 5000
 
static int pgss_track = PGSS_TRACK_TOP
 
static bool pgss_track_utility = true
 
static bool pgss_track_planning = false
 
static bool pgss_save = true
 

Macro Definition Documentation

◆ ASSUMED_LENGTH_INIT

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

Definition at line 96 of file pg_stat_statements.c.

◆ ASSUMED_MEDIAN_INIT

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

Definition at line 95 of file pg_stat_statements.c.

◆ IS_STICKY

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

Definition at line 100 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS

#define PG_STAT_STATEMENTS_COLS   43 /* maximum of above */

Definition at line 1467 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_0

#define PG_STAT_STATEMENTS_COLS_V1_0   14

Definition at line 1460 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_1

#define PG_STAT_STATEMENTS_COLS_V1_1   18

Definition at line 1461 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_10

#define PG_STAT_STATEMENTS_COLS_V1_10   43

Definition at line 1466 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_2

#define PG_STAT_STATEMENTS_COLS_V1_2   19

Definition at line 1462 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_3

#define PG_STAT_STATEMENTS_COLS_V1_3   23

Definition at line 1463 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_8

#define PG_STAT_STATEMENTS_COLS_V1_8   32

Definition at line 1464 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_9

#define PG_STAT_STATEMENTS_COLS_V1_9   33

Definition at line 1465 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_INFO_COLS

#define PG_STAT_STATEMENTS_INFO_COLS   2

Definition at line 1860 of file pg_stat_statements.c.

◆ PGSS_DUMP_FILE

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"

Definition at line 79 of file pg_stat_statements.c.

◆ pgss_enabled

#define pgss_enabled (   level)
Value:
(pgss_track == PGSS_TRACK_TOP && (level) == 0)))
#define IsParallelWorker()
Definition: parallel.h:61
static int pgss_track
@ PGSS_TRACK_ALL
@ PGSS_TRACK_TOP

Definition at line 294 of file pg_stat_statements.c.

◆ PGSS_HANDLED_UTILITY

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

Definition at line 106 of file pg_stat_statements.c.

◆ PGSS_TEXT_FILE

#define PGSS_TEXT_FILE   PG_STAT_TMP_DIR "/pgss_query_texts.stat"

Definition at line 84 of file pg_stat_statements.c.

◆ 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 299 of file pg_stat_statements.c.

◆ STICKY_DECREASE_FACTOR

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

Definition at line 98 of file pg_stat_statements.c.

◆ USAGE_DEALLOC_PERCENT

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

Definition at line 99 of file pg_stat_statements.c.

◆ USAGE_DECREASE_FACTOR

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

Definition at line 97 of file pg_stat_statements.c.

◆ USAGE_EXEC

#define USAGE_EXEC (   duration)    (1.0)

Definition at line 93 of file pg_stat_statements.c.

◆ USAGE_INIT

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

Definition at line 94 of file pg_stat_statements.c.

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 124 of file pg_stat_statements.c.

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

◆ PGSSTrackLevel

Enumerator
PGSS_TRACK_NONE 
PGSS_TRACK_TOP 
PGSS_TRACK_ALL 

Definition at line 271 of file pg_stat_statements.c.

272 {
273  PGSS_TRACK_NONE, /* track no statements */
274  PGSS_TRACK_TOP, /* only top level statements */
275  PGSS_TRACK_ALL /* all statements, including nested ones */
PGSSTrackLevel
@ PGSS_TRACK_NONE

◆ pgssVersion

Enumerator
PGSS_V1_0 
PGSS_V1_1 
PGSS_V1_2 
PGSS_V1_3 
PGSS_V1_8 
PGSS_V1_9 
PGSS_V1_10 

Definition at line 113 of file pg_stat_statements.c.

114 {
115  PGSS_V1_0 = 0,
116  PGSS_V1_1,
117  PGSS_V1_2,
118  PGSS_V1_3,
119  PGSS_V1_8,
120  PGSS_V1_9,
121  PGSS_V1_10
122 } pgssVersion;
@ PGSS_V1_9
@ PGSS_V1_10
@ PGSS_V1_1
@ PGSS_V1_3
@ PGSS_V1_2
@ PGSS_V1_8
@ PGSS_V1_0

Function Documentation

◆ _PG_init()

void _PG_init ( void  )

Definition at line 373 of file pg_stat_statements.c.

374 {
375  /*
376  * In order to create our shared memory area, we have to be loaded via
377  * shared_preload_libraries. If not, fall out without hooking into any of
378  * the main system. (We don't throw error here because it seems useful to
379  * allow the pg_stat_statements functions to be created even when the
380  * module isn't active. The functions must protect themselves against
381  * being called then, however.)
382  */
384  return;
385 
386  /*
387  * Inform the postmaster that we want to enable query_id calculation if
388  * compute_query_id is set to auto.
389  */
390  EnableQueryId();
391 
392  /*
393  * Define (or redefine) custom GUC variables.
394  */
395  DefineCustomIntVariable("pg_stat_statements.max",
396  "Sets the maximum number of statements tracked by pg_stat_statements.",
397  NULL,
398  &pgss_max,
399  5000,
400  100,
401  INT_MAX / 2,
403  0,
404  NULL,
405  NULL,
406  NULL);
407 
408  DefineCustomEnumVariable("pg_stat_statements.track",
409  "Selects which statements are tracked by pg_stat_statements.",
410  NULL,
411  &pgss_track,
414  PGC_SUSET,
415  0,
416  NULL,
417  NULL,
418  NULL);
419 
420  DefineCustomBoolVariable("pg_stat_statements.track_utility",
421  "Selects whether utility commands are tracked by pg_stat_statements.",
422  NULL,
424  true,
425  PGC_SUSET,
426  0,
427  NULL,
428  NULL,
429  NULL);
430 
431  DefineCustomBoolVariable("pg_stat_statements.track_planning",
432  "Selects whether planning duration is tracked by pg_stat_statements.",
433  NULL,
435  false,
436  PGC_SUSET,
437  0,
438  NULL,
439  NULL,
440  NULL);
441 
442  DefineCustomBoolVariable("pg_stat_statements.save",
443  "Save pg_stat_statements statistics across server shutdowns.",
444  NULL,
445  &pgss_save,
446  true,
447  PGC_SIGHUP,
448  0,
449  NULL,
450  NULL,
451  NULL);
452 
453  MarkGUCPrefixReserved("pg_stat_statements");
454 
455  /*
456  * Install hooks.
457  */
476 }
ExecutorEnd_hook_type ExecutorEnd_hook
Definition: execMain.c:76
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:75
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:73
ExecutorRun_hook_type ExecutorRun_hook
Definition: execMain.c:74
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:5069
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:4958
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5105
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:4984
@ PGC_SUSET
Definition: guc.h:74
@ PGC_POSTMASTER
Definition: guc.h:70
@ PGC_SIGHUP
Definition: guc.h:71
shmem_startup_hook_type shmem_startup_hook
Definition: ipci.c:56
shmem_request_hook_type shmem_request_hook
Definition: miscinit.c:1785
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1782
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:59
static planner_hook_type prev_planner_hook
static bool pgss_track_planning
static ExecutorRun_hook_type prev_ExecutorRun
static PlannedStmt * pgss_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
static int pgss_max
static const struct config_enum_entry track_options[]
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
static post_parse_analyze_hook_type prev_post_parse_analyze_hook
static shmem_startup_hook_type prev_shmem_startup_hook
static shmem_request_hook_type prev_shmem_request_hook
static void pgss_shmem_request(void)
static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
static bool pgss_save
static void pgss_shmem_startup(void)
static ExecutorStart_hook_type prev_ExecutorStart
static void pgss_ExecutorFinish(QueryDesc *queryDesc)
static ProcessUtility_hook_type prev_ProcessUtility
static void pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
static bool pgss_track_utility
static ExecutorEnd_hook_type prev_ExecutorEnd
static void pgss_ExecutorEnd(QueryDesc *queryDesc)
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
static ExecutorFinish_hook_type prev_ExecutorFinish
planner_hook_type planner_hook
Definition: planner.c:77
void EnableQueryId(void)
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:77

References DefineCustomBoolVariable(), DefineCustomEnumVariable(), DefineCustomIntVariable(), EnableQueryId(), ExecutorEnd_hook, ExecutorFinish_hook, ExecutorRun_hook, ExecutorStart_hook, MarkGUCPrefixReserved(), PGC_POSTMASTER, PGC_SIGHUP, PGC_SUSET, pgss_ExecutorEnd(), pgss_ExecutorFinish(), pgss_ExecutorRun(), pgss_ExecutorStart(), pgss_max, pgss_planner(), pgss_post_parse_analyze(), pgss_ProcessUtility(), pgss_save, pgss_shmem_request(), 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_request_hook, prev_shmem_startup_hook, process_shared_preload_libraries_in_progress, ProcessUtility_hook, shmem_request_hook, shmem_startup_hook, and track_options.

◆ comp_location()

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

Definition at line 2866 of file pg_stat_statements.c.

2867 {
2868  int l = ((const LocationLen *) a)->location;
2869  int r = ((const LocationLen *) b)->location;
2870 
2871  if (l < r)
2872  return -1;
2873  else if (l > r)
2874  return +1;
2875  else
2876  return 0;
2877 }
int b
Definition: isn.c:70
int a
Definition: isn.c:69

References a, and b.

Referenced by fill_in_constant_lengths().

◆ entry_alloc()

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

Definition at line 1929 of file pg_stat_statements.c.

1931 {
1932  pgssEntry *entry;
1933  bool found;
1934 
1935  /* Make space if needed */
1937  entry_dealloc();
1938 
1939  /* Find or create an entry with desired hash code */
1940  entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
1941 
1942  if (!found)
1943  {
1944  /* New entry, initialize it */
1945 
1946  /* reset the statistics */
1947  memset(&entry->counters, 0, sizeof(Counters));
1948  /* set the appropriate initial usage count */
1949  entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
1950  /* re-initialize the mutex each time ... we assume no one using it */
1951  SpinLockInit(&entry->mutex);
1952  /* ... and don't forget the query text metadata */
1953  Assert(query_len >= 0);
1954  entry->query_offset = query_offset;
1955  entry->query_len = query_len;
1956  entry->encoding = encoding;
1957  }
1958 
1959  return entry;
1960 }
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:953
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1377
@ HASH_ENTER
Definition: hsearch.h:114
Assert(fmt[strlen(fmt) - 1] !='\n')
int32 encoding
Definition: pg_database.h:41
static void entry_dealloc(void)
#define USAGE_INIT
static HTAB * pgss_hash
#define SpinLockInit(lock)
Definition: spin.h:60
Counters counters

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

Referenced by pgss_shmem_startup(), and pgss_store().

◆ entry_cmp()

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

Definition at line 1966 of file pg_stat_statements.c.

1967 {
1968  double l_usage = (*(pgssEntry *const *) lhs)->counters.usage;
1969  double r_usage = (*(pgssEntry *const *) rhs)->counters.usage;
1970 
1971  if (l_usage < r_usage)
1972  return -1;
1973  else if (l_usage > r_usage)
1974  return +1;
1975  else
1976  return 0;
1977 }

Referenced by entry_dealloc().

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

Definition at line 1985 of file pg_stat_statements.c.

1986 {
1987  HASH_SEQ_STATUS hash_seq;
1988  pgssEntry **entries;
1989  pgssEntry *entry;
1990  int nvictims;
1991  int i;
1992  Size tottextlen;
1993  int nvalidtexts;
1994 
1995  /*
1996  * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
1997  * While we're scanning the table, apply the decay factor to the usage
1998  * values, and update the mean query length.
1999  *
2000  * Note that the mean query length is almost immediately obsolete, since
2001  * we compute it before not after discarding the least-used entries.
2002  * Hopefully, that doesn't affect the mean too much; it doesn't seem worth
2003  * making two passes to get a more current result. Likewise, the new
2004  * cur_median_usage includes the entries we're about to zap.
2005  */
2006 
2007  entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *));
2008 
2009  i = 0;
2010  tottextlen = 0;
2011  nvalidtexts = 0;
2012 
2013  hash_seq_init(&hash_seq, pgss_hash);
2014  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2015  {
2016  entries[i++] = entry;
2017  /* "Sticky" entries get a different usage decay rate. */
2018  if (IS_STICKY(entry->counters))
2020  else
2022  /* In the mean length computation, ignore dropped texts. */
2023  if (entry->query_len >= 0)
2024  {
2025  tottextlen += entry->query_len + 1;
2026  nvalidtexts++;
2027  }
2028  }
2029 
2030  /* Sort into increasing order by usage */
2031  qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
2032 
2033  /* Record the (approximate) median usage */
2034  if (i > 0)
2035  pgss->cur_median_usage = entries[i / 2]->counters.usage;
2036  /* Record the mean query length */
2037  if (nvalidtexts > 0)
2038  pgss->mean_query_len = tottextlen / nvalidtexts;
2039  else
2041 
2042  /* Now zap an appropriate fraction of lowest-usage entries */
2043  nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
2044  nvictims = Min(nvictims, i);
2045 
2046  for (i = 0; i < nvictims; i++)
2047  {
2048  hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL);
2049  }
2050 
2051  pfree(entries);
2052 
2053  /* Increment the number of times entries are deallocated */
2054  {
2055  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2056 
2057  SpinLockAcquire(&s->mutex);
2058  s->stats.dealloc += 1;
2059  SpinLockRelease(&s->mutex);
2060  }
2061 }
#define Min(x, y)
Definition: c.h:988
#define Max(x, y)
Definition: c.h:982
size_t Size
Definition: c.h:589
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1431
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1421
@ HASH_REMOVE
Definition: hsearch.h:115
int i
Definition: isn.c:73
void pfree(void *pointer)
Definition: mcxt.c:1436
void * palloc(Size size)
Definition: mcxt.c:1210
static int entry_cmp(const void *lhs, const void *rhs)
#define USAGE_DEALLOC_PERCENT
#define STICKY_DECREASE_FACTOR
#define IS_STICKY(c)
#define ASSUMED_LENGTH_INIT
#define USAGE_DECREASE_FACTOR
#define qsort(a, b, c, d)
Definition: port.h:445
#define SpinLockRelease(lock)
Definition: spin.h:64
#define SpinLockAcquire(lock)
Definition: spin.h:62
pgssGlobalStats stats

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(), pgss, pgss_hash, qsort, pgssEntry::query_len, SpinLockAcquire, SpinLockRelease, pgssSharedState::stats, STICKY_DECREASE_FACTOR, Counters::usage, USAGE_DEALLOC_PERCENT, and USAGE_DECREASE_FACTOR.

Referenced by entry_alloc().

◆ entry_reset()

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

Definition at line 2524 of file pg_stat_statements.c.

2525 {
2526  HASH_SEQ_STATUS hash_seq;
2527  pgssEntry *entry;
2528  FILE *qfile;
2529  long num_entries;
2530  long num_remove = 0;
2531  pgssHashKey key;
2532 
2533  if (!pgss || !pgss_hash)
2534  ereport(ERROR,
2535  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2536  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
2537 
2539  num_entries = hash_get_num_entries(pgss_hash);
2540 
2541  if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
2542  {
2543  /* If all the parameters are available, use the fast path. */
2544  memset(&key, 0, sizeof(pgssHashKey));
2545  key.userid = userid;
2546  key.dbid = dbid;
2547  key.queryid = queryid;
2548 
2549  /* Remove the key if it exists, starting with the top-level entry */
2550  key.toplevel = false;
2551  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2552  if (entry) /* found */
2553  num_remove++;
2554 
2555  /* Also remove entries for top level statements */
2556  key.toplevel = true;
2557 
2558  /* Remove the key if exists */
2559  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
2560  if (entry) /* found */
2561  num_remove++;
2562  }
2563  else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
2564  {
2565  /* Remove entries corresponding to valid parameters. */
2566  hash_seq_init(&hash_seq, pgss_hash);
2567  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2568  {
2569  if ((!userid || entry->key.userid == userid) &&
2570  (!dbid || entry->key.dbid == dbid) &&
2571  (!queryid || entry->key.queryid == queryid))
2572  {
2573  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2574  num_remove++;
2575  }
2576  }
2577  }
2578  else
2579  {
2580  /* Remove all entries. */
2581  hash_seq_init(&hash_seq, pgss_hash);
2582  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2583  {
2584  hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
2585  num_remove++;
2586  }
2587  }
2588 
2589  /* All entries are removed? */
2590  if (num_entries != num_remove)
2591  goto release_lock;
2592 
2593  /*
2594  * Reset global statistics for pg_stat_statements since all entries are
2595  * removed.
2596  */
2597  {
2598  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2599  TimestampTz stats_reset = GetCurrentTimestamp();
2600 
2601  SpinLockAcquire(&s->mutex);
2602  s->stats.dealloc = 0;
2603  s->stats.stats_reset = stats_reset;
2604  SpinLockRelease(&s->mutex);
2605  }
2606 
2607  /*
2608  * Write new empty query file, perhaps even creating a new one to recover
2609  * if the file was missing.
2610  */
2612  if (qfile == NULL)
2613  {
2614  ereport(LOG,
2616  errmsg("could not create file \"%s\": %m",
2617  PGSS_TEXT_FILE)));
2618  goto done;
2619  }
2620 
2621  /* If ftruncate fails, log it, but it's not a fatal problem */
2622  if (ftruncate(fileno(qfile), 0) != 0)
2623  ereport(LOG,
2625  errmsg("could not truncate file \"%s\": %m",
2626  PGSS_TEXT_FILE)));
2627 
2628  FreeFile(qfile);
2629 
2630 done:
2631  pgss->extent = 0;
2632  /* This counts as a query text garbage collection for our purposes */
2633  record_gc_qtexts();
2634 
2635 release_lock:
2637 }
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1582
#define PG_BINARY_W
Definition: c.h:1263
int64 TimestampTz
Definition: timestamp.h:39
int errcode_for_file_access(void)
Definition: elog.c:881
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define LOG
Definition: elog.h:31
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
FILE * AllocateFile(const char *name, const char *mode)
Definition: fd.c:2384
int FreeFile(FILE *file)
Definition: fd.c:2582
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1195
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1803
@ LW_EXCLUSIVE
Definition: lwlock.h:115
#define record_gc_qtexts()
#define PGSS_TEXT_FILE
pgssHashKey key
TimestampTz stats_reset
#define ftruncate(a, b)
Definition: win32_port.h:82

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(), pgssEntry::key, sort-test::key, pgssSharedState::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), pgssSharedState::mutex, PG_BINARY_W, pgss, pgss_hash, PGSS_TEXT_FILE, pgssHashKey::queryid, record_gc_qtexts, SpinLockAcquire, SpinLockRelease, pgssSharedState::stats, pgssGlobalStats::stats_reset, and pgssHashKey::userid.

Referenced by pg_stat_statements_reset(), and pg_stat_statements_reset_1_7().

◆ fill_in_constant_lengths()

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

Definition at line 2766 of file pg_stat_statements.c.

2768 {
2769  LocationLen *locs;
2771  core_yy_extra_type yyextra;
2772  core_YYSTYPE yylval;
2773  YYLTYPE yylloc;
2774  int last_loc = -1;
2775  int i;
2776 
2777  /*
2778  * Sort the records by location so that we can process them in order while
2779  * scanning the query text.
2780  */
2781  if (jstate->clocations_count > 1)
2782  qsort(jstate->clocations, jstate->clocations_count,
2783  sizeof(LocationLen), comp_location);
2784  locs = jstate->clocations;
2785 
2786  /* initialize the flex scanner --- should match raw_parser() */
2787  yyscanner = scanner_init(query,
2788  &yyextra,
2789  &ScanKeywords,
2791 
2792  /* we don't want to re-emit any escape string warnings */
2793  yyextra.escape_string_warning = false;
2794 
2795  /* Search for each constant, in sequence */
2796  for (i = 0; i < jstate->clocations_count; i++)
2797  {
2798  int loc = locs[i].location;
2799  int tok;
2800 
2801  /* Adjust recorded location if we're dealing with partial string */
2802  loc -= query_loc;
2803 
2804  Assert(loc >= 0);
2805 
2806  if (loc <= last_loc)
2807  continue; /* Duplicate constant, ignore */
2808 
2809  /* Lex tokens until we find the desired constant */
2810  for (;;)
2811  {
2812  tok = core_yylex(&yylval, &yylloc, yyscanner);
2813 
2814  /* We should not hit end-of-string, but if we do, behave sanely */
2815  if (tok == 0)
2816  break; /* out of inner for-loop */
2817 
2818  /*
2819  * We should find the token position exactly, but if we somehow
2820  * run past it, work with that.
2821  */
2822  if (yylloc >= loc)
2823  {
2824  if (query[loc] == '-')
2825  {
2826  /*
2827  * It's a negative value - this is the one and only case
2828  * where we replace more than a single token.
2829  *
2830  * Do not compensate for the core system's special-case
2831  * adjustment of location to that of the leading '-'
2832  * operator in the event of a negative constant. It is
2833  * also useful for our purposes to start from the minus
2834  * symbol. In this way, queries like "select * from foo
2835  * where bar = 1" and "select * from foo where bar = -2"
2836  * will have identical normalized query strings.
2837  */
2838  tok = core_yylex(&yylval, &yylloc, yyscanner);
2839  if (tok == 0)
2840  break; /* out of inner for-loop */
2841  }
2842 
2843  /*
2844  * We now rely on the assumption that flex has placed a zero
2845  * byte after the text of the current token in scanbuf.
2846  */
2847  locs[i].length = strlen(yyextra.scanbuf + loc);
2848  break; /* out of inner for-loop */
2849  }
2850  }
2851 
2852  /* If we hit end-of-string, give up, leaving remaining lengths -1 */
2853  if (tok == 0)
2854  break;
2855 
2856  last_loc = loc;
2857  }
2858 
2860 }
PGDLLIMPORT const ScanKeywordList ScanKeywords
static int comp_location(const void *a, const void *b)
static core_yyscan_t yyscanner
Definition: pl_scanner.c:106
#define YYLTYPE
Definition: scanner.h:44
core_yyscan_t scanner_init(const char *str, core_yy_extra_type *yyext, const ScanKeywordList *keywordlist, const uint16 *keyword_tokens)
void scanner_finish(core_yyscan_t yyscanner)
PGDLLIMPORT const uint16 ScanKeywordTokens[]
void * core_yyscan_t
Definition: scanner.h:121
int core_yylex(core_YYSTYPE *yylval_param, YYLTYPE *yylloc_param, core_yyscan_t yyscanner)
LocationLen * clocations
Definition: queryjumble.h:41
int clocations_count
Definition: queryjumble.h:47
bool escape_string_warning
Definition: scanner.h:88
char * scanbuf
Definition: scanner.h:72

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

◆ gc_qtexts()

static void gc_qtexts ( void  )
static

Definition at line 2341 of file pg_stat_statements.c.

2342 {
2343  char *qbuffer;
2344  Size qbuffer_size;
2345  FILE *qfile = NULL;
2346  HASH_SEQ_STATUS hash_seq;
2347  pgssEntry *entry;
2348  Size extent;
2349  int nentries;
2350 
2351  /*
2352  * When called from pgss_store, some other session might have proceeded
2353  * with garbage collection in the no-lock-held interim of lock strength
2354  * escalation. Check once more that this is actually necessary.
2355  */
2356  if (!need_gc_qtexts())
2357  return;
2358 
2359  /*
2360  * Load the old texts file. If we fail (out of memory, for instance),
2361  * invalidate query texts. Hopefully this is rare. It might seem better
2362  * to leave things alone on an OOM failure, but the problem is that the
2363  * file is only going to get bigger; hoping for a future non-OOM result is
2364  * risky and can easily lead to complete denial of service.
2365  */
2366  qbuffer = qtext_load_file(&qbuffer_size);
2367  if (qbuffer == NULL)
2368  goto gc_fail;
2369 
2370  /*
2371  * We overwrite the query texts file in place, so as to reduce the risk of
2372  * an out-of-disk-space failure. Since the file is guaranteed not to get
2373  * larger, this should always work on traditional filesystems; though we
2374  * could still lose on copy-on-write filesystems.
2375  */
2377  if (qfile == NULL)
2378  {
2379  ereport(LOG,
2381  errmsg("could not write file \"%s\": %m",
2382  PGSS_TEXT_FILE)));
2383  goto gc_fail;
2384  }
2385 
2386  extent = 0;
2387  nentries = 0;
2388 
2389  hash_seq_init(&hash_seq, pgss_hash);
2390  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2391  {
2392  int query_len = entry->query_len;
2393  char *qry = qtext_fetch(entry->query_offset,
2394  query_len,
2395  qbuffer,
2396  qbuffer_size);
2397 
2398  if (qry == NULL)
2399  {
2400  /* Trouble ... drop the text */
2401  entry->query_offset = 0;
2402  entry->query_len = -1;
2403  /* entry will not be counted in mean query length computation */
2404  continue;
2405  }
2406 
2407  if (fwrite(qry, 1, query_len + 1, qfile) != query_len + 1)
2408  {
2409  ereport(LOG,
2411  errmsg("could not write file \"%s\": %m",
2412  PGSS_TEXT_FILE)));
2413  hash_seq_term(&hash_seq);
2414  goto gc_fail;
2415  }
2416 
2417  entry->query_offset = extent;
2418  extent += query_len + 1;
2419  nentries++;
2420  }
2421 
2422  /*
2423  * Truncate away any now-unused space. If this fails for some odd reason,
2424  * we log it, but there's no need to fail.
2425  */
2426  if (ftruncate(fileno(qfile), extent) != 0)
2427  ereport(LOG,
2429  errmsg("could not truncate file \"%s\": %m",
2430  PGSS_TEXT_FILE)));
2431 
2432  if (FreeFile(qfile))
2433  {
2434  ereport(LOG,
2436  errmsg("could not write file \"%s\": %m",
2437  PGSS_TEXT_FILE)));
2438  qfile = NULL;
2439  goto gc_fail;
2440  }
2441 
2442  elog(DEBUG1, "pgss gc of queries file shrunk size from %zu to %zu",
2443  pgss->extent, extent);
2444 
2445  /* Reset the shared extent pointer */
2446  pgss->extent = extent;
2447 
2448  /*
2449  * Also update the mean query length, to be sure that need_gc_qtexts()
2450  * won't still think we have a problem.
2451  */
2452  if (nentries > 0)
2453  pgss->mean_query_len = extent / nentries;
2454  else
2456 
2457  free(qbuffer);
2458 
2459  /*
2460  * OK, count a garbage collection cycle. (Note: even though we have
2461  * exclusive lock on pgss->lock, we must take pgss->mutex for this, since
2462  * other processes may examine gc_count while holding only the mutex.
2463  * Also, we have to advance the count *after* we've rewritten the file,
2464  * else other processes might not realize they read a stale file.)
2465  */
2466  record_gc_qtexts();
2467 
2468  return;
2469 
2470 gc_fail:
2471  /* clean up resources */
2472  if (qfile)
2473  FreeFile(qfile);
2474  free(qbuffer);
2475 
2476  /*
2477  * Since the contents of the external file are now uncertain, mark all
2478  * hashtable entries as having invalid texts.
2479  */
2480  hash_seq_init(&hash_seq, pgss_hash);
2481  while ((entry = hash_seq_search(&hash_seq)) != NULL)
2482  {
2483  entry->query_offset = 0;
2484  entry->query_len = -1;
2485  }
2486 
2487  /*
2488  * Destroy the query text file and create a new, empty one
2489  */
2490  (void) unlink(PGSS_TEXT_FILE);
2492  if (qfile == NULL)
2493  ereport(LOG,
2495  errmsg("could not recreate file \"%s\": %m",
2496  PGSS_TEXT_FILE)));
2497  else
2498  FreeFile(qfile);
2499 
2500  /* Reset the shared extent pointer */
2501  pgss->extent = 0;
2502 
2503  /* Reset mean_query_len to match the new state */
2505 
2506  /*
2507  * Bump the GC count even though we failed.
2508  *
2509  * This is needed to make concurrent readers of file without any lock on
2510  * pgss->lock notice existence of new version of file. Once readers
2511  * subsequently observe a change in GC count with pgss->lock held, that
2512  * forces a safe reopen of file. Writers also require that we bump here,
2513  * of course. (As required by locking protocol, readers and writers don't
2514  * trust earlier file contents until gc_count is found unchanged after
2515  * pgss->lock acquired in shared or exclusive mode respectively.)
2516  */
2517  record_gc_qtexts();
2518 }
void hash_seq_term(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1507
#define DEBUG1
Definition: elog.h:30
#define free(a)
Definition: header.h:65
static char * qtext_fetch(Size query_offset, int query_len, char *buffer, Size buffer_size)
static char * qtext_load_file(Size *buffer_size)
static bool need_gc_qtexts(void)

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, pgss_hash, PGSS_TEXT_FILE, qtext_fetch(), qtext_load_file(), pgssEntry::query_len, pgssEntry::query_offset, and record_gc_qtexts.

Referenced by pgss_store().

◆ generate_normalized_query()

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

Definition at line 2659 of file pg_stat_statements.c.

2661 {
2662  char *norm_query;
2663  int query_len = *query_len_p;
2664  int i,
2665  norm_query_buflen, /* Space allowed for norm_query */
2666  len_to_wrt, /* Length (in bytes) to write */
2667  quer_loc = 0, /* Source query byte location */
2668  n_quer_loc = 0, /* Normalized query byte location */
2669  last_off = 0, /* Offset from start for previous tok */
2670  last_tok_len = 0; /* Length (in bytes) of that tok */
2671 
2672  /*
2673  * Get constants' lengths (core system only gives us locations). Note
2674  * this also ensures the items are sorted by location.
2675  */
2676  fill_in_constant_lengths(jstate, query, query_loc);
2677 
2678  /*
2679  * Allow for $n symbols to be longer than the constants they replace.
2680  * Constants must take at least one byte in text form, while a $n symbol
2681  * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
2682  * could refine that limit based on the max value of n for the current
2683  * query, but it hardly seems worth any extra effort to do so.
2684  */
2685  norm_query_buflen = query_len + jstate->clocations_count * 10;
2686 
2687  /* Allocate result buffer */
2688  norm_query = palloc(norm_query_buflen + 1);
2689 
2690  for (i = 0; i < jstate->clocations_count; i++)
2691  {
2692  int off, /* Offset from start for cur tok */
2693  tok_len; /* Length (in bytes) of that tok */
2694 
2695  off = jstate->clocations[i].location;
2696  /* Adjust recorded location if we're dealing with partial string */
2697  off -= query_loc;
2698 
2699  tok_len = jstate->clocations[i].length;
2700 
2701  if (tok_len < 0)
2702  continue; /* ignore any duplicates */
2703 
2704  /* Copy next chunk (what precedes the next constant) */
2705  len_to_wrt = off - last_off;
2706  len_to_wrt -= last_tok_len;
2707 
2708  Assert(len_to_wrt >= 0);
2709  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
2710  n_quer_loc += len_to_wrt;
2711 
2712  /* And insert a param symbol in place of the constant token */
2713  n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
2714  i + 1 + jstate->highest_extern_param_id);
2715 
2716  quer_loc = off + tok_len;
2717  last_off = off;
2718  last_tok_len = tok_len;
2719  }
2720 
2721  /*
2722  * We've copied up until the last ignorable constant. Copy over the
2723  * remaining bytes of the original query string.
2724  */
2725  len_to_wrt = query_len - quer_loc;
2726 
2727  Assert(len_to_wrt >= 0);
2728  memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
2729  n_quer_loc += len_to_wrt;
2730 
2731  Assert(n_quer_loc <= norm_query_buflen);
2732  norm_query[n_quer_loc] = '\0';
2733 
2734  *query_len_p = n_quer_loc;
2735  return norm_query;
2736 }
static void fill_in_constant_lengths(JumbleState *jstate, const char *query, int query_loc)
#define sprintf
Definition: port.h:240
int highest_extern_param_id
Definition: queryjumble.h:50

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

◆ need_gc_qtexts()

static bool need_gc_qtexts ( void  )
static

Definition at line 2288 of file pg_stat_statements.c.

2289 {
2290  Size extent;
2291 
2292  /* Read shared extent pointer */
2293  {
2294  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2295 
2296  SpinLockAcquire(&s->mutex);
2297  extent = s->extent;
2298  SpinLockRelease(&s->mutex);
2299  }
2300 
2301  /*
2302  * Don't proceed if file does not exceed 512 bytes per possible entry.
2303  *
2304  * Here and in the next test, 32-bit machines have overflow hazards if
2305  * pgss_max and/or mean_query_len are large. Force the multiplications
2306  * and comparisons to be done in uint64 arithmetic to forestall trouble.
2307  */
2308  if ((uint64) extent < (uint64) 512 * pgss_max)
2309  return false;
2310 
2311  /*
2312  * Don't proceed if file is less than about 50% bloat. Nothing can or
2313  * should be done in the event of unusually large query texts accounting
2314  * for file's large size. We go to the trouble of maintaining the mean
2315  * query length in order to prevent garbage collection from thrashing
2316  * uselessly.
2317  */
2318  if ((uint64) extent < (uint64) pgss->mean_query_len * pgss_max * 2)
2319  return false;
2320 
2321  return true;
2322 }

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

Referenced by gc_qtexts(), and pgss_store().

◆ PG_FUNCTION_INFO_V1() [1/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements  )

◆ PG_FUNCTION_INFO_V1() [2/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_10  )

◆ PG_FUNCTION_INFO_V1() [3/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_2  )

◆ PG_FUNCTION_INFO_V1() [4/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_3  )

◆ PG_FUNCTION_INFO_V1() [5/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_8  )

◆ PG_FUNCTION_INFO_V1() [6/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_1_9  )

◆ PG_FUNCTION_INFO_V1() [7/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_info  )

◆ PG_FUNCTION_INFO_V1() [8/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset  )

◆ PG_FUNCTION_INFO_V1() [9/9]

PG_FUNCTION_INFO_V1 ( pg_stat_statements_reset_1_7  )

◆ pg_stat_statements()

Datum pg_stat_statements ( PG_FUNCTION_ARGS  )

Definition at line 1534 of file pg_stat_statements.c.

1535 {
1536  /* If it's really API 1.1, we'll figure that out below */
1537  pg_stat_statements_internal(fcinfo, PGSS_V1_0, true);
1538 
1539  return (Datum) 0;
1540 }
static void pg_stat_statements_internal(FunctionCallInfo fcinfo, pgssVersion api_version, bool showtext)
uintptr_t Datum
Definition: postgres.h:64

References pg_stat_statements_internal(), and PGSS_V1_0.

◆ pg_stat_statements_1_10()

Datum pg_stat_statements_1_10 ( PG_FUNCTION_ARGS  )

Definition at line 1480 of file pg_stat_statements.c.

1481 {
1482  bool showtext = PG_GETARG_BOOL(0);
1483 
1484  pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
1485 
1486  return (Datum) 0;
1487 }
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_10.

◆ pg_stat_statements_1_2()

Datum pg_stat_statements_1_2 ( PG_FUNCTION_ARGS  )

Definition at line 1520 of file pg_stat_statements.c.

1521 {
1522  bool showtext = PG_GETARG_BOOL(0);
1523 
1524  pg_stat_statements_internal(fcinfo, PGSS_V1_2, showtext);
1525 
1526  return (Datum) 0;
1527 }

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_2.

◆ pg_stat_statements_1_3()

Datum pg_stat_statements_1_3 ( PG_FUNCTION_ARGS  )

Definition at line 1510 of file pg_stat_statements.c.

1511 {
1512  bool showtext = PG_GETARG_BOOL(0);
1513 
1514  pg_stat_statements_internal(fcinfo, PGSS_V1_3, showtext);
1515 
1516  return (Datum) 0;
1517 }

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_3.

◆ pg_stat_statements_1_8()

Datum pg_stat_statements_1_8 ( PG_FUNCTION_ARGS  )

Definition at line 1500 of file pg_stat_statements.c.

1501 {
1502  bool showtext = PG_GETARG_BOOL(0);
1503 
1504  pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
1505 
1506  return (Datum) 0;
1507 }

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_8.

◆ pg_stat_statements_1_9()

Datum pg_stat_statements_1_9 ( PG_FUNCTION_ARGS  )

Definition at line 1490 of file pg_stat_statements.c.

1491 {
1492  bool showtext = PG_GETARG_BOOL(0);
1493 
1494  pg_stat_statements_internal(fcinfo, PGSS_V1_9, showtext);
1495 
1496  return (Datum) 0;
1497 }

References PG_GETARG_BOOL, pg_stat_statements_internal(), and PGSS_V1_9.

◆ pg_stat_statements_info()

Datum pg_stat_statements_info ( PG_FUNCTION_ARGS  )

Definition at line 1866 of file pg_stat_statements.c.

1867 {
1868  pgssGlobalStats stats;
1869  TupleDesc tupdesc;
1871  bool nulls[PG_STAT_STATEMENTS_INFO_COLS] = {0};
1872 
1873  if (!pgss || !pgss_hash)
1874  ereport(ERROR,
1875  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1876  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1877 
1878  /* Build a tuple descriptor for our result type */
1879  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1880  elog(ERROR, "return type must be a row type");
1881 
1882  /* Read global statistics for pg_stat_statements */
1883  {
1884  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1885 
1886  SpinLockAcquire(&s->mutex);
1887  stats = s->stats;
1888  SpinLockRelease(&s->mutex);
1889  }
1890 
1891  values[0] = Int64GetDatum(stats.dealloc);
1893 
1895 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1794
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
#define PG_STAT_STATEMENTS_INFO_COLS
static Datum TimestampTzGetDatum(TimestampTz X)
Definition: timestamp.h:52

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

◆ pg_stat_statements_internal()

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

Definition at line 1544 of file pg_stat_statements.c.

1547 {
1548  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1549  Oid userid = GetUserId();
1550  bool is_allowed_role = false;
1551  char *qbuffer = NULL;
1552  Size qbuffer_size = 0;
1553  Size extent = 0;
1554  int gc_count = 0;
1555  HASH_SEQ_STATUS hash_seq;
1556  pgssEntry *entry;
1557 
1558  /*
1559  * Superusers or roles with the privileges of pg_read_all_stats members
1560  * are allowed
1561  */
1562  is_allowed_role = has_privs_of_role(userid, ROLE_PG_READ_ALL_STATS);
1563 
1564  /* hash table must exist already */
1565  if (!pgss || !pgss_hash)
1566  ereport(ERROR,
1567  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1568  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1569 
1570  InitMaterializedSRF(fcinfo, 0);
1571 
1572  /*
1573  * Check we have the expected number of output arguments. Aside from
1574  * being a good safety check, we need a kluge here to detect API version
1575  * 1.1, which was wedged into the code in an ill-considered way.
1576  */
1577  switch (rsinfo->setDesc->natts)
1578  {
1580  if (api_version != PGSS_V1_0)
1581  elog(ERROR, "incorrect number of output arguments");
1582  break;
1584  /* pg_stat_statements() should have told us 1.0 */
1585  if (api_version != PGSS_V1_0)
1586  elog(ERROR, "incorrect number of output arguments");
1587  api_version = PGSS_V1_1;
1588  break;
1590  if (api_version != PGSS_V1_2)
1591  elog(ERROR, "incorrect number of output arguments");
1592  break;
1594  if (api_version != PGSS_V1_3)
1595  elog(ERROR, "incorrect number of output arguments");
1596  break;
1598  if (api_version != PGSS_V1_8)
1599  elog(ERROR, "incorrect number of output arguments");
1600  break;
1602  if (api_version != PGSS_V1_9)
1603  elog(ERROR, "incorrect number of output arguments");
1604  break;
1606  if (api_version != PGSS_V1_10)
1607  elog(ERROR, "incorrect number of output arguments");
1608  break;
1609  default:
1610  elog(ERROR, "incorrect number of output arguments");
1611  }
1612 
1613  /*
1614  * We'd like to load the query text file (if needed) while not holding any
1615  * lock on pgss->lock. In the worst case we'll have to do this again
1616  * after we have the lock, but it's unlikely enough to make this a win
1617  * despite occasional duplicated work. We need to reload if anybody
1618  * writes to the file (either a retail qtext_store(), or a garbage
1619  * collection) between this point and where we've gotten shared lock. If
1620  * a qtext_store is actually in progress when we look, we might as well
1621  * skip the speculative load entirely.
1622  */
1623  if (showtext)
1624  {
1625  int n_writers;
1626 
1627  /* Take the mutex so we can examine variables */
1628  {
1629  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1630 
1631  SpinLockAcquire(&s->mutex);
1632  extent = s->extent;
1633  n_writers = s->n_writers;
1634  gc_count = s->gc_count;
1635  SpinLockRelease(&s->mutex);
1636  }
1637 
1638  /* No point in loading file now if there are active writers */
1639  if (n_writers == 0)
1640  qbuffer = qtext_load_file(&qbuffer_size);
1641  }
1642 
1643  /*
1644  * Get shared lock, load or reload the query text file if we must, and
1645  * iterate over the hashtable entries.
1646  *
1647  * With a large hash table, we might be holding the lock rather longer
1648  * than one could wish. However, this only blocks creation of new hash
1649  * table entries, and the larger the hash table the less likely that is to
1650  * be needed. So we can hope this is okay. Perhaps someday we'll decide
1651  * we need to partition the hash table to limit the time spent holding any
1652  * one lock.
1653  */
1655 
1656  if (showtext)
1657  {
1658  /*
1659  * Here it is safe to examine extent and gc_count without taking the
1660  * mutex. Note that although other processes might change
1661  * pgss->extent just after we look at it, the strings they then write
1662  * into the file cannot yet be referenced in the hashtable, so we
1663  * don't care whether we see them or not.
1664  *
1665  * If qtext_load_file fails, we just press on; we'll return NULL for
1666  * every query text.
1667  */
1668  if (qbuffer == NULL ||
1669  pgss->extent != extent ||
1670  pgss->gc_count != gc_count)
1671  {
1672  free(qbuffer);
1673  qbuffer = qtext_load_file(&qbuffer_size);
1674  }
1675  }
1676 
1677  hash_seq_init(&hash_seq, pgss_hash);
1678  while ((entry = hash_seq_search(&hash_seq)) != NULL)
1679  {
1681  bool nulls[PG_STAT_STATEMENTS_COLS];
1682  int i = 0;
1683  Counters tmp;
1684  double stddev;
1685  int64 queryid = entry->key.queryid;
1686 
1687  memset(values, 0, sizeof(values));
1688  memset(nulls, 0, sizeof(nulls));
1689 
1690  values[i++] = ObjectIdGetDatum(entry->key.userid);
1691  values[i++] = ObjectIdGetDatum(entry->key.dbid);
1692  if (api_version >= PGSS_V1_9)
1693  values[i++] = BoolGetDatum(entry->key.toplevel);
1694 
1695  if (is_allowed_role || entry->key.userid == userid)
1696  {
1697  if (api_version >= PGSS_V1_2)
1698  values[i++] = Int64GetDatumFast(queryid);
1699 
1700  if (showtext)
1701  {
1702  char *qstr = qtext_fetch(entry->query_offset,
1703  entry->query_len,
1704  qbuffer,
1705  qbuffer_size);
1706 
1707  if (qstr)
1708  {
1709  char *enc;
1710 
1711  enc = pg_any_to_server(qstr,
1712  entry->query_len,
1713  entry->encoding);
1714 
1716 
1717  if (enc != qstr)
1718  pfree(enc);
1719  }
1720  else
1721  {
1722  /* Just return a null if we fail to find the text */
1723  nulls[i++] = true;
1724  }
1725  }
1726  else
1727  {
1728  /* Query text not requested */
1729  nulls[i++] = true;
1730  }
1731  }
1732  else
1733  {
1734  /* Don't show queryid */
1735  if (api_version >= PGSS_V1_2)
1736  nulls[i++] = true;
1737 
1738  /*
1739  * Don't show query text, but hint as to the reason for not doing
1740  * so if it was requested
1741  */
1742  if (showtext)
1743  values[i++] = CStringGetTextDatum("<insufficient privilege>");
1744  else
1745  nulls[i++] = true;
1746  }
1747 
1748  /* copy counters to a local variable to keep locking time short */
1749  {
1750  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1751 
1752  SpinLockAcquire(&e->mutex);
1753  tmp = e->counters;
1754  SpinLockRelease(&e->mutex);
1755  }
1756 
1757  /* Skip entry if unexecuted (ie, it's a pending "sticky" entry) */
1758  if (IS_STICKY(tmp))
1759  continue;
1760 
1761  /* Note that we rely on PGSS_PLAN being 0 and PGSS_EXEC being 1. */
1762  for (int kind = 0; kind < PGSS_NUMKIND; kind++)
1763  {
1764  if (kind == PGSS_EXEC || api_version >= PGSS_V1_8)
1765  {
1766  values[i++] = Int64GetDatumFast(tmp.calls[kind]);
1767  values[i++] = Float8GetDatumFast(tmp.total_time[kind]);
1768  }
1769 
1770  if ((kind == PGSS_EXEC && api_version >= PGSS_V1_3) ||
1771  api_version >= PGSS_V1_8)
1772  {
1773  values[i++] = Float8GetDatumFast(tmp.min_time[kind]);
1774  values[i++] = Float8GetDatumFast(tmp.max_time[kind]);
1775  values[i++] = Float8GetDatumFast(tmp.mean_time[kind]);
1776 
1777  /*
1778  * Note we are calculating the population variance here, not
1779  * the sample variance, as we have data for the whole
1780  * population, so Bessel's correction is not used, and we
1781  * don't divide by tmp.calls - 1.
1782  */
1783  if (tmp.calls[kind] > 1)
1784  stddev = sqrt(tmp.sum_var_time[kind] / tmp.calls[kind]);
1785  else
1786  stddev = 0.0;
1787  values[i++] = Float8GetDatumFast(stddev);
1788  }
1789  }
1790  values[i++] = Int64GetDatumFast(tmp.rows);
1793  if (api_version >= PGSS_V1_1)
1798  if (api_version >= PGSS_V1_1)
1803  if (api_version >= PGSS_V1_1)
1804  {
1807  }
1808  if (api_version >= PGSS_V1_10)
1809  {
1812  }
1813  if (api_version >= PGSS_V1_8)
1814  {
1815  char buf[256];
1816  Datum wal_bytes;
1817 
1819  values[i++] = Int64GetDatumFast(tmp.wal_fpi);
1820 
1821  snprintf(buf, sizeof buf, UINT64_FORMAT, tmp.wal_bytes);
1822 
1823  /* Convert to numeric. */
1824  wal_bytes = DirectFunctionCall3(numeric_in,
1826  ObjectIdGetDatum(0),
1827  Int32GetDatum(-1));
1828  values[i++] = wal_bytes;
1829  }
1830  if (api_version >= PGSS_V1_10)
1831  {
1840  }
1841 
1842  Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
1843  api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
1844  api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
1845  api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
1846  api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
1847  api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
1848  api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
1849  -1 /* fail if you forget to update this assert */ ));
1850 
1851  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1852  }
1853 
1855 
1856  free(qbuffer);
1857 }
bool has_privs_of_role(Oid member, Oid role)
Definition: acl.c:4969
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:627
#define CStringGetTextDatum(s)
Definition: builtins.h:94
#define UINT64_FORMAT
Definition: c.h:533
enc
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:646
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
@ LW_SHARED
Definition: lwlock.h:116
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:677
Oid GetUserId(void)
Definition: miscinit.c:510
#define PG_STAT_STATEMENTS_COLS_V1_0
#define PG_STAT_STATEMENTS_COLS_V1_8
#define PG_STAT_STATEMENTS_COLS
#define PG_STAT_STATEMENTS_COLS_V1_2
#define PG_STAT_STATEMENTS_COLS_V1_3
#define PG_STAT_STATEMENTS_COLS_V1_10
#define PG_STAT_STATEMENTS_COLS_V1_1
#define PG_STAT_STATEMENTS_COLS_V1_9
static char * buf
Definition: pg_test_fsync.c:67
#define snprintf
Definition: port.h:238
#define Int64GetDatumFast(X)
Definition: postgres.h:554
#define Float8GetDatumFast(X)
Definition: postgres.h:556
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
unsigned int Oid
Definition: postgres_ext.h:31
e
Definition: preproc-init.c:82
int64 temp_blks_written
int64 calls[PGSS_NUMKIND]
int64 shared_blks_written
double jit_generation_time
int64 temp_blks_read
double min_time[PGSS_NUMKIND]
int64 local_blks_written
double sum_var_time[PGSS_NUMKIND]
double temp_blk_read_time
int64 jit_emission_count
double jit_emission_time
int64 shared_blks_hit
double jit_optimization_time
double blk_read_time
int64 jit_optimization_count
double total_time[PGSS_NUMKIND]
double max_time[PGSS_NUMKIND]
int64 shared_blks_dirtied
double mean_time[PGSS_NUMKIND]
double temp_blk_write_time
int64 local_blks_dirtied
int64 jit_inlining_count
int64 shared_blks_read
int64 local_blks_hit
int64 local_blks_read
double jit_inlining_time
double blk_write_time
fmNodePtr resultinfo
Definition: fmgr.h:89
TupleDesc setDesc
Definition: execnodes.h:334
Tuplestorestate * setResult
Definition: execnodes.h:333
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750

References Assert(), Counters::blk_read_time, Counters::blk_write_time, BoolGetDatum(), buf, Counters::calls, CStringGetDatum(), CStringGetTextDatum, pgssHashKey::dbid, DirectFunctionCall3, elog(), enc, pgssEntry::encoding, ereport, errcode(), errmsg(), ERROR, pgssSharedState::extent, Float8GetDatumFast, free, pgssSharedState::gc_count, GetUserId(), has_privs_of_role(), hash_seq_init(), hash_seq_search(), i, InitMaterializedSRF(), Int32GetDatum(), Int64GetDatumFast, IS_STICKY, Counters::jit_emission_count, Counters::jit_emission_time, Counters::jit_functions, Counters::jit_generation_time, Counters::jit_inlining_count, Counters::jit_inlining_time, Counters::jit_optimization_count, Counters::jit_optimization_time, 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, Counters::min_time, pgssSharedState::mutex, pgssSharedState::n_writers, TupleDescData::natts, 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_10, 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, PGSS_EXEC, pgss_hash, PGSS_NUMKIND, PGSS_V1_0, PGSS_V1_1, PGSS_V1_10, 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, Counters::rows, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, 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_blk_read_time, Counters::temp_blk_write_time, Counters::temp_blks_read, Counters::temp_blks_written, pgssHashKey::toplevel, Counters::total_time, tuplestore_putvalues(), UINT64_FORMAT, pgssHashKey::userid, values, Counters::wal_bytes, Counters::wal_fpi, and Counters::wal_records.

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

◆ pg_stat_statements_reset()

Datum pg_stat_statements_reset ( PG_FUNCTION_ARGS  )

Definition at line 1452 of file pg_stat_statements.c.

1453 {
1454  entry_reset(0, 0, 0);
1455 
1456  PG_RETURN_VOID();
1457 }
#define PG_RETURN_VOID()
Definition: fmgr.h:349
static void entry_reset(Oid userid, Oid dbid, uint64 queryid)

References entry_reset(), and PG_RETURN_VOID.

◆ pg_stat_statements_reset_1_7()

Datum pg_stat_statements_reset_1_7 ( PG_FUNCTION_ARGS  )

Definition at line 1433 of file pg_stat_statements.c.

1434 {
1435  Oid userid;
1436  Oid dbid;
1437  uint64 queryid;
1438 
1439  userid = PG_GETARG_OID(0);
1440  dbid = PG_GETARG_OID(1);
1441  queryid = (uint64) PG_GETARG_INT64(2);
1442 
1443  entry_reset(userid, dbid, queryid);
1444 
1445  PG_RETURN_VOID();
1446 }
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283

References entry_reset(), PG_GETARG_INT64, PG_GETARG_OID, and PG_RETURN_VOID.

◆ pgss_ExecutorEnd()

static void pgss_ExecutorEnd ( QueryDesc queryDesc)
static

Definition at line 1044 of file pg_stat_statements.c.

1045 {
1046  uint64 queryId = queryDesc->plannedstmt->queryId;
1047 
1048  if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
1050  {
1051  /*
1052  * Make sure stats accumulation is done. (Note: it's okay if several
1053  * levels of hook all do this.)
1054  */
1055  InstrEndLoop(queryDesc->totaltime);
1056 
1057  pgss_store(queryDesc->sourceText,
1058  queryId,
1059  queryDesc->plannedstmt->stmt_location,
1060  queryDesc->plannedstmt->stmt_len,
1061  PGSS_EXEC,
1062  queryDesc->totaltime->total * 1000.0, /* convert to msec */
1063  queryDesc->estate->es_processed,
1064  &queryDesc->totaltime->bufusage,
1065  &queryDesc->totaltime->walusage,
1066  queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
1067  NULL);
1068  }
1069 
1070  if (prev_ExecutorEnd)
1071  prev_ExecutorEnd(queryDesc);
1072  else
1073  standard_ExecutorEnd(queryDesc);
1074 }
void standard_ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:471
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:140
static void pgss_store(const char *query, uint64 queryId, int query_location, int query_len, pgssStoreKind kind, double total_time, uint64 rows, const BufferUsage *bufusage, const WalUsage *walusage, const struct JitInstrumentation *jitusage, JumbleState *jstate)
static int exec_nested_level
#define pgss_enabled(level)
uint64 es_processed
Definition: execnodes.h:664
struct JitContext * es_jit
Definition: execnodes.h:706
WalUsage walusage
Definition: instrument.h:90
BufferUsage bufusage
Definition: instrument.h:89
JitInstrumentation instr
Definition: jit.h:61
int stmt_location
Definition: plannodes.h:102
int stmt_len
Definition: plannodes.h:103
uint64 queryId
Definition: plannodes.h:55
const char * sourceText
Definition: execdesc.h:38
EState * estate
Definition: execdesc.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37
struct Instrumentation * totaltime
Definition: execdesc.h:55

References Instrumentation::bufusage, EState::es_jit, EState::es_processed, QueryDesc::estate, exec_nested_level, JitContext::instr, 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().

◆ pgss_ExecutorFinish()

static void pgss_ExecutorFinish ( QueryDesc queryDesc)
static

Definition at line 1023 of file pg_stat_statements.c.

1024 {
1026  PG_TRY();
1027  {
1028  if (prev_ExecutorFinish)
1029  prev_ExecutorFinish(queryDesc);
1030  else
1031  standard_ExecutorFinish(queryDesc);
1032  }
1033  PG_FINALLY();
1034  {
1036  }
1037  PG_END_TRY();
1038 }
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define PG_FINALLY(...)
Definition: elog.h:387
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:411

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

Referenced by _PG_init().

◆ pgss_ExecutorRun()

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

Definition at line 1001 of file pg_stat_statements.c.

1003 {
1005  PG_TRY();
1006  {
1007  if (prev_ExecutorRun)
1008  prev_ExecutorRun(queryDesc, direction, count, execute_once);
1009  else
1010  standard_ExecutorRun(queryDesc, direction, count, execute_once);
1011  }
1012  PG_FINALLY();
1013  {
1015  }
1016  PG_END_TRY();
1017 }
void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:312

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

Referenced by _PG_init().

◆ pgss_ExecutorStart()

static void pgss_ExecutorStart ( QueryDesc queryDesc,
int  eflags 
)
static

Definition at line 967 of file pg_stat_statements.c.

968 {
969  if (prev_ExecutorStart)
970  prev_ExecutorStart(queryDesc, eflags);
971  else
972  standard_ExecutorStart(queryDesc, eflags);
973 
974  /*
975  * If query has queryId zero, don't track it. This prevents double
976  * counting of optimizable statements that are directly contained in
977  * utility statements.
978  */
979  if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
980  {
981  /*
982  * Set up to track total elapsed time in ExecutorRun. Make sure the
983  * space is allocated in the per-query context so it will go away at
984  * ExecutorEnd.
985  */
986  if (queryDesc->totaltime == NULL)
987  {
988  MemoryContext oldcxt;
989 
990  oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
991  queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false);
992  MemoryContextSwitchTo(oldcxt);
993  }
994  }
995 }
void standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:149
Instrumentation * InstrAlloc(int n, int instrument_options, bool async_mode)
Definition: instrument.c:31
@ INSTRUMENT_ALL
Definition: instrument.h:63
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
MemoryContext es_query_cxt
Definition: execnodes.h:660

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

◆ pgss_memsize()

static Size pgss_memsize ( void  )
static

Definition at line 1901 of file pg_stat_statements.c.

1902 {
1903  Size size;
1904 
1905  size = MAXALIGN(sizeof(pgssSharedState));
1906  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1907 
1908  return size;
1909 }
#define MAXALIGN(LEN)
Definition: c.h:795
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:781
Size add_size(Size s1, Size s2)
Definition: shmem.c:502

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

Referenced by pgss_shmem_request().

◆ pgss_planner()

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

Definition at line 871 of file pg_stat_statements.c.

875 {
876  PlannedStmt *result;
877 
878  /*
879  * We can't process the query if no query_string is provided, as
880  * pgss_store needs it. We also ignore query without queryid, as it would
881  * be treated as a utility statement, which may not be the case.
882  *
883  * Note that planner_hook can be called from the planner itself, so we
884  * have a specific nesting level for the planner. However, utility
885  * commands containing optimizable statements can also call the planner,
886  * same for regular DML (for instance for underlying foreign key queries).
887  * So testing the planner nesting level only is not enough to detect real
888  * top level planner call.
889  */
891  && pgss_track_planning && query_string
892  && parse->queryId != UINT64CONST(0))
893  {
894  instr_time start;
896  BufferUsage bufusage_start,
897  bufusage;
898  WalUsage walusage_start,
899  walusage;
900 
901  /* We need to track buffer usage as the planner can access them. */
902  bufusage_start = pgBufferUsage;
903 
904  /*
905  * Similarly the planner could write some WAL records in some cases
906  * (e.g. setting a hint bit with those being WAL-logged)
907  */
908  walusage_start = pgWalUsage;
909  INSTR_TIME_SET_CURRENT(start);
910 
912  PG_TRY();
913  {
914  if (prev_planner_hook)
915  result = prev_planner_hook(parse, query_string, cursorOptions,
916  boundParams);
917  else
918  result = standard_planner(parse, query_string, cursorOptions,
919  boundParams);
920  }
921  PG_FINALLY();
922  {
924  }
925  PG_END_TRY();
926 
929 
930  /* calc differences of buffer counters. */
931  memset(&bufusage, 0, sizeof(BufferUsage));
932  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
933 
934  /* calc differences of WAL counters. */
935  memset(&walusage, 0, sizeof(WalUsage));
936  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
937 
938  pgss_store(query_string,
939  parse->queryId,
940  parse->stmt_location,
941  parse->stmt_len,
942  PGSS_PLAN,
944  0,
945  &bufusage,
946  &walusage,
947  NULL,
948  NULL);
949  }
950  else
951  {
952  if (prev_planner_hook)
953  result = prev_planner_hook(parse, query_string, cursorOptions,
954  boundParams);
955  else
956  result = standard_planner(parse, query_string, cursorOptions,
957  boundParams);
958  }
959 
960  return result;
961 }
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:181
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:191
WalUsage pgWalUsage
Definition: instrument.c:22
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition: instrument.c:280
BufferUsage pgBufferUsage
Definition: instrument.c:20
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:246
static int plan_nested_level
int duration
Definition: pgbench.c:184
PlannedStmt * standard_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: planner.c:286
static struct subre * parse(struct vars *v, int stopper, int type, struct state *init, struct state *final)
Definition: regcomp.c:717

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

Referenced by _PG_init().

◆ pgss_post_parse_analyze()

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

Definition at line 822 of file pg_stat_statements.c.

823 {
825  prev_post_parse_analyze_hook(pstate, query, jstate);
826 
827  /* Safety check... */
829  return;
830 
831  /*
832  * Clear queryId for prepared statements related utility, as those will
833  * inherit from the underlying statement's one (except DEALLOCATE which is
834  * entirely untracked).
835  */
836  if (query->utilityStmt)
837  {
839  {
840  query->queryId = UINT64CONST(0);
841  return;
842  }
843  }
844 
845  /*
846  * If query jumbling were able to identify any ignorable constants, we
847  * immediately create a hash table entry for the query, so that we can
848  * record the normalized form of the query string. If there were no such
849  * constants, the normalized string would be the same as the query text
850  * anyway, so there's no need for an early entry.
851  */
852  if (jstate && jstate->clocations_count > 0)
853  pgss_store(pstate->p_sourcetext,
854  query->queryId,
855  query->stmt_location,
856  query->stmt_len,
857  PGSS_INVALID,
858  0,
859  0,
860  NULL,
861  NULL,
862  NULL,
863  jstate);
864 }
#define PGSS_HANDLED_UTILITY(n)
const char * p_sourcetext
Definition: parse_node.h:192
int stmt_location
Definition: parsenodes.h:236
Node * utilityStmt
Definition: parsenodes.h:143

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

Referenced by _PG_init().

◆ pgss_ProcessUtility()

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

Definition at line 1080 of file pg_stat_statements.c.

1085 {
1086  Node *parsetree = pstmt->utilityStmt;
1087  uint64 saved_queryId = pstmt->queryId;
1088  int saved_stmt_location = pstmt->stmt_location;
1089  int saved_stmt_len = pstmt->stmt_len;
1090 
1091  /*
1092  * Force utility statements to get queryId zero. We do this even in cases
1093  * where the statement contains an optimizable statement for which a
1094  * queryId could be derived (such as EXPLAIN or DECLARE CURSOR). For such
1095  * cases, runtime control will first go through ProcessUtility and then
1096  * the executor, and we don't want the executor hooks to do anything,
1097  * since we are already measuring the statement's costs at the utility
1098  * level.
1099  *
1100  * Note that this is only done if pg_stat_statements is enabled and
1101  * configured to track utility statements, in the unlikely possibility
1102  * that user configured another extension to handle utility statements
1103  * only.
1104  */
1106  pstmt->queryId = UINT64CONST(0);
1107 
1108  /*
1109  * If it's an EXECUTE statement, we don't track it and don't increment the
1110  * nesting level. This allows the cycles to be charged to the underlying
1111  * PREPARE instead (by the Executor hooks), which is much more useful.
1112  *
1113  * We also don't track execution of PREPARE. If we did, we would get one
1114  * hash table entry for the PREPARE (with hash calculated from the query
1115  * string), and then a different one with the same query string (but hash
1116  * calculated from the query tree) would be used to accumulate costs of
1117  * ensuing EXECUTEs. This would be confusing, and inconsistent with other
1118  * cases where planning time is not included at all.
1119  *
1120  * Likewise, we don't track execution of DEALLOCATE.
1121  */
1123  PGSS_HANDLED_UTILITY(parsetree))
1124  {
1125  instr_time start;
1127  uint64 rows;
1128  BufferUsage bufusage_start,
1129  bufusage;
1130  WalUsage walusage_start,
1131  walusage;
1132 
1133  bufusage_start = pgBufferUsage;
1134  walusage_start = pgWalUsage;
1135  INSTR_TIME_SET_CURRENT(start);
1136 
1138  PG_TRY();
1139  {
1140  if (prev_ProcessUtility)
1141  prev_ProcessUtility(pstmt, queryString, readOnlyTree,
1142  context, params, queryEnv,
1143  dest, qc);
1144  else
1145  standard_ProcessUtility(pstmt, queryString, readOnlyTree,
1146  context, params, queryEnv,
1147  dest, qc);
1148  }
1149  PG_FINALLY();
1150  {
1152  }
1153  PG_END_TRY();
1154 
1155  /*
1156  * CAUTION: do not access the *pstmt data structure again below here.
1157  * If it was a ROLLBACK or similar, that data structure may have been
1158  * freed. We must copy everything we still need into local variables,
1159  * which we did above.
1160  *
1161  * For the same reason, we can't risk restoring pstmt->queryId to its
1162  * former value, which'd otherwise be a good idea.
1163  */
1164 
1166  INSTR_TIME_SUBTRACT(duration, start);
1167 
1168  /*
1169  * Track the total number of rows retrieved or affected by the utility
1170  * statements of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED
1171  * VIEW, REFRESH MATERIALIZED VIEW and SELECT INTO.
1172  */
1173  rows = (qc && (qc->commandTag == CMDTAG_COPY ||
1174  qc->commandTag == CMDTAG_FETCH ||
1175  qc->commandTag == CMDTAG_SELECT ||
1176  qc->commandTag == CMDTAG_REFRESH_MATERIALIZED_VIEW)) ?
1177  qc->nprocessed : 0;
1178 
1179  /* calc differences of buffer counters. */
1180  memset(&bufusage, 0, sizeof(BufferUsage));
1181  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
1182 
1183  /* calc differences of WAL counters. */
1184  memset(&walusage, 0, sizeof(WalUsage));
1185  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
1186 
1187  pgss_store(queryString,
1188  saved_queryId,
1189  saved_stmt_location,
1190  saved_stmt_len,
1191  PGSS_EXEC,
1193  rows,
1194  &bufusage,
1195  &walusage,
1196  NULL,
1197  NULL);
1198  }
1199  else
1200  {
1201  if (prev_ProcessUtility)
1202  prev_ProcessUtility(pstmt, queryString, readOnlyTree,
1203  context, params, queryEnv,
1204  dest, qc);
1205  else
1206  standard_ProcessUtility(pstmt, queryString, readOnlyTree,
1207  context, params, queryEnv,
1208  dest, qc);
1209  }
1210 }
Definition: nodes.h:129
Node * utilityStmt
Definition: plannodes.h:99
uint64 nprocessed
Definition: cmdtag.h:33
CommandTag commandTag
Definition: cmdtag.h:32
void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:547

References BufferUsageAccumDiff(), QueryCompletion::commandTag, generate_unaccent_rules::dest, 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().

◆ pgss_shmem_request()

static void pgss_shmem_request ( void  )
static

Definition at line 483 of file pg_stat_statements.c.

484 {
487 
489  RequestNamedLWLockTranche("pg_stat_statements", 1);
490 }
void RequestAddinShmemSpace(Size size)
Definition: ipci.c:70
void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)
Definition: lwlock.c:693
static Size pgss_memsize(void)

References pgss_memsize(), prev_shmem_request_hook, RequestAddinShmemSpace(), and RequestNamedLWLockTranche().

Referenced by _PG_init().

◆ pgss_shmem_shutdown()

static void pgss_shmem_shutdown ( int  code,
Datum  arg 
)
static

Definition at line 723 of file pg_stat_statements.c.

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

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(), len, LOG, PG_BINARY_W, pgss, PGSS_DUMP_FILE, PGSS_FILE_HEADER, pgss_hash, 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().

◆ pgss_shmem_startup()

static void pgss_shmem_startup ( void  )
static

Definition at line 499 of file pg_stat_statements.c.

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

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, PGSS_DUMP_FILE, PGSS_FILE_HEADER, pgss_hash, 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().

◆ 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,
const struct JitInstrumentation jitusage,
JumbleState jstate 
)
static

Definition at line 1224 of file pg_stat_statements.c.

1232 {
1233  pgssHashKey key;
1234  pgssEntry *entry;
1235  char *norm_query = NULL;
1236  int encoding = GetDatabaseEncoding();
1237 
1238  Assert(query != NULL);
1239 
1240  /* Safety check... */
1241  if (!pgss || !pgss_hash)
1242  return;
1243 
1244  /*
1245  * Nothing to do if compute_query_id isn't enabled and no other module
1246  * computed a query identifier.
1247  */
1248  if (queryId == UINT64CONST(0))
1249  return;
1250 
1251  /*
1252  * Confine our attention to the relevant part of the string, if the query
1253  * is a portion of a multi-statement source string, and update query
1254  * location and length if needed.
1255  */
1256  query = CleanQuerytext(query, &query_location, &query_len);
1257 
1258  /* Set up key for hashtable search */
1259 
1260  /* memset() is required when pgssHashKey is without padding only */
1261  memset(&key, 0, sizeof(pgssHashKey));
1262 
1263  key.userid = GetUserId();
1264  key.dbid = MyDatabaseId;
1265  key.queryid = queryId;
1266  key.toplevel = (exec_nested_level == 0);
1267 
1268  /* Lookup the hash table entry with shared lock. */
1270 
1271  entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
1272 
1273  /* Create new entry, if not present */
1274  if (!entry)
1275  {
1276  Size query_offset;
1277  int gc_count;
1278  bool stored;
1279  bool do_gc;
1280 
1281  /*
1282  * Create a new, normalized query string if caller asked. We don't
1283  * need to hold the lock while doing this work. (Note: in any case,
1284  * it's possible that someone else creates a duplicate hashtable entry
1285  * in the interval where we don't hold the lock below. That case is
1286  * handled by entry_alloc.)
1287  */
1288  if (jstate)
1289  {
1291  norm_query = generate_normalized_query(jstate, query,
1292  query_location,
1293  &query_len);
1295  }
1296 
1297  /* Append new query text to file with only shared lock held */
1298  stored = qtext_store(norm_query ? norm_query : query, query_len,
1299  &query_offset, &gc_count);
1300 
1301  /*
1302  * Determine whether we need to garbage collect external query texts
1303  * while the shared lock is still held. This micro-optimization
1304  * avoids taking the time to decide this while holding exclusive lock.
1305  */
1306  do_gc = need_gc_qtexts();
1307 
1308  /* Need exclusive lock to make a new hashtable entry - promote */
1311 
1312  /*
1313  * A garbage collection may have occurred while we weren't holding the
1314  * lock. In the unlikely event that this happens, the query text we
1315  * stored above will have been garbage collected, so write it again.
1316  * This should be infrequent enough that doing it while holding
1317  * exclusive lock isn't a performance problem.
1318  */
1319  if (!stored || pgss->gc_count != gc_count)
1320  stored = qtext_store(norm_query ? norm_query : query, query_len,
1321  &query_offset, NULL);
1322 
1323  /* If we failed to write to the text file, give up */
1324  if (!stored)
1325  goto done;
1326 
1327  /* OK to create a new hashtable entry */
1328  entry = entry_alloc(&key, query_offset, query_len, encoding,
1329  jstate != NULL);
1330 
1331  /* If needed, perform garbage collection while exclusive lock held */
1332  if (do_gc)
1333  gc_qtexts();
1334  }
1335 
1336  /* Increment the counts, except when jstate is not NULL */
1337  if (!jstate)
1338  {
1339  /*
1340  * Grab the spinlock while updating the counters (see comment about
1341  * locking rules at the head of the file)
1342  */
1343  volatile pgssEntry *e = (volatile pgssEntry *) entry;
1344 
1345  Assert(kind == PGSS_PLAN || kind == PGSS_EXEC);
1346 
1347  SpinLockAcquire(&e->mutex);
1348 
1349  /* "Unstick" entry if it was previously sticky */
1350  if (IS_STICKY(e->counters))
1351  e->counters.usage = USAGE_INIT;
1352 
1353  e->counters.calls[kind] += 1;
1354  e->counters.total_time[kind] += total_time;
1355 
1356  if (e->counters.calls[kind] == 1)
1357  {
1358  e->counters.min_time[kind] = total_time;
1359  e->counters.max_time[kind] = total_time;
1360  e->counters.mean_time[kind] = total_time;
1361  }
1362  else
1363  {
1364  /*
1365  * Welford's method for accurately computing variance. See
1366  * <http://www.johndcook.com/blog/standard_deviation/>
1367  */
1368  double old_mean = e->counters.mean_time[kind];
1369 
1370  e->counters.mean_time[kind] +=
1371  (total_time - old_mean) / e->counters.calls[kind];
1372  e->counters.sum_var_time[kind] +=
1373  (total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
1374 
1375  /* calculate min and max time */
1376  if (e->counters.min_time[kind] > total_time)
1377  e->counters.min_time[kind] = total_time;
1378  if (e->counters.max_time[kind] < total_time)
1379  e->counters.max_time[kind] = total_time;
1380  }
1381  e->counters.rows += rows;
1382  e->counters.shared_blks_hit += bufusage->shared_blks_hit;
1383  e->counters.shared_blks_read += bufusage->shared_blks_read;
1384  e->counters.shared_blks_dirtied += bufusage->shared_blks_dirtied;
1385  e->counters.shared_blks_written += bufusage->shared_blks_written;
1386  e->counters.local_blks_hit += bufusage->local_blks_hit;
1387  e->counters.local_blks_read += bufusage->local_blks_read;
1388  e->counters.local_blks_dirtied += bufusage->local_blks_dirtied;
1389  e->counters.local_blks_written += bufusage->local_blks_written;
1390  e->counters.temp_blks_read += bufusage->temp_blks_read;
1391  e->counters.temp_blks_written += bufusage->temp_blks_written;
1392  e->counters.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time);
1393  e->counters.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time);
1394  e->counters.temp_blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_read_time);
1395  e->counters.temp_blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->temp_blk_write_time);
1396  e->counters.usage += USAGE_EXEC(total_time);
1397  e->counters.wal_records += walusage->wal_records;
1398  e->counters.wal_fpi += walusage->wal_fpi;
1399  e->counters.wal_bytes += walusage->wal_bytes;
1400  if (jitusage)
1401  {
1402  e->counters.jit_functions += jitusage->created_functions;
1403  e->counters.jit_generation_time += INSTR_TIME_GET_MILLISEC(jitusage->generation_counter);
1404 
1406  e->counters.jit_inlining_count++;
1407  e->counters.jit_inlining_time += INSTR_TIME_GET_MILLISEC(jitusage->inlining_counter);
1408 
1410  e->counters.jit_optimization_count++;
1411  e->counters.jit_optimization_time += INSTR_TIME_GET_MILLISEC(jitusage->optimization_counter);
1412 
1414  e->counters.jit_emission_count++;
1415  e->counters.jit_emission_time += INSTR_TIME_GET_MILLISEC(jitusage->emission_counter);
1416  }
1417 
1418  SpinLockRelease(&e->mutex);
1419  }
1420 
1421 done:
1423 
1424  /* We postpone this clean-up until we're out of the lock */
1425  if (norm_query)
1426  pfree(norm_query);
1427 }
Oid MyDatabaseId
Definition: globals.c:89
@ HASH_FIND
Definition: hsearch.h:113
int GetDatabaseEncoding(void)
Definition: mbutils.c:1268
static void gc_qtexts(void)
static bool qtext_store(const char *query, int query_len, Size *query_offset, int *gc_count)
#define USAGE_EXEC(duration)
static char * generate_normalized_query(JumbleState *jstate, const char *query, int query_loc, int *query_len_p)
const char * CleanQuerytext(const char *query, int *location, int *len)
int64 shared_blks_dirtied
Definition: instrument.h:28
int64 local_blks_hit
Definition: instrument.h:30
instr_time temp_blk_write_time
Definition: instrument.h:39
int64 local_blks_written
Definition: instrument.h:33
instr_time blk_write_time
Definition: instrument.h:37
instr_time temp_blk_read_time
Definition: instrument.h:38
int64 temp_blks_read
Definition: instrument.h:34
int64 shared_blks_read
Definition: instrument.h:27
int64 shared_blks_written
Definition: instrument.h:29
int64 temp_blks_written
Definition: instrument.h:35
instr_time blk_read_time
Definition: instrument.h:36
int64 local_blks_read
Definition: instrument.h:31
int64 local_blks_dirtied
Definition: instrument.h:32
int64 shared_blks_hit
Definition: instrument.h:26
instr_time generation_counter
Definition: jit.h:33
size_t created_functions
Definition: jit.h:30
instr_time optimization_counter
Definition: jit.h:39
instr_time emission_counter
Definition: jit.h:42
instr_time inlining_counter
Definition: jit.h:36
uint64 wal_bytes
Definition: instrument.h:53
int64 wal_fpi
Definition: instrument.h:52
int64 wal_records
Definition: instrument.h:51

References Assert(), BufferUsage::blk_read_time, BufferUsage::blk_write_time, CleanQuerytext(), JitInstrumentation::created_functions, JitInstrumentation::emission_counter, encoding, entry_alloc(), exec_nested_level, pgssSharedState::gc_count, gc_qtexts(), generate_normalized_query(), JitInstrumentation::generation_counter, GetDatabaseEncoding(), GetUserId(), HASH_FIND, hash_search(), JitInstrumentation::inlining_counter, INSTR_TIME_GET_MILLISEC, IS_STICKY, sort-test::key, BufferUsage::local_blks_dirtied, BufferUsage::local_blks_hit, BufferUsage::local_blks_read, BufferUsage::local_blks_written, pgssSharedState::lock, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MyDatabaseId, need_gc_qtexts(), JitInstrumentation::optimization_counter, pfree(), pgss, PGSS_EXEC, pgss_hash, PGSS_PLAN, qtext_store(), BufferUsage::shared_blks_dirtied, BufferUsage::shared_blks_hit, BufferUsage::shared_blks_read, BufferUsage::shared_blks_written, SpinLockAcquire, SpinLockRelease, BufferUsage::temp_blk_read_time, BufferUsage::temp_blk_write_time, BufferUsage::temp_blks_read, BufferUsage::temp_blks_written, USAGE_EXEC, USAGE_INIT, WalUsage::wal_bytes, WalUsage::wal_fpi, and WalUsage::wal_records.

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

◆ qtext_fetch()

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

Definition at line 2265 of file pg_stat_statements.c.

2267 {
2268  /* File read failed? */
2269  if (buffer == NULL)
2270  return NULL;
2271  /* Bogus offset/length? */
2272  if (query_len < 0 ||
2273  query_offset + query_len >= buffer_size)
2274  return NULL;
2275  /* As a further sanity check, make sure there's a trailing null */
2276  if (buffer[query_offset + query_len] != '\0')
2277  return NULL;
2278  /* Looks OK */
2279  return buffer + query_offset;
2280 }

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

◆ qtext_load_file()

static char * qtext_load_file ( Size buffer_size)
static

Definition at line 2172 of file pg_stat_statements.c.

2173 {
2174  char *buf;
2175  int fd;
2176  struct stat stat;
2177  Size nread;
2178 
2180  if (fd < 0)
2181  {
2182  if (errno != ENOENT)
2183  ereport(LOG,
2185  errmsg("could not read file \"%s\": %m",
2186  PGSS_TEXT_FILE)));
2187  return NULL;
2188  }
2189 
2190  /* Get file length */
2191  if (fstat(fd, &stat))
2192  {
2193  ereport(LOG,
2195  errmsg("could not stat file \"%s\": %m",
2196  PGSS_TEXT_FILE)));
2198  return NULL;
2199  }
2200 
2201  /* Allocate buffer; beware that off_t might be wider than size_t */
2202  if (stat.st_size <= MaxAllocHugeSize)
2203  buf = (char *) malloc(stat.st_size);
2204  else
2205  buf = NULL;
2206  if (buf == NULL)
2207  {
2208  ereport(LOG,
2209  (errcode(ERRCODE_OUT_OF_MEMORY),
2210  errmsg("out of memory"),
2211  errdetail("Could not allocate enough memory to read file \"%s\".",
2212  PGSS_TEXT_FILE)));
2214  return NULL;
2215  }
2216 
2217  /*
2218  * OK, slurp in the file. Windows fails if we try to read more than
2219  * INT_MAX bytes at once, and other platforms might not like that either,
2220  * so read a very large file in 1GB segments.
2221  */
2222  nread = 0;
2223  while (nread < stat.st_size)
2224  {
2225  int toread = Min(1024 * 1024 * 1024, stat.st_size - nread);
2226 
2227  /*
2228  * If we get a short read and errno doesn't get set, the reason is
2229  * probably that garbage collection truncated the file since we did
2230  * the fstat(), so we don't log a complaint --- but we don't return
2231  * the data, either, since it's most likely corrupt due to concurrent
2232  * writes from garbage collection.
2233  */
2234  errno = 0;
2235  if (read(fd, buf + nread, toread) != toread)
2236  {
2237  if (errno)
2238  ereport(LOG,
2240  errmsg("could not read file \"%s\": %m",
2241  PGSS_TEXT_FILE)));
2242  free(buf);
2244  return NULL;
2245  }
2246  nread += toread;
2247  }
2248 
2249  if (CloseTransientFile(fd) != 0)
2250  ereport(LOG,
2252  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
2253 
2254  *buffer_size = nread;
2255  return buf;
2256 }
#define PG_BINARY
Definition: c.h:1260
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int CloseTransientFile(int fd)
Definition: fd.c:2610
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2434
#define malloc(a)
Definition: header.h:50
#define read(a, b, c)
Definition: win32.h:13
#define MaxAllocHugeSize
Definition: memutils.h:45
static int fd(const char *x, int i)
Definition: preproc-init.c:105
__int64 st_size
Definition: win32_port.h:275
#define fstat
Definition: win32_port.h:285

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

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

◆ qtext_store()

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

Definition at line 2080 of file pg_stat_statements.c.

2082 {
2083  Size off;
2084  int fd;
2085 
2086  /*
2087  * We use a spinlock to protect extent/n_writers/gc_count, so that
2088  * multiple processes may execute this function concurrently.
2089  */
2090  {
2091  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2092 
2093  SpinLockAcquire(&s->mutex);
2094  off = s->extent;
2095  s->extent += query_len + 1;
2096  s->n_writers++;
2097  if (gc_count)
2098  *gc_count = s->gc_count;
2099  SpinLockRelease(&s->mutex);
2100  }
2101 
2102  *query_offset = off;
2103 
2104  /*
2105  * Don't allow the file to grow larger than what qtext_load_file can
2106  * (theoretically) handle. This has been seen to be reachable on 32-bit
2107  * platforms.
2108  */
2109  if (unlikely(query_len >= MaxAllocHugeSize - off))
2110  {
2111  errno = EFBIG; /* not quite right, but it'll do */
2112  fd = -1;
2113  goto error;
2114  }
2115 
2116  /* Now write the data into the successfully-reserved part of the file */
2117  fd = OpenTransientFile(PGSS_TEXT_FILE, O_RDWR | O_CREAT | PG_BINARY);
2118  if (fd < 0)
2119  goto error;
2120 
2121  if (pg_pwrite(fd, query, query_len, off) != query_len)
2122  goto error;
2123  if (pg_pwrite(fd, "\0", 1, off + query_len) != 1)
2124  goto error;
2125 
2127 
2128  /* Mark our write complete */
2129  {
2130  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2131 
2132  SpinLockAcquire(&s->mutex);
2133  s->n_writers--;
2134  SpinLockRelease(&s->mutex);
2135  }
2136 
2137  return true;
2138 
2139 error:
2140  ereport(LOG,
2142  errmsg("could not write file \"%s\": %m",
2143  PGSS_TEXT_FILE)));
2144 
2145  if (fd >= 0)
2147 
2148  /* Mark our write complete */
2149  {
2150  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
2151 
2152  SpinLockAcquire(&s->mutex);
2153  s->n_writers--;
2154  SpinLockRelease(&s->mutex);
2155  }
2156 
2157  return false;
2158 }
#define unlikely(x)
Definition: c.h:295
#define pg_pwrite
Definition: port.h:226

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

Referenced by pgss_store().

Variable Documentation

◆ exec_nested_level

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 76 of file pg_stat_statements.c.

◆ pgss

◆ PGSS_FILE_HEADER

const uint32 PGSS_FILE_HEADER = 0x20220408
static

Definition at line 87 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_hash

◆ pgss_max

int pgss_max = 5000
static

◆ PGSS_PG_MAJOR_VERSION

const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100
static

Definition at line 90 of file pg_stat_statements.c.

Referenced by pgss_shmem_shutdown(), and pgss_shmem_startup().

◆ pgss_save

bool pgss_save = true
static

Definition at line 291 of file pg_stat_statements.c.

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

◆ pgss_track

int pgss_track = PGSS_TRACK_TOP
static

Definition at line 287 of file pg_stat_statements.c.

Referenced by _PG_init().

◆ pgss_track_planning

bool pgss_track_planning = false
static

Definition at line 289 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_planner().

◆ pgss_track_utility

bool pgss_track_utility = true
static

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

Referenced by pgss_planner().

◆ prev_ExecutorEnd

ExecutorEnd_hook_type prev_ExecutorEnd = NULL
static

Definition at line 262 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_ExecutorEnd().

◆ prev_ExecutorFinish

ExecutorFinish_hook_type prev_ExecutorFinish = NULL
static

Definition at line 261 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_ExecutorFinish().

◆ prev_ExecutorRun

ExecutorRun_hook_type prev_ExecutorRun = NULL
static

Definition at line 260 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_ExecutorRun().

◆ prev_ExecutorStart

ExecutorStart_hook_type prev_ExecutorStart = NULL
static

Definition at line 259 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_ExecutorStart().

◆ prev_planner_hook

planner_hook_type prev_planner_hook = NULL
static

Definition at line 258 of file pg_stat_statements.c.

Referenced by _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 257 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_post_parse_analyze().

◆ prev_ProcessUtility

ProcessUtility_hook_type prev_ProcessUtility = NULL
static

Definition at line 263 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_ProcessUtility().

◆ prev_shmem_request_hook

shmem_request_hook_type prev_shmem_request_hook = NULL
static

Definition at line 255 of file pg_stat_statements.c.

Referenced by _PG_init(), and pgss_shmem_request().

◆ prev_shmem_startup_hook

shmem_startup_hook_type prev_shmem_startup_hook = NULL
static

Definition at line 256 of file pg_stat_statements.c.

Referenced by _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 267 of file pg_stat_statements.c.

Referenced by _PG_init().