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 "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "parser/scanner.h"
#include "parser/scansup.h"
#include "pgstat.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/lwlock.h"
#include "storage/shmem.h"
#include "storage/spin.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/queryjumble.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
Include dependency graph for pg_stat_statements.c:

Go to the source code of this file.

Data Structures

struct  pgssHashKey
 
struct  Counters
 
struct  pgssGlobalStats
 
struct  pgssEntry
 
struct  pgssSharedState
 

Macros

#define PGSS_DUMP_FILE   PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
 
#define PGSS_TEXT_FILE   PG_STAT_TMP_DIR "/pgss_query_texts.stat"
 
#define USAGE_EXEC(duration)   (1.0)
 
#define USAGE_INIT   (1.0) /* including initial planning */
 
#define ASSUMED_MEDIAN_INIT   (10.0) /* initial assumed median usage */
 
#define ASSUMED_LENGTH_INIT   1024 /* initial assumed mean query length */
 
#define USAGE_DECREASE_FACTOR   (0.99) /* decreased every entry_dealloc */
 
#define STICKY_DECREASE_FACTOR   (0.50) /* factor for sticky entries */
 
#define USAGE_DEALLOC_PERCENT   5 /* free this % of entries at once */
 
#define IS_STICKY(c)   ((c.calls[PGSS_PLAN] + c.calls[PGSS_EXEC]) == 0)
 
#define PGSS_HANDLED_UTILITY(n)
 
#define pgss_enabled(level)
 
#define record_gc_qtexts()
 
#define PG_STAT_STATEMENTS_COLS_V1_0   14
 
#define PG_STAT_STATEMENTS_COLS_V1_1   18
 
#define PG_STAT_STATEMENTS_COLS_V1_2   19
 
#define PG_STAT_STATEMENTS_COLS_V1_3   23
 
#define PG_STAT_STATEMENTS_COLS_V1_8   32
 
#define PG_STAT_STATEMENTS_COLS_V1_9   33
 
#define PG_STAT_STATEMENTS_COLS_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 1465 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_0

#define PG_STAT_STATEMENTS_COLS_V1_0   14

Definition at line 1458 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_1

#define PG_STAT_STATEMENTS_COLS_V1_1   18

Definition at line 1459 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_10

#define PG_STAT_STATEMENTS_COLS_V1_10   43

Definition at line 1464 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_2

#define PG_STAT_STATEMENTS_COLS_V1_2   19

Definition at line 1460 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_3

#define PG_STAT_STATEMENTS_COLS_V1_3   23

Definition at line 1461 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_8

#define PG_STAT_STATEMENTS_COLS_V1_8   32

Definition at line 1462 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_COLS_V1_9

#define PG_STAT_STATEMENTS_COLS_V1_9   33

Definition at line 1463 of file pg_stat_statements.c.

◆ PG_STAT_STATEMENTS_INFO_COLS

#define PG_STAT_STATEMENTS_INFO_COLS   2

Definition at line 1858 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:162

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:75
ExecutorFinish_hook_type ExecutorFinish_hook
Definition: execMain.c:74
ExecutorStart_hook_type ExecutorStart_hook
Definition: execMain.c:72
ExecutorRun_hook_type ExecutorRun_hook
Definition: execMain.c:73
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:5051
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:4940
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5087
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:4966
@ 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:1768
bool process_shared_preload_libraries_in_progress
Definition: miscinit.c:1765
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:75
void EnableQueryId(void)
Definition: queryjumble.c:151
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 2864 of file pg_stat_statements.c.

2865 {
2866  int l = ((const LocationLen *) a)->location;
2867  int r = ((const LocationLen *) b)->location;
2868 
2869  if (l < r)
2870  return -1;
2871  else if (l > r)
2872  return +1;
2873  else
2874  return 0;
2875 }
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 1927 of file pg_stat_statements.c.

1929 {
1930  pgssEntry *entry;
1931  bool found;
1932 
1933  /* Make space if needed */
1935  entry_dealloc();
1936 
1937  /* Find or create an entry with desired hash code */
1938  entry = (pgssEntry *) hash_search(pgss_hash, key, HASH_ENTER, &found);
1939 
1940  if (!found)
1941  {
1942  /* New entry, initialize it */
1943 
1944  /* reset the statistics */
1945  memset(&entry->counters, 0, sizeof(Counters));
1946  /* set the appropriate initial usage count */
1947  entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
1948  /* re-initialize the mutex each time ... we assume no one using it */
1949  SpinLockInit(&entry->mutex);
1950  /* ... and don't forget the query text metadata */
1951  Assert(query_len >= 0);
1952  entry->query_offset = query_offset;
1953  entry->query_len = query_len;
1954  entry->encoding = encoding;
1955  }
1956 
1957  return entry;
1958 }
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 1964 of file pg_stat_statements.c.

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

Referenced by entry_dealloc().

◆ entry_dealloc()

static void entry_dealloc ( void  )
static

Definition at line 1983 of file pg_stat_statements.c.

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

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

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

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

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

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

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

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

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

1479 {
1480  bool showtext = PG_GETARG_BOOL(0);
1481 
1482  pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
1483 
1484  return (Datum) 0;
1485 }
#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 1518 of file pg_stat_statements.c.

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

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

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

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

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

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

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

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

1865 {
1866  pgssGlobalStats stats;
1867  TupleDesc tupdesc;
1869  bool nulls[PG_STAT_STATEMENTS_INFO_COLS] = {0};
1870 
1871  if (!pgss || !pgss_hash)
1872  ereport(ERROR,
1873  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1874  errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
1875 
1876  /* Build a tuple descriptor for our result type */
1877  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1878  elog(ERROR, "return type must be a row type");
1879 
1880  /* Read global statistics for pg_stat_statements */
1881  {
1882  volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
1883 
1884  SpinLockAcquire(&s->mutex);
1885  stats = s->stats;
1886  SpinLockRelease(&s->mutex);
1887  }
1888 
1889  values[0] = Int64GetDatum(stats.dealloc);
1891 
1893 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1683
#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 1542 of file pg_stat_statements.c.

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

1451 {
1452  entry_reset(0, 0, 0);
1453 
1454  PG_RETURN_VOID();
1455 }
#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 1431 of file pg_stat_statements.c.

1432 {
1433  Oid userid;
1434  Oid dbid;
1435  uint64 queryid;
1436 
1437  userid = PG_GETARG_OID(0);
1438  dbid = PG_GETARG_OID(1);
1439  queryid = (uint64) PG_GETARG_INT64(2);
1440 
1441  entry_reset(userid, dbid, queryid);
1442 
1443  PG_RETURN_VOID();
1444 }
#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 1042 of file pg_stat_statements.c.

1043 {
1044  uint64 queryId = queryDesc->plannedstmt->queryId;
1045 
1046  if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
1048  {
1049  /*
1050  * Make sure stats accumulation is done. (Note: it's okay if several
1051  * levels of hook all do this.)
1052  */
1053  InstrEndLoop(queryDesc->totaltime);
1054 
1055  pgss_store(queryDesc->sourceText,
1056  queryId,
1057  queryDesc->plannedstmt->stmt_location,
1058  queryDesc->plannedstmt->stmt_len,
1059  PGSS_EXEC,
1060  queryDesc->totaltime->total * 1000.0, /* convert to msec */
1061  queryDesc->estate->es_processed,
1062  &queryDesc->totaltime->bufusage,
1063  &queryDesc->totaltime->walusage,
1064  queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
1065  NULL);
1066  }
1067 
1068  if (prev_ExecutorEnd)
1069  prev_ExecutorEnd(queryDesc);
1070  else
1071  standard_ExecutorEnd(queryDesc);
1072 }
void standard_ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:470
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:654
struct JitContext * es_jit
Definition: execnodes.h:696
WalUsage walusage
Definition: instrument.h:90
BufferUsage bufusage
Definition: instrument.h:89
JitInstrumentation instr
Definition: jit.h:61
int stmt_location
Definition: plannodes.h:96
int stmt_len
Definition: plannodes.h:97
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 1021 of file pg_stat_statements.c.

1022 {
1024  PG_TRY();
1025  {
1026  if (prev_ExecutorFinish)
1027  prev_ExecutorFinish(queryDesc);
1028  else
1029  standard_ExecutorFinish(queryDesc);
1030  }
1031  PG_FINALLY();
1032  {
1034  }
1035  PG_END_TRY();
1036 }
#define PG_TRY(...)
Definition: elog.h:309
#define PG_END_TRY(...)
Definition: elog.h:334
#define PG_FINALLY(...)
Definition: elog.h:326
void standard_ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:410

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

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

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

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

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

1900 {
1901  Size size;
1902 
1903  size = MAXALIGN(sizeof(pgssSharedState));
1904  size = add_size(size, hash_estimate_size(pgss_max, sizeof(pgssEntry)));
1905 
1906  return size;
1907 }
#define MAXALIGN(LEN)
Definition: c.h:747
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 869 of file pg_stat_statements.c.

873 {
874  PlannedStmt *result;
875 
876  /*
877  * We can't process the query if no query_string is provided, as
878  * pgss_store needs it. We also ignore query without queryid, as it would
879  * be treated as a utility statement, which may not be the case.
880  *
881  * Note that planner_hook can be called from the planner itself, so we
882  * have a specific nesting level for the planner. However, utility
883  * commands containing optimizable statements can also call the planner,
884  * same for regular DML (for instance for underlying foreign key queries).
885  * So testing the planner nesting level only is not enough to detect real
886  * top level planner call.
887  */
889  && pgss_track_planning && query_string
890  && parse->queryId != UINT64CONST(0))
891  {
892  instr_time start;
894  BufferUsage bufusage_start,
895  bufusage;
896  WalUsage walusage_start,
897  walusage;
898 
899  /* We need to track buffer usage as the planner can access them. */
900  bufusage_start = pgBufferUsage;
901 
902  /*
903  * Similarly the planner could write some WAL records in some cases
904  * (e.g. setting a hint bit with those being WAL-logged)
905  */
906  walusage_start = pgWalUsage;
907  INSTR_TIME_SET_CURRENT(start);
908 
910  PG_TRY();
911  {
912  if (prev_planner_hook)
913  result = prev_planner_hook(parse, query_string, cursorOptions,
914  boundParams);
915  else
916  result = standard_planner(parse, query_string, cursorOptions,
917  boundParams);
918  }
919  PG_FINALLY();
920  {
922  }
923  PG_END_TRY();
924 
927 
928  /* calc differences of buffer counters. */
929  memset(&bufusage, 0, sizeof(BufferUsage));
930  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
931 
932  /* calc differences of WAL counters. */
933  memset(&walusage, 0, sizeof(WalUsage));
934  WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
935 
936  pgss_store(query_string,
937  parse->queryId,
938  parse->stmt_location,
939  parse->stmt_len,
940  PGSS_PLAN,
942  0,
943  &bufusage,
944  &walusage,
945  NULL,
946  NULL);
947  }
948  else
949  {
950  if (prev_planner_hook)
951  result = prev_planner_hook(parse, query_string, cursorOptions,
952  boundParams);
953  else
954  result = standard_planner(parse, query_string, cursorOptions,
955  boundParams);
956  }
957 
958  return result;
959 }
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:89
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:103
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:135
struct timespec instr_time
Definition: instr_time.h:83
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:183
PlannedStmt * standard_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: planner.c:283
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  query->queryId = UINT64CONST(0);
840  return;
841  }
842 
843  /*
844  * If query jumbling were able to identify any ignorable constants, we
845  * immediately create a hash table entry for the query, so that we can
846  * record the normalized form of the query string. If there were no such
847  * constants, the normalized string would be the same as the query text
848  * anyway, so there's no need for an early entry.
849  */
850  if (jstate && jstate->clocations_count > 0)
851  pgss_store(pstate->p_sourcetext,
852  query->queryId,
853  query->stmt_location,
854  query->stmt_len,
855  PGSS_INVALID,
856  0,
857  0,
858  NULL,
859  NULL,
860  NULL,
861  jstate);
862 }
#define PGSS_HANDLED_UTILITY(n)
const char * p_sourcetext
Definition: parse_node.h:182
int stmt_location
Definition: parsenodes.h:202
int stmt_len
Definition: parsenodes.h:203
Node * utilityStmt
Definition: parsenodes.h:134

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_len, 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 1078 of file pg_stat_statements.c.

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

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

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

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

2171 {
2172  char *buf;
2173  int fd;
2174  struct stat stat;
2175  Size nread;
2176 
2178  if (fd < 0)
2179  {
2180  if (errno != ENOENT)
2181  ereport(LOG,
2183  errmsg("could not read file \"%s\": %m",
2184  PGSS_TEXT_FILE)));
2185  return NULL;
2186  }
2187 
2188  /* Get file length */
2189  if (fstat(fd, &stat))
2190  {
2191  ereport(LOG,
2193  errmsg("could not stat file \"%s\": %m",
2194  PGSS_TEXT_FILE)));
2196  return NULL;
2197  }
2198 
2199  /* Allocate buffer; beware that off_t might be wider than size_t */
2200  if (stat.st_size <= MaxAllocHugeSize)
2201  buf = (char *) malloc(stat.st_size);
2202  else
2203  buf = NULL;
2204  if (buf == NULL)
2205  {
2206  ereport(LOG,
2207  (errcode(ERRCODE_OUT_OF_MEMORY),
2208  errmsg("out of memory"),
2209  errdetail("Could not allocate enough memory to read file \"%s\".",
2210  PGSS_TEXT_FILE)));
2212  return NULL;
2213  }
2214 
2215  /*
2216  * OK, slurp in the file. Windows fails if we try to read more than
2217  * INT_MAX bytes at once, and other platforms might not like that either,
2218  * so read a very large file in 1GB segments.
2219  */
2220  nread = 0;
2221  while (nread < stat.st_size)
2222  {
2223  int toread = Min(1024 * 1024 * 1024, stat.st_size - nread);
2224 
2225  /*
2226  * If we get a short read and errno doesn't get set, the reason is
2227  * probably that garbage collection truncated the file since we did
2228  * the fstat(), so we don't log a complaint --- but we don't return
2229  * the data, either, since it's most likely corrupt due to concurrent
2230  * writes from garbage collection.
2231  */
2232  errno = 0;
2233  if (read(fd, buf + nread, toread) != toread)
2234  {
2235  if (errno)
2236  ereport(LOG,
2238  errmsg("could not read file \"%s\": %m",
2239  PGSS_TEXT_FILE)));
2240  free(buf);
2242  return NULL;
2243  }
2244  nread += toread;
2245  }
2246 
2247  if (CloseTransientFile(fd) != 0)
2248  ereport(LOG,
2250  errmsg("could not close file \"%s\": %m", PGSS_TEXT_FILE)));
2251 
2252  *buffer_size = nread;
2253  return buf;
2254 }
#define PG_BINARY
Definition: c.h:1209
int errdetail(const char *fmt,...)
Definition: elog.c:1039
int CloseTransientFile(int fd)
Definition: fd.c:2609
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2433
#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 2078 of file pg_stat_statements.c.

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